一篇文章弄懂 Java 反射的使用

待兔 等级 1142 2 1

说到Java反射,必须先把 Java 的字节码搞明白了,也就是 Class , 大 Class 在之前的文章中,我们知道了Java的大Class就是类的字节码,就是一个普通的类,里面保存的是类的信息,还不太明白Java的大Class的,可以先看一下之前的文章 一篇文章彻底搞懂Java的大Class到底是什么

先想一个问题

1. 给我们一个类,我们如何使用?

这还不简单,通过这个类,创建一个类的对象,再通过这个对象,调用类的方法或者属性

比如有一个类叫 Student , 里面有一个 name字段和一个 age 字段,还有3个方法, 源码如下:

package com.model;

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

    public Student(){
    }

    public Student(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 show(){
        System.out.println("name=" + this.name + " age=" + this.age);
    }
}

上面的代码很简单,应该都能看懂,我们以这个Student类来实验

回到上面的问题:如何使用这个类? ,代码如下:

        //1 创建一个对象
        Student s = new Student();

        //2 调用对象的方法
        s.setName("李雷");
        s.setAge(23);

        //3 打印一下
        s.show();

打印的结果下: name=李雷 age=23

上面就是和 反射 相反的通过正常的方式创建一个类的对象,然后通过对象调用类的方法

其实我们还可以根据类的字节码来创建对象,然后调用类的方法 也就是通过某个类的 Class ,来创建对象,然后调用类的方法

2. 如何获取类的 Class 呢?

3个方法,以Student为例,演示如下:

第一种:通过 Class.forName("com.model.Student") 来获取Student的 Class

代码如下:

Class cls = Class.forName("com.model.Student");

第二种:通过 Student.class

Class cls = Student.class

第三种:通过类的对象来获取,调用类的对象的 getClass()方法

Student s = new Student();
Class cls = s.getClass()

以上就是三种方法获取一个类的 Class 的方法,必须要牢记,尤其是前 2 个,用的最多

3. 如何通过Class来创建对象,进而来调用类的方法或者属性呢?

  • 第一步:获取类的 Class 对象
  • 第二步:获取对应的方法的字节码 Method 以及 构造函数的字节码 Constructor ,或者字段的字节码 Field
  • 第三步:通过ConstructornewInstance()方法生成一个类的对象
  • 第四步:通过调用 Methodinvoke()方法完成调用类的代码

代码演示如下:

        //第一步:获取Student的 Class 对象,即Student的字节码
        Class cls = Class.forName("com.model.Student");

        //第二步:获取无参的构造方法的字节码,当然也可以获取有参的
        Constructor constructor = cls.getConstructor();

        //第三步:调用构造函数的字节码对象的 newInstance 方法创建 Student的对象 obj
        Object obj = constructor.newInstance();

        //第四步:获取 setName 方法的字节码,注意参数传方法的名字以及方法中参数的字节码
        //      获取了setName的字节码 method,调用方法必须要有一个对象,所以上面的obj对象就是用来此处的
        //      一定要传进行
        Method method = cls.getMethod("setName", String.class);
        method.invoke(obj,"待兔");

        //和上面类似,只不过这次 getMethod 的第二个参数传的是 int.class
        //因为第二个参数是int类型
        Method method1 = cls.getMethod("setAge", int.class);
        method1.invoke(obj,23);

        //和上面类似 ,只不过 show()方法是无参的,所以 getMethod 只需要传方法的名字"show" 即可
        Method showMethod = cls.getMethod("show");

        //最后:调用showMethod方法,通过调用showMethod的invoke方法,里面传入前面创建的obj对象
        //就达到了调用对象的show方法
        showMethod.invoke(obj);

通过上面的代码演示可以看出,在不知道 Student 类型的情况下,我们只需要知道 Student类的全类名(包名+类名) 也就是com.model.Student ,就可以获取到 Student类的

和直接通过 Student s = new Student(); s.show(); 这种方法不一样的是,上面是在运行时通过字符串值知道要运行的类是com.model.Student

所以,反射就是在运行的时候 ,才知道这个类是什么,并且可以在运行的时候 ,获取这个类的完整信息,并调用对应的方法

4. 常用的反射API

4.1 在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的 Class 对象

获取Class对象有三种方法,上面已经讲过,这里再次贴出来,加深印象

  • 使用 Class.forName 静态方法,前提是你知道类的全类名 Class cls = Class.forName("com.model.Student"); ,其实这种方法就是加载类的
  • 使用类的 .class 方法 Class cls = Student.class 不过这种方法,只适合在编译时就知道操作的 Class
  • 使用类对象的 getClass() 方法。 Student s = new Student(); Class cls = s.getClass()

4.2 获取所有类的的方法

可以通过 Class对象 getMethods()或者 cls.getDeclaredMethods() 来获取所有的方法的字节码

两者的区别是:getMethods()获取的方法包括父类的,getDeclaredMethods() 获取的是子类的

演示 getMethods()

Method[] methods = cls.getMethods();
for (Method m : methods) {
    System.out.println(m.getName());
}

输出出下:

getName setName setAge show getAge wait wait wait equals toString hashCode getClass notify notifyAll

可以看到,输出了很多父类中的方法(Object类中的方法)

再来看一下 getDeclaredMethods() 方法

Method[] methods = cls.getDeclaredMethods();
for (Method m : methods) {
    System.out.println(m.getName());
}

输出如下:

getName setName setAge show getAge

可以看到,只有子类自己的方法,并没有父类的方法

5. 通过反射创建类的对象需要注意的点

上面我们通过 Constructor 对象的newInstance()方法,来创建对象 其实还有一种方法,也可以使用 Class对象的newInstance()

5.1 第一种:通过 Class 对象 newInstance()方法

Class cls = Class.forName("com.model.Student");
Student obj = (Student) cls.newInstance();

5.2 第二种:通过 Constructor 对象的 newInstance() 方法

//第一步:获取Student的 Class 对象,即Student的字节码
Class cls = Class.forName("com.model.Student");

//第二步:获取无参的构造方法的字节码,当然也可以获取有参的
Constructor constructor = cls.getConstructor();

//第三步:调用构造函数的字节码对象的 newInstance 方法创建 Student的对象 obj
Object obj = constructor.newInstance();

::: warning 通过 Constructor 对象创建类对象可以选择特定构造方法,而通过 Class 对象则只能使用默认的无参数构造方法。 下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。 :::

Class cls = Class.forName("com.model.Student");
Constructor constructor = cls.getConstructor(String.class,int.class);
Object obj = constructor.newInstance("tom",23);

通过上面的讲解,应该对反射的用法有了个大致的了解了,Class有很多方法,感兴趣的可以自己写个helloworld调试一下 不过怎么说,还是要先弄明白 Class 到底是什么,知道了 Class 的本质 ,再来看反射,就很容易了

还不太明白的一定要看看下面的文章 一篇文章彻底搞懂Java的大Class到底是什么

收藏
评论区

相关推荐

JVM--虚拟机方法调用
概述 Java能做到一次编译,随处运行,最要是归功于java虚拟机 和class文件,我们知道,计算机是0和1 的世界,并且只认0和1,所以不管是什么语言什么编译类型,最终给计算机的都是0和1,java也不例外,但是我们的java编译成了class文件,class怎么就转换成0和1了呢,或者说机器码呢?其实这一步是虚拟机帮我们干的。当然,虚拟机是建立在不同
Java多态实现原理
Java多态概述 多态是面向对象编程语言的重要特性,它允许基类的指针或引用指向派生类的对象,而在具体访问时实现方法的动态绑定。Java 对于方法调用动态绑定的实现主要依赖于方法表,但通过类引用调用(invokevirtual)和接口引用调用(invokeinterface)的实现则有所不同。 类引用调用的大致过程为:Java编译器将Java源代码编译成c
深入不可变类 -- 谈谈Java String与包装类的底层实现
深入不可变类——谈谈String与包装类的底层实现 一、走进不可变类 不可变类是指在创建实例后该例变量不可以改变的类。 比如String类和包装类。 定义不可变类需要遵守: 1. 成员变量设置为priavte ,final修饰 2. 提供带参构造器对成员变量进行初始化 3. 只提供getter,
Java枚举类妙用
上代码:java@Getterpublic enum NodeIdStatusRef { / reference / NI001_A001_AGREE(NodeIdEnum.NI001.getNodeId(), OrderStatus.A101.getCode(), OrderStatus.A101.getCo
JAVA-SSM封装字符串转换的工具类
一、MVC中基础的字符串转换在MVC框架中,笔者用的是最基础的转换方法,我们在需要转换时候就进行转换,但是随着需求数量增加,我们要写很多行代码,最近笔者学习了如何自己封装一个字符串类型转换的工具类。 二、自己封装一个字符串转换类话不多说,先上代码javapublic class CahracterUtil public static int getI
工厂模式实例(顺便回忆反射机制的应用)
一、原理 反射机制的原理JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。 工厂模式自述所谓工厂模式,是说由某个产品类接口、产品实现类、工厂类、客户端(单元测试主类)构成的一个模式,大程度的降低了代码的
Java里面的十万个为什么
Java里面的十万个为什么 1.不是说 JVM 是运行 Java 程序的虚拟机吗?那 JRE 和 JVM 的关系是怎么样的呢?简单地说,JRE 包含 JVM 。JVM 是运行 Java 程序的核心虚拟机,而运行 Java 程序不仅需要核心虚拟机,还需要其他的类加载器,字节码校验器以及大量的基础类库。JRE 除包含 JVM 之外,还包含运行 Java 程序的其
普通代码块 静态代码块 构造代码块......傻傻分不清
代码块的定义 在Java中,使用 括起来的代码被称为代码块。 代码块的分类根据其位置和声明方式不同: 面试题: 构造块和静态块执行顺序在上面分类描述中,我们已经给出了答案;接下来我们跑一跑测试代码,根据代码执行结果来验证这些答案 无继承关系javapackage com.milo.java7.codeblock;/ @author Milo Lee @d
Java 实用类
实用类 枚举 Math Random String StringBuffer 日期类 枚举枚举(Enum)是一种有确定取值区间的数据类型,它本质上是一种类,具有简洁、安全、方便等特点。可以这样理解,枚举的值被约束到一个特定的范围,只能取这个范围以内的值。我们为什么要用枚举呢?我们在描述对象的一些属性特征时,可选择的值是一个特定范围的,不能随便定义。比如性别只
Java开发面试高频考点学习笔记(每日更新)
Java开发面试高频考点学习笔记(每日更新) 1.深拷贝和浅拷贝 2.接口和抽象类的区别 3.java的内存是怎么分配的 4.java中的泛型是什么?类型擦除是什么? 5.Java中的反射是什么 6.序列化与反序列化 7.Object有哪些方法? 8.JVM内存模型 9.类加载机制 10.对象的创建和对象的布局 11.Java的四种引用
一篇文章弄懂 Java 反射的使用
说到Java反射,必须先把 Java 的字节码搞明白了,也就是 Class , 大 Class在之前的文章中,我们知道了Java的大Class就是类的字节码,就是一个普通的类,里面保存的是类的信息,还不太明白Java的大Class的,可以先看一下之前的文章 先想一个问题 1. 给我们一个类,我们如何使用?这还不简单,通过这个类,创建一个类的对象,再通过这个
2021年度最全面JVM虚拟机,类加载过程与类加载器
前言类装载器子系统是JVM中非常重要的部分,是学习JVM绕不开的一关。一般来说,Java 类的虚拟机使用 Java 方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表
限时发布!纯手打“RocketMQ笔记”
1 JVM的内存区域布局 java代码的执行步骤有三点 java源码文件编译器字节码文件 字节码文件JVM机器码 机器码系统CPU执行 JVM执行的字节码需要用类加载来载入;字节码文件可以来自本地文件,可以在网络上获取,也可以实时生成。就是说你可以跳过写java代码阶段,直接生成字节码交由JVM执行 其中Jav
2021年度最全面JVM虚拟机,类加载过程与类加载器
前言类装载器子系统是JVM中非常重要的部分,是学习JVM绕不开的一关。一般来说,Java 类的虚拟机使用 Java 方式如下:Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class类的一个实例。每个这样的实例用来表
五面阿里巴巴拿offer后定级P6:分享Java面经及答案总结
一面(电话)说说对JVM的理解treemap和hashmap有什么区别?Java多线程的的5大状态图流转mysql主键和唯一索引的区别说说最近的项目如何实现session共享,用redis如何实现缓存击穿的概念和解决方案说说微服务,微服务之间如何管理二面(现场)java nio常?用的三个类java里面的同步锁了解吗?Countdownlauch和Cylic