上一篇博客:Java反射详解(一)

前言

 在上一篇博客主要说明了反射的基本概念以及 Class类 与反射的关系,以及如何获取 Class对象。获取 Class对象 之后我们就可以获取类的信息以及基于这些信息进行一些操作,也就是我们所说的 反射。下面来逐一介绍一下我们可以通过 Class对象 执行哪些操作。

获取名称信息

Class对象 提供了多种方法来获取与类名称有关的信息,具体方法如下:

getName()

 返回类的真实名称,包括包名。这种名称在编译期间形成,在 Java 虚拟机内部使用(主要用于JVM 在运行时进行类型检查和加载类时使用),通常与编译器生成的符号名称相对应。例如,对于 java.lang.String 类,getName() 将返回 “java.lang.String”

public class TestClass {
    @Test
    public void test() {
        Class<String> stringClass = String.class;
        String className = stringClass.getName();
        System.out.println(className); // java.lang.String
    }
}

 这里需要注意该方法对于数组内部类有特殊表示。当用于数组时它使用 [ 表示数组,有几个 [ 就表示是几维数组;数组的类型用一个字符表示,I 表示 intL 表示类或者接口。

public class TestClass {

    @Test
    public void test() {
        int[][] arr = new int[10][10];
        String className = arr.getClass().getName();
        System.out.println(className); // [[I
    }
}

 对于引用类型的数组末尾会带一个分号(例如String[].class会显示 [Ljava.lang.String;)。其他类型与字符的对应关系为:

getSimpleName()

 返回类的实际名称,不带包信息。例如,对于java.lang.String 类,getSimpleName() 将返回 “String”

public class TestClass {
    @Test
    public void test() {
        Class<String> stringClass = String.class;
        String className = stringClass.getSimpleName();
        System.out.println(className); // String
    }
}

getCanonicalName()

 该方法返回的名称更为友好,它通常会去掉内部命名空间中的冗余部分并且更加易读。例如同样对于二维数组 int[][],该方法返回的是 int[][]getName() 则返回 [[I。这个方法返回的名称通常用于文档和公共 API 中,因为更具有可读性。

public class TestClass {
    @Test
    public void test() {
        int[][] arr = new int[10][10];
        String className = arr.getClass().getCanonicalName();
        System.out.println(className); // int[][]
    }
}

getPackage()

 返回包信息。

public class TestClass {
    @Test
    public void test() {
        Class<String> stringClass = String.class;
        Package aPackage = stringClass.getPackage();
        System.out.println(aPackage); // package java.lang, Java Platform API Specification, version 1.8
    }
}

总览

 不同 Class对象 的各种名称方法的返回值如下图所示:
Java反射详解(二)-LMLPHP

获取字段信息

示例类

 为了方便演示这里手动写一个父类 Creature类 以及子类 Person类 具体代码如下:

Creature类:

package ReflectionTest;

import java.io.Serializable;

public class Creature implements Serializable {
    private char gender;
    public double weight;

    private void breath() {
        System.out.println("生物呼吸");
    }

    public void eat() {
        System.out.println("生物进食");
    }
}

Person类

package ReflectionTest;

public class Person extends Creature {
    private String name;
    protected String id;
    public String age;

    @Override
    public void eat() {
        super.eat();
    }

    public void run() {
        System.out.println("跑步");
    }

    protected void say() {
        System.out.println("说话");
    }
}

getFields()

 返回所有的 public 字段,包括其父类的,如果没有字段则返回空数组。返回类型为 Field[]Field类 也是 Java 反射 API 的一部分,也位于 java.lang.reflect 包下。通过 Field 类,程序可以在运行时访问和修改类的字段,即使这些字段是私有的。

package ReflectionTest;

import org.junit.Test;

import java.lang.reflect.Field;

public class TestClass {
    @Test
    public void test() {
        Class<Person> personClass = Person.class;
        Field[] fields = personClass.getFields();
        for (Field f : fields) {
            System.out.println(f);
            /**
             * 输出结果:
             * public java.lang.String ReflectionTest.Person.age
             * public double ReflectionTest.Creature.weight
             */
        }
    }
}

getDeclaredFields()

 返回本类声明的所有字段,包括非 public 字段,但是不包括父类的。

public class TestClass {
    @Test
    public void test() {
        Class<Person> personClass = Person.class;
        Field[] fields = personClass.getDeclaredFields();
        for (Field f : fields) {
            System.out.println(f);
            /**
             * 输出结果:
             * private java.lang.String ReflectionTest.Person.name
             * protected java.lang.String ReflectionTest.Person.id
             * public java.lang.String ReflectionTest.Person.age
             */
        }
    }
}

getField(String name)

 返回本类或者父类中指定名称的 public 字段,找不到则抛出异常 NoSuchFieldException

public class TestClass {
    @Test
    public void test() throws NoSuchFieldException {
        Class<Person> personClass = Person.class;
        Field field = personClass.getField("age");
        System.out.println(field); // public java.lang.String ReflectionTest.Person.age
    }
}

getDeclaredField(String name)

 返回本类中指定名称的 public 字段,找不到则抛出异常 NoSuchFieldException

Field类

Field 也有很多方法,可以获取字段的信息,也可以通过 Field 访问和操作指定对象中该字段的值。该类的基本方法有:

​​​​​​​​​​// 获取字段的名称
​​​​​​​​public String getName()
​​​​​​​​// 判断当前程序是否有该字段的访问权限
​​​​​​​​public boolean isAccessible()
​​​​​​​​// flag 设为 true 表示忽略 Java 的访问检查机制,以允许读写非 public 的字段
​​​​​​​​public void setAccessible(boolean flag)
​​​​​​​​// 获取指定对象 obj 中该字段的值
​​​​​​​​public Object get(Object obj)
​​​​​​​​// 将指定对象 obj 中该字段的值设为 value
​​​​​​​​public void set(Object obj, Object value)
// 返回字段的修饰符
​​​​​​​​public int getModifiers()
​​​​​​​​// 返回字段的类型
​​​​​​​​public Class<? > getType()
​​​​​​​​// 以基本类型操作字段
​​​​​​​​public void setBoolean(Object obj, boolean z)
​​​​​​​​public boolean getBoolean(Object obj)
​​​​​​​​public void setDouble(Object obj, double d)
​​​​​​​​public double getDouble(Object obj)
​​​​​​​​// 查询字段的注解信息
​​​​​​​​public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
​​​​​​​​public Annotation[] getDeclaredAnnotations()
​​

getModifiers() 方法需要注意一下,该方法用于返回字段的修饰符,但是返回的是一个 int,我们可以通过 Modifier 类的静态方法 isPublic()、isStatic()、isPrivate()等方法 进行解读。

public class TestClass {
    @Test
    public void test() throws NoSuchFieldException {
        Class<Person> personClass = Person.class;
        Field field = personClass.getField("age");

        int modifier = field.getModifiers();
        System.out.println(modifier); // 1
        System.out.println("isPublic: " + Modifier.isPublic(modifier)); // isPublic: true
        System.out.println("isStatic: " + Modifier.isStatic(modifier)); // isStatic: false
        System.out.println("isPrivate: " + Modifier.isPrivate(modifier)); // isPrivate: false
    }
}

通过查看源码发现每一种修饰符 getModifiers() 方法都会返回特定的 int值,以下是代码中定义的修饰符常量及其含义:

  • PUBLIC:0x00000001,表示 public 修饰符。
  • PRIVATE:0x00000002,表示 private 修饰符。
  • PROTECTED:0x00000004,表示 protected 修饰符。
  • STATIC:0x00000008,表示 static 修饰符。
  • FINAL:0x00000010,表示 final 修饰符。
  • SYNCHRONIZED:0x00000020,表示 synchronized 修饰符。
  • VOLATILE:0x00000040,表示 volatile 修饰符。
  • TRANSIENT:0x00000080,表示 transient 修饰符。
  • NATIVE:0x00000100,表示 native 修饰符。
  • INTERFACE:0x00000200,表示 interface 修饰符。
  • ABSTRACT:0x00000400,表示 abstract 修饰符。
  • STRICT:0x00000800,表示 strictfp 修饰符。

Modifier 类提供了如下方法来检查修饰符:

  • isPublic(int mod):检查是否设置了 public 修饰符。
  • isPrivate(int mod):检查是否设置了 private 修饰符。
  • isProtected(int mod):检查是否设置了 protected 修饰符。
  • isStatic(int mod):检查是否设置了 static 修饰符。
  • isFinal(int mod):检查是否设置了 final 修饰符。
  • isSynchronized(int mod):检查是否设置了 synchronized 修饰符。
  • isVolatile(int mod):检查是否设置了 volatile 修饰符。
  • isTransient(int mod):检查是否设置了 transient 修饰符。
  • isNative(int mod):检查是否设置了 native 修饰符。
  • isInterface(int mod):检查是否设置了 interface 修饰符。
  • isAbstract(int mod):检查是否设置了 abstract 修饰符。
  • isStrict(int mod):检查是否设置了 strictfp 修饰符。

获取方法信息

 通过 Class对象 也可以获取类中的方法信息,方法使用方法与获取字段信息部分大体一致,这里就不在赘述代码示例了。具体方法如下:

返回所有的 public 方法,包括其父类的,如果没有方法,返回空数组
​​​​​​​​public Method[] getMethods()
返回本类声明的所有方法,包括 非public 的,但不包括父类的
​​​​​​​​public Method[] getDeclaredMethods()
返回本类或父类中指定名称和参数类型的public方法,找不到抛出异常 NoSuchMethodException
​​​​​​​​public Method getMethod(String name, Class<? >... parameterTypes)
返回本类中声明的指定名称和参数类型的方法,找不到抛出异常 NoSuchMethodException
​​​​​​​​public Method getDeclaredMethod(String name, Class<? >... parameterTypes)​​

Method类

 通过 Method类 可以获取方法的信息,也可以通过该类调用对象的方法,方法如下:

返回此 Method 表示的方法的名称。
public String getName()
返回此 Method 表示的方法的修饰符。与 Field类 的用法一致也是使用 Modifierpublic int getModifiers()
控制是否绕过 Java 语言的访问检查
public void setAccessible(boolean flag)
在指定对象obj上调用 Method 代表的方法,传递的参数列表为 args
public Object invoke(Object obj, Object... args)
返回此 Method 表示的方法的返回类型。
public Class<?> getReturnType()
返回此 Method 表示的方法的参数类型。
public Class<?>[] getParameterTypes()

Method类 还有很多方法,可以获取类的修饰符、参数、返回值、注解等信息,这里就不详细列举了。

总结

 本篇主要介绍了利用反射获取类的名称、获取类中的字段信息以及方法信息。后续再继续介绍如何通过反射创建对象和调用构造方法、获取 Class类 的类型信息、声明信息以及利用反射的一些具体应用。


未完待续……


  1. 《Java编程的逻辑》
01-02 16:37