在 Java
中有两种方式可让咱们在运行时识别对象和类的信息。一种是 RTTI
(运行时类型识别:Run-Time Type Identification),它假定了咱们在编译时已经知道了全部的类型;另外一种是咱们本文要说的反射机制
,它容许咱们在运行时获取和使用类的信息。不管是 RTTI
仍是反射
,其本质都是同样的,都是去动态的获取类的信息。它们惟一不一样的是,RTTI
在编译时期知道要解析的类型,而反射
是在运行时才知道要解析的类型。java
反射就是把 Java
类中的各个部分(属性、方法、构造方法等)映射成一个个对象。Class
类与 java.lang.reflect
类库一块儿对反射的概念提供了支持,类库中包含了 Field
、Method
及 Constructor
类,每一个类都实现了 Member
接口。这些类型的对象都是由 JVM
运行时建立的,用来表示未知类里对应的成员。这样咱们就可使用 Constructor
建立新的对象,用 get
和 set
方法读取和修改类中与 Field
对象关联的字段,用 invoke
方法调用类中与 Method
对象关联的方法等。Java
反射机制是在运行状态中的,对于任意一个类咱们能够经过反射获取这个类的全部属性和方法;对于任意一个对象,都可以调用它的任意一个方法和属性。重要的是,要认识到反射机制并无什么特别之处,当咱们经过反射和一个未知类型的对象打交道时,JVM
只是简单的对这个对象作检查,看它属于哪一个类,在用它作其它事情以前必须先加载那个类 Class
对象。因此那个类的字节码文件对象对于 JVM
来讲必须是可获取的,要么在本地机器上,要么经过网络获取。数组
<!-- more -->网络
想要经过反射获取一个类的信息以前,首先要先获取这个类的 Class
对象,在 Java
中全部类型都有与之关联的 Class
对象。框架
在 Java
中获取一个类的 Class
对象有三种方式:
第 ① 种 使用 Class
类的 forName
静态方法,当咱们知道一个类的全路径时,能够经过 Class.forName
方法获取类的 Class
对象。this
Class stringClass = Class.forName("java.lang.String"); System.out.println(stringClass);
运行结果code
class java.lang.String
第 ② 种 使用 .class
获取,这种方式只适合在编译前就已经知道了要操做的 Class
。对象
Class stringClass = String.class; System.out.println(stringClass);
运行结果blog
class java.lang.String
第 ③ 种 使用 getClass()
方法获取继承
Class stringClass = "mghio".getClass(); System.out.println(stringClass);
运行结果接口
class java.lang.String
经过反射建立类对象有两种方式:
第 ① 种 经过调用 Class
对象的 newInstance()
方法建立
Class<Person> personClass = Person.class; Person person = personClass.newInstance();
第 ② 种 经过调用 Constructor
对象的 newInstance()
方法建立
Class<Person> personClass = Person.class; Constructor personConstructor = personClass.getConstructor(); Person person = (Person) personConstructor.newInstance();
二者的区别是,经过 Class
的 newInstance
方法只能经过无参构造方法建立,这就要求这个类必须有一个无参的构造方法,而经过 Constructor
的 newInstance
能够指定参数来选择特定的构造方法来建立对象。如下代码就是指定参数而后经过特定的构造方法建立对象的。
Class<Person> personClass = Person.class; Constructor personConstructor = personClass.getConstructor(); Person person = (Person) personConstructor.newInstance("mghio", "中国上海");
Class
类提供了两种方式获取一个类的属性,第 ① 种是经过 Class
对象的 getFields
方法获取类的属性,该方法只能获取类的 public
属性。
Class<Person> personClass = Person.class; Field[] fields = personClass.getFields(); System.out.println(Arrays.toString(fields));
运行结果
[public java.lang.String cn.mghio.blogmghiocode.reflect.Person.id, public java.lang.String cn.mghio.blogmghiocode.reflect.Person.name]
第 ② 种是经过 Class
对象的 getDeclaredFields
方法获取类的属性,该方法能够获取类的全部属性(包括 private
修饰的属性)。
Class<Person> personClass = Person.class; Field[] fields = personClass.getDeclaredFields(); System.out.println(Arrays.toString(fields));
运行结果
[public java.lang.String cn.mghio.blogmghiocode.reflect.Person.id, public java.lang.String cn.mghio.blogmghiocode.reflect.Person.name, protected java.lang.Integer cn.mghio.blogmghiocode.reflect.Person.age, private java.lang.String cn.mghio.blogmghiocode.reflect.Person.address]
Class
也提供了两种方式获取类的方法,第 ① 种是经过 Class
对象的 getMethods
方法获取类的方法(包括继承而得的方法)。
Class<Person> personClass = Person.class; Method[] methods = personClass.getMethods(); System.out.println(Arrays.toString(methods));
运行结果
[public java.lang.String cn.mghio.blogmghiocode.reflect.Person.toString(), public java.lang.String cn.mghio.blogmghiocode.reflect.Person.getAddress(), ... public final native java.lang.Class java.lang.Object.getClass(), public final native void java.lang.Object.notify()]
第 ② 种是经过 Class
对象的 getDeclaredMethods
方法获取类的方法(只包含类中定义的方法,不包含继承而来的方法)。
Class<Person> personClass = Person.class; Method[] methods = personClass.getDeclaredMethods(); System.out.println(Arrays.toString(methods));
运行结果
[public java.lang.String cn.mghio.blogmghiocode.reflect.Person.toString(), public java.lang.String cn.mghio.blogmghiocode.reflect.Person.getAddress(), ... protected void cn.mghio.blogmghiocode.reflect.Person.protectedMethod(), private void cn.mghio.blogmghiocode.reflect.Person.privateMethod()]
从以上结果能够看出这个方法只获取当前类中定义的方法,包含 private
方法,不会获取从父类中继承而来的方法。
Class
也提供了两种方式获取类的构造方法,第 ① 种是经过 Class
对象的 getConstructors
方法获取类的构造方法(只能获取当前类的 public
构造方法)。
Class<Person> personClass = Person.class; Constructor[] constructors = personClass.getConstructors(); System.out.println(Arrays.toString(constructors));
运行结果
[public cn.mghio.blogmghiocode.reflect.Person(java.lang.String,java.lang.String,java.lang.Integer,java.lang.String)]
第 ② 种是经过 Class
对象的 getDeclaredConstructors
方法获取类的构造方法(只包含类中定义的全部构造方法)。
Class<Person> personClass = Person.class; Constructor[] constructors = personClass.getDeclaredConstructors(); System.out.println(Arrays.toString(constructors));
运行结果
[public cn.mghio.blogmghiocode.reflect.Person(java.lang.String,java.lang.String,java.lang.Integer,java.lang.String), protected cn.mghio.blogmghiocode.reflect.Person(java.lang.String,java.lang.String), private cn.mghio.blogmghiocode.reflect.Person()]
Class
类提供了两种方式获取类的类名,第 ① 种是经过 getName
方法获取类的全限定名(包含包名)。
Class<Person> personClass = Person.class; String fullPersonClassName = personClass.getName(); System.out.println(fullPersonClassName);
运行结果
cn.mghio.blogmghiocode.reflect.Person
第 ② 种是经过 Class
对象的 getSimpleName
方法获取类的类名(不包含包名)。
Class<Person> personClass = Person.class; String fullPersonClassName = personClass.getSimpleName(); System.out.println(fullPersonClassName);
运行结果
Person
能够经过 Class
类来获取一个类的修饰符,也就是咱们熟知的 public
、protected
、private
等关键字,经过调用 getModifiers
方法来获取一个类的修饰符。
Class<Person> personClass = Person.class; int modifyInt = personClass.getModifiers(); System.out.println(modifyInt);
运行结果
1
返回 1
表示类 Person
的修饰符为 public
,修饰符在 Modifier
类中都被包装成一个 int
类型的数字,部分修饰符定义以下
/** * The {@code int} value representing the {@code public} * modifier. */ public static final int PUBLIC = 0x00000001; /** * The {@code int} value representing the {@code private} * modifier. */ public static final int PRIVATE = 0x00000002; /** * The {@code int} value representing the {@code protected} * modifier. */ public static final int PROTECTED = 0x00000004;
Class
对象经过 getPackage
方法获取类的包相关信息,可使用 Class
对象经过以下的方式获取包信息
Class<Person> personClass = Person.class; Package packageClazz = personClass.getPackage(); System.out.println(packageClazz.getName());
运行结果
cn.mghio.blogmghiocode.reflect
能够经过 Class
类来获取一个类的父类,经过调用 getModifiers
方法来获取一个类的父类。
Class<Person> personClass = Person.class; Class superclass = personClass.getSuperclass(); System.out.println(superclass.getName());
运行结果
java.lang.Object
以上运行结果表示 Person
类的父类是 Object
类,能够看到 superclass
对象其实就是一个 Class
类的实例,因此也能够继续在这个对象上进行反射操做。
能够经过 Class
类来获取一个类的父类,经过调用 getInterfaces
方法来获取一个类实现的接口。
Class<Person> personClass = Person.class; Class<?>[] interfaces = personClass.getInterfaces(); System.out.println(Arrays.toString(interfaces));
运行结果
[interface cn.mghio.blogmghiocode.reflect.IPerson]
在 Java
中一个类能够实现多个接口,所以 getInterfaces
方法返回一个 Class
数组,在 Java
中接口也一样有对应的 Class
对象。这个方法须要注意的是,getInterfaces
方法仅仅只返回当前类所实现的接口。当前类的父类若是实现了接口,这些接口是不会在返回的 Class
集合中的,尽管实际上当前类其实已经实现了父类接口。
当咱们在声明一个类或者接口的时候能够指定它能够参数化,经常使用的 List
接口就是一个参数化接口的例子。好比想要检查 List
接口的参数化类型,咱们是没有办法能知道它具体的参数化类型是什么。这个类型就能够是一个应用中全部的类型。可是,当你检查一个使用了被参数化的类型的变量或者方法,你能够得到这个被参数化类型的具体参数。
第 ① 种 泛型方法返回类型 当你得到了 Method
对象,那么就能够获取到这个方法的泛型返回类型信息。若是方法是在一个被参数化类型之中(例如: T foo()),那么将没法得到它的具体类型,可是若是方法返回的是一个泛型类(例如:List<String> foo()),那么就能够得到这个泛型类的具体参数化类型。下面这个例子中的类定义了一个返回类型是泛型的方法。
/** * @author mghio * @date: 2019-12-29 * @version: 1.0 * @description: 经过反射获取泛型信息 * @since JDK 1.8 */ public class ReflectGenericDemo { protected List<Integer> stringList = Arrays.asList(2, 55, 3, 90, 81); public List<Integer> getStringList(){ return this.stringList; } }
咱们能够获取上面这个类 ReflectGenericDemo
的方法 getStringList
的泛型返回类型。
/** * @author mghio * @date: 2019-12-29 * @version: 1.0 * @description: 经过反射获取泛型信息 * @since JDK 1.8 */ public class ReflectGenericDemoTests { @Test public void testMethodReturnGenericType() throws NoSuchMethodException { Class<ReflectGenericDemo> reflectClass = ReflectGenericDemo.class; Method method = reflectClass.getMethod("getStringList", (Class<?>) null); Type returnType = method.getGenericReturnType(); if (returnType instanceof ParameterizedType) { ParameterizedType type = (ParameterizedType) returnType; Type[] typeArguments = type.getActualTypeArguments(); for (Type typeArgument : typeArguments) { Class typeArgumentClass = (Class) typeArgument; System.out.println("typeArgumentClass = " + typeArgumentClass); } } } }
运行结果
typeArgumentClass = class java.lang.Integer
typeArguments
数组只有一个值,这个数组中惟一的值是 Integer
的 Class
类的实例,同时 Class
类也实现了 Type
接口。
第 ② 种 泛型方法返回类型 泛型方法参数类型,咱们也能够经过反射来获取方法参数的泛型类型。
/** * @author mghio * @date: 2019-12-29 * @version: 1.0 * @description: 经过反射获取泛型信息 * @since JDK 1.8 */ public class ReflectGenericDemo { protected List<Integer> stringList = Arrays.asList(2, 55, 3, 90, 81); public void setStringList(List<Integer> stringList) { this.stringList = stringList; } }
能够经过如下方式获取方法参数的泛型类型。
/** * @author mghio * @date: 2019-12-29 * @version: 1.0 * @description: 经过反射获取泛型信息 * @since JDK 1.8 */ public class ReflectGenericDemoTests { @Test public void testMethodParameterGenericType() throws NoSuchMethodException { Class<ReflectGenericDemo> reflectClass = ReflectGenericDemo.class; Method method = reflectClass.getMethod("setStringList", List.class); Type[] genericParameterTypes = method.getGenericParameterTypes(); for (Type genericParameterType : genericParameterTypes) { if (genericParameterType instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) genericParameterType; Type[] parameterArgTypes = parameterizedType.getActualTypeArguments(); for (Type parameterArgType : parameterArgTypes) { Class parameterArgClass = (Class) parameterArgType; System.out.println("parameterArgClass = " + parameterArgClass); } } } } }
运行结果
parameterArgClass = class java.lang.Integer
第 ③ 种 泛型变量类型 能够经过反射来访问类中定义变量的泛型类型,无论这个变量是一个类的静态成员变量或是实例成员变量。
/** * @author mghio * @date: 2019-12-29 * @version: 1.0 * @description: 经过反射获取泛型信息 * @since JDK 1.8 */ public class ReflectGenericDemo { private List<Integer> stringList = Arrays.asList(2, 55, 3, 90, 81); }
咱们能够经过如下代码来获取类 ReflectGenericDemo
的私有变量 stringList
的泛型变量类型。
/** * @author mghio * @date: 2019-12-29 * @version: 1.0 * @description: 经过反射获取泛型信息 * @since JDK 1.8 */ public class ReflectGenericDemoTests { @Test public void testFieldGenericType() throws NoSuchFieldException { Class<ReflectGenericDemo> reflectClass = ReflectGenericDemo.class; Field field = reflectClass.getDeclaredField("stringList"); Type type = field.getGenericType(); if (type instanceof ParameterizedType) { ParameterizedType fieldGenericType = (ParameterizedType) type; Type[] fieldGenericTypes = fieldGenericType.getActualTypeArguments(); for (Type genericType : fieldGenericTypes) { Class fieldGenericTypeClass = (Class) genericType; System.out.println(fieldGenericTypeClass); } } } }
运行结果
class java.lang.Integer
数组 fieldGenericTypes
只有一个元素,它表明类 Integer
的 Class
类的实例。咱们能够得出经过反射获取泛型信息的套路都是先获取 Class
类对象,而后经过该对象获取相应的类,若是是要获取变量的泛型信息就先获取到 Field
类,若是是要获取方法的泛型信息就先获取到 Method
类,最后再经过是不是 ParameterizedType
的实例来判断是不是泛型类型。
咱们介绍了 Java
泛型的基本使用,反射可能在咱们平常的工做中不怎么接触到,可是,在不少框架中都有运用,好比,Spring
的 IOC/DI
也是反射;还有 JDBC
的 classForName
也是反射。全部深刻了解 Java
反射机制颇有必要。
方法 | 描述 |
---|---|
Constructor getConstructor(Class[] params) | 根据构造方法的参数,返回一个 public 类型的构造方法 |
Constructor getConstructors() | 返回全部 public 类型的构造方法数组 |
Constructor getDeclaredConstructor(Class[] params) | 根据构造方法的参数,返回一个具体的构造方法(全部的类型) |
Constructor getDeclaredConstructors() | 返回该类中全部的构造方法数组(全部的类型) |
Method getMethod(String name, Class[] params) | 根据方法名和参数,返回一个 public 类型的方法 |
Method[] getMethods() | 返回全部 public 类型的方法数组 |
Method getDeclaredMethod(String name, Class[] params) | 根据方法名和参数,返回一个具体的方法(全部的类型) |
Method[] getDeclaredMethods() | 返回该类中的全部的方法数组(全部的类型) |
Field getField(String name) | 根据变量名,返回一个 public 类型的成员变量 |
Field[] getFields() | 返回 public 类型的成员变量的数组 |
Field getDeclaredField(String name) | 根据变量名,返回一个成员变量(全部的类型) |
Field[] getDelcaredField() | 返回全部成员变量组成的数组(全部的类型) |