Java反射

欢迎访问:我的个人网站

Java 反射

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

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

反射提供的功能

在运行时的情况下:

  • 判断一个对象所属的类。
  • 判断一个类所具有的成员变量以及方法。
  • 构造一个类的对象。
  • 调用任意一个对象的方法。
  • 更改任意一个对象的变量。
  • 生成动态代理。

第一个简单的例子

使用下面的Person类进行反射操作:

public class Person {  
    private Integer id;  
	protected String name;  
	public Integer score;  
  
	private int fun(){  
         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类中的所有方法:

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

输出结果:

public java.lang.String com.bestbigkk.Person.getName()
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 getMethod(String name, Class<?> ...paramsType);//获取一个指定名称的方法,并输入与该方法匹配的参数类型。
Method getDeclaredMethod(String name, Class<?>...paramsType);//同上,但是可以获取全部的方法,包括私有类型的。
Method[] getMethods();//获取所有公开的方法
Method[] getDeclaredMethods();//获取所有方法,包括私有。
Method getEnclosingMethod();//获取本地或者匿名类的方法。

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

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

获取修饰符

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

int getMoodifier();

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

OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO

反射操作

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

通过反射创建对象

setAccessible(Boolean access);
public class Person {  
	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对象的过程:

public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {  
      Class clazz = Person.class;  
      //获取构造函数,该函数具有一个String类型的参数
	  Constructor constructor = clazz.getDeclaredConstructor(String.class);  
	  //实例化该对象,并为其传递它所需类型的参数
	  Person p = (Person) constructor.newInstance("Test");  
}

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

public static void main(String[] args) throws NoSuchFieldException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {  
  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的基础上,再为其添加两个方法:

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

通过刚添加的方法演示:

Class clazz = Person.class;  
  
//获取构造函数并实例化一个对象  
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);
发布了27 篇原创文章 · 获赞 1 · 访问量 1862
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 技术工厂 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览