谈谈Java中的反射机制

2020年5月11日15:26:58java评论61阅读模式

前言

在使用框架进行开发时,我们的开发速度大大提升。我们感叹于它的神奇之处,我们使用它的时候,也要知道其“灵魂”。正所谓,无反射,不框架,框架的灵魂就是反射。
另外,我们在eclipse或者IDEA中编辑Java代码时,它们是怎么知道我们的对象有哪些方法,输入一个点就能给提示呢?
带着问题我们来谈谈反射。文章源自随机的未知-https://sjdwz.com/11145.html

概念

反射:是将一个类的各个部分封装为其他对象,这就是反射机制。
看着上面文邹邹的话语,想必大家并没有理解啥是反射。下面我们来通过一个例子来进行讲解。文章源自随机的未知-https://sjdwz.com/11145.html

我们来看一下我们的Java代码在计算机中经历的几个阶段:
第一个阶段:源代码阶段
首先,我们定义一个猫的类,包含名字,年龄,无参和全参构造方法,和一个猫叫的方法。
Cat.java如下:文章源自随机的未知-https://sjdwz.com/11145.html

public class Cat {
    private String name;//猫的名字
    private int age;//猫的年龄

    public Cat() {//无参构造方法
    }

    public Cat(String name, int age) {//全参构造方法
        this.name = name;
        this.age = age;
    }

    public void meow(){//猫叫的方法
        System.out.println("喵喵喵~~~");
    }
}

写完这个代码之后,我们并不能运行它,我们需要执行一个操作:编译。
通过Java自带的编译器,使用 javac 这个命令,编译 Cat.java 文件,如果编写的代码没有问题,会在磁盘上生成一个字节码文件:Cat.class 文件。
这个字节码文件放的是什么呢?
它主要包含三个主要的内容:文章源自随机的未知-https://sjdwz.com/11145.html

谈谈Java中的反射机制
字节码文件包含的主要内容

当然不止这三个内容,还有类的名称等等等等。文章源自随机的未知-https://sjdwz.com/11145.html

这就是java代码在计算机中的第一个阶段:文章源自随机的未知-https://sjdwz.com/11145.html

谈谈Java中的反射机制
第一个阶段

这时我们的代码还在硬盘,并没有进入内存。文章源自随机的未知-https://sjdwz.com/11145.html

我们先不谈第二个阶段,我们先来谈谈第三个阶段,也就是我们通常new对象的阶段。
第三个阶段:运行时阶段
谈谈Java中的反射机制文章源自随机的未知-https://sjdwz.com/11145.html

这就是我们的运行时阶段。
从字节码文件到new出类的对象这又是一个怎样的过程呢?我们需要把字节码文件加载到内存中才能使用,这就要介绍我们的第二个阶段了。
第二个阶段:Class类对象阶段
在Java中万物皆对象,有一个对象来描述字节码文件;这个对象是Class类对象;
需要把字节码中的成员变量,构造方法,成员方法都表示出来,又这些可能不止一个,所以这三种需要由一个数组来存储,所以主要由三个主要的部分组成:文章源自随机的未知-https://sjdwz.com/11145.html

Field[] fields;//成员变量数组
Constuctor[] constuctors;//构造方法数组
Method[] method;//成员方法数组

可以看到,我们把它们存储到对象中了,然后就能够知道有哪些成员变量,哪些方法了,这就解答了我们前面的如何提示问题。
然后通过类对象创建对应的Cat类等对象。
三个阶段文章源自随机的未知-https://sjdwz.com/11145.html

谈谈Java中的反射机制
三个阶段关系

获取字节码Class对象的三种方式

第一种方式
如果我们的java代码在第一个阶段时,它还没有进入内存,我们需要将它加载到内存,需要使用
Class.forName("全类名");方式将其加载到内存,获取Class对象;
第二种方式
如果我们的java代码在第二个阶段时,它还没有创建对象,但是我们已经把它它加载到了内存,获取到了它的类名,我们可以使用 类名.class的方式获取Class对象;
第三种方式
如果我们的java代码在第三个阶段时,已经有了该类的对象,我们只需要用 对象.getClass() 的方式获取Class对象;
下面我们就来演示一下这三种方法的使用;
首先我们创建一个包com.demo.domain(命名随自己来定),存放我们的实体类对象,本文使用Cat.java进行演示;文章源自随机的未知-https://sjdwz.com/11145.html

package com.demo.domain;

public class Cat {
    private String name;//猫的名字
    private int age;//猫的年龄

    public Cat() {//无参构造方法
    }

    public Cat(String name, int age) {//全参构造方法
        this.name = name;
        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;
    }

    public void meow(){//猫叫的方法
        System.out.println("喵喵喵~~~");
    }
}

再创建一个包com.demo.reflect用来演示这三种方式;Demo1.java如下:文章源自随机的未知-https://sjdwz.com/11145.html

package com.demo.reflect;

import com.demo.domain.Cat;

public class Demo1 {
    public static void main(String[] args) throws ClassNotFoundException {
        //第一种方式,Class.forName("全类名")
        Class cls1 = Class.forName("com.demo.domain.Cat");
        System.out.println(cls1);
        //输出class com.demo.domain.Cat

        //第一种方式,类名.class
        Class cls2 = Cat.class;//在使用Cat类之前需要进行导入
        System.out.println(cls2);
        //输出class com.demo.domain.Cat

        //第三种方式,已经具有对象,使用对象.getClass
        Cat cat = new Cat();
        Class cls3 = cat.getClass();
        System.out.println(cls3);
        //输出class com.demo.domain.Cat
    }
}

注意:本文没写Class的泛型。
那获取到的三个对象是什么关系呢?我们用一段代码,验证它们的内存地址是否相等。文章源自随机的未知-https://sjdwz.com/11145.html

//比较三个对象
System.out.println(cls1 == cls2);//true
System.out.println(cls2 == cls3);//true

输出均为true,可见它们的内存地址是相同的。
所以,同一个字节码文件,在同一个程序运行的过程中,只会被加载一次,三种方式获取的Class对象都是同一个。文章源自随机的未知-https://sjdwz.com/11145.html

Class对象功能:

  • 获取功能:文章源自随机的未知-https://sjdwz.com/11145.html

    1. 成员变量的获取文章源自随机的未知-https://sjdwz.com/11145.html

      • Field[] getFields() :获取所有public修饰的成员变量文章源自随机的未知-https://sjdwz.com/11145.html

      • Field getField(String name) 获取指定名称的 public修饰的成员变量文章源自随机的未知-https://sjdwz.com/11145.html

      • Field[] getDeclaredFields() 获取所有的成员变量,不考虑修饰符文章源自随机的未知-https://sjdwz.com/11145.html

      • Field getDeclaredField(String name)文章源自随机的未知-https://sjdwz.com/11145.html

    2. 构造方法的获取文章源自随机的未知-https://sjdwz.com/11145.html

      • Constructor<?>[] getConstructors()文章源自随机的未知-https://sjdwz.com/11145.html

      • Constructor getConstructor(类<?>... parameterTypes)文章源自随机的未知-https://sjdwz.com/11145.html

      • Constructor getDeclaredConstructor(类<?>... parameterTypes)文章源自随机的未知-https://sjdwz.com/11145.html

      • Constructor<?>[] getDeclaredConstructors()文章源自随机的未知-https://sjdwz.com/11145.html

    3. 构造方法的获取文章源自随机的未知-https://sjdwz.com/11145.html

      • Method[] getMethods()文章源自随机的未知-https://sjdwz.com/11145.html

      • Method getMethod(String name, 类<?>... parameterTypes)文章源自随机的未知-https://sjdwz.com/11145.html

      • Method[] getDeclaredMethods()文章源自随机的未知-https://sjdwz.com/11145.html

      • Method getDeclaredMethod(String name, 类<?>... parameterTypes)文章源自随机的未知-https://sjdwz.com/11145.html

    4. 全类名的获取文章源自随机的未知-https://sjdwz.com/11145.html

      • String getName()
  • Field:成员变量文章源自随机的未知-https://sjdwz.com/11145.html

    • 操作:文章源自随机的未知-https://sjdwz.com/11145.html
      1. 设置值文章源自随机的未知-https://sjdwz.com/11145.html

        • void set(Object obj, Object value)
      2. 获取值文章源自随机的未知-https://sjdwz.com/11145.html

        • get(Object obj)
      3. 忽略访问权限修饰符的安全检查文章源自随机的未知-https://sjdwz.com/11145.html

        • setAccessible(true):暴力反射
  • Constructor:构造方法文章源自随机的未知-https://sjdwz.com/11145.html

    • 创建对象:文章源自随机的未知-https://sjdwz.com/11145.html
      • T newInstance(Object... initargs)文章源自随机的未知-https://sjdwz.com/11145.html

      • 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法文章源自随机的未知-https://sjdwz.com/11145.html

  • Method:方法对象文章源自随机的未知-https://sjdwz.com/11145.html

    • 执行方法:文章源自随机的未知-https://sjdwz.com/11145.html

      • Object invoke(Object obj, Object... args)
    • 获取方法名称:文章源自随机的未知-https://sjdwz.com/11145.html

      • String getName:获取方法名

反射好处

反射有很多的优势:文章源自随机的未知-https://sjdwz.com/11145.html

  1. 我们可以在程序运行过程中,操作这些对象。
  2. 可以为我们的程序进行解耦,提高程序的可扩展性。

相信大家对反射有了一定的了解,感谢大家的阅读。文章源自随机的未知-https://sjdwz.com/11145.html

欢迎关注

扫下方二维码即可关注微信公众号:随机的未知
谈谈Java中的反射机制文章源自随机的未知-https://sjdwz.com/11145.html

文章源自随机的未知-https://sjdwz.com/11145.html
欢迎关注本站微信公众号:随机的未知 如果喜欢本文,欢迎点赞,收藏,转发,打赏。
java最后更新:2022-2-24
  • 本文由 发表于 2020年5月11日15:26:58
  • 转载请注明:来源:随机的未知 本文链接https://sjdwz.com/11145.html
java

详解java接口interface

引言 接口这个词在生活中我们并不陌生。 在中国大陆,我们可以将自己的家用电器的插头插到符合它插口的插座上; 我们在戴尔,惠普,联想,苹果等品牌电脑之间传输数据时,可以使用U盘进行传输。 插座的普适性是...
java

Java中的抽象类和抽象方法

引言 实例图片 如上图,二维图形类有三个子类,分别是正方形类,三角形类,圆形类; 我们都知道要求正方形的面积,直接使用面积公式边长的平方即可,同理三角形的是底乘高除以2,圆的面积是**乘以半径的平方。...
java

Java多线程中的虚假唤醒和如何避免

先来看一个例子 一个卖面的面馆,有一个做面的厨师和一个吃面的食客,需要保证,厨师做一碗面,食客吃一碗面,不能一次性多做几碗面,更不能没有面的时候吃面;按照上述操作,进行十轮做面吃面的操作。 用代码说话...
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定