说到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到底是什么

一篇文章弄懂 Java 反射的使用的更多相关文章

  1. 一篇文章看懂java反射机制(反射实例化对象-反射获得构造方法,获得普通方法,获得字段属性)

    Class<?> cls = Class.forName("cn.mldn.demo.Person"); // 取得Class对象传入一个包名+类名的字符串就可以得到C ...

  2. 一篇文章读懂Java类加载器

    Java类加载器算是一个老生常谈的问题,大多Java工程师也都对其中的知识点倒背如流,最近在看源码的时候发现有一些细节的地方理解还是比较模糊,正好写一篇文章梳理一下. 关于Java类加载器的知识,网上 ...

  3. 一篇文章看懂Java并发和线程安全

    一.前言 长久以来,一直想剖析一下Java线程安全的本质,但是苦于有些微观的点想不明白,便搁置了下来,前段时间慢慢想明白了,便把所有的点串联起来,趁着思路清晰,整理成这样一篇文章. 二.导读 1.为什 ...

  4. 一篇文章看懂Java并发和线程安全(一)

    一.前言 长久以来,一直想剖析一下Java线程安全的本质,但是苦于有些微观的点想不明白,便搁置了下来,前段时间慢慢想明白了,便把所有的点串联起来,趁着思路清晰,整理成这样一篇文章. 二.导读 1.为什 ...

  5. Java多线程详解——一篇文章搞懂Java多线程

    目录 1. 基本概念 2. 线程的创建和启动 2.1. 多线程实现的原理 2.2.多线程的创建,方式一:继承于Thread类 2.3.多线程的创建,方式一:创建Thread匿名子类(也属于方法一) 2 ...

  6. 一篇文章弄懂flex布局

     壹 ❀ 引 谈到flex布局,我不知道有多少人跟我一样,在本能的想到justify-content:center与align-items:center两条属性之后,除此之外的其它属性居然显得格外陌生 ...

  7. 一篇文章看懂JS闭包,都要2020年了,你怎么能还不懂闭包?

     壹 ❀ 引 我觉得每一位JavaScript工作者都无法避免与闭包打交道,就算在实际开发中不使用但面试中被问及也是常态了.就我而言对于闭包的理解仅止步于一些概念,看到相关代码我知道这是个闭包,但闭包 ...

  8. 【转】彻底弄懂Java中的equals()方法以及与"=="的区别

    彻底弄懂Java中的equals()方法以及与"=="的区别 一.问题描述:今天在用Java实现需求的时候,发现equals()和“==”的功能傻傻分不清,导致结果产生巨大的偏差. ...

  9. 一篇文章搞懂高级程序员、架构师、技术总监、CTO从薪资到技能的区别

    一篇文章搞懂高级程序员.架构师.技术总监.CTO从薪资到技能的区别 http://youzhixueyuan.com/senior-programmers-architects-technical-d ...

随机推荐

  1. E - Level K Palindrome

    题目大意: As a token of his gratitude, Takahashi has decided to give Snuke a level-KK palindrome. A leve ...

  2. hdu4530 水题

    题意: 小Q系列故事--大笨钟 Time Limit: 600/200 MS (Java/Others) Memory Limit: 65535/32768 K (Java/Others) Total ...

  3. 路由器逆向分析------在QEMU MIPS虚拟机上运行MIPS程序(ssh方式)

    本文博客地址:http://blog.csdn.net/qq1084283172/article/details/69652258 在QEMU MIPS虚拟机上运行MIPS程序--SSH方式 有关在u ...

  4. Python中的时间日期模块(time、datetime)

    目录 Datetime 获取当前时间 获取当前日期 获取当前时间的tuple元组 格式化日期和时间 时间移动 获取两个时间的时间差 时间格式转换 Time 获取距元年(1970.1.1)的秒数 当时时 ...

  5. Nmap 扫描器的使用技巧

    1.nmap语法 -A 全面扫描/综合扫描 例如:nmap -A 127.0.0.1 扫描指定段 例如:nmap 127.0.0.1-200&nmap 127.0.0.1/24 2.Nmap ...

  6. Docker仓库-Docker Hub

    Docker Hub Docker官方维护了一个公共仓库Docker Hub,其中已经包括了数量上百万的镜像,大部分需求都可以通过在Docker Hub 中直接下载镜像来实现. 注册: https:/ ...

  7. 推荐算法-聚类-DBSCAN

    DBSCAN(Density-Based Spatial Clustering of Applications with Noise)是一个比较有代表性的基于密度的聚类算法,类似于均值转移聚类算法,但 ...

  8. java8中的日期和时间API

    一.背景 jdk 1.8 之前, Java 时间使用java.util.Date 和 java.util.Calendar 类. Date today = new Date(); System.out ...

  9. 微信小程序中的常见弹框

    显示加载中的提示框 wx.showLoading() 当我们正在在进行网络请求时,常常就需要这个提示框 手动调用wx.hideLoading()方法才能够关闭这个提示框,通常在数据请求完毕时就应该关闭 ...

  10. 一看就懂的MySQL的聚簇索引,以及聚簇索引是如何长高的

    这一篇笔记我们简述一下 MySQL的B+Tree索引到底是咋回事? 聚簇索引索引到底是如何长高的. 一点一点看,其实蛮好理解的. 如果你看过了我之前的笔记,你肯定知道了MySQL进行CRUD是在内存中 ...