【java.lang.reflect】反射机制应用及详解

最近也是面试的时候问道一个问题,如何将一个java对象转换为json字符串,一听到的时候没有任何思路,之前也有接触过fastjson,知道就是用这个jar包来处理的,但是具体如何运行原理并不了解,导致面试说不出来,面试官提到fastjson其实就是利用反射来获取这个对象对应的信息,然后转化,于是对反射机制做一个详细的掌握。

一、什么是反射机制?

java的反射机制就是在java程序运行中,可以获得任何一个类的所有属性和方法。对于任意一个对象,可以调用其成员和方法。这种动态获取类信息和调用属性和方法的机制叫做java的反射机制。

要剖析一个类,最关键的是获取其字节码文件(.class)也就是Class对象信息。

 

下面我们通过java的类加载机制来分析一下Class对象怎么来的:

反射就是把java类中的各种成分映射成一个个的Java对象

例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)

如图是类的正常加载过程:反射的原理在与class对象。

熟悉一下加载的时候:Class对象的由来是将class文件读入内存,并为之创建一个Class对象。

整个过程其实就是类加载的加载过程,通过类的全限定名将磁盘中的class二进制文件加载到内存中,然后将其静态存储结构转化为方法区可以运行的数据结构,最后生成一个Class对象作为访问的入口。(这里就生成了Class对象就是包含了类的信息)

二、查看Class类在java中的api详解(1.7的API)

如何阅读java中的api详见java基础之——String字符串处理

Class 类的实例表示正在运行的 Java 应用程序中的类和接口。也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)

Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass方法自动构造的。也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。

三、反射的使用

反射的使用依赖的关键就是Class对象,不管需要什么样的操作,都需要先拿到对应类或者对象的Class对象才能起作用。

1.获取Class对象的三种方法

  1. 通过静态.class方法获取,任何数据类型(包括基本数据类型)都有静态class() 方法直接获取Class对象,但是没有这个类就无法调用
  2. 通过getClass()方法,该对象的getClass()方法可以获取该类的Class文件,需要导入对象
  3. 通过Class.forName(String name) 通过一个类的全限定名,拿到其Class对象,不需要任何支持 

反射的常用类和函数:Java反射机制的实现要借助于4个类:Class,Constructor,Field,Method;其中class代 表的是类对象,Constructor-类的构造器对象,Field-类的属性对象,Method-类的方法对象,通过这四个对象我们可以粗略的看到一个类的各个组成部分。其中最核心的就是Class类,它是实现反射的基础

2.通过反射获取构造函数

测试类:

public class BeanDemo {
    private  int age;
    private  String name;
    public  String  pub;
    String tall;
    char sex;

    public String getTall() {
        return tall;
    }

    public void setTall(String tall) {
        this.tall = tall;
    }

    public char getSex() {
        return sex;
    }

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

    /**唯一的方法
     * */
    public void speak(String  name){

        System.out.println("我的名字是"+name);

    }

    public BeanDemo() {
    }

    /**构造函数
     * */
    private BeanDemo(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("私有构造函数调用:"+name+age);
    }

    public BeanDemo(String name) {
        this.name = name;
        System.out.println("调用构造函数:"+name);
    }

    public BeanDemo(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

 构造函数:

        /**一:通过反射获取构造方法
         *     1.通过getConstructors() 所有公有的构造方法
         * */
        Constructor[] constructors  = bean.getConstructors();
        System.out.println("====================================getConstructors()获取公有构造方法==========================");
        for (Constructor c : constructors){
            System.out.println("公有构造函数有:"+c);
        }
        System.out.println("====================================getDeclaredConstructors()获取私有,protected,默认,公有构造方法(也就是所有的构造方法)==========================");
        /** 2.通过getDeclaredConstructors() 获取所有
         * */
        Constructor[] decCon = bean.getDeclaredConstructors();
        for (Constructor c : decCon){
            System.out.println("全部构造函数有:"+c);
        }
        System.out.println("====================================getConstructor(null) 无参构造函数 可以根据传入的参数类型获取对应的构造函数==========================");

        Constructor nullCon = bean.getConstructor(null);
        Constructor intCon = bean.getConstructor(String.class);
        System.out.println(nullCon);
        System.out.println(intCon);
        System.out.println("====================================getConstructor(null) 调用构造函数==========================");
        Object obj = intCon.newInstance("hhh");//调用构造函数,就是利用构造函数对象,创建实例
        //若是私有构造函数,加上setAccessible(true)  要先用getDeclaredConstructor() 
        Constructor intStrCon = bean.getDeclaredConstructor(String.class,int.class);
        intStrCon.setAccessible(true);//忽略访问限制
        Object inStrObj = intStrCon.newInstance("String",100); //不加会抛出异常 java.lang.NoSuchMethodException: t201808.t20180831.BeanDemo.<init>(java.lang.String, int)

常用的获取构造函数方法:

  • getConstructors()获取所有共有的构造方法 
  • getDeclaredConstructors()获取所有的构造方法,私有共有,protected 默认
  • 调用构造函数就是利用获得的构造函数对象创建实例

3.通过反射获取成员变量

        /**二:通过反射拿到成员变量
         *     1.getDeclaredFields()可以拿到所有成员变量
         *     2.getFields() 只能拿到公有的成员变量
         * */
        Field[] NoDecfields = bean.getFields(); //公有成员变量
        Field [] fields = bean.getDeclaredFields();//公有私有都可以拿到
        System.out.println("====================================getFields() 公有成员变量==========================");
        for (Field f  :NoDecfields){
            System.out.println(f);
        }

        System.out.println("====================================getFields() 全部成员变量==========================");
        for (Field f :fields){
            System.out.println(f);
        }
        System.out.println("====================================getFields() 获取对应公有字段并调用==========================");
        Field pubField = bean.getField("pub");
        //首先要获取一个pub成员 有值的对象
        Object object = bean.getConstructor(String.class,String.class).newInstance("name","这是我设置的PUB");
        //获取到该字段,注意get()方法获取返回值为对象,还有getInt()等方法可调用
        System.out.println(pubField.get(object));
        //设置该成员变量
        pubField.set(object,"新设置的PUB值");
        System.out.println(pubField.get(object));
        //Field的其他方法 getName()可以直接拿到属性的名字
        System.out.println(pubField.getName());
        //同理私有变量访问需要setAccessible(true) 忽略权限

常用的获取属性方法:

  • getFields()获取所有共有的构造方法 
  • getDeclaredFields() 获取所有的构造方法,私有共有,protected 默认
  • 获取属性值的时候,需要2个对象,一个是需要获取值的对象obj,一个属性值对象field。
  • 通过调用field.get(obj) 可以获取到对象的对于属性值。
  • 同样,若是私有属性需要setAccessible()

一个fastjson的对象转字符串的例子:

BeanDemo beanObj = new BeanDemo("hhhhh",99);
        Class clazz = beanObj.getClass();
        Field[] fields = clazz.getDeclaredFields();
        System.out.println("{");
       for (Field f : fields){
            Field ageField = clazz.getDeclaredField(f.getName());
            ageField.setAccessible(true);
            System.out.println("\""+f.getName()+"\""+":"+ageField.get(beanObj));
        }
        System.out.println("}");

4.通过反射获取方法以及调用方法

        /**三、通过反射获取方法以及调用方法
         * */
        Method[] pubMethods = bean.getMethods();
        System.out.println("====================================获取公有方法 包括继承的方法 实现的==========================");
        for (Method m :pubMethods){
            System.out.println(m);
        }
        System.out.println("==================================== 获取全部方法,公有私有保护默认,不包括继承的方法==========================");
        Method[] allMethods = bean.getDeclaredMethods();
        for (Method method : allMethods){
            System.out.println(method);
        }
        System.out.println("====================================获取指定方法=========================");
        Method method = bean.getMethod("speak1", String.class);//参数列表为 方法名和方法的参数列表类型
        System.out.println(method);
        //调用该方法 invoke()
        //需要先创建一个对象
        Object obj = bean.getConstructor().newInstance();
        method.invoke(obj,"?????");//方法的调用采用方法对象 调用invoke()方法,传入对象,以及方法需要的参数

        //调用有返回值的方法
        method = bean.getDeclaredMethod("speak4", int.class);
        method.setAccessible(true);
        System.out.println(method);
        Object res =method.invoke(obj,12);
        System.out.println(res);

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值