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 <E extends RuntimeException> 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 <E> 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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,755评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,369评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,799评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,910评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,096评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,159评论 3 411
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,917评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,360评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,673评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,814评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,509评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,156评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,123评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,641评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,728评论 2 351

推荐阅读更多精彩内容

  • 深入理解Class对象 RRTI的概念以及Class对象作用 认识Class对象之前,先来了解一个概念,RTTI(...
    架构师springboot阅读 1,557评论 0 3
  • 一、概述 Java反射机制定义 Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法...
    CoderZS阅读 1,632评论 0 26
  • 问题: 在运行时,对一个JAVA类,能否知道属性和方法;能否调用它的任意方法? 答案是可以的,JAVA提供一种反射...
    糖宝_阅读 760评论 0 1
  • 类加载机制 1 什么是反射 Java反射机制是在运行状态中对于任意一个类,都能知道这个类的所以属性和方法;对于任何...
    凯玲之恋阅读 13,824评论 3 28
  • 愿晨光代替我,看着你的脸庞灿烂如花愿黑夜都穿不透我,挡住你的忧伤因为你在我的心里,是七月流火的清凉是冬雪的拥抱是春...
    ALsoon阅读 179评论 0 0