[Java 09] 注解与反射 2021.11.14 天啊一个坑一个坑的

2021/11/14 9:10:04

本文主要是介绍[Java 09] 注解与反射 2021.11.14 天啊一个坑一个坑的,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

JAVA注解与反射


1. Java. Annotation

  • 入门Java. Annotation

    annotation的作用:1. 对程序做出解释;2. 可以被其他程序读取

    Annotation格式:@注解名,也可以带参数:@SuppressWarnings(value = "unchecked")

    Annotation 在package, class, method, field上附加,相当于添加辅助信息,可以通过反射机制对元数据访问

  • 内置注解(@Override;@Deprecated; @SuppressWarnings 镇压警告:eg:@SuppressWarnings("all"),镇压所有警告,不报任何警告提示。

  • 元注解

    注解其他注解的注解,有四个标准的meta-annotation类型:

    @Target:用于描述注解的使用范围

    @Retention: 表示需要在什么级别保存该注解信息,用于描述注解的生命周期(SOURCE<CLASS<RUNTIME)最常用的就是RUNTIME,表示在运行时注解依然可用

    @Documented: 说明该注解将被包含在javadoc中

    @Inherited: 说明子类可以继承父类中的该注解

  • 自定义注解(通常Target和Retention是必须的)

    格式:@interface 注解名{定义内容}

    定义内容:参数类型 参数名(), eg: String value(); 可以通过default声明参数默认值。

    (这里可以理解为:注解时一个接口,参数是由函数返回的,所以以函数的形式声明参数)

    注解元素必须要有值,定义注解元素时,长使用空字符串,0,作为默认值

    @Target(value = ElementType.METHOD)//可以自定义一些元注解,注解使用范围,不会就看源码
    @interface MyAnnotation{
        int age() default 0;
        String name() default "";
    }
    

2. Java. Reflection

  • 语言类型:

    动态语言:运行时可以改变自身结构的语言,eg:python,JS。。。

    静态语言: 运行时结构不变,Java是准动态,反射机制可以使Java从静态变为准动态

  • 反射机制允许程序在执行期间借助reflectionAPI取得任何类的内部信息,并能直接操作任意对象的内部属性和方法。(接口,字段,方法,属性等)

  • 正常:引入需要的“包类”名称----> new实例化 ----> 得到实例化对象

    反射:实例化对象 ---> getClass()----> 得到完整的“包类”名称

  • Java反射机制提供的功能:

    运行时,判断任意一个对象所属的类,构造任意一个类的对象,获取泛型信息,处理注解,生成动态代理

  • 优缺点:1. 实现动态创建对象和编译,很灵活 2. 对性能有影响,是一种解释操作

  • 相关的API:

    java.lang.Class: 代表一个类 (大写!C)

    java.lang.reflect.Method/Field/Constructor代表类的方法,成员变量,构造器

3. 获取Class类

  1. 创建一个实体类(只有属性的类,其中的方法都是继承父类Object的)

    class User{
        int age;
        int String;
        public User() {
        } 
        
    //打印Class类的hashcode():是一个static方法
    sout(c1.hashCode());
    

获得Class类的方法:

//1. 已知类,通过类的class属性获得
Class c1 = User.class;
//2. 实例对象方法getClass()
Class c2 = user.getClass();
//3. 已知某个类的路径和全名(参数是包路径.类名, 为字符串类型)
Class c3 = Class.forName("path.User"); 
//4. 基本内置类(JDK定义的类,都有一个TYPE属性)(自定义的没有)(primitive type:基本数据类型)
Class c4 = Integer.TYPE;

4. 所有类型的Class类(有Class类的类型)

Class c1 = Object.class;//类
Class c2 = Comparable.class;//接口
Class c3 = String[].class;//数组
Class c4 = Override.class;//注解
Class c5 = ElementType.class;//枚举类
Class c6 = Integer.class;//基本数据类型
Class c7 = void.class;//void类型

5. 类加载内存分析

1. 类加载过程:

  1. 类的加载Load:将class文件加载到内存,把这些静态数据转换成方法区的运行时数据结构中,堆中创建Class对象

  2. 类的连接Link:将类的二进制数据合并到JRE中,(验证,准备,解析)

    验证:检查是否符合JVM规范

    准备:正式为类变量(static)分配内存,设置默认初始值(0,null), 这些内存都在方法区中分配

    解析:JVM常量池内的符号引用(常量名)替换为直接引用(地址)的过程。(自己定义的类名是引用,去找类地址

  3. 类的初始化Initiate:

    JVM调用()方法:JVM执行类构造器,把类变量和静态代码块(都是static的)合并的过程

    如果初始化一个类,发现父类没有初始化,则先出发父类初始化

    JVM会保证此方法在多线程中正确加锁和同步

Eg:

public class Test02 {
    public static void main(String[] args) {
        A a = new A();
        System.out.println(A.m);
    }
}
class A{
    static {
        System.out.println("A类静态代码块初始化");
        m = 300;
    }
    static int m = 100;

    public A() {
        System.out.println("A类的无参构造初始化");
    }
}

2. 内存分析:(栈,堆,方法区)

  1. Load:

    @方法区:
    在方法区中加载
        Test02类的数据:
        //静态变量
        //静态方法
        //常量池
        //代码
        A类的数据:
        //同上
    
    @堆:
        生成Class类对象
        java.lang.Class 代表Test02类
        java.lang.Class 代表A类
    
  2. Link

    1. JVM检查

    2. 准备:

      @栈
          开始执行Main()
          m = 0//初始化值都是0
      
    3. 解析:

      @堆:
          new A(),找到A类的Class对象,然后链接到方法区中的数据
      
  3. Initiate

    调用方法

    @栈:
        m =100//这里合并A类中的静态变量和静态方法(哪个在前先执行哪个),所以m 先=300, 后又=100
    

总结:

  1. 加载到内存,产生对于的Class类
  2. 链接,静态变量初始值为0,m = 0
  3. 初始化:(){静态代码块执行}

6. 类初始化

  • 主动引用:(一定会发生初始化)

    虚拟机启动时,初始化main方法所在的类;

    new一个类时

    调用类的static成员或方法时

    使用java.lang.reflect包方法对类进行反射调用时

    加载类发现父类未初始化时,去初始化父类

    (验证时,写sout在静态代码块中)

  • 类的被动引用(不会发生类的初始化)

    访问static域时,只有真正声明这个域的类才会被初始化。通过子类引用父类的静态变量,只有父类初始化

    通过数组定义类引用,不会触发此类的初始化(son[] array = new Son[5];只对空间命名,不初始化类)即内存分析中,只调用了Load和Link,不调用

    引用常量不会出发此类的初始化(常量在链接时就存入类的常量池中了)(同上)

7. 类加载器

1. 类加载器

Java文件java编译器编译为.class文件,通过类装载器、字节码校验器、解释器,给操作系统

类加载器:把class文件字节码加载到内存中,方法区产生运行时数据结构,堆中产生Class类对象。

类缓存:类加载器可以按照要求查找类,一旦某个类被加载到类加载器中,将维持加载一段时间,然后被gc回收。


类加载器的作用是把类装载进内存,缓存是为了提高效率

2. Java的类加载器有

引导类加载器 Bootstrap Classloader:JVM自带加载器,负责Java平台核心库,用来装载核心类库(rt.jar)

扩展类加载器 Extension Classloader :负责 jre/lib/ext 目录下的jar包加载

系统类加载器 System Classloader :负责项目中的类包加载工作,是最常用的加载器

//获取系统类加载器
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
System.out.println(classLoader);
//获取系统类加载器的父类加载器,扩展类加载器
ClassLoader extendLoader = classLoader.getParent();
System.out.println(extendLoader);
//获取扩展类加载器的父类加载器,引导类加载器(返回值为null,因为是c++写的,)
ClassLoader bootstrapLoader = extendLoader.getParent();
System.out.println(bootstrapLoader);

//sout:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@14ae5a5
null
//获取自己写的类的类加载器:返回依然是系统类加载器,也叫做APPLoader/BootstrapLoader
ClassLoader myClassLoader = Class.forName("AnnotationReflection.Test02").getClassLoader();
System.out.println(myClassLoader);
//返回AppLoader

3. ClassLoader的方法

获取运行时类的完整结构, 包括:

Field, Method, Constructor, SuperClass, Interface,Annotation

class Test03{
    private int age;
    private String name;
    public Test03(){}
}
Class c1 = Class.forName("packetname.Test03");
c1.getName();//返回 包名.类名
c1.getSimpleName();//返回 类名
c1.getDeclaredField();//返回全部属性,没有declared,只返回public的属性
//方法中,打印出的时方法的声明,: 修饰符 返回类型 方法名(参数) 抛出的异常, 都有
c1.getMethod();//如果带参数,则为:String.class,参数也必须时Class类
c1.取得构造器、修饰符等等。。。。。
Constructor constructor = c2.getConstructor(String.class, int.class, int.class);
sout(constructor);

8. 动态创建对象 执行方法

package AnnotationReflection;
class User{
    private  int age;
    private String name;
	//constructors
    public User() {
    }
    public User(int age, String name) {
        this.age = age;
        this.name = name;
    }
    @Override
    public String toString() {
        return "User{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
    //set and get
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

1. 通过反射创建对象

package AnnotationReflection;
public class Test03 {
 public static void main(String[] args) throws Exception {
     Class c1 = Class.forName("AnnotationReflection.User");
     Constructor constructor = c1.getConstructor(int.class, String.class);
     User user = (User) constructor.newInstance(18, "Roy");
     System.out.println(user);
 }
}  

2. 通过反射调用方法,设置属性

invoke():激活 使用方法: XXMethod.invoke(对象名, 传入参数);

package AnnotationReflection;
public class Test03 {
    public static void main(String[] args) throws Exception {
        //创建对象
        Class c1 = Class.forName("AnnotationReflection.User");
        Constructor constructor = c1.getConstructor();//调用无参构造
        User user = (User) constructor.newInstance();
        System.out.println(user);
        //反射调用方法
        Method setName = c1.getDeclaredMethod("setName", String.class);
        setName.invoke(user, "Roy");//方法.激活(给对象,传递的值)
        System.out.println(user.getName());
        //反射设置属性
        Field age = c1.getDeclaredField("age");
        age.setAccessible(true);//允许访问私有属性
        age.set(user, 18);//和invoke一样
        System.out.println(user.getAge());
    }
} 

9. 反射操作泛型

Java采用泛型擦除机制引入泛型,泛型只是给编译器javac使用的,确保安全免去强制类型转换的问题,一旦编译后,和泛型有关的类型全部擦除。

为了通过反射操作这些类型,java用集中类型来代表不能被归一到Class类中,但又和原始类型齐名的类型:

ParameterizedType: 表示一种参数化的类型:Collection比如用于限制字典中key和value的泛型的数组, 看单词理解啊

GenericArrayType:表示一种元素类型是参数化类型或者类型变量的数组类型。比如限制了key和value类型的字典

TypeVariable:是各种类型变量的公共父接口

WildcardType:代表一种通配符类型表达式

ParameterizedType 是 GenericArrayType的父类

public class Test04 {
    public void method01(Map<String, Integer> map, List<String> list){}//方法一
    public List<String> method02(){
        return null;
    }

    public static void main(String[] args) throws Exception{
        Class c1 = Test04.class;
        Method method01 = c1.getMethod("method01", Map.class, List.class);
        Type[] genericParameterTypes = method01.getGenericParameterTypes();
        //打印出genericParamater的Type数组
        for (Type genericParameterType : genericParameterTypes) {
            System.out.println(genericParameterType);
        }
        //打印出每一个限定参数化类型的参数
        for (Type genericParameterType : genericParameterTypes) {
            if(genericParameterType instanceof ParameterizedType){//如果ge是P的子类,强制转换并用getATA方法获取数组
                Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();
                for (Type actualTypeArgument : actualTypeArguments) {
                    System.out.println(actualTypeArgument);
                }
            }
        }
    }
}
//打印结果:
java.util.Map<java.lang.String, java.lang.Integer>
java.util.List<java.lang.String>
class java.lang.String
class java.lang.Integer
class java.lang.String

10. 反射操作注解

ORM: Object relationship Mapping 对象关系映射:一个类正好是数据库的一张表

即: 类和表结构对应, 属性和字段对应,对象和记录对应(对象和行对应)

反射操作注解例子:利用注解和反射完成类和表结构的映射关系

获取类的注解:

public class Test05 {
    public static void main(String[] args) throws Exception{
        //1. 通过反射,获得类注解(总体的)
        Class c1 = Class.forName("AnnotationReflection.Student");
        Annotation[] annotations = c1.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }
        //2. 强制转换为自定义注解类型(从Annotation接口转变为TableStudent接口),获得value值
        TableStudent tableStudent = (TableStudent) annotations[0];
        System.out.println(tableStudent.value());
        //or
        TableStudent tableStudent1 = (TableStudent)c1.getAnnotation(TableStudent.class);
        System.out.println(tableStudent1.value());

        //3. 获得属性注解的值
        FieldStudent fieldStudent = (FieldStudent)c1.getDeclaredField("age").getAnnotation(FieldStudent.class);
        System.out.println(fieldStudent.columnName());
        System.out.println(fieldStudent.type());
        System.out.println(fieldStudent.length());
    }
}
@TableStudent("db_Student")
class Student{
    @FieldStudent(columnName = "SID",type = "int", length = 10)
    private int ID;
    @FieldStudent(columnName = "AGE", type = "int", length = 10)
    private int age;
    @FieldStudent(columnName = "NAME", type = "varchar", length = 3)
    private String name;

    public Student() {
    }
    public Student(int ID, int age, String name) {
        this.ID = ID;
        this.age = age;
        this.name = name;
    }
// set and get
    @Override
    public String toString() {return "Student{" +"ID=" + ID +", age=" + age +", name='" + name + '\'' +'}';}
}

//类名的注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface TableStudent{
    String value();
}
//属性的注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldStudent{
    String columnName();
    String type();
    int length();
}


这篇关于[Java 09] 注解与反射 2021.11.14 天啊一个坑一个坑的的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程