
文章目录
-
- 单元测试
- 反射
-
- [1. 认识反射、获取类](#1. 认识反射、获取类)
-
- [1.1 获取Class对象](#1.1 获取Class对象)
- [2. ⭐获取类中的成分、并对其进行操作](#2. ⭐获取类中的成分、并对其进行操作)
-
- [2.1 获取构造器并操作](#2.1 获取构造器并操作)
- [2.2 获取类的成员变量并操作](#2.2 获取类的成员变量并操作)
- [2.3 获取类的成员方法并操作](#2.3 获取类的成员方法并操作)
- [3. 作用、应用场景](#3. 作用、应用场景)
-
- [3.1 作用](#3.1 作用)
- [3.2 反射做通用框架](#3.2 反射做通用框架)
- 注解
-
- [1. 概述、自定义注解](#1. 概述、自定义注解)
- [2. 元注解](#2. 元注解)
- [3. 注解的解析](#3. 注解的解析)
-
- [3.1 如何解析注解](#3.1 如何解析注解)
- [4. 作用、应用场景](#4. 作用、应用场景)
-
- [4.1 作用](#4.1 作用)
- [4.2 应用场景](#4.2 应用场景)
- [4.3 理解注解的属性](#4.3 理解注解的属性)
- 动态代理
-
- [1. 什么是代理](#1. 什么是代理)
-
- [1.1 如何为Java对象创建一个代理对象?](#1.1 如何为Java对象创建一个代理对象?)
-
- 1) Star类和StarService接口 Star类和StarService接口)
- 2)代理类
- [2. 解决实际问题、掌握使用代理的好处](#2. 解决实际问题、掌握使用代理的好处)
|------|
| 单元测试 |
单元测试
- 针对最小的功能单元:方法,编写测试代码对其进行正确性测试
之前在main方法中测试代码,无法实现自动化测试,一个方法测试失败,可能影响其他方法的测试,也无法得到测试的报告
Junit单元测试
框架
1、认识
- 可以用来对
方法
进行测试,是第三方公司开源出来的 - 可以灵活编写测试代码,可以针对某个方法执行测试,也支持一键完成对全部方法的测试
- 独立测试,一个测试不会影响另一个方法的测试
2、使用步骤
需求
- 某个系统,有多个业务方法,请使用Junit单元测试框架,编写测试代码,完成对这些方法的正确性测试

1) 先定义一个工具类
Java
/**
* 字符串工具类
*/
public class StringUtil {
public static void printNumber(String name){
if(name == null){
System.out.println("名字不能为空");
return ;
}
System.out.println("名字长度是:"+name.length());
}
/**
* 求字符串的最大索引
*/
public static int getMaxIndex(String data){
if(data == null || "".equals(data)){
return -1;
}
return data.length() -1;
}
}
2)编写测试类
⭐测试方法:
- 必须是公开public,无参,无返回值
- 测试方法必须加上@Test注解
java
// 测试类:junit单元测试框架,对业务类中的业务方法进行正确性测试
public class StringUtilTest {
@Test
public void testPrintNumber() {
// 测试步骤:
StringUtil.printNumber("张无忌"); // 预期结果:3
// 测试用例
StringUtil.printNumber("");
StringUtil.printNumber(null);
}
@Test
public void testGetMaxIndex() {
// 测试步骤:
int index = StringUtil.getMaxIndex("abcdefg"); // 预期结果:6
// 测试用例
int index2 = StringUtil.getMaxIndex("");
int index3 = StringUtil.getMaxIndex(null);
System.out.println(index);
System.out.println(index2);
System.out.println(index3);
// 断言测试:断言结果是否与预期结果一致
Assert.assertEquals("本轮测试失败业务获取的最大索引有问题,请检查",6, index);
Assert.assertEquals("本轮测试失败业务获取的最大索引有问题,请检查",-1, index2);
Assert.assertEquals("本轮测试失败业务获取的最大索引有问题,请检查",-1, index3);
}
}
⭐断言测试:
断言结果是否与预期结果一致,有时候没有报错,但是可能不是预期的结果 → 黄色警告

|----|
| 反射 |
反射
1. 认识反射、获取类

反射
加载类
,并允许以编程的方式解剖内中的成分(成员变量、方法、构造器等)- 访问有关已加载类的字段、方法和构造函数的信息,以及使用反射字段,方法和构造函数在封装和安全限制内对其底层对应项进行操作
1.1 获取Class对象

- Student类
Java
@Data
public class Student {
private String name;
private int age;
private double score;
private String sex;
}
- 测试方法
Java
public static void main(String[] args) throws Exception {
// 目标:掌握反射第一步操作:获取Class对象(获取类本身)
// 1、获取类本身:类.class
Class cls = Student.class;
System.out.println(cls); // class com.study.demo2reflect.Student
String className = cls.getSimpleName(); // 获取类简称
System.out.println(className); // Student
String fullName = cls.getName();
System.out.println(fullName); // 获取类全名 com.study.demo2reflect.Student
// 2、获取类本身:Class.forName("类全名")
Class cls2 = Class.forName("com.study.demo2reflect.Student");
System.out.println(cls2);
// 3、获取类本身:对象.getClass()
Student s = new Student();
Class cls3 = s.getClass();
System.out.println(cls3);
System.out.println(cls == cls2); // true
System.out.println(cls == cls3); // true
}
2. ⭐获取类中的成分、并对其进行操作
💡关系反转
🤔个人理解:之前是拿着类对象去访问该类的成员变量、构造器以及成员方法;反射是获取类中成分后,就是拿着成分去找对象操作自己可以操作的部分
2.1 获取构造器并操作

- Dog类
Java
public class Dog {
private String name;
private int age;
private Dog() {
}
public Dog(String name, int age) {
this.name = name;
this.age = age;
}
public void eat(String food) {
System.out.println(this.name + "在吃" + food);
}
public void run() {
System.out.println(this.name + "在跑");
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 测试方法
Java
// 2、获取类的构造器对象并对其进行操作
@Test
public void getConstructor() throws Exception {
// 目标:获取类的构造器对象并对其进行操作
// 1、反射第一步:获取Class对象
Class cls = Dog.class;
// 2、获取构造器对象
Constructor[] cons = cls.getDeclaredConstructors(); //只要有就拿,不管权限
for(Constructor con:cons){
System.out.println(con.getName()+"("+con.getParameterTypes()+")");
}
// 3、获取单个构造器
Constructor con = cls.getDeclaredConstructor(); // 获取无参构造器
System.out.println(con.getName()+"("+con.getParameterCount()+")");
Constructor con2 = cls.getConstructor(String.class,int.class); // 获取有参构造器
System.out.println(con2.getName()+"("+con2.getParameterCount()+")");
System.out.println("====================================================");
// 4、通过获取的构造器创建对象
// 无参构造器是私有方法 → 暴力反射:可以绕过访问权限
con.setAccessible(true);
Dog d1 = (Dog) con.newInstance();
System.out.println(d1.toString());
Dog d2 = (Dog) con2.newInstance("旺财", 5);
System.out.println(d2.toString());
}

2.2 获取类的成员变量并操作

Java
// 3、获取类的成员变量对象并对其进行操作
@Test
public void getFieldInfo() throws Exception {
// 1、获取Class对象
Class cls = Dog.class;
// 2、获取成员变量对象
Field[] fields = cls.getDeclaredFields(); // 只要有就拿,不管权限
for(Field field:fields){
System.out.println(field.getName() + "(" + field.getType().getName() + ")");
}
System.out.println("========================================");
// 3、获取单个成员变量对象
Field field = cls.getDeclaredField("name");
System.out.println(field.getName() + "(" + field.getType().getName() + ")");
// 4、通过获取的成员变量对象操作成员变量
Dog d = new Dog("旺财", 5);
System.out.println(d);
field.setAccessible(true); // 暴力反射:可以绕过访问权限
field.set(d, "大黄");
System.out.println(d);
}

2.3 获取类的成员方法并操作

Java
// 4、获取类的成员方法对象并对其进行操作
@Test
public void getMethodInfo() throws Exception {
// 1、获取Class对象
Class cls = Dog.class;
// 2、获取成员方法对象
Method[] methods = cls.getDeclaredMethods(); // 只要有就拿,不管权限
for(Method method:methods){
System.out.println(method.getName() + "(" + method.getParameterCount() + ")");
}
System.out.println("========================================");
// 3、获取单个成员方法对象
Method eat = cls.getDeclaredMethod("eat", String.class);
System.out.println(eat.getName() + "(" + eat.getParameterCount() + ")");
Method run = cls.getDeclaredMethod("run");
System.out.println(run.getName() + "(" + run.getParameterCount() + ")");
// 4、通过获取的成员方法对象操作成员方法
Dog d = new Dog("旺财", 5);
eat.invoke(d, "骨头");
run.invoke(d);
}

3. 作用、应用场景
3.1 作用
- 可以得到一个类的全部成分然后操作
- 可以破坏封装性(私有的那些)(单例不单例,私有不私有)
- 可以绕过泛型的约束
Java
public static void main(String[] args) throws Exception {
// 目标:反射的基本作用
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("java");
// list.add(34); // 报错,泛型类型,只能放指定的类型
// 通过反射绕过泛型的约束
Class cls = list.getClass();
Method add = cls.getDeclaredMethod("add",Object.class);
add.invoke(list,34);
add.invoke(list,true);
System.out.println(list); // [hello, world, java, 34, true]
}

- 最重要:适合做Java的框架,基本上,主流的框架都会基于反射设计出一些通用的功能
3.2 反射做通用框架
1)通用方法:接收任意对象,并存储对象信息
Java
public class SaveObjectFrameWork {
// 保存任意对象的静态方法
public static void saveObject(Object obj) throws Exception {
// 使用打印流,包装可以追加字节输出流
PrintStream ps = new PrintStream(new FileOutputStream("F:\\object.txt",true));
// 1. 获取字节码文件对象
Class cls = obj.getClass();
String className = cls.getSimpleName();
ps.println("==============="+className+"=========================");
// 2. 获取全部成员变量
Field[] fields = cls.getDeclaredFields();
for(Field field:fields){
// 获取成员变量的名字和值
field.setAccessible(true);
String fieldName = field.getName();
Object fieldValue = field.get(obj) + ""; // 转换为字符串
System.out.println(fieldName + "=" + fieldValue);
ps.println(fieldName + "=" + fieldValue);
}
ps.close();
}
}
2)定义学生类和老师类
Java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
private String name;
private int age;
private double score;
private String sex;
}
Java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Teacher {
private String name;
private int age;
private double salary;
}
3)定义测试类:创建对象,并调用存储对象的方法
Java
public class ReflectDemo4 {
public static void main(String[] args) {
Student s1 = new Student("小明", 14, 90, "男");
Teacher t1 = new Teacher("张老师",26, 5000);
SaveObjectFrameWork sof = new SaveObjectFrameWork();
try {
sof.saveObject(s1);
sof.saveObject(t1);
} catch (Exception e) {
e.printStackTrace();
}
}
}

|----|
| 注解 |
注解
1. 概述、自定义注解
1.1 注解
-
Java代码里的特殊标记,比如:@Override、@Test等
-
让其他程序根据注解信息来决定怎么执行该程序
-
可以用在类上、构造器上、方法上、成员变量上、参数上等
1.2 自定义注解
1)使用

- 自定义注释
java
public @interface A {
String value();
// 特殊属性:默认在使用时,如果只有一个value属性,那么value属性可以省略不写
}
public @interface B {
String value();
// 特殊属性:默认在使用时,如果只有一个value属性,那么value属性可以省略不写
String hobby() default "打游戏";
}
public @interface MyBook {
String name();
int age() default 18;
String[] address();
}
- 使用注释
Java
@MyBook(name = "leo", age = 20, address = {"北京", "上海"})
//@A(value = "leo")
@A("leo") // 如果只有一个value属性,那么value属性可以省略不写
@B("lay")
public class AnnotationDemo1 {
@MyBook(name = "lay", age = 28, address = {"北京", "上海"})
public static void main(@A("why") String[] args) {
// 目标:自定义注解
@A("what")
int a;
}
}
2)原理

2. 元注解
指的是注解注解的注解:放在注解上的注解
@target
:约束注解的范围
Retention
:声明注解的保留周期,一般都是保留到编译运行时RUNTIME
Java
@Retention(RetentionPolicy.RUNTIME) // 注解的保留策略
@Target({ElementType.CONSTRUCTOR,ElementType.FIELD}) // 限制注解的声明位置
public @interface StudentLimit {
int minAge() default 18;
}

3. 注解的解析
判断类上、方法上、成员变量上是否存在注解,并把注解里的内容给解析出来
3.1 如何解析注解
- 要解析谁的注解,就应该先拿到谁
- Class、Method、Field、Constructor、都实现了AnnotatedElement接口,他们都拥有解析注解的能力
1)注解MyTest4
Java
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest4 {
String value();
double aaa() default 100;
String[] bbb();
}
2)Demo类
Java
@MyTest4(value = "aaa", aaa = 100, bbb = {"aaa","bbb"})
public class Demo {
@MyTest4(value = "bbb",bbb={"aaa","bbb"})
public void test1(){
}
}
3)定义AnnotationTest3测试类
- 解析类的注释
Java
public static void main(String[] args) throws Exception {
// 目标:解析注释
parseClass();
parseMethod();
}
public static void parseClass() {
// 1. 获取类对象
Class cls = Demo.class;
// 2. 获取注解对象:先判断是否使用了该注解,再获取注解对象
if (cls.isAnnotationPresent(MyTest4.class)) {
// 3. 获取注解对象
MyTest4 myTest4 = (MyTest4) cls.getAnnotation(MyTest4.class);
// 4. 获取注解的属性值
String value = myTest4.value();
double aaa = myTest4.aaa();
String[] bbb = myTest4.bbb();
// 5. 打印
System.out.println(value); // aaa
System.out.println(aaa); // 100.0
System.out.println(Arrays.toString(bbb)); // [aaa, bbb]
}
}
- 解析成员方法的注释
Java
public static void parseMethod() throws Exception {
// 1. 获取类对象
Class cls = Demo.class;
// 2、获取方法对象
Method method = cls.getDeclaredMethod("test1");
// 3、判断方法是否使用了指定的注解
if(method.isAnnotationPresent(MyTest4.class)){
// 4、获取注解对象
MyTest4 myTest4 = method.getAnnotation(MyTest4.class);
// 5、获取注解的属性值
String value = myTest4.value();
double aaa = myTest4.aaa();
String[] bbb = myTest4.bbb();
// 6、打印
System.out.println(value); // bbb
System.out.println(aaa); // 100.0
System.out.println(Arrays.toString(bbb)); // [aaa, bbb]
}
}
4. 作用、应用场景
4.1 作用
- 对程序做特殊标记,以方便别人针对性的处理
4.2 应用场景
需求
- 定义若干个方法,只要加了MyTest注解,就会触发该方法执行
分析
① 定义一个自定义注解MyTest,只能注解方法,存活范围是一直都在
② 定义若干个方法,部分方法加上@MyTest
Java
public class AnnotationDemo4 {
public static void main(String[] args) throws Exception{
AnnotationDemo4 ad = new AnnotationDemo4();
// 1.获取类对象
Class cls = ad.getClass();
// 2.获取存在的方法
Method[] methods = cls.getDeclaredMethods();
// 3.遍历方法,判断方法上是否使用了指定的注解
for(Method method: methods){
// 判断方法上是否使用了指定的注解
if(method.isAnnotationPresent(MyTest4.class)){
// 4.如果有该注解就执行方法
method.invoke(ad);
}
}
}
@MyTest4(value = "aaa", aaa = 100, bbb = {"aaa","bbb"})
public void test1(){
System.out.println("test1方法执行啦~");
}
public void test2(){
System.out.println("test2方法执行啦~");
}
public void test3(){
System.out.println("test3方法执行啦~");
}
@MyTest4(value = "bbb",bbb={"aaa","bbb"})
public void test4(){
System.out.println("test4方法执行啦~");
}
}
// test1方法执行啦~
// test4方法执行啦~
4.3 理解注解的属性
对该成分做特殊处理:例如多次执行某一方法
|------|
| 动态代理 |
动态代理
1. 什么是代理

1.1 如何为Java对象创建一个代理对象?

1) Star类和StarService接口

2)代理类
通过需代理任务的接口
连接代理对象
和实际执行任务的对象
- 创建代理对象方法的返回类型:StarService接口对象
Java
/**
* 代理工具类:专门负责创建代理对象并返回给别人使用
*/
public class ProxyUtil {
// 创建一个明星对象的代理对象
public static StarService createProxy(Star s) {
/**
* 参数一:用于执行用哪个类加载器去加载生成的代理类
* 参数二:用于指定代理类需要实现的接口
* 参数三:用于指定代理对象需要调用哪个类中的方法
*/
StarService proxy = (StarService) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),
s.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 用来声明代理对象要干的事
// 参数一:proxy接收到代理对象本身(暂时用处不大)
// 参数二:method接收到代理对象调用的方法
// 参数三:args接收到代理对象调用的方法的参数
System.out.println("代理对象开始工作了...");
String methodName = method.getName();
if("sing".equals(methodName)){
System.out.println("准备话筒,收费");
} else if ("dance".equals(methodName)) {
System.out.println("准备场地,收费");
}
// 找真正的明星对象来执行被代理的行为:method方法
Object result = method.invoke(s, args);
return result;
}
}
);
return proxy;
}
}

2. 解决实际问题、掌握使用代理的好处

1)用户类和用户管理类(接口-实现)

Java
/**
* 用户业务实现类
*/
public class UserServiceImpl implements UserService{
@Override
public void login(String loginName, String password) throws Exception {
// long start = System.currentTimeMillis(); // 记录开始时间 1970年1月1日0时0分0秒0毫秒 走到此刻的总毫秒值
if("admin".equals(loginName) && "123".equals(password)){
System.out.println("登录成功");
}else{
System.out.println("登录失败");
}
Thread.sleep(1000);
// long end = System.currentTimeMillis();
// System.out.println("login方法耗时:"+(end-start)/1000.0+"秒");
}
@Override
public void delete(String id) throws Exception {
// long start = System.currentTimeMillis();
System.out.println("删除用户成功");
Thread.sleep(1500);
// long end = System.currentTimeMillis();
// System.out.println("delete方法耗时:"+(end-start)/1000.0+"秒");
}
@Override
public String[] selectUsers() throws Exception{
// long start = System.currentTimeMillis();
System.out.println("查询用户成功");
String[] names = {"张三","李四","王五","赵六","孙七"};
Thread.sleep(500);
// long end = System.currentTimeMillis();
// System.out.println("selectUsers方法耗时:"+(end-start)/1000.0+"秒");
return names;
}
}
🤔记录方法耗时情况的代码具有很大的重复性,且不太适合归类到用户管理类的方法中
2)使用代理类,记录方法耗时情况
Java
public class ProxyUtil {
public static UserService createProxy(UserService userService) {
UserService proxy = (UserService) Proxy.newProxyInstance(
ProxyUtil.class.getClassLoader(),
userService.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理对象开始工作了...");
long start = System.currentTimeMillis();
// 真正调用业务对象被代理的方法
Object result = method.invoke(userService, args);
long end = System.currentTimeMillis();
System.out.println(method.getName() + "耗时:" + (end - start) / 1000.0 + "秒");
System.out.println("代理对象结束了工作了...");
return result;
}
});
return proxy;
}
}
3)使用代理执行方法
Java
public static void main(String[] args) throws Exception {
// 1、创建用户业务对象
UserService userService = new UserServiceImpl();
// 创建代理对象
userService = ProxyUtil.createProxy(userService);
// UserService userService = ProxyUtil.createProxy(new UserServiceImpl());
// 2、调用用户业务的功能
userService.login("admin", "123");
userService.delete("1");
String[] names = userService.selectUsers();
System.out.println(Arrays.toString(names));
}

💡如果要让该代理可以接收任意对象,测试任意对象的方法运行耗时,只需将对象限定改为泛型即可