Java中的反射机制
1 为什么要反射?
正射:我们new创建类的实例时实际上时JVM在运行时根据这个类的class对象构造的。
反射:反射是在运行时通过类的class对象获得的内部定义信息
提出反射应用场景: 当前我们需要实例一个对象,但是不确定创建对象,那么就需要动态创建了。 (通过传入参数进行确定要使用哪一种实体)
反射可以在程序运行过程中动态获取类信息和调用类方法。通过反射构造类实例,代码最终会演变成下面这样。
public <T> T getPoJo(String className) throws Exception {Class clazz = Class.forName(className);return (T) clazz.newInstance();
}
反射的思想
在程序运行过程中确定和解析数据类的类型。
反射的作用
对于在编译其无法确定使用哪个数据类的场景,通过反射可以在程序运行时构造不同的数据类实例
2 什么是Java反射?
3 Java反射机制的实现
class类
正在运行在内存中的所有类都是该类的实例对象,每个Class类都包含本类的所有信息
反射机制的实现
public class Demo01 {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {Person person = new Person();//1.获取类的字节码文件对象//方法一:直接通过一个class的静态变量class获取:Class cls1=Person.class;System.out.println(cls1);//方法二:如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取:Class cls2=person.getClass();System.out.println(cls2);//方法三:如果知道一个class的完整类名,可以通过静态方法Class.forName()获取:Class cls3=Class.forName("apesource.knowledge01.refStudy.Person");System.out.println(cls3);System.out.println("cls1 == cls2?:"+(cls1 == cls2));System.out.println("cls2 == cls3?:"+(cls2 == cls3));System.out.println("cls1 == cls3?:"+(cls1 == cls3));//通过Class.newInstance()可以创建类实例,要求实例时必须要有公共的无参构造方法,否则报错Object object=cls1.newInstance();System.out.println(object);}
}
类内部主要信息
- Field:所有属性
- Method:所有方法
- Constructor:所有构造方法
class类常用的方法
类型 | 访问方法 | 返回值类型 | 说明 |
包路径 | getPackage() | Package 对象 | 获取该类的存放路径 |
类名称 | getName() | String 对象 | 获取该类的名称 |
继承类 | getSuperclass() | Class 对象 | 获取该类继承的类 |
实现接口 | getlnterfaces() | Class 型数组 | 获取该类实现的所有接口 |
构造方法 | getConstructors() | Constructor 型数组 | 获取所有权限为 public 的构造方法 |
getDeclaredContruectors() | Constructor 对象 | 获取当前对象的所有构造方法 | |
方法 | getMethods() | Methods 型数组 | 获取所有权限为 public 的方法 |
getDeclaredMethods() | Methods 对象 | 获取当前对象的所有方法 | |
成员变量 | getFields() | Field 型数组 | 获取所有权限为 public 的成员变量 |
getDeclareFileds() | Field 对象 | 获取当前对象的所有成员变量 |
constructor类
描述 Class类中的构造方法的一个类,该类提供了关于所有描述构造方法的信息
类内部主要信息
①构造方法的访问修饰符(public / private / protected -)
②构造方法的参数
参数的数据类型(int / double….)
参数名字
标注在参数上的注解
反射机制的实现
public class Demo04 {public static void main() throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {//1.获取类的字节码文件Class cls=Person.class;//2.获取构造方法//getConstructors() 所有被public修饰的构造方法
// Constructor[] constructors=cls.getConstructors();//getDeclaredConstructors() 所有声明的方法Constructor[] constructors=cls.getDeclaredConstructors();for(Constructor c:constructors){System.out.println(c);}//getConstructor() 获取某个被public修饰的构造方法Constructor constructor=cls.getConstructor(String.class);System.out.println(constructor);//getDeclaredConstructor() 获取指定的某个构造方法Constructor<Person> constructor1=cls.getDeclaredConstructor(String.class,int.class);System.out.println(constructor1);//3.解剖构造方法对象//包-0 public-1 private-2 protected-4int modifier=constructor1.getModifiers();System.out.println(modifier);System.out.println("权限修饰符是否是公共的?"+ Modifier.isPublic(modifier));//获取方法的形参Parameter[] parameters=constructor1.getParameters();for(Parameter p:parameters){System.out.println(p.getType()+" "+p.getName());}System.out.println("构造方法的名字:"+constructor1.getName());//4.实例化对象constructor1.setAccessible(true); //临时取消权限校验符Person person=constructor1.newInstance("张三",18);System.out.println(person);}
}
作用
创建目标对象
Field类
通过反射获取到一个 Field对象时,其内部包含了某个类中其中一个属性的所有信息
类内部主要信息
①标注在属性上的注解
②属性名
③ 属性的数据类型(boolean / double / int /String …)
④ 属性访问修饰符(public / private / protected)
反射机制的实现
public class Demo05 {public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {//1.获取类的字节码文件对象Class cls=Dog.class;//2.获取成员变量//getFields() 获取所有的public成员变量(包含父类的成员变量)Field[] fields=cls.getFields();for(Field f:fields){System.out.println(f);}System.out.println("=======================================================================");//getDeclaredFields() 获取到本类中所有声明的成员变量Field[] fields1=cls.getDeclaredFields();for(Field f:fields1){System.out.println(f);}System.out.println("========================================================================");//getField() 获取本类或父类中某个被public修饰的成员变量Field field=cls.getField("name");System.out.println(field);System.out.println("========================================================================");//getDeclaredField() 获取到本类中某个成员变量Field field1=cls.getDeclaredField("color");System.out.println(field1);System.out.println("========================================================================");//3.解剖//获取权限修饰符int modifier=field1.getModifiers();System.out.println("权限修饰符为:"+modifier);System.out.println("是否是静态成员变量?"+ Modifier.isStatic(modifier));System.out.println("是否是私有成员变量?"+Modifier.isPrivate(modifier));System.out.println("========================================================================");//获取数据类型Class typeClass=field1.getType();System.out.println("数据类型为:"+typeClass);System.out.println("========================================================================");//获取变量名String name=field1.getName();System.out.println("变量名为:"+name);//4.应用Dog dog=new Dog("旺财",3,"拉布拉多",20.0,12,"金黄");Dog dog1=new Dog();field1.setAccessible(true); //临时取消权限校验符//获取对象dog中此成员变量的值
// Object object=field1.get(dog);//获取静态成员变量,不依赖于对象而创建,所以获取此成员变量的值时,可以不传对象Object object=field1.get(null);System.out.println("获取到成员变量对应的值:"+object);//修改成员变量的值
// field1.set(dog,"黑白");//设置静态成员变量值,静态成员变量共享一片空间,所以可以修改field1.set(null,"黑白");System.out.println(dog);System.out.println(dog1);}
}
Method类
描述 Class类中所有方法(不包括构造方法)的类,包括抽象方法。
类内部主要信息
① Constructor类内部主要信息相同
② 方法返回值类型(int /double…)
反射机制的实现
public class Demo07 {public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {//1.获取Student类的字节码文件对象Class cls=Student.class;//2.获取方法//getMethods() 获取本类和父类中所有被public修饰的方法Method[] methods=cls.getMethods();for(Method m:methods){System.out.println(m);}System.out.println("===============================================================");//getDeclaredMethods() 获取本类中所有声明的方法Method[] methods1=cls.getDeclaredMethods();for(Method m:methods1){System.out.println(m);}System.out.println("===============================================================");//getMethod() 通过方法名和参数项明确要获取的本类或父类中被public修饰的方法Method method=cls.getMethod("eat",String.class);System.out.println(method);System.out.println("===============================================================");//getDeclaredMethod() 通过方法名和参数项明确要获取的本类中被声明的方法Method method1=cls.getDeclaredMethod("eat");System.out.println(method1);System.out.println("===============================================================");//3.解剖//获取权限修饰符 getModifiers()int modifier=method1.getModifiers();System.out.println("权限修饰符为:"+modifier);//获取返回值类型 getReturnType()Class returnType=method1.getReturnType();System.out.println("返回值类型为:"+returnType);//获取方法名 getName()String methodName=method1.getName();System.out.println("方法名:"+methodName);//获取参数项 getParameters()System.out.println("获取参数项");Parameter[] parameters=method.getParameters();for(Parameter p:parameters){System.out.print(p.getType()+": "+p.getName()+" ");}System.out.println();//获取异常信息 getExceptionTypes()System.out.println("获取异常信息");Class[] exceptions=method.getExceptionTypes();for(Class c:exceptions){System.out.print(c);}System.out.println("===============================================================");//4.方法的对象 invoke(obj--对象名,args.. 参数值)Person student=new Student();method.setAccessible(true);Object object=method.invoke(student,"大盘鸡拌面");System.out.println(object);System.out.println("===============================================================");//5.静态方法调用(不依赖于对象)Method method2=cls.getDeclaredMethod("getUUid");method2.setAccessible(true);Object uid=method2.invoke(null); //静态方法,可以不传入对象System.out.println(uid);System.out.println("===============================================================");//6.多态Person person=new Person();Class cls1=person.getClass();Method method3=cls1.getMethod("hello");method3.setAccessible(true);method3.invoke(person);Method method4=cls.getMethod("hello");method4.setAccessible(true);method4.invoke(student);}
}
作用
执行方法逻辑
4 反射的应用场景
- Spring 实例化对象
当程序启动时,Spring 会读取配置文件applicationContext.xml并解析出里面所有的标签实例化到IOC容器中。
- 反射 + 工厂模式
通过反射消除工厂中的多个分支,如果需要生产新的类,无需关注工厂类,工厂类可以应对各种新增的类,反射可以使得程序更加健壮。
- JDBC连接数据库
使用JDBC连接数据库时,指定连接数据库的驱动类时用到反射加载驱动。
5 反射的优点和缺点
优点:增加程序的灵活性:面对需求变更时,可以灵活地实例化不同对象。
缺点:
- 破坏类的封装性:可以强制访问 private 修饰的信息;
- 性能损耗:反射相比直接实例化对象、调用方法、访问变量,中间需要非常多的检查步骤和解析步骤,JVM无法对它们优化。