简介
在new对象时,jvm在磁盘中找到类的"**.class"文件,由jvm将"**.class"加载到内存,为该对象分配空间,并生成一个相应的类型类(class)对象,jvm自动完成类的实例化,这是类加载的正向流程。反射则是在运行时动态地手动获取类中所有的属性、方法,此时类属性和方法在手动加载时均被加载为对象,通过这些对象去访问实际的属性/方法。
public class Apple {
private int price;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public static void main(String[] args) throws Exception{
//正常的调用--------------
Apple apple = new Apple();
apple.setPrice(5);
System.out.println("Apple Price:" + apple.getPrice());
//使用反射调用------------------
Class clz = Class.forName("com.chenshuyi.api.Apple");// 获得class对象,一个类只产生一个class对象
Method setPriceMethod = clz.getMethod("setPrice", int.class);// 获得类中方法的对象
Constructor appleConstructor = clz.getConstructor();//调用构造函数的对象
Object appleObj = appleConstructor.newInstance();//使用构造函数的对象进行实例化
setPriceMethod.invoke(appleObj, 14);//使用函数对象通过invoke方法调用对应的函数
Method getPriceMethod = clz.getMethod("getPrice");
System.out.println("Apple Price:" + getPriceMethod.invoke(appleObj));
}
}
反射API用来生成JVM中的类、接口或则对象的信息。
- Class类:反射的核心类,可以获取类的属性,方法等信息。
- Field类:Java.lang.reflec包中的类,表示类的成员变量,可以用来获取和设置类之中的属性值。
- Method类: Java.lang.reflec包中的类,表示类的方法,它可以用来获取类中的方法信息或者执行方法。
- Constructor类: Java.lang.reflec包中的类,表示类的构造方法。
2. 反射的用途
2.1 运行时配置文件
由于在运行时,动态加载class对象,动态获取对象属性和方法,因此可以将属性和方法配置在文件中,可在程序运行时对class文件和属性、函数即改即生效。
2.2 越过泛型检查
泛型用在编译期,编译过后泛型擦除(消失掉)。所以是可以通过反射越过泛型检查的,因此使用反射可以在List<String>中添加int数据。
总结
反射被广泛地用于那些需要在运行时检测或修改程序行为的程序中。这是一个相对高级的特性,只有那些语言基础非常扎实的开发者才应该使用它。如果能把这句警示时刻放在心里,那么反射机制就会成为一项强大的技术,可以让应用程序做一些几乎不可能做到的事情。
尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。
在我们使用反射技术时,下面几条内容应该牢记于心:
- 反射包括了一些动态类型,所以 JVM 无法对这些代码进行优化。因此,反射操作的效
率要比那些非反射操作低得多。我们应该避免在经常被 执行的代码或对性能要求很高的程
序中使用反射。 - 使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有
安全限制的环境中运行,如 Applet,那么这就是个问题了。 - 由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方
法),所以使用反射可能会导致意料之外的副作用--代码有功能上的错误,降低可移植性。
反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。