一、反射
- java反射机制:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
- 反射就是把java类的各种成分映射成一个个java对象。
要想解剖一个类,必须先要获取该类的字节码文件对象;而解剖使用的就是Class类中的方法,所以先要获取到每一个字节码文件对应的Class类型的对象。 -
一个类有:成员变量、方法、构造方法、包等信息,利用反射技术可以对一个类进行解剖。把各个组成部分映射成一个个对象。
Class对象的由来是将class文件读入内存,并为之创建一个class对象。
二、反射的理解
1.正射
一般情况下,我们使用某个类时必定知道它是什么类,是用来干什么的。于是我们直接对这个类进行实例化,之后使用这个类对象进行操作。
User user = new User(); //直接初始化,正射
user.setAge(20);
2. 反射
反射则是一开始并不知道我们要初始化的类对象是什么,自然无法使用new关键字来创建对象。
反射就是在运行时才知道要操作的类是什么,并且可以运行时获取类的完整构造,并调用对应的方法。
@Test
public void test02() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取类的Class对象
Class c1 = Class.forName("demo01.User");
//根据 Class对象 实例获取 Constructor对象
Constructor constructor = c1.getConstructor();
//根据 Constructor对象 的 newInstance方法 获取反射类对象
Object o = constructor.newInstance();
//获取方法的 Method对象
Method setAgeMethod = c1.getMethod("setAge", Integer.class);
//利用 invoke方法 调用方法
setAgeMethod.invoke(o,13);
Method getAge = c1.getMethod("getAge");
System.out.println(getAge.invoke(o));
}
三、反射的常用API
1.获取反射中的Class对象
- 使用Class.forName静态方法。当我们知道某类的全路径名时,可以使用此方法获取Class类对象。用的最多,但可能抛出 ClassNotFoundException 异常。
Class c1 = Class.forName("java.lang.String");
- 直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高。这说明任何一个类都有一个隐含的静态成员变量class。这种方法只适合在编译前就知道操作的class。
Class c2 = String.class;
- 通过对象调用 getClass() 方法来获取,通常应用在:比如你传过来一个 Object 类型的对象,而我不知道你具体是什么类,用这种方法。
String str = new String("Hello");
Class c3 = str.getClass();
需要注意的是:一个类在 JVM 中只会有一个 Class 实例,即我们对上面获取的 c1、c2和c3进行 equals 比较,发现都是true。
2. 通过反射创建类对象
- 通过 Class对象 的newInstance()方法
Class c1 = User.class;
User user = (User)c1.newInstance();
- 通过 Constructor对象 的newInstance()方法
Class c1 = User.class;
Constructor constructor = c1.getConstructor();
User user = (User)constructor.newInstance();
通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。
Class c1 = User.class;
Constructor constructor = c1.getConstructor(String.class,Integet.class);
User user = (User)constructor.newInstance("aaa",12);
3.通过反射获取类属性、方法、构造器
- 我们通过 Class 对象的 getFields() 方法可以获取 Class 类的属性,但无法获取私有属性。
Class clz = Phone.class;
Field[] fields = clz.getFields();
for (Field field : fields) {
System.out.println(field.getName());
}
输出的结果是:
price
- 如果使用 Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性
Class clz = Phone.class;
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName());
}
输出结果是:
name
price
四、使用反射函数的例子
反射就是把java类中的各种成分映射成一个个对象
User.java
public class User {
private String username;
public void sayHi(String words){
System.out.println(username+":"+words);
}
private String sayHello(String tag){
return tag;
}
}
ReflectDemo.java
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
Class> aClass = Class.forName("com.msj.reflect.User");
System.out.println(aClass.getName());
User user = (User)aClass.newInstance();
// 私有方法
Method sayHello = aClass.getDeclaredMethod("sayHello", String.class);
//访问私有方法、属性都要设置setAccessible(true)
sayHello.setAccessible(true);
Object aaa = sayHello.invoke(user, "aaa");
System.out.println(aaa);
// 公有方法
Method sayHi = aClass.getMethod("sayHi", String.class);
sayHi.invoke(user, "bbb");
// 私有属性
Field username = aClass.getDeclaredField("username");
username.setAccessible(true);
username.set(user,"msj");
sayHi.invoke(user,"bbb");
}
}
五、new 对象和反射得到对象的区别
- 在使用反射的时候,必须确保这个类已经加载并已经连接了。使用new的时候,这个类可以没有被加载,也可以已经被加载。
- new关键字可以调用任何public构造方法,而反射只能调用无参构造方法。
- new关键字是强类型的,效率相对较高。反射是弱类型的,效率较低。
- 反射提供了一种更加灵活的方式创建对象,得到对象的信息。如Spring中AOP等的使用,动态代理的使用,都是基于反射的。