Java注解和反射 --狂神说笔记4(待更新)

Java注解和反射 --狂神说笔记4

反射

java.Reflection

反射的用途:

  1. 反编译:.class-->.java

  2. 通过反射机制访问java对象的属性,方法,构造方法等

  3. 当我们在使用IDE,比如Ecplise时,当我们输入一个对象或者类,并想调用他的属性和方法是,一按点号,编译器就会自动列出他的属性或者方法,这里就是用到反射。

  4. 反射最重要的用途就是开发各种通用框架。比如很多框架(Spring)都是配置化的(比如通过XML文件配置Bean),为了保证框架的通用性,他们可能需要根据配置文件加载不同的类或者对象,调用不同的方法,这个时候就必须使用到反射了,运行时动态加载需要的加载的对象。

反射常用类:

  1. Java.lang.Class
  2. Java.lang.reflect.Constructor
  3. Java.lang.reflect.Field
  4. Java.lang.reflect.Method
  5. Java.lang.reflect.Modifier

反射基本使用:

  1. 获取Class(三种方法)

    //第一种获取Class方法,通过类实例的getClass()方法
    		Student stu1 = new Student();
    		Class c1 = stu1.getClass();
    		System.out.println(c1.getName());
    		//第二种获取Class方法,类名.class
    		Class c2 = Student.class;
    		System.out.println(c2.getName());
    		//第三种获取Class方法,
    		try {
    			Class c3 = Class.forName("com.zhong.reflection.Student");
    			System.out.println(c3.getName());
    		} catch (ClassNotFoundException e) {
    			e.printStackTrace();
    		}
    
  2. 判断是否为某个类的实例

    ​ isInstance():检查指定的对象是否兼容分配给该Class的实例。如果指定对象为非null,并且可以强制转换为此类的实例,则该方法返回true。否则返回false。

    //父类
    Person p1 = new Person();
    Class pc1 = Person.class;
    //子类
    Student stu1 = new Student();
    Class stuc1 = Student.class;
    //Person的子类,Student的兄弟类
    Teacher t1 = new Teacher();
    Class tp1 = Teacher.class;
    
    //输出true p1是Person类的实例,当然可以强制转化为Person类,因此输出结果true
    System.out.println(pc1.isInstance(p1));
    //输出true stu1是Person子类Student的实例,子类可以强制转化为父类,因此输出结果也是true
    System.out.println(pc1.isInstance(stu1));
    //输出false 父类不能强制转化为子类,因此输出结果是false
    System.out.println(stuc1.isInstance(p1));
    //输出true stu1是Student类的实例,当然可以转化为Studet类,因此输出结果是true
    System.out.println(stuc1.isInstance(stu1));
    //输出false Teacher类和Student类不能互相转化,因此输出false
    System.out.println(tp1.isInstance(stu1));
    
  3. 创建实例:通过反射来生成对象

    //使用Class对象的newInstance()方法创建Class对象对应类的实例
    Class<?> c = String.class;
    Object str = c.newInstance();
    		
    //先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建对象,这种方法可以用指定的构造器构造类的实例
    //通过Class对象获取指定的Constructor对象,
    Constructor constructor = c.getConstructor(String.class);
    //根据构造器构建实例(可以有构造参数,上面那种不可以)
    Object obj = constructor.newInstance("hello word");
    System.out.println(obj);
    
  4. 通过反射获取构造方法并使用

    Class clazz = Class.forName("com.zhong.reflection.Student");
    
    //		获取到所有公有构造方法
    Constructor[] conArray = clazz.getConstructors();
    for (Constructor c : conArray){
        System.out.println(c);
    }
    System.out.println("------------------------------");
    //		获取所有构造方法(包括:私有、受保护、默认、公有)
    conArray = clazz.getDeclaredConstructors();
    for (Constructor c : conArray){
        System.out.println(c);
    }
    System.out.println("------------------------------");
    //		获取公有、无参方法  这里需要的是一个参数的类型
    Constructor constructor = clazz.getConstructor(char.class);
    System.out.println("con = "+ constructor);
    System.out.println("------------------------------");
    constructor = clazz.getDeclaredConstructor(int.class);
    System.out.println(constructor);
    constructor.setAccessible(true);	//暴力访问,忽略访问修饰符
    Object obj = constructor.newInstance(123);
    
  5. 获取成员变量并调用

  6. 获取成员方法并调用

  7. 反射main方法

  8. 利用反射创建数值

  9. 通过反射运行配置文件内容

  10. 通过反射越过泛型检查

参考博客:https://blog.csdn.net/a745233700/article/details/82893076

(博客里有具体代码)

Java反射机制概述

  • 动态语言VS静态语言
    • 动态语言:类在运行时可以改变其结构的语言 eg:Python,js,PHP
    • 静态语言:运行时结构不可改变的语言 eg:Java,C,C++
    • Java具有一定的动态性
  • Java Reflection
    • Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API去的任何内部类的内部信息,并能直接操作任意对象的内部属性和方法
      • Class c = Class.forName("java.lang.String")
    • 加载完类之后,在堆内存的方法区中就产生了一个Class对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,反射出类的结构。
      • 正常方式:引入需要的“包类”名称->通过new实例化->取得实例化对象
      • 反射方式:实例化对象->getClass()方法->得到完整的“包类”名称
    • 反射相关的API
      • java.lang.Class:代表一个类
      • java.lang.reflect.Method:代表类的方法
      • java.lang.reflect.Field:代表类的成员变量
      • java.lang.reflect.Constructor:代表类的构造器

理解Class类并获取Class实例

//什么叫反射
public class Test02 {
	public static void main(String[] args) throws ClassNotFoundException {
//		通过反射获取类的Class对象
		Class c1 =  Class.forName("com.zhong.reflection.User");
		System.out.println(c1.getName());

//		一个类在内存中只有一个Class对象
//		一个类被加载后,类的整个结构都被封装在Class对象中
		Class c2 =  Class.forName("com.zhong.reflection.User");
		Class c3 =  Class.forName("com.zhong.reflection.User");
		Class c4 =  Class.forName("com.zhong.reflection.User");
		System.out.println(c2.getName());
		System.out.println(c3.getName());
		System.out.println(c4.getName());


	}

在Object类中定义了getClass()方法,将被所有子类继承。该方法返回值类型是一个Class类,此类是Java反射的源头,实际上所谓反射就是:可以通过对象反射求出类的名称。

  • Class本身也是一个类

  • Class对象只能由系统创建对象

  • 一个加载的类在JVM中只会有一个Class实例

  • 一个Class对象对应的是一个加载到JVM中的一个.class文件

  • 每个类的实例都会记得自己是由哪个Class实例所生成

  • 通过Class可以完整的得到一个类中的所有被加载的结构

  • Class类是Reflection的根源,针对任何你想动态加载,运行的类,唯有先获得相应的Class对象

  • Class类的获取:

    1. 已知具体类,通过类的class属性获取
    2. 已知某个类的实例,调用该实例的getClass()方法获取Class对象
    3. 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException()
    4. 内置基本类型可以直接用类名.Type
    5. 还可以利用ClassLoader
    public static void main(String[] args) throws ClassNotFoundException {
    		Person person = new Student();
    		System.out.println("这个人是"+person.name);
    
    //		方式一 通过对象获得
    		Class c1 =  person.getClass();
    		System.out.println(c1.hashCode());
    //		方式二 通过forName获得
    		Class c2 =  Class.forName("com.zhong.reflection.Student");
    		System.out.println(c2.hashCode());
    //		方式三 类名.class
    		Class c3 = Student.class;
    		System.out.println(c3.hashCode());
    //		方式四 基本内置类型的包装类都有一个Type属性
    		Class c4 = Integer.TYPE;
    		System.out.println(c4.hashCode());
    //		获得父类类型
    		Class c5 = c1.getSuperclass();
    		System.out.println(c5);
    }
    

    哪些类型可以有Class对象?

    • class
    • interface:接口
    • []:数组
    • enum:枚举
    • annotation:注解
    • primitive type:基本数据类型
    • void

类的加载与ClassLoader

  • 加载
    • 将class文件加载到内存,生成代表这个类的Class对象
  • 链接
    • 将Java类的二进制代码合并到JVM的运行状态中的过程
  • 初始化
    • 执行类构造器

分析类初始化:

  • 类的主动引用(一定会初始化)

    • 虚拟机启动,先初始化main方法所在的类
    • new一个类的对象
    • 调用类的静态成员(final常亮除外)和静态方法
    • 使用java.lang.reflect包的方法对类进行反射调用
    • 当初始化一个类,如果其父类没有被初始化,则会先初始化它的父类
  • 类的被动引用(不会发生初始化)

    • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。eg:通过子类引用父类的静态变量,不会导致子类初始化,
    • 通过 数组定义类引用,不会触发此类的初始化
    • 引用常量不会触发此类的初始化。
  • 类加载器的作用

    • 类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class文件,作为方法区中类数据的访问入口。
    • 类缓存:标准的JacaSE类加载器可以按照要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。

创建运行时类的对象

获取运行时类的完整结构

调用运行时类的指定结构

posted @ 2022-01-17 19:28  t0p1Crayon  阅读(68)  评论(0编辑  收藏  举报