一、反射概述
反射 (Reflection) 是 Java 的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性。
通过反射,我们可以在运行时获得程序或程序集中每一个类型的成员和成员的信息。程序中一般的对象的类型都是在编译期就确定下来的,而 Java 反射机制可以动态地创建对象并调用其属性,这样的对象的类型在编译期是未知的。所以我们可以通过反射机制直接创建对象,即使这个对象的类型在编译期是未知的。
反射的核心是 JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁。
Java 反射主要提供以下功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用private方法);
- 在运行时调用任意一个对象的方法
二、原理
所有的java类都是继承了object这个类,在object这个类中有一个方法:getclass().这个方法是用来取得该类已经被实例化了的对象的该类的引用,这个引用指向的是Class类的对象。我们自己无法生成一个Class对象(构造函数为private),而 这个Class类的对象是在当各类被调入时,由 Java 虚拟机自动创建 Class 对象,或通过类装载器中的 defineClass 方法生成。我们生成的对象都会有个字段记录该对象所属类在CLass类的对象的所在位置。如下图所示:
三、反射的基本运用
1、获得 Class 对象
(1)使用 Class 类的 forName
静态方法:
public static Class<?> forName(String className)
//eg
Class<?> clazz1 = Class.forName(“java.lang.String”);
(2)直接获取某一个对象的 class:
Class<?> klass = int.class;
Class<?> classInt = Integer.TYPE;
(3)调用某个对象的 getClass()
方法
StringBuilder str = new StringBuilder("123");
Class<?> klass = str.getClass();
2、判断是否为某个类的实例
我们用 instanceof 关键字来判断是否为某个类的实例。同时我们也可以借助反射中 Class 对象的 isInstance() 方法来判断是否为某个类的实例,它是一个 native 方法:
public native boolean isInstance(Object obj);
3、创建实例
(1)使用Class对象的newInstance()方法来创建Class对象对应类的实例
Class<?> c = String.class;
Object str = c.newInstance();
(2)先通过Class对象获取指定的Constructor对象,再调用Constructor对象的newInstance()方法来创建实例。这种方法可以用指定的构造器构造类的实例
//获取String所对应的Class对象
Class<?> c = String.class;
//获取String类带一个String参数的构造器
Constructor constructor = c.getConstructor(String.class);
//根据构造器创建实例
Object obj = constructor.newInstance("23333");
System.out.println(obj);
4、获取方法和调用
Class.getMethods():获取当前类及所有继承的父类的public修饰的方法。仅包括public
Class.getMethod 方法返回一个特定的方法
public Method getMethod(String name, Class<?>... parameterTypes)
Class.getDeclaredMethods():获取当前类的所有方法,包括public/private/protected/default修饰的方法。(但不包括继承的方法)
Class.getDeclaredMethod 方法返回一个特定的方法
public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
获取方法具体示例如下:
class methodClass {
public final int fuck = 3;
public int add(int a,int b) {
return a+b;
}
public int sub(int a,int b) {
return a+b;
}
}
public static void main(String[] args) throws llegalAccessException,InstantiationException, NoSuchMethodException {
Class<?> c = methodClass.class;
Object object = c.newInstance();
Method[] methods = c.getMethods();
Method[] declaredMethods = c.getDeclaredMethods();
//获取methodClass类的add方法
Method method = c.getMethod("add", int.class, int.class);
}
调用方法使用invoke方法
public Object invoke(Object obj, Object... args)
//创建methodClass的实例
Object obj = klass.newInstance();
//获取methodClass类的add方法
Method method = klass.getMethod("add",int.class,int.class);
//调用method对应的方法 => add(1,4)
Object result = method.invoke(obj,1,4);
5、获取类的成员变量(字段)信息
- getFiled:访问公有的成员变量
- getDeclaredField:所有已声明的成员变量,但不能得到其父类的成员变量
getFileds 和 getDeclaredFields 方法用法同上(参照 Method)。
6、利用反射创建数组
数组在Java里是比较特殊的一种类型,它可以赋值给一个Object Reference。
public static void testArray() throws ClassNotFoundException {
Class<?> cls = Class.forName("java.lang.String");
Object array = Array.newInstance(cls,25);
//往数组里添加内容
Array.set(array,0,"hello");
Array.set(array,1,"Java");
Array.set(array,2,"fuck");
Array.set(array,3,"Scala");
Array.set(array,4,"Clojure");
//获取某一项的内容
System.out.println(Array.get(array,3));
}
其中的Array类为java.lang.reflect.Array类。我们通过Array.newInstance()创建数组对象,它的原型是:
public static Object newInstance(Class<?> componentType, int length)
throws NegativeArraySizeException {
return newArray(componentType, length);
}
其中newArray 方法和Array 类的 set
和 get
方法都为 native 方法。
具体参考深入解析Java反射和Java反射
详细推荐阅读反射、注解、动态代理