引言
反射是一种机制,能够使java程序在运行过程中,检查,获取类的基本信息(包,属性,方法等),并且可以操作对象的属性和方法
反射是框架实现的基础
反射的原理
讲述反射的原理之前,我们先看下java是如何创建对象的
- javac将java源文件,编译成 字节码文件(class文件)
- JVM 按需加载 字节码文件,将class文件加载为 对应的Class 对象,存放在 堆内存
(每个class文件只会被加载成一个 Class对象, 因为类只加载一次且类加载是同步机制,只能有一个线程加载这个类) - 根据Class对象 生成 我们熟知的 java对象
也就是说 平时我们通过new 的方式创建对象就是通过 内存中的Class对象 来创建的。
而反射是如何创建的呢?可以看下面这张图
首先可以确定的是,反射也是通过 Class对象来创建的。因为只有Class对象 有目标对象的详细信息。
与new 一个对象不同的是:
反射在执行之前,不知道自己要创建什么样的类,甚至不知道这个类是否存在,所以说,反射只有在运行到自己时,才能获取Class对象。也只有在运行时,可以检查和使用对应的类信息。
如上图 红色箭头所指,反射是在运行时,根据用户配置的参数(全限定名等)获取对应的Class对象,然后才能创建对象。
那么这样有什么好处呢?
根据反射可以通过类名参数,动态的生成对象,降低了耦合性,可以使程序更加灵活,几乎所有的框架技术都是依赖反射完成的
有什么弊端?
前面说了,反射是在运行时去找自己的Class对象,那么就有一些问题要处理,这个Class对象是否真的存在,或者是否合法等。处理这些问题自然需要时间的,所以反射的性能肯定不如直接new对象
但以现在的硬件水平,这种差距几乎是微乎其微的
new 一个对象 是在 编译期就进行了合法性检查
反射的使用介绍
相关类
java.lang.Class
代表一个类,Class对象表示某个类加载后在堆中的对象
内部含有类的所有信息,是创建的对象的关键
获得方法
// 1. 通过对象类
Class<Cat> cls1 = Cat.class;
Cat cat1 = cls1.newInstance();
// 2. 通过全限定名
Class<?> cls2 = Class.forName("com.wenzhen.interview.basics.reflection.Cat");
Cat cat2 = (Cat) cls2.newInstance();
// 3. 通过对象
Cat cat = new Cat();
Class<? extends Cat> cls3 = cat.getClass();
Cat cat3 = cls3.newInstance();
// 4. 通过类加载器(4种)
ClassLoader classLoader = cat.getClass().getClassLoader();
Class<?> aClass = classLoader.loadClass("com.wenzhen.interview.basics.reflection.Cat");
// 5. 包装类
Class<Integer> type = Integer.TYPE; // 此时获取的是 int.class
Class<Integer> integerClass = Integer.class; // 这个才是真正的包装类的class
java.lang.reflect.Field
代表类的成员变量,Field对象表示某个类的成员变量
使用案例
Class<Cat> cls1 = Cat.class;
Cat cat = cls1.newInstance();//实例化
System.out.println(cat);//Cat(id=null, name=null)
//cls1.getFields() 只能获取public 修饰的属性
Field[] fields = cls1.getDeclaredFields();//获取全部声明的属性
for (Field field : fields) {
field.setAccessible(true);//设置访问允许,//取消反射在方法调用时安全检查 破坏封装性,可以直接操作私有成员变量,提高性能
System.out.println(field.getName());//id,name
field.set(cat,"1");//设置属性值
}
System.out.println(cat);//Cat(id=1, name=1)
java.lang.reflect.Method
代表类的方法,Method对象表示某个类的方法
使用案例
Class<Cat> cls1 = Cat.class;
Cat cat = cls1.newInstance();//实例化
Field name = cls1.getDeclaredField("name");
name.setAccessible(true);
name.set(cat,"五花肉");//设置属性值,猫咪叫 五花肉
//cls1.getMethods() 只能获取public修饰的方法
Method[] declaredMethods = cls1.getDeclaredMethods();//获取全部方法
for (Method method : declaredMethods) {
if(!"cry".equals(method.getName())){
//排除非cry的方法
continue;
}
method.setAccessible(true);//设置访问允许
if(method.getParameterCount() == 0){
//如果一个参数,执行无参方法
Object invoke = method.invoke(cat);//五花肉: 喵喵
}else if(method.getParameterCount() == 1){
//如果一个参数,执行有参方法,多个参数以此类推
Object invoke = method.invoke(cat,"hihi");//五花肉: hihi
}
}
java.lang.reflect.Constructor
代表类的构造方法,Constructor对象表示构造器
使用案例
Class<Cat> cls1 = Cat.class;
Constructor<Cat> constructor = cls1.getConstructor();//无参构造
Cat cat = constructor.newInstance();
System.out.println(cat);//Cat(id=null, name=null)
Constructor<Cat> constructor1 = cls1.getConstructor(String.class);//有参构造(一个参数)
Cat cat1 = constructor1.newInstance("五花肉");
System.out.println(cat1);//Cat(id=null, name=五花肉)
Constructor<Cat> constructor2 = cls1.getConstructor(String.class,String.class);//有参构造(两个参数)
Cat cat2 = constructor2.newInstance("1","五花肉");
System.out.println(cat2);//Cat(id=1, name=五花肉)
常用方法介绍
Class<Cat> cls1 = Cat.class;
//getName:获取全类名
System.out.println(cls1.getName());//com.wenzhen.interview.basics.reflection.Cat
//getSimpleName 获取简单类名
System.out.println(cls1.getSimpleName());//Cat
//getPackage 以Package形式返回 包信息
System.out.println(cls1.getPackage());//package com.wenzhen.interview.basics.reflection
//getSuperClass 以Class形式返回父类信息
Class<?> superclass = cls1.getSuperclass();
System.out.println(superclass);//class com.wenzhen.interview.basics.reflection.Animal
//getInterfaces 以Class[]形式返回接口信息
Class<?>[] interfaces = cls1.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println(anInterface);//interface com.wenzhen.interview.basics.reflection.Base
}
Annotation[] annotations = cls1.getAnnotations();
for (Annotation annotation : annotations) {
//注意:Lombok 在编译时生成代码,生成的代码不包括注解信息
System.out.println(annotation);//注解信息
}