1 反射机制
1.1 Java程序的三阶段
image-20210526212134502.png1.2 反射的优缺点
- 优点:可以动态创建和使用对象(底层框架核心),使用灵活,没有反射机制,框架就失去底层支撑
- 缺点:使用反射基本是解释执行,对执行速度有影响
//测试使用new,反射和优化后的反射,执行方法的速度
public class SpeedTest {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
m1();
m2();
m3();
}
//使用传统new对象的方式
public static void m1() {
Person person = new Person();
long start = System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
person.sayHi();
}
long end = System.currentTimeMillis();
System.out.println("m1: " + (end - start));
}
//使用反射的方式
public static void m2() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> personClass = Class.forName("com.yqj.domain.Person");
Object person = personClass.newInstance();
Method sayHi = personClass.getMethod("sayHi");
long start = System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
sayHi.invoke(person);
}
long end = System.currentTimeMillis();
System.out.println("m2: " + (end - start));
}
//使用优化后的反射
//1.Method,Field,Constructor 对象都有 setAccessible() 方法
//2.setAccessible 作用是启动和禁用访问安全检查的开关,true为取消访问检查,false开启
public static void m3() throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class<?> personClass = Class.forName("com.yqj.domain.Person");
Object person = personClass.newInstance();
Method sayHi = personClass.getMethod("sayHi");
sayHi.setAccessible(true);
long start = System.currentTimeMillis();
for (int i = 0; i < 900000000; i++) {
sayHi.invoke(person);
}
long end = System.currentTimeMillis();
System.out.println("m2: " + (end - start));
}
}
- result
m1: 5
m2: 2988
m2: 1888
2 Class类
2.1 基本说明
- Class也是类,也继承Object类
- Class类对象不是new出来的,而是系统创建的
//ClassLoader 类,不管是new还是反射的方法,第一次创建对象时候均需要通过该类创建单例的Class对象
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
- 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
Person person1 = new Person();
Person person2 = new Person();
System.out.println(person1.getClass().hashCode()); //hashCode一致说明是同一个对象
System.out.println(person2.getClass().hashCode());
- 每个类的实例都会记得自己是由哪个Class实例所生成( getClass方法)
- 通过Class对象可以完整地得到一个类的完整结构,通过API
- Class对象存放在堆中
- 类的字节码二进制数据放在方法区,或称为类的元数据
2.2 Class类的常用方法
getMethod()返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法
getDeclaredMethod()返回表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法
public class ClassBaseMethod {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
String classPath = "com.yqj.domain.Person";
//1.获取Person类对应的Class对象
Class<?> personClass = Class.forName(classPath);
//2.输出Class
System.out.println(personClass); //显示哪个类的Class对象
System.out.println(personClass.getClass()); //显示Class本身
//3.得到包名
System.out.println(personClass.getPackage().getName());
//4.得到全类名
System.out.println(personClass.getName());
//5.创建对象实例
Person person = (Person) personClass.newInstance();
System.out.println(person);
//6.通过反射获取属性
//可以获取包括私有属性
Field age = personClass.getDeclaredField("age");
age.setAccessible(true); //允许访问私有属性
age.set(person,24);
System.out.println(age.get(person));
//类似方法
// personClass.getDeclaredMethod();
// personClass.getMethod();
// personClass.getDeclaredConstructor()
// personClass.getConstructor()
}
}
2.3 获取Class对象的方法
public class GetClass {
public static void main(String[] args) throws ClassNotFoundException {
//1.已知类的全类名,且该类在类路径下,可通过Class静态方法forName获取
//多用于配置文件,读取类全路径,加载类
Class personClass1 = Class.forName("com.yqj.domain.Person");
System.out.println(personClass1);
//2.若已知具体的类,通过类的class获取,安全且程序性能最高
//多用于参数传递,比如通过反射得到对应的构造器对象
Class personClass2 = Person.class;
System.out.println(personClass2);
//3.已知某个类的实例,通过getClass方法获取Class对象
//通过创建好的对象,获取Class对象
Class personClass3 = new Person().getClass();
System.out.println(personClass3);
//4.通过类加载器获取Class对象
Class personClass4 = new Person().getClass().getClassLoader().loadClass("com.yqj.domain.Person");
System.out.println(personClass4);
//5.基本数据类型通过class获取
Class intClass = int.class;
System.out.println(intClass);
//6.包装类通过TYPE获取其基本类型的Class对象
Class integerClass = Integer.TYPE;
System.out.println(integerClass);
}
}
2.4 具有Class对象的类型
- 外部类,成员内部类,静态内部类,局部内部类,匿名内部类
- interface接口
- 数组
- enum枚举
- annotation注解
- 基本数据类型
- void
2.5 类加载各阶段任务
image-20210527084625015.png- 加载阶段:JVM在该阶段将字节码从不同的数据源(class文件,jar包,网络)转换为二进制字节流加载至内存中,并生成一个代表该类的Class对象
- 连接阶段-验证:为了确保Class文件字节流中包含的信息符合当前虚拟机的要求,包括对文件格式的验证,元数据验证,字节码验证和符号引用验证,可通过 -Xverify:none 关闭
- 连接阶段-准备:JVM在该阶段对静态变量分配内存并默认初始化(0,0L,null,false),这些变量所使用的内存在方法区分配
- 连接阶段-解析:JVM将常量池内的符号引用替换为直接引用的过程
- Initialization初始化:此阶段执行<clinit>() 方法,该方法依次收集类中所有静态变量的赋值动作和静态代码块中的语句,并按顺序进行合并。JVM包装该方法在多线程环境中被正确加锁同步
2.6 通过反射获取结构信息
1. Class类
getName()
getSimpleName()
getFields() 获取所有public修饰的属性,包含本类和父类
getDeclaredFields() 获取本类所有属性
getMethods()
getDeclaredMethods()
getConstructors()
getDeclaredConstructors()
getPackage()
getSuperClass()
getInterfaces()
getAnnotations()
2. Field类
getModifiers() 以int形式返回修饰符,默认修饰符0,public1,private2,protected4,static8,final16,若 public1 + static8 = 9
getType()
getName()
3. Method类
getModifiers()
getReturnType()
getName()
getParameterTypes()
4. Constructor类
getModifiers()
getName()
getParameterTypes()
网友评论