1、反射机制
在Java中,反射指的是在运行时动态的获取一个类的信息,创 建该类的对象,或者访问该对象的成员(成员变量,方法等)。
2、运行的过程
当我们通过java命令来运行某个类时,会经历如下的过程:
类的加载 由类加载器将指定的class文件载入。载入仅执行一 次。类加载后,就会创建一个对应类的Class类的实例。
链接 链接可以分为验证,准备,与解析。验证用于验证之前 所加载class文件结构上是否符合规范。准备阶段则准备为类 结构分配相应的空间。解析则是把class文件中的符号引用替 换为真实的实际引用。
初始化 为类的静态成员进行相应的初始化赋值。(声明时初 始化与使用静态初始化块初始化)。
3、时间点
以上只是基本的过程说明。但是, Java虚拟机规范( JVMS) 没有规定具体的执行时刻,例如,对于初始化而言,可以在 链接之后立即初始化,也可以直到使用类的相关成员时才进 行初始化。
无论如何, JVMS都要求,在第一次使用类的静态成员时,该 成员一定是初始化完成的。也就是说,类的静态成员一定会 在第一次使用前得到初始化。
4、ClassLoader类加载器
Java使用ClassLoader(子类)来实现类的加载。 ClassLoader 调用loadClass方法来加载参数指定的类型。该方法会返回一个 指定类型的Class对象。
程序:通过ClassLoader获取系统类加载器。
5、Class
每个类在加载后都会由系统创建一个Class对象, Class对象存放 类的相关信息,如方法,变量,构造器等。
通过Class类的静态方法forName方法可以获取指定类型的Class 对象。这与ClassLoader类的loadClass方法类似。不同的是, forName默认是初始化类的。
说明:加载类不代表会初始化类。
6、获取Class对象
可以通过以下方式获取Class对象:
通过ClassLoader对象的loadClass方法。 (Class<?>)
通过Class类的静态方法forName方法。 (Class<?>)
通过类名.class获取。 (Class<T>)
通过对象.getClass方法获取。 (Class<? extends T>)
7、创建对象
可以通过Class对象的newInstance方法来创建对象。(类似于 调用无参的构造器)。
如果要创建含有参数的构造器,需要通过Class对象的 getConstructor(只能获取public权限)或 getDeclaredConstructor来获取相应的构造器对象
( Constructor),然后通过构造器的newInstance方法创建对 象。
说明:可以调用Constructor对象的setAccessible方法设置访问 权限。
8、调用方法
通过Class对象的getMethod或getDeclaredMethod获取相关的 方法对象Method,然后通过Method对象的invoke方法调用。
/* * ClassLoader的loadClass方法与Class的forName方法: * 二者都可以加载参数的类型,不同的是: * loadClass方法仅加载类型,不会对类型进行初始化操作。 * 而forName方法不仅加载类型,而且会对类型进行初始化。即类中 * 声明的静态成员会得到初始化(执行)。 */ package day17; public class ClassTest { public static void main(String[] args) { try { // 加载参数指定的类型。并且会对指定的类型进行 // 初始化。 // Class<?> c = Class.forName("day17.Value"); // 第二个参数可以指定是否在加载后对类进行初始化。 // 第三个参数类型加载器。 Class.forName("day17.Value", false, ClassLoader.getSystemClassLoader()); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
/* * 获取Class对象 * 1 ClassLoader类的loadClass方法,返回Class<?> * 2 Class类的forName方法,返回Class<?> * 3 通过类型T.class,返回Class<T> * 4 通过Object类的getClass方法,返回Class<? extends T> */ package day17; public class ClassObject { public static void main(String[] args) { ClassLoader cl = ClassLoader.getSystemClassLoader(); try { Class<?> c = cl.loadClass("day17.Value"); Class<?> c2 = Class.forName("day17.Value"); Class<Value> c3 = Value.class; Value v = new Value(); Class<? extends Value> c4 = v.getClass(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
package day17; public class CreateObject { public static void main(String[] args) { CreateObject o = new CreateObject(); Object obj = o.createObject("day17.Value"); if (obj instanceof Value) { Value v = (Value) obj; } } // 通过反射动态去创建一个对象,并将该对象返回。 public Object createObject(String type) { // Object o = new type(); Object o = null; try { Class<?> c = Class.forName(type); // 通过无参的构造器创建对象。 o = c.newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return o; } }
/* * 通过Constructor类型创建 * 相关类的对象。 * * Class类的getConstructor与getDeclaredConstructor: * getConstructor只能获得声明为public的构造器。 * getDeclaredConstructor可以获得任意访问权限的构造器。 */ package day17; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class CreateObject2 { public static void main(String[] args) { /* * Value v = new Value(100); System.out.println(v.getX()); */ Class<Value> c = Value.class; // 获取声明为public访问权限的构造器 // c.getConstructor(parameterTypes) try { // 获得任意访问权限的构造器,参数为相应构造器参数对应的 // Class类型。 Constructor<Value> con = c.getDeclaredConstructor(int.class); Value v = con.newInstance(100); System.out.println(v.getX()); // 获得私有的构造器 con = c.getDeclaredConstructor(String.class); // 设置为可访问的,这样可以访问私有成员(构造器)。 con.setAccessible(true); v = con.newInstance("abcs"); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }
package day17; public class ClassLoaderTest { public static void main(String[] args) { // 获得系统类加载器 ClassLoader cl = ClassLoader.getSystemClassLoader(); // 主动加载一个类型,需要提供类型的全限定名。 try { // 加载类型后,就会创建该类型的Class对象。 // 返回加载类型的Class对象。 // 加载一个类型,不会初始化该类型。(静态初始化不会 // 得到执行。 Class<?> c = cl.loadClass("day17.Value"); // 获得类型的加载器。 System.out.println(c.getClassLoader()); // 启动类加载器负责加载API(JAVA类库提供的)类型,启动类 // 加载时不是Java语言实现的,因此会输出null。 System.out.println(String.class.getClassLoader()); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } class Value { static { // System.out.println("静态初始化块执行"); } public void f(int k) { System.out.println(k); } public int g(int k) { return k * k; } public static void staticF() { System.out.println("静态方法"); } private int x; @Override public String toString() { return "Value [x=" + x + "]"; } public int getX() { return x; } public void setX(int x) { this.x = x; } public Value() { } public Value(int x) { this.x = x; } private Value(String s) { } }
/* * 通过反射动态调用一个方法。 */ package day17; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class MethodTest { /* * Value v = new Value(); v.f(10); */ public static void main(String[] args) { Class<Value> c = Value.class; try { Constructor<Value> con = c.getDeclaredConstructor(); Value v = con.newInstance(); // 获得相关方法的对象 Method m = c.getDeclaredMethod("f", int.class); // 调用方法,第一个参数为调用该方法的对象, // 第二个参数为方法的实际参数列表。 m.invoke(v, 10); // 有返回值的方法。 Method m2 = c.getDeclaredMethod("g", int.class); // 获得方法的返回值。 Object rtnValue = m2.invoke(v, 2); System.out.println(rtnValue); // 静态方法 Method m3 = c.getDeclaredMethod("staticF"); // 因为静态方法调用不需要对象的引用,因此,可以传递null值。 m3.invoke(null); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }
9、访问成员变量
通过Class对象的getField或getDeclaredField方法获取相关的成 员变量Field对象(域对象),然后通过Field对象的set与get方 法设置与获取变量的值。
说明: set与get配有相关的setXXX与getXXX, XXX为类型,可以 简化操作。
10、安全管理器
我们可以设置安全管理器, 进而限制对私有成员的访问。
/* * 通过反射动态获得成员变量。 */ package day17; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; public class FieldTest { public static void main(String[] args) { Class<Value> c = Value.class; try { Constructor<Value> con = c.getDeclaredConstructor(); Value v = con.newInstance(); Field f = c.getDeclaredField("x"); f.setAccessible(true); // 设置Field对象所代表的成员变量的值。 f.set(v, 99); // 获取Field对象所代表的成员变量的值。 // int k = f.get(v); System.out.println(f.get(v)); // 如果成员变量是基本数据类型,我们可以使用 // 更加简便的getT或setT方法,T为相应的 // 基本数据类型。 f.setInt(v, 20); int x = f.getInt(v); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } }
/* * 通过反射,我们可以访问到类的私有成员,这将 * 打破类的封装性,我们可以设置安全管理器来 * 解决这个问题。 */ package day17; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class Security { public static void main(String[] args) { // 设置安全管理器 System.setSecurityManager(new SecurityManager()); Class<Value> c = Value.class; try { Constructor<Value> con = c.getDeclaredConstructor(String.class); con.setAccessible(true); con.newInstance("ab"); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }