Java中的反射

前言:

  反射之中包含了一个「反」字,所以了解反射我们先从「正」开始。一般情况下,我们使用某个类时必定知道它是什么类,是用来做什么的。于是我们直接对new 出这个类进行实例化,之后使用这个类对象进行操作。

  反射则是一开始并不知道我要初始化的类对象是什么,自然也无法使用 new 关键字来创建对象了。这时候,我们使用 JDK 提供的反射 API 进行反射调用。反射就是在运行时才知道要操作的类是什么,并且可以在运行时获取类的完整构造,并调用对应的方法。

Reflection(反射)

  是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的內部信息,并能直接操作任意对象的内部属性及方法。
Java反射机制主要提供了以下功能:

在运行时构造任意一个类的对象
在运行时获取任意一个类所具有的成员变量和方法
在运行时调用任意一个对象的方法(属性)

Java 是一门面向对象的语言。在面向对象的世界里,万事万物皆对象,既然万事万物皆对象,那么我们的类是不是对象呢?
我们写的每一个类都可以看成一个对象,是 java.lang.Class 类的对象。每一个类对应的Class放在哪里呢?当我们写完一个类的Java文件,编译成class文件的时候,编译器都会将这个类的对应的class对象放在class文件的末尾。里面都保存了些什么?
  大家可以理解保存了类的元数据信息,一个类的元数据信息包括什么?有哪些属性,方法,构造器,实现了哪些接口等等,那么这些信息在Java里都有对应的类来表示。
测试实体类

public abstract class Animal {
    public String sex;
    protected String address;
    private String hobby;
    boolean isRight;

    protected String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public abstract String getAddress();

    public void setAddress(String address) {
        this.address = address;
    }

    public String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    public boolean isRight() {
        return isRight;
    }

    public void setRight(boolean right) {
        isRight = right;
    }
}
public class Person extends Animal {
    public String name;
    private int age;
    boolean isMan;
    protected boolean isWorker;
    public String getName() {
        return name;
    }
    private void setName(String name) {
        this.name = name;
        System.out.println("this is setName()!");
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
        System.out.println("this is setAge()!");
    }

    //包含一个带参的构造器和一个不带参的构造器
    public Person(String name, int age) {
        super();
        this.name = name;
        this.age = age;
    }
    public Person() {
        super();
    }

    //私有方法
    private void privateMethod(){
        System.out.println("this is private method!");
    }

    //受保护的方法
    protected void testProtected(){

    }


    /**
     * 实现的父类方法
     * @return
     */
    @Override
    public String getAddress() {
        return null;
    }
}
1、Class类

Class是一个类,封装了当前对象所对应的类的信息

一个类中有属性,方法,构造器等,比如说有一个Person类,一个Order类,一个Book类,这些都是不同的类,现在需要一个类,用来描述类,这就是Class,它应该有类名,属性,方法,构造器等。Class是用来描述类的类
Class类是一个对象照镜子的结果,对象可以看到自己有哪些属性,方法,构造器,实现了哪些接口等等
对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。
对象只能由系统建立对象,一个类(而不是一个对象)在 JVM 中只会有一个Class实例

获取Class对象的三种方式

1.通过类名获取 类名.class
2.通过对象获取 对象名.getClass()
3.通过全类名获取 Class.forName(全类名)
Class类的常用方法

image.gif

2、类加载器
        //1. 获取一个系统的类加载器
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();
        System.out.println("系统的类加载器:"+classLoader);


        //2. 获取系统类加载器的父类加载器(扩展类加载器,可以获取).
        classLoader = classLoader.getParent();
        System.out.println("扩展类加载器:"+classLoader);


        //3. 获取扩展类加载器的父类加载器(引导类加载器,不可获取).
        classLoader = classLoader.getParent();
        System.out.println("引导类加载器:"+classLoader);


        //4. 测试当前类由哪个类加载器进行加载(系统类加载器):
        classLoader = Class.forName("com.jzsk.javaadvancetest.ExampleUnitTest").getClassLoader();
        System.out.println("当前类加载器:"+classLoader);


        //5. 测试 JDK 提供的 Object 类由哪个类加载器负责加载(引导类)
        classLoader = Class.forName("java.lang.Object")
                .getClassLoader();
        System.out.println("Object类加载器:"+classLoader);

打印的结果:

系统的类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
扩展类加载器:sun.misc.Launcher$ExtClassLoader@4f023edb
引导类加载器:null
当前类加载器:sun.misc.Launcher$AppClassLoader@18b4aac2
Object类加载器:null
3、构造器
        //1、全类名-->class对象
        String className = "com.jzsk.javaadvancetest.reflect.refle.more.Person";
        Class<com.jzsk.javaadvancetest.reflect.refle.more.Person> clazz = (Class<com.jzsk.javaadvancetest.reflect.refle.more.Person>) Class.forName(className);

        //2、获取所有的构造对象
        System.out.println("获取全部Constructor对象----->");
        Constructor<com.jzsk.javaadvancetest.reflect.refle.more.Person>[] constructors
                = (Constructor<com.jzsk.javaadvancetest.reflect.refle.more.Person>[]) clazz.getConstructors();
        for(Constructor<com.jzsk.javaadvancetest.reflect.refle.more.Person> constructor: constructors){
            System.out.println(constructor);
        }


        //3、获取某个构造方法 备注:参数类型格式(String.class,int.class)
        System.out.println("获取指定构造方法,需要参数列表---->");
        Constructor<com.jzsk.javaadvancetest.reflect.refle.more.Person> constructor
                = clazz.getConstructor(String.class, int.class);
        System.out.println(constructor);

        //4. 调用构造器的 newInstance() 方法创建对象
        System.out.println("调用构造器的 newInstance() 方法创建对象---->");
        Person obj = constructor.newInstance("XXX", 18);
        System.out.println(obj.getName());

打印结果:

获取全部Constructor对象----->
public com.jzsk.javaadvancetest.reflect.refle.more.Person(java.lang.String,int)
public com.jzsk.javaadvancetest.reflect.refle.more.Person()
获取指定构造方法,需要参数列表---->
public com.jzsk.javaadvancetest.reflect.refle.more.Person(java.lang.String,int)
调用构造器的 newInstance() 方法创建对象---->
XXX
4、Field
        String className = "com.jzsk.javaadvancetest.reflect.refle.more.Person";
        Class clazz = Class.forName(className);

        //1、获取该类公用字段,包含父类public修饰的字段 private、protected、未修饰字段获取不到
        System.out.println("获取公用的所有字段,包含父类字段");
        Field[] fieldsAll = clazz.getFields();
        for(Field field: fieldsAll){
            System.out.print(" "+ field.getName());
        }
        System.out.println();
        System.out.println("<--------------------------->");

        //2、获取公用和私有的所有字段,不包含父类字段
        System.out.println("获取公用和私有的所有字段,不包含父类字段");
        Field[] fields = clazz.getDeclaredFields();
        for(Field field: fields){
            System.out.print(" "+ field.getName());
        }
        System.out.println();
        System.out.println("<--------------------------->");

        //3、获取指定字段
        System.out.println("获取指定字段-名字");
        Field field = clazz.getDeclaredField("name");
        field.getName();
        System.out.println("name="+field.getName());
        System.out.println();
        System.out.println("<--------------------------->");

        Person person = new Person("ABC",12);
        System.out.println("获取指定字段的值--通过对象拿值");
        Object val = field.get(person);
        System.out.println(field.getName()+"="+val);
        System.out.println();
        System.out.println("<--------------------------->");

        //4、设置指定字段的值

        System.out.println("设置指定对象指定字段的值");
        field.set(person,"DEF");
        System.out.println();
        System.out.println(field.getName()+"="+person.getName());


        //5、操作私有属性
        System.out.println("<--------------------------->");
        System.out.println("字段是私有的,不管是读值还是写值," +
                "都必须先调用setAccessible(true)方法");
        //比如Person类中,字段name字段是非私有的,age是私有的
        field = clazz.getDeclaredField("age");
        field.setAccessible(true);
        System.out.println(field.get(person));

打印结果:

获取公用的所有字段,包含父类字段
 name sex
<--------------------------->
获取公用和私有的所有字段,不包含父类字段
 name age isMan isWorker
<--------------------------->
获取指定字段-名字
name=name

<--------------------------->
获取指定字段的值--通过对象拿值
name=ABC

<--------------------------->
设置指定对象指定字段的值

name=DEF
<--------------------------->
字段是私有的,不管是读值还是写值,都必须先调用setAccessible(true)方法
12
5、Method
        Class clazz = Class.forName("com.jzsk.javaadvancetest.reflect.refle.more.Person");
        //1、获取所有方法
        System.out.println("获取clazz对应类中的所有public方法," +
                "不能获取private、protected方法,且获取从父类继承来的所有方法+public方法");
        Method[] methods = clazz.getMethods();
        for(Method method:methods){
            System.out.print(" "+method.getName()+"()");
        }
        System.out.println("");
        System.out.println("---------------------------");

        System.out.println("获取所有方法,包括私有方法," +
                "所有声明的方法,都可以获取到,且只获取当前类的方法");
        methods = clazz.getDeclaredMethods();
        for(Method method:methods){
            System.out.print(" "+method.getName()+"()");
        }
        System.out.println("");
        System.out.println("---------------------------");

        System.out.println("获取指定的方法," +
                "需要参数名称和参数列表,无参则不需要写");
        //  方法public void setName(String name) {  }
        Method method = clazz.getDeclaredMethod("setName", String.class);
        System.out.println(method);
        System.out.println("---");

        //  方法public void setAge(int age) {  }
        /* 这样写是获取不到的,如果方法的参数类型是int型
        如果方法用于反射,那么要么int类型写成Integer: public void setAge(Integer age) {  }
        要么获取方法的参数写成int.class*/
        method = clazz.getDeclaredMethod("setAge", int.class);
        System.out.println(method);
        System.out.println("---------------------------");


        System.out.println("执行方法,第一个参数表示执行哪个对象的方法" +
                ",剩下的参数是执行方法时需要传入的参数");
        Object obje = clazz.newInstance();
        method.invoke(obje,18);

        /*私有方法的执行,必须在调用invoke之前加上一句method.setAccessible(true);*/
        method = clazz.getDeclaredMethod("privateMethod");
        System.out.println(method);
        System.out.println("---------------------------");
        System.out.println("执行私有方法");
        method.setAccessible(true);
        method.invoke(obje);

打印结果:

获取clazz对应类中的所有public方法,不能获取private、protected方法,且获取从父类继承来的所有方法+public方法
 getAddress() getName() setAge() getAge() isRight() setSex() getHobby() setHobby() setRight() setAddress() wait() wait() wait() equals() toString() hashCode() getClass() notify() notifyAll()
---------------------------
获取所有方法,包括私有方法,所有声明的方法,都可以获取到,且只获取当前类的方法
 getAddress() getName() setName() setAge() privateMethod() getAge() testProtected()
---------------------------
获取指定的方法,需要参数名称和参数列表,无参则不需要写
private void com.jzsk.javaadvancetest.reflect.refle.more.Person.setName(java.lang.String)
---
public void com.jzsk.javaadvancetest.reflect.refle.more.Person.setAge(int)
---------------------------
执行方法,第一个参数表示执行哪个对象的方法,剩下的参数是执行方法时需要传入的参数
this is setAge()!
private void com.jzsk.javaadvancetest.reflect.refle.more.Person.privateMethod()
---------------------------
执行私有方法
this is private method!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342