前言
反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。它功能很强大,它能在运行时做很多操作,例如:判断任意一个对象所属的类、运行时构造任意一个类的对象、调用任意一个对象的方法、动态代理生成等等。
Core
在核心包中,主要由以下类来实现反射机制,且这些类都位于java.lang.reflect中。
- Class:java.lang.Class,代表一个类。
- Field:java.lang.reflect.Field,代表类的成员变量。
- Method:java.lang.reflect.Method,代表类的方法。
- Constructor:java.lang.reflect.Constructor,代表类的构造方法。
Class
Class类是反射机制中的核心类,其也是方法最多的一个,它的源码如下:
/**
* Instances of the class {@code Class} represent classes and
* interfaces in a running Java application. An enum is a kind of
* class and an annotation is a kind of interface. Every array also
* belongs to a class that is reflected as a {@code Class} object
* that is shared by all arrays with the same element type and number
* of dimensions. The primitive Java types ({@code boolean},
* {@code byte}, {@code char}, {@code short},
* {@code int}, {@code long}, {@code float}, and
* {@code double}), and the keyword {@code void} are also
* represented as {@code Class} objects.
*
* <p> {@code Class} has no public constructor. Instead {@code Class}
* objects are constructed automatically by the Java Virtual Machine as classes
* are loaded and by calls to the {@code defineClass} method in the class
* loader.
*
* <p> The following example uses a {@code Class} object to print the
* class name of an object:
*
* <blockquote><pre>
* void printClassName(Object obj) {
* System.out.println("The class of " + obj +
* " is " + obj.getClass().getName());
* }
* </pre></blockquote>
*
* <p> It is also possible to get the {@code Class} object for a named
* type (or for void) using a class literal. See Section 15.8.2 of
* <cite>The Java™ Language Specification</cite>.
* For example:
*
* <blockquote>
* {@code System.out.println("The name of class Foo is: "+Foo.class.getName());}
* </blockquote>
*
* @param <T> the type of the class modeled by this {@code Class}
* object. For example, the type of {@code String.class} is {@code
* Class<String>}. Use {@code Class<?>} if the class being modeled is
* unknown.
*
* @author unascribed
* @see java.lang.ClassLoader#defineClass(byte[], int, int)
* @since JDK1.0
*/
public final class Class<T> implements java.io.Serializable,
GenericDeclaration,
Type,
AnnotatedElement {
//......
}
它的泛型参数是由此Class对象类的类型。例如:String.class的类型是Class,如果类型未知,则可以使用未知泛型Class<?>
Get Class
无论你生成某个类的多少个对象,首先这些对象都会属于同一个Class对象。 要想使用反射,则必须要获得待处理类或对象所对应的Class对象,而获取的方式有如下几种:
- Class.forName("com.org.Example"):使用Class类的静态方法。
- String.class:使用每个类都有的.class属性。
- XXX.getClass():使用每个对象都有的getClass()方法。
public class GetGlassTest {
public static void main(String[] args) throws Exception {
Class<?> class1 = Class.forName("com.org.Example");
Example example = new Example();
Class<?> class2 = example.getClass();
Class<?> class3 = Example.class;
System.out.println("ClassName:" + class1.getName());
System.out.println("ClassName:" + class2.getName());
System.out.println("ClassName:" + class3.getName());
}
}
Core API
getName
返回此Class对象所表示的实体(类、接口、数组类、基本类型)名称,如果此类对象表示的是非数组类型的引用类型,则返回该类的二进制名称,如果此类对象表示一个基本类型或void,则返回的名字是一个与该基本类型或void所对应的Java语言关键字相同的名称,如果此类对象表示一个数组类,则名字的内部形式为:表示该数组嵌套深度的一个或多个“[”字符加元素类型名。
元素类型名的编码如下:
Element Type | Encoding |
---|---|
boolean | Z |
byte | B |
char | C |
class or interface | Lclassname; |
double | D |
float | F |
int | I |
long | J |
short | S |
代码如下:
public static void main(String[] args) throws Exception {
// java.lang.String
System.out.println(String.class.getName());
// byte
System.out.println(byte.class.getName());
// [Ljava.lang.Object;
System.out.println((new Object[3]).getClass().getName());
// [[[I
System.out.println((new int[3][4][5]).getClass().getName());
// class sun.misc.Launcher$AppClassLoader
System.out.println(URLClassLoader.getSystemClassLoader().getClass());
}
forName
返回与带有给定字符串名的类或接口相关联的Class对象。调用此方法等效于:Class.forName(className, true, currentLoader),className是所需类的完全限定名;true表示该类将被初始化;currentLoader表示当前类的定义类加载器。指定的类加载器用于加载该类或接口。如果参数loader为 null,则该类通过引导类加载器加载。只有initialize参数为true且以前未被初始化时,才初始化该类。
public static void main(String[] args) throws Exception {
Class<?> aClass = Class.forName("java.lang.String");
System.out.println(aClass.getName());
}
用法详解
Class.forName返回的是一个类,它的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段。
new和Class.forName有什么区别?
newInstance是一个方法,而new是一个关键字,Class下的newInstance的使用有局限,因为它生成对象只能调用无参的构造函数,而使用new关键字生成对象没有这个限制。
Class.forName做了什么工作?
假设一个类以前从来没有被装进内存过,Class.forName会做以下几件事情:
- 装载,将字节码读入内存,并产生一个与之对应的java.lang.Class类对象。
- 链接,这一步会验证字节码,为static变量分配内存,并赋默认值,并可选的解析符号引用。
- 初始化,为类的static变量赋初始值,假如有static int age = 1,这个赋值为1的操作就是这个时候做的。除此之外,还要调用类的static静态代码块。
@ToString
class User {
private static int age = getAge();
static {
System.out.println("static block!!!!");
}
private static int getAge() {
System.out.println("static method!!!!");
return 0;
}
}
public static void main(String[] args) throws Exception {
final Class<?> aClass = Class.forName("cn.gov.zcy.User");
// static method!!!!
// static block!!!!
}
isPrimitive
- 判定指定的Class对象是否表示一个基本类型。有九种预定义的Class对象,表示八个基本类型和void。这些类对象由Java虚拟机创建,与其表示的基本类型同名,即boolean、byte、char、short、int、long、float和double。
- 这些对象仅能通过下列声明为public static final的变量访问,也是这几个Class对象才能使此方法返回 true。
// 判断Class是否Java基本类型
public static void main(String[] args) throws Exception {
System.out.println(boolean.class.isPrimitive()); // true
System.out.println(byte.class.isPrimitive()); // true
System.out.println(char.class.isPrimitive()); // true
System.out.println(short.class.isPrimitive()); // true
System.out.println(int.class.isPrimitive()); // true
System.out.println(long.class.isPrimitive()); // true
System.out.println(float.class.isPrimitive()); // true
System.out.println(double.class.isPrimitive()); // true
System.out.println(void.class.isPrimitive()); // true
System.out.println(Boolean.class.isPrimitive()); // false
System.out.println(Byte.class.isPrimitive()); // false
System.out.println(Character.class.isPrimitive()); // false
System.out.println(Short.class.isPrimitive()); // false
System.out.println(Integer.class.isPrimitive()); // false
System.out.println(Long.class.isPrimitive()); // false
System.out.println(Float.class.isPrimitive()); // false
System.out.println(Double.class.isPrimitive()); // false
}
getDeclaredFields
返回Field对象的一个数组,这些对象反映此Class对象所表示的类或接口所声明的所有字段。包括公共、保护、默认(包)访问和私有字段,但不包括继承的字段。返回数组中的元素没有排序,也没有任何特定的顺序。如果该类或接口不声明任何字段,或者此Class对象表示一个基本类型、一个数组类或void,则此方法返回一个长度为0的数组。
获取属性值
public static void main(String[] args) throws Exception {
User user = new User("晓断", 22);
Field[] fields = user.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Object o = field.get(user);
// 晓断
// 22
System.out.println(o);
}
}
Java反射获取属性上的注解内容
@ToString
class User {
@JSONField(name = "name")
private String name;
@JSONField(name = "age")
private int age;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) throws Exception {
User user = new User("晓断", 22);
Field[] fields = user.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
JSONField jsonField = field.getAnnotation(JSONField.class);
// name
// age
System.out.println(jsonField.name());
}
}
getFields
- 返回一个包含某些Field对象的数组,这些对象反映此Class对象所表示的类或接口的所有可访问公共public字段。返回数组中的元素没有排序,也没有任何特定的顺序。如果类或接口没有可访问的公共字段,或者表示一个数组类、一个基本类型或void,则此方法返回长度为0的数组。如果该Class对象表示一个类,则此方法返回该类及其所有超类的公共字段。如果该Class对象表示一个接口,则此方法返回该接口及其所有超接口的公共字段。
@ToString
class User {
@JSONField(name = "name")
private String name;
@JSONField(name = "age")
private int age;
public String sex;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) throws Exception {
User user = new User("晓断", 22);
Field[] fields = user.getClass().getFields();
for (Field field : fields) {
// sex
System.out.println(field.getName());
}
}
getDeclaredMethods
- 返回所有本类声明的方法,它返回Method对象的一个数组,这些对象反映此Class对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。返回数组中的元素没有排序,也没有任何特定的顺序。如果该类或接口不声明任何方法,或者此Class对象表示一个基本类型、一个数组类或void,则此方法返回一个长度为0的数组。类初始化方法 不包含在返回数组中。如果该类声明带有相同参数类型的多个公共成员方法,则它们都包含在返回的数组中。
getDeclaredMethods、getMethods的区别?
getDeclaredMethods获取当前类的所有声明的方法,包括public、protected和private修饰的方法。这些方法一定是在当前类中声明的,从父类中继承的不算,实现接口的方法由于有声明所以包括在内。而getMethods是获取当前类和父类的所有public的方法。这里的父类,指的是继承层次中的所有父类。
@ToString
class User {
@JSONField(name = "name")
private String name;
@JSONField(name = "age")
private int age;
public String sex;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) throws Exception {
User user = new User("晓断", 22);
Method[] methods = user.getClass().getDeclaredMethods();
for (Method method : methods) {
// toString
// setName
// setAge
System.out.println(method.getName());
}
}
getMethods
- 返回一个包含某些Method对象的数组,这些对象反映此Class对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共public成员方法。数组类返回从 Object类继承的所有公共的方法。返回数组中的元素没有排序,也没有任何特定的顺序。如果此 Class 对象表示没有公共成员方法的类或接口,或者表示一个基本类型或void,则此方法返回长度为0的数组。
public static void main(String[] args) throws Exception {
User user = new User("晓断", 22);
Method[] methods = user.getClass().getMethods();
for (Method method : methods) {
// toString
// setName
// setAge
// wait
// wait
// wait
// equals
// hashCode
// getClass
// notify
// notifyAll
System.out.println(method.getName());
}
}
@ToString
class User {
@JSONField(name = "name")
private String name;
@JSONField(name = "age")
private int age;
public String sex;
public User(String name, int age) {
this.name = name;
this.age = age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
}
newInstance
- 创建此Class对象所表示的类的实例。如同用一个带有一个空参数列表的new关键字实例化该类。如果该类尚未初始化,则初始化这个类。
@ToString
class User {
private String name;
private int age;
public User() {
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
通过Class对象的newInstance方法调用类的默认构造方法实例化
public static void main(String[] args) throws Exception {
Class<?> userClass = Class.forName("cn.gov.zcy.item.User");
User user = (User) userClass.newInstance();
// User(name=null, age=0)
System.out.println(user);
}
通过Class对象获取Constructor,再通过Constructor对象的newInstance()方法实例化
public static void main(String[] args) throws Exception {
Class<?> userClass = Class.forName("cn.gov.zcy.item.User");
Constructor<?> constructor = userClass.getConstructor(String.class, int.class);
user = (User) constructor.newInstance("晓断", 22);
// User(name=晓断, age=22)
System.out.println(user);
}
getClassLoader
由于BootStrap ClassLoader是用c++写的,所以在返回该ClassLoader时会返回null。
返回该类的类加载器。有些实现可能使用null来表示引导类加载器。如果该类由引导类加载器加载,则此方法在这类实现中将返回null。如果存在安全管理器,并且调用者的类加载器不是null,也不同于或是请求其类加载器的类的类加载器的祖先,则此方法通过 RuntimePermission("getClassLoader") 权限调用此安全管理器的 checkPermission方法,以确保可以访问该类的类加载器。如果此对象表示一个基本类型或void,则返回null。
// 判断一个类是否自定义类
public static void main(String[] args) throws Exception {
Class<?> clz = Integer.class;
// true
System.out.println(clz.getClassLoader() == null);
Class<?> userclz = User.class;
// false
System.out.println(userclz.getClassLoader() == null);
}
isAssignableFrom
- 判定此Class对象所表示的类或接口与指定的Class参数所表示的类或接口是否相同,或是否是其超类或超接口。如果是则返回true,否则返回 false。如果该Class表示一个基本类型,且指定的Class参数正是该Class对象,则该方法返回true,否则返回 false。
public static void main(String[] args) throws Exception {
// false
System.out.println(String.class.isAssignableFrom(Object.class));
// true
System.out.println(Object.class.isAssignableFrom(String.class));
// true
System.out.println(Object.class.isAssignableFrom(Object.class));
}