一、
1. 反射的概述(熟悉)
-
Java给我们提供了一套API,使用这套API我们可以在运行时动态的获取指定对象所属的类,创建运行时类的对象,调用指定的结构(属性、方法)等。
-
API:
-
java.lang.Class
:代表一个类 -
java.lang.reflect.Method:代表类的方法
-
java.lang.reflect.Field:代表类的成员变量
-
java.lang.reflect.Constructor:代表类的构造器
-
… …
-
-
反射的优点和缺点
-
优点:
-
提高了Java程序的灵活性和扩展性,
降低了耦合性
,提高自适应
能力 -
允许程序创建和控制任何类的对象,无需提前
硬编码
目标类
-
-
缺点:
-
反射的
性能较低
。-
反射机制主要应用在对灵活性和扩展性要求很高的系统框架上
-
-
反射会
模糊
程序内部逻辑,可读性较差
。
-
-
-
反射,平时开发中,我们使用并不多。主要是在框架的底层使用。
2. Class:反射的源头
-
Class的理解 (掌握)
针对于编写好的.java源文件进行编译(使用javac.exe),会生成一个或多个.class字节码文件。接着,我们使用 java.exe命令对指定的.class文件进行解释运行。这个解释运行的过程中,我们需要将.class字节码文件加载(使用类的加载器)到内存中(存放在方法区)。加载到内存中的.class文件对应的结构即为Class的一个实例。
-
获取Class的实例的几种方式(前三种)
-
类.class
-
对象.getClass()
-
(使用较多)Class调用静态方法forName(String className)
-
(了解)使用ClassLoader的方法loadClass(String className)
-
-
Class 可以指向哪些结构。
简言之,所有Java类型! (1)class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类 (2)interface:接口 (3)[]:数组 (4)enum:枚举 (5)annotation:注解@interface (6)primitive type:基本数据类型 (7)void
3. 类的加载过程、类的加载器(理解)
-
类的加载过程
过程1:类的装载(loading) 将类的class文件读入内存,并为之创建一个java.lang.Class对象。此过程由类加载器完成 过程2:链接(linking) > 验证(Verify):确保加载的类信息符合JVM规范,例如:以cafebabe开头,没有安全方面的问题。 > 准备(Prepare):正式为类变量(static)分配内存并设置类变量默认初始值的阶段,这些内存都将在方法区中进行分配。 > 解析(Resolve):虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。 过程3:初始化(initialization) 执行类构造器<clinit>()方法的过程。 类构造器<clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。
-
类的加载器
5.1 作用:负责类的加载,并对应于一个Class的实例。 5.2 分类(分为两种): > BootstrapClassLoader:引导类加载器、启动类加载器 > 使用C/C++语言编写的,不能通过Java代码获取其实例 > 负责加载Java的核心库(JAVA_HOME/jre/lib/rt.jar或sun.boot.class.path路径下的内容) > 继承于ClassLoader的类加载器 > ExtensionClassLoader:扩展类加载器 > 负责加载从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下加载类库 > SystemClassLoader/ApplicationClassLoader:系统类加载器、应用程序类加载器 > 我们自定义的类,默认使用的类的加载器。 > 用户自定义类的加载器 > 实现应用的隔离(同一个类在一个应用程序中可以加载多份);数据的加密。
4. 反射的应用1:创建运行时类的对象(重点)
Class clazz = Person.class; //创建Person类的实例 Person per = (Person) clazz.newInstance(); System.out.println(per);
要想创建对象成功,需要满足: 条件1:要求运行时类中必须提供一个空参的构造器 条件2:要求提供的空参的构造器的权限要足够。
5. 反射的应用2:获取运行时类所有的结构
(了解)获取运行时类的内部结构1:所有属性、所有方法、所有构造器 (熟悉)获取运行时类的内部结构2:父类、接口们、包、带泛型的父类、父类的泛型等
6. 反射的应用3:调用指定的结构(重点)
3.1 调用指定的属性(步骤) 步骤1.通过Class实例调用getDeclaredField(String fieldName),获取运行时类指定名的属性 步骤2. setAccessible(true):确保此属性是可以访问的 步骤3. 通过Filed类的实例调用get(Object obj) (获取的操作) 或 set(Object obj,Object value) (设置的操作)进行操作。 3.2 调用指定的方法(步骤) 步骤1.通过Class的实例调用getDeclaredMethod(String methodName,Class ... args),获取指定的方法 步骤2. setAccessible(true):确保此方法是可访问的 步骤3.通过Method实例调用invoke(Object obj,Object ... objs),即为对Method对应的方法的调用。 invoke()的返回值即为Method对应的方法的返回值 特别的:如果Method对应的方法的返回值类型为void,则invoke()返回值为null 3.3 调用指定的构造器(步骤) 步骤1.通过Class的实例调用getDeclaredConstructor(Class ... args),获取指定参数类型的构造器 步骤2.setAccessible(true):确保此构造器是可以访问的 步骤3.通过Constructor实例调用newInstance(Object ... objs),返回一个运行时类的实例。
7. 反射的应用4:注解的使用(了解)
略
8. 体会:反射的动态性
public class ReflectTest { //体会:静态性 public Person getInstance(){ return new Person(); } //体会:反射的动态性 //举例1: public <T> T getInstance(String className) throws Exception { Class clazz = Class.forName(className); Constructor con = clazz.getDeclaredConstructor(); con.setAccessible(true); return (T) con.newInstance(); } @Test public void test1() throws Exception { Person p1 = getInstance(); System.out.println(p1); String className = "com.atguigu04.other.dynamic.Person"; Person per1 = getInstance(className); System.out.println(per1); String className1 = "java.util.Date"; Date date1 = getInstance(className1); System.out.println(date1); } //体会:反射的动态性 //举例2: public Object invoke(String className,String methodName) throws Exception { //1. 创建全类名对应的运行时类的对象 Class clazz = Class.forName(className); Constructor con = clazz.getDeclaredConstructor(); con.setAccessible(true); Object obj = con.newInstance(); //2. 获取运行时类中指定的方法,并调用 Method method = clazz.getDeclaredMethod(methodName); method.setAccessible(true); return method.invoke(obj); } @Test public void test2() throws Exception { String className = "com.atguigu04.other.dynamic.Person"; String methodName = "show"; Object returnValue = invoke(className,methodName); System.out.println(returnValue); } }
二、需要理解的问题
2.1 反射概述
1. 对反射了解吗?反射有什么好处?为什么需要反射?
类似问题: > Java反射的作用是什么? > Java反射机制的作用有什么? > 反射的具体用途?
2. 反射的使用场合和作用、及其优缺点
类似问题: > 反射机制的优缺点 > Java反射你怎么用的?
3. 实现Java反射的类有什么?
类似问题: > Java反射 API 有几类?
问API。
4. 反射是怎么实现的?
从Class说起。
2.2 Class的理解
1. Class类的作用?生成Class对象的方法有哪些?
反射的源头。 主要有三种。
2. Class.forName("全路径") 会调用哪些方法 ? 会调用构造方法吗?加载的类会放在哪?
Class.forName() 会执行执行类构造器<clinit>()方法。
不会调用构造方法
加载的类放在方法区。
2.3 创建对象
1. 说一下创建对象的几种方法?
类似问题: > 除了使用new创建对象之外,还可以用什么方法创建对象?
2. 如何找到对象实际类的?
对象.getClass();
Object obj = new Date(); obj.getClass();// 获取到的是Date。
3. Java反射创建对象效率高还是通过new创建对象的效率高?
new 的方式。
2.4 调用属性、方法
1. 如何利用反射机制来访问一个类的方法?
调用指定的方法(步骤) 步骤1.通过Class的实例调用getDeclaredMethod(String methodName,Class ... args),获取指定的方法 步骤2. setAccessible(true):确保此方法是可访问的 步骤3.通过Method实例调用invoke(Object obj,Object ... objs),即为对Method对应的方法的调用。 invoke()的返回值即为Method对应的方法的返回值 特别的:如果Method对应的方法的返回值类型为void,则invoke()返回值为null
2. 说一下Java反射获取私有属性,如何改变值?
调用指定的属性(步骤) 步骤1.通过Class实例调用getDeclaredField(String fieldName),获取运行时类指定名的属性 步骤2. setAccessible(true):确保此属性是可以访问的 步骤3. 通过Filed类的实例调用get(Object obj) (获取的操作) 或 set(Object obj,Object value) (设置的操作)进行操作。
针对于核心源码的api,内部的私有的结构在jdk17中就不可以通过反射调用了。
反射机制拓展练习
关于Class
1、获取Class对象
使用4种方式获取java.time.LocalDate类的Class对象,并打印输出。
package com.atguigu.exercise1; import java.time.LocalDate; public class Exercise1 { public static void main(String[] args){ try { Class c1 = LocalDate.class; Class c2 = LocalDate.now().getClass(); Class c3 = Class.forName("java.time.LocalDate"); Class c4 = ClassLoader.getSystemClassLoader().loadClass("java.time.LocalDate"); System.out.println("c1 = " + c1); System.out.println("c2 = " + c2); System.out.println("c3 = " + c3); System.out.println("c4 = " + c4); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
反射的应用
2、创建对象
(1)通过反射创建java.util.Date类的对象。
(2)通过反射创建java.time.LocalDate类的对象。
package com.atguigu.exercise2; import org.junit.Test; import java.lang.reflect.Constructor; public class Exercise2 { @Test public void test01() throws Exception { Class clazz = Class.forName("java.util.Date"); Object nowObj = clazz.newInstance(); System.out.println(nowObj); } @Test public void test02() throws Exception{ Class clazz = Class.forName("java.time.LocalDate"); Constructor constructor = clazz.getDeclaredConstructor(int.class, int.class, int.class); constructor.setAccessible(true); Object obj = constructor.newInstance(2022, 2, 4); System.out.println("obj = " + obj); } }
3、修改属性值
已知有如下字符串:
String str = "hello";
通过反射修改str对象的"hello"为"hallo"。
package com.atguigu.exercise3; import java.lang.reflect.Field; public class Exercise3 { public static void main(String[] args) throws Exception{ String str = "hello"; Class clazz = str.getClass(); Field valueField = clazz.getDeclaredField("value"); valueField.setAccessible(true); char[] value = (char[]) valueField.get(str); value[1] = 'a'; System.out.println("str = " + str); } }
4、调用方法
通过反射分别调用java.time.LocalDate的如下方法:
-
public static LocalDate now()
-
public LocalDate plusDays(long daysToAdd)
package com.atguigu.exercise4; import java.lang.reflect.Method; public class Exercise4 { public static void main(String[] args)throws Exception{ Class clazz = Class.forName("java.time.LocalDate"); Method nowMethod = clazz.getMethod("now"); Object obj = nowMethod.invoke(null); System.out.println("obj = " + obj); Method plusDaysMethod = clazz.getMethod("plusDays", long.class); Object future = plusDaysMethod.invoke(obj, 100); Syste.out.println("future = " + future); } }
5、获取类型信息
通过反射获取java.time.LocalDate类的详细信息。
package com.atguigu.exercise5; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; public class Exercise5 { @Test public void test() throws ClassNotFoundException { //1、先得到某个类型的Class对象 Class clazz = Class.forName("java.time.LocalDate"); //2、获取类信息 //(1)获取包对象,即所有java的包,都是Package的对象 Package pkg = clazz.getPackage(); System.out.println("包名:" + pkg.getName()); //(2)获取修饰符 int mod = clazz.getModifiers(); System.out.println("类的修饰符有:" + Modifier.toString(mod)); //(3)类型名 String name = clazz.getName(); System.out.println("类名:" + name); //(4)父类,父类也有父类对应的Class对象 Class superclass = clazz.getSuperclass(); System.out.println("父类:" + superclass); //(5)父接口们 System.out.println("父接口们:"); Class[] interfaces = clazz.getInterfaces(); for (Class iter : interfaces) { System.out.println(iter); } //(6)类的属性,你声明的一个属性,就是一个Field的对象 System.out.println("------------------------------"); System.out.println("成员如下:"); System.out.println("属性有:"); Field[] declaredFields = clazz.getDeclaredFields(); for (Field field : declaredFields) { //修饰符、数据类型、属性名 int modifiers = field.getModifiers(); System.out.println("属性的修饰符:" + Modifier.toString(modifiers)); String name2 = field.getName(); System.out.println("属性名:" + name2); Class<?> type = field.getType(); System.out.println("属性的数据类型:" + type); } System.out.println("-------------------------"); //(7)构造器们 System.out.println("构造器列表:"); Constructor[] constructors = clazz.getDeclaredConstructors(); for (int i = 0; i < constructors.length; i++) { Constructor constructor = constructors[i]; System.out.println("第" + (i + 1) + "个构造器:"); //修饰符、构造器名称、构造器形参列表 、抛出异常列表 int modifiers = constructor.getModifiers(); System.out.println("构造器的修饰符:" + Modifier.toString(modifiers)); String name2 = constructor.getName(); System.out.println("构造器名:" + name2); //形参列表 System.out.println("形参列表:"); Class[] parameterTypes = constructor.getParameterTypes(); for (Class parameterType : parameterTypes) { System.out.println(parameterType); } //异常列表 System.out.println("异常列表:"); Class<?>[] exceptionTypes = constructor.getExceptionTypes(); for (Class<?> exceptionType : exceptionTypes) { System.out.println(exceptionType); } System.out.println(); } System.out.println("---------------------------------"); //(8)方法们 System.out.println("方法列表:"); Method[] declaredMethods = clazz.getDeclaredMethods(); for (int i = 0; i < declaredMethods.length; i++) { Method method = declaredMethods[i]; System.out.println("第" + (i + 1) + "个方法:"); //修饰符、返回值类型、方法名、形参列表 、异常列表 int modifiers = method.getModifiers(); System.out.println("方法的修饰符:" + Modifier.toString(modifiers)); Class<?> returnType = method.getReturnType(); System.out.println("返回值类型:" + returnType); String name2 = method.getName(); System.out.println("方法名:" + name2); //形参列表 System.out.println("形参列表:"); Class[] parameterTypes = method.getParameterTypes(); for (Class parameterType : parameterTypes) { System.out.println(parameterType); } //异常列表 System.out.println("异常列表:"); Class<?>[] exceptionTypes = method.getExceptionTypes(); for (Class<?> exceptionType : exceptionTypes) { System.out.println(exceptionType); } System.out.println(); } } }
6、获取泛型参数
代码填空题
package com.atguigu.exercise7; import java.lang.reflect.ParameterizedType; public class Exercise7 { public static void main(String[] args) { SubA a = new SubA(); System.out.println(a.getType()); SubB b = new SubB(); System.out.println(b.getType()); } } abstract class Base<T>{ private Class type; public Base(){ //为type属性赋值为T的实际类型 _____________________________________ } public Class getType() { return type; } } class SubA extends Base<String>{ } class SubB extends Base{ }
package com.atguigu.exercise7; import java.lang.reflect.ParameterizedType; public class Exercise7 { public static void main(String[] args) { SubA a = new SubA(); System.out.println(a.getType()); SubB b = new SubB(); System.out.println(b.getType()); } } abstract class Base<T>{ private Class type; public Base(){ Class<? extends Base> clazz = this.getClass(); try { ParameterizedType pt = (ParameterizedType) clazz.getGenericSuperclass(); type = (Class) pt.getActualTypeArguments()[0]; } catch (Exception e) { type = Object.class; } } public Class getType() { return type; } } class SubA extends Base<String>{ } class SubB extends Base{ }
7、自定义注解1
(1)声明自定义注解类型MyAnnotation,
-
注解使用位置只能是方法上面
-
注解声明周期为RetentionPolicy.RUNTIME
-
注解包含抽象方法:String value();
(2)在主方法上标记MyAnnotation注解,并且为抽象方法value()指定返回值“主方法”
(3)在主方法中读取MyAnnotation注解信息,并调用value()抽象方法获取返回值,打印返回值。
package com.atguigu.exercise8; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value(); }
package com.atguigu.exercise8; import java.lang.reflect.Method; public class Exercise8 { @MyAnnotation("主方法") public static void main(String[] args) { try { Class clazz = Exercise8.class; Method mainMethod = clazz.getMethod("main", String[].class); MyAnnotation myAnnotation = mainMethod.getAnnotation(MyAnnotation.class); String value = myAnnotation.value(); System.out.println("value = " + value); } catch (NoSuchMethodException e) { e.printStackTrace(); } } }
8、自定义注解2
案例:
1、声明自定义注解@Table
(1)加上String类型的配置参数value
(2)并限定@Table的使用位置为类上
(3)并指定生命周期为“运行时”
2、声明自定义注解@Column
(1)加上String类型的配置参数name,表示表格的列名
(2)加上String类型的配置参数type,表示表格的列数据类型
(3)并限定@Column的使用位置在属性上
(4)并指定生命周期为“运行时”
3、声明User类,
(1)属性:id, username, password, email
(2)在User类上,标记@Table注解,并为value赋值为"t_user"
(3)在User类的每一个属性上标记@Column,并为name和type赋值,例如:
id:name赋值为no,type赋值为int
username:name赋值为username,type赋值为varchar(20)
password:name赋值为pwd,type赋值为char(6)
email:name赋值为email,type赋值为varchar(50)
4、在测试类Test04中,通过反射,获取User类以及每一个属性声明的注解,并获取注解值
运行效果:
package com.atguigu.exercise9; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface Table { String value(); }
package com.atguigu.Exercise9; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface Column { String name(); String type(); }
package com.atguigu.exercise9; @Table("t_user") public class User { @Column(name="no",type="int") private int id; @Column(name="username",type="varchar(20)") private String username; @Column(name="pwd",type="char(6)") private String password; @Column(name="email",type="varchar(50)") private String email; public User(int id, String username, String password, String email) { super(); this.id = id; this.username = username; this.password = password; this.email = email; } public User() { super(); } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public String toString() { return "User [id=" + id + ", username=" + username + ", password=" + password + ", email=" + email + "]"; } }
package com.atguigu.exercise9; import java.lang.reflect.Field; public class Exercise9 { @Test public void test() throws NoSuchFieldException { Class clazz = User.class; Table t = (Table) clazz.getAnnotation(Table.class); String table = t.value(); System.out.println("User类对应数据库表:" + table); Field idField = clazz.getDeclaredField("id"); Column idColumn = idField.getAnnotation(Column.class); String idName = idColumn.name(); String idType = idColumn.type(); System.out.println("id属性对应数据库表的字段:" + idName + ",类型:" + idType); Field usernameField = clazz.getDeclaredField("username"); Column usernameColumn = usernameField.getAnnotation(Column.class); String usernameName = usernameColumn.name(); String usernameType = usernameColumn.type(); System.out.println("username属性对应数据库表的字段:" + usernameName + ",类型:" + usernameType); Field passwordField = clazz.getDeclaredField("password"); Column passwordColumn = passwordField.getAnnotation(Column.class); String passwordName = passwordColumn.name(); String passwordType = passwordColumn.type(); System.out.println("password属性对应数据库表的字段:" + passwordName + ",类型:" + passwordType); Field emailField = clazz.getDeclaredField("email"); Column emailColumn = emailField.getAnnotation(Column.class); String emailName = emailColumn.name(); String emailType = emailColumn.type(); System.out.println("email属性对应数据库表的字段:" + emailName + ",类型:" + emailType); } }
综合练习
9、AtguiguDemo
案例:用反射获取某个类的信息,并用反射使用某个类
开发提示:
1、声明一个类:com.atguigu.test01.demo.AtguiguDemo,
(1)包含静态变量:学校school(显式初始化为"尚硅谷")
(2)包含属性:班级名称className
(3)并提供构造器,get/set等
(4)实现Serializable和Comparable接口(按照班级名称排序)
2、把com.atguigu.test01.demo.Exercise10class导出为一个atguigu.jar并放到D:\ProgramFiles\Java\jdk1.8.0_141\jre\lib\ext目录(注意,以你自己的JDK安装目录为准)
3、在测试类Test01的test01()测试方法中,用反射获取AtguiguDemo类的Class对象,并获取它的所有信息,包括类加载器、包名、类名、父类、父接口、属性、构造器、方法们等。
4、在测试类Test01的test02()测试方法中,用反射获取school的值,并修改school的值为“尚硅谷大学”,然后再获取school的值
5、在测试类Test01的test03()测试方法中,用反射创建AtguiguDemo类的对象,并设置班级名称className属性的值,并获取它的值
6、在测试类Test01的test04()测试方法中,用反射获取有参构造创建2个AtguiguDemo类的对象,并获取compareTo方法,调用compareTo方法,比较大小。
import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.util.Arrays; import org.junit.Test; public class Exercise10 { @Test public void test01() throws ClassNotFoundException{ Class clazz = Class.forName("com.atguigu.test01.demo.AtguiguDemo"); ClassLoader classLoader = clazz.getClassLoader(); System.out.println("类加载器:" + classLoader); Package pkg = clazz.getPackage(); System.out.println("包名:" + pkg.getName()); int cMod = clazz.getModifiers(); System.out.println("类的修饰符:" + Modifier.toString(cMod)); System.out.println("类名:" + clazz.getName()); System.out.println("父类:" + clazz.getSuperclass().getName()); Class[] interfaces = clazz.getInterfaces(); System.out.println("父接口们:"+Arrays.toString(interfaces)); Field[] declaredFields = clazz.getDeclaredFields(); for (int i =0 ;i<declaredFields.length; i++) { System.out.println("第" + (i+1) + "个字段:"); int fMod = declaredFields[i].getModifiers(); System.out.println("修饰符:" + Modifier.toString(fMod)); System.out.println("数据类型:" + declaredFields[i].getType().getName()); System.out.println("属性名:" + declaredFields[i].getName()); } Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); for (int i = 0; i < declaredConstructors.length; i++) { System.out.println("第" + (i+1) + "个构造器:"); int csMod = declaredConstructors[i].getModifiers(); System.out.println("修饰符:" + Modifier.toString(csMod)); System.out.println("构造器名:" + declaredConstructors[i].getName()); System.out.println("形参列表:" + Arrays.toString(declaredConstructors[i].getParameterTypes())); } Method[] declaredMethods = clazz.getDeclaredMethods(); for (int i = 0; i < declaredMethods.length; i++) { System.out.println("第" + (i+1) + "个成员方法:"); int csMod = declaredMethods[i].getModifiers(); System.out.println("修饰符:" + Modifier.toString(csMod)); System.out.println("返回值类型:" + declaredMethods[i].getReturnType().getName()); System.out.println("方法名:" + declaredMethods[i].getName()); System.out.println("形参列表:" + Arrays.toString(declaredMethods[i].getParameterTypes())); } } @Test public void test02() throws Exception{ Class clazz = Class.forName("com.atguigu.test01.demo.AtguiguDemo"); Field field = clazz.getDeclaredField("school"); field.setAccessible(true); Object value = field.get(null); System.out.println("school = " + value); field.set(null, "尚硅谷大学"); value = field.get(null); System.out.println("school = " + value); } @Test public void test03() throws Exception{ Class clazz = Class.forName("com.atguigu.test01.demo.AtguiguDemo"); Object object = clazz.newInstance(); Field field = clazz.getDeclaredField("className"); field.setAccessible(true); Object value = field.get(object); System.out.println("className = " + value); field.set(object, "190513班"); value = field.get(object); System.out.println("className = " + value); } @Test public void test04() throws Exception{ Class clazz = Class.forName("com.atguigu.test01.demo.AtguiguDemo"); Constructor c = clazz.getDeclaredConstructor(String.class); Object obj1 = c.newInstance("190513BJ班"); Object obj2 = c.newInstance("190325SH班"); Method m = clazz.getDeclaredMethod("compareTo", Object.class); System.out.println("obj1与obj2比较结果:" + m.invoke(obj1, obj2)); } }
package com.atguigu.exercise10; import java.io.Serializable; public class AtguiguDemo implements Serializable,Comparable<AtguiguDemo>{ private static final long serialVersionUID = 1L; private static String school = "尚硅谷"; private String className; public AtguiguDemo(String className) { super(); this.className = className; } public AtguiguDemo() { super(); } public static String getSchool() { return school; } public static void setSchool(String school) { AtguiguDemo.school = school; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } @Override public String toString() { return "AtguiguDemo [className=" + className + "]"; } @Override public int compareTo(AtguiguDemo o) { return this.className.compareTo(o.getClassName()); } }
10、AtguiguStudent
(1)声明一个尚硅谷学生类AtguiguStudent,
-
包含静态变量:学校school
-
包含属性:班级名称className
-
并提供构造器,get/set等
-
实现Serializable
-
实现Comparable<T>接口,重写int compareTo(T t)方法按照班级名称排序
(2)在测试类的test01()测试方法中,用反射获取AtguiguStudent类的Class对象,并获取它的所有信息,包括类加载器、包名、类名、父类、父接口、属性、构造器、方法们等。
(3)在测试类的test02()测试方法中,用反射设置school的值为“尚硅谷”,获取school的值
(4)在测试类的test03()测试方法中,用反射创建AtguiguStudent类的对象,并设置班级名称className属性的值,并获取它的值
(5)在测试类的test04()测试方法中,用反射获取有参构造创建2个AtguiguStudent类的对象,并获取compareTo方法,调用compareTo方法,比较大小。
import java.io.Serializable; public class AtguiguStudent implements Serializable,Comparable<AtguiguStudent>{ private static final long serialVersionUID = 1L; private static String school = "尚硅谷"; private String className; public AtguiguStudent(String className) { super(); this.className = className; } public AtguiguStudent() { super(); } public static String getSchool() { return school; } public static void setSchool(String school) { AtguiguStudent.school = school; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } @Override public String toString() { return "AtguiguDemo [className=" + className + "]"; } @Override public int compareTo(AtguiguStudent o) { return this.className.compareTo(o.getClassName()); } }
import org.junit.Test; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; public class Exercise11 { @Test public void test01() throws ClassNotFoundException{ Class clazz = Class.forName("com.atguigu.homework1.AtguiguStudent"); ClassLoader classLoader = clazz.getClassLoader(); System.out.println("类加载器:" + classLoader); Package pkg = clazz.getPackage(); System.out.println("包名:" + pkg.getName()); int cMod = clazz.getModifiers(); System.out.println("类的修饰符:" + Modifier.toString(cMod)); System.out.println("类名:" + clazz.getName()); System.out.println("父类:" + clazz.getSuperclass().getName()); Class[] interfaces = clazz.getInterfaces(); System.out.println("父接口们:"+ Arrays.toString(interfaces)); Field[] declaredFields = clazz.getDeclaredFields(); for (int i =0 ;i<declaredFields.length; i++) { System.out.println("第" + (i+1) + "个字段:"); int fMod = declaredFields[i].getModifiers(); System.out.println("修饰符:" + Modifier.toString(fMod)); System.out.println("数据类型:" + declaredFields[i].getType().getName()); System.out.println("属性名:" + declaredFields[i].getName()); } Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); for (int i = 0; i < declaredConstructors.length; i++) { System.out.println("第" + (i+1) + "个构造器:"); int csMod = declaredConstructors[i].getModifiers(); System.out.println("修饰符:" + Modifier.toString(csMod)); System.out.println("构造器名:" + declaredConstructors[i].getName()); System.out.println("形参列表:" + Arrays.toString(declaredConstructors[i].getParameterTypes())); } Method[] declaredMethods = clazz.getDeclaredMethods(); for (int i = 0; i < declaredMethods.length; i++) { System.out.println("第" + (i+1) + "个成员方法:"); int csMod = declaredMethods[i].getModifiers(); System.out.println("修饰符:" + Modifier.toString(csMod)); System.out.println("返回值类型:" + declaredMethods[i].getReturnType().getName()); System.out.println("方法名:" + declaredMethods[i].getName()); System.out.println("形参列表:" + Arrays.toString(declaredMethods[i].getParameterTypes())); } } @Test public void test02() throws Exception{ Class clazz = Class.forName("com.atguigu.homework1.AtguiguStudent"); Field field = clazz.getDeclaredField("school"); field.setAccessible(true); field.set(null, "尚硅谷"); Object value = field.get(null); System.out.println("school = " + value); } @Test public void test03() throws Exception{ Class clazz = Class.forName("com.atguigu.test01.demo.AtguiguDemo"); Object object = clazz.newInstance(); Field field = clazz.getDeclaredField("className"); field.setAccessible(true); Object value = field.get(object); System.out.println("className = " + value); field.set(object, "190513班"); value = field.get(object); System.out.println("className = " + value); } @Test public void test04() throws Exception{ Class clazz = Class.forName("com.atguigu.homework1.AtguiguStudent"); Constructor c = clazz.getDeclaredConstructor(String.class); Object obj1 = c.newInstance("20210513BJ班"); Object obj2 = c.newInstance("20210325SH班"); Method m = clazz.getDeclaredMethod("compareTo", Object.class); System.out.println("obj1与obj2比较结果:" + m.invoke(obj1, obj2)); } }
11、自定义注解
(1)声明注解类型PathAnnotation
-
使用Target元注解限定注解使用位置:ElementType.TYPE
-
使用Retention元注解限定注解的生命周期:RetentionPolicy.RUNTIME
-
声明抽象方法String value()
(2)在当前模块根目录下包含一个users.properties文件,里面包含如下内容:=左边是用户名,=右边是密码
#key=value chai=999 lin=888 yan=666 admin=123456
(3)声明抽象类MyServlet,包含抽象方法:
public abstract void service(HashMap<String,String> map)throws Exception;
(4)声明LoginServlet类,继承MyServlet类,重写service方法,遍历map,如果里面包含key为username和password的键值对,则取出对应的值,并判断它和users.properties文件中用户名和密码是否匹配,如果用户名和密码都匹配则输出“登录成功”,否则输出“登录失败”。在LoginServlet类加注解PathAnnotation("/login")。
(5)声明SignServlet类,继承MyServlet类,重写service方法,遍历map,如果里面包含key为username和password的键值对,则取出对应的值,并判断它和users.properties文件中用户名是否重复,如果用户名已存在则输出“注册失败”,否则输出“注册成功”,并把新的用户名和密码追加到users.properties文件新的一行。在SignServlet类加注解PathAnnotation("/sign")。
(6)在src包含一个servlet.properties文件,并包含
servletClass=所有MyServlet类子类的全名称(每个类使用,分割)
(7)在测试类中,
-
创建HashMap<String,Class>集合对象pathsMap。
-
读取servlet.properties文件,获取所有MyServlet类子类的全名称,根据“,"拆分所有类名。
-
根据上面拆分的类名,通过Class.forName(类名)方法获取所有MyServlet类子类的Class对象,并读取在它们上面定义的PathAnnotation注解,并将PathAnnotation注解值作为key,对应的Class对象作为value存放到pathsMap中。
-
从键盘输入用户名和密码,并创建HashMap<String,String>,添加"username=输入的用户名","password=输入的密码"两个(key,value)键值对。
-
从键盘输入路径名,通过反射创建对应Servlet对象,并调用对应类的service方法,完成登录或注册功能。
(8)运行效果如下:
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface PathAnnotation { String value(); }
#key=value chai=999 lin=888 yan=666 admin=123456
import java.util.HashMap; public abstract class MyServlet { public abstract void service(HashMap<String,String> map)throws Exception; }
import java.io.FileInputStream; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.Set; @PathAnnotation("/login") public class LoginServlet extends MyServlet { @Override public void service(HashMap<String,String> map) throws Exception{ Set<Map.Entry<String, String>> entries = map.entrySet(); String username = ""; String password = ""; for (Map.Entry<String, String> entry : entries) { String key = entry.getKey(); String value = entry.getValue(); if("username".equals(key)){ username = value; }else if("password".equals(key)){ password = value; } } Properties properties = new Properties(); properties.load(new FileInputStream("day21_homework/users.properties")); String value = properties.getProperty(username); if(value != null && password.equals(value)){ System.out.println("登录成功"); }else{ System.out.println("登录失败"); } } }
import java.io.FileInputStream; import java.io.FileWriter; import java.util.HashMap; import java.util.Map; import java.util.Properties; import java.util.Set; @PathAnnotation("/sign") public class SignServlet extends MyServlet { @Override public void service(HashMap<String, String> map) throws Exception { Set<Map.Entry<String, String>> entries = map.entrySet(); String username = ""; String password = ""; for (Map.Entry<String, String> entry : entries) { String key = entry.getKey(); String value = entry.getValue(); if("username".equals(key)){ username = value; }else if("password".equals(key)){ password = value; } } Properties properties = new Properties(); properties.load(new FileInputStream("day21_homework/users.properties")); String value = properties.getProperty(username); if(value == null){ System.out.println("注册成功"); FileWriter fw = new FileWriter("day21_homework/users.properties",true); fw.write("\r\n" + username+"="+password); fw.close(); }else{ System.out.println("注册失败"); } } }
servletClass=com.atguigu.exercise13.LoginServlet,com.atguigu.exercise13.SignServlet
package com.atguigu.exercise13; import java.util.HashMap; import java.util.Properties; import java.util.Scanner; public class Exercise13 { public static void main(String[] args) throws Exception{ HashMap<String,Class> pathsMap = new HashMap<>(); Properties properties = new Properties(); properties.load(LoginServlet.class.getClassLoader().getResourceAsStream("servlet.properties")); String servletClasses = properties.getProperty("servletClass"); String[] classNames = servletClasses.split(","); for (String className : classNames) { Class<?> clazz = Class.forName(className); PathAnnotation pathAnnotation = clazz.getAnnotation(PathAnnotation.class); if(pathAnnotation != null){ String path = pathAnnotation.value(); pathsMap.put(path, clazz); } } Scanner input = new Scanner(System.in); System.out.print("用户名:"); String username = input.next(); System.out.print("密码:"); String password = input.next(); System.out.print("访问路径:"); String path = input.next(); HashMap<String,String> map = new HashMap<>(); map.put("username",username); map.put("password",password); Class servletClass = pathsMap.get(path); MyServlet servlet = (MyServlet) servletClass.newInstance(); servlet.service(map); input.close(); } }