Bootstrap

Java反射机制

一、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 方法
通过反射调用方法时,仍然遵循多态原则。

;