(4)Field类的基本使用和解析

上面一小节主要讲解了class的基本用法和一些方法的源码解析,这一小节主要从讲解字段的基本使用,主要从下面三个方面进行讲解:

  • 获取字段类型
  • 检索和解析字段修饰符
  • 设置和访问字段的值
  • 字段常见错误用法

1.获取字段类型

下面的例子实现了一个类中指定字段的扫描,要得到一个指定字段类型,首先需要得到字段所在的类的类型信息,然后得到给出字段的名称的信息,然后调用class.getField("字段名称")可以得到字段的信息

/**
 * @Project: jdk
 * @description:  类中字段扫描
 * @author: sunkang
 * @create: 2018-09-27 13:28
 * @ModificationHistory who      when       What
 **/
public class FieldSpy {
    public boolean[][] b = {{ false, false }, { true, true } };
    public String name  = "Alice";
    public List list;
    public T val;

    public static void main(String... args) {
//        args = new String[]{FieldSpy.class.getName(),"b"};
        args = new String[]{FieldSpy.class.getName(),"val"};
        try {
            Class c = Class.forName(args[0]);
            Field f = c.getField(args[1]);
            System.out.format("Type: %s%n", f.getType()); //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();
        }
    }
}

输出结果为:

Type: class java.lang.Object
GenericType: T

思考 :
1.为什么Type 为Object类的信息呢?
原有的泛型信息会在编译期间进行擦除,T会被这个类型的上限进行取代,T的类型上限是Object,所以f.getType()为java.lang.Object的类
2.为什么Field.getGenericType()可以得到泛型的类型呢?
原因在于getgenerictype()将在类文件中查询签名属性,如果属性不存在则调用Field.getType()方法,对应的源码如下:

    public Type getGenericType() {
        if (getGenericSignature() != null)
            return getGenericInfo().getGenericType();
        else
            return getType();
    }
 private String getGenericSignature() {return signature;}

思考一下,那么字段的signature的字段信息是怎么设置的,泛型的信息是根据signature来进行解析,这里涉及的比较复杂,这个signature解析出泛型的部分暂时不看。

先看看signature怎么设置的,字段信息是从class对象来的,所以需要找到class.getField(),对应的源码如下:

   @CallerSensitive
    public Field getField(String name)
        throws NoSuchFieldException, SecurityException {
        checkMemberAccess(Member.PUBLIC, Reflection.getCallerClass(), true);
        Field field = getField0(name); //找到指定的字段
        if (field == null) {
            throw new NoSuchFieldException(name);
        }
        return field;
    }
   private Field getField0(String name) throws NoSuchFieldException {
        // Note: the intent is that the search algorithm this routine
        // uses be equivalent to the ordering imposed by
        // privateGetPublicFields(). It fetches only the declared
        // public fields for each class, however, to reduce the number
        // of Field objects which have to be created for the common
        // case where the field being requested is declared in the
        // class which is being queried.
        Field res;
        // Search declared public fields  从本地声明的开始找
        if ((res = searchFields(privateGetDeclaredFields(true), name)) != null) {
            return res;
        }
        // Direct superinterfaces, recursively  //从接口找
        Class[] interfaces = getInterfaces();
        for (int i = 0; i < interfaces.length; i++) {
            Class c = interfaces[i];
            if ((res = c.getField0(name)) != null) {
                return res;
            }
        }
        // Direct superclass, recursively
        if (!isInterface()) {
            Class c = getSuperclass();  //从父类找
            if (c != null) {
                if ((res = c.getField0(name)) != null) {
                    return res;
                }
            }
        }
        return null;
    }

searchFields找到指定的字段信息

   private static Field searchFields(Field[] fields, String name) {
        String internedName = name.intern();
        for (int i = 0; i < fields.length; i++) {
            if (fields[i].getName() == internedName) {//循环字段,然后进行名称匹配
                return getReflectionFactory().copyField(fields[i]); //这里实现了字段的拷贝,关键在这个方法,返回的是拷贝后的对象
            }
        }
        return null;
    }

最终字段的拷贝见下面的Field.copy()方法,signature签名信息是从这里开始设置进去的

Field copy() {
        // This routine enables sharing of FieldAccessor objects
        // among Field objects which refer to the same underlying
        // method in the VM. (All of this contortion is only necessary
        // because of the "accessibility" bit in AccessibleObject,
        // which implicitly requires that new java.lang.reflect
        // objects be fabricated for each reflective call on Class
        // objects.)
        if (this.root != null)
            throw new IllegalArgumentException("Can not copy a non-root Field");

        Field res = new Field(clazz, name, type, modifiers, slot, signature, annotations);//这里的signature被设置进来。
        res.root = this;
        // Might as well eagerly propagate this if already present
        res.fieldAccessor = fieldAccessor;
        res.overrideFieldAccessor = overrideFieldAccessor;
        return res;
    }

2.检索和解析字段修饰符

先给出获取修饰符的字段的例子:

/**
 * @Project: jdk
 * @description:  字段修饰符的测试案例
 * @author: sunkang
 * @create: 2018-10-05 18:40
 * @ModificationHistory who      when       What
 **/
public class FieldModifierTest {
    @Deprecated
    private  volatile int share;

    enum Color { BLACK , WHITE }

    public static void main(String[] args) {
        try {
            Class clazz =   FieldModifierTest.class;
            //得到指定的字段信息
            Field field  =  clazz.getDeclaredField("share");
            //得到所有的修饰符之和,不同的值代表不同修饰符,但是这些值都是进行相加的
            int modifiers =  field.getModifiers();
            //Modifier是一个工具类,toString 表示生成不同的修饰符
            System.out.println("share字段所指定的所有的修饰符:"+Modifier.toString(modifiers));
            //判断修饰符是否是Volatile
            System.out.println("isVolatile:"+Modifier.isVolatile(modifiers));
            //判断修饰符是否是私有的
            System.out.println("isPrivate:"+Modifier.isPrivate(modifiers));
            //得到字段的注解信息
            Deprecated  annotation = field.getAnnotation(Deprecated.class);
            System.out.println("字段的注解信息:"+ annotation.annotationType());


            //得到所有声明的字段
            Field[] flds = Color.class.getDeclaredFields();
            for (Field f: flds ){
                System.out.format("%-8s [ synthetic=%-5b enum_constant=%-5b ]%n",
                        f.getName(), f.isSynthetic(),//f.isSynthetic()表示编译是否是自动生成的方法
                        f.isEnumConstant());// f.isEnumConstant()表示是否是枚举实例
            }
            //class.getEnumConstants可以得到一个类的枚举实例
            System.out.println(Arrays.asList(Color.class.getEnumConstants()));
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

输出结果为:

share字段所指定的所有的修饰符:private volatile
isVolatile:true
isPrivate:true
字段的注解信息:interface java.lang.Deprecated
BLACK    [ synthetic=false enum_constant=true  ]
WHITE    [ synthetic=false enum_constant=true  ]
$VALUES  [ synthetic=true  enum_constant=false ]
[BLACK, WHITE]

从输出结果来看$VALUES信息并没有在代码中有这个字段的信息,原因是在编译期间会生成自动生成合成的字段,枚举类型的$VALUES字段就是在编译期间自动生成,可以用Field.isSynthetic()方法来判断是否是编译期间生成的合成字段
由于 Field 实现了接口 java.lang.reflect.AnnotatedElement,可以检索出带有 java.lang.annotation.RetentionPolicy.RUNTIME的运行注解

3.设置和访问字段的值

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

enum Tweedle {DEE, DUM}

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

    public static void main(String... args) {
        GetSetFiled book = new GetSetFiled();
        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();
        }
    }
}

输出结果为:

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

4.字段常见错误用法

设置不可转换的类型的错误 (字段类型为Integer,结果设置为setInt())
非公有字段的设值错误(字段的修饰符为private)
修改Final字段时的IllegalAccessException (字段类型为修饰符为final,这里是不可以进行修改设值的)

例子如下:

import java.lang.reflect.Field;

public class FieldTrouble {
    public Integer val;
    public static void main(String... args) {
        FieldTrouble ft = new FieldTrouble();
        try {
            Class c = ft.getClass();
            Field f = c.getDeclaredField("val");
            //f.setInt(ft, 42)会抛出IllegalArgumentException异常,参数类型不合法异常,val本身是个Integere类型,但是设置却是int类型
            //此时编译器并不能自动装箱,要转换的类型必须符合Class.isAssignableFrom()
//          f.setInt(ft, 42);
            //下面的两个结果都是不符合,都返回false
            System.out.println(Integer.class.isAssignableFrom(int.class) );
            System.out.println(int.class.isAssignableFrom(Integer.class) );

            //成功设置
            f.set(ft, new Integer(43));

            //只能转换为Class.isAssignableFrom()中的类型
            // 比如Nuber的子类,Integer,或者Number,反射调用的时候,符合 声明类型.isAssignableFrom(设置的类型)
            System.out.println(Number.class.isAssignableFrom(Integer.class));
            System.out.println(Number.class.isAssignableFrom(Float.class));
            c = Foo.class;
            Field f2 = c.getDeclaredField("va2");
            //加了 final字段表示不可改变.加了该修饰符,不可在反射进行设置值
            // 私有成员对外部类是不可见的,如果直接通过反射调用,会报错,需要设置访问权限,setAccessibel为true
            f2.setAccessible(true);
            f2.set(ft,new Integer(12));
            f2.set(ft,new Float(12));
            System.out.println(Foo.getNumber());
    } catch (NoSuchFieldException x) {
        x.printStackTrace();
    } catch (IllegalAccessException x) {
        x.printStackTrace();
    }
    }
}
class Foo{
    private    static   Number va2 = null;

    public static Number getNumber() {
        return  va2;
    }
}

输出结果为:

false
false
true
true
12.0

如果把f.setInt(ft, 42)的注释打开,运行上面的方法会报下面的错误:

Exception in thread "main" java.lang.IllegalArgumentException: Can not set java.lang.Integer field com.java.reflect.merbers.field.FieldTrouble.val to (int)42
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
    at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:191)
    at sun.reflect.UnsafeObjectFieldAccessorImpl.setInt(UnsafeObjectFieldAccessorImpl.java:114)
    at java.lang.reflect.Field.setInt(Field.java:949)
    at com.java.reflect.merbers.field.FieldTrouble.main(FieldTrouble.java:17)

思考一下:为什么会报上面的错误,setInt该方法是怎么调用的

可以对setInt方法进行debug追踪,发现调用链如下:

 public void setInt(Object obj, int i)
        throws IllegalArgumentException, IllegalAccessException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, obj, modifiers);
            }
        }
        getFieldAccessor(obj).setInt(obj, i);//得到字段访问器进行设置
    }
// security check is done before calling this method
    private FieldAccessor getFieldAccessor(Object obj)
        throws IllegalAccessException
    {
        boolean ov = override;
        FieldAccessor a = (ov) ? overrideFieldAccessor : fieldAccessor;
        return (a != null) ? a : acquireFieldAccessor(ov); //直接看acquireFieldAccessor方法
    }
 private FieldAccessor acquireFieldAccessor(boolean overrideFinalCheck) {
        // First check to see if one has been created yet, and take it
        // if so
        FieldAccessor tmp = null;
        if (root != null) tmp = root.getFieldAccessor(overrideFinalCheck);
        if (tmp != null) {
            if (overrideFinalCheck)
                overrideFieldAccessor = tmp;
            else
                fieldAccessor = tmp;
        } else {
            // Otherwise fabricate one and propagate it up to the root
            tmp = reflectionFactory.newFieldAccessor(this, overrideFinalCheck);/创建新的字段访问器
            setFieldAccessor(tmp, overrideFinalCheck);
        }

        return tmp;
    }
 public FieldAccessor newFieldAccessor(Field var1, boolean var2) {
        checkInitted();
        return UnsafeFieldAccessorFactory.newFieldAccessor(var1, var2);//创建字段访问器
    }
static FieldAccessor newFieldAccessor(Field var0, boolean var1) {
        Class var2 = var0.getType();
        boolean var3 = Modifier.isStatic(var0.getModifiers()); //获取字段类型是否是静态的
        boolean var4 = Modifier.isFinal(var0.getModifiers()); //获取字段类型是否是final
        boolean var5 = Modifier.isVolatile(var0.getModifiers());//获取字段类型是否是Volatile
        boolean var6 = var4 || var5;
        boolean var7 = var4 && (var3 || !var1);
        if (var3) {
            UnsafeFieldAccessorImpl.unsafe.ensureClassInitialized(var0.getDeclaringClass());
            if (!var6) {
                if (var2 == Boolean.TYPE) {
                    return new UnsafeStaticBooleanFieldAccessorImpl(var0);
                } else if (var2 == Byte.TYPE) {
                    return new UnsafeStaticByteFieldAccessorImpl(var0);
                } else if (var2 == Short.TYPE) {
                    return new UnsafeStaticShortFieldAccessorImpl(var0);
                } else if (var2 == Character.TYPE) {
                    return new UnsafeStaticCharacterFieldAccessorImpl(var0);
                } else if (var2 == Integer.TYPE) {
                    return new UnsafeStaticIntegerFieldAccessorImpl(var0);
                } else if (var2 == Long.TYPE) {
                    return new UnsafeStaticLongFieldAccessorImpl(var0);
                } else if (var2 == Float.TYPE) {
                    return new UnsafeStaticFloatFieldAccessorImpl(var0);
                } else {
                    return (FieldAccessor)(var2 == Double.TYPE ? new UnsafeStaticDoubleFieldAccessorImpl(var0) : new UnsafeStaticObjectFieldAccessorImpl(var0));
                }
            } else if (var2 == Boolean.TYPE) {
                return new UnsafeQualifiedStaticBooleanFieldAccessorImpl(var0, var7);
            } else if (var2 == Byte.TYPE) {
                return new UnsafeQualifiedStaticByteFieldAccessorImpl(var0, var7);
            } else if (var2 == Short.TYPE) {
                return new UnsafeQualifiedStaticShortFieldAccessorImpl(var0, var7);
            } else if (var2 == Character.TYPE) {
                return new UnsafeQualifiedStaticCharacterFieldAccessorImpl(var0, var7);
            } else if (var2 == Integer.TYPE) {
                return new UnsafeQualifiedStaticIntegerFieldAccessorImpl(var0, var7);
            } else if (var2 == Long.TYPE) {
                return new UnsafeQualifiedStaticLongFieldAccessorImpl(var0, var7);
            } else if (var2 == Float.TYPE) {
                return new UnsafeQualifiedStaticFloatFieldAccessorImpl(var0, var7);
            } else {
                return (FieldAccessor)(var2 == Double.TYPE ? new UnsafeQualifiedStaticDoubleFieldAccessorImpl(var0, var7) : new UnsafeQualifiedStaticObjectFieldAccessorImpl(var0, var7));
            }
        } else if (!var6) {
            if (var2 == Boolean.TYPE) {
                return new UnsafeBooleanFieldAccessorImpl(var0);
            } else if (var2 == Byte.TYPE) {
                return new UnsafeByteFieldAccessorImpl(var0);
            } else if (var2 == Short.TYPE) {
                return new UnsafeShortFieldAccessorImpl(var0);
            } else if (var2 == Character.TYPE) {
                return new UnsafeCharacterFieldAccessorImpl(var0);
            } else if (var2 == Integer.TYPE) {
                return new UnsafeIntegerFieldAccessorImpl(var0);
            } else if (var2 == Long.TYPE) {
                return new UnsafeLongFieldAccessorImpl(var0);
            } else if (var2 == Float.TYPE) {
                return new UnsafeFloatFieldAccessorImpl(var0);
            } else {
                return (FieldAccessor)(var2 == Double.TYPE ? new UnsafeDoubleFieldAccessorImpl(var0) : new UnsafeObjectFieldAccessorImpl(var0));
//最终调用UnsafeObjectFieldAccessorImpl这个类
            }
        } else if (var2 == Boolean.TYPE) {
            return new UnsafeQualifiedBooleanFieldAccessorImpl(var0, var7);
        } else if (var2 == Byte.TYPE) {
            return new UnsafeQualifiedByteFieldAccessorImpl(var0, var7);
        } else if (var2 == Short.TYPE) {
            return new UnsafeQualifiedShortFieldAccessorImpl(var0, var7);
        } else if (var2 == Character.TYPE) {
            return new UnsafeQualifiedCharacterFieldAccessorImpl(var0, var7);
        } else if (var2 == Integer.TYPE) {
            return new UnsafeQualifiedIntegerFieldAccessorImpl(var0, var7);
        } else if (var2 == Long.TYPE) {
            return new UnsafeQualifiedLongFieldAccessorImpl(var0, var7);
        } else if (var2 == Float.TYPE) {
            return new UnsafeQualifiedFloatFieldAccessorImpl(var0, var7);
        } else {
            return (FieldAccessor)(var2 == Double.TYPE ? new UnsafeQualifiedDoubleFieldAccessorImpl(var0, var7) : new UnsafeQualifiedObjectFieldAccessorImpl(var0, var7));
        }
    }

UnsafeObjectFieldAccessorImpl的setInt方法如下:

public int getInt(Object var1) throws IllegalArgumentException {
        throw this.newGetIntIllegalArgumentException();//抛出异常
    }

再来总结一下setInt的调用链,本质上是根据字段类型的信息和修饰符信息匹配到具体的处理字段访问器进行设值处理
Field.setInt()-->
Field.getFieldAccessor() -->
Field.acquireFieldAccessor() -->
ReflectionFactory.newFieldAccessor() -->
UnsafeFieldAccessorFactory.newFieldAccessor -->
new UnsafeObjectFieldAccessorImpl() -->
UnsafeObjectFieldAccessorImpl.setInt() -->
this.throwSetIllegalArgumentException(var2)

再来看看 UnsafeObjectFieldAccessorImpl.set方法

public void set(Object var1, Object var2) throws IllegalArgumentException, IllegalAccessException {
        this.ensureObj(var1);
        if (this.isFinal) {
            this.throwFinalFieldIllegalAccessException(var2);
        }

        if (var2 != null && !this.field.getType().isAssignableFrom(var2.getClass())) {
            //this.field.getType().isAssignableFrom(var2.getClass()检查设置值的类型信息
            this.throwSetIllegalArgumentException(var2);
        }

        unsafe.putObject(var1, this.fieldOffset, var2);
    }
    protected void ensureObj(Object var1) {
        if (!this.field.getDeclaringClass().isAssignableFrom(var1.getClass())) {//检查字段的所在的类是否等于要设置值的对象的类的信息
            this.throwSetIllegalArgumentException(var1);
        }

    }

这也就是为什么要转换的类型必须符合Class.isAssignableFrom(),具体的字段访问器实际上检查了这一个条件。

你可能感兴趣的:((4)Field类的基本使用和解析)