Java 反射

官方文档的描述,反射通常用于需要检查或修改 Java 虚拟机中运行的应用程序的运行时行为的程序。反射是一种功能强大的技术,可以使应用程序执行不可能执行的操作。

1. 获取类对象的几种方式

所有反射操作的入口都是 java.lang.Class。所以,我们首先要知道怎么去获取到 Class,即类对象。根据代码是否有权访问对象、类的名称、类型或现有类,有几种方法可以获得 Class

1.1 Object.getClass()

如果有类的实例,则可以直接 getClass() 方法获取对应的 ClassgetClassObejct 类中的方法,所以只要是继承自 Obejct 的类都可以调用。

例如:

public class Box {

    public void method() {
    }
}

public static void main(String[] args) {
     Box box = new Box();
     Class clazz = box.getClass();
}

1.2 ".class"

如果没有类的实例,但是类是可以访问到的,那么可以通过追加 .class 来获得这个类。这也是获取基本类型的最简单方法

例如:

Class clazzBox = Box.class;
Class clazzBoolean = boolean.class; //不能通过 getClass() 获取

1.3 Class.forName()

如果类的包名和类名是已知的,那么可以通过静态方法 Class.forName() 获取类对象。此方法可能会抛出 ClassNotFoundException 异常。

例如:

try {
     Class box = Class.forName("testjava.Box");
} catch (ClassNotFoundException e) {
     e.printStackTrace();
}

1.4 TYPT 属性

对于基本类型,最方便快捷地获取 Class 的方式是通过追加 .class。但是,我们还可以通过另外一种方式获取基本类型的类对象。
我们知道,每一种基本类型都会对应的有一个包装类,比如,boolean 的 包装类为 Booleanint 的包装类为 Integer。每一个包装类中,都一个静态的 TYPE 变量,它就是用来存放包装类对应的基本类型的类对象的。
所以,我们可以这样获取基本类型的类对象:

Class clazzInt = Integer.TYPE;

if (clazzInt == int.class) { //int.class 和 Integer.TYPE 是相同的
    System.out.println("true");
}

2. 获取构造器实例化对象和属性信息

2.1 构造函数

构造函数用于创建类的实例,完成一些初始化操作,构造方法不能被继承。
构造函数可能包含名称、修饰符、参数和一系列的异常等。可以通过 java.lang.reflect.Constructor 提供的方法获取到相应的信息。

public class Box {

    private Box() {
        System.out.println("***** Box()");
    }

    public Box(String name) {
        System.out.println("***** Box(name) name:" + name);
    }
}


public static void main(String[] args) {
        Class boxClass = Box.class;
        //获取 Box 类的所有构造方法
        Constructor[] allConstructor = boxClass.getDeclaredConstructors();
        for (Constructor constructor : allConstructor) {
            System.out.println("***** " + constructor.toGenericString());
            //获取构造方法的参数
            Type[] gpTypes = constructor.getGenericParameterTypes();
            for (int j = 0; j < gpTypes.length; j++) {
                System.out.println("***** GenericParameterType " + j + " :" + gpTypes[j]);
            }
            System.out.println("***** --------------------------");
        }
    }

打印

***** private testjava.Box()
***** --------------------------
***** public testjava.Box(java.lang.String)
***** GenericParameterType 0 :class java.lang.String
***** --------------------------

通过反射实例化对象,有两种方式:

  • 直接使用 Class.newInstance() 实例化对象。
  • 先获取到类的构造方法 Constructor,然后通过 Constructor.newInstance() 实例化对象。

两者相比,通常建议使用后者,因为前者调用的是无参数的构造函数实例化对象,并且需要无参构造函数是可访问的。而后者对参数没有要求,并且就算是 private 的也是可以调用的。

例如:

public class Box {

    private String name;

    private Box() {
        name = "Box";
        System.out.println("***** Box()");
    }

    public Box(String name) {
        this.name = name;
        System.out.println("***** Box(name) name:" + name);
    }

    public String getName() {
        return name;
    }
}

public static void main(String[] args) {
        Class boxClass = Box.class;
        //获取 Box 类的所有构造方法
        Constructor[] allConstructor = boxClass.getDeclaredConstructors();
        Constructor constorZeroParams = null; //无参构造方法
        Constructor constorParams = null;     //有参构造方法
        for (Constructor constructor : allConstructor) {
            if (constructor.getGenericParameterTypes().length == 0) {
                constorZeroParams = constructor;
            } else {
                constorParams = constructor;
            }
        }
        if (constorZeroParams != null) {
            constorZeroParams.setAccessible(true);
            try {
                Box box = (Box) constorZeroParams.newInstance();
                //Box box = Box.class.newInstance(); //会报异常 java.lang.IllegalAccessException: Class testjava.TestJava can not access a member of class testjava.Box with modifiers "private"
                System.out.println("name:" + box.getName());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if (constorParams != null) {
            constorParams.setAccessible(true);
            try {
                Box box = (Box) constorParams.newInstance("haha");
                System.out.println("name:" + box.getName());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

打印结果:

***** Box()
name:Box
***** Box(name) name:haha
name:haha

2.2 方法

java.lang.reflect.Method 类提供了 API 去获取方法的信息:修饰符、返回值、参数、注解信息和异常等。

public class Box {

    private String name;

    private Box() {
        name = "Box";
        System.out.println("***** Box()");
    }

    public Box(String name) {
        this.name = name;
        System.out.println("***** Box(name) name:" + name);
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("***** setName(name) name:" + name);
    }

    public String getName() {
        return name;
    }

    private void method1() {
        System.out.println("private 方法");
    }

    public void method2() {
        System.out.println("public 方法");
    }

    public int method3() {
        System.out.println("带返回值 方法");
        return 0;
    }

    public  void method4() throws E {
        System.out.println("异常 方法");
    }

    public static void staticMethod() {
        System.out.println("静态 方法");
    }
}
public static void main(String[] args) {
        Class boxClass = Box.class;
        Method[] allMethods = boxClass.getMethods();
        for (Method method : allMethods) {
            System.out.println("---------------------");
            //获取所有的方法
            System.out.println("method: " + method.toGenericString());
            //参数类型
            Type[] gpTypes = method.getGenericParameterTypes();
            Class[] pTypes = method.getParameterTypes();
            for (int i = 0; i < gpTypes.length; i++) {
                System.out.println("pType: " + pTypes[i]);
                System.out.println("gpType: " + gpTypes[i]);
            }
            //异常类型
            Class[] xType  = method.getExceptionTypes();
            Type[] gxType = method.getGenericExceptionTypes();
            for (int i = 0; i < xType.length; i++) {
                System.out.println("ExceptionType: "+ xType[i]);
                System.out.println("GenericExceptionType: " + gxType[i]);
            }
            //返回值
            Class returnClazz = method.getReturnType();
            System.out.println("return class: " + returnClazz);
        }
        System.out.println("---------------------");

        //获取指定的方法并使用
        try {
            Method method = boxClass.getDeclaredMethod("setName", String.class);
            method.setAccessible(true);
            //成员方法,需要先实例化一个对象
            Constructor constructor = null;
            Constructor[] constructors = boxClass.getDeclaredConstructors();
            for (Constructor cons : constructors) {
                if (cons.getGenericParameterTypes().length == 0) {
                    constructor = cons;
                    break;
                }
            }
            constructor.setAccessible(true);
            Box box = (Box) constructor.newInstance();
            method.invoke(box, "method");
            System.out.println("name: " + box.getName());

            //调用静态函数,不需要实例化对象
            Method staticMethod = boxClass.getDeclaredMethod("staticMethod");
            staticMethod.invoke(null, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

打印结果:

---------------------
method: public java.lang.String testjava.Box.getName()
return class: class java.lang.String
---------------------
method: public void testjava.Box.setName(java.lang.String)
pType: class java.lang.String
gpType: class java.lang.String
return class: void
---------------------
method: public static void testjava.Box.staticMethod()
return class: void
---------------------
method: public int testjava.Box.method3()
return class: int
---------------------
method: public  void testjava.Box.method4() throws E
ExceptionType: class java.lang.RuntimeException
GenericExceptionType: E
return class: void
---------------------
method: public void testjava.Box.method2()
return class: void
---------------------
method: public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
pType: long
gpType: long
pType: int
gpType: int
ExceptionType: class java.lang.InterruptedException
GenericExceptionType: class java.lang.InterruptedException
return class: void
---------------------
method: public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
pType: long
gpType: long
ExceptionType: class java.lang.InterruptedException
GenericExceptionType: class java.lang.InterruptedException
return class: void
---------------------
method: public final void java.lang.Object.wait() throws java.lang.InterruptedException
ExceptionType: class java.lang.InterruptedException
GenericExceptionType: class java.lang.InterruptedException
return class: void
---------------------
method: public boolean java.lang.Object.equals(java.lang.Object)
pType: class java.lang.Object
gpType: class java.lang.Object
return class: boolean
---------------------
method: public java.lang.String java.lang.Object.toString()
return class: class java.lang.String
---------------------
method: public native int java.lang.Object.hashCode()
return class: int
---------------------
method: public final native java.lang.Class java.lang.Object.getClass()
return class: class java.lang.Class
---------------------
method: public final native void java.lang.Object.notify()
return class: void
---------------------
method: public final native void java.lang.Object.notifyAll()
return class: void
---------------------
***** Box()
***** setName(name) name:method
name: method
静态 方法

属性

同样的,可以通过 java.lang.reflect.Field 获取到类的所有属性相关的信息。

public class Box {

    public static final String TAG = "Box";
    public String name = "box";
    private int age = 18;

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
public static void main(String[] args) {
        Class boxClass = Box.class;
        //获取所有属性
        Field[] fields = boxClass.getFields();
        for (Field field : fields) {
            System.out.println("field: " + field.toGenericString());
        }

        try {
            //修改 public 的 name 变量
            Field nameField = boxClass.getField("name");
            //实例化一个 Box 对象
            Object box = boxClass.getConstructor().newInstance();
            //获取 name 值
            String name = (String) nameField.get(box);
            System.out.println("name 修改前:" + name);
            //修改 name 值
            nameField.set(box, "filedName");
            name = (String) nameField.get(box);
            System.out.println("name 修改后:" + name);

            //修改 private 的 age 属性
            Field ageField = boxClass.getDeclaredField("age");//getField 和 getDeclaredField 的区别是 前者只能获取public的,后者是获取所有的
            ageField.setAccessible(true); //设置访问权限
            //获取 age 的值
            int age = (int) ageField.get(box);
            System.out.println("age 修改前:" + age);
            //修改 age 的值
            ageField.set(box, 24);
            age = (int) ageField.get(box);
            System.out.println("age 修改后:" + age);

            //获取静态的变量 TAG 的值
            Field tagField = boxClass.getDeclaredField("TAG");
            String tag = (String) tagField.get(null); //不需要具体对象
            System.out.println("tag:" + tag);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
field: public static final java.lang.String testjava.Box.TAG
field: public java.lang.String testjava.Box.name
name 修改前:box
name 修改后:filedName
age 修改前:18
age 修改后:24
tag:Box



参考:

Trail: The Reflection API

你可能感兴趣的:(Java 反射)