Java高级—反射
在程序运行期间,根据类的字节码文件对象来获取类中的成员并使用的一项技术
使用方法:
1.使用构造方法
2.使用成员方法
3.使用成员变量
反射概述
**反射:**在程序运行过程中分析类的一种能力
程序运行:在源文件(.java)中编写源代码;—>通过编译,将其编译成后缀名为.class的字节码文件(.class);—>运行时再通过对象.method()(方法名)
反射能做什么:
分析类(通过类加载器来实现的):加载并初始化一个类;查看类的所有属性和方法
**查看并使用对象:**查看一个对象的所有属性和方法;使用对象的任意属性和方法
反射的应用场景:
1.构建通用的工具
2.搭建具有高度灵活性和扩展性的系统框架
类加载器(ClassLoader)
简介:负责将类的字节码文件(.class文件)加载到内存中,并生成对应的Class对象
Class对象
java.lang.Class类的对象,也叫字节码文件对象,每个Class对象对应一个字节码文件(一个.class文件对应一个.java文件)
类的加载时机
1.创建类的实例
Student stu = new Student();
2.访问类的静态成员
Calender.getInstance();
3.初始化类的子类
class User extends Person{}
User user = new User(); //先加载父类
4.反射方式创建类的Class对象
Class clazz = Class.forName(“类的正名”);
正名:包类路径名(包名 + 类名),例如:io.demo.Student
**总结:**当第一次使用类中的成员时,类加载器就会将该类的字节码文件加载到内存中
获取Class对象的三种方式:
1.根据对象获取
2.根据类名获取
3.根据类名字符串获取(通过类加载器)
1.Object类的getClass()方法(所有的类都默认具有这个方法:Object类是所有类的父类);
Class c = 对象名.getClass();
2.类的静态属性
Class c = 类名.class;
3.Class类的静态方法
Class c = Class.forName(“类的正名”);
package study6.demo;
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
// 需求:获取Class对象
// 方式一
Student s = new Student();
Class c = s.getClass();
System.out.println(c);
System.out.println("————————————");
// 方式二
Class l = Student.class;
System.out.println(l);
System.out.println("————————————");
// 方式三
// 获得正名的快捷方式:右键指定的类——>Copy——>Copy Reference。已复制成功,在粘贴到双引号内即可
Class a = Class.forName("study6.demo.Student"); //异常:ClassNotFoundException,有异常就抛出
System.out.println(a);
// 一个源文件(.java)对应一个字节码文件(.class)
// 思考:如何验证这三个Class对象是同一个对象
System.out.println(c == l); //true
System.out.println(l == a); //true
}
}
Constructor对象
简介:构造器对象,属于java.base模块,java.lang.reflect包
反射方式获取构造方法并使用
通过Class对象获取构造器对象
public Constructor getConstructor(Class<?>… parameterTypes):根据参数列表,返回(获取)一个构造器(Constructor)对象,仅公开构造函数;
Class<?>…:可变参数(可传任意个字节码参数,也可不传),代表Class类型(字节码文件类型)的数组
?:通配符,代表不确定的任意类型
public Constructor getDeclaredConstructor(Class<?>… parameterTypes):根据参数列表,返回一个构造器(Constructor)对象,可获取私有构造函数
public Constructors[] getConstructors():返回此类所有(不含私有)构造函数的数组
Constructor的常用方法
**public String getName()😗*返回构造函数名
**public T newInstance(Object… initargs)😗*根据此构造函数和指定参数创建并初始化对象
步骤:
1.获取Student类的字节码对象
2.根据第一步获取到的字节码文件对象,获取指定的构造器对象
3.根据构造器对象和参数,创建对应的Student对象(调用newInstance方法)
4.打印结果
package study6.demo;
//学生类
public class Student {
//公共的无参构造
public Student() {
}
// 公共的带参构造
public Student(String name) {
System.out.println("您录入的name为:"+name);
}
//私有的带参构造
private Student(int age) {
System.out.println("您录入的age为:"+age);
}
}
package study6.demo;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
/*
*/
public class ReflectDemo2 {
// 原本的两个异常:ClassNotFoundException,NoSuchMethodException,直接抛出父类Exception
public static void main(String[] args) throws Exception {
//需求:通过反射的方式创建:Student类型的对象
// Student s = new Student();
// 1.获取Student类的字节码对象
Class c = Class.forName("study6.demo.Student"); //ClassNotFoundException:有异常,抛出
// 2.根据第一步获取到的字节码文件对象,获取指定的构造器对象
// 2.1获取公共的无参构造
// getConstructor()返回的是一个Constructor对象
// Constructor con = c.getConstructor();//NoSuchMethodException:没有这样的方法异常
// System.out.println(con); //public study6.demo.Student()
2.2获取公共的有参构造
String.class:Student类中公共的有残构造的参数类型是String类型的,字节码文件类型
// Constructor con2 = c.getConstructor(String.class);
// System.out.println(con2); //public study6.demo.Student(java.lang.String)
//
2.3获取私有的有参构造
// Constructor con3 = c.getDeclaredConstructor(int.class); //一定要看好单词有没有出错
// System.out.println(con3); //private study6.demo.Student(int)
// System.out.println("————————————");
2.4获取Student类的所有公共的构造函数
// Constructor[] con4 = c.getConstructors();
遍历数组:增强for
// for (Constructor constructor : con4) {
// System.out.println(constructor);
// //public study6.demo.Student()
// //public study6.demo.Student(java.lang.String)
// }
Constructor con = c.getConstructor(String.class);
System.out.println(con); //public study6.demo.Student(java.lang.String)
// 获取构造器的名字看看他是哪个类的构造
String name = con.getName();
System.out.println(name); //study6.demo.Student说明是学生类中的构造器
// 3.根据构造器对象和参数,创建对应的Student对象
// Object s = con.newInstance("张三"); //newInstance创建的s是Object类型的,要想获得Student类的对象,要通过向下转型
Student s =(Student) con.newInstance("张三");
// 4.打印结果
System.out.println(s);
//您录入的name为:张三
// study6.demo.Student@135fbaa4:当看到@135fbaa4这个地址值时,就说明学生对象创建成功了
}
}
反射方式获取成员方法并使用
Method对象
方法对象,属于java.base模块,java.lang.reflect包
通过Class对象获取方法
**public Method getMethod(String name,Class<?>… parameterTypes)😗*返回一个Method对象,仅公共成员方法
name:方法名
parameterTypes:方法的参数列表
**public Method getDeclaredMethod(String,Class<?>…)😗*返回一个Method对象,可获取私有成员方法
**public Method[] getMethods()😗*返回此类所有(不含私有)方法的数组
Method常用方法
**public String getName()😗*返回方法名
public Object invoke(Object obj,Object… args):(用来执行对应的方法的)在 指定对象上调用此方法,参数为args;obj是对象名;args是调用方式时,所需的具体的参数
**public void setAccessible(boolean flag)😗*是否开启暴力反射(true:开启)
package study6.demo;
//学生类
public class Student {
//公共的空参方法
public void show() {
System.out.println("我是公共的空参方法");
}
// 公共的带参方法
public void show2(int a) {
System.out.println("我是公共的带参方法,你传入的值为:"+a);
}
// 私有的带参方法
private int show3(int a,int b) {
System.out.println("我是私有的带参方法");
return a+b;
}
}
package study6.demo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectDemo3 {
public static void main(String[] args) throws Exception {
// 需求:通过反射获取Student类中的成员方法并使用
// 1.先获取Student类的字节码文件对象
Class c = Class.forName("study6.demo.Student");
// 2.获取该类的构造器对象,再创建Student类的对象
Constructor con = c.getConstructor();
Student s =(Student) con.newInstance();
// System.out.println(s); //study6.demo.Student@135fbaa4创建成功
// 3.获取该类的成员方法对象,然后调用此方法
//3.1调用公共的空参方法
Method method1 = c.getMethod("show");
//打印方法对象
System.out.println(method1); //public void study6.demo.Student.show()
//打印方法名
System.out.println(method1.getName()); //show
//调用此方法
method1.invoke(s); //因为show方法返回值类型是void,所以不用通过变量接收,直接写就可以了
// 调用公共的带参方法
Method method2 = c.getMethod("show2", int.class);
// 调用方法
method2.invoke(s,100); //我是公共的带参方法,你传入的值为:100
// 3.3调用私有的带参方法
Method method3 = c.getDeclaredMethod("show3", int.class, int.class);
// 报错解决: 开启暴力反射,因为show3方法是私有的
method3.setAccessible(true);
// 调用此方法;method3是Object类型的,用int类型的sum接收,就要向下转型
int sum = (int) method3.invoke(s,10,20);
System.out.println("您录入的两个数的和是:"+sum);//报错:IllegalAccessException
// 3.4获取Student类中所有的成员方法
Method[] methods = c.getMethods();
for (Method method : methods) {
System.out.println(method);
}
/*因为所有的类都直接或间接的继承了Object类,除了本身定义的方法外还有从Object类中继承来到方法
public void study6.demo.Student.show()
public void study6.demo.Student.show2(int)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()
*/
}
}
案例:通过反射获取方法并使用
**需求:**通过反射获取类的setter方法,使用该方法为属性赋值
分析:
1.setter方法的方法名由set和属性名(首字母大写)组成:
举例:setName,setAge
2.setter方法有且只有一个参数,参数类型为属性的类型:
举例:setName(String name),setAge(int age)
3.setter方法为public修饰的方法,反射获取该方法使用:
getMethod(String,Class<?>…);
4.根据上述分析分别为属性name\age赋值并使用
package study6.demo;
//学生类
public class Student {
//成员变量
private String name;
//无参构造
public Student() {
}
//带参构造
public Student(String name) {
this.name = name;
}
//getXxx()、setXxx()
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//toString()
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
}
package study6.demo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectDemo4 {
public static void main(String[] args) throws Exception {
// 通过反射获取类的setter方法,使用该方法为属性赋值
// 1.通过反射获取Student类的字节码文件对象
Class c = Class.forName("study6.demo.Student");
// 2.通过反射获取Student类的构造方法,并创建该类的对象
Constructor con = c.getConstructor();
Student s =(Student) con.newInstance();
// 3.获取到指定的setName()方法,给Student对设置值
Method name = c.getMethod("setName", String.class);
// 调用此方法
name.invoke(s,"张三");
// 4.打印学生对象
System.out.println(s);
}
}
反射方式获取成员变量并使用
Field对象
域(属性、成员变量)对象,属于java.base模块,java.lang.reflect包
通过Class对象获取属性
**public Field getField(String name):**返回一个Field对象,仅公共成员变量
**public Field getDeclaredField(String name)😗*返回一个Field对象,可获取私有成员变量
**public Field[] getDeclaredFields()😗*返回此类所有(含私有)成员变量的数组
Field的常用方法
**public void set(Object obj,Object value)😗*设置obj对象的指定属性值为value
**public void setAccessible(boolean flag)😗*将此属性的可访问性设置为指定布尔值(是否开启暴力反射)
package study6.demo;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class ReflectDemo5 {
public static void main(String[] args) throws Exception {
// 需求:通过反射获取成员变量并使用
// 1.获取Student类的字节码文件对象
Class c = Class.forName("study6.demo.Student");
// 2.通过字节码文件对象获取构造器对象,然后创建学生类对象
// 分写
Constructor con = c.getConstructor();
Student s = (Student)con.newInstance();
// 合并
// Student s = (Student) c.getConstructor().newInstance(); //链式编程
// 3.设置学生对象的各个属性
//3.1设置姓名
Field f = c.getField("name");
f.set(s,"张三");
// 3.2设置年龄
Field i = c.getDeclaredField("age");
// 开启暴力反射
i.setAccessible(true); //没加这个就会报错
i.set(s,25);
// 4.打印学生对象
System.out.println(s);
}
}