美文网首页
Java 反射学习《一》 Field详解

Java 反射学习《一》 Field详解

作者: lq_ios | 来源:发表于2020-10-29 10:29 被阅读0次

Java 反射学习《一》 Field详解

Java 反射学习《二》 Constructor详解

Java 反射学习《三》Method详解

如何获取Class对象

每个类在被加载之后,系统就会为该类生成一个Class对象,通过该Class对象,就可以访问大炮JVM中的这个类。
Java中获取Class对象的方式

  1. 使用Class的 public static Class<?> forName(String className),参数的值是某个类的全限定类名(必须添加完整包名)。

  2. 调用某个的class属性来获取Class对象,如Person.class。

  3. 调用某个对象的的getClass()方法,该方法是java.lang.Object类中的一个方法,所以所有的Java对象都可以调用该方法,该方法将会返回该对象所属类对应的Class对象。

    try {
       Class p1 = Class.forName("com.company.Person");
       System.out.println("p1:" + p1.toString());
    }catch (Exception e){
    }
    Class p2 = Person.class;
    Class p3 = (new Person("", 0, "")).getClass();
    System.out.println("p2:" + p2.toString());
    System.out.println("p3:" + p3.toString());
    
    //输出
    p1:class com.company.Person
    p2:class com.company.Person
    p3:class com.company.Person

如何获取父类Class

public class Person<T,U> {
    public  T name;
    public  U age;
}

public class Student extends Person<String,Integer>{
}
  1. public native Class<? super T> getSuperclass() 返回直接继承的父类(没有包含泛型参数)

    Class stuCls = Student.class.getSuperclass();
    System.out.println(stuCls.getName());
    //输出
    //com.company.Person
    
  2. public Type getGenericSuperclass() 返回直接继承的父类(包含泛型参数)

    Type cls = Student.class.getGenericSuperclass();
    System.out.println(cls.toString());
    //输出
    //com.company.Person<java.lang.String, java.lang.Integer>
    
    • Type的方法Type[] getActualTypeArguments(), 获取运行时期泛型的具体类型

      Type cls = Student.class.getGenericSuperclass();
      Type[] types = ((ParameterizedType) cls).getActualTypeArguments();
      for (Type type : types) {
          System.out.println(type.getTypeName());
      }
      //输出 Student继承 Person<String,Integer>
      //java.lang.String
      //java.lang.Integer
      

通过Class对象获取Field

  1. public Field[] getDeclaredFields() throws SecurityException
    返回所有由类或者接口声明的字段,这些变量包括public, protected, default, private修饰的字段,但是不包括继承的字段。返回的字段没有做排序。
    • 如果这个Class对象表示的类或者接口,没有声明变量,这个方法返回一个空数组。
    • 如果这个Class对象表示的是数组,primitive,void类型,这个方法返回一个空组。
public class Person {
    public String name;
    public int age;
    public String sex;

    public Person(String name, int age,String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public void printNameAndAge(String name,int age){
        System.out.println("name:" + name + " age:" + age);
    }
}
   
 Field[] perosnField = Person.class.getDeclaredFields();
 for (int i = 0; i < perosnField.length; i++) {
        System.out.println(perosnField[i].getName());
 }

//输出
name
age
sex
public interface MyInterface {
   final static String a = "a";
   final static String b = "b";
   final static String c = "c";
}

Field[] interfacefields = MyInterface.class.getDeclaredFields();
for (int i = 0; i < interfacefields.length; i++) {
      System.out.println(interfacefields[i].getName());
}

//输出
a
b
c

  1. public Field[] getFields() throws SecurityException
    返回所有由类或者接口声明的public变量,返回的字段没有做排序。
    • 如果Class表示的类或者接口,没有可以访问的公共字段,则返回一个空数组。
    • 如果Class表示的是一个类,则返回这类的父类和已经实现的的接口中的公共字段。
    • 如果Class表示的是一个接口,则返回这个接口和父接口中的公共字段
 public class Person {
    public String name;
    //以下这个字段不会输出
    private int age;
    private String sex;
    
    public Person(String name, int age,String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public void printNameAndAge(String name,int age){
        System.out.println("name:" + name + " age:" + age);
    }
  }
  
    Field[] perosnField = Person.class.getFields();
    for (int i = 0; i < perosnField.length; i++) {
         System.out.println(perosnField[i].getName());
    }
  // 输出
  name
 
public class Student extends Person implements MyInterface{
    public String stuName;
    private int stuAge;

    public Student(String name, int age, String sex) {
        super(name, age, sex);
    }
}

Field[] stuField = Student.class.getFields();
for (int i = 0; i < stuField.length; i++) {
     System.out.println(stuField[i].getName());
}

//输出
stuName
a
b
c
name
public interface SuperInterface {
    final static String superA = "superA";
}

public interface MyInterface extends SuperInterface {
   final static String a = "a";
   final static String b = "b";
   final static String c = "c";
}

 Field[] myField = MyInterface.class.getFields();
 for (int i = 0; i < myField.length; i++) {
     System.out.println(myField[i].getName());
 }
 //输出
 a
 b
 c
 superA
  1. public Field getDeclaredField(String name)
    返回指定名称的Field,如果该Class所指向的对象是数组,找不到数组的length字段
    int[] a = {1, 2, 3, 4};
    Class cls = a.getClass();
    Field[] fields = cls.getDeclaredFields();
    for (int i = 0; i <fields.length ; i++) {
        System.out.println(fields[i].getName());
    }
    try{
       System.out.println(cls.getDeclaredField("length"));
    }catch (Exception e){

    }
    //输出(没有任何输出)
    
  1. public Field getField(String name) 只能返回public修饰的 Field,和上面的getFields()对应,如果该Class所指向的对象是数组,找不到数组的length字段
    int[] a = {1, 2, 3, 4};
    Class cls = a.getClass();
    Field[] fields = cls.getDeclaredFields();
    for (int i = 0; i <fields.length ; i++) {
        System.out.println(fields[i].getName());
    }
    try{
       System.out.println(cls.getField("length"));
    }catch (Exception e){

    }
    //输出(没有任何输出)
    
    Class pcls = Person.class;
    try{
        System.out.println(pcls.getField("name").getName());
    }catch (Exception e){
        System.out.println(e.toString());
    }
   try{
       System.out.println(pcls.getField("age").getName());
   }catch (Exception e){
       System.out.println(e.toString());
   }
   
   //输出
    name
    java.lang.NoSuchFieldException: age

Field的常用方法

  1. public String getName() 返回字段对应的变量名称
    Class pcls = Person.class;
    Field[] fields = pcls.getDeclaredFields();
    for (Field field:fields) {
        System.out.println(field.getName());
    }
    //输出
    name
    age
    sex
    pField
  1. public Class<?> getType() 字段的声明类型(Class)
     Class pcls = Person.class;
     Field[] fields = pcls.getDeclaredFields();
     for (Field field:fields) {
         System.out.println(field.getType());
     }
     //输出
     class java.lang.String
     int
     class java.lang.String
     class java.lang.String
  1. public Type getGenericType() 返回字段的声明的类型(Type)
   Class pcls = Person.class;
   Field[] fields = pcls.getDeclaredFields();
   for (Field field:fields) {
       System.out.println(field.getGenericType().getTypeName());
   }
   //输出
     class java.lang.String
     int
     class java.lang.String
     class java.lang.String
  1. public int getModifiers() 获取成员变量的修饰符
    成员变量可以被以下修饰符修饰:
    • 访问权限控制符:public, protected, private
    • 限制只能有一个实例的:static
    • 不允许修改的:final
    • 不会被序列化:transient
    • 线程共享数据的一致性:volatile

类似获取 Class 的修饰符,我们可以使用 Field.getModifiers() 方法 获取当前 成员变量的修饰符。返回 java.lang.reflect.Modifier 中定义的整形值。然后使用 Modifier.toString(int mod)解码成字符串

   Class pcls = Person.class;
   Field[] fields = pcls.getDeclaredFields();
   for (Field field : fields) {                      System.out.println(Modifier.toString(field.getModifiers()));
   }
   //输出
    public
    private
    private
    private
  1. boolean isEnumConstant() 如果此字段表示枚举类型的元素,则返回 true;否则返回 false。

  2. boolean isSynthetic() 如果此字段是复合字段,则返回 true;否则返回 false。

  1. <T extends Annotation> T getAnnotation(Class<T> annotationClass) 如果存在该元素的指定类型的注释,则返回这些注释,否则返回 null。

  2. Annotation[] getDeclaredAnnotations() 返回直接存在于此元素上的所有注释。

获取和修改Field的值

如果是使用private修饰的字段,在外部类是无法直接访问到这些成员属性的,想要获取和修改只能通过类的getters和setters方法进行。使用反射Field.setAccessible(true);可以解除这些限制。
Field的setAccessible()方法是从AccessibleObject类继承而来的(class Field extends AccessibleObject implements Member)。AccessibleObject 类是 Field、Method 和 Constructor 对象的基类。

  • 基本类型的的getters方法和setters方法:
********getters方法
 //获取一个静态或实例 byte 字段的值。 
 byte getByte(Object obj) 
 // 获取 int 类型或另一个通过扩展转换可以转换为 int 类型的基本类型的静态或实例字段的值。 
 int getInt(Object obj) 
 //获取 short 类型或另一个通过扩展转换可以转换为 short 类型的基本类型的静态或实例字段的值。 
 short getShort(Object obj)
 //获取 long 类型或另一个通过扩展转换可以转换为 long 类型的基本类型的静态或实例字段的值。 
 long getLong(Object obj) 
 //获取 float 类型或另一个通过扩展转换可以转换为 float 类型的基本类型的静态或实例字段的值。         
 float getFloat(Object obj) 
 //获取 double 类型或另一个通过扩展转换可以转换为 double 类型的基本类型的静态或实例字段的值。     
 double getDouble(Object obj) 
 //获取一个静态或实例 boolean 字段的值。        
 boolean getBoolean(Object obj) 
 //获取 char 类型或另一个通过扩展转换可以转换为 char 类型的基本类型的静态或实例字段的值。
 char getChar(Object obj) 
 
 // ********setter方法
 
  //将字段的值设置为指定对象上的一个 byte 值。 
 void setByte(Object obj, byte b) 
 // 将字段的值设置为指定对象上的一个 short 值。   
 void setShort(Object obj, short s) 
 //将字段的值设置为指定对象上的一个 int 值。 
 void setInt(Object obj, int i) 
 //将字段的值设置为指定对象上的一个 long 值。         
 void setLong(Object obj, long l) 
 //将字段的值设置为指定对象上的一个 float 值。       
 void setFloat(Object obj, float f) 
 //将字段的值设置为指定对象上的一个 double 值。       
 void setDouble(Object obj, double d) 
 //将字段的值设置为指定对象上的一个 boolean 值。      
 void setBoolean(Object obj, boolean z) 
 //将字段的值设置为指定对象上的一个 char 值。     
 void setChar(Object obj, char c) 
  • 引用类型的getters方法和setters方法:
  //返回指定对象上此 Field 表示的字段的值。 
  //字段不是静态字段的话,要传入反射类的对象.如果传null是会报 
  //java.lang.NullPointerException 
  //但是如果字段是静态字段的话,传入任何对象都是可以的,包括null 
  Object get(Object obj) 
  
  //将指定对象变量上此 Field 对象表示的字段设置为指定的新值。
  void set(Object obj, Object value)      
public class Person {
    public String name;
    private int age;
    private String sex;
    private String pField;

    public Person(String name, int age,String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public void printNameAndAge(String name,int age){
        System.out.println("name:" + name + " age:" + age);
    }

    public void print(){
        System.out.println("name:" + name + " age:" + age);
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                ", pField='" + pField + '\'' +
                '}';
    }
}

public class FieldDemo {
    public int a = 0;
    long b = (long) 0.0;
    protected float c = (float) 0.0;
    private boolean d = false;
    public final short e = 0;
    //如果将e写成这个样子就能通个Field修改e的值
//    public final short e = (short) (d ? 0 : 0);

    public Person person = new Person("小明",20,"男");

    @Override
    public String toString() {
        return "FieldDemo{" +
                "a=" + a +
                ", b=" + b +
                ", c=" + c +
                ", d=" + d +
                ", e=" + e +
                ", person=" + person +
                '}';
    }
}

public static void main(String[] args) {
        Class demoClass = FieldDemo.class;
        FieldDemo demo = new FieldDemo();
        System.out.println(demo);
        try {
            System.out.println("***********************");
            Field a = demoClass.getDeclaredField("a");
            System.out.println("a被修改前:" + a.getInt(demo));
            a.setInt(demo, 10);
            System.out.println("a被修改后:" + a.getInt(demo));
            System.out.println("***********************");
            Field b = demoClass.getDeclaredField("b");
            System.out.println("b被修改前:" + b.getLong(demo));
            b.setLong(demo, (long) 20.0);
            System.out.println("b被修改后:" + b.getLong(demo));
            System.out.println("***********************");
            Field c = demoClass.getDeclaredField("c");
            System.out.println("c被修改前:" + c.getFloat(demo));
            c.setFloat(demo, (float) 30.0);
            System.out.println("c被修改后:" + c.getFloat(demo));
            System.out.println("***********************");
            Field d = demoClass.getDeclaredField("d");
            //d是私有属性,必须要设置setAccessible(true)否会抛出IllegalAccessException异常
            d.setAccessible(true);
            System.out.println("d被修改前:" + d.getBoolean(demo));
            d.setBoolean(demo, true);
            System.out.println("d被修改后:" + d.getBoolean(demo));
            System.out.println("***********************");
            //e是final修饰的,必须要设置setAccessible(true)否会抛出IllegalAccessException异常
            Field e = demoClass.getDeclaredField("e");
            e.setAccessible(true);
            //这句话是不会生效的,也就是e的值还是0,这是因为在jvm中e被优化成了常量,也就是
            // System.out.println(demo.e);就相当于System.out.println(0);
            System.out.println("e被修改前:" + e.getShort(demo));
            e.setShort(demo, (short) 40);
            System.out.println("e被修改前:" + e.getShort(demo));
            System.out.println("***********************");
            Field person = demoClass.getDeclaredField("person");
            Person p = new Person("小红",10,"女");
            ((Person)person.get(demo)).name = "小明明";
            System.out.println("person 被修改前:" + person.get(demo));
            person.set(demo,p);
            System.out.println("person 被修改后:" + person.get(demo));
            System.out.println(demo);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

//输出
   ***********************
    a被修改前:0
    a被修改后:10
    ***********************
    b被修改前:0
    b被修改后:20
    ***********************
    c被修改前:0.0
    c被修改后:30.0
    ***********************
    d被修改前:false
    d被修改后:true
    ***********************
    e被修改前:0
    e被修改前:40
    ***********************
    person 被修改前:Person{name='小明明', age=20, sex='男', pField='null'}
    person 被修改后:Person{name='小红', age=10, sex='女', pField='null'}

使用反射修改final + static修饰符的变量

public class Demo {
    //防止被jvm编译成常量
    private  static boolean b = false;
    public  static  final int a = b ? 0: 0;
}


public static void main(String[] args) {
      Class class1 = Demo.class;
      Demo demo = new Demo();
        try {
            Field field = class1.getDeclaredField("a");
            field.setAccessible(true);
            System.out.println(demo.a);
            field.setInt(demo,10);
            System.out.println(demo.a);
        } catch (NoSuchFieldException | IllegalAccessException e){
            e.printStackTrace();
        }
    }
   //输出
   java.lang.IllegalAccessException: Can not set static final int field com.company.Demo.a to (int)10
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
    at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:100)
    at java.base/jdk.internal.reflect.UnsafeQualifiedStaticIntegerFieldAccessorImpl.setInt(UnsafeQualifiedStaticIntegerFieldAccessorImpl.java:129)
    at java.base/java.lang.reflect.Field.setInt(Field.java:960)
    
    
  public static void main(String[] args) {
      Class class1 = Demo.class;
      Demo demo = new Demo();
       try {
            Field field = class1.getDeclaredField("a");
            field.setAccessible(true);
            //修改修饰符
            Field modifiers =           field.getClass().getDeclaredField("modifiers");
            modifiers.setAccessible(true);
            modifiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);

            System.out.println(demo.a);
            field.setInt(demo,10);
            System.out.println(demo.a);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    
    //输出
    WARNING: An illegal reflective access operation has occurred
    WARNING: Illegal reflective access by com.company.Main      (file:/Users/qli/Desktop/Android%e9%a1%b9%e7%9b%ae/JavaDemo/out/pro duction/JavaDemo/) to field java.lang.reflect.Field.modifiers
    WARNING: Please consider reporting this to the maintainers of   com.company.Main
    WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
    WARNING: All illegal access operations will be denied in a future release
    0
    10

相关文章

网友评论

      本文标题:Java 反射学习《一》 Field详解

      本文链接:https://www.haomeiwen.com/subject/nelkvktx.html