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

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

先想一个问题

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

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

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

1package com.model; 2 3public class Student { 4 private String name; 5 private int age; 6 7 public Student(){ 8 } 9 10 public Student(String name,int age){ 11 this.name = name; 12 this.age = age; 13 } 14 15 public String getName() { 16 return name; 17 } 18 19 public void setName(String name) { 20 this.name = name; 21 } 22 23 public int getAge() { 24 return age; 25 } 26 27 public void setAge(int age) { 28 this.age = age; 29 } 30 31 public void show(){ 32 System.out.println("name=" + this.name + " age=" + this.age); 33 } 34} 35

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

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

1 //1 创建一个对象 2 Student s = new Student(); 3 4 //2 调用对象的方法 5 s.setName("李雷"); 6 s.setAge(23); 7 8 //3 打印一下 9 s.show();

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

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

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

2. 如何获取类的 Class 呢?

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

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

代码如下:

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

第二种:通过 Student.class

1Class cls = Student.class

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

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

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

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

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

代码演示如下:

1 //第一步:获取Student的 Class 对象,即Student的字节码 2 Class cls = Class.forName("com.model.Student"); 3 4 //第二步:获取无参的构造方法的字节码,当然也可以获取有参的 5 Constructor constructor = cls.getConstructor(); 6 7 //第三步:调用构造函数的字节码对象的 newInstance 方法创建 Student的对象 obj 8 Object obj = constructor.newInstance(); 9 10 //第四步:获取 setName 方法的字节码,注意参数传方法的名字以及方法中参数的字节码 11 // 获取了setName的字节码 method,调用方法必须要有一个对象,所以上面的obj对象就是用来此处的 12 // 一定要传进行 13 Method method = cls.getMethod("setName", String.class); 14 method.invoke(obj,"待兔"); 15 16 //和上面类似,只不过这次 getMethod 的第二个参数传的是 int.class 17 //因为第二个参数是int类型 18 Method method1 = cls.getMethod("setAge", int.class); 19 method1.invoke(obj,23); 20 21 //和上面类似 ,只不过 show()方法是无参的,所以 getMethod 只需要传方法的名字"show" 即可 22 Method showMethod = cls.getMethod("show"); 23 24 //最后:调用showMethod方法,通过调用showMethod的invoke方法,里面传入前面创建的obj对象 25 //就达到了调用对象的show方法 26 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()

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

输出出下:

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

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

再来看一下 getDeclaredMethods() 方法

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

输出如下:

getName setName setAge show getAge

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

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

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

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

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

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

1//第一步:获取Student的 Class 对象,即Student的字节码 2Class cls = Class.forName("com.model.Student"); 3 4//第二步:获取无参的构造方法的字节码,当然也可以获取有参的 5Constructor constructor = cls.getConstructor(); 6 7//第三步:调用构造函数的字节码对象的 newInstance 方法创建 Student的对象 obj 8Object obj = constructor.newInstance();

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

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

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

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

点赞
收藏

评论区

加载中...

相关推荐

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java类的加载与加载器

java代码在计算机中经历的三个阶段:1.Source源代码阶段(代码还是在硬盘上,并没有进入内存)  Student.java通过javac编译Student.class字节码文件2.类加载器ClassLoader将字节码文件加载进入内存,成为Class类对象(成员变量Field\\fields、构造方法Const

java 面试知识点笔记(一)底层知识 jvm相关

问:谈谈你对java的理解?平台无关性(一处编译多处运行)GC(垃圾回收机制)语言特性(泛型、反射、lambda表达式)面向对象(封装继承多态)类库(集合、网络库、并发库、nio)异常处理javac将.java文件进行编译,编译出.class文件(二进制字节码,包含java类中的属性

RPC框架手撕之路---java反射以及动态代理机制

在上一篇文章中,我们提到了,RPC框架所需要的java基础,第一点就是java的动态代理机制,动态代理机制的基础是反射,无论是在实际编程或者是面试时,都是java知识的重中之重。java反射:定义:在运行状态中,对于任意一个类,都能够知道这一个类的所有属性和方法,对于任意一个对象都能够通过反射机制调用一个类的任意方法,这种动态获取类信息以及动态调用类方法

Spring的两种代理JDK和CGLIB的区别浅谈

一、原理区别:java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP 2、如果目标对象实现了接口,可以

Java文件格式

\.java文件是保存源代码的文本文件(\代表类名)使用javac\.java可以编译该文件使用java\可以运行该类\.class是用于保存Java类的二进制编码以及Class对象,每一个Java类都有一个解释该类特征的Class对象。\.jar文件是一种压缩文件格式打包命令jarcvf