Java反射机制
前言:反射是框架的基础
一.反射机制
若有db文件:
classfullPath=Mysourses.cat
method=cat
通过反射机制,可以得到该类的实例化对象,并调用方法
//加载类,返回Class类型的对象cls
Class cls=Class.forName(classfullPath);
//通过cls得到你加载类的实例化对象
Object o=cls.newInstance();
//万物皆对象,将method方法也视为对象,通过cls得到
Method method01=cls.getMethod(method);
//通过方法对象调用方法
method01.invoke(o);
反射即是从Class类阶段拿到Class类对象,然后去创建对象,调用方法。
2.反射主要类
java.lang.reflection.*;//包括以下类
java.lang.Class //Class对象表示某个类加载后在堆中的对象
java.lang.reflect.Method //代表类的方法
java.lang.reflect.Field //属性
java.lang.reflect.Constructor //构造器
Method使用参考上文
Field使用
Field nameField=cls.getField("age");//得到成员变量名
System.out.print(nameField.get(o));//调用方法得到o对象中的成员变量
Constructor
Constructor constructor=cls.getConstructor();//返回无参构造器
Constructor constructor=cls.getConstructor(String Class);//有参构造器
System.out.print(constructor); //public Mysourses.cat()或public Mysourses.cat(java.lang.String)
3.反射优缺点
优点:动态的创建对象和使用方法(框架底层的核心),使用灵活。
缺点:使用反射基本是解释执行,影响执行速度
优化:关掉检查
method.setAccessible(true);
二.Class类
- Class也是类,所以继承了Object类
- Class不是new出来的,是系统创建的
- 对于某个类的Class对象,在内存中只加载一次,地址相同
- 类的实例对象知道自己从哪个Class而来
- Class对象存放在堆中
- 通过Class对象可以得到类的完整结构
1.常用方法
有前置类car
public class car {
public int money=10000;
car(){}
public void m1(){
System.out.println("我是小猫咪");
}
@Override
public String toString() {
return "小车价值:"+money;
}
}
常用方法
String classAllPath="test.car";
//得到Car类的class对象
Class cls = Class.forName(classAllPath);
System.out.println(cls);//显示的是哪个类的class对象 class test.car
System.out.println(cls.getClass());//输出cls运行类型 class java.lang.Class
System.out.println(cls.getPackage().getName()); //得到包名 test
System.out.println(cls.getName()); //test.car
//创建对象实例
Object o=cls.newInstance();
System.out.println(o);//重写toString()方法
//获取成员变量 属性brand
Field brand= cls.getField("money");
System.out.println(brand.get(o));
//通过反射改变成员变量数值
brand.set(o,-100);
System.out.println(brand.get(o));
//通过反射使用成员函数
Method method01=cls.getMethod("m1");
method01.invoke(o);
2.获取Class对象的六种方式
由上图可知,大体有4种方式,
//1.从配置文件中获取
String classAllPath="test.car";
Class<?> cls1 = Class.forName(classAllPath);
System.out.println(cls1);
//2.类名.class,用于参数传递
Class cls2=car.class;
System.out.println(cls2);
//3.通过已知实例化对象,得到类
car car01=new car();
Class cls3 = car01.getClass();
System.out.println(cls3);
//4.通过类加载器获得类的Class对象
//得到类加载器
ClassLoader classLoader=car01.getClass().getClassLoader();
//通过类加载器得到Class
Class cls4 = classLoader.loadClass(classAllPath);
System.out.println(cls4);
其实是同一个对象,在四个不同的阶段去获得Class对象
另外两种:
//5.基本数据类型可以获得自己的Class类对象
Class<Integer> integerClass = int.class; //int
Class<Character> characterClass = char.class; //char
Class<Boolean> booleanClass = boolean.class; //boolean
//6.基本数据类型对应的包装类,通过.TYPE 得到Class类对象
Class<Integer> type = Integer.TYPE; //int
而这两种得到Class对象的哈希值相同,说明底层对象相同,不过进行了装箱拆箱。
三.类加载
1.静态加载与动态加载
- 静态加载:编译时加载相关的类,如果没有则报错,依赖性太强
- 动态加载:运行时加载相关的类,用哪再加载哪,不用就不加载。即使不存在该类都不会报错,依赖性降低
静态加载时机:
- new一个对象时
- 子类被加载,父类也被加载
- 调用类中的静态成员
动态加载时机:
- 通过反射调用
2.类加载流程
-
加载:
JVM将字节码从不同的数据源中转化成二进制字节流读入内存,并创建一个java.lang.Class对象
-
连接:将类的二进制数据合并到jre中
1.验证:
目的:为了确保字节流中包含的信息符合要求并,没有危害性
包括字节码验证,文件格式验证,元数据验证等。
2.准备
JVM会对静态变量分配内存并初始化
class A{ public int n1=10; //常量,不分配内存 public static n2=20; //静态变量,分配内存但值为默认值--0 public static final int n3=30; //final修饰的静态变量,是常量,不分配内存 }
3.解析
将符号引用替换成直接引用的过程
-
初始化:JVM负责初始化,主要指的是静态成员
- 执行()方法的过程
- 按照语句在源文件中出现的顺序,依次收集所有的静态对象的复制动作和静态代码块语句进行合并。
- 虚拟机会保证一个类的()方法在多线程环境中被正确地加锁同步。如果多个线程去初始化一个类,那么只会有一个
线程去执行,其他都堵塞住。
四.反射获取类的信息
需要哪个上网查对应的api即可
五.通过反射创建对象
两种方法
- 调用类中public修饰的无参构造器
- 调用指定的构造器
class类相关方法
newInstance 调用无参构造器
getConstructor(Class..):根据参数列表,获取相应的public构造器对象
getDecalaredConstructor(Class...):获取所有的构造器对象
Constructor类相关方法
setAccessible 暴破
newInstance(Object...) 调用构造器
实例:
//先获得Class类对象
Class cls = Class.forName(classAllPath);
//1.通过public的无参构造器创建实例
Object o=userClass.newInstance();
//2.通过public的有参构造器创建实例
Constructor constructor1=userClass.getConstructor(String.Class);
Object o1=constructor1.newInstance("石头");
//3.1 通过非public的有参构造器创建实例
Constructor constructor2=userClass.getDecalaredConstructor(String.Class,int.class);
//3.2 暴破获取private构造器/属性/方法
constructor2.setAccessible(true);
Object o1=constructor.newInstance("石头1","1000");