溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何在Java中使用Field類

發(fā)布時(shí)間:2021-04-08 15:28:12 來(lái)源:億速云 閱讀:214 作者:Leah 欄目:開(kāi)發(fā)技術(shù)

如何在Java中使用Field類?很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。

Field 成員變量的介紹

每個(gè)成員變量有類型和值。

java.lang.reflect.Field 為我們提供了獲取當(dāng)前對(duì)象的成員變量的類型,和重新設(shè)值的方法。

獲取變量的類型

類中的變量分為兩種類型:基本類型和引用類型:

基本類型( 8 種)

整數(shù):byte, short, int, long

浮點(diǎn)數(shù):float, double

字符:char

布爾值:boolean

引用類型

所有的引用類型都繼承自 java.lang.Object

類,枚舉,數(shù)組,接口都是引用類型

java.io.Serializable 接口,基本類型的包裝類(比如 java.lang.Double)也是引用類型

java.lang.reflect.Field 提供了兩個(gè)方法獲去變量的類型:

Field.getType():返回這個(gè)變量的類型

Field.getGenericType():如果當(dāng)前屬性有簽名屬性類型就返回,否則就返回 Field.getType()

Class<?> getType() 
          返回一個(gè) Class 對(duì)象,它標(biāo)識(shí)了此 Field 對(duì)象所表示字段的聲明類型。
Type getGenericType() 
          返回一個(gè) Type 對(duì)象,它表示此 Field 對(duì)象所表示字段的聲明類型。

實(shí)例:

測(cè)試類:

public class A
{
 public String id;
 protected String name;
 int age;
 private String sex;
 int[][] ints;
}

main方法:

public class Test
{ 
 /**
  * 返回該類所在包的包名字字符串
  * @param thisClass 一個(gè)類的Class對(duì)象
  * @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類,然后獲取規(guī)范化的名字
   System.out.printf("Type:%-18s|",field.getType().getCanonicalName());
   //獲取字段的類型的Type類對(duì)象,然后獲取類的名字
   System.out.printf("GenericType:%-18s|",field.getGenericType().getTypeName());
   System.out.println();
  }
 }
}

運(yùn)行結(jié)果:

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[][]           |

可以看到這個(gè)兩個(gè)方法都能正確的返回當(dāng)前字段的類型。這兩個(gè)方法的區(qū)別還是在返回類型上

getType()方法返回的是Class<?>類型的,而getGenericType()返回的是Type類型的。

獲取成員變量的修飾符

成員變量可以被以下修飾符修飾:

訪問(wèn)權(quán)限控制符:public, protected, private

限制只能有一個(gè)實(shí)例的:static

不允許修改的:final

不會(huì)被序列化:transient

線程共享數(shù)據(jù)的一致性:volatile

注解

類似獲取 Class 的修飾符,我們可以使用 Field.getModifiers() 方法獲取當(dāng)前成員變量的修飾符。

返回 java.lang.reflect.Modifier 中定義的整形值。然后使用 Modifier.toString(int mod)解碼成字符串

實(shí)例:獲取上面的A類的字段的類型和修飾符:

public static void printField(Class<?> class1)
{
 // 獲取類的所有聲明的字段
 Field[] sField = class1.getDeclaredFields();
 for (Field field : sField)
 {
  // 獲取字段的名字
  System.out.printf("字段:%-4s|", field.getName());
  // 獲取字段的類型的Class類,然后獲取規(guī)范化的名字
  System.out.printf("類型:%-18s|",field.getGenericType().getTypeName());
  //使用Field.getModifiers(),可獲取字段的修飾符編碼,
  //然后再使用Modifier.toString(int code),來(lái)解碼成字字符串
  System.out.printf("修飾符:%s", Modifier.toString(field.getModifiers()));
  System.out.println();
 }
}

main方法:

public static void main(String[] args) throws ClassNotFoundException,
        NoSuchFieldException, SecurityException
{
    Class AClass = Class.forName(getThisPackageName(Test.class) + ".A");
    printField(AClass);    
}

運(yùn)行結(jié)果:

字段:id  |類型:java.lang.String  |修飾符:public
字段:name|類型:java.lang.String  |修飾符:protected
字段:age |類型:int               |修飾符:
字段:sex |類型:java.lang.String  |修飾符:private
字段:ints|類型:int[][]           |修飾符:

由于 Field 間接繼承了 java.lang.reflect.AnnotatedElement ,因此運(yùn)行時(shí)也可以獲得修飾成員變量的注解,當(dāng)然前提是這個(gè)注解被 java.lang.annotation.RetentionPolicy.RUNTIME 修飾。

獲取和修改成員變量的值

拿到一個(gè)對(duì)象后,我們可以在運(yùn)行時(shí)修改它的成員變量的值,對(duì)運(yùn)行時(shí)來(lái)說(shuō),反射修改變量值的操作和類中修改變量的結(jié)果是一樣的。

1.基本類型的獲取方法:

byte getByte(Object obj) 
          獲取一個(gè)靜態(tài)或?qū)嵗?nbsp;byte 字段的值。 
int getInt(Object obj) 
          獲取 int 類型或另一個(gè)通過(guò)擴(kuò)展轉(zhuǎn)換可以轉(zhuǎn)換為 int 類型的基本類型的靜態(tài)或?qū)嵗侄蔚闹怠?nbsp;
 short getShort(Object obj) 
          獲取 short 類型或另一個(gè)通過(guò)擴(kuò)展轉(zhuǎn)換可以轉(zhuǎn)換為 short 類型的基本類型的靜態(tài)或?qū)嵗侄蔚闹怠?nbsp;
 long getLong(Object obj) 
          獲取 long 類型或另一個(gè)通過(guò)擴(kuò)展轉(zhuǎn)換可以轉(zhuǎn)換為 long 類型的基本類型的靜態(tài)或?qū)嵗侄蔚闹怠?nbsp;
 float getFloat(Object obj) 
          獲取 float 類型或另一個(gè)通過(guò)擴(kuò)展轉(zhuǎn)換可以轉(zhuǎn)換為 float 類型的基本類型的靜態(tài)或?qū)嵗侄蔚闹怠?nbsp;
 double getDouble(Object obj) 
          獲取 double 類型或另一個(gè)通過(guò)擴(kuò)展轉(zhuǎn)換可以轉(zhuǎn)換為 double 類型的基本類型的靜態(tài)或?qū)嵗侄蔚闹怠?nbsp;
 boolean getBoolean(Object obj) 
          獲取一個(gè)靜態(tài)或?qū)嵗?nbsp;boolean 字段的值。 
 char getChar(Object obj) 
          獲取 char 類型或另一個(gè)通過(guò)擴(kuò)展轉(zhuǎn)換可以轉(zhuǎn)換為 char 類型的基本類型的靜態(tài)或?qū)嵗侄蔚闹怠?/pre>

2.基本類型的setter方法:

 void setByte(Object obj, byte b) 
          將字段的值設(shè)置為指定對(duì)象上的一個(gè) byte 值。 
 void setShort(Object obj, short s) 
  將字段的值設(shè)置為指定對(duì)象上的一個(gè) short 值。
 void setInt(Object obj, int i) 
          將字段的值設(shè)置為指定對(duì)象上的一個(gè) int 值。 
 void setLong(Object obj, long l) 
          將字段的值設(shè)置為指定對(duì)象上的一個(gè) long 值。 
 void setFloat(Object obj, float f) 
          將字段的值設(shè)置為指定對(duì)象上的一個(gè) float 值。
 void setDouble(Object obj, double d) 
          將字段的值設(shè)置為指定對(duì)象上的一個(gè) double 值。
 void setBoolean(Object obj, boolean z) 
          將字段的值設(shè)置為指定對(duì)象上的一個(gè) boolean 值。 
 void setChar(Object obj, char c) 
          將字段的值設(shè)置為指定對(duì)象上的一個(gè) char 值。

3.引用類型的getters方法:

Object get(Object obj)

返回指定對(duì)象上此 Field 表示的字段的值。

4.引用類型的setters方法:

void set(Object obj, Object value)

將指定對(duì)象變量上此 Field 對(duì)象表示的字段設(shè)置為指定的新值。

實(shí)例:

(1)基本類型的獲取和修改:

測(cè)試類:

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方法類:

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對(duì)象的值:");
  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("現(xiàn)在的c對(duì)象的值:");
  System.out.println(c);
 }
}

運(yùn)行結(jié)果:

c對(duì)象的值:
C [a=10, d=123.456, flag=true]
-------------------------------
獲取到字段:int,值:10
獲取到字段:double,值:123.456
獲取到字段:boolean,值:true
-------------------------------
現(xiàn)在的c對(duì)象的值:
C [a=30, d=6789.9901, flag=false]
(2)引用類型的獲取和修改

測(cè)試類:

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;
 }
}

這里測(cè)試的類B的字段都是private類型的,在外部類是無(wú)法直接訪問(wèn)到這些成員屬性的,想要獲取和修改只能通過(guò)B類的getters和setters方法進(jìn)行。使用反射我可以繞過(guò)這些限制,因?yàn)槭撬接蓄愋偷囊褂?Field.setAccessible(true); 方法解除限制

main方法類:

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對(duì)象的值:");
        System.out.println(b);
        System.out.println("---------------------------------");
        Class bClass=b.getClass();
        //獲取共有的字段列表
        Field[] bFields=bClass.getDeclaredFields();
        System.out.println("通過(guò)反射獲取字段的值:");
        //獲取字段的值,F(xiàn)ield.get(對(duì)象);
        for (Field field : bFields)
        {
            field.setAccessible(true);//獲取權(quán)限
            //獲取b對(duì)象的字段field里面的值
            System.out.println("字段:"+field.getType().getName()+",值:"+field.get(b));
        }
        //修改字段的值
        for (Field field : bFields)
        {
            field.set(b, "哈哈");
        }
        System.out.println("---------------------------------");
        System.out.println("現(xiàn)在的b對(duì)象的值:");
        System.out.println(b);
    }
}

運(yùn)行結(jié)果:

b對(duì)象的值:
B [id=B1000, Name=小明]
---------------------------------
通過(guò)反射獲取字段的值:
字段:java.lang.String,值:B1000
字段:java.lang.String,值:小明
---------------------------------
現(xiàn)在的b對(duì)象的值:
B [id=哈哈, Name=哈哈]

再說(shuō)一下setAccessible()方法,F(xiàn)ield的setAccessible()方法是從AccessibleObject類繼承而來(lái)的。AccessibleObject 類是 Field、Method 和 Constructor 對(duì)象的基類。

它提供了在使用時(shí) 取消默認(rèn) Java 語(yǔ)言訪問(wèn)控制檢查的能力。

一般情況下,我們并不能對(duì)類的私有字段進(jìn)行操作,利用反射也不例外,但有的時(shí)候,例如要序列化的時(shí)候,我們又必須有能力去處理這些字段,這時(shí)候,我們就需要調(diào)用AccessibleObject上的setAccessible()方法來(lái)允許這種訪問(wèn),而由于反射類中的Field,Method和Constructor繼承自AccessibleObject,因此,通過(guò)在Field,Method和Constructor這些類上調(diào)用setAccessible()方法,我們可以操作這些字段無(wú)法訪問(wèn)的字段。

返回boolean的方法:

boolean equals(Object obj) 
          將此 Field 與指定對(duì)象比較。 
boolean isEnumConstant() 
          如果此字段表示枚舉類型的元素,則返回 true;否則返回 false。 
boolean isSynthetic() 
          如果此字段是復(fù)合字段,則返回 true;否則返回 false。

返回String的方法:

String getName() 
         返回此 Field 對(duì)象表示的字段的名稱。 
String toGenericString() 
         返回一個(gè)描述此 Field(包括其一般類型)的字符串。 
String toString() 
         返回一個(gè)描述此 Field 的字符串。

其他方法:

1.equals()和hashCode()

int hashCode()
          返回該 Field 的哈希碼。
boolean equals(Object obj)
          將此 Field 與指定對(duì)象比較。

2.返回注釋的方法:

<T extends Annotation> T  getAnnotation(Class<T> annotationClass)
          如果存在該元素的指定類型的注釋,則返回這些注釋,否則返回 null。
Annotation[] getDeclaredAnnotations()
          返回直接存在于此元素上的所有注釋。

3.返回字段所在的類或者接口的Class對(duì)象

 Class<?> getDeclaringClass()
          返回表示類或接口的 Class 對(duì)象,該類或接口聲明由此 Field 對(duì)象表示的字段。

4.返回字段的類型(Type)

Type getGenericType()
          返回一個(gè) Type 對(duì)象,它表示此 Field 對(duì)象所表示字段的聲明類型。

5.返回修飾符編碼:這個(gè)方法上面已經(jīng)提到了,可以使用Modifier.toString(int mod)方法,把獲取到的編碼轉(zhuǎn)換成修飾符字符串

 int getModifiers()
以整數(shù)形式返回由此 Field 對(duì)象表示的字段的 Java 語(yǔ)言修飾符。

常見(jiàn)錯(cuò)誤 1 :無(wú)法轉(zhuǎn)換類型導(dǎo)致的 java.lang.IllegalArgumentException

在使用反射獲取或者修改一個(gè)變量的值時(shí),編譯器不會(huì)進(jìn)行自動(dòng)裝/拆箱。所以我們無(wú)法給 Integer 類型的屬性使用 setInt() 方法重新設(shè)值,必須給它賦一個(gè) Integer 對(duì)象才可以。

否則會(huì)因?yàn)闊o(wú)法轉(zhuǎn)換類型而出現(xiàn)java.lang.IllegalArgumentException

實(shí)例:

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類型的,會(huì)自動(dòng)裝箱成Integer類型
        TestInterger testInterger = new TestInterger(30);
        System.out.println("testInteger對(duì)象:");
        System.out.println(testInterger);
        System.out.println("----------------------------------");
        Class thisClass = testInterger.getClass();
        // 獲取integer字段
        Field inField = thisClass.getDeclaredField("integer");
        inField.setAccessible(true);// 不做訪問(wèn)控制檢查
        // 獲取字段的值
        System.out.println("成員屬性:" + inField.getType().getCanonicalName()
                + ",值:" + inField.get(testInterger));
        // 修改成員屬性integer:
        inField.setInt(testInterger, 90);
        System.out.println("----------------------------------");
        System.out.println(testInterger);
    }
}

運(yùn)行結(jié)果:

testInteger對(duì)象:
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這個(gè)int類型的數(shù)據(jù)時(shí),在編譯階段編譯器會(huì)自動(dòng)進(jìn)行裝箱等同于

inField.set(testInterger, new Integer(90))。這樣運(yùn)行時(shí),獲取到的是Integer類型的,能正常的傳入。

這里再來(lái)說(shuō)一下自動(dòng)拆箱裝箱的事情:

自動(dòng)裝箱是java編譯器在java原生類型和對(duì)應(yīng)的對(duì)象包裝類型上做的自動(dòng)轉(zhuǎn)換。

例如,把int 裝換成 Integer double轉(zhuǎn)換成Double等等。

如果是反過(guò)來(lái)轉(zhuǎn)換,那么叫做自動(dòng)拆箱,也是編譯器為我們做的事情。

強(qiáng)調(diào):自動(dòng)拆箱裝箱發(fā)生在編譯時(shí)刻,反射時(shí)發(fā)生在程序運(yùn)行時(shí)刻。

為了不混淆,利用反射修改包裝類的值的時(shí)候,使用set方法,并且盡量手動(dòng)裝箱,也就是寫成下面的形式:

inField.set(testInterger, new Integer(90))

常見(jiàn)錯(cuò)誤 2:反射非 public 的變量導(dǎo)致的 NoSuchFieldException

如果你使用 Class.getField() 或者 Class.getFields() 獲取非 public 的變量,編譯器會(huì)報(bào) java.lang.NoSuchFieldException 錯(cuò)。

常見(jiàn)錯(cuò)誤 3 :修改 final類型的變量導(dǎo)致的 IllegalAccessException

當(dāng)你想要獲取或者修改 不可修改(final)的變量時(shí),會(huì)導(dǎo)致IllegalAccessException。

由于 Field 繼承自 AccessibleObject , 我們可以使用 AccessibleObject.setAccessible() 方法告訴安全機(jī)制,這個(gè)變量可以訪問(wèn)。

也就是Field.setAccessible(true)。告訴安全機(jī)制當(dāng)前的這字段不做訪問(wèn)權(quán)限檢查,這樣我們就能反射修改final修飾成常量了。

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI