JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
RTTI(Run-Time Type Indentification) 运行时类型识别,就是在运行时识别使用一个对象/类的信息,不同于编译时已经确定好的类型
反射就是将一个类中的各种属性和方法等解剖为一个个对象
Class类
Class(java.lang)类 Java应用里每一个类或接口都是Class类的实例,每个Java类在jvm都表现为一个Class对象,基本数据类型和void数组也是Class类的对象
- public final class Class
implements java.io.Serializable, - GenericDeclaration,
- Type,
- AnnotatedElement {
- private static final int ANNOTATION= 0x00002000;
- private static final int ENUM = 0x00004000;
- private static final int SYNTHETIC = 0x00001000;
-
- private static native void registerNatives();
- static {
- registerNatives();
- }
-
- /*
- * Private constructor. Only the Java Virtual Machine creates Class objects.
- * This constructor is not used and prevents the default constructor being
- * generated.
- */
- private Class(ClassLoader loader) {
- // Initialize final field for classLoader. The initialization value of non-null
- // prevents future JIT optimizations from assuming this final field is null.
- classLoader = loader;
- }
- 复制代码
阅读源码可得:
class
关键字private
,仅由JVM调用创建和加载类加载
类加载机制图解
在Java中,Class类和java.lang.reflect类库共同支持反射技术
其中,Constructor
类表示Class对象所表示类的构造方法,Field
类表示成员变量,Method
类表示成员方法等
方法介绍
方法 | 说明 |
---|---|
forName() | (1)获取Class对象的一个引用,但引用的类还没有加载(该类的第一个对象没有生成)就加载了这个类。 |
(2)为了产生Class引用,forName()立即就进行了初始化。 | |
Object-getClass() | 获取Class对象的一个引用,返回表示该对象的实际类型的Class引用。 |
getName() | 取全限定的类名(包括包名),即类的完整名字。 |
getSimpleName() | 获取类名(不包括包名) |
getCanonicalName() | 获取全限定规范 的类名(包括包名,一般在log时使用) |
isXXX() | 判断是否为某一类型,比如isInterface()是否为接口,isEnum()等 |
getXXX() | 获取类的某一个信息,比如getFiled(String name)获取名字为name的属性,getMethod(),getAnnotation()等 |
getXXXs() | 获取某个信息的数组,比如getInterfaces(),getMethods,getFileds() 注意:后两个方法会获取到包括父类所拥有的全部信息 |
getDeclredXXX/s() | 获取已经声明的信息,仅包含本类,不包含父类 ,比如:getDeclaredAnnotations() ,getDeclaredMethod() |
newInstance() | 返回一个Oject对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器。 |
Class对象的读取
在类加载的时候,JVM会创建一个class对象 class对象是可以说是反射中最常用的,获取class对象的方式的主要有三种
package.ClassName$InnerClass
- logger.info("根据类名: \t"+test.class); //根据类名: class zjamss.test
- logger.info("根据对象: \t"+this.getClass()); //根据对象: class zjamss.test
- logger.info("根据全限定类名: \t"+Class.forName("zjamss.test")); //根据全限定类名: class zjamss.test
- Class test = zjamss.test.class;
- // 常用的方法
- logger.info("获取全限定类名:\t" + test.getName()); //获取全限定类名: zjamss.test
- logger.info("获取类名:\t" + test.getSimpleName()); //获取类名: test
- logger.info("实例化:\t" + test.newInstance()); //实例化: zjamss.test@17d10166
- 复制代码
Construct类及使用方法
Constructor类存在于反射包(java.lang.reflect)中,反映的是Class 对象所表示的类的构造方法。
- //Constructor使用
- @Test
- public void t2() throws Exception {
- Class> clazz = Class.forName("zjamss.reflect.User");
- User user = (User) clazz.newInstance(); //调用无参构造函数
- System.out.println(user); //User{name='null', age=0}
-
- Constructor cs1 = clazz.getConstructor(int.class);
- User user1 = (User) cs1.newInstance(10); //调用一个参数的构造函数
- System.out.println(user1); //User{name='null', age=10}
-
- Constructor cs2 = clazz.getDeclaredConstructor(String.class,int.class);
- User user2 = (User) cs2.newInstance("12", 12); //调用两个参数的构造函数
- System.out.println(user2); //User{name='12', age=12}
-
- Constructor[] constructors = clazz.getDeclaredConstructors(); //获取User类里所有的构造方法
- for(Constructor constructor : constructors){
- System.out.print(constructor.toGenericString+":\t");
- Class[] parameterTypes = constructor.getParameterTypes(); //获取构造方法的所有形参类型
- for(Class param : parameterTypes){
- System.out.print(param.getCanonicalName()+"\t");
- }
- System.out.println();
- }
- }
-
- //public zjamss.reflect.User(int): int
- //public zjamss.reflect.User(java.lang.String,int): java.lang.String int
- //public zjamss.reflect.User():
- 复制代码
-
- public class User{
- private String name;
- public int age;
-
- public User() {
- }
-
- public User(String name, int age) {
- this.name = name;
- this.age = age;
- }
- public User(int age) {
- this.age = age;
- }
-
- @Override
- public String toString() {
- return "User{" +
- "name='" + name + '\'' +
- ", age=" + age +
- '}';
- }
- }
- 复制代码
Field类及其用法
Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类静态字段或实例字段。
- //Field
- @Test
- public void t3() throws Exception{
- Class clazz = User.class;
- //获取指定字段名称且修饰符为public的字段 包括父类
- Field age = clazz.getField("age");
- System.out.println("age: "+age); //age: public int zjamss.reflect.User.age
-
- //获取所有修饰符为public的字段 包括父类
- Field[] fields = clazz.getFields();
- for(Field field : fields){
- System.out.println(field.getName()+": "+field.getDeclaringClass());
- //age: class zjamss.reflect.User
- //score: class zjamss.reflect.User
- }
-
- //获取当前类所有字段
- Field[] declaredFields = clazz.getDeclaredFields();
- for(Field field : declaredFields){
- System.out.println(field.getName()+": "+field.getDeclaringClass());
- //name: class zjamss.reflect.User
- //tel: class zjamss.reflect.User
- //age: class zjamss.reflect.User
- //score: class zjamss.reflect.User
- }
-
- //获取当前类指定字段,任意修饰符都可
- Field name = clazz.getDeclaredField("name");
- System.out.println(name.getName()+": "+name); //name: private java.lang.String zjamss.reflect.User.name
- }
- 复制代码
上述方法需要注意的是,如果我们不期望获取其父类的字段,则需使用Class类的getDeclaredField/getDeclaredFields
方法来获取字段即可,倘若需要连带获取到父类的字段,那么请使用Class类的getField/getFields
,但是也只能获取到public修饰的的字段,无法获取父类的私有字段。
设置字段
- Class clazz = User.class;
- final Field name = clazz.getDeclaredField("name");
- final Constructor constructor = clazz.getConstructor(int.class);
- final User user = (User) constructor.newInstance(10);
- System.out.println(user); //User{name='null', age=10}
- name.setAccessible(true); //解除private
- name.set(user,"name");
- System.out.println(user); //User{name='name', age=10}
- System.out.println(name.get(user)); //name
- 复制代码
Method类及其用法
- Class clazz = User.class;
- //获取所有public的方法,包括父类
- Method[] methods =clazz.getMethods();
- for (Method m:methods){
- System.out.println("m::"+m);
- }
- //m::public int zjamss.reflect.User.getAge()
- //m::public java.lang.String zjamss.reflect.User.toString()
- //m::public final void java.lang.Object.wait() throws java.lang.InterruptedException
- //m::public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
- //m::public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
- //m::public boolean java.lang.Object.equals(java.lang.Object)
- //m::public native int java.lang.Object.hashCode()
- //m::public final native java.lang.Class java.lang.Object.getClass()
- //m::public final native void java.lang.Object.notify()
- //m::public final native void java.lang.Object.notifyAll()
- //获取当前类的方法包含private,该方法无法获取继承自父类的method
- Method method1 = clazz.getDeclaredMethod("display");
- System.out.println("method1::"+method1); //method1::private void zjamss.reflect.User.display()
- method1.setAccessible(true); //修改访问权限
- method1.invoke(clazz.newInstance()); //调用方法 User{name='null', age=0} 12
-
- Method method2 = clazz.getDeclaredMethod("getAge");
- System.out.println("method2:"+method2); //method2:public int zjamss.reflect.User.getAge()
- int age = (int) method2.invoke(new User(12)); //获取方法返回值
- System.out.println(age);
- //获取当前类的所有方法包含private,该方法无法获取继承自父类的method
- Method[] methods1=clazz.getDeclaredMethods();
- for (Method m:methods1){
- System.out.println("m1::"+m);
- }
- //m1::private void zjamss.reflect.User.display()
- //m1::public int zjamss.reflect.User.getAge()
- //m1::public java.lang.String zjamss.reflect.User.toString()
- 复制代码
Spring
框架想必大家都耳熟能详了,是一个提供IOC和AOP支持等等的框架,那里面的IOC又是怎么实现的呢?其实就是反射,比如最使用的@Autowired
注解,自动类型注入
简单来说,当spring扫描bean的时候,会获取一个bean的Class对象
,然后获取所有的field
,遍历查找属性上是否有@Autowired
注解,如果有,则先设置Accessile
为true
,接着就将字段赋值为对应的bean对象