Java-反射
1.反射概述
1.1 反射引入
-
回忆一下之前如何使用一个Java类?
-
-
调用构造方法创建对象
-
使用对象调用方法或属性
-
-
问题:
-
如果仅仅知道一个类的类名,能否动态得到类的定义信息,包括哪些方法,属性等?
-
-
答案:可以通过反射做到
1.2 Java反射的概念
-
JAVA反射机制是在运行状态中
-
对于任意一个类,都能够知道这个类的所有属性和方法;
-
对于任意一个对象,都能够调用它的任意一个方法和属性;
-
这种动态获取信息以及动态调用对象的方法的功能称为java语言的反射机制。
-
-
如果做到的呢?
当一个字节码文件加载到内存的时候,jvm会对该字节码进行解剖,然后创建一个对象的Class对象,把字节码文件的信息全部都存储到该Class对象中,我们只要获取到Class对象,我们就可以使用字节码对象设置对象的属性或者调用对象的方法等操作。
1.3 Java反射的作用
-
动态获取类的信息,进一步实现需要的功能
-
例如:Spring框架通过XML文件描述类的基本信息,使用反射机制动态装配对象
- Java反射机制可以完成
- 1.在运行时判断任意一个对象所属的类
- 2.在运行时构造任意一个类的对象
- 3.在运行时得到任意一个类所具有的成员变量和方法
- 4.在运行时调用任意一个对象的成员变量和方法
- 5.生成动态代理
1.4 Java反射相关的类
-
Java反射相关的类主要包括
-
Class 类型
-
Constructor 构造方法
-
Method 方法
-
Field 属性
-
……
-
除了Class外,其他类都位于java.lang.reflect包中
-
-
可见,反射API将类的类型、方法、属性都封装成了类
-
其中最重要的类是Class,可以说,反射的使用都是从Class开始
2.Class类的使用
- Class<T>
T
– 由此类对象建模的类的类型。- 例如,
String.class
的类型是Class<String>
。 - 如果正在建模的类是未知的,请使用
Class<?>
。
-
如何获得Class实例?为一个class生成对应的Class对象
-
案例
public class Person { String name; Integer age; public Person() {} public Person(String name) {this.name = name;} public Person(String name, Integer age) { super(); this.name = name; this.age = age; } public String getName() {return name;} public void setName(String name) {this.name = name;} public Integer getAge() {return age;} public void setAge(Integer age) {this.age = age;} @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
import java.util.HashMap; //反射案例 public class ReflectTest { public static void main(String[] args) throws Exception { String str = "Yi"; Class c11 = str.getClass(); /*class java.lang.String*/ System.out.println(c11); Person p = new Person("Yi", 21); // 方式一:通过对象名.getClass()获取Class类 Class c1 = p.getClass(); System.out.println(c1);/*class com.tjetc.reflect.Person*/ Person p1 = new Person(); Class c2 = p1.getClass(); System.out.println(c2);/*class com.tjetc.reflect.Person*/ // 方式二:通过对象名.getSuperClass()方法获得Class类----获取父类的Class对象 Class c22 = c2.getSuperclass(); System.out.println(c22);/*class java.lang.Object*/ try { // 方式三:通过Class.forName("") 获取Class对象 Class c33 = Class.forName("java.lang.String"); System.out.println(c33); Class c3 = Class.forName("com.tjetc.reflect.Person"); System.out.println(c3); } catch (ClassNotFoundException e) { e.printStackTrace(); } // 方式四:通过类名.class来获取Class对象 Class c44 = String.class; Class c444 = Person.class; Class c4 = int.class; Class c4444 = HashMap.class; System.out.println(c44);/*class java.lang.String*/ System.out.println(c444);/*class com.tjetc.reflect.Person*/ System.out.println(c4);/*int*/ System.out.println(c4444);/*class java.util.HashMap*/ } }
-
Class类是Java反射机制的基础,通过Class类,可以得到一个类的基本信息。以下类是Class类的常用方法:
- 创建由此
类
对象表示的类的新实例。 该类被实例化为一个具有空参数列表的new
表达式。如果类尚未初始化,则初始化该类。
public T newInstance()
throws InstantiationException,
IllegalAccessException
-
- 通过 newInstance() 创建的实例中,所有属性值都是对应类型的
初始值
- 因为 newInstance() 构造实例会调用默认无参构造器。
- 通过 newInstance() 创建的实例中,所有属性值都是对应类型的
-
案例
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class ClassTest { public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, InstantiationException, IllegalAccessException { Class personClass = Person.class; //Field[] getFields() Field[] fields = personClass.getFields(); for (Field field : fields) {/*类的公共属性*/ System.out.println(field.getName()); } System.out.println(); /*Field[] getDeclaredFields() 返回数组Field对象反映此表示的类或接口声明的所有字段类对象。 包括公共,受保护,默认(包)访问和私有字段,但不包括继承的字段。 */ Field[] declaredFields = personClass.getDeclaredFields(); for (Field declaredField : declaredFields) {/*类的所有属性*/ System.out.println(declaredField.getName()); } System.out.println(); //Field getField(String name) Field nameField = personClass.getField("puEmail"); System.out.println(nameField);/*获取指定公有属性*/ System.out.println(); /* Field getDeclaredField(String name) 返回一个Field对象,它反映此表示的类或接口的指定已声明字段类对象。 name参数是一个String ,它指定了所需字段的简单名称。 如果此类对象表示数组类型,则此方法不会找到数组类型的length字段。 */ Field nameDeclared = personClass.getDeclaredField("name"); System.out.println(nameDeclared); System.out.println(); /*Method[] getMethods() 返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 类对象, 包括那些由类或接口和那些从超类和超接口继承的声明。 */ Method[] methods = personClass.getMethods(); for (Method method : methods) {/**/ System.out.println(method); } System.out.println(); //Method[] getDeclaredMethods() Method[] declaredMethods = personClass.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) {/**/ System.out.println(declaredMethod); } System.out.println(); //Method getMethod(String name, Class<?>... parameterTypes) Method hello1 = personClass.getMethod("sayHello"); System.out.println(hello1); Method hello2 = personClass.getMethod("sayHello", String.class); System.out.println(hello2); System.out.println(); //Method getDeclaredMethod(String name, Class<?>... parameterTypes) Method my0 = personClass.getDeclaredMethod("my"); System.out.println(my0); Method my1 = personClass.getDeclaredMethod("my", Integer.class); System.out.println(my1); Method my2 = personClass.getDeclaredMethod("my", int.class); System.out.println(my2); System.out.println(); //Constructor<?>[] getConstructors() Constructor[] publicConstructors = personClass.getConstructors(); for (Constructor publicConstructor : publicConstructors) { System.out.println(publicConstructor); } System.out.println(); //Constructor<?>[] getDeclaredConstructors() Constructor[] declaredConstructors = personClass.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); } System.out.println(); /* Constructor<T>类提供了访问构造器的信息,以及利用构造器创建对象的一系列方法。 */ //Constructor<T> getConstructor(Class<?>... parameterTypes) Constructor constructor = personClass.getConstructor(String.class, String.class); System.out.println(constructor); System.out.println(); //Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) Constructor declaredConstructor = personClass.getDeclaredConstructor(int.class, String.class); System.out.println(declaredConstructor); System.out.println(); //String getName() System.out.println(personClass.getName()); //Package getPackage() System.out.println(personClass.getPackage() + "\n"); //new Person() Person person0 = new Person(); Person person1 = new Person("carat"); System.out.println(person0); System.out.println(person1 + "\n"); //T newInstance() 默认无参构造方法 Object o = personClass.newInstance(); System.out.println(o); Person p = (Person) personClass.newInstance(); System.out.println(p); } }
-
案例练习
使用三种方式获得java.lang.String类的Class实例,并打印输出String类的所有方法名字
// 使用三种方式获得java.lang.String类的Class实例,并打印输出String类的所有方法名字 private static void classTest02() { String s = "hello"; // 使用对象名获得Class实例 Class clazz1 = s.getClass(); // 使用类名获得Class实例,类名必须是常量 Class clazz2 = String.class; try { // 使用类名获得Class实例,类名可以是变量 Class clazz3 = Class.forName("java.lang.String"); } catch (ClassNotFoundException e) { e.printStackTrace(); } Method[] methods = clazz1.getMethods(); for (Method m : methods) { System.out.println(m.getName()); } }
3.Constructor类的使用
-
获取Constructor类的实例方式(Class类的方法):
-
Constructor<T> getConstructor(Class<?>… parameterTypes) :根据
参数类型
获取类中某个构造器,该构造器必须被public
修饰。 -
Constructor[] getConstructors() :获取类中所有被
public
修饰的构造器。
-
-
Constructor类可以通过getXXX方法获得构造方法的基本信息,例如:
-
String getName():以字符串形式返回此构造函数的名称。
-
Class<?>[] getParameterTypes():返回一个类对象的数组, 类以声明顺序表示由该对象表示的可执行文件的形式参数类型。 如果底层可执行文件没有参数,则返回长度为0的数组。
-
-
除了获得构造方法的基本信息,还可以创建实例
public T newInstance(Object… initargs)
throws InstantiationException,
IllegalAccessException,
IllegalArgumentException,
InvocationTargetException
-
- e(Constructor 调用 newInstance(Object… paramValues) 时传入构造方法参数的值,同样可以构造一个实例,且内部属性已经被赋值。
-
案例
package com.tjetc.reflect; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class ConstructorTest { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException { Class personClass = Class.forName("com.tjetc.reflect.Person"); Constructor[] declaredConstructors = personClass.getDeclaredConstructors(); for (Constructor declaredConstructor : declaredConstructors) { Person p = null; //int getParameterCount() int parameterCount = declaredConstructor.getParameterCount(); switch (parameterCount) { case 0: { //T newInstance(Object ... initargs) p = (Person) declaredConstructor.newInstance(); System.out.println("构造方法0个参数:" + p); break; } case 1: { //Class<?>[] getParameterTypes() Class[] parameterTypes = declaredConstructor.getParameterTypes(); if (parameterTypes[0].equals(String.class)) { //T newInstance(Object ... initargs) p = (Person) declaredConstructor.newInstance("carat"); System.out.println("构造方法1个(String)参数:" + p); } else if (parameterTypes[0].equals(int.class)) { p = (Person) declaredConstructor.newInstance(17); System.out.println("构造方法1个(int)参数:" + p); } else { System.out.println("构造方法参数是其他类型,暂不处理"); } break; } case 2: { Class[] parameterTypes = declaredConstructor.getParameterTypes(); if (parameterTypes[0].equals(int.class) && parameterTypes[1].equals(String.class)) { /*boolean isAccessible()*/ //void setAccessible(boolean flag) declaredConstructor.setAccessible(true); /*true的值表示反射对象应该在使用时抑制Java语言访问检查 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true 使得原本无法访问的私有成员也可以访问。 */ p = (Person) declaredConstructor.newInstance(17, "seventeen"); System.out.println("构造方法2个(int,String)参数:" + p); } else if (parameterTypes[0].equals(String.class) && parameterTypes[1].equals(String.class)) { p = (Person) declaredConstructor.newInstance("carat", "seventeen"); System.out.println("构造方法2个(String,String)参数:" + p); } else if (parameterTypes[0].equals(String.class) && parameterTypes[1].equals(int.class)) { p = (Person) declaredConstructor.newInstance("carat", 526); System.out.println("构造方法2个(String,int)参数:" + p); } else { System.out.println("构造方法参数是其他类型,暂不处理"); } break; } case 3: { Class[] parameterTypes = declaredConstructor.getParameterTypes(); if (parameterTypes[0].equals(String.class) && parameterTypes[1].equals(String.class) && parameterTypes[2].equals(int.class)) { p = (Person) declaredConstructor.newInstance("carat", "seventeen", 17); System.out.println("构造方法3个(String,String,int)参数:" + p); } break; } default: { System.out.println("构造方法的参数数量大于3,暂不处理"); } } } } }
4.Method类的使用
-
获取Method实例的方式:
-
Method getMethod(String name, Class<?>… parameterTypes) :通过指定方法名,参数类型,返回一个Method实例
-
Method[] getMethods() :返回该类中所有方法的Method实例
-
-
Method类将类中的方法进行封装,可以动态获得方法的信息,例如
-
getReturnType:获得方法返回值类型
-
getName:获得方法名字
-
getParameterTypes:获得方法参数类型
-
-
除了动态获得方法信息外,Method还能动态调用某一个对象的具体方法
- Object invoke(Object obj, Object… args) :使用obj调用该方法,参数为args
- Object对应原方法的返回值,若原方法无返回值,此时返回null。
- 若原方法若为静态方法,此时形参Object obj可为null。
- 若原方法形参列表为空,则Object[] args为null。
- 若原方法声明为private,则需要在调用此invoke()方法前,显示调用方法对象的setAccessible(true)方法,将可访问private的方法。
- Object invoke(Object obj, Object… args) :使用obj调用该方法,参数为args
- void setAccessible(boolean flag):
- true的值表示反射对象应该在使用时抑制Java语言访问检查
提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true
使得原本无法访问的私有成员也可以访问。
- true的值表示反射对象应该在使用时抑制Java语言访问检查
-
案例
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; public class MethodTest { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { Class<Person> personClazz = Person.class; //Method getMethod(String name, Class<?>... parameterTypes) Method sayHello = personClazz.getMethod("sayHello", String.class); //T newInstance() Person p = personClazz.newInstance(); //Object invoke(Object obj, Object... args) Object returnResult = sayHello.invoke(p, "carat"); System.out.println(returnResult); //Method getDeclaredMethod(String name, Class<?>... parameterTypes) Method add = personClazz.getDeclaredMethod("add", int.class, Double.class); /*true的值表示反射对象应该在使用时抑制Java语言访问检查 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true 使得原本无法访问的私有成员也可以访问。 */ add.setAccessible(true); Object returnAdd = add.invoke(p, 20, 30.0); System.out.println(returnAdd);/*50.0*/ //String getName() System.out.println(add.getName());/*add*/ //Class<?> getReturnType() System.out.print(add.getReturnType() + " ");/*double int*/ //Class<?>[] getParameterTypes() Class<?>[] parameterTypes = add.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println(parameterType);/*class java.lang.Double*/ } System.out.println(Arrays.toString(add.getParameterTypes()));/*[int, class java.lang.Double]*/ //int getParameterCount() System.out.println(add.getParameterCount());/*2*/ } }
5.Field类的使用
-
获得Field实例,都是通过Class中的方法实现
-
public Field getField(String name)
-
通过指定Field名字,返回Field实例
-
注意Field的访问权限 (一定是public)
-
-
Field类将类的属性进行封装,可以获得属性的基本信息、属性的值,也可以对属性进行赋值
-
getName:返回属性的名字
-
getXXX:例如,getFloat返回该属性float类型的值
-
setXXX:例如,setFloat为属性赋值float类型的值
-
-
案例
import java.lang.reflect.Field; public class FieldTest { public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException { //Class<?> forName(String className) Class<?> personClazz = Class.forName("com.tjetc.reflect.Person"); //Field getField(String name) Field puEmail = personClazz.getField("puEmail"); //T newInstance() Object p = personClazz.newInstance(); System.out.println(p);/*Person{puEmail='null', name='null', age=0}*/ //void set(Object obj, Object value) /* obj - 其字段应被修改的对象 value - 修改了 obj的新值 */ puEmail.set(p,"yijing@163.com"); System.out.println(p);/*Person{puEmail='yijing@163.com', name='null', age=0}*/ //Object get(Object obj) System.out.println(puEmail.get(p));/*yijing@163.com*/ //Field getDeclaredField(String name) Field agePrivate = personClazz.getDeclaredField("age"); agePrivate.setAccessible(true); agePrivate.set(p,17); System.out.println(p); } }