您的位置:首页 > 编程语言 > Java开发

Java反射基础(二)--Fileds对象的使用

2015-10-25 17:50 701 查看
在说Filed之前,我们先来了解一下Member接口. 反射中定义了一个接口
java.lang.reflect.Member
.
java.lang.reflect.Field
,
java.lang.reflect.Method
,
java.lang.reflect.Constructor
都实现了该接口.我们将在接下来的部分介绍这些类.对于每个Member,
我们都会介绍相关的API去获取和操作该Member.每个概念我们都会使用示例代码和示例输出说明.

1.获得字段(Field)的类型

一个filed可以是一个基本数据类型或者一个引用类型.java中有八种基本的数据类型:
boolean,
byte
,
short
,
int
,
long
,
char
,
float
,
double
.引用类型可以使任何的直接或者间接的继承
java.lang.Object
的接口,数组,或者枚举等.
FiledSpy示例类实现了将一个给定的类的二进制文件中包含的field类型和泛型打印出来.

import java.lang.reflect.Field;
import java.util.List;

public class FieldSpy<T> {
public boolean[][] b = {{ false, false }, { true, true } };
public String name  = "Alice";
public List<Integer> list;
public T val;

public static void main(String... args) {
try {
Class<?> c = Class.forName(args[0]);
Field f = c.getField(args[1]);
System.out.format("Type: %s%n", f.getType());
System.out.format("GenericType: %s%n", f.getGenericType());

// production code should handle these exceptions more gracefully
} catch (ClassNotFoundException x) {
x.printStackTrace();
} catch (NoSuchFieldException x) {
x.printStackTrace();
}
}
}


实例输出:

$ java FieldSpy FieldSpy b
Type: class [[Z
GenericType: class [[Z
$ java FieldSpy FieldSpy name
Type: class java.lang.String
GenericType: class java.lang.String
$ java FieldSpy FieldSpy list
Type: interface java.util.List
GenericType: java.util.List<java.lang.Integer>
$ java FieldSpy FieldSpy val
Type: class java.lang.Object
GenericType: T


字段b的类型是一个二维的布尔型数组.
字段val的类型被认定为java.lang.Object.因为泛型的实现方式是在编译的过程中将和泛型有关的信息用相关的类替换.因此此处为java.lang.Object.

2.获取和解析字段修饰符(Filed Modifier)
java中有以下几种字段修饰符:

访问控制修饰符:
public
,
protected
, and
private

运行时领域管理修饰符:
transient
and
volatile

控制一个实例修饰符:
static

禁止值修改修饰符:
final

注解

方法
Field.getModifiers()
可以用来获得一个以整型表示的字段修饰符.这些整形被定义在
java.lang.reflect.Modifier
中.

示例类
FieldModifierSpy
说明了如何搜索一个类中指定的修饰符所修饰的字段.

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import static java.lang.System.out;

enum Spy { BLACK , WHITE }

public class FieldModifierSpy {
volatile int share;
int instance;
class Inner {}

public static void main(String... args) {
try {
Class<?> c = Class.forName(args[0]);
int searchMods = 0x0;
for (int i = 1; i < args.length; i++) {
searchMods |= modifierFromString(args[i]);
}

Field[] flds = c.getDeclaredFields();
out.format("Fields in Class '%s' containing modifiers:  %s%n",
c.getName(),
Modifier.toString(searchMods));
boolean found = false;
for (Field f : flds) {
int foundMods = f.getModifiers();
// Require all of the requested modifiers to be present
if ((foundMods & searchMods) == searchMods) {
out.format("%-8s [ synthetic=%-5b enum_constant=%-5b ]%n",
f.getName(), f.isSynthetic(),
f.isEnumConstant());
found = true;
}
}

if (!found) {
out.format("No matching fields%n");
}

// production code should handle this exception more gracefully
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}

private static int modifierFromString(String s) {
int m = 0x0;
if ("public".equals(s))           m |= Modifier.PUBLIC;
else if ("protected".equals(s))   m |= Modifier.PROTECTED;
else if ("private".equals(s))     m |= Modifier.PRIVATE;
else if ("static".equals(s))      m |= Modifier.STATIC;
else if ("final".equals(s))       m |= Modifier.FINAL;
else if ("transient".equals(s))   m |= Modifier.TRANSIENT;
else if ("volatile".equals(s))    m |= Modifier.VOLATILE;
return m;
}
}


示例输出:

$ java FieldModifierSpy FieldModifierSpy volatile
Fields in Class 'FieldModifierSpy' containing modifiers:  volatile
share    [ synthetic=false enum_constant=false ]

$ java FieldModifierSpy Spy public
Fields in Class 'Spy' containing modifiers:  public
BLACK    [ synthetic=false enum_constant=true  ]
WHITE    [ synthetic=false enum_constant=true  ]

$ java FieldModifierSpy FieldModifierSpy\$Inner final
Fields in Class 'FieldModifierSpy$Inner' containing modifiers:  final
this$0   [ synthetic=true  enum_constant=false ]

$ java FieldModifierSpy Spy private static final
Fields in Class 'Spy' containing modifiers:  private static final
$VALUES  [ synthetic=true  enum_constant=false ]


注意到一些字段被显示出来,虽然他们并没有被定义在该类的源代码中.原因是编译器会自动生成一些字段(synthetic fields :这些字段指的是不是有用户显示声明的,而是在编译的时候,由编译器合成的).如果你想要知道一个字段是否是合成的(synthetic), 也已使用
Field.isSynthetic()
方法.合成字段的集合是依赖于编译器的.然而普遍的使用this$0在内部类中表示最外层的封装类.枚举中使用$VALUES类定义隐式的静态方法values().合成类的名称不一定总是一样的,不同的编译器可能有不同的名字.并且并不是所有的合成字段都会被声明为public.
因为Field实现了
java.lang.reflect.AnnotatedElement
接口,因此我们也可以使用
java.lang.annotation.RetentionPolicy.RUNTIME
获取运行时注解.具体示例见Examining
Class Modifiers and Types..

3.获取和设置字段值.

给我们一个Class实例,我们可以使用反射去修改字段的值.这经常被使用在不能通过通常的方式修改该字段的值的环境下.因为这样的操作通常违反类的设计意图,这应该被谨慎的使用.

Book示例类说明了如何设置long, array, enum类型的字段值.其他类型的对应方法,参考java API.

import java.lang.reflect.Field;
import java.util.Arrays;
import static java.lang.System.out;

enum Tweedle { DEE, DUM }

public class Book {
public long chapters = 0;
public String[] characters = { "Alice", "White Rabbit" };
public Tweedle twin = Tweedle.DEE;

public static void main(String... args) {
Book book = new Book();
String fmt = "%6S:  %-12s = %s%n";

try {
Class<?> c = book.getClass();

Field chap = c.getDeclaredField("chapters");
out.format(fmt, "before", "chapters", book.chapters);
chap.setLong(book, 12);
out.format(fmt, "after", "chapters", chap.getLong(book));

Field chars = c.getDeclaredField("characters");
out.format(fmt, "before", "characters",
Arrays.asList(book.characters));
String[] newChars = { "Queen", "King" };
chars.set(book, newChars);
out.format(fmt, "after", "characters",
Arrays.asList(book.characters));

Field t = c.getDeclaredField("twin");
out.format(fmt, "before", "twin", book.twin);
t.set(book, Tweedle.DUM);
out.format(fmt, "after", "twin", t.get(book));

// production code should handle these exceptions more gracefully
} catch (NoSuchFieldException x) {
x.printStackTrace();
} catch (IllegalAccessException x) {
x.printStackTrace();
}
}
}


示例输出:

$ java Book
BEFORE:  chapters     = 0
AFTER:  chapters     = 12
BEFORE:  characters   = [Alice, White Rabbit]
AFTER:  characters   = [Queen, King]
BEFORE:  twin         = DEE
AFTER:  twin         = DUM


注意: 通过反射设置字段的值往往需要很多的操作.因为很多额外的操作必须被执行,例如检测数据的可访问性.但是从运行时的角度来看,结果是一样的.因为所有的操作被看做一个院子操作来执行,等同于直接修改该变量的值.

e:使用反射会导致一些运行时优化失效.例如,下面的代码很容易被java虚拟机优化:

int x = 1;
x = 2;
x = 3;


但是如果使用反射,则要使用Field.set*(),这是优化失效.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: