本文最后更新于:December 3, 2021 pm
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。要想解剖一个类,必须先要获取到该类的字节码文件对象。而解剖使用的就是Class类中的方法。所以先要获取到每一个字节码文件对应的Class类型的对象。反射就是把java类中的各种成分映射成一个个的Java对象。
目录 Class类,Class类也是一个实实在在的类,存在于JDK的java.lang包中。Class类的实例表示java应用运行时的类(class ans enum)或接口(interface and annotation)(每个java类运行时都在JVM里表现为一个class对象,可通过类名.class、类型.getClass()、Class.forName(“类名”)等方法获取class对象)。数组同样也被映射为为class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。基本类型boolean,byte,char,short,int,long,float,double和关键字void同样表现为 class 对象。
1.获取Class对象的方式
Class.forName(“全限定类名”); 将字节码文件加载进内存,返回Class对象。多用于配置文件,将类名定义在配置文件中。读取文件,加载类。
类名.class; 通过类名的属性class获取。多用于参数的传递。
对象.getClass(); getClass()方法在Object类中定义。用于获取对象字节码的方式。
Class cls1 = Class.forName("com.tothefor.Person" ); System.out.println(cls1); Class cls2 = Person.class; System.out.println(cls2); Person p = new Person(); Class cls3 = p.getClass(); System.out.println(cls3); System.out.println(cls1 == cls2 ); System.out.println(cls1 == cls3 );
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象,都是同一个。
2.Class类 2.1 获取类名 在Java中,Class类与java.lang.reflect类库一起对反射技术进行了全力的支持。在反射包中,我们常用的类主要有Constructor类表示的是Class 对象所表示的类的构造方法,利用它可以在运行时动态创建对象、Field表示Class对象所表示的类的成员变量,通过它可以在运行时动态修改成员变量的属性值(包含private)、Method表示Class对象所表示的类的成员方法,通过它可以动态调用对象的方法(包含private)。
Class类的方法:
方法名
说明
forName()
(1)获取Class对象的一个引用,但引用的类还没有加载(该类的第一个对象没有生成)就加载了这个类。(2)为了产生Class引用,forName()立即就进行了初始化。
Object-getClass()
获取Class对象的一个引用,返回表示该对象的实际类型的Class引用。
getName()
取全限定的类名(包括包名),即类的完整名字。
getSimpleName()
获取类名(不包括包名)
getCanonicalName()
获取全限定的类名(包括包名)
isInterface()
判断Class对象是否是表示一个接口
getInterfaces()
返回Class对象数组,表示Class对象所引用的类所实现的所有接口。
getSupercalss()
返回Class对象,表示Class对象所引用的类所继承的直接基类。应用该方法可在运行时发现一个对象完整的继承结构。
newInstance()
返回一个Oject对象,是实现“虚拟构造器”的一种途径。使用该方法创建的类,必须带有无参的构造器。
getFields()
获得某个类的所有的公共(public)的字段,包括继承自父类的所有公共字段。 类似的还有getMethods和getConstructors。
getDeclaredFields
获得某个类的自己声明的字段,即包括public、private和proteced,默认但是不包括父类声明的任何字段。类似的还有getDeclaredMethods和getDeclaredConstructors。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 package com.cry;import java.lang.reflect.Field;interface I1 { }interface I2 { }class Cell { public int mCellPublic; }class Animal extends Cell { private int mAnimalPrivate; protected int mAnimalProtected; int mAnimalDefault; public int mAnimalPublic; private static int sAnimalPrivate; protected static int sAnimalProtected; static int sAnimalDefault; public static int sAnimalPublic; }class Dog extends Animal implements I1 , I2 { private int mDogPrivate; public int mDogPublic; protected int mDogProtected; private int mDogDefault; private static int sDogPrivate; protected static int sDogProtected; static int sDogDefault; public static int sDogPublic; }public class Test { public static void main (String[] args) throws IllegalAccessException, InstantiationException { Class<Dog> dog = Dog.class; System.out.println(dog.getName()); System.out.println(dog.getSimpleName()); System.out.println(dog.getCanonicalName()); System.out.println(dog.isInterface()); for (Class iI : dog.getInterfaces()) { System.out.println(iI); } System.out.println(dog.getSuperclass()); Dog d = dog.newInstance(); for (Field f : dog.getFields()) { System.out.println(f.getName()); } System.out.println("---------" ); for (Field f : dog.getDeclaredFields()) { System.out.println(f.getName()); } } }
getName、getCanonicalName与getSimpleName的区别:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package com.cry;public class Test { private class inner { } public static void main (String[] args) throws ClassNotFoundException { System.out.println(Test.class.getSimpleName()); System.out.println(Test.class.getName()); System.out.println(Test.class.getCanonicalName()); System.out.println(inner.class.getSimpleName()); System.out.println(inner.class.getName()); System.out.println(inner.class.getCanonicalName()); System.out.println(args.getClass().getSimpleName()); System.out.println(args.getClass().getName()); System.out.println(args.getClass().getCanonicalName()); Class.forName(inner.class.getName()); } }
2.2 获取构造方法 Constructor类存在于反射包(java.lang.reflect)中,反映的是Class 对象所表示的类的构造方法。获取Constructor对象是通过Class类中的方法获取的。
Class类与Constructor相关的主要方法:
方法返回值
方法名称
方法说明
static Class<?>
forName(String className)
返回与带有给定字符串名的类或接口相关联的 Class 对象。
Constructor
getConstructor(Class<?>… parameterTypes)
返回指定参数类型、具有public访问权限的构造函数对象
Constructor<?>[]
getConstructors()
返回所有具有public访问权限的构造函数的Constructor对象数组
Constructor
getDeclaredConstructor(Class<?>… parameterTypes)
返回指定参数类型、所有声明的(包括private)构造函数对象
Constructor<?>[]
getDeclaredConstructor()
返回所有声明的(包括private)构造函数对象
T
newInstance()
调用无参构造器创建此 Class 对象所表示的类的一个新实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 public class ConstructionTest implements Serializable { public static void main (String[] args) throws Exception { Class<?> clazz = null ; clazz = Class.forName("com.example.javabase.User" ); User user = (User) clazz.newInstance(); user.setAge(20 ); user.setName("Jack" ); System.out.println(user); System.out.println("--------------------------------------------" ); Constructor cs1 =clazz.getConstructor(String.class); User user1= (User) cs1.newInstance("hiway" ); user1.setAge(22 ); System.out.println("user1:" +user1.toString()); System.out.println("--------------------------------------------" ); Constructor cs2=clazz.getDeclaredConstructor(int .class,String.class); cs2.setAccessible(true ); User user2= (User) cs2.newInstance(25 ,"hiway2" ); System.out.println("user2:" +user2.toString()); System.out.println("--------------------------------------------" ); Constructor<?> cons[] = clazz.getDeclaredConstructors(); for (int i = 0 ; i < cons.length; i++) { Class<?> clazzs[] = cons[i].getParameterTypes(); System.out.println("构造函数[" +i+"]:" +cons[i].toString() ); System.out.print("参数类型[" +i+"]:(" ); for (int j = 0 ; j < clazzs.length; j++) { if (j == clazzs.length - 1 ) System.out.print(clazzs[j].getName()); else System.out.print(clazzs[j].getName() + "," ); } System.out.println(")" ); } } }class User { private int age; private String name; public User () { super (); } public User (String name) { super (); this .name = name; } private User (int age, String name) { super (); this .age = age; this .name = name; } public int getAge () { return age; } public void setAge (int age) { this .age = age; } public String getName () { return name; } public void setName (String name) { this .name = name; } @Override public String toString () { return "User{" + "age=" + age + ", name='" + name + '\'' + '}' ; } } User{age=20 , name='Jack' } -------------------------------------------- user1:User{age=22 , name='hiway' } -------------------------------------------- user2:User{age=25 , name='hiway2' } -------------------------------------------- 构造函数[0 ]:private com.example.javabase.User(int ,java.lang.String) 参数类型[0 ]:(int ,java.lang.String) 构造函数[1 ]:public com.example.javabase.User(java.lang.String) 参数类型[1 ]:(java.lang.String) 构造函数[2 ]:public com.example.javabase.User() 参数类型[2 ]:()
Constructor类本身一些常用方法:
方法返回值
方法名称
方法说明
Class
getDeclaringClass()
返回 Class 对象,该对象表示声明由此 Constructor 对象表示的构造方法的类,其实就是返回真实类型(不包含参数)
Type[]
getGenericParameterTypes()
按照声明顺序返回一组 Type 对象,返回的就是 Constructor对象构造函数的形参类型。
String
getName()
以字符串形式返回此构造方法的名称。
Class<?>[]
getParameterTypes()
按照声明顺序返回一组 Class 对象,即返回Constructor 对象所表示构造方法的形参类型
T
newInstance(Object… initargs)
使用此 Constructor对象表示的构造函数来创建新实例
String
toGenericString()
返回描述此 Constructor 的字符串,其中包括类型参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 Constructor cs3 = clazz.getDeclaredConstructor(int .class,String.class); System.out.println("-----getDeclaringClass-----" ); Class uclazz=cs3.getDeclaringClass(); System.out.println("构造方法的类:" +uclazz.getName()); System.out.println("-----getGenericParameterTypes-----" ); Type[] tps=cs3.getGenericParameterTypes();for (Type tp:tps) { System.out.println("参数名称tp:" +tp); } System.out.println("-----getParameterTypes-----" ); Class<?> clazzs[] = cs3.getParameterTypes();for (Class claz:clazzs) { System.out.println("参数名称:" +claz.getName()); } System.out.println("-----getName-----" ); System.out.println("getName:" +cs3.getName()); System.out.println("-----getoGenericString-----" ); System.out.println("getoGenericString():" +cs3.toGenericString()); -----getDeclaringClass----- 构造方法的类:com.example.javabase.User -----getGenericParameterTypes----- 参数名称tp:int 参数名称tp:class java .lang .String -----getParameterTypes ----- 参数名称:int 参数名称:java.lang.String -----getName----- getName:com.example.javabase.User -----getoGenericString----- getoGenericString():private com.example.javabase.User(int ,java.lang.String)
2.3 获取字段 Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段。
通过Class类的提供的方法来获取代表字段信息的Field对象,Class类与Field对象相关方法:
方法返回值
方法名称
方法说明
Field
getDeclaredField(String name)
获取指定name名称的(包含private修饰的)字段,不包括继承的字段
Field[]
getDeclaredField()
获取Class对象所表示的类或接口的所有(包含private修饰的)字段,不包括继承的字段
Field
getField(String name)
获取指定name名称、具有public修饰的字段,包含继承字段
Field[]
getField()
获取修饰符为public的字段,包含继承字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public class ReflectField { public static void main (String[] args) throws ClassNotFoundException, NoSuchFieldException { Class<?> clazz = Class.forName("reflect.Student" ); Field field = clazz.getField("age" ); System.out.println("field:" +field); Field fields[] = clazz.getFields(); for (Field f:fields) { System.out.println("f:" +f.getDeclaringClass()); } System.out.println("================getDeclaredFields====================" ); Field fields2[] = clazz.getDeclaredFields(); for (Field f:fields2) { System.out.println("f2:" +f.getDeclaringClass()); } Field field2 = clazz.getDeclaredField("desc" ); System.out.println("field2:" +field2); } }class Person { public int age; public String name; }class Student extends Person { public String desc; private int score; }
上述方法需要注意的是,如果我们不期望获取其父类的字段,则需使用Class类的getDeclaredField/getDeclaredFields方法来获取字段即可,倘若需要连带获取到父类的字段,那么请使用Class类的getField/getFields,但是也只能获取到public修饰的的字段,无法获取父类的私有字段。
通过Field类本身的方法对指定类属性赋值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 Class<?> clazz = Class.forName("reflect.Student" ); Student st= (Student) clazz.newInstance(); Field ageField = clazz.getField("age" ); ageField.set(st,18 ); Field nameField = clazz.getField("name" ); nameField.set(st,"Lily" ); Field descField = clazz.getDeclaredField("desc" ); descField.set(st,"I am student" ); Field scoreField = clazz.getDeclaredField("score" ); scoreField.setAccessible(true ); scoreField.set(st,88 ); System.out.println(st.toString()); System.out.println(scoreField.get(st));
其中的set(Object obj, Object value)方法是Field类本身的方法,用于设置字段的值,而get(Object obj)则是获取字段的值。
Field类还有其他常用的方法:
方法返回值
方法名称
方法说明
void
set(Object obj, Object value)
将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
Object
get(Object obj)
返回指定对象上此 Field 表示的字段的值
Class<?>
getType()
返回一个 Class 对象,它标识了此Field 对象所表示字段的声明类型。
boolean
isEnumConstant()
如果此字段表示枚举类型的元素则返回 true;否则返回 false
String
toGenericString()
返回一个描述此 Field(包括其一般类型)的字符串
String
getName()
返回此 Field 对象表示的字段的名称
Class<?>
getDeclaringClass()
返回表示类或接口的 Class 对象,该类或接口声明由此 Field 对象表示的字段
void
setAccessible(boolean flag)
将此对象的 accessible 标志设置为指示的布尔值,即设置其可访问性
上述方法可能是较为常用的,事实上在设置值的方法上,Field类还提供了专门针对基本数据类型的方法,如setInt()/getInt()
、setBoolean()/getBoolean
、setChar()/getChar()
等等方法。需要特别注意的是被final关键字修饰的Field字段是安全的,在运行时可以接收任何修改,但最终其实际值是不会发生改变的。
2.4 获取方法 Method 提供关于类或接口上单独某个方法(以及如何访问该方法)的信息,所反映的方法可能是类方法或实例方法(包括抽象方法)。 Class类获取Method对象相关的方法:
方法返回值
方法名称
方法说明
Method
getDeclaredMethod(String name, Class<?>… parameterTypes)
返回一个指定参数的Method对象,该对象反映此 Class 对象所表示的类或接口的指定已声明方法。
Method[]
getDeclaredMethod()
返回 Method 对象的一个数组,这些对象反映此 Class 对象表示的类或接口声明的所有方法,包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。
Method
getMethod(String name, Class<?>… parameterTypes)
返回一个 Method 对象,它反映此 Class 对象所表示的类或接口的指定公共成员方法。
Method[]
getMethods()
返回一个包含某些 Method 对象的数组,这些对象反映此 Class 对象所表示的类或接口(包括那些由该类或接口声明的以及从超类和超接口继承的那些的类或接口)的公共 member 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 import java.lang.reflect.Method;public class ReflectMethod { public static void main (String[] args) throws ClassNotFoundException, NoSuchMethodException { Class clazz = Class.forName("reflect.Circle" ); Method method = clazz.getMethod("draw" ,int .class,String.class); System.out.println("method:" +method); Method[] methods =clazz.getMethods(); for (Method m:methods){ System.out.println("m::" +m); } System.out.println("=========================================" ); Method method1 = clazz.getDeclaredMethod("drawCircle" ); System.out.println("method1::" +method1); Method[] methods1=clazz.getDeclaredMethods(); for (Method m:methods1){ System.out.println("m1::" +m); } } }class Shape { public void draw () { System.out.println("draw" ); } public void draw (int count , String name) { System.out.println("draw " + name +",count=" +count); } }class Circle extends Shape { private void drawCircle () { System.out.println("drawCircle" ); } public int getAllCount () { return 100 ; } } method:public void reflect.Shape.draw(int ,java.lang.String) m::public int reflect.Circle.getAllCount() m::public void reflect.Shape.draw() m::public void reflect.Shape.draw(int ,java.lang.String) 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 final void java.lang.Object.wait() throws java.lang.InterruptedException m::public boolean java.lang.Object.equals(java.lang.Object) m::public java.lang.String java.lang.Object.toString() 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() ========================================= method1::private void reflect.Circle.drawCircle() m1::public int reflect.Circle.getAllCount() m1::private void reflect.Circle.drawCircle()
在通过getMethods方法获取Method对象时,会把父类的方法也获取到,如上的输出结果,把Object类的方法都打印出来了。而getDeclaredMethod/getDeclaredMethods方法都只能获取当前类的方法。我们在使用时根据情况选择即可。 通过Method对象调用指定类的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 Class clazz = Class.forName("reflect.Circle" ); Circle circle = (Circle) clazz.newInstance(); Method method = clazz.getMethod("draw" ,int .class,String.class); method.invoke(circle,15 ,"圈圈" ); Method method1 = clazz.getDeclaredMethod("drawCircle" ); method1.setAccessible(true ); method1.invoke(circle); Method method2 =clazz.getDeclaredMethod("getAllCount" ); Integer count = (Integer) method2.invoke(circle); System.out.println("count:" +count); draw 圈圈,count=15 drawCircle count:100
在上述代码中调用方法,使用了Method类的invoke(Object obj,Object… args)第一个参数代表调用的对象,第二个参数传递的调用方法的参数。这样就完成了类方法的动态调用。
方法返回值
方法名称
方法说明
Object
invoke(Object obj, Object… args)
对带有指定参数的指定对象调用由此 Method 对象表示的底层方法。
Class<?>
getReturnType()
返回一个 Class 对象,该对象描述了此 Method 对象所表示的方法的正式返回类型,即方法的返回类型
Type
getGenericReturnType()
返回表示由此 Method 对象所表示方法的正式返回类型的 Type 对象,也是方法的返回类型。
Class<?>[]
getParameterTypes()
按照声明顺序返回 Class 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型。即返回方法的参数类型组成的数组
Type[]
getGenericParameterTypes()
按照声明顺序返回 Type 对象的数组,这些对象描述了此 Method 对象所表示的方法的形参类型的,也是返回方法的参数类型
String
getName()
以 String 形式返回此 Method 对象表示的方法名称,即返回方法的名称
boolean
isVarArgs()
判断方法是否带可变参数,如果将此方法声明为带有可变数量的参数,则返回 true;否则,返回 false。
String
toGenericString()
返回描述此 Method 的字符串,包括类型参数。
getReturnType方法/getGenericReturnType方法
都是获取Method对象表示的方法的返回类型,只不过前者返回的Class类型后者返回的Type(前面已分析过),Type就是一个接口而已,在Java8中新增一个默认的方法实现,返回的就参数类型信息。
public interface Type { default String getTypeName () { return toString(); } }
而getParameterTypes/getGenericParameterTypes
也是同样的道理,都是获取Method对象所表示的方法的参数类型,其他方法与前面的Field和Constructor是类似的。