前言
框架通常借助设计模式、反射和配置文件等技术实现灵活的开发和可扩展性。今天我们就来了解反射这项技术。
一、反射概述
1.1 概念
Java中的反射是指在程序运行时检查、访问、修改变量、方法、调用构造函数的机制。
Java中反射机制主要是由java.lang.Class类及其相关方法以及java.lang.reflect包中的Field、Method、Constructo
r类和访问控制相关类构成的。
1.2 作用
(1)可以在运行时动态地获取类、接口、方法、属性等对象的信息,实现了代码和数据的分离,增加了程序的灵活性和可扩展性。
(2)可以在运行时动态地创建对象、调用方法、操作属性,尤其使用在框架的开发中,可以通过反射机制灵活的使用已经存在的类库、组件库等。
(3)可以实现一些基于注解的编程技术,例如通过注解定义一些特定的属性信息,然后可以使用反射机制在程序运行时动态地读取和处理这些注解信息。
(4)反射机制可以实现一些基本Java中无法实现的特殊操作,例如通过反射机制访问私有成员,执行私有方法,实现单例模式等。
二、类类
Java中的类类是指java.lang.Class
类,是Java反射机制的核心类之一。类类表示Java程序中的一个类或接口。
2.1常用方法
方法 | 描述 |
getName() | 返回类的名称,包括包名 |
getSimpleName() | 返回类的简单名称,不包括包名 |
getModifiers() | 返回类的修饰符,例如public、private、static等 |
getPackage() | 返回类所在的包 |
getFields() | 返回类或接口的public字段 |
getDeclaredFields() | 返回类或接口声明的所有字段,包括私有字段 |
getMethods() | 返回类或接口的public方法 |
getDeclaredMethods() | 返回类或接口声明的所有方法,包括私有方法 |
getConstructors() | 返回类的public构造器 |
getDeclaredConstructors() | 返回类声明的所有构造器,包括私有构造器 |
2.2实例化类类
这里有个Student类
package com.xqx.reflect; public class Student { private String sid; private String sname; public Integer age; static { System.out.println("加载进jvm中!"); } 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,String sname) { System.out.println("调用Student类私且多参的构造方法创建一个学生对象"); this.age = age; this.sname = sname; } 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()); } }
2.2.1Class.forName()
这个方法可以根据给定的类名获取对应的Class对象
Class c = Class.forName("com.xqx.reflect.Student");
2.2.2getClass()
该方法可以返回当前对象的类的Class对象。例如
Student stu=new Student(); Class class1 = stu.getClass();
2.2.3类名.class
Class c1=Student.class;
System.out.println(c); System.out.println(c1); System.out.println(class1);
打印结果:
class com.xqx.reflect.Student class com.xqx.reflect.Student class com.xqx.reflect.Student
2.2.4创建类实例
获取了类的Class对象之后,就可以使用Class.newInstance()
方法来创建类实例:
//一切反射从类类开始 Class c = Class.forName("com.xqx.reflect.Student"); //创建类实例 无参实例化 Student stu = (Student) c.newInstance();
需要注意的是,newInstance()方法只能创建无参构造函数的类对象,如果存在有参构造函数,则需要使用Constructor类来创建类实例。同时,在Java 9中,newInstance()方法已被废弃,建议使用Class.getDeclaredConstructor()
方法代替。
三、反射属性
通过Field类来获取对象的属性,也可以获取到私有属性,还可以设置属性的值。
3.1公有属性的获取与赋值
getDeclaredField()方法获取属性、get()方法打印获取到属性的值、set()方法为熟悉赋值
Student student = new Student(); student.setSid("s001"); student.setSname("xqx"); student.age=18; //一切反射从类类开始 Class<? extends Student> c = student.getClass(); //获取到公有属性age Field ageField = c.getDeclaredField("age"); System.out.println("修改前:"+ageField.get(student)); ageField.set(student, 16); System.out.println("修改后:"+ageField.get(student));
打印结果:
加载进jvm中! 调用无参构造方法创建了一个学生对象 修改前:18 修改后:16
3.2私有属性的获取与赋值
获取与赋值方法跟公有属性一致,但必须打开权限setAccessible才可调用,否则报错
//拿到私有属性 Field idField = c.getDeclaredField("sid"); Field nameField = c.getDeclaredField("sname"); //打开私有属性访问权限 idField.setAccessible(true); nameField.setAccessible(true); System.out.println("修改前:"+idField.get(student)); //修改 idField.set(student, "s110"); System.out.println("修改后:"+idField.get(student)); System.out.println("修改前:"+nameField.get(student)); nameField.set(student, "nb"); System.out.println("修改后:"+nameField.get(student));
打印结果:
修改前:s001 修改后:s110 修改前:xqx 修改后:nb
3.3 获取所有属性
//拿到所有字段 Field[] fields = c.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); //拿到所有字段名、值 System.out.println(field.getName() + ":" + field.get(student)); }
打印结果:
sid:s110 sname:nb age:16
3.4获取修饰符
Class类的
getModifiers()
方法可以返回一个int类型的修饰符,这个修饰符是由多个常量值通过位运算组合而成的。获取修饰符可以帮助我们更方便地了解一个类的特性,从而更好地理解并使用该类。
//拿到所有字段 Field[] fields = c.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); int modifiers = field.getModifiers(); if (Modifier.isPublic(modifiers)) { System.out.println("public:"+field.getName()); System.out.println("public修饰符的常量值:"+modifiers); } if (Modifier.isPrivate(modifiers)) { System.out.println("private:"+field.getName()); System.out.println("private修饰符的常量值:"+modifiers); } }
打印结果:
private:sid private修饰符的常量值:2 private:sname private修饰符的常量值:2 public:age public修饰符的常量值:1
四、反射方法
4.1 获取公有无参方法
// 一切反射从类类开始 Class<Student> c = Student.class; // 创建类实例 Student stu = (Student) c.newInstance(); //获取无参方法 hello是方法名 Method m = c.getMethod("hello"); //调用方法 Object invoke = m.invoke(stu); System.out.println(invoke);
打印结果:
加载进jvm中! 调用无参构造方法创建了一个学生对象 你好!我是null null
4.2 获取公有一参方法
//获取有参方法 hello是方法名 String.class是参数类型 Method m2 = c.getMethod("hello", String.class); //调用方法 stu是类实例 "nb"是要传的参数 Object invoke2 = m2.invoke(stu, "nb"); System.out.println(invoke2);
打印结果:
nb你好!我是null null
4.3 获取私有多参方法
跟调用私有属性一样,必须打开访问权限
setAccessible
//获取私有多参方法 add是方法名 Integer.class是参数类型 Method m3 = c.getDeclaredMethod("add", Integer.class, Integer.class); // 打开访问权限 m3.setAccessible(true); //调用方法 stu是类实例 22、33是要传的参数 Object invoke3 = m3.invoke(stu, 22, 33); System.out.println(invoke3);
打印结果:
55
五、反射构造函数
5.1获取无参
//一切反射从类类开始 Class c = Class.forName("com.xqx.reflect.Student"); //创建类实例 无参实例化 Student stu = (Student) c.newInstance(); System.out.println(stu);
打印结果:
加载进jvm中! 调用无参构造方法创建了一个学生对象 com.xqx.reflect.Student@33909752
5.2 获取公有有参
//获取有参构造 Constructor c1 =c.getConstructor(String.class); Student stu2 = (Student)c1.newInstance("s001"); System.out.println(stu2);
打印结果:
调用带一个参数的构造方法创建了一个学生对象 com.xqx.reflect.Student@55f96302
5.3获取私有多参
//私有 Constructor c3 =c.getDeclaredConstructor(Integer.class,String.class); //打开访问权限 c3.setAccessible(true); Student stu4 = (Student)c3.newInstance(120,"xqx"); System.out.println(stu4);
打印结果:
调用Student类私且多参的构造方法创建一个学生对象 com.xqx.reflect.Student@42a57993
六、总结
反射机制的基本步骤如下:
(1)获取Class对象。可以通过多种方式来获取Class对象,比如Class.forName()、Object.getClass()以及类字面常量等。
(2)创建实例对象。获取Class对象后,可以使用Class.newInstance()方法来创建类实例对象。
(3)获取或设置对象的属性。通过Field类来获取对象的属性,可以获取私有属性,也可以设置属性的值。
(4)调用对象的方法。通过Method类来获取对象的方法,可以获取私有方法,也可以调用方法。
(5)调用对象的构造函数。通过Constructor类来调用对象的构造函数。
好啦,今天的分享就到此为止!希望你看完本篇文章有所收获,祝你变得更强、未来出“粽”!!!