一、前言
1.什么是反射机制
Java反射指的是Java语言在运行时动态地获取类信息、构造类对象、调用对象方法以及访问或修改对象属性的能力。Java反射机制允许Java程序在运行期间检查其本身的结构并获取类属性、方法和构造函数的信息,也可以动态地创建类的实例、调用其方法和访问其属性。JDK 中 java.lang.Class 类,就是为了实现反射提供的核心类之一。
2.为什么要使用反射
1.获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器(类类)等
2.获取任意对象的属性,并且能改变对象的属性
3.调用任意对象的方法
4.判断任意一个对象所属的类
5.实例化任意一个类的对象
二、使用反射
接下来的所有操作用Student类来演示
package com.xw; public class Student { private String sid; private String sname; public Integer age; static{ System.out.println("静态代码块加载中。。。"); } public Student() { super(); System.out.println("调用无参构造方法创建了一个学生对象"); } public Student(String sid) { super(); this.sid = sid; System.out.println("调用带一个参数的构造方法创建了一个学生对象"); } public Student(String sid, String sname) { super(); this.sid = sid; this.sname = sname; System.out.println("调用带二个参数的构造方法创建了一个学生对象"); } @SuppressWarnings("unused") private Student(Integer age) { System.out.println("调用Student类私有的构造方法创建一个学生对象"); this.age = age; } public String getSid() { return sid; } public void setSid(String sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public void hello() { System.out.println("你好!我是" + this.sname); } public void hello(String name) { System.out.println(name + "你好!我是" + this.sname); } @SuppressWarnings("unused") private Integer add(Integer a, Integer b) { return new Integer(a.intValue() + b.intValue()); } }
1.什么是类类(类加载器)
在Java中,类加载器是一种重要的机制,它负责把 Java 字节码文件(.class文件) 加载到内存中,并将其转换成 Java 类的运行时表示形式。Java类加载器按照加载的方式和顺序可以分为三类:Bootstrap Class Loader(启动类加载器)、Extention Class Loader(扩展类加载器)和App Class Loader(应用程序类加载器)。
类加载器是Java虚拟机(JVM)的一部分,主要用于实现Java程序的动态扩展和代码的动态加载。类加载器可以加载本地的class文件,也可以从网络或其他JVM中获取class文件进行加载。通过Java反射和类加载器机制,开发人员可以动态地创建对象、调用类的方法、访问类的属性等等,从而实现软件组件的灵活性和可扩展性。
注意:类加载器并不等同于类,它只负责将类的字节码文件加载到内存中,虚拟机会根据字节码文件实例化和加载类。类加载器按照加载的顺序和层次关系形成一个树状结构,父类加载器可以委托子类加载器来加载类,在默认情况下,父类加载器可以看到其子类加载器所加载的类,但反过来则不行,这样可以使得类在整个JVM中得到合适的共享。
2.获取类类(类加载器)
Class.forName
//第一种:Class.forName 参数是类的全路径名 Student stu=new Student(); Class forName = Class.forName("com.xw.Student"); System.out.println(forName); //打印结果:class com.xw.Student
主要作用于连接sql,加载驱动。
类名.class
//第二种:类名.class Class StudentClass =Student.class; System.out.println(StudentClass); //打印结果:class com.xw.Student
实例化对象.getClass
//第三种:对象.getClass Student stu=new Student(); Class objectClass=stu.getClass(); System.out.println(objectClass); //打印结果:class com.xw.Student
注意:
①所有反射先从类类(类加载器)开始,我们首先需要通过获取类类(类加载器),来加载目标类的字节码文件,然后解析字节码文件并生成类的元数据信息,最终使我们能够使用Java反射机制来操作该类的实例。
②反射机制默认调用无参构造器来创建类的实例,但是并不是说所有的类都必须有无参构造器。如果一个类没有定义无参构造器,那么反射机制就无法直接使用Class.newInstance()方法来创建该类的实例,因为该方法会默认调用无参构造器,如果该类没有定义无参构造器,就会抛出InstantiationException异常。
③如果一个类没有定义无参构造器,我们可以通过使用其他的构造器来创建类的实例。例如,我们可以使用Constructor.newInstance()方法来调用指定的构造器来创建类的实例。
3.反射实例
反射实例要用到的方法
方法 | 描述 |
newInstance() | 用于创建一个类的实例。用 newInstance() 方法时,该类必须有可访问的无参构造方法,否则会抛出异常。 |
getConstructo() | 获取指定类中的公共构造器,可以通过这个构造器来创建该类的实例。 注意:该方法需要传入一个Class数组作为参数,用于指定构造器的参数类型列表。 |
getDeclaredConstructor() | 获取指定类中声明的构造器。与getConstructor() 方法不同的是,getDeclaredConstructor() 方法可以获取到指定类中的私有构造器。 |
setAccessible() | 用于设置一个 AccessibleObject 对象(包括构造器、方法、字段等)的访问标志为 true,使得在访问该对象时被访问的实体(例如:Java 虚拟机)可以忽略 JAVA 语言访问控制检查。 |
没有无参构造器的错误
Class<Student> c0=Student.class; Student newInstance = c0.newInstance(); System.out.println(newInstance); //java.lang.NoSuchMethodException: com.xw.Student.<init>()
无参构造器
Class<Student> c1=Student.class; Student newInstance2 = c1.newInstance(); System.out.println(newInstance2); //com.xw.Student@7852e922
一个参数的构造器
Class<Student> c2=Student.class; Constructor<Student> cr2 = c2.getConstructor(String.class); Student newInstance3 = cr2.newInstance("01"); System.out.println(newInstance3); //com.xw.Student@182decdb
两个参数的构造器
Class<Student> c3=Student.class; Constructor<Student> cr3 = c3.getConstructor(String.class,String.class); Student newInstance4 = cr3.newInstance("01","xw"); System.out.println(newInstance4); //com.xw.Student@182decdb
私有的有参构造器
Class<Student> c4=Student.class; Constructor<Student> cr4 = c4.getDeclaredConstructor(Integer.class); cr4.setAccessible(true); Student newInstance5 = cr4.newInstance(2); System.out.println(newInstance5); //com.xw.Student@182decdb
4.反射动态方法调用
如果你看懂了反射实例那么反射动态方法只是换个方法那么简单。
反射动态方法要用到的方法
方法 | 描述 |
getMethod(name, parameterTypes) | 获取某个类中的公共方法。这个方法属于 Class 类的实例方法,因此必须先获取到该类的 Class 对象,然后调用 getMethod() 方法。 name:方法名 parameterTypes:类型 |
invoke(obj, args) | 调用一个对象的方法,并且可以向该方法传递参数。 obj:类实例 args:参数 |
getDeclaredMethod(name, parameterTypes) | 获取某个类中声明的方法,不包括父类中声明的方法。这个方法属于 Class 类的实例方法,因此必须先获取到该类的 Class 对象,然后调用 getDeclaredMethod() 方法。 name:方法名 parameterTypes:类型 |
调用无参方法
// 所有反射先从类类开始 Class<Student> c = Student.class; Student stu = new Student(); Method m1 = c.getMethod("hello"); Object invoke = m1.invoke(stu); System.out.println(invoke); /* * 你好!我是null null */
调用有参方法
// 所有反射先从类类开始 Class<Student> c = Student.class; Student stu = new Student(); Method m2 = c.getMethod("hello", String.class); Object invoke2 = m2.invoke(stu, "xw"); System.out.println(invoke2); /* * xw你好!我是null null */
调用私有方法
// 所有反射先从类类开始 Class<Student> c = Student.class; Student stu = new Student(); Method m3 = c.getDeclaredMethod("add", Integer.class,Integer.class); m3.setAccessible(true); Object invoke3 = m3.invoke(stu, 1,2); System.out.println(invoke3); //打印结果:3
5.通过反射进行读写属性
反射读写属性要用的方法
方法 | 描述 |
getDeclaredField() | 获取类中指定名称的成员变量,不包括父类中的成员变量。 |
getDeclaredFields() | 获取类中全部的成员变量,不包括父类中的成员变量。 |
get() | 获取类的成员变量的值的方法。这个方法属于 Field 类的实例方法,因此必须先获取到该类的 Field 对象,然后调用 get() 方法。 |
getName() | 用于获取成员变量名称的方法,可以获取所有可访问的类成员(包括私有或受保护成员)。 |
set() | 设置类的成员变量值的方法,可以设置公共成员变量和私有成员变量的值。需要注意的是。注意:在设置私有成员变量的值时,需要先使用 setAccessible(true) 方法将可访问性设置为 true。 |
根据属性名称获取指定实例的值
// 实例化student类 Student stu = new Student("01", "xw"); // 获取类类 Class cc = Student.class; Field declaredField = cc.getDeclaredField("sname"); declaredField.setAccessible(true); Object object = declaredField.get(stu); String name = declaredField.getName(); System.out.println(name); System.out.println(object); //打印结果:xw
获取实例的所有属性名称以及属性值
// 实例化student类 Student stu = new Student("01", "xw"); // 先获取类类 Class cc = Student.class; Field[] field = cc.getDeclaredFields(); for (Field field2 : field) { field2.setAccessible(true); System.out.println(field2.getName() + " " + field2.get(stu)); /* * sid 01 * sname xw * age null * */ }
修改实例student的sname的属性值
// 实例化student类 Student stu = new Student("01", "xw"); // 先获取类类 Class cc = Student.class; Field declaredField2 = cc.getDeclaredField("sname"); declaredField2.setAccessible(true); declaredField2.set(stu, "xw帅逼"); Object object2 = declaredField2.get(stu); System.out.println(object2); //打印结果:xw帅逼
觉得有用的话记得点个赞哦!!