共计 8536 个字符,预计需要花费 22 分钟才能阅读完成。
-
啥是反射 1. 初识反射刚开始学反射的时候,我是一脸懵逼的,这玩意真的是“形象的妈妈给形象开门 - 形象到家了。”为什么创建对象要先获取 Class 对象?这不多此一举吗?我间接 new 一下不是更简略吗?什么是程序运行时获取类的属性和办法?平时都是程序编译出错了再批改代码,我为什么要思考程序运行时的状态?我平时开发也用不到,学这玩意有啥用?起初学了注解、spring、SpringMVC 等技术之后,发现反射无处不在。2.JVM 加载类咱们写的 java 程序要放到 JVM 中运行,所以要学习反射,首先须要理解 JVM 加载类的过程。
1. 咱们写的 .java 文件叫做源代码。2. 咱们在一个类中写了一个 main 办法,而后点击 IDEA 的 run 按钮,JVM 运行时会触发 jdk 的 javac 指令将源代码编译成 .class 文件,这个文件又叫做字节码文件。
3.JVM 的类加载器(你能够了解成一个工具)通过一个类的全限定名来获取该类的二进制字节流,而后将该 class 文件加载到 JVM 的办法区中。4. 类加载器加载一个 .class 文件到办法区的同时会在堆中生成一个惟一的 Class 对象,这个 Class 蕴含这个类的成员变量、构造方法以及成员办法。5. 这个 Class 对象会创立与该类对应的对象实例。所以外表上你 new 了一个对象,实际上当 JVM 运行程序的时候,真正帮你创建对象的是该类的 Class 对象。也就是说反射其实就是 JVM 在运行程序的时候将你创立的所有类都封装成惟一一个 Class 对象。这个 Class 对象蕴含属性、构造方法和成员办法。你拿到了 Class 对象,也就能获取这三个货色。你拿到了反射之后(Class)的属性,就能获取对象的属性名、属性类别、属性值,也能给属性设置值。你拿到了反射之后(Class)的构造方法,就能创建对象。你拿到了反射之后(Class)的成员办法,就能执行该办法。3. 反射的概念 JAVA 反射机制是在程序运行状态中,对于任意一个类,都可能晓得这个类的所有属性和办法;对于任意一个对象,都可能调用它的任意办法和属性;这种动静获取信息以及动静调用对象办法的性能称为 java 语言的反射机制。晓得了 JVM 加载类的过程,置信你应该更加深刻的理解了反射的概念。反射:JVM 运行程序 –> .java 文件 –> .class 文件 –> Class 对象 –> 创建对象实例并操作该实例的属性和办法接下来我就讲一下反射中的相干类以及罕用办法。2. Class 对象获取 Class 对象先建一个 User 类:public class User {
private String name = “ 知否君 ”;
public String sex = “ 男 ”;public User() {
}public User(String name, String sex) {
this.name = name; this.sex = sex;
}
public void eat(){
System.out.println("人要吃饭!");
}
private void run(){
System.out.println("人要跑步!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
}
复制代码获取 Class 对象的三种形式:1. Class.forName(“ 全类名 ”)全类名:包名 + 类名 Class userClass = Class.forName(“com.xxl.model.User”);
复制代码 2. 类名.classClass userClass = User.class;
复制代码 3. 对象.getClass()User user = new User();
Class userClass = user.getClass();
复制代码只管有三种形式获取 Class 对象,然而咱们个别采纳上述第一种形式。拿到 Class 对象之后,咱们就能够操作与它相干的办法了。3. 获取类名 1. 获取残缺类名:包名 + 类名 getName()Class userClass = Class.forName(“com.xxl.model.User”);
String name = userClass.getName();
System.out.println(name);
复制代码打印后果:2. 获取简略类名:不包含包名 getSimpleName()Class userClass = Class.forName(“com.xxl.model.User”);
String simpleName = userClass.getSimpleName();
System.out.println(simpleName);
复制代码打印后果: - 属性 4.1 获取属性 1. 获取所有私有属性:public 润饰 getFields()Class userClass = Class.forName(“com.xxl.model.User”);
Field[] fields = userClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
复制代码打印后果:2. 获取单个私有属性 getField(“ 属性名 ”)Class userClass = Class.forName(“com.xxl.model.User”);
Field field = userClass.getField(“sex”);
System.out.println(field);
复制代码打印后果:3. 获取所有属性:私有 + 公有 getDeclaredFields()Class userClass = Class.forName(“com.xxl.model.User”);
Field[] fields = userClass.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
复制代码打印后果:4. 获取单个属性:私有或者公有 getDeclaredField(“ 属性名 ”)Class userClass = Class.forName(“com.xxl.model.User”);
Field nameField = userClass.getDeclaredField(“name”);
Field sexField = userClass.getDeclaredField(“sex”);
System.out.println(nameField);
System.out.println(sexField);
复制代码打印后果:4.2 操作属性 1. 获取属性名称 getName()Class userClass = Class.forName(“com.xxl.model.User”);
Field nameField = userClass.getDeclaredField(“name”);
System.out.println(nameField.getName());
复制代码打印后果:2. 获取属性类型 getType()Class userClass = Class.forName(“com.xxl.model.User”);
Field nameField = userClass.getDeclaredField(“name”);
System.out.println(nameField.getType());
复制代码打印后果:3. 获取属性值 get(object)Class userClass = Class.forName(“com.xxl.model.User”);
Field nameField = userClass.getDeclaredField(“sex”);
User user = new User();
System.out.println(nameField.get(user));
复制代码打印后果:注:通过反射不能间接获取公有属性的值,然而能够通过批改拜访入口来获取公有属性的值。设置容许拜访公有属性:field.setAccessible(true);
复制代码例如:Class userClass = Class.forName(“com.xxl.model.User”);
Field nameField = userClass.getDeclaredField(“name”);
nameField.setAccessible(true);
User user = new User();
System.out.println(nameField.get(user));
复制代码打印办法:4. 设置属性值 set(object,” 属性值 ”)Class userClass = Class.forName(“com.xxl.model.User”);
Field nameField = userClass.getDeclaredField(“name”);
nameField.setAccessible(true);
User user = new User();
nameField.set(user,” 张无忌 ”);
System.out.println(nameField.get(user));
复制代码打印后果: - 构造方法 1. 获取所有私有构造方法 getConstructors()Class userClass = Class.forName(“com.xxl.model.User”);
Constructor[] constructors = userClass.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
复制代码打印后果:2. 获取与参数类型匹配的构造方法 getConstructor(参数类型)Class userClass = Class.forName(“com.xxl.model.User”);
Constructor constructor = userClass.getConstructor(String.class, String.class);
System.out.println(constructor);
复制代码打印后果: - 成员办法 6.1 获取成员办法 1. 获取所有公共办法 getMethods()Class userClass = Class.forName(“com.xxl.model.User”);
Method[] methods = userClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
复制代码打印后果:咱们发现,打印后果除了自定义的公共办法,还有继承自 Object 类的公共办法。2. 获取某个公共办法 getMethod(“ 办法名 ”, 参数类型)Class userClass = Class.forName(“com.xxl.model.User”);
Method method = userClass.getMethod(“setName”, String.class);
System.out.println(method);
复制代码打印后果:3. 获取所有办法:私有 + 公有 getDeclaredMethods()Class userClass = Class.forName(“com.xxl.model.User”);
Method[] declaredMethods = userClass.getDeclaredMethods();
for (Method method : declaredMethods) {
System.out.println(method);
}
复制代码打印后果:4. 获取某个办法:私有或者公有 getDeclaredMethod(“ 办法名 ”, 参数类型)Class userClass = Class.forName(“com.xxl.model.User”);
Method method = userClass.getDeclaredMethod(“run”);
System.out.println(method);
复制代码打印后果:6.2 执行成员办法 invoke(object,” 办法参数 ”)Class userClass = Class.forName(“com.xxl.model.User”);
Method method = userClass.getDeclaredMethod(“eat”);
User user = new User();
method.invoke(user);
复制代码打印后果:注:通过反射不能间接执行公有成员办法,然而能够设置容许拜访。设置容许执行公有办法:method.setAccessible(true);
复制代码 7. 注解 1. 判断类上或者办法上时候蕴含某个注解 isAnnotationPresent(注解名.class)
复制代码例如:Class userClass = Class.forName(“com.xxl.model.User”);
if(userClass.isAnnotationPresent(Component.class)){
Component annotation = (Component)userClass.getAnnotation(Component.class);
String value = annotation.value();
System.out.println(value);
};
复制代码 2. 获取注解 getAnnotation(注解名.class)
复制代码例如:Class userClass = Class.forName(“com.xxl.model.User”);
// 获取类上的注解
Annotation annotation1 = userClass.getAnnotation(Component.class);
Method method = userClass.getMethod(“eat”);
// 获取办法上的某个注解
Annotation annotation2 = userClass.getAnnotation(Component.class);
复制代码 8. 创立类的实例 1. 通过 Class 实例化对象 Class.newInstance()Class userClass = Class.forName(“com.xxl.model.User”);
User user = (User)userClass.newInstance();
System.out.println(“ 姓名:”+user.getName()+” 性别:”+user.getSex());
复制代码打印后果:2. 通过构造方法实例化对象 constructor.newInstance(参数值)Class userClass = Class.forName(“com.xxl.model.User”);
Constructor constructor = userClass.getConstructor(String.class, String.class);
User user = (User)constructor.newInstance(“ 李诗情 ”, “ 女 ”);
System.out.println(“ 姓名:”+user.getName()+” 性别:”+user.getSex());
复制代码打印后果: -
反射案例有一天技术总监对张三说:” 张三,据说你最近学反射了呀。那你设计一个对象的工厂类给我看看。” 张三心想:” 哟,快过年了,领导这是要给我涨工资啊。这次我肯定好好体现一次。”5 分钟过后,张三提交了代码:public class ObjectFactory {
public static User getUser() {
User user = null; try {Class userClass = Class.forName("com.xxl.model.User"); user = (User) userClass.newInstance();} catch (ClassNotFoundException e) {e.printStackTrace(); } catch (IllegalAccessException e) {e.printStackTrace(); } catch (InstantiationException e) {e.printStackTrace(); } return user;
}
public static UserService getUserService() {
UserService userService = null; try {Class userClass = Class.forName("com.xxl.service.impl.UserServiceImpl"); userService = (UserService) userClass.newInstance();} catch (ClassNotFoundException e) {e.printStackTrace(); } catch (IllegalAccessException e) {e.printStackTrace(); } catch (InstantiationException e) {e.printStackTrace(); } return userService;
}
}
复制代码技术总监瞄了一眼代码,对张三说:” 你这个工厂类存在两个问题。”1. 代码存在大量冗余。如果有一万个类,你是不是要写一万个静态方法?2. 代码耦合度太高。如果这些类寄存的包门路产生扭转,你再用 forName()获取 Class 对象是不是就会有问题?你还要一个个手动改代码,而后再编译、打包、部署。。你不感觉麻烦吗?“发散你的思维想一下,能不能只设计一个动态类,通过传参的形式用反射创建对象,传递的参数要升高和工厂类的耦合度。顺便揭示你一下,能够参考一下 JDBC 获取数据库连贯参数的形式。”张三一听:” 不愧是总监啊,醍醐灌顶啊!等我 10 分钟。”10 分钟后,张三再次提交了代码:object.propertiesuser=com.xxl.model.User
userService=com.xxl.service.impl.UserServiceImpl
复制代码 ObjectFactorypublic class ObjectFactory {
private static Properties objectProperty = new Properties();
// 静态方法在类初始化时执行,且只执行一次
static{
try {InputStream inputStream = ObjectFactory.class.getResourceAsStream("/object.properties");
objectProperty.load(inputStream);
inputStream.close();} catch (IOException e) {e.printStackTrace();
}
}
public static Object getObject(String key){
Object object = null;
try {Class objectClass = Class.forName(objectProperty.getProperty(key));
object = objectClass.newInstance();} catch (Exception e) {e.printStackTrace();
}
return object;
}
}
复制代码测试方法:@Test
void testObject() {
User user = (User)ObjectFactory.getObject("user");
UserService userService = (UserService)ObjectFactory.getObject("userService");
System.out.println(user);
System.out.println(userService);
}
复制代码执行后果:
总监看后连连拍板,笑着对张三说:“用 properties 文件寄存类的全限定名升高了代码的耦合度,通过传参的形式应用反射创建对象又升高了代码的冗余性,这次改的能够。”” 好啦,今晚我的项目要上线,先吃饭去吧,一会还要改 bug。”张三:”………. 好的总监。”10. 反射的作用咱们或多或少都据说过设计框架的时候会用到反射,例如 Spring 的 IOC 就用到了工厂模式和反射来创建对象,BeanUtils 的底层也是应用反射来拷贝属性。所以反射无处不在。只管咱们日常开发简直用不到反射,然而咱们必须要搞懂反射的原理,因为它能帮咱们了解框架设计的原理。