Java简易教程——反射类的字段访问和设置字段值详解

 

img

一、class类

除了int等基本类型外,Java的其他类型全部都是class。可以这样理解,比如String类型,其实也是class类型。这个class是由JVM在执行过程中动态加载的。JVM在第一次读取到一种class类型时,将其加载进内存。这里的Class类型是一个名叫Class的class。

public final class Class {
    private Class() {}
}

JVM为每个加载的class创建了对应的Class实例,并在实例中保存了该class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段等。反射的目的是为了获得某个实例的信息。获取一个class对应的Class实例后,就可以获取该class的所有信息。所以如果想要取得class实例的信息,我们可以通过以下三个方法:

1、静态方法

这个方法有点像对象访问属性的方法,可以直接.获取class实例。 例如:

Class class1 = String.class;

2、getClass()方法

如果我们已经知道变量,那么可以通过getClass()方法获取class实例

String s = "Hello";
Class class1 = s.getClass();

3、Class.forName()获取

如果已经知道class的完整类名,可以通过Class.forName()获取。

Class class1 = Class.forName("java.lang.String");

以上三种方法指向的都是同一个class,所以可以用==来比较,只是可以精确地判断数据类型,但不能作子类型比较。JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,而是动态加载,在运行期根据条件加载不同的实现类。

二、访问字段

获取一个class对应的Class实例后,就可以获取该class的所有信息,就可以获取的class的字段。获取class的字段的方法有4种:

  • Field getField(name):根据字段名获取某个public的field(包括父类)
  • Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
  • Field[] getFields():获取所有public的field(包括父类)
  • Field[] getDeclaredFields():获取当前类的所有field(不包括父类)

可以先来看个例子:


package abc;

public class color {
	public static void main(String[] args) throws Exception {
		Class stuClass = Student.class;
		System.out.println(stuClass.getField("score"));
		// public int abc.Student.score
		System.out.println(stuClass.getDeclaredField("grade"));
		// public int abc.Student.grade
	}

}

class Person {
	public String name;
}

class Student extends Person {
	public int score;
	public int grade;
}


1、getDeclaredField()获取字段值

既然可以获取字段了,那自然就可以获取字段的值了。要注意的是Field事例引入的包是java.lang.reflect.Field;。还要注意的是,代码的执行顺序,获取Class实例,再获取Field实例,然后,用Field.get(Object)获取指定实例的指定字段的值。

另外,如果name字段不是public的话,要跑出错误,这时需要加入Field.setAccessible(true),不管name是不是public,一律允许访问。

例如:

package abc;

import java.lang.reflect.Field;

public class color {
	public static void main(String[] args) throws Exception {
		Object p = new Person("Xiao Ming"); // 创建事例平且赋值
		Class c = p.getClass(); // 获取对象事例
		
		Field f = c.getDeclaredField("name"); // 获取字段
		f.setAccessible(true); // 如果name字段是private,则要加这行。不管name是不是public,一律允许访问。
		
		Object value = f.get(p); // 获取字段的值
		
		System.out.println(value); // Xiao Ming
		
	}

}

class Person {
	public String name;
	// private String name;
	public Person(String name) {
        this.name = name;
    }
}


2、set()设置字段值

可以获取,也可以设置,可以通过Field.set(Object, Object)实现的,其中第一个Object参数是指定的实例,第二个Object参数是待修改的值。

package abc;

import java.lang.reflect.Field;

public class color {
	public static void main(String[] args) throws Exception {
		Person p = new Person("Xiao Ming"); // 创建实例
		System.out.println(p.getName()); // Xiao Ming
		Class c = p.getClass(); // 获取p对象的class信息
		Field f = c.getDeclaredField("name"); // 获取class字段信息
		f.setAccessible(true); // 不管是不是private都可以访问
		f.set(p, "xiao xiao"); // 设置字段的值
		System.out.println(p.getName()); // xiao xiao

	}

}

class Person {
	private String name;

	public Person(String name) {
		this.name = name;
	}

	public String getName() {
		return this.name;
	}
}

上述是Java反射class类和访问class类字段的值和设置字段值。

推荐阅读:

本文为博主原创文章,知识共享,开源精神,转载注明出处。