为什么会有反射机制?
假如现在有一个配置文件,里面有path=com.study.reflection类的全路径和method=fn方法名,传统的方式能否创建出这个类并且调用它的fn方法呢?答案是不能的
传统的方式想创建一个对象只能new,但是这样的需求在学习框架时特别多,即通过外部文件配置,在不修改源码情况下来控制程序,也符合设计模式的OCP原则(开闭原则:不修改源码,扩容功能)
反射创建对象如下所示
package src.month09.day0922.demo02;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Demo02 {
public static void main(String[] args) throws Exception {
String path = "src.month09.day0922.demo02.Cat";
String method = "run";
String property = "age";
// Class.forName()用来加载类,返回一个src.month09.day0922.demo02.Cat对应的Class类型的对象aClass
Class<?> aClass = Class.forName(path);// <?>表示不确定类型
// 通过aClass对象得到加载的src.month09.day0922.demo02.Cat实例
Object o = aClass.newInstance();
// 1 通过aClass得到src.month09.day0922.demo02.Cat类的 run 方法对象
// 在反射里,可以把方法也视为对象
Method method1 = aClass.getMethod(method);
// 得到方法对象后,可以在invoke方法中传入对象,调用对象身上的方法
method1.invoke(o);
// 2 通过aClass得到age属性对象
Field age = aClass.getField(property);
// 通过属性对象调用get方法,传入对象,得到属性值
System.out.println(age.get(o));
// 3 通过aClass得到构造器对象
Constructor<?> constructor = aClass.getConstructor();
}
}
- 反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对像的属性及方法,反射在设计模式和框架底层都会用到
- 加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息,通过这个对象得到类的结构,这个Class对象就像一面镜子,透过这个镜子看到类的结构,所以形象的称之为:反射
反射机制可以干嘛
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时得到任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
反射优缺点
- 优点:可以动态的创建和使用对像(也是框架底层核心),使用灵活,没有反射机制,框架技术就失去底层支撑
- 缺点:使用反射基本是解释执行,对执行速度有影响
反射调用优化-关闭访问检查
- Method,Field和Constructor对象都有setAccessible()方法
- setAccessible作用是启动和禁用访问安全检查的开关
- 参数值为true表示反射的对象在使用时取消访问检查,提高反射的效率,参数值为false则表示反射的对象执行访问检查
Class类
- Class也是类,因此也继承Object类
- Class类对象不是new出来的,而是系统创建的
- 对于某个类的Class类对象,在内存中只有一份,因为类只加载一次
- 每个类的实例都会记得自己是由哪个Class实例所生成
- 通过Class对象可以完整地得到一个类的完整结构,通过一系列API
- Class对象是存放在堆的
- 类的字节码二进制数据,是放在方法区的,有的地方称为类的元数据(包括方法代码,变量名,方法名,访问权限等等)
Class类的常用方法
static Class forName(String name) 返回指定类名name的Class对象
Object newInstance() 调用缺省构造函数,返回该Class对象的一个实例
getName() 返回此Class对象所表示的实体(类,接口,数组类,基本类型等)名称
Class getSuperClass() 返回当前Class对象的父类的Class对象
Class [] getInterfaces() 获取当前Class对象的接口
ClassLoader getClassLoader() 返回该类的类加载器
Class getSuperclass() 返回表示此Class所表示的实体的超类的Class
Constructor[] getConstructors() 返回一个包含某些Constructor对象的数组
Field[] getDeclaredFields() 返回Field对象的一个数组
Method getMethod(String name,Class ...paramTypes) 返回一个Method对象,此对象的形参类型为paramType
getPackage().getName 得到包名
getName() 得到全类名
getField() 获得属性,的到属性后可以用set()赋值
Field[] getFields() 获取所有属性
获取Class类对象的6种方式
不同阶段有不同获取方式
- 代码/编译阶段通过 Class.forName() 获取
- Class类阶段(加载阶段)通过 类.class 获取
- 运行阶段通过 对象.getClass() 获取
-
前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException,实例:Class cls1 = Class.forName("java.lang.Cat"); 应用场景:多用于配置文件,读取类全路径,加载类
-
前提:若已知具体的类,通过类的class获取,该方式最为安全可靠,程序性能最高,实例:Class cls2=Cat.class; 应用场景:多用于参数传递,比如通过反射得到对应构造器对象
-
前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象,实例:Class clazz = 对象.getClass(); 应用场景:通过创建好的对象,获取Class对象
-
其他方式
(1)先得到类加载器
ClassLoader cl = 对象.getClass().getClassLoader();
(2)通过类加载器得到Class对象
Class clazz4 = cl.loadClass("类的全类名");
-
基本数据(int,char,boolean,float,double,byte,long,short)按如下方式得到Class类对象
Class cls = 基本数据类型.class
-
基本数据类型对应的包装类,可以通过.type得到Class类对象
Class cls = 包装类.TYPE
哪些类型有Class对象
外部类,内部类,接口,数组,注解,枚举,基本数据类型,void,Class
反射机制是java实现动态语言的关键,也就是通过反射实现类动态加载
-
静态加载:编译时加载相关的类
如:
Dog dog = new Dog();如果没有则报错,依赖性太强- 动态加载:运行时加载需要的类,如果运行时不用该类,即使该类不存在,也不报错,降低了依赖性
Class<?> aClass == Class.forName("com.study.Dog"); Object o = aClass.newInstance(); -
类加载时机
- 当创建对象时(new) 静态加载
- 当子类被加载时 静态加载
- 调用类中的静态成员时 静态加载
- 通过反射 动态加载
类的加载过程
源代码Cat.java ==javac编译==> 字节码文件Cat.class ==java运行==> 类加载
其中类加载有三个阶段: 加载, 连接, 初始化
-
加载:将类的class文件读入内存,并为之创建java.lang.Class对象,此过程由类加载器完成
-
连接:将类的二进制数据合并到 JRE 中
-
初始化:JVM负责对类进行初始化,这里主要指静态成员
类加载后内存布局情况
-
方法区内存放类的字节码二进制数据
-
堆内存放类的Class对象
通过反射获取类的结构信息
第一组:java.lang.Class类
getName:获取全类名
getSimpleName:获取简单类名
getFields:获取所有public修饰的属性,包含本类以及父类的
getDeclaredFields:获取本类中所有属性
getMethods:获取所有public修饰的方法,包含本类以及父类的
getDeclaredMethods:获取本类中所有方法
getConstructors:获取所有public修饰的构造器,包含本类以及父类的
getDeclaredConstructors:获取本类中所有构造器
getPackage:以Package形式返回包信息
getSuperClass:以Class形式返回父类信息
getlnterfaces:以Class[]形式返回接口信息
getAnnotations:以Annotation[]形式返回注解信息
第二组:java.lang.reflect.Field类
getModifiers:以int形式返回修饰符 (说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16)
getType:以Class形式返回类型
getName:返回属性名
第三组:java.lang.reflect.Method类
getModifiers:以int形式返回修饰符 (说明:默认修饰符是0,public是1,private是2,protected是4,static是8,final是16)
getReturnType:以Class形式获取返回类型
getName:返回方法名
getParameterTypes:以Class[]返回参数类型数组
第四组:java.lang.reflect.Constructor类
getModifiers:以int形式返回修饰符
getName:返回构造器名(全类名)
getParameterTypes:以Class返回参数类型数组
反射爆破
通过反射机制,可以访问对象中的私有属性和方法
访问构造器,创建实例
package src.month09.day0922.demo03;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Demo03 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// 1 先获取到User类的Class对象
Class<?> userClass = Class.forName("src.month09.day0922.demo03.User");
// 2 通过public的无参构造器创建实例
Object o = userClass.newInstance();
System.out.println(o);
// 3 通过public的有参构造器创建实例
Constructor<?> constructor = userClass.getConstructor(String.class);
// constructor对象就是
// public User(String name) {// public
// this.name = name;
// }
Object tom = constructor.newInstance("tom");
System.out.println(tom);
// 4 通过非public的有参构造器创建实例
Constructor<?> constructor1 = userClass.getDeclaredConstructor(String.class, int.class);
constructor1.setAccessible(true);// 爆破:使用反射可以访问私有构造器/属性/方法,在反射面前一切都是纸老虎,类似于留了个后门
Object jerry = constructor1.newInstance("jerry", 12);
System.out.println(jerry);
}
}
class User {
private String name;
private int age;
public User() {// 无参
}
public User(String name) {// public
this.name = name;
}
private User(String name, int age) {// private
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + "\t" + age;
}
}
访问属性
package src.month09.day0922.demo04;
import java.lang.reflect.Field;
public class Demo04 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
// 1 得到Student类对应的Class对象
Class<?> stuClass = Class.forName("src.month09.day0922.demo04.Student");
// 2 创建对象
Object o = stuClass.newInstance();// o的运行类型示Student
// 3 拿到对象的age属性,公共的
Field age = stuClass.getField("age");
age.set(o, 34);// 通过反射设置属性
System.out.println(o);// null 34
// 4 使用反射操作name属性,私有的
Field name = stuClass.getDeclaredField("name");
name.setAccessible(true);// 爆破,可以操作私有属性
name.set(o, "jerry");
System.out.println(o);// jerry 34
}
}
class Student {
private String name;// 私有
public int age;// 公共
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + "\t" + age;
}
}
访问方法
package src.month09.day0922.demo04;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo04 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
// 1 得到Student类对应的Class对象
Class<?> stuClass = Class.forName("src.month09.day0922.demo04.Student");
// 2 创建对象
Object o = stuClass.newInstance();
// 3 调用public的eat方法
Method eat = stuClass.getMethod("eat");
eat.invoke(o);
// 4 调用private的day方法
Method say = stuClass.getDeclaredMethod("say");
say.setAccessible(true);// 爆破
say.invoke(o);
}
}
class Student {
public void eat() {
System.out.println("eat");
}
private void say() {
System.out.println("hello");
}
}