参考博客:
https://www.cnblogs.com/ysocean/p/6516248.html
https://www.sczyh30.com/posts/Java/java-reflection-1/
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
反射的核心是 JVM 在运行时才动态加载类或调用方法 / 访问属性,它不需要事先(写代码时或编译期)知道运行对象是谁;
1、Java 反射主要提供以下功能:**
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法(通过反射甚至可以调用 private 方法);
- 在运行时调用任意一个对象的方法。
2、反射的主要用途
很多人都认为反射在实际的 Java 开发应用中并不广泛,其实不然。当我们在使用 IDE(如 Eclipse,IDEA)时,当我们输入一个对象或类并想调用它的属性或方法时,一按点号,编译器就会自动列出它的属性或方法,这里就会用到反射。
反射最重要的用途就是开发各种通用框架,各框架大量使用了动态代理,而动态代理的实现依赖于反射技术.
3、基本运用
以下我会全部使用 clazz 表示某个类的 Class 实例
(1)、获取 Class 对象
-
// Class 类的 forName 静态方法 Class.forName("xxx");
-
// 直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高 Class clazz = Person.class;
-
// 通过对象的 getClass() 方法,通常用在方法参数 Person p = new Person(); Class clazz = p.getClass();
(2)、判断是否为某个类的实例
- 通常使用
instanceof
关键字判断是否为某个类的实例; - 也可以使用 Class 对象的 isInstance() 方法判断是否为某个类的实例。
public native boolean isInstance(Object obj);
(3)、创建实例
-
使用 Class 对象的
newInstance()
方法创建 Class 对象对应的实例;(调用无参构造函数)Class clazz = Person.class; Person person = clazz.newInstance();
-
通过 Class 对象获取指定的
Constructor
对象(带参数的构造函数),在调用Constructor
对象的newInstance()
方法创建实例Class clazz = Person.class; // 获取 Person 类带一个 String 类型的参数的构造器 Constructor con = clazz.getConstructor(String.class); // 根据构造器创建实例 Person person = con.newInstance("xiaojian");
(4)、获取构造器
// 通过 Class 类的实例获取 Constructor,参数为指定构造器的参数的类型的Class对象
Class clazz = Person.class;
Constructor c = clazz.getConstructor(String.class);
// 再由构造器使用 newInstance()方法 创建实例对象
Person person = c.newInstance("xiaojian");
(5)、获取方法
-
getDeclaredMethods
方法返回接口或接口声明的所有方法,包括公共、保护、默认和私有方法,但不包括继承方法;public Method[] getDeclaredMethods() throws SecurityException
-
getMethods
方法返回某个类的所有公用 (public) 方法,包括其继承类的公用方法;public Method[] getMethods() throws SecurityException
-
getDeclaredMethod
方法可返回所有方法 (不包括继承方法), 其中第一个参数为方法名称,后面的参数为方法的参数对应的 Class 的对象。并且,如果获取的是私有方法(并且不是当前类的,是包里其他的类的私有方法),使用invoke()
方法调用之前,必须给 Method 对象setAccessible(true)
来设置或取消访问检查,以达到访问私有对象的目的。Field 也一样。public Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 如 Method method = clazz.getMethod("study",String.class,String.class); method.setAccessible(true); method.invoke(clazz.newInstance(),"xiaojian","abc");
-
getMethod
方法返回某个特定非私有的方法(包括其继承类的公用方法),其中第一个参数为方法名称,后面的参数为方法的参数对应的 Class 的对象public Method getMethod(String name, Class<?>... parameterTypes) // 如 clazz.getMethod("study",String.class,String.class);
(6)、调用方法
从类中获取方法后,使用
invoke()
方法调用这个方法
public class ReflectTest {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// 使用带参数的构造器,创建实例对象
Class clazz = Person.class;
Constructor constructor = clazz.getConstructor(String.class,int.class);
Object object = constructor.newInstance("小贱",38);
System.out.println(object);
System.out.println("====== 方法调用 ======");
// 调用没有参数的公共 eat 方法
Method m1 = clazz.getMethod("eat");
// invoke 的参数是方法的类的实例对象,不是 Class 对象
m1.invoke(object);
System.out.println("mm");
// 调用父类 Object 的方法
Method m = clazz.getMethod("hashCode");
Object o = m.invoke(object);
System.out.println(o);
System.out.println("mm");
// 调用带一个参数的公共 eat 方法
Method m2 = clazz.getMethod("eat",String.class);
m2.invoke(object,"米饭");
// 调用带三个参数的私有 eat 方法
Method m3 = clazz.getDeclaredMethod("eat",String.class,String.class,int.class);
// 不是当前 main 方法的类 ReflectTest,调用其他类的私有方法,必须 `setAccessible(true)`
m3.setAccessible(true);
System.out.println(m3.toGenericString());
Object result = m3.invoke(object,"牛肉面","可乐",3);
System.out.println(result);
}
}
// 其他的类
class Person{
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("吃");
}
public void eat(String food){
System.out.println("吃" + food);
}
// 私有方法
private String eat(String food,String drink,int count){
return count + "份" + food + "," + count + "份" + drink + ".";
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
(7)、获取成员变量
-
getFields
访问所有公有的成员变量,包括父类的;public Field[] getFields() throws SecurityException;
-
getDeclaredFields
所有已声明的成员变量,包括私有 (private修饰的) 变量,不包括父类的;public Field[] getDeclaredFields() throws SecurityException;
-
getField
访问指定的 成员变量**(非私有的、父类的)**;public Field getField(String name) throws NoSuchFieldException,SecurityException // 获取成员变量值 Object object = clazz.newInstance(); Field field = clazz.getField("xxx"); System.out.println(field.get(object)); // 重新设置成员变量值为 "abc" field.set(object,"abc"); System.out.println(field.get(object));
-
getDeclaredField
访问指定的成员变量**(所有的、不包括父类的)**,访问或修改该成员变量前要setAccessible(true)
。public Field getDeclaredField(String name) throws NoSuchFieldException,SecurityException // 获取成员变量值 Object object = clazz.newInstance(); Field field = clazz.getField("xxx"); field.setAccessible(true); System.out.println(field.get(object)); // 重新设置成员变量值为 "abc" field.set(object,"abc"); System.out.println(field.get(object));
public class ReflectTest_Field { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException { Class clazz = Class.forName("com.xiaojian.basics.basics_test.Son"); Object object = clazz.newInstance(); System.out.println("====== 获取公有的成员变量,包括父类的 ======"); // 获取公有的成员变量,包括父类的 Field[] fields1 = clazz.getFields(); for(Field field : fields1){ System.out.println(field.getName()); } System.out.println("====== 获取所有的成员变量,包括私有的,不包括父类的 ======"); // 获取所有的成员变量,不包括父类的 Field[] fields2 = clazz.getDeclaredFields(); for(Field field : fields2){ System.out.println(field.getName()); } System.out.println("====== 获取成员变量,公共的 ======"); Field field1 = clazz.getField("son_pub"); System.out.println(field1.get(object)); field1.set(object,"重新set son_pub"); System.out.println(field1.get(object)); System.out.println("====== 获取成员变量,父类的 ======"); Field field2 = clazz.getField("father_pub"); System.out.println(field2.get(object)); field1.set(object,"重新set father_pub"); System.out.println(field1.get(object)); System.out.println("====== 获取成员变量,私有的 ======"); Field field3 = clazz.getDeclaredField("son_pri"); // 不止是修改前,访问该成员变量前,都要设置 setAccessible field3.setAccessible(true); System.out.println(field3.get(object)); field1.set(object,"重新set son_pri"); System.out.println(field1.get(object)); } } class Son extends Father{ public String son_pub = "son pub"; private String son_pri = "son pri"; } class Father{ public String father_pub; private String father_pri; }
4、反射的优缺点
尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。
- 优点
- 可扩展性:应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。
- 缺点
- 性能开销:反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多
- 安全问题:由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用。