单元测试、反射、注解、动态代理 课程安排
单元测试
单元测试概述
单元测试
- 单元测试就是针对最小的功能单元编写测试代码,Java 程序最小的功能单元是方法,因此,单元测试就是针对 Java 方法的测试,进而检查方法的正确性。
目前测试方法是怎么进行的,存在什么问题
- 只有一个 main 方法,如果一个方法的测试失败了,其他方法测试会受到影响。
- 无法得到测试的结果报告,需要程序员自己去观察测试是否成功。
- 无法实现自动化测试。
JUnit 单元测试框架
- JUnit 是使用 Java 语言实现的单元测试框架,它是开源的,Java 开发者都应当学习并使用 JUnit 编写单元测试。
- 此外,几乎所用的 IDE 工具都集成了 JUnit,这样我们就可以直接在 IDE 中编写并运行 JUint 测试,JUnit 目前最先版本是 5。
Junit 优点
- JUnit 可以灵活的选择执行哪些测试方法,可以一键执行全部测试方法。
- JUnit 可以生成全部方法的测试报告。
总结
- JUnit 单元测试是做什么的?
- 测试类中方法的正确性的。
- JUnit 单元测试的优点是什么?
- JUnit 可以选择执行哪些测试方法,可以一键执行全部测试方法的测试。
- JUnit 可以生成测试报告,如果测试良好则是绿色;如果测试失败,则是红色。
- 单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。
单元测试快速入门
步骤 单元测试快速入门
需求:使用单元测试进行业务方法预期结果、正确性测试的快速入门
分析:
-
将 JUint 的 jar 包导入到项目中
-
IDEA 通常整合好可了 JUint 框架,一般需要要导入。
-
如果 IDEA 没有整合好,需要自己手工导入如下 2 个 JUnit 的 jar 包到模板
-
-
编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法。
-
在测试方法上使用 @Test 注解:标注该方法是一个测试方法。
-
在测试方法中完成被测试方法的预期正确性测试。
-
选中测试方法,选中 “JUnit 运行”,如果 测试良好则是绿色;如果测试失败,则是红色。
业务方法
/**
* @author : gxd
* @date : 2022/7/22 17:27
* 业务方法
*/
public class UserService {
public String loginName(String loginName,String password){
if ("admin".equals(loginName) && "123456".equals(password)){
return "登录成功";
}else {
return "用户名或者密码有问题";
}
}
public void selectNames(){
System.out.println(10/0);
System.out.println("查询全部用户名称成功~~~");
}
}
测试类
/**
* @author : gxd
* @date : 2022/7/22 17:32
* 测试类
* 目标:单元测试快速入门
*
* 步骤 单元测试快速入门
* 需求:使用单元测试进行业务方法预期结果、正确性测试的快速入门
* 分析:
* 1. 将 JUint 的 jar 包导入到项目中
* - IDEA 通常整合好可了 JUint 框架,一般需要要导入。
* - 如果 IDEA 没有整合好,需要自己手工导入如下 2 个 JUnit 的 jar 包到模板
* 2. 编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法。
* 3. 在测试方法上使用 @Test 注解:标注该方法是一个测试方法。
* 4. 在测试方法中完成被测试方法的预期正确性测试。
* 5. 选中测试方法,选中 “JUnit 运行”,如果 试良好.则是绿色;如果测试失败,则是红色。
*/
public class TestUserService {
/**
* 测试方法
* 注意点:
* 1、必须是公开的,无参数 无返回值的方法
* 2、测试方法必须使用 @Test 注解标记
*/
@Test
public void testLoginName(){
UserService userService = new UserService();
String rs = userService.loginName("admin","123456");
//进行预期结构的正确性测试:断言。
/**
* public static void assertEquals(String message, Object expected, Object actual)
* 参数一:消息提示
* 参数二:你调的这个方法的 userService.loginName 的返回结果一样 ,预期的结果
* 参数三:实际的结果
*/
Assert.assertEquals("您的登录业务可能出现问题","登录成功",rs);
}
@Test
public void testSelectNames(){
UserService userService = new UserService();
userService.selectNames();
}
}
总结
- JUnit 单元测试的实现过程是什么样的?
- 必须导入 JUnit 框架的 jar 包。
- 定义的测试方法必须是无参数无返回值,且公开的方法。
- 测试方法使用 @Test 注解标记。
- JUint 测试某个方法,测试全部方法怎么处理?成功的标志是什么?
- 测试某个方法直接右键该方法启动测试。
- 测试全部方法,可以选择类或者模块启动。
- 红色失败,绿色成功。
单元测试常用注解
JUnit 常用注解(JUnit 4.xxxx版本)
- 开始执行的方法:初始化资源。
- 执行完毕之后的方法:释放资源。
/**
* @author : gxd
* @date : 2022/7/22 17:32
* 测试类
* 目标:单元测试快速入门
*
* 步骤 单元测试快速入门
* 需求:使用单元测试进行业务方法预期结果、正确性测试的快速入门
* 分析:
* 1. 将 JUint 的 jar 包导入到项目中
* - IDEA 通常整合好可了 JUint 框架,一般需要要导入。
* - 如果 IDEA 没有整合好,需要自己手工导入如下 2 个 JUnit 的 jar 包到模板
* 2. 编写测试方法:该测试方法必须是公共的无参数无返回值的非静态方法。
* 3. 在测试方法上使用 @Test 注解:标注该方法是一个测试方法。
* 4. 在测试方法中完成被测试方法的预期正确性测试。
* 5. 选中测试方法,选中 “JUnit 运行”,如果 试良好.则是绿色;如果测试失败,则是红色。
*/
public class TestUserService {
//修饰实例方法的
@Before
public void before(){
System.out.println("======before方法执行一次======");
}
@After
public void after(){
System.out.println("======after方法执行一次======");
}
//修饰静态方法
@BeforeClass
public static void beforeClass(){
System.out.println("======beforeClass方法执行一次======");
}
@AfterClass
public static void afterClass(){
System.out.println("======afterClass方法执行一次======");
}
/**
* 测试方法
* 注意点:
* 1、必须是公开的,无参数 无返回值的方法
* 2、测试方法必须使用 @Test 注解标记
*/
@Test
public void testLoginName(){
UserService userService = new UserService();
String rs = userService.loginName("admin","123456");
//进行预期结构的正确性测试:断言。
/**
* public static void assertEquals(String message, Object expected, Object actual)
* 参数一:消息提示
* 参数二:你调的这个方法的 userService.loginName 的返回结果一样 ,预期的结果
* 参数三:实际的结果
*/
Assert.assertEquals("您的登录业务可能出现问题","登录成功",rs);
}
@Test
public void testSelectNames(){
UserService userService = new UserService();
userService.selectNames();
}
}
JUnit 常用注解(JUnit 5.xxxx 版本)
- 开始执行的方法:初始化资源。
- 执行完毕之后的方法:释放资源。
使用与(JUnit 4.xxxx版本) 一样,只是名字换了。
反射
反射概述
反射概述
- 反射是指对于任何一个 Class 类,在“运行的时候”都可以直接得到这个类全部部分。
- 在运行时,可以直接得到这个类的构造器对象:Constructor
- 在运行时,可以直接得到这个类的成员变量对象:Field
- 在运行时,可以直接得到这个类的成员方法对象:Method
- 这种运行时动态获取类信息以及动态调用类中成分的能力称为 Java 语言的反射机制。
反射的关键:
- 反射的第一步都是先得到编译后的 Class 类对象,然后就可以得到 Class 的全部成分。
|
总结
- 反射的基本作用、关键?
- 反射是在运行时获取类的字节码文件对象:然后可以解析类中的全部成分。
- 反射的核心思想和关键就是:得到编译以后的 class 文件对象。
反射获取类对象
反射的第一步:获取 Class 类的对象
Student
/**
* @author : gxd
* @date : 2022/7/23 17:31
*/
public class Student {
}
Test
/**
* @author : gxd
* @date : 2022/7/23 17:32
* 目标:反射的第一步:获取Class对象
*/
public class Test {
public static void main(String[] args) throws Exception {
//1、Class 类中的一个静态方法:forName(全限名:包名 + 类名)
Class c = Class.forName("com.zwzl.d2_reflect_class.Student");
System.out.println(c);//Student.class
//2、类名.class
Class c1 = Student.class;
System.out.println(c1);
//3、对象.getClass() 获取对象对应类的Class对象
Student s = new Student();
Class c2 = s.getClass();
System.out.println(c2);
}
}
总结
- 反射的第一步是什么?
- 获取 Class 类对象,如此才可以解析类的全部成分
- 获取 Class 类的对象三种方式
- 方式一:Class c1 = Class.forName(“全类名”);
- 方式二:Class c2 = 类名.class;
- 方式三:Class c3 = 对象.getClass();
反射获取构造器对象
使用反射技术获取构造器对象并使用
使用反射技术获取构造器对象并使用
-
反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
-
Class 类中用于构造器的方法
Student
/**
* @author : gxd
* @date : 2022/7/23 17:54
*/
public class Student {
private String name;
private int age;
private Student() {
System.out.println("无参数构造器执行!");
}
public Student(String name, int age) {
System.out.println("有参数构造器执行!");
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
TestStudent1
/**
* @author : gxd
* @date : 2022/7/23 17:56
* 目标:使用反射技术获取构造器对象并使用
* - 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
* - Class 类中用于构造器的方法
*/
public class TestStudent1 {
/**
* 1、getConstructors:
* 获取全部的构造器:只能获取public修饰的构造器。
* public Constructor<?>[] getConstructors()
*/
@Test
public void getConstructors(){
//a、第一步:获取类对象
Class c = Student.class;
//b、提取类中的全部的构造器对象(这里只能拿public修饰的构造器)
Constructor[] constructors = c.getConstructors();
//c、遍历构造器
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
}
}
/**
* 2、getDeclaredConstructors():
* 获取全部的构造器:只要你敢写,这里就能拿到,无所谓权限是否可及。
* ublic Constructor<?>[] getDeclaredConstructors()
*/
@Test
public void getDeclaredConstructors(){
//a、第一步:获取类对象
Class c = Student.class;
//b、提取类中的全部的构造器对象
Constructor[] constructors = c.getDeclaredConstructors();
//c、遍历构造器
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
}
}
/**
* 3、getConstructor(Class… parameterTypes)
* 获取某个构造器:只能拿public修饰的某个构造器
* public Constructor<T> getConstructor(Class<?>... parameterTypes)
*/
@Test
public void getConstructor() throws Exception {
//a、第一步:获取类对象
Class c = Student.class;
//b、定位单个构造器对象(按照参数定位无参数构造器,只能拿public修饰的某个构造器)
Constructor constructor = c.getConstructor();
System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
//c、定位某个有参构造器(只能拿public修饰的某个构造器)
Constructor constructor1 = c.getConstructor(String.class, int.class);
System.out.println(constructor1.getName() + "===>" + constructor1.getParameterCount());
}
/**
* 4、getDeclaredConstructor
* 获取某个构造器:只要你敢写,这里就能拿到,无所谓权限是否可及。
* public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes)
*/
@Test
public void getDeclaredConstructor() throws Exception {
//a、第一步:获取类对象
Class c = Student.class;
//b、定位单个构造器对象(按照参数定位无参数构造器)
Constructor constructor = c.getDeclaredConstructor();
System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
//c、定位某个有参构造器
Constructor constructor1 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(constructor1.getName() + "===>" + constructor1.getParameterCount());
}
}
使用反射技术获取构造器对象并使用
- 获取构造器的作用依然是初始化一个对象返回。
Constructor 类中用于创建对象的方法
/**
* @author : gxd
* @date : 2022/7/23 17:57
* 目标:使用反射技术获取构造器对象并使用
* - 获取构造器的作用依然是初始化一个对象返回。
* Constructor 类中用于创建对象的方法
* - public T newInstance(Object ... initargs):根据指定的构造器创建对象
* - public void setAccessible(boolean flag):设置为 true,表示取消访问检查,进行暴力反射
*/
public class TestStudent2 {
//1、调用构造器得到一个类的对象返回。
@Test
public void getDeclaredConstructor() throws Exception {
Class c = Student.class;
Constructor constructor = c.getDeclaredConstructor();
System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
//如果遇到了私有的构造器,可以暴力反射
constructor.setAccessible(true);//权限被打开
Student s = (Student) constructor.newInstance();
System.out.println(s);
System.out.println("-------------------------------------");
Constructor constructor1 = c.getDeclaredConstructor(String.class, int.class);
System.out.println(constructor1.getName() + "===>" + constructor1.getParameterCount());
Student s1 = (Student) constructor1.newInstance("张三", 35);
System.out.println(s1);
}
}
总结
- 利用反射技术获取构造器对象的方式
- getDeclaredConstructors()
- getDeclaredConstructor(Class<?>… parameterTypes)
- 反射得到的构造器可以做什么?
- 依然是创建对象的
- public newInstance(Object… initargs)
- 如果是非 public 的构造器,需要打开权限(暴力反射),然后再创建对象
- setAccessible(boolean)
- 反射可以破坏封装性,私有的也可以执行了。
- 依然是创建对象的
反射获取成员变量对象
使用反射技术获取成员变量对象并使用
使用反射技术获取成员变量对象并使用
-
反射的第一步是先得到类对象,然后从类对象中或缺类的成分对象。
-
Class 类中用于获取成员变量的方法
blog.csdnimg.cn/18757b8d91e24ab986297a3abdf112c1.png#pic_center)
Student
/**
* @author : gxd
* @date : 2022/7/23 23:07
*/
public class Student {
private String name;
private int age;
public static String schoolName;
public static final String COUNTTPY = "中国";
public Student() {
System.out.println("无参数构造器执行!");
}
public Student(String name, int age) {
System.out.println("有参数构造器执行!");
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
FieldTest1
/**
* @author : gxd
* @date : 2022/7/23 23:07
* 目标:使用反射技术获取成员变量对象并使用
* - 反射的第一步是先得到类对象,然后从类对象中或缺类的成分对象。
*
* -Class类中用于获取成员变量的方法
* - public Field[] getFields():返回所有成员变量对象的数组(只能拿public的)
* - public Field[] getDeclaredFields():返回所有成员变量对象的数组,存在就能拿到
* - public Field getField(String name):返回单个成员变量对象(只能拿public的)
* - public Field getDeclaredField(String name):返回单个成员变量对象,存在就能拿到
*/
public class FieldTest1 {
/**
* 1、获取全部的成员变量
* public Field[] getDeclaredFields()
* 获取所有的成员变量对应的Field对象,只要申明了就可以得到
*/
@Test
public void getDeclaredFields(){
//a、定位Class类
Class c = Student.class;
//b、定位全部成员变量
Field[] fields = c.getDeclaredFields();
//c、遍历一下
for (Field field : fields) {
System.out.println(field.getName() + "====>" + field.getType());
}
}
/**
* 2、获取某个成员变量对象,只要申明了就可以得到
* public Field getDeclaredField(String name)
* 参数:成员变量名
*/
@Test
public void getDeclaredField() throws Exception {
//a、定位Class对象
Class c = Student.class;
//b、根据名称定位某个成员变量
Field field = c.getDeclaredField("name");
System.out.println(field.getName() + "===>" + field.getType());
}
/**
* 3、返回所有成员变量对象的数组(只能拿public的)
* public Field[] getFields()
*/
@Test
public void getFields(){
Class c = Student.class;
//(这里只能拿public修饰的成员变量)
Field[] fields = c.getFields();
for (Field field : fields) {
System.out.println(field.getName() + "===>" + field.getType());
}
}
/**
* 4、返回单个成员变量对象(只能拿public的)
* public Field getField(String name)
*/
@Test
public void getField() throws Exception {
Class c = Student.class;
//(只能拿public修饰的某个成员变量)
Field field = c.getField("schoolName");
System.out.println(field.getName() + "===>" + field.getType());
}
}
使用反射技术获取成员变量对象并使用
- 获取成员变量的作用依然是在某个对象中取值、赋值
Field 类中用于取值、赋值的方法
/**
* @author : gxd
* @date : 2022/7/23 23:07
* 目标:反射获取成员变量:取值和赋值
*
* Field的方法:给成员变量赋值和取值
* - public void set(Object obj, Object value):给对象注入某个成员变量数据
* - public Object get(Object obj):获取对象的成员变量的值
* - public void setAccessible(boolean flag):暴力反射,设置为可以直接访问私有类型的属性。
* - public Class<?> getType():获取属性的类型,返回Class对象
* - public String getName():获取属性的名称
*/
public class FieldTest2 {
@Test
public void setField() throws Exception{
//a、定位Class对象
Class c = Student.class;
//b、根据名称定位某个成员变量
Field field = c.getDeclaredField("name");
field.setAccessible(true);//暴力打开权限
//c、赋值
Student s = new Student();
field.set(s,"张三");//s.setName("张三");
System.out.println(s);
//d、取值
String name = (String) field.get(s);//s.getName();
System.out.println(name);
}
}
总结
- 利用反射技术获取成员变量的方式
- 获取类中成员变量对象的方法
- getDeclaredFields()
- getDeclaredField(String name)
- 获取类中成员变量对象的方法
- 反射得到成员变量可以做什么?
- 依然是某个对象中取值和赋值。
- void set(Object obj, Object value)
- Object get(Object obj)
- 如果某成员变量是非public的,需要打开权限(暴力反射),然后再取值、赋值。
- setAccessible(boolean)
- 依然是某个对象中取值和赋值。
反射获取方法对象
使用反射技术获取方法对象并使用
使用反射技术获取方法对象并使用
-
反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
-
Class类中用于获取成员方法的方法
使用发射技术获取方法对象并使用
- 获取成员方法的作用依然是在某个对象进行执行此方法
Method 类中用于触发执行的方法
dog
/**
* @author : gxd
* @date : 2022/7/25 8:53
*/
public class Dog {
private String name;
public Dog() {
}
public Dog(String name) {
this.name = name;
}
public void run(){
System.out.println("狗跑的贼快~~~");
}
private void eat(){
System.out.println("狗吃骨头");
}
private String eat(String name){
System.out.println("狗吃" + name);
return "吃的很开心";
}
public static void inAddr(){
System.out.println("在吉山区有一群单身狗!");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
MethodTest1
/**
* @author : gxd
* @date : 2022/7/25 9:17
* 目标:使用反射技术获取方法对象并使用
*
* 使用反射技术获取方法对象并使用
* - 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象。
* - Class类中用于获取成员方法的方法
* - public Method[] getMethods():、返回所有成员方法对象的数组(只能拿public修饰的)
* - public Method[] getDeclaredMethods():返回所有成员方法对象的数组,存在就能拿到
* - public Method getMethod(String name, Class<?>... parameterTypes):返回的单个成员方法对象(只能拿public修饰的)
* - public Method getDeclaredMethod(String name, Class<?>... parameterTypes):返回单个成员方法对象,存在就能拿到
*
* Method的方法执行:
* public Object invoke(Object obj, Object... args)
* 参数一:触发的是哪个对象的方法执行。
* 参数二:args:调用方法时传递的实际参数
*/
public class MethodTest1 {
/**
* 1、返回所有成员方法对象的数组,存在就能拿到
* public Method[] getDeclaredMethods()
*/
@Test
public void getDeclaredMethods(){
//a、获取类对象
Class c = Dog.class;
//b、提取全部方法:包括私有的
Method[] methods = c.getDeclaredMethods();
//c、遍历全部方法
for (Method method : methods) {
System.out.println(method.getName() + ",返回类型:" + method.getReturnType() + ",个数:" + method.getParameterCount());
}
}
/**
* 2、返回单个成员方法对象,存在就能拿到
* public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
*/
@Test
public void getDeclaredMethod() throws Exception {
//a、获取类对象
Class c = Dog.class;
//b、提取单个方法对象
Method method = c.getDeclaredMethod("eat");
Method method1 = c.getDeclaredMethod("eat",String.class);
//暴力打开权限了
method.setAccessible(true);
method1.setAccessible(true);
//c、触发方法的执行
Dog dog = new Dog();
//注意:方法如果是没有结果回来的,那么返回的是 null。
Object rs = method.invoke(dog);
System.out.println(rs);
Object rs1 = method1.invoke(dog, "骨头");
System.out.println(rs1);
}
/**
* 3、返回所有成员方法对象的数组(只能拿public修饰的全部方法)
* public Method[] getMethods()
*/
@Test
public void getMethods(){
Class c = Dog.class;
//(只能拿public修饰的全部方法)
Method[] methods = c.getMethods();
for (Method method : methods) {
System.out.println(method.getName() + ",返回类型:" + method.getReturnType() + ",个数:" + method.getParameterCount());
}
}
/**
* 4、返回的单个成员方法对象(只能拿public修饰的单个方法)
* public Method getMethod(String name, Class<?>... parameterTypes)
*/
@Test
public void getMethod() throws Exception {
Class c = Dog.class;
//(只能拿public修饰的单个方法)
Method method = c.getMethod("run");
System.out.println(method.getName() + ",返回类型:" + method.getReturnType() + ",个数:" + method.getParameterCount());
Method method1 = c.getMethod("eat",String.class);
System.out.println(method1.getName() + ",返回类型:" + method1.getReturnType() + ",个数:" + method1.getParameterCount());
}
}
总结
- 利用反射技术获取成员对象的方法
- 获取类中成员方法对象
- getDeclaredMethods()
- getDeclaredMethodString name, Class<?>… parameterTypes)
- 获取类中成员方法对象
- 反射得到成员方法可以做什么?
- 依然是在某个对象中触发该方法执行。
- Object invoke(Object obj, Object… args)
- 如果某成员方法是非 public 的,需要打开权限(暴力反射),然后再触发执行
- setAccessible(boolean)
- 依然是在某个对象中触发该方法执行。
反射的作用-绕过编译阶段为集合添加数据
反射的作用-绕过编译阶段为集合添加数据
-
反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素的。
-
泛型只是在编译阶段可以约束结合只能操作某种数据类型,在编译成 Class 文件进入运行阶段的时候,其真实类型都是 ArrayList 了,泛型相当于被擦除了。
/**
* @author : gxd
* @date : 2022/7/25 10:27
* 目标:反射的作用-绕过编译阶段为集合添加数据
* - 反射是作用在运行时的技术,此时集合的泛型将不能产生约束了,此时是可以为集合存入其他任意类型的元素的。
* -泛型只是在编译阶段可以约束结合只能操作某种数据类型,在编译成 Class 文件进入运行阶段的时候,其真实类型都是 ArrayList 了,泛型相当于被擦除了。
*/
public class ReflectTet {
public static void main(String[] args) throws Exception {
ArrayList<Integer> list1 = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
System.out.println(list1.getClass());
System.out.println(list2.getClass());
System.out.println(list1.getClass() == list2.getClass());//ArrayList.class
System.out.println("-----------------------------------");
ArrayList<Integer> list3 = new ArrayList<>();
list3.add(15);
list3.add(85);
//list3.add("张三");
Class c = list3.getClass();//ArrayList.class ===> public boolean add(E e)
//定位c类中的add方法
Method method = c.getDeclaredMethod("add", Object.class);
boolean rs = (boolean)method.invoke(list3,"张三");
System.out.println(rs);
System.out.println(list3);
//不用反射也可以做到
ArrayList list4 = list1;
list4.add("asdfsd");
System.out.println(list4);
}
}
总结
- 反射为何可以给约定了泛型的集合存入其他类型的元素?
- 编译成Class文件进入运行阶段的时候,泛型会自动擦除。
- 反射是作用在运行时的技术,此时已经不存在泛型了。
反射的作用-通用框架的底层原理
案例 反射做通用框架
需求:给你任意一个对象,在不清楚对象字段的情况,可以把对象的字段名称和对应值存储到文件中去。
分析:
- 定义一个方法,可以接收任意类的对象。
- 每次收到一个对象后,需要解析这个对象的全部成员变量名称。
- 这个对象可能是任意的,那么怎么样才可以知道这个对象的全部成员变量名称呢?
- 使用反射获取对象的 Class 类对象,然后获取全部成员变量信息。
- 遍历成员变量信息,然后提取本成员变量在对象中的具体值。
- 存入成员变量名称和值到文件中去即可。
实体类:Student
/**
* @author : gxd
* @date : 2022/7/25 11:09
*/
public class Student {
private String name;
private char sex;
private int age;
private String className;
private String hobby;
public Student() {
}
public Student(String name, char sex, int age, String className, String hobby) {
this.name = name;
this.sex = sex;
this.age = age;
this.className = className;
this.hobby = hobby;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
}
实体类:Teacher
/**
* @author : gxd
* @date : 2022/7/25 11:11
*/
public class Teacher {
private String name;
private char sex;
private double salary;
public Teacher() {
}
public Teacher(String name, char sex, double salary) {
this.name = name;
this.sex = sex;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
工具类:MybatisUtil
/**
* @author : gxd
* @date : 2022/7/25 11:14
* 通用框架工具类
*/
public class MybatisUtil {
/**
* 保存任意类型的对象
* @param obj
*/
public static void save(Object obj){
try (
PrintStream ps = new PrintStream(new FileOutputStream("junit-reflect-annotation-proxy-app/src/data.txt",true))
){
//1、提取这个对象的全部成员变量:只有反射可以解决
Class c = obj.getClass();//c.getSimpleName()获取当前类 c.getName获取全限名:包名+类名
ps.println("=====================" + c.getSimpleName() + "=====================");
//2、提取它的全部成员变量
Field[] fields = c.getDeclaredFields();
//3、获取成员变量的信息
for (Field field : fields) {
String name = field.getName();
//提取本成员变量在 obj 对象中的值(取值)
field.setAccessible(true);
String value = field.get(obj) + "";
ps.println(name + "=" + value);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
主程序:ReflectTest
/**
* @author : gxd
* @date : 2022/7/25 11:14
* 目标: 反射的作用-通用框架的底层原理
*
* 案例 反射做通用框架*
* 需求:给你任意一个对象,在不清楚对象字段的情况,可以把对象的字段名称和对应值存储到文件中去。
* 分析:
* 1. 定义一个方法,可以接收任意类的对象。
* 2. 每次收到一个对象后,需要解析这个对象的全部成员变量名称。
* 3. 这个对象可能是任意的,那么怎么样才可以知道这个对象的全部成员变量名称呢?
* 4. 使用反射获取对象的 Class 类对象,然后获取全部成员变量信息。
* 5. 遍历成员变量信息,然后提取本成员变量在对象中的具体值。
* 6. 存入成员变量名称和值到文件中去即可。
*/
public class ReflectTest {
public static void main(String[] args) {
Student s = new Student();
s.setName("张三");
s.setAge(22);
s.setSex('男');
s.setClassName("大四");
s.setHobby("学习");
MybatisUtil.save(s);
Teacher t = new Teacher();
t.setName("老师");
t.setSex('男');
t.setSalary(10000);
MybatisUtil.save(t);
}
}
总结
- 反射的作用?
- 可以在运行时得到一个类的全部成分然后操作。
- 也可以破坏封装性。(很突出)
- 也可以破坏泛型的约束性。(很突出)
- 更重要的用途是适合:做Java高级框架
注解
注解的概述
注解概述、作用
-
Java 注解(Annotation)又称 Java 标注,是 JDK5.0 引入的一种注释机制。
-
Java 语言中的类、构造器、方法、成员变量、参数等都可以被注解进行标注。
注解的作用是什么呢?
- 对 Java 中类、方法、成员变量做标记,然后进行特殊处理,至于到底做何种处理有业务需求来决定。
- 例如:JUint 框架中,标记了注解 @Test 的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行。
总结
- 注解的作用
- 对 Java 中类、方法、成员变量做标记,然后进行特殊处理。
- 例如:JUint 框架中,标记了注解 @Test 的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行
自定义注解
自定义注解—格式
-
自定义注解就是自己做一个注解来使用。
自定义注解:MyBook
/**
* @author : gxd
* @date : 2022/7/25 14:33
*/
public @interface MyBook {
String name();
String[] authors();
double price();
}
主程序:AnnotationTest1
/**
* @author : gxd
* @date : 2022/7/25 15:29
* 目标:学会自定义注解。掌握其自定义格式和语法。
*/
@MyBook(name = "《精通JavaSE》",authors = {"自我自律","zwzl"},price = 100)
public class AnnotationTest1 {
@MyBook(name = "《精通JavaSE》",authors = {"自我自律","zwzl"},price = 100)
private AnnotationTest1(){
}
@MyBook(name = "《精通JavaSE1》",authors = {"自我自律","zwzl"},price = 100)
public static void main(String[] args) {
@MyBook(name = "《精通JavaSE2》",authors = {"自我自律","zwzl"},price = 100)
int age = 21;
}
}
特殊属性
- value 属性,如果只有一个 value 属性的情况下,使用 value 属性的时候可以省略 value 名称 不写!
- 但是如果有多个属性,且多个属性没有默认值,那么 value 名称是不能省略的。
总结
-
自定义注解
元注解
元注解
- 元注解:就是注解注解的注解。
元注解有两个:
- @Target:约束自定义注解只能在哪些地方使用。
- @retention:申明注解的生命周期
MyTest
/**
* @author : gxd
* @date : 2022/7/25 16:35
*/
@Target({ElementType.METHOD,ElementType.FIELD})//元注解
@Retention(RetentionPolicy.RUNTIME)//一直或者,在运行阶段这个注解也不消失
public @interface MyTest {
}
AnnotationTest2
/**
* @author : gxd
* @date : 2022/7/25 16:36
* 目标:认识元注解
* @MyTest //只能注解方法和成员变量,有@Target在@MyTest注解里面控制
*/
public class AnnotationTest2 {
@MyTest
private String name;
@MyTest
public void test(){
}
public static void main(String[] args) {
}
}
@Target 中可使用的值定义在 ElementType 枚举类中,常用值如下
- TYPE:类、接口
- FIELD:成员变量
- METHOD:成员方法
- PARAMETER:方法参数
- CONSTRUCTOR:构造器
- LOCAL_VARIABLE:局部变量
@Retention 中可使用的值定义在 RetentionPolicy 枚举类汇总,常用值如下
- SOURCE:注解只作用在源码阶段,生成的字节码文件中不存在
- CLASS:注解作用在源码阶段,字节码文件阶段,运行阶段不存在,默认值。
- RUNTIME:注解作用在源码阶段,字节码文件阶段,运行阶段(开发常用)
总结
- 元注解是什么?
- 注解注解的注解
- @Target 约束自定义注解可以标记的范围。
- @Rentention 用来约束自定义注解的存活范围。
注解解析
注解的解析
- 注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。
与注解解析相关的接口
-
Annotation:注解的顶级接口,注解都是 Annotation 类型的对象
-
AnnotatedElement:该接口定义了注解解析相关的解析方法
-
所有的类成分 Class,Method,Filed,Constructor,都实现了 AnnotatedElement 接口他们都拥有解析注解的能力。
解析注解的技巧
- 注解在哪个成分上,我们就先拿哪个成分对象。
- 比如注解作用成员方法,则要获得该成员方法对应的 Method 对象,再来拿上面的注解
- 比如注解作用在类上,则要该类的 Class 对象,再来拿上面的注解
- 比如注解作用在成员变量上,则要获得该成员变量对应的 Field 对象,再来拿上面的注解
案例 注解解析的案例
需求:注解解析的案例
分析:
- 定义注解 Book,要求如下:
- 包含属性:String value() 书名
- 包含属性:double price() 价格,默认值为 100
- 包含属性:String[] authors() 多位作者
- 限制注解使用的位置:类和成员方法上
- 指定注解的有效范围:RUNTIME
- 定义 BookStore 类,在类和成员方法上使用 Book 注解
- 定义 AnnotationTest1 测试类获取 Book 注解上的数据
NewBook:
/**
* @author : gxd
* @date : 2022/7/25 22:07
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NewBook {
String value();
double price() default 100;
String[] author();
}
AnnotationTest3 和 BookStore:
/**
* @author : gxd
* @date : 2022/7/25 22:10
* 目标:学会注解解析
*
* 注解的解析
* - 注解的操作中经常需要进行解析,注解的解析就是判断是否存在注解,存在注解就解析出内容。
* 与注解解析相关的接口
* - Annotation:注解的顶级接口,注解都是 Annotation 类型的对象
* - AnnotatedElement:该接口定义了注解解析相关的解析方法
* - public Annotation[] getDeclaredAnnotations():获得当前对象上使用的所有注解,返回注解数组。
* - public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass):根据注解类型获得对应注解对象
* - public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判断当前对象是否使用了指定的注解,如果使用了则返回true,否则false
* -所有的类成分 Class,Method,Filed,Constructor,都实现了 AnnotatedElement 接口他们都拥有解析注解的能力。
*
* 解析注解的技巧
* - 注解在哪个成分上,我们就先拿哪个成分对象。
* - 比如注解作用成员方法,则要获得该成员方法对应的 Method 对象,再来拿上面的注解
* - 比如注解作用在类上,则要该类的 Class 对象,再来拿上面的注解
* - 比如注解作用在成员变量上,则要获得该成员变量对应的 Field 对象,再来拿上面的注解
*
* 案例 注解解析的案例
* 需求:注解解析的案例
* 分析:
* 1. 定义注解 Book,要求如下:
* - 包含属性:String value() 书名
* - 包含属性:double price() 价格,默认值为 100
* - 包含属性:String[] authors() 多位作者
* - 限制注解使用的位置:类和成员方法上
* - 指定注解的有效范围:RUNTIME
* 2. 定义 BookStore 类,在类和成员方法上使用 Book 注解
* 3. 定义 AnnotationTest1 测试类获取 Book 注解上的数据
*/
public class AnnotationTest3 {
@Test
public void parseClass(){
//a、先得到类的对象
Class c = BookStore.class;
//b、判断这个类上面是否存在这个注解
if (c.isAnnotationPresent(NewBook.class)){
//c、直接获取该注解对象
NewBook newBook = (NewBook) c.getDeclaredAnnotation(NewBook.class);
System.out.println(newBook.value());
System.out.println(newBook.price());
System.out.println(Arrays.toString(newBook.author()));
}
}
@Test
public void parseMethod() throws Exception {
//a、先得到类的对象
Class c = BookStore.class;
Method method = c.getDeclaredMethod("test");
//b、判断这个方法上面是否存在这个注解
if (method.isAnnotationPresent(NewBook.class)){
//c、直接获取该注解对象
NewBook newBook = method.getDeclaredAnnotation(NewBook.class);
System.out.println(newBook.value());
System.out.println(newBook.price());
System.out.println(Arrays.toString(newBook.author()));
}
}
}
@NewBook(value = "《法外狂徒张三》",price = 100,author = {"张三","罗老师"})
class BookStore{
@NewBook(value = "《三少爷的剑》",price = 66,author = {"古龙","熊耀华"})
public void test(){
}
}
总结
-
注解解析的方式
注解的应用场景一:JUnit框架
案例 模拟JUnit框架
需求:
- 定义若干个方法,只要加了 @MyTest 注解,就可以在启动时被触发执行
分析:
- 定义一个自定义注解 @MyTest,只能注解方法,存活范围是一直都在。
- 定义若干个方法,只要有 @MyTest 注解的方法就能在启动时被触发执行,没有这个主机的方法不能执行。
MyTest:
/**
* @author : gxd
* @date : 2022/7/25 16:35
*/
@Target({ElementType.METHOD})//元注解
@Retention(RetentionPolicy.RUNTIME)//一直或者,在运行阶段这个注解也不消失
public @interface MyTest {
}
AnnotationTest4
/**
* @author : gxd
* @date : 2022/7/25 22:57
*/
public class AnnotationTest4 {
@MyTest
public void test1(){
System.out.println("=================test1执行=================");
}
public void test2(){
System.out.println("=================test2执行=================");
}
@MyTest
public void test3(){
System.out.println("=================test3执行=================");
}
/**
* 启动菜单,有注解的才被调用
*/
public static void main(String[] args) throws Exception {
AnnotationTest4 t = new AnnotationTest4();
//a、获取类对象
Class c = AnnotationTest4.class;
//b、提取全部方法
Method[] methods = c.getDeclaredMethods();
//c、遍历方法,看是否有 @MyTest 注解,有就跑它
for (Method method : methods) {
if (method.isAnnotationPresent(MyTest.class)){
//跑它
method.invoke(t);
}
}
}
}
动态代理
动态代理概述、快速入门
什么是代理?
- 代理指:某些场景下对象会找一个代理对象,来辅助自己完成一些工作,如:歌星(经纪人),买房的人(房产中介)。
代理主要干什么,他是如何工作的?
代理主要是针对对象的行为额外做一些辅助操作。
如何创建代理对象
-
Java 中代理的代表类是:java.lang.reflect.Proxy。
-
Proxy 提供了一个静态方法,用于为对象产生一个代理对象返回。
接口:Skill
/**
* @author : gxd
* @date : 2022/7/25 23:21
*/
public interface Skill {
void jump();//跳舞
void sing();//唱歌
}
实体类:Star
/**
* @author : gxd
* @date : 2022/7/25 23:21
*/
public class Star implements Skill{
private String name;
public Star() {
}
public Star(String name) {
this.name = name;
}
@Override
public void jump() {
System.out.println(name + "开始跳舞!");
}
@Override
public void sing() {
System.out.println(name + "开始唱歌!");
}
}
代理类:StarAgentProxy
/**
* @author : gxd
* @date : 2022/7/25 23:32
* 代理类
*/
public class StarAgentProxy {
/**
* 设计一个方法来返回一个明星对象的代理对象。
*/
public static Skill getProxy(Star s){
/**
* public static Object newProxyInstance(ClassLoader loader,
* Class<?>[] interfaces,
* InvocationHandler h)
* 参数一:定义代理类的类加载器
* 参数二:代理类要实现的接口列表
* 参数三:将方法调用分派到的处理程序。(代理对象的核心处理程序)
*/
return (Skill) Proxy.newProxyInstance(s.getClass().getClassLoader(),
s.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("收首付款……");
//真正让明星去唱歌和跳舞…
//method 正在调用的方法对象 , args 代表这个方法的参数。
Object rs = method.invoke(s, args);
System.out.println("收尾款,把杨超月接回来……");
return rs;
}
});
}
}
主程序:Test
/**
* @author : gxd
* @date : 2022/7/25 23:25
* 目标:学习开发一个动态代理的对象出来,理解动态代理的执行流程
*/
public class Test {
public static void main(String[] args) {
//1、创建一个对象(杨超月),对象的了必须实现接口
Star star = new Star("杨超月");
//为杨超月对象,生成一个代理对象(经纪人)
Skill skill = StarAgentProxy.getProxy(star);
skill.jump();//走代理的
skill.sing();
}
}
Java 中如何生成代理,并指定代理干什么事
总结
-
代理是什么?
- 一个对象,用来对被代理对象的行为额外做一些辅助工作。
-
在 Java 中实现动态代理的步骤是什么样的?
-
必须存在接口
-
被代理对象需要实现接口。
-
使用 Proxy 类提供的方法,的对象的代表对象。
-
-
通过代理对象调用方法,执行流程是什么样的?
- 先走向代理
- 代理可以为方法额外做一些辅助工作。
- 开发真正触发对象的方法的执行。
- 回到代理中,有代理负责返回结果给方法的调用者。
动态代理的应用案例:做性能分析、代理的好处小结
案例 模拟企业业务功能开发,并完成每个功能的性能统计
需求:
- 模拟企业用户管理业务,需包含用户登录,用户删除,用户查询功能,并要统计每个功能的耗时。
分析:
- 定义一个 UserService 表示用户业务接口,规定必须完成用户登录,用户删除,用户查询功能。
- 定义一个实现类 UserServiceImpl 实现 UserService ,并完成相关功能,且统计每个功能的耗时。
- 定义测试列,创建实现类对象,调用方法。
本案例存在哪些问题?
- 业务对象的每个方法都要进行性能统计,存在大量重复的代码。
- 解决方案,使用动态代理
接口:UserService
/**
* @author : gxd
* @date : 2022/7/26 0:13
*/
public interface UserService {
String login(String loginName,String passWord);
void deleteUsers();
String selectUsers();
}
实现类:UserServiceImpl
/**
* @author : gxd
* @date : 2022/7/26 0:14
*/
public class UserServiceImpl implements UserService{
@Override
public String login(String loginName, String passWord) {
String rs = "登录名和密码错误!";
if ("admin".equals(loginName) && "123456".equals(passWord)){
rs = "登录成功!";
}
try {
Thread.sleep(1000);
} catch (Exception e) {
e.printStackTrace();
}
return rs;
}
@Override
public void deleteUsers() {
try {
System.out.println("您正在删除用户数据中……");
Thread.sleep(2500);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public String selectUsers() {
String rs = "查询了10000个用户数据!";
try {
Thread.sleep(3000);
} catch (Exception e) {
e.printStackTrace();
}
return rs;
}
}
代理工具类:ProxyUtil
/**
* @author : gxd
* @date : 2022/7/26 0:25
* 代理工具类
*/
public class ProxyUtil {
/**
* 通过一个静态方法,为用户业务对象返回一个代理对象
*/
public static <T> T getProxy(T userService){
return (T) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
Object rs = method.invoke(userService, args);
long endTime = System.currentTimeMillis();
System.out.println(method.getName() + "方法耗时:" + (endTime - startTime) / 1000.0 + "s");
return rs;
}
});
}
}
主程序:Test
/**
* @author : gxd
* @date : 2022/7/26 0:11
* 目标:掌握使用动态代理解决问题,理解使用动态代理的优势。
*
* 案例 模拟企业业务功能开发,并完成每个功能的性能统计
*
* 需求:
* - 模拟企业用户管理业务,需包含用户登录,用户删除,用户查询功能,并要统计每个功能的耗时。
*
* 分析:
* - 定义一个 UserService 表示用户业务接口,规定必须完成用户登录,用户删除,用户查询功能。
* - 定义一个实现类 UserServiceImpl 实现 UserService ,并完成相关功能,且统计每个功能的耗时。
* - 定义测试列,创建实现类对象,调用方法。
*/
public class Test {
public static void main(String[] args) {
UserService userService = ProxyUtil.getProxy(new UserServiceImpl());
System.out.println(userService.login("admin","123456"));
System.out.println(userService.selectUsers());
userService.deleteUsers();
}
}
优化的关键步骤
-
必须有接口,实现类要实现接口(代理通常是基于接口实现的)。
-
创建一个实现类的对象,该对象为业务对象,紧接着为业务对象做一个代理对象。
动态代理的优点
- 可以在不改变方法源码的情况下,实现对方法功能的增强,提高了代码的复用。
- 简化了编程工作、提高了开发效率,同时提高了软件系统的可扩展性。
- 可以为被代理对象的所有方法做代理。
- 非常的灵活,支持任意接口类型的实现类对象做代理,也可以直接为接口本身做代理。