Java0X1——反射
文章目录
反射简介
什么是反射,根据wiki的给出的解释:
在计算机学中,反射(英语:reflection)是指计算机程序在运行时(runtime)可以访问、检测和修改它本身状态或行为的一种能力。[1]用比喻来说,反射就是程序在运行的时候能够“观察”并且修改自己的行为。
在Java中,简单来说就是在Java程序运行的过程中可以了解到任意的对象的属性和方法。
一般情况下我们可以自己暴露出接口来获取相应的属性,所以初学Java会比较少接触到反射,但是反射机制在一些方面有意想不到的效果。
Class对象
Class对象简介
注意是Class不是class,Class对象是JVM生成的用来加载类的,Java源码编译后会生成字节码文件,JVM就是通过这些.class文件来生成对应的Class类加载到内存中。
注意:每一个类只有一个Class对象
Class对象获取
- 直接通过类获取 Class test1 = Test.class;
- 通过实例 Class test2 = t1.getClass(); Class test2 = t1.getClass();
- 通过完整的类名 Class test3 = Class.forName(“reflection.Test”);
package reflection;
public class Main {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
Class test2 = t1.getClass();
Class test1 = Test.class; //直接通过类获取Class
Class test2 = t1.getClass(); //通过实例对象获取Class
Class test3 = Class.forName("reflection.Test"); //通过类的路径获取
System.out.println(test1);
System.out.println(test2);
System.out.println(test3);
}
}
class Test{
public int id;
private String name ;
}
output:
class reflection.Test
class reflection.Test
class reflection.Test
Class获取信息
通过Class类就可以获取这个类的一系列信息
方法 | 返回 | 描述 |
---|---|---|
getName() | Stirng | 返回类的完整名称 |
getSimpleName() | String | 返回类名 |
getPackage() | String | 获取包名 |
getSuperclass() | Class | 获取父类 |
getInterfaces() | Class[] | 获取所有接口 |
getAnnotations() | Class[] | 获取所有接口 |
package reflection;
public class Main {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
Test t1 = new Test();
Class test1 = Test.class; //直接通过类获取Class
System.out.println("获取完整名称:"+test1.getName());
System.out.println("获取类名:"+test1.getSimpleName());
System.out.println("获取包名:"+test1.getPackage());
System.out.println("获取父类:"+test1.getSuperclass().getName());
System.out.println("获取所有的接口:"+test1.getInterfaces()[0].getName());
System.out.println("获取注解"+test1.getAnnotations()[0].toString());
}
}
@Deprecated(forRemoval = false)
class Test extends Father implements InterfaceTest{
public int id;
private String name ;
}
class Father{}
interface InterfaceTest{}
output:
获取完整名称:reflection.Test
获取类名:Test
获取包名:package reflection
获取父类:reflection.Father
获取所有的接口:reflection.InterfaceTest
获取注解@java.lang.Deprecated(forRemoval=false, since="")
Field
反射机制还可以获取类的字段和属性
获取Field
主要是通过以下的方法获取
方法 | 返回 | 描述 |
---|---|---|
getField(String name) | Field | 获取对应名称的public属性,包括父类的属性 |
getFields() | Field[] | 获取所有public属性,包括父类的属性 |
getDeclareField(String name) | Field | 获取对应名称的属性(只能返回类内声明的属性) |
getDeclareFields() | Field[] | 获取所有属性(只能返回类内声明的属性) |
package reflection;
import java.lang.reflect.Field;
public class Main {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
Class test = Test.class;
Field f1 = test.getField("id");
Field[] f1s = test.getFields();
Field f2 = test.getDeclaredField("name");
Field[] f2s = test.getDeclaredFields();
System.out.println(f1);
System.out.println("------------");
for (Field field : f1s) {
System.out.println(field);
}
System.out.println("------------");
System.out.println(f2);
System.out.println("------------");
for (Field field : f2s) {
System.out.println(field);
}
}
}
output:
public int reflection.Test.id
------------
public int reflection.Test.id
------------
private java.lang.String reflection.Test.name
------------
public int reflection.Test.id
private java.lang.String reflection.Test.name
获取以及修改字段的值
根据获取到的Field以及对象的实例获取该实例对应的值
值得注意的是:当访问私有的成员变量的时候需要设置访问的权限field.setAccessible(true)
通过**field.getModifiers()获取属性的修饰符,其返回值是int,再通过Modifier.isPrivate(int)**判断其是否私有
最后通过**field.get(Object)**获取其对于的值
或者通过**field.set(Object obj , Object value)**修改对象的值
package reflection;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
public class Main {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException, ClassNotFoundException {
Class test = Test.class;
Test example = new Test(1 , "Java");
Field[] fields = test.getDeclaredFields();
/***获取字段的值***/
/***在获取私有成员变量的值的时候需要获得权限***/
for (Field field : fields) {
if(Modifier.isPrivate(field.getModifiers())){
field.setAccessible(true);
}
System.out.println(field.getName()+":"+field.get(example));
}
/*** 修改对象私有成员变量的时候同样需要获取权限,但是前面的已经获取过了,无需再次获取***/
System.out.println("----------------");
fields[0].set(example , 10);
fields[1].set(example,"C++");
for (Field field : fields) {
System.out.println(field.getName()+":"+field.get(example));
}
}
}
class Test{
public int id;
private String name;
public Test(int id, String name) {
this.id = id;
this.name = name;
}
}
output:
id:1
name:Java
----------------
After set:
id:10
name:C++
Method
Method获取
对象方法的获取可以参考属性的获取
package reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class test = Test.class;
Test example = new Test(1 , "Java");
Method m1 = test.getMethod("getId");
Method[] m1s = test.getMethods();
Method m2 = test.getDeclaredMethod("getAverage", int.class, int.class);
Method[] m2s = test.getDeclaredMethods();
System.out.println(m1);
System.out.println("----------");
for (Method method : m1s) {
System.out.println(method);
}
System.out.println("----------");
System.out.println(m2);
System.out.println("----------");
for (Method method : m2s) {
System.out.println(method);
}
}
}
class Test{
public int id;
private String name;
public Test(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
private int getAverage(int a , int b){
return a+(b-a)/2;
}
}
public int reflection.Test.getId()
----------
public java.lang.String reflection.Test.getName()
public int reflection.Test.getId()
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
----------
private int reflection.Test.getAverage(int,int)
----------
public java.lang.String reflection.Test.getName()
public int reflection.Test.getId()
private int reflection.Test.getAverage(int,int)
Process finished with exit code 0
可以发现,getMethods()能获取所有public的方法,包括父类继承的方法toString , wait等等,但还值得注意的是构造函数不在其中
值得注意的是,获取Method的时候传入的参数除了函数名称之外,还有多个可变参数描述方法的参数:
test.getDeclaredMethod(“getAverage”, int.class, int.class);
如果是通过**getMethods()**获取的Method因为缺失函数的参数描述,直接invoke会报错
Method调用
获取Method之后只需要通过invoke调用即可,同Field访问私有方法需要获取权限
如果是通过getDeclareMethods()获取的Method因为缺失函数的参数描述,直接invoke会报错,因此需要使用getDeclareMethod(Object obj , Class … params)
package reflection;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Main {
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
Class test = Test.class;
Test example = new Test(1 , "Java");
Method getAverage = test.getDeclaredMethod("getAverage", int.class, int.class);
getAverage.setAccessible(true);
Object ans = getAverage.invoke(example , Integer.MAX_VALUE ,Integer.MAX_VALUE);
System.out.println("getAverage(example , Integer.MAX_VALUE ,Integer.MAX_VALUE):"+ans);
}
}
class Test{
public int id;
private String name;
public Test(int id, String name) {
this.id = id;
this.name = name;
}
private int getAverage(int a , int b){
return a+(b-a)/2;
}
}
output:
getAverage(example , Integer.MAX_VALUE ,Integer.MAX_VALUE):2147483647
通过反射创建新类
newInstance
可以通过newInstance创建构造新类,不过该方法只能调用无参构造,而且在Java9之后该方法已被废弃
package reflection;
public class Main {
public static void main(String[] args) throws IllegalAccessException, InstantiationException {
Class test = Test.class;
Test example = (Test) test.newInstance();
}
}
class Test{
public int id;
private String name;
}
getDeclareConstructor
通过getDeclareConstructor获取构造函数再调用newInstance
package reflection;
import java.lang.reflect.InvocationTargetException;
public class Main {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
Class test = Test.class;
Test example1 = (Test) test.getDeclaredConstructor(int.class).newInstance(10);
Test example2 = (Test) test.getDeclaredConstructor(String.class).newInstance("Java");
Test example3 = (Test) test.getDeclaredConstructor().newInstance();
}
}
class Test {
public int id;
private String name;
public Test(int id) {
this.id = id;
System.out.println("constructor 1");
}
public Test(String name) {
this.name = name;
System.out.println("constructor 2");
}
private Test() {
System.out.println("constructor 3");
}
}
output:
constructor 1
constructor 2
constructor 3
可以发现,Constructor和上面的Method十分相似,那么应该还有一个getConstructor函数,这个函数和getDeclareConstructor的区别在于getConstructor无法获取私有构造函数
业务场景
虽然日常的算法题目、小项目很少能用到反射,但是不可否认反射在一些业务场景还是十分有用的,举几个栗子:
-
AOP:面向切面的编程,这个的实现就离不开反射机制,对一些方法进行增强
-
SpringBoot中大量的注解,注解本身是没有意义的,但是可以通过反射让注解变得有意义,通过反射机制获取参数、函数、类等等的注解,再做相应的逻辑处理