一、Java反射机制
是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在Java中,只要给定类的名字,就可以通过反射机制来获得类的所有信息。
例如:在主流的ORM框架的实现中,运用Java反射机制可以读取任意一个JavaBean的所有属性,或者给这些属性赋值。
二、Class类:
由于 JVM 为每个加载的 class 创建了对应的 class 实例,并在实例中保存了该 class 的
所有信息,包括类名、包名、父类、实现的接口、所有方法、字段(成员变量)等,因此,如果获
取了某个 class 实例,我们就可以通过这个 class 实例获取到该实例对应的 class 的所有信
息。这种通过 class 实例获取 class 信息的方法称为反射(Reflection)。
如何获取class信息呢?有三种方法
//方式1:通过类名
Class stringClass1=String.class;
//方式2:通过Class类的forName()方法
Class stringClass2=Class.forName("java.lang.String");
//方式3:通过对象调用getClass()方法
Class stringClass3="".getClass();
这三种方式获取的 class实例都是同一个实例,因为JVM 对每个加载的 class只创建一个 class 实例来表示它的类型
如果获取到了一个class 实例,我们就可以通过该 class 实例来创建对应类型的实例:
String s=(String)stringClass1.newInstance();
上述代码相当于 new String()。通过 Class.newInstance()可以创建类实例,它的局限是:只能调用 public的无参数构造方法。带参数的构造方法,或者非public的构造方法都无法通过 Class.newInstance()被调用。
三、Class常用方法:
四、动态加载机制
JVM 在执行 Java 程序的时候,并不是一次性把所有用到的 class 全部加载到内存,而是
第一次需要用到 class 时才加载。
总结:
- JVM 为每个加载的 class 及 interface 创建了对应的 class 实例来保存 class 及 interface 的所有信息;
- 获取一个 class 对应的 class 实例后,就可以获取该 class 的所有信息
- 通过 class实例获取 class 信息的方法称为反射(Reflection);
- JVM 总是动态加载 class,可以在运行期根据条件来控制加载 class;
五、调用构造方法
上述说到:我们通常使用new操作符创建新的实例,如果通过反射来创建新的实例,可以调用Class提供的newInstance()方法,调用newInstance()方法的局限是,它只能调用该类的public无参构造方法。如果构造方法带有参数,或者不是public,就无法直接通过Class.newInstance()来调用。
解放办法如下:
5.1Constructor类
为了调用任意的构造方法,Java的反射 API 提供了constructor 对象,它包含一个构造
方法的所有信息,可以创建一个实例。
实例:
Class clz=Class.forName("com.wei.demo01.Document");
//方式1:直接通过Class对象,调用newInstance()方法
Object objx=clz.newInstance();//相当于在执行无参构造方法
//方式2:通过构造器(构造方法)
//无参构造方法
Constructor constructor1=clz.getDeclaredConstructor();//获取无参构造方法
Object obj1=constructor1.newInstance();//执行构造器(构造方法),创建对象
//有参构造方法
Constructor constructor2=clz.getDeclaredConstructor(String.class);//获取有参构造方法
Object obj2=constructor2.newInstance("两京十五日");
Constructor constructor3=clz.getDeclaredConstructor(int.class);//获取有参构造方法
Object obj3=constructor3.newInstance(12);
Constructor constructor4=clz.getDeclaredConstructor(String.class,int.class);//获取有参构造方法
Object obj4=constructor4.newInstance("风起陇西",24);
通过 Class 实例获取 Constructor 的方法如下:
- getConstructor(class...):获取某个public的Constructor
- getDeclaredconstructor(class...):获取某个定义的Constructor
- getconstructors():获取所有public的Constructor;
- getDeclaredconstructors():获取所有定义的Constructor
总结:
Constructor 对象封装了构造方法的所有信息;
通过 class 实例的方法可以获取 constructor实例:getconstructor(),getConstructors(),getDeclaredConstructors(),getDeclaredConstructor()
通过 constructor 实例可以创建一个实例对象:newInstance(0bject...parameters);通过设置 setAccessible(true)来访问非 public 构造方法。
六、访问字段
6.1获取Field字段
对任意的一个 Object 实例,只要我们获取了它的Class,就可以获取它的一切信息。比如
通过一个 Class 实例获取字段信息。 Class 类提供了以下几个方法来获取字段:
- Field getField(name):根据字段名获取当前类中某个 public 的field(包括父类)
- Field getDeclaredField(name):根据字段名获取当前类中定义的某个 field(不包括父类)
- Field[]getFields():获取所有 public的field (包括父类)
- Field[]getDeclaredFields():获取当前类中定义的所有 field(不包括父类)
6.2设置字段值
通过 Field 实例既然可以获取到指定实例的字段值,自然也可以设置字段的值。设置字段值是通过 Field.set(0bject,object)实现的,其中第一个 0bject 参数是指定的实例,第二个 object 参数是待修改的值。示例代码如下:
//反射的方式
Class clz=Class.forName("com.wei.demo01.Document");//获取类型信息
Object obj=clz.newInstance();//创建对象
//获取指定名称的成员变量
Field nameField=clz.getDeclaredField("name");
Field priceField=clz.getDeclaredField("price");
//访问私有的成员变量,必须设置权限
nameField.setAccessible(true);
priceField.setAccessible(true);
//使用成员变量,将指定数据存入对象中
nameField.set(obj, "长安十二时");
priceField.setInt(obj, 128);
System.out.println(obj);
总结:
Java 的反射 API 提供的 Field 类封装了字段的所有信息:
通过 class 实例的方法可以获取 Field 实例:getField(),getFields(),getDeclaredField(),getDeclaredFields();
通过 Field 实例可以获取字段信息:getName(),getType(),getModifiers();
通过 Field 实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用 setAccessible(true)来访问非public字段
通过反射读写字段是一种非常规方法,它会破坏对象的封装。
七、调用方法
7.1Method类
我们已经能通过 Class实例获取所有 Field 对象,同样的,可以通过 Class 实例获取所有
方法(Method 类型的对象)。Class类提供了以下几个方法来获取 Method:
- Method getMethod(name,Class...):获取某个public的Method(包括父类)
- Method getDeclaredMethod(name,Class...):获取当前类的某个 Method(不包括父类)
- Method[] getMethods():获取所有 public 的 Method(包括父类)
- Method[] getDeclaredmethods():获取当前类的所有 Method(不包括父类)
实例代码如下:
//反射的方式
Class clz=Class.forName("com.wei.demo01.Document");//获取类型信息
Object doc1=clz.newInstance();//创建对象
//获取指定名称和参数类型的方法
Method setNameMethod=clz.getMethod("setName", String.class);
Method setPriceMethod=clz.getMethod("setPrice", int.class);
//执行方法
//doc1.setName("海底两万里");
setNameMethod.invoke(doc1,"海底两万里");
//doc1.setPrice(100);
setPriceMethod.invoke(doc1, 125);
System.out.println(doc1);
总结:
Java 的反射 API 提供的 Method 对象封装了方法的所有信息:
通过 Class 实例的方法可以获取 Method 实例:getMethod(),getMethods(),getDeclareMethod(),getDeclaredMethods()
通过 Method 实例可以获取方法信息:getReturnType(),getName(),getParameterTypes(),getModifiers()
通过 Method 实例可以调用某个对象的方法:0bject invoke(0bject instance,object...parameters)
通过设置 setAccessible(true)来访问非 public 方法
通过反射调用方法时,仍然遵循多态原则。