- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Java反射 Field类的使用全方位解析由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
Field 提供有关类或接口的单个字段的信息,以及对它的动态访问权限。反射的字段可能是一个类(静态)字段或实例字段.
每个成员变量有类型和值.
java.lang.reflect.Field 为我们提供了获取当前对象的成员变量的类型,和重新设值的方法.
类中的变量分为两种类型:基本类型和引用类型:
基本类型( 8 种) 。
整数:byte, short, int, long 。
浮点数:float, double 。
字符:char 。
布尔值:boolean 。
引用类型 。
所有的引用类型都继承自 java.lang.Object 。
类,枚举,数组,接口都是引用类型 。
java.io.Serializable 接口,基本类型的包装类(比如 java.lang.Double)也是引用类型 。
java.lang.reflect.Field 提供了两个方法获去变量的类型:
Field.getType():返回这个变量的类型 。
Field.getGenericType():如果当前属性有签名属性类型就返回,否则就返回 Field.getType() 。
1
2
3
4
|
Class<?> getType()
返回一个 Class 对象,它标识了此 Field 对象所表示字段的声明类型。
Type getGenericType()
返回一个 Type 对象,它表示此 Field 对象所表示字段的声明类型。
|
实例:
测试类:
1
2
3
4
5
6
7
8
|
public
class
A
{
public
String id;
protected
String name;
int
age;
private
String sex;
int
[][] ints;
}
|
main方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
public
class
Test
{
/**
* 返回该类所在包的包名字字符串
* @param thisClass 一个类的Class对象
* @return 该类的包名字字符串
*/
public
static
String getThisPackageName(Class<?> thisClass)
{
String thisClassName=thisClass.getName();
String thispackage=thisClassName.substring(
0
,thisClassName.lastIndexOf(
"."
));
return
thispackage;
}
public
static
void
main(String[] args)
throws
ClassNotFoundException, NoSuchFieldException, SecurityException
{
Class strClass=Class.forName(getThisPackageName(Test.
class
)+
".A"
);
//获取类的所有声明的字段
Field[] sField=strClass.getDeclaredFields();
for
(Field field : sField)
{
//获取字段的名字
System.out.printf(
"Field:%-4s|"
,field.getName());
//获取字段的类型的Class类,然后获取规范化的名字
System.out.printf(
"Type:%-18s|"
,field.getType().getCanonicalName());
//获取字段的类型的Type类对象,然后获取类的名字
System.out.printf(
"GenericType:%-18s|"
,field.getGenericType().getTypeName());
System.out.println();
}
}
}
|
运行结果:
1
2
3
4
5
|
Field:id |Type:java.lang.String |GenericType:java.lang.String |
Field:name|Type:java.lang.String |GenericType:java.lang.String |
Field:age |Type:
int
|GenericType:
int
|
Field:sex |Type:java.lang.String |GenericType:java.lang.String |
Field:ints|Type:
int
[][] |GenericType:
int
[][] |
|
可以看到这个两个方法都能正确的返回当前字段的类型。这两个方法的区别还是在返回类型上 。
getType()方法返回的是Class<?>类型的,而getGenericType()返回的是Type类型的.
成员变量可以被以下修饰符修饰:
访问权限控制符:public, protected, private 。
限制只能有一个实例的:static 。
不允许修改的:final 。
不会被序列化:transient 。
线程共享数据的一致性:volatile 。
注解 。
类似获取 Class 的修饰符,我们可以使用 Field.getModifiers() 方法获取当前成员变量的修饰符.
返回 java.lang.reflect.Modifier 中定义的整形值。然后使用 Modifier.toString(int mod)解码成字符串 。
实例:获取上面的A类的字段的类型和修饰符:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public
static
void
printField(Class<?> class1)
{
// 获取类的所有声明的字段
Field[] sField = class1.getDeclaredFields();
for
(Field field : sField)
{
// 获取字段的名字
System.out.printf(
"字段:%-4s|"
, field.getName());
// 获取字段的类型的Class类,然后获取规范化的名字
System.out.printf(
"类型:%-18s|"
,field.getGenericType().getTypeName());
//使用Field.getModifiers(),可获取字段的修饰符编码,
//然后再使用Modifier.toString(int code),来解码成字字符串
System.out.printf(
"修饰符:%s"
, Modifier.toString(field.getModifiers()));
System.out.println();
}
}
|
main方法:
1
2
3
4
5
6
|
public
static
void
main(String[] args)
throws
ClassNotFoundException,
NoSuchFieldException, SecurityException
{
Class AClass = Class.forName(getThisPackageName(Test.
class
) +
".A"
);
printField(AClass);
}
|
运行结果:
1
2
3
4
5
|
字段:id |类型:java.lang.String |修饰符:
public
字段:name|类型:java.lang.String |修饰符:
protected
字段:age |类型:
int
|修饰符:
字段:sex |类型:java.lang.String |修饰符:
private
字段:ints|类型:
int
[][] |修饰符:
|
由于 Field 间接继承了 java.lang.reflect.AnnotatedElement ,因此运行时也可以获得修饰成员变量的注解,当然前提是这个注解被 java.lang.annotation.RetentionPolicy.RUNTIME 修饰.
拿到一个对象后,我们可以在运行时修改它的成员变量的值,对运行时来说,反射修改变量值的操作和类中修改变量的结果是一样的.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
byte
getByte(Object obj)
获取一个静态或实例
byte
字段的值。
int
getInt(Object obj)
获取
int
类型或另一个通过扩展转换可以转换为
int
类型的基本类型的静态或实例字段的值。
short
getShort(Object obj)
获取
short
类型或另一个通过扩展转换可以转换为
short
类型的基本类型的静态或实例字段的值。
long
getLong(Object obj)
获取
long
类型或另一个通过扩展转换可以转换为
long
类型的基本类型的静态或实例字段的值。
float
getFloat(Object obj)
获取
float
类型或另一个通过扩展转换可以转换为
float
类型的基本类型的静态或实例字段的值。
double
getDouble(Object obj)
获取
double
类型或另一个通过扩展转换可以转换为
double
类型的基本类型的静态或实例字段的值。
boolean
getBoolean(Object obj)
获取一个静态或实例
boolean
字段的值。
char
getChar(Object obj)
获取
char
类型或另一个通过扩展转换可以转换为
char
类型的基本类型的静态或实例字段的值。
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
void
setByte(Object obj,
byte
b)
将字段的值设置为指定对象上的一个
byte
值。
void
setShort(Object obj,
short
s)
将字段的值设置为指定对象上的一个
short
值。
void
setInt(Object obj,
int
i)
将字段的值设置为指定对象上的一个
int
值。
void
setLong(Object obj,
long
l)
将字段的值设置为指定对象上的一个
long
值。
void
setFloat(Object obj,
float
f)
将字段的值设置为指定对象上的一个
float
值。
void
setDouble(Object obj,
double
d)
将字段的值设置为指定对象上的一个
double
值。
void
setBoolean(Object obj,
boolean
z)
将字段的值设置为指定对象上的一个
boolean
值。
void
setChar(Object obj,
char
c)
将字段的值设置为指定对象上的一个
char
值。
|
1
|
Object get(Object obj)
|
返回指定对象上此 Field 表示的字段的值.
1
|
void
set(Object obj, Object value)
|
将指定对象变量上此 Field 对象表示的字段设置为指定的新值.
实例:
(1)基本类型的获取和修改:
测试类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
|
public
class
C
{
private
int
a;
private
double
d;
private
boolean
flag;
@Override
public
String toString()
{
return
"C [a="
+ a +
", d="
+ d +
", flag="
+ flag +
"]"
;
}
public
C(
int
a,
double
d,
boolean
flag)
{
super
();
this
.a = a;
this
.d = d;
this
.flag = flag;
}
public
int
getA()
{
return
a;
}
public
void
setA(
int
a)
{
this
.a = a;
}
public
double
getD()
{
return
d;
}
public
void
setD(
double
d)
{
this
.d = d;
}
public
boolean
isFlag()
{
return
flag;
}
public
void
setFlag(
boolean
flag)
{
this
.flag = flag;
}
}
|
main方法类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
import
java.lang.reflect.Field;
public
class
TestGetSetBase
{
public
static
void
main(String[] args)
throws
IllegalArgumentException, IllegalAccessException
{
C c =
new
C(
10
,
123.456
,
true
);
System.out.println(
"c对象的值:"
);
System.out.println(c);
System.out.println(
"-------------------------------"
);
Class cClass = c.getClass();
// 获取所有的字段
Field[] cFields = cClass.getDeclaredFields();
for
(Field field : cFields)
{
field.setAccessible(
true
);
System.out.println(
"获取到字段:"
+ field.getType().getCanonicalName()
+
",值:"
+ field.get(c));
}
for
(Field field : cFields)
{
if
(field.getType().getCanonicalName() ==
"int"
)
{
field.setInt(c,
30
);
}
else
if
(field.getType().getCanonicalName() ==
"double"
)
{
field.setDouble(c,
6789.9901
);
}
else
if
(field.getType().getCanonicalName() ==
"boolean"
)
{
field.setBoolean(c,
false
);
}
// System.out.println(field.getType().getCanonicalName());
}
System.out.println(
"-------------------------------"
);
System.out.println(
"现在的c对象的值:"
);
System.out.println(c);
}
}
|
运行结果:
1
2
3
4
5
6
7
8
9
10
|
c对象的值:
C [a=
10
, d=
123.456
, flag=
true
]
-------------------------------
获取到字段:
int
,值:
10
获取到字段:
double
,值:
123.456
获取到字段:
boolean
,值:
true
-------------------------------
现在的c对象的值:
C [a=
30
, d=
6789.9901
, flag=
false
]
(
2
)引用类型的获取和修改
|
测试类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
public
class
B
{
private
String id;
private
String Name;
public
B(String id, String name)
{
super
();
this
.id = id;
Name = name;
}
public
B(){}
@Override
public
String toString()
{
return
"B [id="
+ id +
", Name="
+ Name +
"]"
;
}
public
String getId()
{
return
id;
}
public
void
setId(String id)
{
this
.id = id;
}
public
String getName()
{
return
Name;
}
public
void
setName(String name)
{
Name = name;
}
}
|
这里测试的类B的字段都是private类型的,在外部类是无法直接访问到这些成员属性的,想要获取和修改只能通过B类的getters和setters方法进行。使用反射我可以绕过这些限制,因为是私有类型的要使用 Field.setAccessible(true); 方法解除限制 。
main方法类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
import
java.lang.reflect.Field;
public
class
TestGetSet
{
public
static
void
main(String[] args)
throws
IllegalArgumentException, IllegalAccessException
{
B b=
new
B(
"B1000"
,
"小明"
);
System.out.println(
"b对象的值:"
);
System.out.println(b);
System.out.println(
"---------------------------------"
);
Class bClass=b.getClass();
//获取共有的字段列表
Field[] bFields=bClass.getDeclaredFields();
System.out.println(
"通过反射获取字段的值:"
);
//获取字段的值,Field.get(对象);
for
(Field field : bFields)
{
field.setAccessible(
true
);
//获取权限
//获取b对象的字段field里面的值
System.out.println(
"字段:"
+field.getType().getName()+
",值:"
+field.get(b));
}
//修改字段的值
for
(Field field : bFields)
{
field.set(b,
"哈哈"
);
}
System.out.println(
"---------------------------------"
);
System.out.println(
"现在的b对象的值:"
);
System.out.println(b);
}
}
|
运行结果:
1
2
3
4
5
6
7
8
9
|
b对象的值:
B [id=B1000, Name=小明]
---------------------------------
通过反射获取字段的值:
字段:java.lang.String,值:B1000
字段:java.lang.String,值:小明
---------------------------------
现在的b对象的值:
B [id=哈哈, Name=哈哈]
|
再说一下setAccessible()方法,Field的setAccessible()方法是从AccessibleObject类继承而来的。AccessibleObject 类是 Field、Method 和 Constructor 对象的基类.
它提供了在使用时 取消默认 Java 语言访问控制检查的能力.
一般情况下,我们并不能对类的私有字段进行操作,利用反射也不例外,但有的时候,例如要序列化的时候,我们又必须有能力去处理这些字段,这时候,我们就需要调用AccessibleObject上的setAccessible()方法来允许这种访问,而由于反射类中的Field,Method和Constructor继承自AccessibleObject,因此,通过在Field,Method和Constructor这些类上调用setAccessible()方法,我们可以操作这些字段无法访问的字段.
返回boolean的方法:
1
2
3
4
5
6
|
boolean
equals(Object obj)
将此 Field 与指定对象比较。
boolean
isEnumConstant()
如果此字段表示枚举类型的元素,则返回
true
;否则返回
false
。
boolean
isSynthetic()
如果此字段是复合字段,则返回
true
;否则返回
false
。
|
返回String的方法:
1
2
3
4
5
6
|
String getName()
返回此 Field 对象表示的字段的名称。
String toGenericString()
返回一个描述此 Field(包括其一般类型)的字符串。
String toString()
返回一个描述此 Field 的字符串。
|
1.equals()和hashCode() 。
1
2
3
4
|
int
hashCode()
返回该 Field 的哈希码。
boolean
equals(Object obj)
将此 Field 与指定对象比较。
|
2.返回注释的方法:
1
2
3
4
|
<T
extends
Annotation> T getAnnotation(Class<T> annotationClass)
如果存在该元素的指定类型的注释,则返回这些注释,否则返回
null
。
Annotation[] getDeclaredAnnotations()
返回直接存在于此元素上的所有注释。
|
3.返回字段所在的类或者接口的Class对象 。
1
2
|
Class<?> getDeclaringClass()
返回表示类或接口的 Class 对象,该类或接口声明由此 Field 对象表示的字段。
|
4.返回字段的类型(Type) 。
1
2
|
Type getGenericType()
返回一个 Type 对象,它表示此 Field 对象所表示字段的声明类型。
|
5.返回修饰符编码:这个方法上面已经提到了,可以使用Modifier.toString(int mod)方法,把获取到的编码转换成修饰符字符串 。
1
2
|
int
getModifiers()
以整数形式返回由此 Field 对象表示的字段的 Java 语言修饰符。
|
在使用反射获取或者修改一个变量的值时,编译器不会进行自动装/拆箱。所以我们无法给 Integer 类型的属性使用 setInt() 方法重新设值,必须给它赋一个 Integer 对象才可以.
否则会因为无法转换类型而出现java.lang.IllegalArgumentException 。
实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
import
java.lang.reflect.Field;
public
class
TestInterger
{
private
Integer integer;
public
TestInterger(Integer integer)
{
this
.integer = integer;
}
public
String toString()
{
return
"TestInterger [integer="
+ integer +
"]"
;
}
public
static
void
main(String[] args)
throws
NoSuchFieldException,
SecurityException, IllegalArgumentException, IllegalAccessException
{
// 这里传入的30是int类型的,会自动装箱成Integer类型
TestInterger testInterger =
new
TestInterger(
30
);
System.out.println(
"testInteger对象:"
);
System.out.println(testInterger);
System.out.println(
"----------------------------------"
);
Class thisClass = testInterger.getClass();
// 获取integer字段
Field inField = thisClass.getDeclaredField(
"integer"
);
inField.setAccessible(
true
);
// 不做访问控制检查
// 获取字段的值
System.out.println(
"成员属性:"
+ inField.getType().getCanonicalName()
+
",值:"
+ inField.get(testInterger));
// 修改成员属性integer:
inField.setInt(testInterger,
90
);
System.out.println(
"----------------------------------"
);
System.out.println(testInterger);
}
}
|
运行结果:
1
2
3
4
5
6
7
8
9
10
11
|
testInteger对象:
TestInterger [integer=
30
]
----------------------------------
成员属性:java.lang.Integer,值:
30
Exception in thread
"main"
java.lang.IllegalArgumentException:
Can not set java.lang.Integer field reflect.fieldtest.type.TestInterger.integer to (
int
)
90
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(Unknown Source)
at sun.reflect.UnsafeObjectFieldAccessorImpl.setInt(Unknown Source)
at java.lang.reflect.Field.setInt(Unknown Source)
at reflect.fieldtest.type.TestInterger.main(TestInterger.java:
34
)
|
解决方法:把上面的 inField.setInt(testInterger, 90);改成 inField.set(testInterger, 90);即可; 。
set方法原型:
Field.set(Objet obj2,Object obj2),这样我们传入90这个int类型的数据时,在编译阶段编译器会自动进行装箱等同于 。
inField.set(testInterger, new Integer(90))。这样运行时,获取到的是Integer类型的,能正常的传入.
这里再来说一下自动拆箱装箱的事情:
自动装箱是java编译器在java原生类型和对应的对象包装类型上做的自动转换.
例如,把int 装换成 Integer double转换成Double等等.
如果是反过来转换,那么叫做自动拆箱,也是编译器为我们做的事情.
强调:自动拆箱装箱发生在编译时刻,反射时发生在程序运行时刻.
为了不混淆,利用反射修改包装类的值的时候,使用set方法,并且尽量手动装箱,也就是写成下面的形式:
1
|
inField.set(testInterger,
new
Integer(
90
))
|
如果你使用 Class.getField() 或者 Class.getFields() 获取非 public 的变量,编译器会报 java.lang.NoSuchFieldException 错.
当你想要获取或者修改 不可修改(final)的变量时,会导致IllegalAccessException.
由于 Field 继承自 AccessibleObject , 我们可以使用 AccessibleObject.setAccessible() 方法告诉安全机制,这个变量可以访问.
也就是Field.setAccessible(true)。告诉安全机制当前的这字段不做访问权限检查,这样我们就能反射修改final修饰成常量了.
以上为个人经验,希望能给大家一个参考,也希望大家多多支持我。如有错误或未考虑完全的地方,望不吝赐教.
原文链接:https://blog.csdn.net/qq_21808961/article/details/80391029 。
最后此篇关于Java反射 Field类的使用全方位解析的文章就讲到这里了,如果你想了解更多关于Java反射 Field类的使用全方位解析的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
关闭。这个问题是opinion-based .它目前不接受答案。 想要改进这个问题? 更新问题,以便 editing this post 可以用事实和引用来回答它. 关闭 5 年前。 Improve
我有一个 mysql 表,其中包含一个名为“id”、“name”和“number”的字段。 每一行的字段'number',都有一个数字。 id name number 1 test 30
我需要获得两个字段之间的最大和最小值。我将 JPA 2.1 与 EclipsLink 结合使用。 这是我的简化查询: SELECT GREATEST(c.min, mc.max), LEAST(c.m
我想知道是否可以询问具有相同字段名称的多个表,并且只写入一次询问的值。可能是为了避免裁员。 例如: SELECT * FROM table WHERE Table1.Status AND Ta
我想知道如何以负增量更新字段,但如果新值小于 1,则删除该行? 是否可以在 case 或 if/else block 中放置和更新语句? 目前我正在执行一个 select 语句来获取当前值,然后使用
嗨,我一直在寻找 secnhatouch 字段的 readOnly 属性,但没有找到它......有人可以帮助我解决这个问题吗 { xtype: 'textfield
SQL Server 2005 报告服务。 我想在报告文本框中使用以下内容: =IIF(IsNothing(Fields!Certification.Value), "", "Certs: "
考虑下表: un_id avl_id avl_date avl_status 1738 6377398 2011-03-10 unavailable 1738 6377399
鉴于集合将包含 50 多万份文档,每个文档都有最大数量的字段(如选项 a 所示)处理可能为空/稀疏的字段的最佳实践是什么? a)将每个具有相同字段和空字段的文档保存为 null 是否更好? { "
尝试开始使用 apioto http://apiato.io/A.getting-started/installation/ 如果我尝试测试 http://api.apiato.dev/registe
我在教程中找不到这两个指令之间的区别。 th:field="${something}"和 th:field="*{something}" 谁能告诉我一些例子? 最佳答案 Reference site
在 MongoDb 中 - 如果我的字段并不总是包含值 - 更好的做法是:在所有记录中保留相同的字段,即使有时这些字段为空或根本不创建这些字段? 10 倍! 最佳答案 字段会占用键的磁盘空间,即使没有
如何使用 factory-boy 定义依赖于其他字段的字段? 例如,我想定义一个 email这取决于 first name和 last name的 User . 我尝试使用 post_generati
嘿嘿, 我遇到了以下问题:我尝试阻止用户为“用户名”和“电子邮件”字段选择相同的值。我正在使用 jquery 表单验证插件 (http://bassistance.de/jquery-plugins/
在性能方面,哪个更适合使用? ...关于可读性/可理解性? ...关于公认的标准? SELECT * FROM Wherever WHERE Greeting IN ('hello', 'hi', '
我想知道使用 this 和 super 访问父类字段的区别。 我们有以下名为 ListItem 的抽象类,它扩展了 Node 类。 public abstract class ListItem {
假设 this 是一个指针,(2) 和 (3) 行如何在下面的 C++ 类中编译,所以应该需要 -> 符号来访问字段(如 (1) 行所示)? ( Source ) #include #include
我想更好地理解通过单独使用 this.field 和 field 来引用类字段有什么区别 this.integerField = 5; 和 integerField = 5; 最佳答案 this 关键
问题:我有一张库存表,还有一张列出正在拍卖的元素的表格。我想要一个别名字段(“isAuction”)来表示具有库存库存编号的项目是否存在于拍卖项目表中。 我写了以下查询: SELECT FROM in
如果我将包含多个字段的文档添加到 Elasticsearch 索引,当我在 Kibana 中查看它时,我每次都会得到相同的字段两次。其中之一将被称为 some_field 另一个将被调用 some_f
我是一名优秀的程序员,十分优秀!