Java反射机制是在运行过程中,对于任意一个类,能够动态的获取对象的属性和方法
作用
获取类的变量,调用类的私有方法
增加代码的灵活性
获取类的Class对象 通过对象的getClass()方法 1 2 3 String str = "Hello World" ;Class<?> clazz = str.getClass(); System.out.println(clazz);
输入结果
通过类的.class属性 1 2 Class<?> clazz2 = String.class; System.out.println(clazz2);
输出结果
通过Class类的静态方法forName(String className) 1 2 Class<?> clazz3 = Class.forName("java.lang.String" ); System.out.println(clazz3);
输出结果
注意:使用这种方法,需要写上包名,不需要写.class
应用场景 先创建一个实体类User,具体内容如下
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 public class User { private int id; private String name; private String password; private int level; private String phone; public User () { } public User (int id, String name, String password, int level, String phone) { super (); this .id = id; this .name = name; this .password = password; this .level = level; this .phone = phone; } public User (int id, String name, String password) { super (); this .id = id; this .name = name; this .password = password; } private User (int id, String name) { super (); this .id = id; this .name = name; } }
获取所有的构造方法
public Constructor<?>[] getConstructors()
获得所有 public 访问权限的构造方法
1 2 3 4 5 6 7 Class<?> clazz = Class.forName("org.spirit.lemon.reflect.User" ); System.out.println("获取所有的公共构造方法:" ); Constructor<?>[] constructors = clazz.getConstructors(); for (int i = 0 ; i < constructors.length; i++) { System.out.println(constructors[i]); }
输出结果
1 2 3 4 获取所有的公共构造方法: public org.spirit.lemon.reflect.User(java.lang.Integer,java.lang.String,java.lang.String)public org.spirit.lemon.reflect.User(java.lang.Integer,java.lang.String,java.lang.String,int ,java.lang.String)public org.spirit.lemon.reflect.User()
可以看出,只输出了三个public修饰的构造方法,私有的构造方法并未输出
public Constructor getConstructor(Class<?>… parameterTypes)
获得指定的构造方法,注意只能获得 public 权限的构造方法,其他访问权限的获取不到
1 2 3 4 5 6 7 8 System.out.println("获取指定的公共构造方法:" ); Constructor<?> constructor = clazz.getConstructor(Integer.class, String.class, String.class); System.out.println(constructor); System.out.println(); System.out.println("私有构造方法测试:" ); constructor = clazz.getConstructor(Integer.class, String.class); System.out.println(constructor);
输出结果
1 2 3 4 5 6 7 8 获取指定的公共构造方法: public org.spirit.lemon.reflect.User(java.lang.Integer,java.lang.String,java.lang.String)私有构造方法测试: Exception in thread "main" java.lang.NoSuchMethodException: org.spirit.lemon.reflect.User.<init>(java.lang.Integer, java.lang.String) at java.lang.Class.getConstructor0(Class.java:3082 ) at java.lang.Class.getConstructor(Class.java:1825 ) at org.spirit.lemon.reflect.Test.main(Test.java:37 )
可以看到,只能获取public修饰的构造方法,在获取private修饰的构造方法时,抛出了异常信息
public Constructor<?>[] getDeclaredConstructors()
获得所有的构造方法,包括(public, private,protected,默认权限的)
1 2 3 4 5 System.out.println("获取所有的公共构造方法(包括私有构造方法):" ); Constructor[] constructors = clazz.getDeclaredConstructors(); for (int i = 0 ; i < constructors.length; i++) { System.out.println(constructors[i]); }
输出结果
1 2 3 4 5 获取所有的公共构造方法(包括私有构造方法): private org.spirit.lemon.reflect.User(java.lang.Integer,java.lang.String)public org.spirit.lemon.reflect.User(java.lang.Integer,java.lang.String,java.lang.String)public org.spirit.lemon.reflect.User(java.lang.Integer,java.lang.String,java.lang.String,int ,java.lang.String)public org.spirit.lemon.reflect.User()
不仅输出了public修饰的构造方法,private的构造方法也有输出
public Constructor getDeclaredConstructor(Class<?>… parameterTypes)
获得指定的构造方法,注意可以获取到任何访问权限的构造方法。
1 2 3 System.out.println("获取指定的构造方法(私有构造方法也可以获取):" ); Constructor constructor = clazz.getDeclaredConstructor(Integer.class, String.class);System.out.println(constructor);
输出结果
1 2 获取指定的构造方法(私有构造方法也可以获取): private org.spirit.lemon.reflect.User(java.lang.Integer,java.lang.String)
可以看出,private修饰的构造方法也可以获取到
通过构造方法构建实例对象 获取到构造方法Constructor的对象之后,可以通过Constructor对象创建类的实例
1 2 3 Constructor constructor = clazz.getDeclaredConstructor(Integer.class, String.class, String.class);User user = (User)constructor.newInstance(1 , "lemon" , "a12345+" );System.out.println(user.toString());
输出结果
1 User [id=1 , name=lemon, password=a12345+, level=null , phone=null ]
不过私有构造方法好像是不能通过这个方式创建对象的
1 2 3 Constructor constructor = clazz.getDeclaredConstructor(Integer.class, String.class);User user = (User)constructor.newInstance(1 , "lemon" );System.out.println(user.toString());
输出结果
1 2 3 4 5 6 Exception in thread "main" java.lang.IllegalAccessException: Class org.spirit.lemon.reflect.Test can not access a member of class org .spirit.lemon.reflect.User with modifiers "private" at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102 ) at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296 ) at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288 ) at java.lang.reflect.Constructor.newInstance(Constructor.java:413 ) at org.spirit.lemon.reflect.Test.main(Test.java:55 )
抛出异常,对象创建失败
通过Class对象创建实例对象 1 2 User user2 = (User)clazz.newInstance(); System.out.println(user2.toString());
输出结果
1 User [id=0 , name=null , password=null , level=null , phone=null ]
更多创建对象的方法可以参考Java 创建对象的几种方式
获取属性 添加Student类,继承User的属性
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 public class Student extends User { private String cardNo; private Integer profession; public String cls; public Student () { } public Student (String cardNo, Integer profession) { super (); this .cardNo = cardNo; this .profession = profession; } public String getCardNo () { return cardNo; } public void setCardNo (String cardNo) { this .cardNo = cardNo; } public Integer getProfession () { return profession; } public void setProfession (Integer profession) { this .profession = profession; } public String getCls () { return cls; } public void setCls (String cls) { this .cls = cls; } @Override public String toString () { return "Student [cardNo=" + cardNo + ", profession=" + profession + ", cls=" + cls + ", id=" + id + ", name=" + name + ", password=" + password + ", level=" + level + ", phone=" + phone + "]" ; } }
public Field[] getDeclaredFields()
返回 Field
对象的一个数组,包括public、private、protected和default属性,但是不包括继承的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Class<?> clazz = Student.class; Student student = new Student ();student.setCardNo("1001" ); student.setProfession(11055225 ); student.setCls("数媒2班" ); Field[] fields = clazz.getDeclaredFields(); for (int i = 0 ; i < fields.length; i++) { Field field = fields[i]; field.setAccessible(true ); System.out.println(field); System.out.println(field.getName()); System.out.println(field.getType()); System.out.println(field.get(student)); System.out.println("-------------------------" ); }
输出结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 private java.lang.String org.spirit.lemon.reflect.Student.cardNocardNo class java .lang.String1001 ------------------------- private java.lang.Integer org.spirit.lemon.reflect.Student.professionprofession class java .lang.Integer11055225 ------------------------- public java.lang.String org.spirit.lemon.reflect.Student.clscls class java .lang.String数媒2 班 -------------------------
public Field[] getFields()
获取类的公共属性,包括继承的属性;私有属性无法获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Field[] fields = clazz.getFields(); for (int i = 0 ; i < fields.length; i++) { Field field = fields[i]; field.setAccessible(true ); System.out.println(field); System.out.println(field.getName()); System.out.println(field.getType()); System.out.println(field.get(student)); System.out.println("-------------------------" ); }
输出结果
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 public java.lang.String org.spirit.lemon.reflect.Student.clscls class java .lang.String数媒2 班 ------------------------- public int org.spirit.lemon.reflect.User.idid int 0 ------------------------- public java.lang.String org.spirit.lemon.reflect.User.namename class java .lang.Stringnull ------------------------- public java.lang.String org.spirit.lemon.reflect.User.passwordpassword class java .lang.Stringnull ------------------------- public java.lang.Integer org.spirit.lemon.reflect.User.levellevel class java .lang.Integernull ------------------------- public java.lang.String org.spirit.lemon.reflect.User.phonephone class java .lang.Stringnull -------------------------
可以看出,cardNo和profession两个私有属性没有输出
public Field getDeclaredField(String name)
获取指定属性的Field对象,包括private属性;不可以获取继承自父类的属性
1 2 3 4 5 6 7 8 9 Field field = clazz.getDeclaredField("cls" );field.setAccessible(true ); System.out.println(field); System.out.println(field.getName()); System.out.println(field.getType()); System.out.println(field.get(student));
输出结果
1 2 3 4 public java.lang.String org.spirit.lemon.reflect.Student.cls cls class java.lang.String 数媒2班
public Field getField(String name)
获取指定属性的Field对象,包括继承自父类的属性;只能获取public修饰的属性
1 2 3 4 5 6 7 8 9 Field field = clazz.getField("cls" );field.setAccessible(true ); System.out.println(field); System.out.println(field.getName()); System.out.println(field.getType()); System.out.println(field.get(student));
输出结果
1 2 3 4 public java.lang.String org.spirit.lemon.reflect.User.namename class java .lang.Stringnull
获取方法 在User类中添加方法
1 2 3 public void test () { System.out.println("User" ); }
在Student类中添加私有方法
1 2 3 private void test2 () { System.out.println("Student" ); }
public Method[] getDeclaredMethods()
获取所有的方法,包括private方法,但是不包括继承自父类的方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 Method[] methods = clazz.getDeclaredMethods(); for (int i = 0 ; i < methods.length; i++) { Method method = methods[i]; System.out.println(method); System.out.println(method.getName()); System.out.println(method.getReturnType()); if (method.getName().contains("test2" )) { method.setAccessible(true ); System.out.println("执行test2方法:" ); method.invoke(student, args); } System.out.println("---------分割线----------" ); }
输出结果
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 public java.lang.String org.spirit.lemon.reflect.Student.toString()toString class java .lang.String---------分割线---------- public void org.spirit.lemon.reflect.Student.setCls(java.lang.String)setCls void ---------分割线---------- private void org.spirit.lemon.reflect.Student.test2()test2 void 执行test2方法: Student ---------分割线---------- public void org.spirit.lemon.reflect.Student.setCardNo(java.lang.String)setCardNo void ---------分割线---------- public void org.spirit.lemon.reflect.Student.setProfession(java.lang.Integer)setProfession void ---------分割线---------- public java.lang.String org.spirit.lemon.reflect.Student.getCardNo()getCardNo class java .lang.String---------分割线---------- public java.lang.String org.spirit.lemon.reflect.Student.getCls()getCls class java .lang.String---------分割线---------- public java.lang.Integer org.spirit.lemon.reflect.Student.getProfession()getProfession class java .lang.Integer---------分割线----------
可以看出,User的方法test并没有获取到
public Method[] getMethods()
获取类的方法,包括继承自父类的方法;但是只能获取public修饰的方法,私有方法不能获取
1 2 3 4 5 6 7 8 9 Method[] methods = clazz.getMethods(); for (int i = 0 ; i < methods.length; i++) { Method method = methods[i]; System.out.println(method); System.out.println(method.getName()); System.out.println(method.getReturnType()); System.out.println("---------分割线----------" ); }
输出结果
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 public java.lang.String org.spirit.lemon.reflect.Student.toString()toString class java .lang.String---------分割线---------- public void org.spirit.lemon.reflect.Student.setProfession(java.lang.Integer)setProfession void ---------分割线---------- public void org.spirit.lemon.reflect.Student.setCardNo(java.lang.String)setCardNo void ---------分割线---------- public void org.spirit.lemon.reflect.Student.setCls(java.lang.String)setCls void ---------分割线---------- public java.lang.String org.spirit.lemon.reflect.Student.getCardNo()getCardNo class java .lang.String---------分割线---------- public java.lang.Integer org.spirit.lemon.reflect.Student.getProfession()getProfession class java .lang.Integer---------分割线---------- public java.lang.String org.spirit.lemon.reflect.Student.getCls()getCls class java .lang.String---------分割线---------- public void org.spirit.lemon.reflect.User.test()test void ---------分割线---------- public final void java.lang.Object.wait() throws java.lang.InterruptedExceptionwait void ---------分割线---------- public final void java.lang.Object.wait(long ,int ) throws java.lang.InterruptedExceptionwait void ---------分割线---------- public final native void java.lang.Object.wait(long ) throws java.lang.InterruptedExceptionwait void ---------分割线---------- public boolean java.lang.Object.equals(java.lang.Object)equals boolean ---------分割线---------- public native int java.lang.Object.hashCode()hashCode int ---------分割线---------- public final native java.lang.Class java.lang.Object.getClass()getClass class java .lang.Class---------分割线---------- public final native void java.lang.Object.notify()notify void ---------分割线---------- public final native void java.lang.Object.notifyAll()notifyAll void ---------分割线----------
可以看到Student的私有方法test2()并没有打印出来
public Method getDeclaredMethod(String name, Class<?>… parameterTypes)
获取本类指定的方法,包括私有方法;但是不能获取继承自父类的方法
1 2 3 4 5 6 7 Method method = clazz.getDeclaredMethod("test2" , null );System.out.println(method); System.out.println(method.getName()); System.out.println(method.getReturnType()); method.setAccessible(true ); method.invoke(student, args);
输出结果
1 2 3 4 private void org.spirit.lemon.reflect.Student.test2()test2 void Student
public Method getMethod(String name, Class<?>… parameterTypes)
获取指定的方法,包括继承自父类的方法,但是只能获取public方法
1 2 3 4 5 Method method = clazz.getMethod("test" , null );System.out.println(method); System.out.println(method.getName()); System.out.println(method.getReturnType()); method.invoke(student, args);
输出结果
1 2 3 4 public void org.spirit.lemon.reflect.User.test()test void User
获取注解 通过Class对象也可以获取对应的类的注解信息。自定义注解就可以通过获取类/方法/属性的注解信息来判定是否需要进行必要的逻辑处理。
在类Student的声明上添加两个注解,如下:
1 2 3 4 5 @Description("test") @Deprecated public class Student extends User { }
public Annotation[] getAnnotations()
获取类的注解信息,返回注解类的对象数组;可以获取到继承自父类的注解信息(@Inherited修饰的注解才行)
1 2 3 4 5 Annotation[] annotations = clazz.getAnnotations(); for (int i = 0 ; i < annotations.length; i++) { Annotation annotation = annotations[i]; System.out.println(annotation); }
输出结果
1 2 @com .sun.org.glassfish.gmbal.Description(key=, value=test)@java .lang.Deprecated()
可以看出,获取到了类的注解信息
public Annotation[] getDeclaredAnnotations()
获取直接作用在类上的注解信息,不包含继承的注解
获取类的指定类型的注解信息,如果该类配置了指定注解,返回注解的对象信息,否则返回null
1 2 3 4 5 Annotation annotation = clazz.getDeclaredAnnotation(Deprecated.class);System.out.println(annotation); annotation = clazz.getDeclaredAnnotation(Documented.class); System.out.println(annotation);
输出结果
1 2 @java .lang.Deprecated()null
Student声明了Deprecated注解,未声明Documented注解,所以第二行输出null
其他 也可以通过Class类获取包(Package)、修饰符、父类、泛型等其他信息,此处不做介绍了。
总结 个人在开发过程中用到反射的地方并不是很多,但是使用的框架中(如Spring)大量的应用了反射技术,了解反射的使用方法有助于了解这些框架的实现;
此外,反射会额外的消耗系统资源,效率上较直接创建对象、方法调用较差,但是更加灵活、方便。
待补充