日照营销型网站建设seo优化包括哪些
第十章 反射机制
前言
2024年最新版零基础Java笔记,最全最详细笔记,适用于零基础小白、软件设计师、就业、提升等,笔记会持续更新,共同进步,希望可以帮助更多的小伙伴!
感谢杜老师的超级无敌详细讲解,感谢动力节点,真的受益匪浅!第三次看老杜Java了,每次都有不一样的收获!由于老杜最新版Java没有md笔记,因此鄙人把所学所写的更新于此,共同进步,希望对大家有帮助!
笔记根据老杜《动力节点》视频进行编写,视频地址:动力节点Java零基础视频(下)
笔记有写的不好的地方,恳请在评论区指正批评,谢谢!
反射目录
- 第十章 反射机制
- 前言
- 10.1 反射机制概述
- 10.2 获取Class
- 10.3 反射作用的体现
- 10.4 反射Field
- 10.5 反射Method
- 10.6 反射Constructor
- 10.7 模拟框架的部分实现
- 10.8 类加载及双亲委派机制
- 10.8.1 类加载过程
- 10.8.2 获取Class的四种方式
- 10.8.3 类加载器
- 10.8.4 双亲委派机制
- 10.9 反射泛型(了解)
10.1 反射机制概述
- 反射机制是JDK中的一套类库,这套类库可以帮助我们操作/读写class字节码文件。
- Java反射机制是指在运行时动态获取类的信息或动态调用对象的方法、修改属性等操作。
- 主要核心就是Class类、Constructor类、Field类、Method类等API。
- 反射机制主要应用于框架开发、动态代理、ORM框架、JDBC驱动等方面。
- 通过反射机制,程序员能够获得在编译期间不被知晓的类、属性、方法等信息。但是反射机制的性能较低,常常被认为是一种牺牲性能换取灵活性的实现方式。
- Java反射机制核心包:
java.lang.reflect.*
- Java反射机制核心类:
java.lang.Class
:Class类型的实例代表硬盘上的某个class文件。或者说代表某一种类型。java.lang.reflect.Field
:Filed类型的实例代表类中的属性/字段java.lang.reflect.Method
:类型的实例代表类中的方法java.lang.reflect.Constructor
:Constructor类型的实例代表类中的构造方法java.lang.reflect.Modifier
10.2 获取Class
Java中获取Class对象有以下三种方式:
- 调用Object类的getClass()方法
- 可以通过对象的getClass()方法来获取Class对象,例如:
Object obj = new Object(); Class clazz = obj.getClass();
- 注意:这个方法是通过引用去调用的。
- 使用“
类.class
”语法
可以使用“类.class”语法来获取Class对象,例如:
Class clazz = Object.class;
- 使用Class类的forName()方法
- 可以使用Class类的forName()方法来获取Class对象,例如:
Class clazz = Class.forName("java.lang.Object(完整的全限定类名)");
- 注意:
- 全限定类名是带有包名的
- 是lang包下的,
java.lang
也不能省略 - 这个是字符串参数
- 如果这个类根本不存在,运行时会报异常:
java.lang.ClassNotFoundException
- 这个方法的执行会导致类的加载动作的发生
10.3 反射作用的体现
-
通过IO流读取属性配置文件,获取类名,再通过反射机制实例化对象。(获取到Class类型的实例之后,可以实例化对象,通过反射机制实例化对象。)
- 使用
newInstance
方法实例化对象,底层实现原理:调用User类的无参数构造方法完成了对象的实例化。- 执行顺序:静态代码块→无参构造→main方法
- 要使用此方法,必须保证这个类中是存在无参构造方法,如果没有无参数构造方法,则会出现
NoSuchMethodException
public class ReflectTest{public static void main(String[]args) throws Exception{Class userClass = Class.forName("com.xfanny.javase.reflect");User user = (User)userClass.newInstance();System.out.println(user);} }
- 使用
-
在属性配置文件中配置类名:classInfo.properties
className=java.util.Date
/*** 读取属性配置文件,获取类名,通过反射机制实例化对象。* 通过这个案例的演示就知道反射机制是灵活的。这个程序可以做到对象的动态创建。* 只要修改属性配置文件就可以完成不同对象的实例化。*/ public class ReflectTest03 {public static void main(String[] args) throws Exception {// 资源绑定器ResourceBundle bundle = ResourceBundle.getBundle("com.xfanny.javase.reflect.classInfo");// 通过key获取valueString className = bundle.getString("className");// 通过反射机制实例化对象Class classObj = Class.forName(className);// 实例化Object obj = classObj.newInstance();System.out.println(obj);} }
config.properties
className=com.xfanny.javase.reflect.UserService methodName=concat parameterTypes=java.lang.String,java.lang.String,java.lang.String parameterValues=aaa,bbb,ccc
如果要创建其他类的实例对象,只需要修改classInfo.properties配置文件即可。
这说明反射机制可以让程序变的更加灵活。在进行系统扩展时,可以达到OCP开闭原则。
10.4 反射Field
public class ReflectTest07 {public static void main(String[] args) throws Exception{// 如果不使用反射机制,怎么访问对象的属性?Customer customer = new Customer();// 修改属性的值(set动作)// customer要素一// name要素二// "张三"要素三customer.name = "张三";// 读取属性的值(get动作)// 读取哪个对象的哪个属性System.out.println(customer.name);// 如果使用反射机制,怎么访问对象的属性?// 获取类Class clazz = Class.forName("com.xfanny.javase.reflect.Customer");// 获取对应的FieldField ageField = clazz.getDeclaredField("age");// 调用方法打破封装ageField.setAccessible(true);// 修改属性的值// 给对象属性赋值三要素:给哪个对象 的 哪个属性 赋什么值ageField.set(customer, 30);// 读取属性的值System.out.println("年龄:" + ageField.get(customer));// 通过反射机制给name属性赋值,和读取name属性的值Field nameField = clazz.getDeclaredField("name");// 修改属性name的值nameField.set(customer, "李四");// 读取属性name的值System.out.println(nameField.get(customer));}
}
在属性配置文件中配置类名:classInfo.properties
className=java.util.Date
通过IO流读取属性配置文件,获取类名,再通过反射机制实例化对象。
如果要创建其他类的实例对象,只需要修改classInfo.properties配置文件即可。
这说明反射机制可以让程序变的更加灵活。在进行系统扩展时,可以达到OCP开闭原则。
10.5 反射Method
反射Method包括两方面:
-
一方面:通过反射机制获取Method
Method method = clazz.getDeclaredMethod("methodName", paramTypes);
-
另一方面:通过Method调用方法
Class clazz = MyClass.class;
Method method = clazz.getDeclaredMethod(“methodName”, paramTypes);
Object[] args = {arg1, arg2, arg3};
Object result = method.invoke(myObject, args);
```
代码:
public class ReflectTest10 {public static void main(String[] args) throws Exception {// 不使用反射机制怎么调用方法?// 创建对象UserService userService = new UserService();// 调用方法// 分析:调用一个方法需要几个要素?四要素// 调用哪个对象的哪个方法,传什么参数,返回什么值boolean isSuccess = userService.login("admin", "123456");System.out.println(isSuccess ? "登录成功" : "登录失败");// 调用方法userService.logout();// 通过反射机制调用login方法// 获取ClassClass clazz = Class.forName("com.xfanny.javase.reflect.UserService");// 获取login方法Method loginMethod = clazz.getDeclaredMethod("login", String.class, String.class);// 调用login方法Object retValue = loginMethod.invoke(userService, "admin", "123456");System.out.println(retValue);// 调用logout方法Method logoutMethod = clazz.getDeclaredMethod("logout");logoutMethod.invoke(userService);}
}
10.6 反射Constructor
反射Constructor包括两方面:
- 一方面:通过反射机制获取Constructor
Constructor constructor2 = clazz.getDeclaredConstructor(paramTypes);
- 另一方面:通过Constructor创建对象
Class clazz = MyClass.class; Constructor constructor = clazz.getDeclaredConstructor(paramTypes); Object[] args = {arg1, arg2, arg3}; Object myObject = constructor.newInstance(args);
10.7 模拟框架的部分实现
- 配置文件中配置如下信息:classInfo.properties
className=com.xfanny.javase.reflect.UserService methodName=login parameterTypes=java.lang.String,java.lang.String parameterValues=admin,123456
- 通过反射机制创建对象,调用配置的方法
- 本案例的实现类似于Spring框架中部分实现,主要是通过修改配置文件来达到创建不同的对象,调用不同的方法。
public class ReflectTest13 {public static void main(String[] args) throws Exception {// 读取属性配置文件ResourceBundle bundle = ResourceBundle.getBundle("com.xfanny.javase.reflect.config");String className = bundle.getString("className");String methodName = bundle.getString("methodName");String parameterTypes = bundle.getString("parameterTypes");String parameterValues = bundle.getString("parameterValues");// 通过反射机制调用方法// 创建对象(依赖无参数构造方法)Class<?> clazz = Class.forName(className);Constructor<?> defaultCon = clazz.getDeclaredConstructor();Object obj = defaultCon.newInstance();// 获取方法// java.lang.String,java.lang.StringString[] strParameterTypes = parameterTypes.split(",");Class[] classParameterTypes = new Class[strParameterTypes.length];for (int i = 0; i < strParameterTypes.length; i++) {classParameterTypes[i] = Class.forName(strParameterTypes[i]);}Method method = clazz.getDeclaredMethod(methodName, classParameterTypes);// 调用方法// parameterValues=admin,123456Object retValue = method.invoke(obj, parameterValues.split(","));System.out.println(retValue);}
}
10.8 类加载及双亲委派机制
10.8.1 类加载过程
- 装载(loading)
- 类加载器负责将类的class文件读入内存,并创建一个java.lang.Class对象
- 连接(linking)
- 验证(Verify)
- 确保加载类的信息符合JVM规范。
- 准备(Prepare)
- 正式为静态变量在方法区中开辟存储空间并设置默认值
- public static int k = 10; 此时:k会赋值0
- public static final int f = 10; 此时: f会赋值10
- 解析(Resolve)
- 将虚拟机常量池内的符号引用替换为直接引用(地址)的过程。
- 验证(Verify)
- 初始化(initialization)
- 静态变量赋值,静态代码块执行
低版本的JDK中类加载器的名字:
启动类加载器:负责加载rt.jar
扩展类加载器:ext/*.jar
系统类加载器:classpath
10.8.2 获取Class的四种方式
- 静态方法
Class clazz = Class.forName(“全限定类名”)
- 实例方法
Class clazz = 引用.getClass();
- class属性
Class clazz = 类型名.class;
- 通过类加载器获取
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
Class clazz = classLoader.loadClass(“全限定类名”);
```
Class.forName和classLoader.loadClass()的区别?
- Class.forName():类加载时会进行初始化。
- classLoader.loadClass():类加载时不会进行初始化,直到第一次使用该类。
10.8.3 类加载器
- 虚拟机内部提供了三种类加载器(Java9+):
- 启动类加载器(BootstrapClassLoader):加载Java最核心的类,例如String
- 平台类加载器(PlatformClassLoader):加载Java平台扩展的类库,例如解析XML的
- 应用类加载器(AppClassLoader):加载classpath中的
- 同时我们还可以自定义一个类加载器(UserClassLoader)
- 获取类加载器可以通过
getParent()
方法一级一级获取
10.8.4 双亲委派机制
- 某个类加载器接收到加载类的任务时,通常委托给“父 类加载”完成加载。
- 最“父 类加载器”无法加载时,一级一级向下委托加载任务。
- 作用:
- 保护程序的安全。
- 防止类加载重复。
10.9 反射泛型(了解)
- 反射父类的泛型
- 反射接口的泛型
- 反射属性上的泛型
- 反射方法参数上的泛型
- 反射方法返回值的泛型
- 反射构造方法参数上的泛型