反射
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();
}
}
}

浙公网安备 33010602011771号