反射可以让我们在运行时动态的加载类,访问、调用类的字段、方法、构造器。反射机制是构建框架的基础,如Spring框架核心,各种RPC框架等。
Class:class的抽象
首先我们需要知道在java中,类的信息是由Class对象来描述的,每个类对应一个Class对象,它表示一个类或者接口(一个annotation也算是一个接口)在运行时的类型信息。Class只有一个private的构造器,你并不能手动的去创建它,Class对象只能由JVM在加载一个类或者是在调用一个方法的时候去创建它。
获取一个类的Class对象有以下几种方式:
- Class.forName(String className)
- obj.getClass()
- ClassName.class
相较于forName()的方式,.class方式能获得编译期的检查更加安全。以上三种方式都可以获得类的Class对象,但是他们之间也有一些差别,Class.forName(String className)默认会对这个类进行初始化操作,而.class的方式则不会自动初始化该Class对象。
1 2 3 4 5 6 7 8 9 10 11 12
| import java.util.Random;
public class InitObj1 {
public static final int value1 = 1;
public static final int value2 = new Random().nextInt();
static{ System.out.println("static block initializing."); } }
|
1 2 3 4 5 6 7
| public class InitObj2 { public static final int value1 = 1;
static{ System.out.println("static block initializing."); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class ClassInitTest {
public static void main(String[] args) throws ClassNotFoundException {
System.out.println("----通过.class获取Class对象-----"); Class c1 = InitObj1.class; System.out.println("created c1"); System.out.println("value1:" + InitObj1.value1); System.out.println("value2:" + InitObj1.value2);
System.out.println("----通过forName获取Class对象-----"); Class c2 = Class.forName("me.l4j.thinkingInJava.reflectionAPI.InitObj2"); System.out.println("created c2"); System.out.println("value1:" + InitObj2.value1); } }
|
输出:
—-通过.class获取Class对象—–
created c1
value1:1
static block initializing.
value2:-2130950913
—-通过forName获取Class对象—–
static block initializing.
created c2
value1:1
从结果可以看到.class获取Class引用不会引发初始化,延迟到了对value2的访问,但是forName()方法会立即引发初始化。
获取类型信息的简单示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays;
public class ReflectString {
public static void main(String[] args) {
Class<?> strC = String.class;
System.out.println(strC.getName());
Class<?>[] inter = strC.getInterfaces(); System.out.println("String implements interfaces:"); for (Class c : inter) System.out.println(" --" + c.getName());
Constructor<?>[] cons = strC.getDeclaredConstructors(); System.out.println("All Constructors:"); for (Constructor csr : cons) { System.out.println(" constructor name:"+csr.getName()); System.out.println(" constructor modifier:" + Modifier.toString(csr.getModifiers())); Arrays.stream(csr.getParameterTypes()).forEach(x-> System.out.println(" param:"+x.getName()));
System.out.println(" ------------------------------------"); }
Field[] fields = strC.getDeclaredFields();
System.out.println("All fields:"); for (Field f : fields) { System.out.println(" name:" + f.getName()); System.out.println(" modifier:"+Modifier.toString(f.getModifiers())); System.out.println(" ------------------------------------"); }
Method[] methods = strC.getDeclaredMethods(); System.out.println("All methods:"); for (Method m : methods){ System.out.println(" name:"+m.getName()); System.out.println(" modifier:"+Modifier.toString(m.getModifiers())); System.out.println(" returnType:"+m.getReturnType().getName()); Arrays.stream(m.getParameterTypes()).forEach(x-> System.out.println(" param:"+x.getName())); Arrays.stream(m.getAnnotations()).forEach(x-> System.out.println(" annotation:"+x.annotationType().getName())); System.out.println(" ------------------------------------"); }
CharSequence cs = new String("str"); Class csClass = cs.getClass(); System.out.println(csClass.getName());
} }
|
类型判断
对类型的判断主要有istanceof关键字、Class.isInstance(Object obj)方法和”==”三种方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import java.io.Serializable;
public class ClassType { public static void main(String[] args) {
CharSequence str = new String(); CharSequence sb = new StringBuilder();
System.out.println("str instanceof CharSequence:" + (str instanceof CharSequence)); System.out.println("str instanceof String:" + (str instanceof String)); System.out.println("str instanceof Serializable:" + (str instanceof Serializable));
System.out.println();
System.out.println("CharSequence.class.isInstance(str): "+CharSequence.class.isInstance(str)); System.out.println("String.class.isInstance(str): "+String.class.isInstance(str));
System.out.println();
System.out.println("String.class == str.getClass(): " + (String.class == str.getClass())); System.out.println("CharSequence.class == str.getClass(): " + (CharSequence.class == str.getClass()));
System.out.println("str.getClass(): "+str.getClass()); System.out.println("sb.getClass(): "+sb.getClass()); } }
|
instanceof
和isInstance()
保持了类型的概念(是否是这个类,或是否是这个类的派生类),而==比较的是实际的class对象。
类型转换
因为知道一只狗一定是一只动物,所以编译器允许自由的向上转型,不需要任何显示的转换。但是一直动物是不是一只狗这是不确定的,在进行“向下转型”的时候需要显式的进行转换(Dog)object
,如果这个object并不属于Dog,那么在运行时会触发ClassCastException
异常。在进行“向下转型”时我们可以先使用instanceof
或者isInstance()
进行类型检查,以避免触发异常。
在JAVA SE5开始我们可以使用Class引用的cast()
进行转型,暂没有发现这种转型语法的特异功能。
1 2 3
| Animal a = new Dog(); Class<?> dogCls = Dog.class; Dog d = dogCls.cast(a);
|
反射
通过java反射机制,我们可以动态的创建对象,体现出很大的灵活性。相较于静态编译,通过反射执行的操作要慢得多。
java在java.lang.reflect包下提供了反射的工具和接口,reflect包只是java反射机制的一部分,其他还包括前面提到的Class类以及Object类等。
ReflectionAPI简介
在reflect包中Field、Method和Constructor这三个类比较常用。我们可以通过Constrcutor创建新的对象(Class引用的newInstance
方法也可以创建对象,但是需要这个类有无参构造器),利用Field中的set()
和get()
方法来读取和修改字段,以及使用invoke()
方法来调用与Method关联的方法。下面的实例展示了这三个类的基本用法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| import java.util.List;
public class Student { private String name; private int age; private List<String> courses;
public Student(String name, int age) { this.name = name; this.age = age; } public Student(){ this.name = "John Doe"; this.age = -1; } public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
public List<String> getCourses() { return courses; }
public void setCourses(List<String> courses) { this.courses = courses; }
@Override public String toString() { return "Student{" + "name='" + name + '\'' + ", age=" + age + ", courses=" + courses + '}'; } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| import java.lang.reflect.*; import java.util.Arrays;
public class ReflectAPI { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
Class<Student> stCls = Student.class;
Student sDefault = stCls.newInstance(); System.out.println("调用无参构造器创建Student对象: "+sDefault);
Constructor ctr = stCls.getConstructor(String.class, int.class); Student s = (Student)ctr.newInstance("john", 20); System.out.println(s);
Field fld = stCls.getDeclaredField("courses");
fld.setAccessible(true); fld.set(s, Arrays.asList("计算机网络","操作系统")); System.out.println(fld.get(s));
Method m = stCls.getMethod("setAge", int.class); m.invoke(s, 24); System.out.println(s); } }
|
动态代理
首先了解下什么是代理模式:其目的是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。而动态代理可以动态的创建代理并动态的处理对所代理方法的调用。
JDK实现的动态代理只能代理实现了某接口的被代理对象。java动态代理主要用到java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口。动态代理的实现主要包括以下步骤:
1、实现InvocationHandler接口创建调用处理器
2、调用Proxy的newInstance()方法创建动态代理实例
以下是一个动态代理的示例程序:
1 2 3
| public interface WorkerInterface { void doJob(String job); }
|
1 2 3 4 5 6
| public class Worker implements WorkerInterface{ @Override public void doJob(String job) { System.out.println("doing "+job); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;
public class MyProxyHandler implements InvocationHandler {
private Object proxied;
public MyProxyHandler(Object proxied) { this.proxied = proxied; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("----------------info------------------------"); System.out.println("Proxy: "+proxy.getClass()); System.out.println("Method: "+method); System.out.println("args: "+args); System.out.println("----------------info------------------------"); return method.invoke(proxied, args); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class SimpleProxy{ public static void doJob(WorkerInterface wi) { System.out.println("Get ready to start work"); wi.doJob("Whitewashing"); System.out.println("Finish the work"); }
public static void main(String[] args) { Worker w = new Worker();
WorkerInterface proxy = (WorkerInterface)Proxy.newProxyInstance(WorkerInterface.class.getClassLoader(), new Class[]{WorkerInterface.class}, new MyProxyHandler(w));
doJob(proxy); } }
|
我们在调用Proxy.newInstance方法时实际上是经历了一下三步:
1、创建代理类的类对象Class> cl = getProxyClass0(loader, intfs)
2、反射从生成的类对象cls中获取构造器对象:Constructor> cons = cl.getConstructor(constructorParams)
3、用上一步获取的构造器对象创建动态代理类实例cons.newInstance(new Object[]{h})
通过动态代理我们可以为目标类预处理消息、添加修饰等。