Java反射

Java 反射

在正常编写Java程序的过程中,我们是知道某个类具有什么样的功能,什么样的调用形式。在这种前提下,我们创建实例并进行相关方法的调用。这种在编译前就已经确认类型的编译方式成为静态编译。反射指的是程序可以在运行期间访问,检测,修改它本身的状态的一种行为,并根据自身行为状态和结果,调整或修改应用所描述行为的状态和相关的语义。换句话说,通过反射Java程序可以加载一个运行时候才能得知名字的类并对其进行操作。

反射是Java中一种非常强大的工具,能够创建更加灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接,因此这种这种反射方式也被成为动态编译。

反射提供的功能 在运行时的情况下:

判断一个对象所属的类。 判断一个类所具有的成员变量以及方法。 构造一个类的对象。 调用任意一个对象的方法。 更改任意一个对象的变量。 生成动态代理。 第一个简单的例子 使用下面的Person类进行反射操作:

    private Integer id;  
	protected String name;  
	public Integer score;  
  
	private int fun(){  language
         System.out.println("私有方法");  
		 return 1;  
    }  
    public Person(){}  
    private Person(Integer val){}  
    protected Person(Boolean val){ }  
  
    public Integer getId() { return id; }  
    public void setId(Integer id) { this.id = id; }  
    public String getName() { return name; }  
    public void setName(String name) { this.name = name; }  
    public Integer getScore() { return score; }  
    public void setScore(Integer score) { this.score = score; }  
}


获取Person类中的所有方法:

Method[] declaredMethods = clazz.getDeclaredMethods();  
for (Method method : declaredMethods) {  
    System.out.println(method.toGenericString());  
}


输出结果:

public void com.bestbigkk.Person.setName(java.lang.String)
public java.lang.Integer com.bestbigkk.Person.getId()
public java.lang.Integer com.bestbigkk.Person.getScore()
public void com.bestbigkk.Person.setScore(java.lang.Integer)
public void com.bestbigkk.Person.setId(java.lang.Integer)
private int com.bestbigkk.Person.fun()


反射可以获取这个类中的字节码信息,包括:类修饰符(public,static等) , 基类,实现的接口,字段,方法 。 并根据获取到的信息来实例化该类的对象,并操作实例对象。Class类是Java反射中的一个核心类,代表了内存中的一个Java类,反射获取的信息都是通过Class对象获取到的。一个Class对象包含以下的几个部分:

Constructor:描述一个类的构造方法。 Field:描述一个类的成员变量。 Method:描述一个类的方法。 Modifier:描述修饰符。 Array:对数组进行操作。 对一个类进行反射操作,首先需要加载这个被操作的类,提供了三种加载类的方式:

使用类的路径进行加载: Class clazz = Class.forName(“类路径”); 使用关键字进行加载: Class clazz = new ClassName().getClass(); 直接进行加载: Class clazz = ClassName.class; 在正常加载一个类之后,就可以尝试获取类中的信息了:

所有方法可以通过其名称了解其含义,需要注意的是,部分方法为getDeclaedXXX形式表示可以获取私有的XXX。比如下面的获取构造函数示例,后续将不再进行说明:

获取构造函数

Package getPackage();	
//获取类的名称
String getName();	
	
//仅获取类public的构造参数
Constructor<?>[] getConstructors();				
//获取类的全部构造参数,包含私有的构造函数
Constructor<?>[] getDeclaedConstructors();		
//获取类的构造参数,该构造函数具有具有指定的参数类型
Constructor<?>   getDeclaredConstructor(Class<?>...params);		
//获取本地或者匿名类的构造函数。
Constructor<?> getEnclosingConstructor();	

在获取一个类的构造函数对象之后,可以对其进行相关操作,包含:

获取其参数。 获取其参数个数。 获取其参数类型。 获取其参数注解。 获取其名称。 获取其修饰符。 获取其抛出的异常。 等等等等…

获取变量

Filed getField(String name);	//获取类或接口的指定公共成员字段
Field getDeclaredField(String name);	//获取指定接口或类的成员字段(所有修饰符修饰均可获取);
Fields[] getFieled();	//获取类或接口的全部字段
Fields[] getDeclaredField();	//获取类或接口所有的字段

//操作, 这里由于大部分方法都是一样的形式,所以使用XXX代替了具体的类型。Object指的是为哪个对象设置属性。
XXX getXXX(Object obj);	//获取某个字段的值,该字段的类型为XXX(Boolean, Byte, Char...);
void setXXX(Object obj, XXX x);//设置某个字段的值,该字段的类型为XXX, 需要传递一个同类型的参数以设置。


获取方法

Method getDeclaredMethod(String name, Class<?>...paramsType);//同上,但是可以获取全部的方法,包括私有类型的。
Method[] getMethods();//获取所有公开的方法
Method[] getDeclaredMethods();//获取所有方法,包括私有。
Method getEnclosingMethod();//获取本地或者匿名类的方法。


在获取方法之后,同样提供了一些操作:

取得参数类型。 取得返回类型。 执行方法。 等等等等…

获取修饰符

针对Method类,Class类, Parameter类均提供了获取修饰符的方法:



返回的值代表了一种修饰符,在 Modifier类里面对其含义进行了全面的定义,内部同时也定义了一些方法以判断当前返回值的含义,比较好理解,下面仅列举了部分: 2019.jpg

反射操作

注意在操作过程中,如果获取到了私有的构造函数,字段,方法,是无法直接对其操作的。需要设置可访问性:

通过反射创建对象



	private String name;  
	private int age;  
  
    public Person(){  
        System.out.println("默认构造方法");  
    }  
    private Person(Integer val){  
        this.age = val;  
        System.out.println("私有构造方法,传递一个int值:"+val);  
    }  
    protected Person(String str){  
        this.name = str;  
        System.out.println("受保护的构造方法,传递一个字符串:"+str);  
    }  
}


下面描述了实例化一个Person对象的过程:

      Class clazz = Person.class;  
      //获取构造函数,该函数具有一个String类型的参数
	  Constructor constructor = clazz.getDeclaredConstructor(String.class);  
	  //实例化该对象,并为其传递它所需类型的参数
	  Person p = (Person) constructor.newInstance("Test");  
}


###通过反射修改属性的值: 依旧使用上面的Person类进行操作:

  Class clazz = Person.class;  
  
  //获取构造函数并实例化一个对象  
  Constructor constructor = clazz.getDeclaredConstructor(Integer.class);  
  constructor.setAccessible(true);  
  Person person = (Person) constructor.newInstance(21);  
  //获取name字段  
  Field field = clazz.getField("name");  
  field.set(person, "XGK");  
  //由于是设置给person对象,可以直接访问  
  System.out.println(person.name);  
  //也可以直接通过Filed类,获取person对象的name值  
  System.out.println(field.get(person)); 
}


通过反射调用方法

在上面Person的基础上,再为其添加两个方法:

    this.name = name;  
	this.age = age;  
}   
private void sayInfo(){  
    System.out.println("你好,我是"+name+", 今年"+age+"岁");  
}


通过刚添加的方法演示:

  
//获取构造函数并实例化一个对象  
Constructor constructor = clazz.getDeclaredConstructor(String.class, Integer.class);  
constructor.setAccessible(true);  
Person person = (Person) constructor.newInstance("小感触" , 21);  
  
//获取方法  
Method method = clazz.getDeclaredMethod("sayInfo");  
method.setAccessible(true);  
method.invoke(person);


———————————————— 版权声明:本文为CSDN博主「BestBigKK」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/Big_KK/article/details/90732769

最近的文章

Spring IOC容器初识

继续阅读