JAVA反射认识
2020-03-07 本文已影响0人
QM
什么是反射
Java反射就是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;并且能改变它的属性。这样动态获取新的以及动态调用对象方法的功能就叫做反射。
反射的作用
我们知道反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。那么我们便可以更灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;还有动态代理的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!
1 Class类
- 一个类在 JVM 中只会有一个 Class 实例
- Class可以说是反射能够实现的基础
- class关键字是在声明java类时使用的;而Class 是java JDK提供的一个类,完整路径为 java.lang.Class
- 对于每一种类,Java虚拟机都会初始化出一个Class类型的实例,每当我们编写并且编译一个新创建的类就会产生一个对应Class对象,并且这个Class对象会被保存在同名.class文件里。
- 当我们new一个新对象或者引用静态成员变量时,Java虚拟机(JVM)中的类加载器系统会将对应Class对象加载到JVM中,然后JVM再根据这个类型信息相关的Class对象创建我们需要实例对象或者提供静态变量的引用值。
构造器是私有的,只有JVM才可以调用这个构造函数创建Class的对象 - 每个class(注意class是小写,代表普通类)类,无论创建多少个实例对象,在JVM中都对应同一个Class对象。
- Class是反射能够实现的基础的另一个原因是:Java反射包
java.lang.reflect
中的所有类都没有public构造方法,要想获得这些类实例,只能通过Class类获取。所以说如果想使用反射,必须得获得Class对象。
得到 Class 的三种基本方式
Class c1 = Test.class; //这说明任何一个类都有一个隐含的静态成员变量class,这种方式是通过获取类的静态成员变量class得到的()
Class c2 = test.getClass();// test是Test类的一个对象,这种方式是通过一个类的对象的getClass()方法获得的 (对于基本类型无法使用这种方法)
Class c3 = Class.forName("com.catchu.me.reflect.Test"); //这种方法是Class类调用forName方法,通过一个类的全量限定名获得(基本类型无法使用此方法)
通过 Class 类获取成员变量、成员方法、接口、超类、构造方法等
- getName():获得类的完整名字。
- getFields():获得类的public类型的属性。
- getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
- getMethods():获得类的public类型的方法。
- getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
- getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
- getConstructors():获得类的public类型的构造方法。
- getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
- newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
//获得类完整的名字
String className = c2.getName();
System.out.println(className);//输出com.ys.reflex.Person
//获得类的public类型的属性。
Field[] fields = c2.getFields();
for(Field field : fields){
System.out.println(field.getName());//age
}
//获得类的所有属性。包括私有的
Field [] allFields = c2.getDeclaredFields();
for(Field field : allFields){
System.out.println(field.getName());//name age
}
//获得类的public类型的方法。这里包括 Object 类的一些方法
Method [] methods = c2.getMethods();
for(Method method : methods){
System.out.println(method.getName());//work waid equls toString hashCode等
}
//获得类的所有方法。
Method [] allMethods = c2.getDeclaredMethods();
for(Method method : allMethods){
System.out.println(method.getName());//work say
}
//获得指定的属性
Field f1 = c2.getField("age");
System.out.println(f1);
//获得指定的私有属性
Field f2 = c2.getDeclaredField("name");
//启用和禁用访问安全检查的开关,值为 true,则表示反射的对象在使用时应该取消 java 语言的访问检查;反之不取消
f2.setAccessible(true);
System.out.println(f2);
//创建这个类的一个对象
Object p2 = c2.newInstance();
//将 p2 对象的 f2 属性赋值为 Bob,f2 属性即为 私有属性 name
f2.set(p2,"Bob");
//使用反射机制可以打破封装性,导致了java对象的属性不安全。
System.out.println(f2.get(p2)); //Bob
//获取构造方法
Constructor [] constructors = c2.getConstructors();
for(Constructor constructor : constructors){
System.out.println(constructor.toString());//public com.ys.reflex.Person()
}
通过反射调用方法
- 反射通过Method的
invoke()
方法来调用目标方法。第一个参数为需要调用的目标类对象,如果方法为static的,则该参数为null。后面的参数都为目标方法的参数值,顺序与目标方法声明中的参数顺序一致。
- 注意:如果方法是private的,可以使用method.setAccessible(true)方法绕过权限检查
- 被调用的方法本身所抛出的异常在反射中都会以
InvocationTargetException
抛出。换句话说,反射调用过程中如果异常InvocationTargetException
抛出,说明反射调用本身是成功的,因为这个异常是目标方法本身所抛出的异常。
Class<?> c = Cat.class;
try {
//构造Cat实例
Constructor constructor = c.getConstructor(String.class, int.class);
Object cat = constructor.newInstance( "Jack", 3);
//调用无参方法
Method sleep = c.getDeclaredMethod("sleep");
sleep.invoke(cat);
//调用定项参数方法
Method eat = c.getDeclaredMethod("eat", String.class);
eat.invoke(cat, "grass");
//调用不定项参数方法
//不定项参数可以当成数组来处理
Class[] argTypes = new Class[] { String[].class };
Method varargsEat = c.getDeclaredMethod("eat", argTypes);
String[] foods = new String[]{
"grass", "meat"
};
varargsEat.invoke(cat, (Object)foods);
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
反射缺点
性能问题。因为反射是在运行时而不是在编译时,所有不会利用到编译优化,同时因为是动态生成,因此,反射操作的效率要比那些非反射操作低得多。
安全问题。使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如Applet,那么这就是个问题了。
代码问题。由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。