【Java】 RTTI 与 反射

前言

刚开始学习Java的时候,我就只是随便看了一点点,根本没有深入了解,反射这一章也是一点没看,因为我觉得可能反射不是那么重要,现在发现反射真的太重要了。很多优秀的开源框架都是通过反射完成的,反射被称为框架设计的灵魂。反射的重要性不言而喻,但是如果刚开始接触反射,完全不了解反射到底是干嘛的。我们也不要着急,反射入门还是比较简单。看了这篇文章,相信你就会对反射有了一个大概的了解。
记录一下学习笔记,希望可以对大家有所帮助。

在Java中,想要在运行时识别对象和类的信息,主要有两种方式:

  • RTTI   它假定了我们在编译时已经知道了所有的类型
  • 反射    它允许我们在运行时发现和使用类的信息

反射

先看一下网上关于反射的概念:

Java反射就是在运行状态中,
对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意方法和属性;
并且能改变它的属性。而这也是Java被视为动态语言的一个关键性质。

Java反射的功能是在运行时判断任意一个对象所属的类,
在运行时构造任意一个类的对象,
在运行时判断任意一个类所具有的成员变量和方法,
在运行时调用任意一个对象的方法,生成动态代理。

其实这么说还是比较让人懵逼的。我初学的时候,将反射先简单的理解为:反射就是获取类的信息
学习新技术的时候不要一直纠结在一点上,有的时候遇到这种不好理解的定义先在心中有一个大致的概念,然后继续往前看,有了一定了解之后回头再看就比较容易理解。

一.反射机制

我们每天都在和代码打交道,一份代码从我们在键盘上输入,到运行结束得到结果,中间过程经历了什么,估计大部分人的回答就是:编辑java文件,经过javac编译,然后就直接创建对象了。
这比我还好一点,因为我之前都不知道java文件还要经过javac编译。

我们总是忽略了中间的Class类对象阶段
我们看一下下面这个图
在这里插入图片描述
需要了解一下,Source 源代码阶段是在磁盘上进行的,而Class 类对象阶段是在内存中进行的。
将类的组成部分封装为其它对象,这就是反射机制,也就是Class类对象阶段所做的事情,主要有三件事:

  • 将类的成员变量封装成Filed对象
  • 将类的构造方法封装成Constructor对象
  • 将类的成员方法封装成Method对象

反射机制:将类的组成部分封装为其它对象
这么说是不是对反射有了一定的了解了呢?

如果还是迷迷糊糊的状态,我们再来看一下Java反射机制的一个应用,相信你了解了之后,肯定会说:“卧槽,反射这么牛逼?”

我们在编程的时候,几乎一直在接触反射,只是我太菜了一直没有发现。举一个简单的例子
在这里插入图片描述
现在大家使用的编译器都会有自动提示的功能,比如我们定义一个字符串s,操作s的时候发现,编译器会将s的操作方法自动显示出来,使用的时候我们不必一个一个找方法了,非常快捷方便,但是大家有没有想过这些方法是如何得到的呢?

我没有学习反射之前肯定会说:String类中有这些方法,s是String的对象,自然可以操作这些方法。没有毛病啊!

但是我们的关注重点不是这个,而是编译器如何知道有这些方法的存在呢? 这就用到了反射的知识。
在这里插入图片描述
我们定义了一个字符串之后,系统会将字符串的字节码文件加载进内存,在内存中,有一个Class类对象,Class类对象将String的所有方法都抽取出来作为Method的对象并放入Method[] 数组中,使用时,将Method[] 数组中的成员抽取出来,显示在列表中即可。这就是一个反射的过程。

反射的好处:
     (1) 可以在程序的运行过程中操作这些对象
     (2) 可以解耦(降低程序耦合性),提高程序的可扩展性。

二.获取Class对象的三种方式

经过前面的解释,相信你对反射有了一定的了解,
在这里插入图片描述
获取Class 对象的三种方式:

  1. Class.forName(“全类名”) 将字节码文件加载进内存,返回Class对象
    • 多用于配置文件,将类名定义在配置文件中,读取类名,加载类
    • 比如JDBC
  2. 类名.class 类名的属性.class 来获取
    • 多用于参数传递
  3. 对象.getClass() getClass()方法在Object中
    • 多用于对象的获取字节码的方式
      有这样一个Person类:
package com.ahmu.yx.reflect;

public class Person {
   
    private String name;
    private int age;

    public Person(String name, int age) {
   
        this.name = name;
        this.age = age;
    }

    public Person(){
   
        
    }

    @Override
    public String toString() {
   
        return super.toString();
    }

    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;
    }
}

获取Class对象:

package com.ahmu.yx.reflect;

public class ReflectClass {
   
    public static void main(String[] args) throws Exception {
   
        //1.Class.forName("全类名")
        Class c1 = Class.forName("com.ahmu.yx.reflect.Person");
        System.out.println(c1);

        //类名.class
        Class c2 = Person.class;
        System.out.println(c2);

        //对象.getClass()
        Person p = new Person();
        Class c3 = p.getClass();
        System.out.println(c3);

        //比较三个对象
        System.out.println("c1 == c2: " + (c1 == c2));
        System.out.println("c2 == c3: " + (c2 == c3));
        System.out.println("c3 == c1: " + (c3 == c1));
    }
}

在这里插入图片描述
结论
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class 对象都是同一个

这是一个静态方法,通过类名直接调用
Class.forName(“全类名”)

三种方式对应文件的三种不同状态

三.使用Class对象

前面说过了如何获取Class 对象,那么我们应该如何使用呢?

获取功能

获取成员变量

有这样一个X类,其中有四个成员变量a,b,c,d,分别有着不同的访问修饰权限

class X {
   
    public String a;
    protected String b;
    String c;
    private String d;
}

获取其成员变量的方法:

  • Field[] getFields()
    获取所有public修饰的成员变量

    Class xClass = X.class;
    
    //获取所有public修饰的成员变量
    Field[] fields = xClass.getFields();
    for (Field field : fields) {
         
        System.out.println("使用getFields获得的成员变量:" + field);
    }
    
  • Field getField(String name)
    获取指定名称的 public修饰的成员变量

    //获取名称为a的public修饰的成员变量
    Class xClass = X.class;
    Field a = xClass.getField("a");
    System.out.println("获取名称为a的public修饰的成员变量:" + a);
    
  • Field[] getDeclaredFields()
    获取所有的成员变量,不考虑修饰符

    //获取所有成员变量,不考虑修饰符
    Class xClass = X.class;
    Field[] declaredFields = xClass.getDeclaredFields();
    System.out.
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值