反射
反射的概念
- 反射机制允许程序在执行期借助于ReflectionAPI取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到
- 加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为反射
反射机制
Java反射机制可以完成
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时得到任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
反射相关的主要类:
- java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
- java.lang.reflect.Method: 代表类的方法
- java.lang.reflect.Field: 代表类的成员变量
- java.lang.reflect.Constructor: 代表类的构造方法
一般使用:对象.方法 反射使用:方法.对象
一般使用:对象.变量 反射使用:变量.对象
反射优点和缺点
**优点:**可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑。
**缺点:**使用反射基本是解释执行,对执行速度有影响,
反射调用优化-关闭访问检查
Method和Field、Constructor对象都有setAccessible()方法
setAccessible作用是启动和禁用访问安全检查的开关。(默认为false)
- 参数值为true表示:反射的对象在使用时取消访问检查,提高反射的效率。
- 参数值为false表示:反射的对象执行访问检查
Class类
- Class也是类,因此也继承Object类
- Class类对象不是new出来的,而是系统创建的
- 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次(以hashcode区别)(使用synchronized同步)
- 每个类的实例都会记得自己是由哪个 Class 实例所生成
- 通过Class对象可以完整地得到一个类的完整结构,通过一系列API
- Class对象是存放在堆的
- 类的字节码二进制数据,是放在方法区的, 有的地方称为类的元数据(包括 方法代码变量名,方法名,访问权限等等)https://www.zhihu.com/question/38496907
Class类的常用方法:
java.lang.Class
- getName:获取全类名
- getSimpleName:获取简单类名
- getFields:获取所有public修饰的属性,包含本类以及父类的
- getDeclaredFields:获取本类中所有属性
- getMethods:获取所有public修饰的方法,包含本类以及父类的
- getDeclaredMethods:获取本类中所有方法
- getConstructors: 获取本类所有public修饰的构造器
- getDeclaredConstructors:获取本类中所有构造器
- getPackage:以Package形式返回 包信息
- getsuperClass:以Class形式返回父类信息
- getlnterfaces:以Class[]形式返回接口信息
- getAnnotations:以Annotation[] 形式返回注解信息
获取Class对象的几种方式
- 代码阶段:Class.forName()
前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException,
例:Class clazz = Class.forName( "java.lang.Cat”);
应用场景:多用于配置文件,读取类全路径,加载类
- Class类阶段:类.class
前提:若已知具体的类,通过类的class 获取,该方式最为安全可靠,程序性能最高
应用场景:多用于参数传递,比如通过反射得到对应构造器对象
- Runtime阶段:对象.getClass()
前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象,
例:Class clazz = object.qetClass();
应用场景:通过创建好的对象,获取Class对象
- 其他方式(类加载器):
ClassLoader classLoader = object.getClass().getClassLoader();
Class clazz = classLoader.loadClass("类的全类名");
- 基本数据(int, char,boolean,float,double,byte,long,short) 按如下方式得到Class类对象
Class clazz = 基本数据类型.class;
- 基本数据类型对应的包装类,可以通过.type 得到Class类对象
Class clazz = 包装类.TYPE;
以下类型有Class对象
- 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
- interface:接口
- 数组
- enum:枚举
- annotation:注解
- 最本基本数据类型
- void
类加载
基本说明
反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。
- 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
- 动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性
类加载时机
- 创建类的实例:属于静态加载。当使用 new 关键字创建一个类的实例时,该类会被加载、连接和初始化,这是在编译期就确定需要加载的类。
- 访问类的静态变量:属于静态加载。当访问一个类的静态变量(static field)时,如果该类还没有加载,则会触发类的加载、连接和初始化,这也是在编译期就确定需要加载的类。
- 调用类的静态方法:属于静态加载。当调用一个类的静态方法时,如果该类还没有加载,则会触发类的加载、连接和初始化,同样是在编译期确定需要加载的类。
- 访问类的静态字段的值:属于静态加载。当访问一个类的静态字段的值(static final field)时,不会触发类的初始化,因为这些字段在编译期就会被赋值。
- 使用反射:属于动态加载。通过反射机制来创建类的实例、访问类的静态变量或调用类的静态方法时,是在程序运行时根据需要动态加载类。
类加载过程
类加载各阶段完成任务
前两个阶段在JVM中
加载阶段(Loading)
加载阶段是指查找并加载类的字节码文件(.class 文件),一般是通过类加载器(ClassLoader)来完成的。类加载器根据类的全限定名在类路径中查找对应的字节码文件,并将其加载到内存中。
链接阶段(Linking)-验证
验证确保被加载的类符合 Java 虚拟机规范,比如检查字节码的格式、语义等。
目的是为了确保 Class 文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
包括:文件格式验证(是否以魔数 oxçafebabe开头)、元数据验证、字节码验证和符号引用验证。
可以考虑使用 -Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间。
链接阶段(Linking)-准备
JVM 会在该阶段对静态变量,分配内存并默认初始化(对应数据类型的默认初始值如 0、0L、null、false等)。这些变量所使用的内存都将在方法区中进行分配
链接阶段(Linking)-解析
虚拟机将常量池内的符号引用转换为直接引用,比如将类、方法、字段等的符号引用解析为实际内存地址
初始化(Initialization)
到初始化阶段,才真正开始执行类中定义的 Java 程序代码,此阶段是执行()方法的过程。
()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并。类初始化是按需进行的,只有在首次主动使用类时才会触发。
虚拟机会保证一个类的()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行()方法完毕
通过反射获取类的结构
通过反射创建对象
- 方式一:调用类中的public修饰的无参构造器
- 方式二:调用类中的指定构造器
- Class类相关方法
- newlnstance:调用类中的无参构造器,获取对应类的对象
- getConstructor(Class…clazz):根据参数列表,获取对应的构造器对象
- getDecalaredConstructor(Class…clazz):根据参数列表,获取对应的构造器对象
- Constructor类相关方法
- setAccessible():参数使用true:暴破,使用反射可以访问私有(private)构造器/属性/方法
- newlnstance(Object…obj):调用构造器
package com.gg
class abc{puiblic String name ;abc(String name){this.name = name ;}
}
Class<?> clazz = Class.forName("com.gg.abc");
Object o = clazz.newInstance(); // 调用无参构造 o的运行对象类型为abc类Constructor<?> c = clazz.getDecalaredConstructor(String.class);//调用有参构造
Object o1 = c.newInstance("gggg"); // o1的运行对象类型为abc类
通过反射访问类中的成员
- 根据方法名和参数列表获取Method方法对象:Method m =clazz.getDeclaredMethod(方法名,XX.class);
- 获取对象:Object o = clazz.newInstance();
- 暴破:m.setAccessible(true);
- 访问:Object returnValue = m.invoke(o,实参列表); //o表示对象
- 注意:如果是静态方法,则invoke的参数o,可以写成null
- 如果方法有返回值,统一返回Object,但其运行类型依旧是方法中定义的返回类型
package com.gg
class abc{puiblic String name ;abc(String name){this.name = name ;}public void aabc(String name){System.out.println(name + " " + this.name);}
}
// 获取Class对象
Class<?> clazz = Class.forName("com.gg.abc");
//调用有参构造
Constructor<?> c = clazz.getDecalaredConstructor(String.class);
// o1的运行对象类型为abc类
Object o1 = c.newInstance("gggg");
// 获取aabc方法对象
Method m = clazz.getDeclaredMethod("aabc",String.class);
// 反射调用aabc方法
Object res = m.invoke(o1,"aaaa");
// 输出 "aaaa gggg"
通过反射访问类中的属性
- 根据属性名获取Field对象Field f = clazz.getDeclaredField(属性名);
- 暴破:
- f.setAccessible(true); // f 是Field
- 访问
- f.set(o,值);// o表示对象
- f.get(o); //o表示对象
- 注意:如果是静态属性,则set和get中的参数o,可以写成null
package com.gg
class abc{puiblic String name ;abc(String name){this.name = name ;}public void aabc(String name){System.out.println(name + " " + this.name);}
}
// 获取Class对象
Class<?> clazz = Class.forName("com.gg.abc");
//调用有参构造
Constructor<?> c = clazz.getDecalaredConstructor(String.class);
// o1的运行对象类型为abc类
Object o1 = c.newInstance("gggg"); //此时name="gggg"
// 获取name属性对象
Field f = clazz.getDeclaredField("name");
// 设置name属性的值
f.set(o1,"123");// 此时name="123"
// 获取并输出name属性值
System.out.println(f.get(o1));
// 输出 "123"
Field类常用方法
java.lang.reflect.Field
- getModifiers:以int形式返回修饰符、
- [说明:默认修饰符 是0, public 是1,private 是2,protected 是 4,static是8,final是16]
- public static = public(1) + static(8) = 1 + 8 = 9
- getType:以Class形式返回类型
- getName:返回属性名
Method类常用方法
java.lang.reflect.Method
- getModifiers:以int形式返回修饰符
- [说明:默认修饰符 是0, public 是1,private 是2,protected 是 4,static是8,final是16]
- getReturnType:以Class形式获取 返回类型
- getName:返回方法名
- getParameterTypes:以Class[ ]返回参数类型数组
Constructor类常用方法
java.lang.reflect.Constructor
- getModifiers:以int形式返回修饰符
- getName:返回构造器名(全类名)
- getParameterTypes:以Class[ ]返回参数类型数组