目录
一、反射机制
1、反射机制
反射机制是Java的动态机制,可以程序运行期间确定实例化对象,方法调用,属性操作等。
2、反射机制的原理
Java Reflection
1. 反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到
2. 加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射。
3、反射相关的主要类:
- java.lang.Class:代表一个类,Class对象表示某个类加载后在堆中的对象
- java.lang.reflect.Method:代表类的方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Constructor:代表类的构造方法
- java.lang.reflect.Array:创建和访问动态数组
4、反射优点和缺点
1.优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑。
2.缺点:使用反射基本是解释执行,对执行速度有影响。
二、反射的应用
0、准备条件--创建一个类
public class Cat {
private String name;
public int age;
public String sex;
//无参构造器
public Cat() {}
private Cat(String name){
this.name = name;
}
public Cat(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public void hi() {
System.out.println(name+": hi");
}
public void eat(String food) {
System.out.println(name+": 吃"+food);
}
public int sum(int x, int y) {
return x+y;
}
public void dosome(int x,float y, char z, boolean b, String... strs){
System.out.println(name+": "+x+" "+y+" "+z+" "+b);
System.out.println(name+": "+strs);
}
private void WC() {
System.out.println(name + ": 上厕所..");
}
@Override
public String toString() {
return "Cat{" +
"name='" + name + '\'' +
", age=" + age +
", sex='" + sex + '\'' +
'}';
}
}
1、反射:使用指定的构造器创建对象
forName() 获取Class对象 newInstance() 该方法会调用无参构造器创建对象 getConstructor() 只能获取公有构造器 getDeclaredConstructor() 获取指定的所有构造器(包括私有的) getDeclaredConstructors() 获取所有构造器,返回一个构造器列表 setAccessible() 设置访问安全检查
public class Reflection02 {
public static void main(String[] args) throws Exception {
String className = "reflection.question.Cat"; //Cat类的路径
//第一种方法 使用无参构造器 创建类对象
//1.加载类,返回class的类对象
Class cls = Class.forName(className);
//2.使用无参构造器创建对象
Object obj = cls.newInstance();
System.out.println(obj);
//第二种方法 使用指定的构造器创建对象
//1.加载类,返回class的类对象
Class cls02 = Class.forName(className);
//2.获取指定的构造器,不传递参数就是无参构造器
Constructor constructor = cls02.getConstructor(String.class,int.class,String.class);
//3.使用指定的构造器,实例化类对象
Object obj02 = constructor.newInstance("星猫",18,"男");
System.out.println(obj02);
//第三种方法 使用私有的构造器创建对象
//访问私有的构造器需要禁用访问安全检查
Class<?> cls03 = Class.forName(className);
Constructor<?> constructor03 = cls03.getDeclaredConstructor(String.class);
//setAccessible:作用是启动和禁用访问安全检查的开关
constructor03.setAccessible(true);
Object obj03 = constructor03.newInstance("上帝猫");
System.out.println(obj03);
//获取所有构造器,返回一个构造器列表
Class<?> cls04 = Class.forName(className);
Constructor<?>[] declaredConstructors = cls04.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
}
}
2、反射:操作类对象的属性对象
getField() 只能获取公共属性值 getFields() 获取全部的共有属性 getDeclaredField() 可以获取全部的属性(包括私有) getDeclaredFields() 获取本类中所有属性,包括私有属性 set(obj,值) 设置属性值 get(obj) 获取属性值 setAccessible() 设置访问安全检查
public class Reflection04 {
public static void main(String[] args) throws Exception {
String catName = "reflection.question.Cat";
String nameName = "name";
String ageName = "age";
String sexName = "sex";
Class cls = Class.forName(catName);
Constructor constructor = cls.getConstructor(String.class,int.class,String.class);
Object obj = constructor.newInstance("天使猫",776,"男");
//获取指定的共有属性 getField()
Field age = cls.getField(ageName);
Field sex = cls.getField(sexName);
System.out.println(age.get(obj) + " "+sex.get(obj));
//获取全部的共有属性 getFields()
Field[] fields = cls.getFields();
for (Field field : fields) {
System.out.println(field.getName()+ ": "+field.get(obj));
}
//getDeclaredField() 可以获取全部的属性(包括私有)
Field name = cls.getDeclaredField(nameName); //获取私有属性
name.setAccessible(true); //关闭访问安全检查
name.set(obj,"变坏的天使猫"); //设置私有属性的值
System.out.println(name.get(obj)); //获取私有属性的值
//getDeclaredFields() 获取本类中所有属性,包括私有属性
Field[] declaredFields = cls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
}
}
3、反射:操作类对象的方法对象
getMethod() 访问公有方法 getMethods() 访问所有公有方法 getDeclaredMethod() 可以调用类中的所有方法(包括私有方法) getDeclaredMethods() 可以调用类中的所有方法 setAccessible() 设置访问安全检查
public class Reflection03 {
public static void main(String[] args) throws Exception {
String catName = "reflection.question.Cat";
String hiMethod = "hi";
String eatMethod = "eat";
String wcMethod = "WC";
String sumMethod = "sum";
String dosomeMethod = "dosome";
//调用无参方法
Class cls = Class.forName(catName);
Constructor constructor = cls.getConstructor(String.class,int.class,String.class);
Object obj = constructor.newInstance("加菲猫",24,"男");
Method method = cls.getMethod(hiMethod);
method.invoke(obj);
//调用参数方法
Class cls02 = Class.forName(catName);
Constructor constructor02 = cls.getConstructor(String.class, int.class, String.class);
Object obj02 = constructor02.newInstance("海盗猫", 12, "男");
Method method02 = cls02.getMethod(eatMethod, String.class);
method02.invoke(obj02,"牛排");
//调用有参有返回值的方法
Class cls03 = Class.forName(catName);
Constructor constructor03 = cls03.getConstructor(String.class, int.class, String.class);
Object obj03 = constructor03.newInstance("算术猫", 76, "男");
Method method03 = cls.getMethod(sumMethod,int.class,int.class);
Object result = method03.invoke(obj03,7,8);
System.out.println(result);
//调用私有方法(在类的外部通过反射调用私有方法会破坏封装性)
Class cls04 = Class.forName(catName);
Constructor constructor04 = cls04.getConstructor(String.class, int.class, String.class);
Object obj04 = constructor04.newInstance("僵尸猫", 17, "男");
//可以调用类中的所有方法(不包括父类中继承的方法)
Method method04 = cls.getDeclaredMethod(wcMethod);
//禁止访问安全检查
method04.setAccessible(true);
method04.invoke(obj03);
//getDeclaredMethods() 获取本类中所有方法,包括私有方法
Class cls05 = Class.forName(catName);
Method[] declaredMethods = cls05.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println(declaredMethod);
}
//可变长参数:可变长参数只能是参数列表的最后一位参数
Class<?> cls06 = Class.forName(catName);
Constructor<?> constructor06 = cls06.getConstructor(String.class, int.class, String.class);
Object obj06 = constructor06.newInstance("冰猫", 12, "男");
Method method06 = cls06.getMethod(dosomeMethod, int.class, float.class, char.class,boolean.class,String[].class);
String[] strs = {"小牛马","打工人","社畜"};
method06.invoke(obj06,5,5.5f,'a',true,strs);
}
}
4、配置文件使用反射
创建配置文件-reflection.properties
#类名
catName=reflection.question.Cat
#方法名
hiMethod=hi
eatMethod=eat
wcMethod=WC
sumMethod=sum
#属性
nameName=name
ageName=age
sexName=sex
通过配置文件,使用反射创建对象,设置属性,调用方法.
public class Reflection01 {
public static void main(String[] args) throws Exception{
//使用reflection.properties类
Properties properties = new Properties();
//加载配置文件
properties.load(new FileInputStream("src\\reflection.properties"));
String className = properties.get("catName").toString(); //获得配置文件中的 类的路径
String nameName = properties.get("nameName").toString(); //获得配置文件中的 属性名字
String hiMethod = properties.get("hiMethod").toString(); //获得配置文件中的 方法名字
//1.加载类,返回class的类对象
Class<?> cls = Class.forName(className);
//2.实例化类对象
//newInstance() 会调无参构造器创建对象
Object obj = cls.newInstance();
//3.访问私有属性,并设置值
Field name = cls.getDeclaredField(nameName);
name.setAccessible(true); //关闭访问安全检查
name.set(obj,"历史猫"); //设置私有属性的值
//4.获取 方法对象
Method method01 = cls.getMethod(hiMethod);
//5.指定类对象调用 方法对象的方法
method01.invoke(obj);
}
}
5、优化反射的执行效率
反射调用优化-关闭访问检查
- Method和Field、Constructor对象都有setAccessible(0)方法
- setAccessible:作用是启动和禁用访问安全检查的开关
- 参数值为true表示反射的对象在使用时取消访问检查,提高反射的效率。参数值为false则表示反射的对象执行访问检查。
public class Reflection11 {
public static void main(String[] args) throws Exception {
m1();//传统
m2();//反射
m3();//反射优化
}
//传统方法来调用 hi
public static void m1() {
Cat cat = new Cat();
long start = System.currentTimeMillis();
for (int i = 0; i < 90; i++) {
cat.hi();
}
long end = System.currentTimeMillis();
System.out.println("m1() 耗时=" + (end - start));
}
//反射机制调用方法 hi
public static void m2() throws Exception {
Class cls = Class.forName("reflection.question.Cat");
Object o = cls.newInstance();
Method hi = cls.getMethod("hi");
long start = System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
hi.invoke(o);//反射调用方法
}
long end = System.currentTimeMillis();
System.out.println("m2() 耗时=" + (end - start));
}
//反射调用优化 + 关闭访问检查
public static void m3() throws Exception{
Class cls = Class.forName("reflection.question.Cat");
Object o = cls.newInstance();
Method hi = cls.getMethod("hi");
hi.setAccessible(true);//在反射调用方法时,取消访问检查
long start = System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
hi.invoke(o);//反射调用方法
}
long end = System.currentTimeMillis();
System.out.println("m3() 耗时=" + (end - start));
}
}
三、Class类
1、class类
-
Class类称为类的类对象。
-
JVM加载一个类的class文件时,就会创建一个class实例与该类的绑定。
-
因此每一个类只有一个class实例,这个实例就是该加载的类的类对象。
-
通过类的类对象可以获取这个类的一切信息(属性,方法),从而在程序运行期间进行相关操作。
2、class类基本介绍
- 1.Class也是类,因此也继承Object类。
- 2. Class类对象不是new出来的,而是系统创建的。
- 3.对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
- 4.每个类的实例都会记得自己是由哪个Class 实例所生成
- 5.通过Class可以完整地得到一个类的完整结构,通过一系列API
- 6.Class对象是存放在堆的。
- 7.类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法代码,变量名,方法名,访问权限等等)
3、哪些类型有Class对象
基本数据类型
Class cls = int.class;
Class cls = float.class;
Class cls = double.class;
Class cls = char.class;
Class cls = boolean.class;
引用数据类型
Class<String> cls1 = String.class; //外部类
接口类型
Class<Serializable> cls2 = Serializable.class; //接口
数组类型
Class<Integer[]> cls3 = Integer[].class; //数组
Class<float[][]> cls4 = float[][].class; //二维数组
注解类型
Class<Deprecated> cls5 = Deprecated.class; //注解
枚举类型
Class<Thread.State> cls6 = Thread.State.class;
Class<Long> cls7 = long.class;
Class<Void> cls8 = void.class;
Class<Class> cls9 =Class.class;
Void类型
Class<Void> cls8 = void.class;
CLass类型
Class<Class> cls9 = Class.class;
4、获取Class对象的方式
第一种方法:通过forName()方法获取
public class Reflection11 {
public static void main(String[] args) throws Exception {
//前提: 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取。
//应用场景: 多用于配置文件,读取类全路径,加载类.
Class cls = Class.forName("com.Demo.Cat");
}
}
第二种方法:通过类名.class获取
public class Reflection11 {
public static void main(String[] args) throws Exception {
//前提:若已知具体的类,通过类的class 获取,该方式最为安全可靠,程序性能最高。
//应用场景:多用于参数传递,比如通过反射得到对应构造器对象.
Class cls2 = Cat.class;
}
}
第三种方法:通过对象.getClass()获取
public class Reflection11 {
public static void main(String[] args) throws Exception {
//前提: 已知某个类的实例,调用该实例的getClass()方法获取Class对象,
//应用场景: 通过创建好的对象,获取Class对象
Cat cat = new Cat();
Class cl3 = cat.getClass();
}
}
第四种方法:通过 类加载器 获取Class对象
public class Reflection11 {
public static void main(String[] args) throws Exception {
Cat cat = new Cat();
//(1)先获取的是类加载器
ClassLoader classLoader = cat.getClass().getClassLoader();
//(2)通过类加载器获取Class对象
Class cls4 = classLoader.loadClass("com.Demo.Cat");
}
}
第五种方法:基本数据(int,char , boolean, float , double,byte, long , short)按如下方式得到Class类对象
public class Reflection11 {
public static void main(String[] args) throws Exception {
Class<Integer> integerClass = int.class;
Class<Float> floatClass = float.class;
Class<Double> doubleClass = double.class;
Class<Character> characterClass = char.class;
Class<Boolean> booleanClass = boolean.class;
}
}
第六方法:基本数据类型对应的包装类,可以通过.TYPE 得到Class类对象
public class Reflection11 {
public static void main(String[] args) throws Exception {
Class<Integer> type = Integer.TYPE;
Class<Float> type1 = Float.TYPE;
Class<Double> type2 = Double.TYPE;
Class<Character> type3 = Character.TYPE;
Class<Boolean> type4 = Boolean.TYPE;
}
}
5、Class类的常用方法
public class Reflection06 {
public static void main(String[] args) throws Exception {
//使用reflection.properties类,读写配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src\\reflection.properties"));
String catName = properties.get("catName").toString();
//获取CLass类
//<?> 表示不确定的Java类型
Class<?> clazz = Class.forName(catName);
// (2) 通过 cls 得到加载类的对象实例
Object obj = clazz.newInstance();
//获取类名
String name = clazz.getName();
System.out.println(name);
//获取类的包名
String packName = clazz.getPackage().getName();
System.out.println(packName);
//获取类的父类
Class<?> superclass = clazz.getSuperclass();
System.out.println(superclass);
//获取类的接口
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> c : interfaces) {
System.out.println(c);
}
//获取注解
Annotation[] annotations = clazz.getDeclaredAnnotations();
for (Annotation a : annotations) {
System.out.println(a);
}
//获取类的构造方法
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> c : constructors) {
System.out.println(c);
}
//获取类的属性
Field[] fields = clazz.getFields();
for (Field f : fields) {
System.out.println(f);
}
//获取类的方法
Method[] methods = clazz.getMethods();
for (Method m : methods) {
System.out.println(m);
}
}
}
四、类加载
1、基本说明
反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。
- 1.静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
- 2.动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性
2、类加载时机
- 1.当创建对象时(new) //静态加载
- 2.当子类被加载时 //静态加载
- 3.调用类中的静态成员时 //静态加载
- 4.通过反射 //动态加载
3、类加载过程
类加载各阶段完成任务
加载阶段
连接阶段-验证
- 1.目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
- 2.包括:文件格式验证(是否以魔数oxcafebabe开头)、元数据验证、字节码验证和符号引用验证
- 3.可以考虑使用-Xverify:none 参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间。
连接阶段-准备
1.JVM会在该阶段对静态变量,分配内存并默认初始化(对应数据类型的默认初始值,如0、OL、null、false等)。这些变量所使用的内存都将在方法区中进行分配。
连接阶段-解析
1.虚拟机将常量池内的符号引用替换为直接引用的过程。
Initialization(初始化)
- 到初始化阶段,才真正开始执行类中定义的Java程序代码,此阶段是执行<clinit>0方法的过程。
- <clinit>0方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合并。
- 虚拟机会保证一个类的<clinit>)方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>0方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>0方法完毕[debug源码]
五、反射获取 类的结构信息
1、反射:获取类的结构信息-Class类
Class类 1. getName:获取全类名 2. getSimpleName:获取简单类名 3. getFields:获取所有public修饰的属性,包含本类以及父类的 4. getDeclaredFields:获取本类中所有属性 5. getMethods:获取所有public修饰的方法,包含本类以及父类的 6. getDeclaredMethods:获取本类中所有方法 7. getConstructors:获取本类所有public修饰的构造器 8. getDeclaredConstructors:获取本类中所有构造器 9. getPackage:以Package形式返回包信息 10. getSuperClass:以Class形式返回父类信息 11. getInterfaces:以Class0形式返回接口信息 12. getAnnotations:以Annotation门形式返回注解信息
public class Reflection07 {
public static void main(String[] args) throws Exception{
String catName = "reflection.question.Cat";
Class<?> cls = Class.forName(catName);
//1. getName:获取全类名
System.out.println(cls.getName());
//2. getSimpleName:获取简单类名
System.out.println(cls.getSimpleName());
//3. getFields:获取所有public修饰的属性,包含本类以及父类的
Field[] fields = cls.getFields();
for (Field field : fields) {
System.out.println(field);
}
//4. getDeclaredFields:获取本类中所有属性(包括私有属性)
Field[] fields04 = cls.getDeclaredFields();
for (Field field : fields04) {
System.out.println(field);
}
//5. getMethods:获取所有public修饰的方法,包含本类以及父类的
Method[] methods = cls.getMethods();
for (Method method : methods) {
System.out.println(method.getName());
}
//6. getDeclaredMethods:获取本类中所有方法(包括私有方法)
Method[] methods06 = cls.getDeclaredMethods();
for (Method method : methods06) {
System.out.println(method.getName());
}
//7. getConstructors:获取本类所有public修饰的构造器
Constructor[] constructors = cls.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
//8. getDeclaredConstructors:获取本类中所有构造器(包括私有的)
Constructor[] constructors08 = cls.getDeclaredConstructors();
for (Constructor constructor : constructors08) {
System.out.println(constructor);
}
//9. getPackage:以Package形式返回包信息
System.out.println(cls.getPackage());
//10. getSuperClass:以Class形式返回父类信息
Class<?> superclass = cls.getSuperclass();
System.out.println(superclass);
//11. getInterfaces:以Class0形式返回接口信息
Class<?>[] interfaces = cls.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println(anInterface);
}
//12. getAnnotations:以Annotation门形式返回注解信息
Annotation[] annotations = cls.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
}
}
2、反射:获取类的结构信息-Field类
Field类 1. getModifiers: 以int形式返回修饰符 [说明:默认修饰符是0,public是1,private是2,protected是4, static是8, final是16], public(1) + static (8)=9 2. getType: 以Class形式返回类型 3. getName: 返回属性名
public class Reflection08 {
public static void main(String[] args) throws Exception {
String catName = "reflection.question.Cat";
String ageName = "age";
Class<?> cls = Class.forName(catName);
Field age = cls.getField(ageName);
//1. getModifiers(): 以int形式返回属性修饰符
//[说明:默认修饰符是0,public是1,private是2,protected是4, static是8, final是16], public(1) + static (8)=9]
int modifiers = age.getModifiers();
System.out.println(modifiers);
//2. getType(): 以Class形式返回属性类型
Class<?> type = age.getType();
System.out.println(type);
//3. getName(): 获取属性名
String name = age.getName();
System.out.println(name);
}
}
3、反射:获取类的结构信息-Method类
Method类 1. getModifiers: 以lint形式返回修饰符 [说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16] 2. getReturnType:以Class形式获取返回类型 3. getName:返回方法名 4. getDeclaringClass(): 获取方法所属的类 5. aetParameterTypes:以Class返回参教类型数组 6. getParameterCount() 获取方法参数的个数 7. getModifiers() 获取方法的访问修饰符 8. isAnnotationPresent() 判断某个类/方法/属性/构造器有没有被某个注解标注过
public class Reflection09 {
public static void main(String[] args) throws Exception {
String catName = "reflection.question.Cat";
String hiMethod = "hi";
Class<?> cls = Class.forName(catName);
Method method = cls.getMethod(hiMethod);
//1. getModifiers: 以lint形式返回方法的修饰符
//[说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16]
int modifiers = method.getModifiers();
System.out.println(modifiers);
//2. getReturnType:以Class形式获取返回类型
Class<?> returnType = method.getReturnType();
System.out.println(returnType);
//3. getName: 获取方法名
String name = method.getName();
System.out.println(name);
//4. getParameterTypes:以Class返回参教类型数组
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println(parameterType);
}
}
}
4、反射:获取类的结构信息-Constructor类
Constructor类 1. getModifiers: 以int形式返回修饰符 2. getName:返回构造器名(全类名) 3. getParameterTypes:以Class[]返回参数类型数组
public class Reflection10 {
public static void main(String[] args) throws Exception {
String catName = "reflection.question.Cat";
Class<?> cls = Class.forName(catName);
Constructor<?> constructor = cls.getConstructor(String.class,int.class,String.class);
//1. getModifiers: 以int形式返回构造器修饰符
//[说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16]
int modifiers = constructor.getModifiers();
System.out.println(modifiers);
//2. getName:返回构造器名(全类名)
String name = constructor.getName();
System.out.println(name);
//3. getParameterTypes:以Class[]返回参数类型数组
Class<?>[] parameterTypes = constructor.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println(parameterType);
}
}
}