反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。常用在框架上
常用API
获取Class对象
//获取Class对象实例 ,加载类进JVM内存
Class clz = Class.forName("java.lang.String");//使用全类名
Class clz = String.class;//通过类名的属性class获取
String str = new String("Hello");
Class clz = str.getClass(); //通过对象.getClass()获取
//一个对象在运行期间,只能由一个class对象,所以上面三种获取的都是同一个class对象
通过包装类的TYPE属性
对于基本类型和 void 都有对应的包装类。在包装类中有一个静态属性 TYPE,保存了该来的类类型。以 Integer 类为例,其源码中定义了如下的静态属性:
/**
* The {@code Class} instance representing the primitive type
* {@code int}.
*
* @since JDK1.1
*/
@SuppressWarnings("unchecked")
public static final Class<Integer> TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
生成 Class 类实例的方法:
Class cls1 = Integer.TYPE;
Class cls2 = Void.TYPE;
JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,而是第一次需要用到class时才加载。
注意一下Class
实例比较和instanceof
的差别:
Integer n = new Integer(123);
boolean b1 = n instanceof Integer; // true,因为n是Integer类型
boolean b2 = n instanceof Number; // true,因为n是Number类型的子类
boolean b3 = n.getClass() == Integer.class; // true,因为n.getClass()返回Integer.class
boolean b4 = n.getClass() == Number.class; // false,因为Integer.class!=Number.class
获取其他Class
Class.getSuperclass()//获取该类的父类
Class.getClasses() //获取该类所有公共类、接口、枚举组成的Class 数组,包括继承的
Class.getDeclaredClasses()//获取该类显式声明的所有类、接口、枚举组成的 Class 数组
(Class/Field/Method/Constructor).getDeclaringClass()//获取该类/属性/方法/构造函数所在的类
Class.getInterfaces(); //获取接口,只返回当前类直接实现的接口类型,并不包括其父类实现的接口类型:
//获取接口的父接口要用getInterfaces()
/*要判断一个向上转型是否成立,可以调用isAssignableFrom():*/
// Number n = ?
Number.class.isAssignableFrom(Integer.class); // true,因为Integer可以赋值给Number
获取Field,Method,Constructor
Member
是一个接口,表示 Class
的成员, Field、Method、Constructor
都是其实现类。
AccessibleObject
是 Field、Method、Constructor
三个类共同继承的父类,它提供了将反射的对象标记为在使用时取消默认 Java
语言访问控制检查的能力。通过 setAccessible
方法可以忽略访问级别,从而访问对应的内容。并且 AccessibleObject
实现了 AnnotatedElement
接口,提供了与获取注解相关的能力。
//Class对象功能
//获取成员变量
Field[] getFields() //获取所有public修饰的成员变量
Field getField(String name) //获取指定名称的 public修饰的成员变量
Field[] getDeclaredFields() //获取所有的(包含private修饰的)字段,不包括继承的字段
Field getDeclaredField(String name) //获取指定名称的
//获取构造方法
Constructor<?>[] getConstructors() // 返回所有具有public访问权限的构造函数的Constructor对象数组
Constructor<T> getConstructor(Class<?>... parameterTypes) //返回指定参数类型、具有public访问权限的构造函数对象
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) //返回指定参数类型、所有声明的(包括private)构造函数对象
Constructor<?>[] getDeclaredConstructors() //返回所有声明的(包括private)构造函数对象
//获取成员方法
Method[] getMethods() //返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
Method getMethod(String name, Class<?>... parameterTypes) //返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
Method[] getDeclaredMethods() //返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
Method getDeclaredMethod(String name, Class<?>... parameterTypes) //返回一个指定参数的Method对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
//获取全类名
String getName()
创建对象
//通过反射创建类对象
Class clz = String.class;
String s = (String)clz.newInstance();//直接通过class对象,只适合无参构造方法
//通过 Constructor 对象的 newInstance() 方法
Constructor constructor = clz.getConstructor(Object... initargs);//获取构造器
String s = constructor.newInstance("hello");
成员变量 Field
void set(Object obj, Object value) //设置值
void get(Object obj) //获取值
void setAccessible(true) //true的值表示反射对象应该在使用时抑制Java语言访问检查。 false的值表示反映的对象应该强制执行Java语言访问检查。
getName() //返回字段名称,例如,"name";
Class getType() //返回字段类型,也是一个Class实例,例如,String.class;
getModifiers() //返回字段的修饰符,它是一个int,不同的bit表示不同的含义。
方法对象 Method
Object invoke(Object obj, Object... args) //执行方法
//appleMethod.invoke(appleObj, 14);
String getName() //获取方法名
getReturnType()//返回方法返回值类型,也是一个Class实例,例如:String.class;
getParameterTypes()//返回方法的参数类型,是一个Class数组,例如:{String.class, int.class};
getModifiers()//返回方法的修饰符,它是一个int,不同的bit表示不同的含义。
/*调用静态方法*/
// 获取Integer.parseInt(String)方法,参数为String:
Method m = Integer.class.getMethod("parseInt", String.class);
// 调用该静态方法并获取结果:
Integer n = (Integer) m.invoke(null, "12345");
Method.setAccessible(true);
/*对于多态,会调用传进去的对象的方法,(如果覆写了父类的方法的话)*/
Class类
Class 类有以下的特点:
- Class 类也是类的一种,class 则是关键字。
- Class 类只有一个私有的构造函数,只有 JVM 能够创建 Class 类的实例。
- 对于同一类的对象,在 JVM 中只有唯一一个对应的 Class 类实例来描述其类型信息。(同一个类:即包名 + 类名相同,且由同一个类加载器加载)
.class 文件存储了一个 Class 的所有信息,比如所有的方法,所有的构造函数,所有的字段(成员属性)等等。JVM 启动的时候通过 .class 文件会将相关的类加载到内存中,过程如下: