注解和反射

简介

Annotation是jdk5引入的,可以对程序做出解释,可以被其他程序读取,可以附加在package、class、method、field上面,相当于给它们添加了额外的辅助信息。

内置注解

@Override :字类重写父类方法

@Deprecated :不建议使用的方法,过时

@SuppressWarnings :抑制编译时的警告信息

元注解

  • 元注解的作用就是负责注解其他注解,java定义了4个标准的meta-annotation类型,,它们被用来提供对其他注解类型作说明;
  • 这些类型和它们支持的类在java.lang.annotation包中可以找到
    • @Target:用于描述注解的使用范围,(TYPE, FIELD, METHOD)类、成员属性、方法等
    • @Retention:表示需要什么级别保存该注解信息,用于描述注解的生命周期
      • SOURCE<CLASS<RUNTIME
    • @Document:说明该注解将被包含在javadoc中
    • @Inherited:说明字类可以继承父类中的该注解

自定义注解

@interface关键字

@Karotte(age = 28,name = "悟空")
public class KaDemo {
    public static void main(String[] args) {
    }
}
@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface Karotte{
   String name() default "卡卡";
   int age();
}

反射机制

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {
    private int id;
    private String name;
    public void study(){
        System.out.println("学习");
    }
    private void friend(Student student){
        System.out.println("社交"+student.getName());
    }
}

动态语言和静态语言

动态语言:在运行时可以改变其结构的语言,包括Object-C、C#、JavaScript、PHP、Python等

静态语言:运行时结构不可变的语言,如java、C、C++等,但是java利用***反射(Reflection)***机制获得类似动态语言的特性,被称之为“准动态语言

  • 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。

正常方式: 引入需要的包类名称=》通过new实例化=》取得实例化对象

反射方式:实例化对象=》getClass()方法=》得到完整的包类名称

反射相关的主要API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:类的方法
  • java.lang.reflect.Field:类的成员变量
  • java.lang.reflect.Constructor:代表类的构造方法

获得Class对象的三种方式

Class stu = Class.forName("com.karotte.demo.Student");//类全路径
Student student = new Student();
Class stu = student.getClass();//实例对象
Class stu = Student.class;//类名
Class superclass = stu.getSuperclass();
//==================================
1956725890
1956725890
1956725890
class java.lang.Object

所有类型的class

Class c1 = Object.class;//类
Class c1 = Comparable.class;//接口
Class c1 = String[].class;//数组
Class c1 = int[][].class;//二维数组
Class c1 = Override.class;//注解
Class c1 = ElementType.class;//枚举
Class c1 = Integer.class;//基本包装类
Class c1 = void.class;//void
Class c1 = Class.class;//Class

类的加载

  • 加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法去的运行时数据结构,然后生成一个代表这个类的Class对象
  • 链接:将java的二进制代码合并到JVM的运行状态之中的过程
    • 验证:确保加载的类信息符合JVM规范,没有安全方面的问题
    • 准备:正式为类变量(static)分配内存并设置类变量默认初始化值的阶段,这些内存都将在方法区分配
    • 解析:虚拟机常量池内的符号引用(常量名)替换为直接引用的过程
  • 初始化
    • 执行类构造器clinit方法的过程。类构造器方法由编译期自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)
    • 当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化
    • 虚拟机会保证一个类的clinit方法在多线程环境中被正确加锁和同步

类初始化

  • 类的主动引用-一定会发生初始化

    • 虚拟机启动,main方法所在的类
    • new一个类对象
    • 调用类的静态成员和静态方法,不包含final常量
    • 使用反射包的方法对类进行反射调用
    • 初始化一个类时,如果父类没有初始化,那么先初始化父类
  • 类的被动引用-不会发生初始化

    • 当访问静态域时,只有真正声明这个域的类才会被初始化,如当子类引用父类的静态常量,不会导致字类初始化
    • 通过数组定义类引用,不会触发此类的初始化
    • 引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)

类加载器

  • 引导类加载器(BootstrapClassloader):用C++编写,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取
  • 扩展类加载器(ExtensionClassloader):负责jre/lib/ext目录下的jar包或者 -D java.ext.dirs指定目录下的jar包装入工作库
  • 系统类加载器(ApplicationClassloader):负责java -classpath 或者 -D java.class.path所指目录下的类与jar包装入工作库,是最常用的加载器
System.out.println(System.getProperty("java.ext.dirs"));
System.out.println(System.getProperty("java.class.path"));
//=====================================================
E:\developTools\Java\jdk1.8.0_73\jre\lib\ext;C:\WINDOWS\Sun\Java\lib\ext
E:\developTools\Java\jdk1.8.0_73\jre\lib\charsets.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\deploy.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\ext\access-bridge-64.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\ext\cldrdata.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\ext\dnsns.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\ext\jaccess.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\ext\jfxrt.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\ext\localedata.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\ext\nashorn.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\ext\sunec.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\ext\sunjce_provider.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\ext\sunmscapi.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\ext\sunpkcs11.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\ext\zipfs.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\javaws.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\jce.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\jfr.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\jfxswt.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\jsse.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\management-agent.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\plugin.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\resources.jar;E:\developTools\Java\jdk1.8.0_73\jre\lib\rt.jar;F:\java开发\workspace\net-code\target\classes;C:\Users\Administrator\.m2\repository\org\projectlombok\lombok\1.18.24\lombok-1.18.24.jar;F:\developTool\IntelliJ IDEA 2020.2.1\lib\idea_rt.jar

常用方法

Class stu = Class.forName("com.karotte.demo.Student");
System.out.println(stu.getName());//含包名类名
System.out.println(stu.getSimpleName());//类名

System.out.println(stu.getFields());//非私有成员
System.out.println(stu.getDeclaredFields());//所有成员

System.out.println(stu.getMethod("study", null));//不含参数方法
System.out.println(stu.getMethod("friend", Student.class));//含参数方法
System.out.println(stu.getMethods());//所有非私有方法
System.out.println(stu.getDeclaredMethods());//所有方法

System.out.println(stu.getDeclaredConstructors());//获取所有构造器
System.out.println(stu.getDeclaredConstructor(int.class, String.class));//获取有参构造器
Constructor<Student> s = stu.getDeclaredConstructor(int.class, String.class);
Student o = s.newInstance(2,"喔");
System.out.println(o);
Method friend = stu.getDeclaredMethod("friend", Student.class);
friend.setAccessible(true);//关闭检测,同时可以提高访问效率
friend.invoke(o,new Student(5,"金陵科技学院"));
//================================================================
com.karotte.demo.Student
Student
[Ljava.lang.reflect.Field;@677327b6
[Ljava.lang.reflect.Field;@14ae5a5
public void com.karotte.demo.Student.study()
private void com.karotte.demo.Student.friend(com.karotte.demo.Student)
[Ljava.lang.reflect.Method;@7f31245a
[Ljava.lang.reflect.Method;@6d6f6e28
[Ljava.lang.reflect.Constructor;@135fbaa4
public com.karotte.demo.Student(int,java.lang.String)
[Ljava.lang.annotation.Annotation;@330bedb4
Student(id=2, name=喔)
社交金陵科技学院

反射操作泛型

  • java采用泛型擦除机制来引入泛型,java中的泛型仅仅是给编译器使用的,确保数据的安全性和免去强制类型转换问题,但是一旦编译完成,所有与泛型有关的类型全部擦除。
  • 为了通过反射操作这些类型,java新增ParameterizedType、GenericArrayType、TypeVariable、WildcardType几种类型来代表不能被归一到Class类中的类型但是又和原始类型齐名的类型
    • ParameterizedType:表示一种参数化类型,比如Collection
    • GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型
    • TypeVariable:各种类型变量的公共父接口
    • WildcardType:代表一种通配符类型表达式
public class FanTest {
    public void test01(Map<String,Student> map){
        System.out.println("test01");
    }
    public static void main(String[] args) throws Exception {
        Class<FanTest> fanTestClass = FanTest.class;
        Method method = fanTestClass.getMethod("test01", Map.class);
        Type[] genericParameterTypes = method.getGenericParameterTypes();
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println(genericParameterType);
            if(genericParameterType instanceof ParameterizedType){
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }
    }
}
//===============================================
java.util.Map<java.lang.String, com.karotte.demo.Student>
class java.lang.String
class com.karotte.demo.Student