https://www.bilibili.com/video/BV1Kb411W75N?p=636
一:java 反射 机制概述
1.反射(Reflecttion):
反射 被视为 动态语言 的关键,反射机制 允许程序 在执行期,借助于 Reflection API,
取得任何 类的 内部信息,并能直接操作 任意对象的 内部属性及方法。
2.反射:
加载完类之后,在堆内存的方法区中,就产生一个 class 类型的对象,这个 class 对象,
包含了 类的完整信息,可以通过这个 class 对象,看到类的结构。
3.反射相关主要 API:
Method:方法
Field:代表类的成员变量
Constructor:代表类的构造器
面试:
1)通过new方式 或者 反射方式都可以调用公共类结构,开发中到底用哪个?
建议:用 new
动态的创建,会用到反射
2)反射机制 与 面向对象中的封装性,是不是矛盾的?如何看待两个技术?
不矛盾,封装性:不建议,也不能够,调用私有的属性和方法。
反射:虽然不建议,但是可以调用的。
二:理解 Class 类,并 获取 Class 实例
1.理解 Class 类
1)类加载过程:
程序经过 javac 命令后,会生成一个字节码文件(.class),结尾,
接着使用 java 对字节码文件,进行解释运行。
就相当于 把字节码对象加载到内存中。此过程,称为类的加载。
加载到内存中的类,我们就称为运行时类,此运行时类,就称为 一个 Class 实例。
2.Class 实例的获取:
注意:
Class<? extends Person> b
Class 类,加上泛型限制,避免了 之后在使用(newInstance),再去强转。
1)方式一:
Class<Person> a = Person.class;
2)方式二:
Person p1 = new Person();
Class<? extends Person> b = p1.getClass();
3)方式三:
Class<?> c = forName("com.classloader.Son");
4)方式四:(了解,使用类加载器)
//获取 ClassLoader 类加载器
ClassLoader classLoader = Test1.class.getClassLoader();
Class<?> d = classLoader.loadClass("com.classloader.Son");
注意:a,b,c,d 指向同一个对象。
3.哪些类型 可以有 Class 对象
1)class:类(外部类,内部类)
2)interface:接口
3)[]:数组
4)enum:枚举
5)annotation:注解@interface
6)基本数据类型
7)void
4.只要 数组的 元素类型 与 维度 一样,就是同一个 class
元素类型:int,String,Byte。
维度:一维数组,二维数组。
三:类的加载 与 ClassLoader 的理解
1.类的加载过程:
1)类的加载(Load):
java 命令,将 class 文件,加载到内存,并将这些静态数据,
转换成方法区的,运行时数据结构。并为之创建一个 Class 实例。
此过程由类加载器完成。
2)类的链接(Link):
将 Java 类的二进制代码,合并到 JVM 的运行状态之中的过程。
验证:确保加载类信息符合 JVM 规范
准备:为 类变量(static),分配内存,并设置类变量默认初始值,在方法去中分配。
解析:虚拟机常量池内 的符号引用(常量名),替换为直接引用(地址)的过程。
3)类的初始化(Intialize):
2.类加载顺序:
1)先为 父类 类变量(static),分配空间,然后赋值。
2)再为 子类 类变量(static),分配空间,然后赋值。
3)开始创建对象,子类构造方法,为 子类 和 父类 本地变量分配内存地址,
4)父类本地变量先赋值,在执行父类构造方法。
5)子类本地变量先赋值,在执行子类构造方法。
6)创建子类对象完毕。
7)虚拟机会保证,类的初始化,在多线程环境下,被正确 加锁 和 同步
3.类加载器(ClassLoader)的理解
1)ClassLoader:类加载器的作用,是把类(class文件),加载到内存中,
2)读取配置文件,可以使用流,也可以使用 类加载器 读取。
示例:
ClassLoader classLoader = ClassLoader_01.class.getClassLoader();
InputStream ras = classLoader.getResourceAsStream("a.properties");
--目录默认为 :src 下
相当于:
FileInputStream fileInputStream1 = new FileInputStream(new File(""));
四:创建 运行时 类的对象
1.根据字节码对象,创建对象
Person person = Person.class.newInstance();
2.本质:
本质还是调用 Person 类的无参构造 方法。
如需要创建成功:
1)对象有无参构造方法
2)无参构造方法,权限够。
3.练习:写一个方法,传入字节码对象,创建对应的对象实例并返回。
五:获取 运行时 类的完整结构(获取私有的:Declared)
1.获取类 结构信息
1).获取所有属性:(包含父类)
Field[] fields = Person.class.getFields();
2).获取所有方法:(包含父类)
Method[] methods = Person.class.getMethods();
3).获取所有构造方法:(包含父类)
Constructor<?>[] constructors = Person.class.getConstructors();
2.所获得都是数组,可以使用 增强循环 遍历结果。
每遍历一次,获得一个实例对象,在其中也可以获取信息。
3.获取 Person 类,父类字节码对象
Class<? super Person> superclass = Person.class.getSuperclass();
Type genericSuperclass = Person.class.getGenericSuperclass();
4.获取 类 的 接口,所在包,注解等。
六:调用 运行时 类的指定结构
1.调用属性:
Person person = Person.class.newInstance();
Field age = Person.class.getDeclaredField("age");
//为属性赋值
age.set(person, 12);
//获取属性值
Object o = age.get(person);
2.调用方法:
1.普通方法:
Class<Person> p = Person.class;
Person person = p.newInstance();
//获取指定方法 -- 方法名 参数类型
Method method = p.getDeclaredMethod( "setName" , String.class );
//调用指定方法: --person 调用对象。传入参数
//invoke() 返回值就是,调用方法返回值。
Object invoke = method.invoke( person , "123" );
System.out.println( person.toString() );
2.静态方法:
Object o = method.invoke( Person.class , String.class );
3.调用构造方法:
Constructor<Person> dc =
Person.class.getDeclaredConstructor( String.class , Integer.class );
Person zhangsan = dc.newInstance( "zhangsan" , 12 );
4.调用时,碰见私有方法
clazz.setAccessible(true);
七:反射的应用:动态代理
1. 静态代理:
1)实现接口的静态代理,在调用方法前后,加上增强功能。
2)特点:代理类 和 被代理类,在编译期间,就被确定下来。
2.动态代理举例:
1)用 反射 实现动态代理。
2)
3.反射的应用:AOP
4.