java 反射机制精讲
目录
1. 反射机制的概念
2. 反射的基础 Class 类
3. 反射的用法
4. 反射的应用示例
作者简介: 全栈学习笔记, 一个正在努力的人
反射机制的概念:
在运行状态中, 对于任意一个类, 都能够获取到这个类的所有属性和方法, 对于任意一个对象, 都能够调用它的任意一个方法和属性 (包括私有的方法和属性), 这种动态获取的信息以及动态调用对象的方法的功能就称为 java 语言的反射机制. 反射被视为动态语言的关键. 简单来说反射就是 java 的各种成分映射成对应的 java 类.
通俗点讲, 通过反射, 该类对我们来说是完全透明的, 想要获取任何东西都可以. 包括构造方法, 属性, 方法.
java 反射机制提供的功能:
在运行时判断任意一个对象所属的类;
在运行时构造任意一个类的对象;
在运行时判断任意一个类所具有的成员变量和方法;
在运行时调用任意一个对象的方法;
生成动态代理.
这其实也涉及到了语言的动态与静态, java 语言本身不算是动态语言, 但是他有一个非常突出的动态机制, 就是我们所说的反射机制.
什么是动态语言呢? 就是说, 程序在运行的时候,(注意是运行的时候, 不是编译的时候) 允许改变程序结构或者变量类型. 反之静态就是没有这些特点了.
反射的基础 Class 类
Class 类是反射实现的基础, 所以想要学会反射, 必须先掌握 Class 类的一些基本的概念.
类是什么? 类是 Class 类的实例对象, 所以说 Class 类是所有类的类.
要想解剖一个类, 必须先获取到该类的字节码文件对象. 而解剖使用的就是 Class 类中的方法, 所以先要获取每一个字节码文件对应的 Class 类型的对象.
Class 类没有公共的构造方法, Class 对象是在类加载的时候由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的, 因此不能显式地声明一个 Class 对象. 这里又涉及到一个东西, 类的加载
简要的说明一下:
类加载器: 当程序需要是用某个类时, 如果该类还没有被加载到内存中, 则, 系统会通过加载, 连接, 初始化 这三步来对类进行初始化
加载: 就是指将 class 文件读入内存 (编译之后的文件是. class 文件), 并为之创建一个 Class 对象
任何类被使用时, 系统都会建立一个 Class 对象, 第一次的时候会, 第二次则会判断这个类是否存在.
连接: 验证是否有正确的内部结构, 并和其他类协调一致
准备为类的静态成员分配内存, 并设置默认初始化值
并做一个解析: 将类的二进制数据中的字符引用替换为直接引用.
上面说到 Class 对象是不能直接创建的, 但是我们可以通过其他方式得到 Class 类的, 目前有三种方式可以得到我们想要的 Class 类, 得到 Class 类之后就能正常的使用反射了.
获取 Class 的三种方式 (获取一个类的字节码对象):
第一种: 使用对象获取, 使用对象的 getClass 获取
- Person person = new Person();
- Class clazz = person.getClass();
第二种: 使用静态属性 class
Class clazz = Person.class
第三种: 使用 Class 类的静态方法 forName(字符串的类名)
注; 类名要写全包名
Class clazz = Calss.forName(".......");
好了, 重点来了, 反射怎么玩才有趣!
反射的用法
上面说了通过反射可以得到任意一个类的什么什么, 下面来看看是不是真的.
第一步要干啥? 当然是通过之前的哪三种方法来得到这个可以为所欲为的 Class 类. 有三种方法, 我们先都做个示例吧!
上代码
- // 获取 Class 第一种方法
- Student student = new Student();
- Class clazz = student.getClass();
- // 获取 Class 第二种方法
- Class clazzTwo = Student.class;
- // 获取 Class 第三种方法
- Class clazzThree = Class.forName("demo.qzxxbj.entity.Student");
- System.out.println("第一个"+clazz+"\n 第二个"+clazzTwo+"\n 第三个"+clazzThree);
结果
第一个 class demo.qzxxbj.entity.Student
第二个 class demo.qzxxbj.entity.Student
第三个 class demo.qzxxbj.entity.Student
可以看到三种方法得到的 Class 对象是一样的, 没有区别.
第三种方法是会有一个找不到类的异常抛出的.
其中 Student 就是一个简单的类, 可以是任何类.
通过 Class 获取任意一个类的属性
Student 类的代码
- package demo.qzxxbj.entity;
- /**
- * @author 微信公众号: 全栈学习笔记
- * @date 2020/3/29
- * @description
- */
- public class Student {
- private String name;
- private Integer age;
- private String sex;
- public int number;
- public int getNumber() {
- return number;
- }
- public void setNumber(int number) {
- this.number = number;
- }
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public Integer getAge() {
- return age;
- }
- public void setAge(Integer age) {
- this.age = age;
- }
- public String getSex() {
- return sex;
- }
- public void setSex(String sex) {
- this.sex = sex;
- }
- }
以下获取 Class 的方法都采用第二种, 比较简洁
- // 获取 Class 第二种方法
- Class clazzTwo = Student.class;
- // 获取该类指定属性名的 public 成员变量, 包括父类的
- Field field = clazzTwo.getField("number");
- //field public int demo.qzxxbj.entity.Student.number
- System.out.println("该类指定属性名的 public 成员变量, 包括父类的"+field);
- // 获取该类指定名称声明的变量, 即不包括父类的
- Field deField = clazzTwo.getDeclaredField("name");
- // deField private java.lang.String demo.qzxxbj.entity.Student.name
- System.out.println("该类所有声明的变量, 即不包括父类的"+deField);
- // 获取该类所有的 public 声明的成员变量
- Field fields[] = clazzTwo.getFields();
- System.out.println("public 声明的变量:");
- //public int demo.qzxxbj.entity.Student.number
- for (Field field1:fields){
- System.out.println(field1);
- }
- // 获取该对象的所有成员变量
- Field deFields[] = clazzTwo.getDeclaredFields();
- System.out.println("该对象的所有成员变量");
- //private java.lang.String demo.qzxxbj.entity.Student.name
- //private java.lang.Integer demo.qzxxbj.entity.Student.age
- //private java.lang.String demo.qzxxbj.entity.Student.sex
- //public int demo.qzxxbj.entity.Student.number
- for (Field field1:deFields){
- System.out.println(field1);
- }
记住 getFields(),getField(String name),getDeclaredFields(),getDeclaredField(String name) 的区别, 你就能好好掌握这个知识点!
通过 Class 获取任意成员方法
还是看代码吧!
获取成员方法 Method
- // 获取 Class 第二种方法
- Class clazzTwo = Student.class;
- // 根据方法名以及参数类型获取, 只能获取 public 声明的方法, 包括父类的
- Method method = clazzTwo.getMethod("setAge",Integer.class);
- //public java.lang.Integer demo.qzxxbj.entity.Student.getAge()
- System.out.println(method);
- // 根据方法名以及参数名称获取该类声明的所有的属性方法, 不包括父类的
- Method deMethod = clazzTwo.getDeclaredMethod("setAge", Integer.class);
- System.out.println(deMethod);
- // 获取该对象声明的所有的 public 方法, 包括父类的
- Method methods[] = clazzTwo.getMethods();
- // 获取该对象声明的所有的方法, 但是不包含父类的方法
- Method deMethods[] = clazzTwo.getDeclaredMethods();
一个 Method 方法打印出来是什么呢? 上面代码中也包含了
public void demo.qzxxbj.entity.Student.setAge(java.lang.Integer)
和之前讲的 Field 是不是很相似.
既然说到了方法, 那么就肯定涉及到了方法调用, 我们得到了这些方法, 又该怎么调用这个类里面的方法呢? 使用 invoke 函数, Method 这个类里面包含了一个 invoke 函数, 英语好的就知道了, 这个 invoke 的中文意思就是 "调用".
怎么用呢?
- // 获取 Class 第二种方法
- Class clazzTwo = Student.class;
- // 根据方法名以及参数类型获取, 只能获取 public 声明的方法, 包括父类的
- Method method = clazzTwo.getMethod("setAge",Integer.class);
- //public java.lang.Integer demo.qzxxbj.entity.Student.getAge()
- System.out.println(method);
- // 利用 Class 创建一个对象的实例
- Student student = (Student) clazzTwo.newInstance();
- // 函数调用
- Object value = method.invoke(student,20);
- //null
- System.out.println(value);
以上的代码, 你可能会看不懂, 我来讲一下, 首先, 我们获取一个类的 Class, 然后我们通过这个 Class 获取该类的一个 setAge 方法, 获取到这个方法后继续调用这个方法, 调用方法是不是应该调用一个实例对象里面的方法? 所以我们需要先实例化一个对象, 通过什么方法呢, 通过 class 里面的 newInstance(), 创建一个实例, 这种方法需要该实例化的类具有一个无参构造方法. 还有其他方法也能创建一个实例, 后面我们会说. 创建出一个实例对象之后, 我们就能开始调用方法了.
通过 invoke 对方法进行调用, invoke 的第一个参数就是一个实例化对象, 不然我去哪找这个方法. 第二个参数, 或者第三个, 等等, 后面的所有参数都是我调用的该方法所具有的参数, 按照顺序填进去就 OK 了. 然后这个函数返回的是一个 Object 对象, 你都能想到, 我调用一个方法是不是要让他做一些事, 做了这些事需要返回一个东西, 不知道这个东西是啥, 就用 Object 获取嘛. 由于我们调用的这个方法不需要返回值, 所以就是 null 了. 很简单是不是. 学到了记得给我点个关注哦! 精彩美文第一时间推送到你的手中.
通过 Class 获取构造方法
这个被我放到了最后来学习, 毕竟我觉得用的比例比较少. 一起来学习一下怎么用 Class 获取构造方法, 并调用他.
- public Student(String name, int id) {
- this.name = name;
- this.id = id;
- }
这里我们在 Student 类里面添加了一个构造方法.
然后我们来获取这个构造方法.
- // 获取 Class 第二种方法
- Class clazzTwo = Student.class;
- // 获取无参构造方法, public 声明的, 包括父类, 加上参数时就是获取特定的构造方法
- Constructor constructor = clazzTwo.getConstructor();
- //public demo.qzxxbj.entity.Student()
- System.out.println(constructor);
- // 获取该类所有的 public 声明的构造方法
- Constructor constructors[] = clazzTwo.getConstructors();
- // 获取指定参数的构造方法
- Constructor deConstructor = clazzTwo.getDeclaredConstructor(String.class,Integer.class);
- // 获取所有的该类的构造方法, 不包括父类的
- Constructor deConstructors[] =clazzTwo.getDeclaredConstructors();
上面代码应该很容易看懂吧, 我就不细说了. 这里说一下如何使用得到的构造方法, 构造方法顾名思义就是来实例化对象的, 上面我们也有说到怎么通过 Class 实例化一个对象, 现在我们来通过构造方方法实例化一个对象
- Student student = (Student) deConstructor.newInstance("全栈学习笔记",21);
- //21
- System.out.println(student.getAge());
- public String insert(Object object) throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {`
- //insert into student(id,name,sex) values (1,"全栈学习笔记","男")
- StringBuilder sql = new StringBuilder();
- Class clazz = object.getClass();
- sql.append("insert into");
- sql.append(clazz.getSimpleName()+"(");
- Field[] fields = clazz.getDeclaredFields();
- for(Field field:fields){
- sql.append(field.getName()+",");
- }
- sql.deleteCharAt(sql.length()-1);
- sql.append(")");
- sql.append("values (");
- for(Field field:fields){
- field.setAccessible(true);
- Object value = field.get(object);
- String fieldName = field.getName();
- String str1 = fieldName.substring(0,1).toUpperCase();
- String str2 = fieldName.substring(1,fieldName.length());
- String strValue = str1.concat(str2);
- //String strValue = fieldName.substring(0,1).toUpperCase().concat(fieldName.substring(1,fieldName.length()));
- Method method = clazz.getMethod("get"+strValue,null);
- Object value1 = method.invoke(object,null);
- // if(value1.getClass().equals(String.class))
- // if(field.getType().equals(String.class))
- if(value1 instanceof String){
- sql.append("\"").append(value1).append("\"").append(",");
- }else {
- sql.append(value1).append(",");
- }
- }
- sql.deleteCharAt(sql.length()-1);
- sql.append(")");
- System.out.println(sql.toString());
- return sql.toString();
- }
来源: https://www.cnblogs.com/swzx-1213/p/12597159.html