反射
反射(框架的灵魂)
为什么需要反射?
问题:
通过读取配置文件里的类和方法,创建一个对象和调用这个方法
我的想法:用文件流获取配置文件的内容
public class quesion1 {
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
properties.load(new FileReader("D:\\java高级部分\\Reflex\\src\\com\\LiuDeHen\\Reflexquesion\\re.properties"));
String lei = properties.getProperty("lei");
String method = properties.getProperty("method");
System.out.println(lei);
System.out.println(method);
// 按我的想法可以直接 new lei,字符串lei内容确实是类名,但此时lei是一个字符串,怎么可能new 一个字符串呢
}
}
所以目前我们传统的方法和技术做不到这一步
这样的需求在学习框架时特别多,即通过外部文件配置,在不修改源码情况下,来控制程序,也符合设计模式的ocp原则(开闭原则:不修改源码,开扩容功能)
用反射解决上面的问题
// 通过反射解决
// 1. 加载类,返回Class类型的的对象aClass
Class aClass = Class.forName(lei);
// 2.通过newInstance方法获取配置文件中com.LiuDeHen.Reflex.cat(lei)这个对象
Object o = aClass.newInstance();
** 3.这里我们不要把object转换成cat类型,因为反射就是要在不修改源代码,修改配置文件的情况下操作代码
**如果我们这里转成cat类型,我们是可以调用hi方法,但是有另外一个方法时,我们就要修改源码调用另外一个方法
// 这里我们用getMethod获取一个方法的对象(万物皆对象)
Method Method = aClass.getMethod(method);
// 4.通过Method方法对象实现调用方法
Method.invoke(o);//传统是对象.方法,反射机制也是方法对象.invoke方法(也可以说是方法.invoke(对象))
}
反射的作用
作用:
加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,形象的称之为:反射
java反射机制原理图

每一个类都有一个Class类对象,也就是说,所有的类都是Class类的实例化对象
程序的三个阶段:
首先我们写的源码/代码(.java文件)会被javac编译成(.class字节码文件),然后通过类加载器Classloader里的loadClass()方法将对应的类生成一个Class类对象(这里体现了反射)并到Class类加载阶段,并把Class对象放在堆里(对象里有我们当前类的所有内容),加载完后到我们平时看的运行阶段,我们在运行阶段可以通过cat对象获取到cat对象是在哪一个Class类对象,得到这个Class类对象后可以对当前类进行操作
从这里我们创建一个对象就有:1.new 出来
2.通过反射机制去创建
反射的优缺点
优点:能够动态的创建和使用对象(框架的核心),使用灵活
缺点:使用反射基本都是解释运行,对执行速度有影响
反射相关的类
- java.lang.Class:代表个类Cass对象表示某个类加载后在堆中的对象
- java.lang.reflect.Method:代表类的方法, Method对象表示某个类的方法
- java.lang.reflect.Field:代表类的成员变量, Field对象表示某个类的成员变量
- java.lang.reflect.Constructor:代表类的构造方法,Constructor对象表示·构造器
// 通过反射解决
// 1. 加载类,返回Class类型的的对象aClass
Class aClass = Class.forName(lei);
// 2.通过newInstance方法创建配置文件中com.LiuDeHen.Reflex.cat(lei)这个对象
Object o = aClass.newInstance();
// 3.这里我们不要把object转换成cat类型,因为反射就是要在不修改源代码,修改配置文件的情况下操作代码
// 如果我们这里转成cat类型,我们是可以调用hi方法,但是有另外一个方法时,我们就要修改源码调用另外一个方法
// 这里我们用getMethod获取一个方法的对象(万物皆对象)
Method Method = aClass.getMethod(method);
// 4.通过getMethod方法获取能够操作方法的对象
Method.invoke(o);//传统是对象.方法,反射机制也是方法对象.invoke方法(也可以说是方法对象.invoke(类对象))
// 5.通过getField获取能操作属性的对象,注意:这里不能获取到私有化的属性
Field age = aClass.getField("Age");
System.out.println(age.get(o)); //一样的属性对象.get(类对象)
// 6.通过getConstructor获取能操作构造器的对象
Constructor constructor = aClass.getConstructor();//行参为空就是获取无参构造器
Constructor constructor1 = aClass.getConstructor(int.class,String.class);//行参为类对象就是对应的构造器
System.out.println(constructor1);
}
反射优化



全都继承了这个方法,所以我们可以通过设置setAccessible(true),关闭安全检查的开关
public void m2() throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
Class<?> aClass = Class.forName("com.LiuDeHen.Reflex.cat");
Method hi = aClass.getMethod("hi");
Object o = aClass.newInstance();
// 设置setAccessible关闭安全检查开关
hi.setAccessible(true);
long start = System.currentTimeMillis();
for (int i = 0; i <900000000 ; i++) {
hi.invoke(o);
}
long end = System.currentTimeMillis();
System.out.println("m2:"+(end-start));
}
}
Class类

重要概念:
- 也是Object的子类,它其实也是一个普通的类
- 对象不是new出来的,而是系统类加载器通过loadClass()创建的
//public Class<?> loadClass(String name) throws ClassNotFoundException {
// return loadClass(name, false);
// }
3.对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
4.通过Class对象可以获取到一个类的完整结构,通过API
5 .Class对象是放在堆中的
获取Class对象的方式:
不同的阶段:
我们写代码的阶段(编译阶段):Class.forName()====》在已知类的全类名
应用场景:多用于配置文件,读取全路径,加载类
类加载阶段(此时类Class已经存在):类名.class
应用场景:参数传递
运行阶段(已经new了对象):对象.getClass()
应用场景:在已经有了实例化对象
类加载器阶段:获取类加载器,调用loadClass()
public static void main(String[] args) throws ClassNotFoundException {
// 第一种
Class<?> aClass1 = Class.forName("com.LiuDeHen.Reflex.cat");
// 第二种
Class<cat> aClass2 = cat.class;
// 第三种
cat cat = new cat();
Class<? extends com.LiuDeHen.Reflex.cat> aClass3 = cat.getClass();
// 第四种
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class<?> aClass4 = classLoader.loadClass("com.LiuDeHen.Reflex.cat");
System.out.println(aClass1.hashCode());
System.out.println(aClass2.hashCode());
System.out.println(aClass3.hashCode());
System.out.println(aClass4.hashCode());
}
常用方法
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
Class<?> aClass = Class.forName("com.LiuDeHen.Reflex.cat");
// 1.获取包名
System.out.println(aClass.getPackage());
// 2.获取类名
System.out.println(aClass); //获取是哪个类的Class对象 com.LiuDeHen.Reflex.cat
System.out.println(aClass.getClass()); //获取的是aClass的类型java.lang.Class
System.out.println(aClass.getName()); //全类名 com.LiuDeHen.Reflex.cat
// 3.创建对象
Object o = aClass.newInstance();
System.out.println(o); //输出的是对象.tostring
// 4.获取属性
Field field = aClass.getField("Age");
System.out.println(field.get(o));
// 5。设置属性
field.set(o,120);
System.out.println(field.get(o));
// 6.获取所有属性
Field[] fields = aClass.getFields();
for (Field f:fields
) {
System.out.println(f);
}
// 7.获取方法
Method hi = aClass.getMethod("hi");
hi.invoke(o);
// 8.获取所有方法
for (Method method : aClass.getMethods()) {
System.out.println(method);
}
}
静态和动态类加载
反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载。
1.静态加载:编译时加载相关的类,如果没有则报错,依赖性太强(普通new 对象)
2.动态加载:运行时加载需要的类,即使不存在这个类编译也不会报错,只有运行时没有该类,才会报错,降低了依赖性(使用反射创建对象)
普通创建对象就是在编译期要编译所以会报错,而用反射创建的对象,是在类加载阶段,只有在运行阶段运行到这段代码,调用了Class对象发现没有才报错
类加载


加载阶段
JVM在该阶段的主要目的是将字节码从不同的数据源(可能是class 文件、也可能是jar包,甚至网络)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class 对象
连接-验证阶段
验证Class文件的字节流是不是满足jvm虚拟机格式
连接-准备阶段
静态变量的初始化赋值
连接-解析阶段
虚拟机将常量池内的符号引用替换成成直接引用(解析过程中,并没有放到内存,所以没有具体地址)
初始化
在初始化阶段,才真正开始执行类中定义的java程序代码,此阶段是执行clinit():依次搜集类中所有的静态变量和静态代码块的赋值
class A{
// 进入类加载阶段,先进入加载阶段,生成Class对象
// 再进入连接阶段验证,准备,所以这里静态代码块在上面,没有初始化也不会编译错误,因为在准备阶段就会给静态变量赋初始值值(这里为0)
// 再到解析
// 再到我们能够修改的阶段(前面都是jvm的工作),初始化阶段,执行静态代码块
// 顺序:静态变量n初始化=0 -- 静态代码块执行,赋值为100, 再执行静态变量赋值为300
static {
System.out.println("静态代码块");
n=100;
}
static int n=300;
A(int m){
n=m;
}
}
获取类的结构信息
java.lang.Class类
- getName:获取全类名
- getSimpleName:获取简单类名
- getFields:获取所有public修饰的属性,包含本类以及父类的
- getDeclaredFields:获取本类中所有属性
- getMethods:获取所有public修饰的方法,包含本类以及父类的
- getDeclaredMethods:获取本类中所有方法
- getConstructors: 获取所有public修饰的构造器(不包括父类)
- getDeclaredConstructors:获取本类中所有构造器
- getPackage:以Package形式返回包信息
- getSuperClass:以Class形式返回父类信息
- getlnterfaces:以Class[]形式返回接口信息
- getAnnotations:以Annotation[]形式返回注解信息
public static void main(String[] args) throws ClassNotFoundException {
// 1. getName:获取全类名
Class<?> aClass = Class.forName("com.LiuDeHen.Reflex.cat");
// 2. getSimpleName:获取简单类名
String simpleName = aClass.getSimpleName();
System.out.println("简单类名"+simpleName);
// 3. getFields:获取所有public修饰的属性,包含本类以及父类的
for (Field field : aClass.getFields()) {
System.out.println("所有public修饰的属性"+field);
}
// 4. getDeclaredFields:获取本类中所有属性
for (Field declaredField : aClass.getDeclaredFields()) {
System.out.println("本类中所有属性"+declaredField);
}
// 5. getMethods:获取所有public修饰的方法,包含本类以及父类的
for (Method method : aClass.getMethods()) {
System.out.println("获取所有public修饰的方法"+method);
}
// 6. getDeclaredMethods:获取本类中所有方法
for (Method declaredMethod : aClass.getDeclaredMethods()) {
System.out.println("本类中所有方法"+declaredMethod);
}
// 7. getConstructors: 获取所有public修饰的构造器
for (Constructor<?> constructor : aClass.getConstructors()) {
System.out.println("所有public修饰的构造器"+constructor);
}
// 8. getDeclaredConstructors:获取本类中所有构造器
for (Constructor<?> declaredConstructor : aClass.getDeclaredConstructors()) {
System.out.println("本类中所有构造器"+declaredConstructor);
}
// 9. getPackage:以Package形式返回包信息
Package aPackage = aClass.getPackage();
System.out.println("Package形式返回包信息"+aPackage);
// 10. getSuperClass:以Class形式返回父类信息
Class<?> superclass = aClass.getSuperclass();
System.out.println("Class形式返回父类信息"+superclass);
// 11. getlnterfaces:以Class[]形式返回接口信息
for (Class<?> anInterface : aClass.getInterfaces()) {
System.out.println("Class[]形式返回接口信息"+anInterface);
}
// 12. getAnnotations:以Annotation[]形式返回注解信息
for (Annotation annotation : aClass.getAnnotations()) {
System.out.println("以Annotation[]形式返回注解信息"+annotation);
}
}
java.lang.reflect.Field类
- getModifiers: 以int形式返回修饰符
[说明:默认修饰符是0,public是1,private是2,protected是4 static是8 , final是16] public(1) +static (8) - getType:以Class形式返回类型
- getName:返回属性名
java.lang.reflect.Method类
- 1.getModifiers:以int形式返回修饰符[说明:默认修饰符是0,public是1,private是2, protected是4static是 8, final是16]
- getReturnType:以Class形式获取返回类型
- getName:返回方法名
- getParameterTypes:以Class[]返回参数类型数组
java.lang.reflect.Constructor
通过反射创建对象
Class类相关方法
- newlnstance:调用类中的无参构造器,获取对应类的对象
- getConstructor(Class...clazz):根据参数列表,获取对应的public构造器对象
- getDecalaredConstructor(Class..clazz):根据参数列表,获取对应的所有构造器对象
Constructor类相关方法.
- setAccessible:暴破
- newlnstance(Object...obj):调用构造器
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// 通过Class类
// 1. newlnstance:调用类中的无参构造器,获取对应类的对象
Class<?> aClass = Class.forName("com.LiuDeHen.Reflex.cat");
Object o = aClass.newInstance();
System.out.println(o);
// 2. getConstructor(Class...clazz):根据参数列表,获取对应的public构造器对象
// 先得到对应构造器,再创建实例 newlnstance(Object...obj):调用构造器
Constructor<?> constructor = aClass.getConstructor(int.class,String.class);
Object o1 = constructor.newInstance(1, "你哈");
System.out.println(o1);
// 3. getDecalaredConstructor(Class..clazz):根据参数列表,获取对应的所有构造器对象
// 这个可以获取所有的构造器,保括私有
Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(String.class);
// setAccessible:暴破
declaredConstructor.setAccessible(true);//暴力破解,使用反射运行访问私有构造器,反射面前都是弟弟
Object o2 = declaredConstructor.newInstance("nimd");
System.out.println(o2);
}
通过反射获取类中属性
-
根据属性名获取Field对象
Field f = clazz对象.getDeclaredField(属性名);
-
暴破: f.setAccessible(true); //f 是Field
-
访问f.set(o,值);
sout(f.get(o));
-
如果是静态属性,则set和get中的参数o,可以写成null
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
Class<?> aClass = Class.forName("com.LiuDeHen.Reflex.cat");
Object o = aClass.newInstance();
// 拿到公有属性
// Field field = aClass.getField("age");
// 拿到所有的属性
Field field = aClass.getDeclaredField("age");
field.setAccessible(true); //爆破(记住爆破的前提是获取了所有的,而不只是公共的)
Object o1 = field.get(o);
System.out.println(o1);
// field.set(o,20); //跟对象.set一样要想设置对象里的属性,就需要这个对象
field.set(null,20); //这里传null是因为age为静态,在类加载的阶段就有值了,不是靠对象访问
// 设置完后再次获取
System.out.println(field.get(null));
}
通过反射获取类中方法
1.根据方法名和参数列表获取Method方法对象:Method m=clazz.getDeclaredMethod(方法名,XX.class);//得到本类的所有方·
2.获取对象:Object o=clazz.newlnstance();
3.暴破:m.setAccessible(true);
4、访问:Object returnValue = m.invoke(o,实参列表);5.注意:如果是静态方法,则invoke的参数o,可以写成null!
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("com.LiuDeHen.Reflex.cat");
Object o = aClass.newInstance();
// 获取公共方法
Method h1 = aClass.getMethod("hi");
h1.invoke(o);
// 获取所有方法,处理私有方法
Method h2= aClass.getDeclaredMethod("hi2");
h2.setAccessible(true);//爆破
h2.invoke(o);
// 处理静态方法时,对象可传null
Method h3= aClass.getDeclaredMethod("hi3",String.class);
h3.setAccessible(true);
h3.invoke(null,"ni");
// 当方法有返回值,统一返回Object类型,但是它运行类型和方法定义的返回值类型一致
Method h4 = aClass.getDeclaredMethod("hi4", String.class);
h4.setAccessible(true);
System.out.println(h4.invoke(o, "你好"));
}
浙公网安备 33010602011771号