Java Reflection
Java 反射机制原理示意图
Java程序计算机有三个阶段:代码阶段/编译阶段、Class类阶段(加载阶段)、Runtime运行阶段
比如说有一个Cat.java的源代码文件,里面有name属性,Cat构造器,hi方法
然后对它进行编译,通过编译就会得到一个Cat.class的字节码文件
那么字节码文件里面有什么呢?
里面肯定是包含了属性,构造器,方法等等。
如果我们在Runtime运行阶段,去new一个Cat对象,并且调用hi()方法
那么它会把字节码文件加载到内存里的堆里面,就会生成Class类对象(Cat的),里面就有成员变量、构造器、成员方法等等。。
那么为什么Cat.class字节码文件为什么会生成一个Class对象并且放在堆里面呢?
这里面就有个特别重要的东西,叫做类加载器——ClassLoader,其实是通过它加载进去的,并且生成Class对象。这里就已经体现出反射机制
它把生成的Class对象在堆里面其实就是数据结构,也就是可以操作的数据,它会在底层把成员变量当成对象来看待、构造器也是,当然构造器也可能有多个,也会映射成数组,方法也会当成对象来对待。
有这些东西过后————也就是类加载过后,就会生成对象,生成的这个对象就是在堆里面,该对象能够知道它是属于哪个Class对象。
当我们得到Class对象后,可以做很多工作,比如说创建对象,调用对象方法,还可以操作它的属性。
Java 反射机制可以完成什么?
反射相关的主要类
package com.godairo.reflection;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* @author GodAiro
* Creation Time: 2022/7/17 15:33.
*/
@SuppressWarnings({"all"})
public class Reflection01 {
public static void main(String[] args) throws Exception {
//根据配置文件 re.properties 指定信息, 创建 Cat 对象并调用方法 hi
Properties properties = new Properties();
properties.load(new FileInputStream("F:\\Java_Basis\\FanShe\\src\\re.properties"));
String classfullpath = properties.get("classfullpath").toString();
String methodName = properties.get("method").toString();
System.out.println(classfullpath+"+"+methodName);
//使用反射机制解决
//(1) 加载类,返回class类型的对象
Class cls = Class.forName(classfullpath);
//(2) 通过cls可以得到你加载的类。com.godairo.Cat的对象实例
Object o= cls.newInstance();
System.out.println("o的运行类型:"+o.getClass());//运行类型
//(3) 通过cls得到你加载的类 com.godairo.Cat的 methodName"hi" 的对象方法
// 即:在反射中,可以把方法视为对象(万物皆对象)
Method method1 = cls.getMethod(methodName);
//(4) 通过method调用方法:即通过方法对象来实现调用方法
System.out.println("======================");
method1.invoke(o);//传统方法:对象.方法(),反射机制:方法.invoke(对象)
//java.lang.reflect.Field: 代表类的成员变量, Field 对象表示某个类的成员变量
//得到name字段
//getFired不能得到私有的属性
Field nameField = cls.getField("age");
System.out.println(nameField.get(o));//传统写法是:对象.成员变量。反射:成员变量对象.get(对象)
//构造器
Constructor constructor = cls.getConstructor(); //()中可以指定构造器参数类型,里面没东西返回的就是无参构造器
System.out.println(constructor); //Cat() 没有形参的构造器
Constructor constructor1 = cls.getConstructor(String.class);//这里传入的String.class 就是String类的Class对象
System.out.println(constructor1);//Cat(String name) 有形参的构造器
}
}
反射优点和缺点
package com.godairo.reflection;
import com.godairo.Cat;
import java.lang.reflect.Method;
/**
* @author GodAiro
* Creation Time: 2022/7/17 16:06.
* 测试反射调用的性能,和优化方案
*/
public class Reflection02 {
public static void main(String[] args) throws Exception {
m1();
m2();
}
//传统方法调用hi()方法
public static void m1(){
Cat cat = new Cat();
long start = System.currentTimeMillis();
for (int i = 0; i < 9000000; i++) {
cat.hi();
}
long end = System.currentTimeMillis();
System.out.println("m1() 耗时="+(end-start));
}
//反射机制调用hi()方法
public static void m2 ()throws Exception{
Class cls = Class.forName("com.godairo.Cat");
Object o = cls.newInstance();
Method hi = cls.getMethod("hi");
long start = System.currentTimeMillis();
for (int i = 0; i < 9000000; i++) {
hi.invoke(o); //反射调用方法
}
long end = System.currentTimeMillis();
System.out.println("m2() 耗时="+(end-start));
}
}
为了方便调用,把实体类的hi方法输出语句可以先去掉。
确实反射方法调用性能会低点
反射调用优化-关闭访问检查
public static void m3 ()throws Exception{
Class cls = Class.forName("com.godairo.Cat");
Object o = cls.newInstance();
Method hi = cls.getMethod("hi");
hi.setAccessible(true); //取消在反射调用方法,取消访问检查
long start = System.currentTimeMillis();
for (int i = 0; i < 9000000; i++) {
hi.invoke(o); //反射调用方法
}
long end = System.currentTimeMillis();
System.out.println("m3() 耗时="+(end-start));
}