Java筑基——反射(3):类内部信息获取

相关文章:

1. 获取类类型

获取类类型的方法有四种:

  • 通过类对象获取:对象.getClass(),这个方法是属于 Object 类的;
  • 通过类的 class 对象获取:类名.class
  • 通过全类名获取:Class.forName(“全类名”),这是Class 类的静态方法。当类不能定位到时,会抛出 ClassNotFoundException
  • 通过 ClassLoader.loadClass()加载:loadClass() 也需要传入全类名。当类不能定位到时,会抛出 ClassNotFoundException

下面是代码演示:

package com.java.advanced.features.reflect.clazz;

import com.java.advanced.features.reflect.Apple;

public class GetClassTest {
    public static void main(String[] args) {
        // 1, 通过类对象获取
        Apple apple = new Apple();
        Class appleClass1 = apple.getClass();
        System.out.println("appleClass1 = " + appleClass1);
        // 2, 通过类的 class 对象获取
        Class appleClass2 = Apple.class;
        System.out.println("appleClass2 = " + appleClass2);
        // 3, 通过全类名获取
        Class appleClass3 = null;
        try {
            appleClass3 = Class.forName("com.java.advanced.features.reflect.Apple");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println("appleClass3 = " + appleClass3);
        // 4, 通过 ClassLoader.loadClass()加载
        Class appleClass4 = null;
        try {
            appleClass4 = GetClassTest.class.getClassLoader().loadClass("com.java.advanced.features.reflect.Apple");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        System.out.println("appleClass4 = " + appleClass4);

        System.out.println("result = " + (appleClass1 == appleClass2
                && appleClass2 == appleClass3
                && appleClass3 == appleClass4));

    }
}
/*
打印结果:
appleClass1 = class com.java.advanced.features.reflect.Apple
appleClass2 = class com.java.advanced.features.reflect.Apple
appleClass3 = class com.java.advanced.features.reflect.Apple
appleClass4 = class com.java.advanced.features.reflect.Apple
result = true
 */

上面的代码演示了获取类类型的四种方法,并且我们还可以看到 appleClass1appleClass2appleClass3appleClass4 是相等的,它们都指向同一个对象。这就说明:类只会被加载一次

2. 获取类内部信息

这是 Apple.java,下边我们会多次用到它:

package com.java.advanced.features.reflect;

public class Apple extends Fruit {
    private static int count;
    private final int id = count++;
    String color;
    private int size;
    public float price;

    public Apple() {
    }

    public Apple(String color, int size) {
        this.color = color;
        this.size = size;
    }

    private Apple(String color, Float price) {
        this.color = color;
        this.price = price;
    }

    Apple(String color) {
        this.color = color;
    }

    public static int getCount() {
        return count;
    }

    public String getColor() {
        return color;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        checkSize(size);
        this.size = size;
    }

    public float getPrice() {
        return price;
    }

    private void checkPrice(float price) {
        if (price < 0) {
            throw new IllegalArgumentException("price is not valid, price = " + price);
        }
    }

    void checkSize(Integer size) {
        if (size > 1000) {
            throw new IllegalArgumentException("size is not valid, size = " + size);
        }
    }

    public boolean initColorAndPrice(String color, float price) {
        this.color = color;
        this.price = price;
        return true;
    }
}

package com.java.advanced.features.reflect;

public class Fruit {
    public String taste;

    public Fruit() {
    }

    public String getTaste() {
        return taste;
    }

    public void setTaste(String taste) {
        this.taste = taste;
    }
}

Apple 类包含 :

  • 5 个属性:
    private static int count;
    private final int id = count++;
    String color;
    private int size;
    public float price;
    
  • 4 个构造方法:
    public Apple()
    public Apple(String color, int size)
    private Apple(String color, Float price)
    Apple(String color)
    
  • 1个静态方法:
    public static int getCount()
    
  • 7个成员方法:
    public String getColor()
    public int getSize()
    public void setSize(int size)
    public float getPrice()
    private void checkPrice(float price)
    void checkSize(Integer size)
    public boolean initColorAndPrice(String color, float price)
    

Fruit 类包括:

  • 1个属性:
    public String taste;
    
  • 1个构造方法:
    public Fruit()
    
  • 2个成员方法:
    public String getTaste()
    public void setTaste(String taste)
    

2.1 构造函数

2.1.1 获取构造函数

Class 类中提供了 4 种相应的 API 可以用来获取构造函数的 Constructor 对象:

  1. public Constructor<T> getConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException:返回与指定的 parameterTypes 相匹配的公共构造方法的 Constructor 对象;
  2. public Constructor<?>[] getConstructors() throws SecurityException:返回此类公共构造方法的 Constructor 对象数组;
  3. public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException:带有指定参数列表的构造方法的 Constructor 对象;
  4. public Constructor<?>[] getDeclaredConstructors() throws SecurityException:返回此类所有已声明的构造方法的 Constructor 对象的数组。

在进行代码演示之前,我们先观察一下上面的 4 个 API:
1 和 2 都是只能获取公共的构造方法对象;
3 和 4 是可以获取所有(publicproctectedprivate 等)符合的构造方法对象,它们的函数名都包含了 Declared;
1 和 3 都是可以传入参数列表,返回单个的构造方法对象,还有可以抛出NoSuchMethodException
2 和 4 都不需要传参数,返回一个构造方法对象的数组。

下面是演示代码:

package com.java.advanced.features.reflect.classinternalinfo.constructor;

import com.java.advanced.features.reflect.Apple;

import java.lang.reflect.Constructor;

public class ConstructorGetTest {
    public static void main(String[] args) {
        Class<Apple> appleClass = Apple.class;

        System.out.println("1, 演示 public Constructor<?>[] getConstructors()");
        System.out.println("获取所有的公共构造方法对象数组:");
        Constructor<?>[] constructors = appleClass.getConstructors();
        for (Constructor<?> constructor : constructors) {
            System.out.println(constructor);
        }

        System.out.println("2, 演示 public Constructor<T> getConstructor(Class<?>... parameterTypes)");
        System.out.println("获取指定参数列表的公共构造方法对象");
        try {
            System.out.println("获取 public Apple():");
            Constructor<Apple> constructor = appleClass.getConstructor();
            System.out.println("constructor = " + constructor);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        try {
            System.out.println("获取 public Apple(String color, int size):");
            Constructor<Apple> constructor = appleClass.getConstructor(String.class, int.class);
            // 需要注意的是,下面这种写法会抛出异常:
            // java.lang.NoSuchMethodException: com.java.advanced.features.reflect.Apple.<init>(java.lang.String, java.lang.Integer)
            // 这是因为没有找到参数列表为(String, Integer) 的构造方法
            // Constructor<Apple> constructor1 = appleClass.getConstructor(String.class, Integer.class);
            System.out.println("constructor = " + constructor);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        try {
            System.out.println("获取 private Apple(String color, Float price):");
            appleClass.getConstructor(String.class, Float.class);
        } catch (NoSuchMethodException e) {
            System.out.println(e);
        }

        System.out.println("3, 演示 public Constructor<?>[] getDeclaredConstructors()");
        Constructor<?>[] declaredConstructors = appleClass.getDeclaredConstructors();
        System.out.println("获取所有的构造方法对象:");
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }

        System.out.println("4, 演示 public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)");
        System.out.println("获取指定参数列表的构造方法对象");
        try {
            System.out.println("获取 private Apple(String color, Float price):");
            Constructor<Apple> constructor = appleClass.getDeclaredConstructor(String.class, Float.class);
            System.out.println("constructor = " + constructor);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        try {
            System.out.println("获取 public Apple():");
            Constructor<Apple> constructor = appleClass.getDeclaredConstructor();
            System.out.println("constructor = " + constructor);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }
}
/*
打印结果:
1, 演示 public Constructor<?>[] getConstructors()
获取所有的公共构造方法对象数组:
public com.java.advanced.features.reflect.Apple(java.lang.String,int)
public com.java.advanced.features.reflect.Apple()
2, 演示 public Constructor<T> getConstructor(Class<?>... parameterTypes)
获取指定参数列表的公共构造方法对象
获取 public Apple():
constructor = public com.java.advanced.features.reflect.Apple()
获取 public Apple(String color, int size):
constructor = public com.java.advanced.features.reflect.Apple(java.lang.String,int)
获取 private Apple(String color, Float price):
java.lang.NoSuchMethodException: com.java.advanced.features.reflect.Apple.<init>(java.lang.String, java.lang.Float)
3, 演示 public Constructor<?>[] getDeclaredConstructors()
获取所有的构造方法对象:
com.java.advanced.features.reflect.Apple(java.lang.String)
private com.java.advanced.features.reflect.Apple(java.lang.String,java.lang.Float)
public com.java.advanced.features.reflect.Apple(java.lang.String,int)
public com.java.advanced.features.reflect.Apple()
4, 演示 public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
获取指定参数列表的构造方法对象
获取 private Apple(String color, Float price):
constructor = private com.java.advanced.features.reflect.Apple(java.lang.String,java.lang.Float)
获取 public Apple():
constructor = public com.java.advanced.features.reflect.Apple()
 */

Apple 类有 2 个 public 的构造方法,1 个 private 的构造方法,1 个 default 的构造方法,看一下上面的打印结果,是符合预期的。

需要特别注意的地方是:
需要指定参数列表的两个 API,如果构造方法的参数列表里有基本数据类型(如 intfloatboolean 等)的形参,就应当传入对应的基本数据类型的 Class 对象(如 int.classfloat.classboolean.class 等),而不应该传入(如Integer.classFloat.classBoolean.class 等),同样地,如果构造方法的参数列表里有基本数据类型的包装器类型(如IntegerFloatBoolean 等)的形参,就应当传入对应的包装器类型的 Class 对象(如Integer.classFloat.classBoolean.class 等),而不应该传入基本数据类型的 Class 对象(如 int.classfloat.classboolean.class 等)。否则,就会抛出 NoSuchMethodException
还有一点就是,parameterTypes参数列表的元素顺序一定要与打算获取的构造方法的形参列表中中的参数类型,个数与顺序完全一致。

有同学可能会问,getConstructors() 方法为什么没有获取 Apple 的父类 Fruitpublic 构造方法呢?这是因为父类的构造函数不能被子类继承。

2.1.2 使用构造函数构造实例

使用在 Constructor 类中下的 :

public T newInstance(Object... initargs)
              throws InstantiationException,
                     IllegalAccessException,
                     IllegalArgumentException,
                     InvocationTargetException

参数列表 initargs 是一个Object类型的可变参数列表,我们可以把原本传递给构造函数的形参列表的实参,传递给这个可变参数列表,这里要保证参数的个数,顺序,类型都是一致的。但是,如果实参是基本数据类型,可以直接传入,因为这里基本类型的值会被包装在对应的包装器对象之中。
返回的 T 是一个泛型参数,代表创建出来的新对象。

下面是演示代码:

package com.java.advanced.features.reflect.classinternalinfo.constructor;

import com.java.advanced.features.reflect.Apple;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ConstructorNewInstanceTest {
    public static void main(String[] args) {
        Class<Apple> appleClass = Apple.class;
        try {
            // 1,获取到 private Apple(String color, Float price) 对应的 Constructor 对象
            Constructor<Apple> declaredConstructor = appleClass.getDeclaredConstructor(String.class, Float.class);
            // 因为目标构造方法是 private 的,所以需要设置下边的代码为 true。
            declaredConstructor.setAccessible(true);
//            Apple apple = declaredConstructor.newInstance("red", Float.valueOf(1.8f));
            // 上面一行等价于下面一行
            Apple apple = declaredConstructor.newInstance("red", 1.8f);
            // 错误写法演示1: 参数顺序写错,抛出:java.lang.IllegalArgumentException: argument type mismatch
            // declaredConstructor.newInstance(Float.valueOf(1.8f), "red");
            // 错误写法演示2:参数个数写错,抛出: java.lang.IllegalArgumentException: wrong number of arguments
            // declaredConstructor.newInstance("red");
            System.out.println(apple);
            System.out.println("color = " + apple.getColor() + ", price = " + apple.getPrice());
        } catch (NoSuchMethodException | IllegalAccessException
                | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
        }
        try {
            // 2, 获取 public Apple(String color, int size) 对应的 Constructor 对象
            Constructor<Apple> constructor = appleClass.getConstructor(String.class, int.class);
            Apple apple = constructor.newInstance("red", 100);
            // 上面一行等价于下面一行
            //  Apple apple = constructor.newInstance("red", Integer.valueOf(100));
            System.out.println(apple);
            System.out.println("color = " + apple.getColor() + ", size = " + apple.getSize());
        } catch (NoSuchMethodException | IllegalAccessException
                | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}
/*
打印结果:
com.java.advanced.features.reflect.Apple@4554617c
color = red, price = 1.8
com.java.advanced.features.reflect.Apple@74a14482
color = red, size = 100
 */

需要注意的一点是,public void setAccessible(boolean flag)AccessibleObject 类中的一个方法,而 AccessibleObjectConstructorFieldMethod 的基类。这个方法的作用是修改对象的 accessible 标志(其实是 AccessibleObject 里的 override 字段),默认为 false,表示任何函数,字段,构造方法是否可以访问。如果设置为 true,不管函数,字段,构造方法是什么类型的权限修饰符,都可以访问;如果为 false,那么只有 public 修饰的才可以访问。

在上面的例子中,若不设置declaredConstructor.setAccessible(boolean flag)true,那么会抛出如下异常:

java.lang.IllegalAccessException: Class com.java.advanced.features.reflect.classinternalinfo.constructor.ConstructorNewInstanceTest 
can not access a member of class com.java.advanced.features.reflect.Apple with modifiers "private"
	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)

如果参数列表 initargs 里包含基本数据类型,那么传基本数据类型或对应的包装器类型都是正确的;如果参数列表 initargs 里包含基本数据类型的包装器类型,那么传基本数据类型还是对应的包装器类型也是正确的。

2.2 Field 对象

2.2.1 获取 Field 对象

Class 类有 4 个相应的 API :

  1. public Field getDeclaredField(String name) throws NoSuchFieldException,SecurityException:返回本类声明的指定字段名的 Field 对象;
  2. public Field[] getDeclaredFields() throws SecurityException:返回本类声明的所有字段名的 Field 对象的数组;
  3. public Field getField(String name) throws NoSuchFieldException, SecurityException:返回这个 Class 对象指定字段名的 公共Field 对象,也包括从父类继承来的 公共 Field 对象;
  4. public Field[] getFields() throws SecurityException:返回这个 Class 对象的所有公共Field 对象,,也包括从父类继承来的 公共 Field 对象。

在进行代码演示之前,我们先耐心观察一下上面的 4 个 API:
1 和 2 都是可以获取所有(publicproctectedprivate 等)符合的 Field 对象,但不包括继承的字段,它们的函数名都包含了 Declared(Declared 的含义是本类声明的,不包含从父类继承来的字段);
3 和 4 都是只能获取公共的 Field 对象,但是它的范围是本类和父类中的公共 Field 对象;
1 和 3 都是可以传入一个参数,即变量名,返回单个的 Field 对象,并且都可能抛出 NoSuchFieldException
2 和 4 都不需要传入参数,它们返回的是 Field 对象的数组。

下面是演示代码:

package com.java.advanced.features.reflect.classinternalinfo.field;

import com.java.advanced.features.reflect.Apple;

import java.lang.reflect.Field;

public class FieldGetTest {
    public static void main(String[] args) {
        Class<Apple> appleClass = Apple.class;

        System.out.println("1, 演示 public Field[] getDeclaredFields()");
        System.out.println("获取本类所有已声明字段的 Field 对象数组");
        Field[] declaredFields = appleClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
        
        System.out.println("2, 演示 public Field getDeclaredField(String name)");
        System.out.println("获取本类指定字段名的 Field 对象");
        try {
            System.out.println("获取 private int size;");
            Field sizeField = appleClass.getDeclaredField("size");
            System.out.println("sizeField = " + sizeField);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        try {
            System.out.println("获取 public float price;");
            Field priceField = appleClass.getDeclaredField("price");
            System.out.println("priceField = " + priceField);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
        try {
            System.out.println("获取父类 Fruit 的 public String taste;");
            appleClass.getDeclaredField("taste");
        } catch (NoSuchFieldException e) {
            // 此处抛出异常:java.lang.NoSuchFieldException: taste
            System.out.println(e);
        }

        System.out.println("3, 演示 public Field[] getFields()");
        System.out.println("获取所有已声明字段的公共Field 对象数组,包括继承自父类的");
        Field[] fields = appleClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

        System.out.println("4, 演示 public Field getField(String name)");
        System.out.println("获取指定字段名的公共的 Field 对象");
        try {
            System.out.println("获取 private String size");;
            Field sizeField = appleClass.getField("size");
        } catch (NoSuchFieldException e) {
            System.out.println(e);
        }

        try {
            System.out.println("获取 public float price;");
            Field priceField = appleClass.getField("price");
            System.out.println(priceField);
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }
}

/*
打印结果:
1, 演示 public Field[] getDeclaredFields()
获取本类所有已声明字段的 Field 对象数组
private static int com.java.advanced.features.reflect.Apple.count
private final int com.java.advanced.features.reflect.Apple.id
java.lang.String com.java.advanced.features.reflect.Apple.color
private int com.java.advanced.features.reflect.Apple.size
public float com.java.advanced.features.reflect.Apple.price
2, 演示 public Field getDeclaredField(String name)
获取本类指定字段名的 Field 对象
获取 private int size;
sizeField = private int com.java.advanced.features.reflect.Apple.size
获取 public float price;
priceField = public float com.java.advanced.features.reflect.Apple.price
获取父类 Fruit 的 public String taste;
java.lang.NoSuchFieldException: taste
3, 演示 public Field[] getFields()
获取所有已声明字段的公共Field 对象数组,包括继承自父类的
public float com.java.advanced.features.reflect.Apple.price
public java.lang.String com.java.advanced.features.reflect.Fruit.taste
4, 演示 public Field getField(String name)
获取指定字段名的公共的 Field 对象
获取 private String size
java.lang.NoSuchFieldException: size
获取 public float price;
public float com.java.advanced.features.reflect.Apple.price
 */

回头看一下 Apple 类以及其父类 Fruit 类:
Apple 类本身声明了 5 个属性:

    private static int count;
    private final int id = count++;
    String color;
    private int size;
    public float price;

Fruit 类是 Apple 类的父类,只声明了 1 个属性:

	public String taste;

对照一下上面的代码,可以知道打印结果是符合预期的。

2.2.2 Field 的 set,get 操作

Field 类中,有一系列的 set,get 相关的 API:
我们先来看这一对:

public void set(Object obj, Object value) throws IllegalArgumentException, IllegalAccessException
public Object get(Object obj) throws IllegalArgumentException, IllegalAccessException

简单看一下上面的两个 API:
set 函数:在指定对象(obj)上把 Field 对象表示的字段设置为指定的新值(value);
get 函数:返回指定对象(obj)上这个 Field 对象表示的字段的值。

为了进一步掌握它们的用法,我们去看下面的代码:

package com.java.advanced.features.reflect.classinternalinfo.field;


import com.java.advanced.features.reflect.Apple;
import com.java.advanced.features.reflect.Fruit;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class FieldSetGetTest1 {
    public static void main(String[] args) throws NoSuchMethodException,
            IllegalAccessException,
            InvocationTargetException,
            InstantiationException, NoSuchFieldException {
        Class<Apple> appleClass = Apple.class;
        Constructor<Apple> constructor = appleClass.getConstructor();
        Apple apple = constructor.newInstance();
        System.out.println("1, 获取 String color; 字段,修改它的值并获取修改后的值:");
        Field colorField = appleClass.getDeclaredField("color");
        // 解除此 Field 对象的 Java 语言访问控制
        colorField.setAccessible(true);
        colorField.set(apple, "red");
        String color = (String) colorField.get(apple);
        System.out.println("color = " + color + ", getColor() = " + apple.getColor());

        System.out.println("2, 获取 private int size; 字段,修改它的值并获取修改后的值:");
        Field sizeField = appleClass.getDeclaredField("size");
        // 下面这行不写,会报异常:
        // java.lang.IllegalAccessException: Class com.java.advanced.features.reflect.
        // classinternalinfo.field.FieldApiTest can not access a member of class
        // com.java.advanced.features.reflect.Apple with modifiers "private"
        sizeField.setAccessible(true);
        sizeField.set(apple, 10);
        int size = (int) sizeField.get(apple);
        System.out.println("size = " + size + ", getSize() = " + apple.getSize());

        System.out.println("3, 获取 private static int count; 字段,修改它的值并获取修改后的值:");
        Field countField = appleClass.getDeclaredField("count");
        countField.setAccessible(true);
        // 对于 set 来说, 如果底层字段是一个静态字段,则忽略 obj 变量;它可能为 null。
        // 所以,这里第一个参数 obj,可以为 null。当然,也可以填入对象值。
        countField.set(null, 33);
        // 对于 get 来说, 如果底层字段是一个静态字段,则忽略 obj 变量;它可能为 null。
        // 所以,这里第一个参数 obj,可以为 null。当然,也可以填入对象值。
        int count = (int) countField.get(null);
        System.out.println("count = " + count + ", getCount() = " + Apple.getCount());

        System.out.println("下面演示几种异常:");
        System.out.println("1: 实例字段下,指定对象变量为 null,抛出 NullPointerException 异常");
        try {
            Field priceField = appleClass.getField("price");
            priceField.set(null, 12f);
        } catch (Exception e) {
            System.out.println(e);
        }
        System.out.println("2: 实例字段下,指定对象变量不是类的实例,抛出 IllegalArgumentException 异常");
        try {
            Field priceField = appleClass.getField("price");
            priceField.set(new Fruit(), 12f);
        } catch (Exception e) {
            System.out.println(e);
        }
        System.out.println("3: 底层字段的类型是基本类型,但是设置给 obj 的字段的新值无法转换为基本类型,抛出 IllegalArgumentException");
        try {
            Field priceField = appleClass.getField("price");
            priceField.set(apple, "price");
        } catch (Exception e) {
            System.out.println(e);
        }
    }

}

/*
打印结果:
1, 获取 String color; 字段,修改它的值并获取修改后的值:
color = red, getColor() = red
2, 获取 private int size; 字段,修改它的值并获取修改后的值:
size = 10, getSize() = 10
3, 获取 private static int count; 字段,修改它的值并获取修改后的值:
count = 33, getCount() = 33
下面演示几种异常:
1: 实例字段下,指定对象变量为 null,抛出 NullPointerException 异常
java.lang.NullPointerException
2: 实例字段下,指定对象变量不是类的实例,抛出 IllegalArgumentException 异常
java.lang.IllegalArgumentException: Can not set float field com.java.advanced.features.reflect.Apple.price to com.java.advanced.features.reflect.Fruit
3: 底层字段的类型是基本类型,但是设置给 obj 的字段的新值无法转换为基本类型,抛出 IllegalArgumentException
java.lang.IllegalArgumentException: Can not set float field com.java.advanced.features.reflect.Apple.price to java.lang.String
*/

虽然上面的 setget 函数确实可以用,但是却有一些问题存在:
在取出数据时,我们不得不进行强转。
那么,有没有什么更好的办法呢?有的,有的。在 Field 类中,针对基本数据类型的值,有一系列的 setget 方法:

public boolean getBoolean(Object obj) throwsIllegalArgumentException, IllegalAccessException
public void setBoolean(Object obj, boolean z) throws IllegalArgumentException, IllegalAccessException

public byte getByte(Object obj) throws IllegalArgumentException, IllegalAccessException
public void setByte(Object obj, byte b) throws IllegalArgumentException, IllegalAccessException

public char getChar(Object obj) throws IllegalArgumentException, IllegalAccessException
public void setChar(Object obj, char c) throws IllegalArgumentException, IllegalAccessException

public double getDouble(Object obj) throws IllegalArgumentException, IllegalAccessException
public void setDouble(Object obj, double d) throws IllegalArgumentException, IllegalAccessException

public float getFloat(Object obj) throws IllegalArgumentException, IllegalAccessException
public void setFloat(Object obj, float f) throws IllegalArgumentException, IllegalAccessException

public int getInt(Object obj) throws IllegalArgumentException, IllegalAccessException
public void setInt(Object obj, int i) throws IllegalArgumentException, IllegalAccessException

public long getLong(Object obj) throws IllegalArgumentException, IllegalAccessException
public void setLong(Object obj, long l) throws IllegalArgumentException, IllegalAccessException

public short getShort(Object obj) throws IllegalArgumentException, IllegalAccessException
public void setShort(Object obj, short s) throws IllegalArgumentException, IllegalAccessException

关于这些 API 的使用,下面是一个例子:

package com.java.advanced.features.reflect.classinternalinfo.field;


import com.java.advanced.features.reflect.Apple;
import com.java.advanced.features.reflect.Fruit;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

public class FieldSetGetTest2 {
    public static void main(String[] args) throws NoSuchMethodException,
            IllegalAccessException,
            InvocationTargetException,
            InstantiationException, NoSuchFieldException {
        Class<Apple> appleClass = Apple.class;
        Constructor<Apple> constructor = appleClass.getConstructor();
        Apple apple = constructor.newInstance();

        System.out.println("1, 获取 private int size; 字段,修改它的值并获取修改后的值:");
        Field sizeField = appleClass.getDeclaredField("size");
        sizeField.setAccessible(true);
        sizeField.setInt(apple, 10);
        int size = sizeField.getInt(apple);
        System.out.println("size = " + size + ", getSize() = " + apple.getSize());

        System.out.println("2, 获取 private static int count; 字段,修改它的值并获取修改后的值:");
        Field countField = appleClass.getDeclaredField("count");
        countField.setAccessible(true);
        // 对于 set 来说, 如果底层字段是一个静态字段,则忽略 obj 变量;它可能为 null。
        // 所以,这里第一个参数 obj,可以为 null。当然,也可以填入对象值。
        countField.setInt(null, 33);
        // 对于 get 来说, 如果底层字段是一个静态字段,则忽略 obj 变量;它可能为 null。
        // 所以,这里第一个参数 obj,可以为 null。当然,也可以填入对象值。
        int count = countField.getInt(null);
        System.out.println("count = " + count + ", getCount() = " + Apple.getCount());

        System.out.println("3, 获取 public float price; 字段,修改它的值并获取修改后的值:");
        Field priceField = appleClass.getField("price");
        priceField.setFloat(apple, 12f);
        float price = priceField.getFloat(apple);
        System.out.println("price = " + price + ", getPrice() = " + apple.getPrice());
    }
}
/*
打印结果:
1, 获取 private int size; 字段,修改它的值并获取修改后的值:
size = 10, getSize() = 10
2, 获取 private static int count; 字段,修改它的值并获取修改后的值:
count = 33, getCount() = 33
3, 获取 public float price; 字段,修改它的值并获取修改后的值:
price = 12.0, getPrice() = 12.0
*/

2.3 Method 对象

2.2.1 获取 Method 对象

Class 类有 4 个相应的 API :

  1. public Method getDeclaredMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException:返回本类声明的指定方法名的 Method 对象;
  2. public Method[] getDeclaredMethods() throws SecurityException:返回本类声明的所有方法名的 Method 对象的数组;
  3. public Method getMethod(String name, Class<?>... parameterTypes) throws NoSuchMethodException, SecurityException:返回这个 Class 对象指定方法名的 公共Method 对象,也包括从父类继承来的 公共 Method 对象;
  4. public Method[] getMethods() throws SecurityException:返回这个 Class 对象的所有公共Method 对象,,也包括从父类继承来的 公共 Method 对象。

在进行代码演示之前,我们先观察一下上面的 4 个 API:
1 和 2 都是可以获取所有(publicproctectedprivate 等)符合的 Method 对象,但不包括继承的字段,它们的函数名都包含了 Declared(Declared 的含义是本类声明的,不包含从父类继承来的字段);
3 和 4 都是只能获取公共的 Method 对象,但是它的范围是本类和父类中的公共 Method 对象;
1 和 3 都是可以传入参数,第一个参数name是函数名,第二参数 parameterTypes是可变参数,返回单个的 Method 对象,并且都可能抛出 NoSuchMethodException
2 和 4 都不需要传入参数,它们返回的是 Method 对象的数组。

package com.java.advanced.features.reflect.classinternalinfo.method;

import com.java.advanced.features.reflect.Apple;

import java.lang.reflect.Method;

public class MethodGetTest {
    public static void main(String[] args) {
        Class<Apple> appleClass = Apple.class;
        System.out.println("1, 演示 public Method[] getDeclaredMethods()");
        System.out.println("获取本类声明的所有方法对象,但不包括继承的方法");
        Method[] declaredMethods = appleClass.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println(declaredMethod);
        }

        System.out.println("2, 演示 public Method getDeclaredMethod(String name, Class<?>... parameterTypes)");
        try {
            System.out.println("获取 void checkSize(Integer size)");
            Method checkSizeMethod = appleClass.getDeclaredMethod("checkSize", Integer.class);
            // 下面的写法会抛出异常:java.lang.NoSuchMethodException: com.java.advanced.features.reflect.Apple.checkSize(int)
            // Method checkSizeMethod = appleClass.getDeclaredMethod("checkSize", int.class);
            System.out.println(checkSizeMethod);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        try {
            System.out.println("获取 private void checkPrice(float price)");
             Method checkPriceField = appleClass.getDeclaredMethod("checkPrice", float.class);
            // 下面的写法会抛出异常:java.lang.NoSuchMethodException: com.java.advanced.features.reflect.Apple.checkPrice(java.lang.Float)
            // Method checkPriceField = appleClass.getDeclaredMethod("checkPrice", Float.class);
            System.out.println(checkPriceField);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        System.out.println("3, 演示 public Method[] getMethods()");
        Method[] methods = appleClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }

        System.out.println("4, 演示 public Method getMethod(String name, Class<?>... parameterTypes)");
        try {
            System.out.println("获取 private void checkPrice(float price)");
            Method checkPriceField = appleClass.getMethod("checkPrice", float.class);
        } catch (NoSuchMethodException e) {
            System.out.println(e);
        }

        try {
            System.out.println("获取  public String getColor()");
            Method colorMethod = appleClass.getMethod("getColor");
            System.out.println(colorMethod);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

    }
}
/*
1, 演示 public Method[] getDeclaredMethods()
获取本类声明的所有方法对象,但不包括继承的方法
void com.java.advanced.features.reflect.Apple.checkSize(java.lang.Integer)
private void com.java.advanced.features.reflect.Apple.checkPrice(float)
public java.lang.String com.java.advanced.features.reflect.Apple.getColor()
public float com.java.advanced.features.reflect.Apple.getPrice()
public static int com.java.advanced.features.reflect.Apple.getCount()
public boolean com.java.advanced.features.reflect.Apple.initColorAndPrice(java.lang.String,float)
public int com.java.advanced.features.reflect.Apple.getSize()
public void com.java.advanced.features.reflect.Apple.setSize(int)
2, 演示 public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
获取 void checkSize(Integer size)
void com.java.advanced.features.reflect.Apple.checkSize(java.lang.Integer)
获取 private void checkPrice(float price)
private void com.java.advanced.features.reflect.Apple.checkPrice(float)
3, 演示 public Method[] getMethods()
public java.lang.String com.java.advanced.features.reflect.Apple.getColor()
public float com.java.advanced.features.reflect.Apple.getPrice()
public static int com.java.advanced.features.reflect.Apple.getCount()
public boolean com.java.advanced.features.reflect.Apple.initColorAndPrice(java.lang.String,float)
public int com.java.advanced.features.reflect.Apple.getSize()
public void com.java.advanced.features.reflect.Apple.setSize(int)
public java.lang.String com.java.advanced.features.reflect.Fruit.getTaste()
public void com.java.advanced.features.reflect.Fruit.setTaste(java.lang.String)
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
4, 演示 public Method getMethod(String name, Class<?>... parameterTypes)
获取 private void checkPrice(float price)
java.lang.NoSuchMethodException: com.java.advanced.features.reflect.Apple.checkPrice(float)
获取  public String getColor()
public java.lang.String com.java.advanced.features.reflect.Apple.getColor()
 */

可以看到:
getDeclaredMethods() 是获取本类声明的所有方法对象,但不包括继承的方法,所以获取的是 Apple 自己声明的 8 个方法,而不能获取到从父类继承而来的 2 个方法;
getMethods() 是获取所有的公共方法对象,且包括继承的方法,所以获取的是 Apple自己声明的 6 个公共的方法,从父类 Fruit 继承而来的 2 个公共方法,从 Object 继承而来的 8 个公共方法;
对于getDeclaredMethodgetMethod,需要特别注意参数列表中的可变参数传值问题:这一点和构造方法部分的可变参数列表是一致的。

2.2.2 Method 的 invoke 操作

Method 类中,

public Object invoke(Object obj, Object... args) throws IllegalAccessException,   
IllegalArgumentException,   InvocationTargetException

这个方法的作用是对带有指定参数(args)的指定对象(obj)调用由此 Method 对象表示的底层方法。
下面是演示代码:

package com.java.advanced.features.reflect.classinternalinfo.method;

import com.java.advanced.features.reflect.Apple;

import java.lang.reflect.Method;

public class MethodInvokeTest {
    public static void main(String[] args) throws Exception {
        // 获取 Apple 对象
        Class<Apple> appleClass = Apple.class;
        Apple apple = appleClass.newInstance();
        // 演示 public Object invoke(Object obj, Object... args)
        // 获取 public void initColorAndPrice(String color, float price) 方法,并调用
        Method initColorAndPriceMethod = appleClass.getDeclaredMethod("initColorAndPrice", String.class, float.class);
        boolean result = (boolean) initColorAndPriceMethod.invoke(apple, "red", 18f);
        System.out.println("getColor() = " + apple.getColor() + ", getPrice() = " + apple.getPrice() + ", result = " + result);

        // 获取 void setSize(int size) 方法,并调用
        Method checkSizeMethod = appleClass.getDeclaredMethod("setSize", int.class);
        // 解除此 Method 对象的 Java 语言访问控制
        checkSizeMethod.setAccessible(true);
        // 没有返回值时,返回 null
        Object invoke = checkSizeMethod.invoke(apple, 100);
        System.out.println("getSize() = " + apple.getSize() + ", result = " + invoke);


        // 获取 public static int getCount() 方法,并调用
        Method getCountMethod = appleClass.getMethod("getCount");
        // 底层方法是静态的,那么可以忽略指定的 obj 参数。该参数可以为 null。
        int count = (int) getCountMethod.invoke(null);
        System.out.println("count = " + count);
    }
}
/*
打印结果:
getColor() = red, getPrice() = 18.0, result = true
getSize() = 100, result = null
count = 1
 */

3. 最后

本篇文章,对反射知识使用频率比较高的部分,主要是类内部信息获取做了讲解,以及代码演示。
这里是代码地址的github地址:https://github.com/jhwsx/Java_01_AdvancedFeatures/tree/master/src/com/java/advanced/features/reflect

参考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章