🔥 本文由 程序喵正在路上 原创,CSDN首发!
💖 系列专栏:Java从入门到大牛
🌠 首发时间:2023年11月27日
🦋 欢迎关注🖱点赞👍收藏🌟留言🐾
目录
- 单元测试
- 快速入门
- Junit框架的常见注解
- 反射
- 认识反射
- 获取类
- 获取类的构造器
- 获取类的成员变量
- 获取类的成员方法
- 作用和应用场景
- 注解
- 概述
- 自定义注解
- 注解的原理
- 元注解
- 注解的解析
- 应用场景
- 动态代理
- 概述、快速入门
- 应用案例、使用代理的好处
单元测试
快速入门
单元测试
- 单元测试就是针对最小的功能单元(方法),编写测试代码对该功能进行正确性测试
目前测试方法是怎么进行的?存在什么问题?
- 只能编写 main 方法,并在 main 方法中再去调用其他方法进行测试
- 使用起来很不灵活,无法实现自动化测试
- 无法得到测试报告,需要程序员自己去观察测试是否成功
Junit单元测试框架
- Junit 是使用 Java 语言实现的单元测试框架,它是第三方公司开源出来的,很多开发工具已经集成了 Junit 框架,比如 IDEA
- Junit 编写的测试代码很灵活,可以指定某个测试方法进行测试,也支持一键完成自动化测试
- 不需要程序员去分析测试的结果,会自动生成测试报告出来
- 提供了更强大的测试能力
Junit 快速入门
需求
- 某个系统,有多个业务方法,请使用Junit框架完成对这些方法的单元测试。
具体步骤
- 将Junit框架的jar包导入到项目中(注意:IDEA集成了Junit框架,不需要我们自己手工导入了)
- 编写测试类、测试类方法(注意:测试方法必须是公共的,无参数,无返回值的非静态方法)
- 必须在测试方法上使用@Test注解(标注该方法是一个测试方法)
- 在测试方法中,编写程序调用被测试的方法即可
- 选中测试方法,右键选择 “JUnit运行” ,如果测试通过则是绿色;如果测试失败,则是红色
准备工作
为了进行 Junit 的测试,要先准备一些方法,我们定义了一个字符工具类如下
/**
* 字符串工具类
*/
public class StringUtil {
public static void printNumber(String name){
if(name == null){
System.out.println(0);
return; // 停掉方法
}
System.out.println("名字长度是:" + name.length());
}
/**
* 求字符串的最大索引
*/
public static int getMaxIndex(String data){
if(data == null) {
return -1;
}
return data.length() - 1;
}
}
下面,我们用 Junit 框架来写对应的测试类对其进行测试
import org.junit.Assert;
import org.junit.Test;
/**
* 测试类
*/
public class StringUtilTest {
@Test// 测试方法
public void testPrintNumber(){
StringUtil.printNumber("admin");
StringUtil.printNumber(null);
}
@Test// 测试方法
public void testGetMaxIndex(){
int index1 = StringUtil.getMaxIndex(null);
System.out.println(index1);
int index2 = StringUtil.getMaxIndex("admin");
System.out.println(index2);
// 断言机制:程序员可以通过预测业务方法的结果。
Assert.assertEquals("方法内部有bug!", 4, index2);
}
}
写完后,有几种测试方式:
- 将鼠标移动到某个测试方法名称处,右键选择执行,会进行单个测试方法的测试
- 将鼠标移动到类名处,右键选择执行,这个方式会进行这个类中所有测试方法的测试
- 在模块名处右键选择 Run ‘All Tests’ 执行,这样会进行模块中所有测试方法的测试
执行结果
Junit框架的常见注解
Junit单元测试框架的常用注解(Junit 4.xxxx版本)
作用
- 在测试方法执行前执行的方法,常用于初始化资源
- 在测试方法执行后再执行的方法,常用于释放资源
Junit单元测试框架的常用注解(Junit 5.xxxx版本),改了名字,但功能相同
测试代码
import org.junit.*;
import org.junit.Test;
/**
* 测试类
*/
public class StringUtilTest {
@Before
public void test1(){
System.out.println("---> test1 Before 执行了---------");
}
@BeforeClass
public static void test11(){
System.out.println("---> test11 BeforeClass 执行了---------");
}
@After
public void test2(){
System.out.println("---> test2 After 执行了---------");
}
@AfterClass
public static void test22(){
System.out.println("---> test22 AfterClass 执行了---------");
}
@Test// 测试方法
public void testPrintNumber(){
StringUtil.printNumber("admin");
StringUtil.printNumber(null);
}
@Test// 测试方法
public void testGetMaxIndex(){
int index1 = StringUtil.getMaxIndex(null);
System.out.println(index1);
int index2 = StringUtil.getMaxIndex("admin");
System.out.println(index2);
// 断言机制:程序员可以通过预测业务方法的结果。
Assert.assertEquals("方法内部有bug!", 4, index2);
}
}
执行结果
反射
认识反射
什么是反射 ?
- 反射,指的是允许以编程方式访问已加载类的成分,包括成员变量、成员方法、构造器等
反射学什么 ?
学习如何获取这些信息,并操作它们
- 反射第一步,获取类:Class
- 获取类的构造器:Constructor
- 获取类的成员变量:Field
- 获取类的成员方法:Method
获取类
反射的关键
- 反射的第一步都是先得到加载后的类,然后才可以去获取类的其他成分
获取Class对象的三种方式
Class c1 = 类名.class
- 调用 Class 提供的方法:
public static Class forName(String package);
- Object 提供的方法:
public Class getClass(); Class c3 = 对象.getClass();
写个学生类来进行获取
public class Student {
private String name;
private int age;
private char sex;
private double height;
private String hobby;
public Student() {
}
public Student(String name, int age, char sex, double height, String hobby) {
this.name = name;
this.age = age;
this.sex = sex;
this.height = height;
this.hobby = hobby;
}
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;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
}
获取Class对象
/**
* 目标:获取Class对象
*/
public class Test1Class {
public static void main(String[] args) throws Exception {
Class c1 = Student.class;
System.out.println(c1.getName()); // 全类名
System.out.println(c1.getSimpleName()); // 简名:Student
Class c2 = Class.forName("com.itheima.d2_reflect.Student"); // 名字从包名开始
System.out.println(c1 == c2); // Class对象只有一个
Student s = new Student();
Class c3 = s.getClass();
System.out.println(c3 == c2);
}
}
执行结果
获取类的构造器
使用反射技术获取构造器对象并使用
- 反射的第一步是先得到类对象,然后从类对象中获取类的成分对象
Class 类中用于获取构造器的方法
- 获取构造器的作用依然是初始化一个对象返回
Constructor类中用于创建对象的方法
setAccessible 方法让反射可以破坏封装性,私有的也可以执行了
代码演示
准备一个 Cat.java,用于获取其构造器
public class Cat {
public static int a;
public static final String COUNTRY = "中国";
private String name;
private int age;
public Cat(){
System.out.println("无参数构造器执行了~~");
}
private Cat(String name, int age) {
System.out.println("有参数构造器执行了~~");
this.name = name;
this.age = age;
}
private void run(){
System.out.println("🐱跑的贼快~~");
}
public void eat(){
System.out.println("🐱爱吃猫粮~");
}
private String eat(String name){
return "🐱最爱吃:" + name;
}
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 "Cat{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
Test2Constructor.java
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 目标:掌握获取类的构造器,并对其进行操作
*/
public class Test2Constructor {
@Test
public void testGetConstructors(){
// 1、反射第一步:必须先得到这个类的Class对象
Class c = Cat.class;
// 2、获取类的全部构造器
// Constructor[] constructors = c.getConstructors();
Constructor[] constructors = c.getDeclaredConstructors();
// 3、遍历数组中的每个构造器对象
for (Constructor constructor : constructors) {
System.out.println(constructor.getName() + "--->"
+ constructor.getParameterCount()); // 获取构造器名和参数个数
}
}
@Test
public void testGetConstructor() throws Exception {
// 1、反射第一步:必须先得到这个类的Class对象
Class c = Cat.class;
// 2、获取类的某个构造器:无参数构造器
Constructor constructor1 = c.getDeclaredConstructor();
System.out.println(constructor1.getName() + "--->"
+ constructor1.getParameterCount());
constructor1.setAccessible(true); // 禁止检查访问权限,使其可以调用私有构造器
Cat cat = (Cat) constructor1.newInstance();
System.out.println(cat);
AtomicInteger a;
// 3、获取有参数构造器
Constructor constructor2 =
c.getDeclaredConstructor(String.class, int.class);
System.out.println(constructor2.getName() + "--->"
+ constructor2.getParameterCount());
constructor2.setAccessible(true); // 禁止检查访问权限
Cat cat2 = (Cat) constructor2.newInstance("叮当猫", 3);
System.out.println(cat2);
}
}
执行结果
获取类的成员变量
使用反射技术获取成员变量对象并使用
Class中用于获取成员变量的方法
Field类中的常用方法
代码演示
Test3Field.java
import org.junit.Test;
import java.lang.reflect.Field;
/**
* 目标:掌握获取类的成员变量,并对其进行操作
*/
public class Test3Field {
@Test
public void testGetFields() throws Exception {
// 1、反射第一步:必须是先得到类的Class对象
Class c = Cat.class;
// 2、获取类的全部成员变量
Field[] fields = c.getDeclaredFields();
// 3、遍历这个成员变量数组
for (Field field : fields) {
System.out.println(field.getName() + "---> "+ field.getType());
}
// 4、定位某个成员变量
Field fName = c.getDeclaredField("name");
System.out.println(fName.getName() + "--->" + fName.getType());
Field fAge = c.getDeclaredField("age");
System.out.println(fAge.getName() + "--->" + fAge.getType());
// 赋值
Cat cat = new Cat();
fName.setAccessible(true); // 禁止访问控制权限
fName.set(cat, "卡菲猫");
System.out.println(cat);
// 取值
String name = (String) fName.get(cat);
System.out.println(name);
}
}
执行结果
获取类的成员方法
使用反射技术获取方法对象并使用
Class类中用于获取成员方法的方法
- 获取成员方法的作用依然是在某个对象中进行执行此方法
Method类中用于触发执行的方法
代码演示
import org.junit.Test;
import java.lang.reflect.Method;
/**
* 目标:掌握获取类的成员方法,并对其进行操作
*/
public class Test4Method {
@Test
public void testGetMethods() throws Exception {
// 1、反射第一步:先得到Class对象
Class c = Cat.class;
// 2、获取类的全部成员方法
Method[] methods = c.getDeclaredMethods();
// 3、遍历这个数组中的每个方法对象
for (Method method : methods) {
System.out.println(method.getName() + "--->"
+ method.getParameterCount() + "---->"
+ method.getReturnType());
}
// 4、获取某个方法对象
Method run = c.getDeclaredMethod("run"); // 拿run方法,无参数的
System.out.println(run.getName() + "--->"
+ run.getParameterCount() + "---->"
+ run.getReturnType());
Method eat = c.getDeclaredMethod("eat", String.class);
System.out.println(eat.getName() + "--->"
+ eat.getParameterCount() + "---->"
+ eat.getReturnType());
Cat cat = new Cat();
run.setAccessible(true); // 禁止检查访问权限
Object rs = run.invoke(cat); // 调用无参数的run方法,用cat对象触发调用的
System.out.println(rs);
eat.setAccessible(true); // 禁止检查访问权限
String rs2 = (String) eat.invoke(cat, "鱼儿");
System.out.println(rs2);
}
}
执行结果
作用和应用场景
反射的作用
- 基本作用:可以得到一个类的全部成分然后操作
- 可以破坏封装性
- 最重要的用途是:适合做 Java 的框架,基本上,主流的框架都会基于反射设计出一些通用的功能
案例
使用反射做一个简易版的框架
需求:对于任意一个对象,该框架都可以把对象的字段名和对应的值,保存到文件中去
实现步骤:
- 定义一个方法,可以接收任意对象
- 每收到一个对象后,使用反射获取该对象的 Class 对象,然后获取全部的成员变量
- 遍历成员变量,然后提取成员变量在该对象中的具体值
- 把成员变量名和其值,写出到文件中去即可
代码实现
准备一个学生类和教师类进行测试
Student.java
public class Student {
private String name;
private int age;
private char sex;
private double height;
private String hobby;
public Student() {
}
public Student(String name, int age, char sex, double height, String hobby) {
this.name = name;
this.age = age;
this.sex = sex;
this.height = height;
this.hobby = hobby;
}
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;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public String getHobby() {
return hobby;
}
public void setHobby(String hobby) {
this.hobby = hobby;
}
}
Teacher.java
public class Teacher {
private String name;
private double salary;
public Teacher() {
}
public Teacher(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
}
写一个简单的框架 ObjectFrame.java
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;
public class ObjectFrame {
// 目标:保存任意对象的字段和其数据到文件中去
public static void saveObject(Object obj) throws Exception {
PrintStream ps = new PrintStream(new FileOutputStream("src\\data.txt", true));
// obj是任意对象,到底有多少个字段要保存
Class c = obj.getClass();
String cName = c.getSimpleName();
ps.println("---------------" + cName + "------------------------");
// 从这个类中提取它的全部成员变量
Field[] fields = c.getDeclaredFields();
// 历每个成员变量
for (Field field : fields) {
// 拿到成员变量的名字
String name = field.getName();
// 拿到这个成员变量在对象中的数据
field.setAccessible(true); // 禁止检查访问控制
String value = field.get(obj) + "";
ps.println(name + "=" + value);
}
ps.close();
}
}
测试框架 Test5Frame.java
import org.junit.Test;
/**
* 目标:使用反射技术:设计一个保存对象的简易版框架
*/
public class Test5Frame {
@Test
public void save() throws Exception {
Student s1 = new Student("黑马吴彦祖", 45, '男', 185.3, "蓝球,冰球,阅读");
Teacher t1 = new Teacher("播妞", 999.9);
// 需求:把任意对象的字段名和其对应的值等信息,保存到文件中去
ObjectFrame.saveObject(s1);
ObjectFrame.saveObject(t1);
}
}
执行结果
注解
概述
注解(Annotation)
- 就是 Java 代码里的特殊标记,比如:@Override、@Test 等,作用是:让其他程序根据注解信息来决定怎么执行该程序
自定义注解
自定义注解,就是自己定义注解
在IDEA的包下,右键新建,选择 Java Class,再选择 Annotation 即可自定义注解
代码演示
注解 MyTest1.java
public @interface MyTest1 {
String aaa();
boolean bbb() default true; // 默认为true
String[] ccc();
}
特殊属性
- value 属性,如果注解里只有一个 value 属性的情况下,使用 value 属性的时候可以省略 value 名称不写
- 但是如果有多个属性,且多个属性没有默认值,那么 value 名称不能省略
特殊属性代码演示
注解 MyTest2.java
public @interface MyTest2 {
String value(); // 特殊属性
int age() default 23; // 有默认值
}
测试 AnnotationTest1.java
@MyTest1(aaa="牛魔王", ccc={"HTML", "Java"}) // 注解里存在的属性,要填
//@MyTest2(value = "孙悟空")
//@MyTest2(value = "孙悟空", age = 1000)
@MyTest2("孙悟空")
public class AnnotationTest1 {
// 对方法进行注解标记
@MyTest1(aaa="铁扇公主", bbb=false, ccc={"Python", "前端", "Java"})
public void test1(){
}
public static void main(String[] args) {
}
}
注解的原理
这是前面我们自定义的注解
public @interface MyTest1 {
String aaa();
boolean bbb() default true; // 默认为true
String[] ccc();
}
将其编译后,它真正的样子是这样的
public interface MyTest1 extends Annotation {
public abstract String aaa();
public abstract boolean bbb();
public abstract String[] ccc();
}
可以看到,注解本质上是一个接口,Java 中所有注解都是继承了 Annotation 接口的
@MyTest1(aaa="铁扇公主", bbb=false, ccc={"Python", "前端", "Java"})
public void test1(){
}
使用注解,@注解(...);
其实就是一个实现类对象,实现了该注解以及 Annotation 接口
元注解
什么是元注解
- 修饰注解的注解
元注解有两个:
@Target
:约束自定义注解只能在哪些地方使用@Retention
:申明注解的生命周期
@Target
中可使用的值定义在 ElementType 枚举类中,常用值如下
- TYPE:类,接口
- FIELD:成员变量
- METHOD:成员方法
- PARAMETER:方法参数
- CONSTRUCTOR:构造器
- LOCAL_VARIABLE:局部变量
@Retention
中可使用的值定义在 RetentionPolicy 枚举类中,常用值如下
- SOURCE:注解只作用在源码阶段,生成的字节码文件中不存在
- CLASS(默认值):注解作用在源码阶段和字节码文件阶段,运行阶段不存在
- RUNTIME:注解作用在源码阶段、字节码阶段和运行阶段(开发常用)
代码演示
注解 MyTest3.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD}) // 当前被修饰的注解只能用在类上,方法上
@Retention(RetentionPolicy.RUNTIME) // 控制下面的注解一直保留到运行时
public @interface MyTest3 {
}
注解的解析
什么是注解的解析
- 就是判断是否存在注解,存在注解就解析出内容
与注解解析相关的接口
- Annotation:注解的顶级接口,注解都是 Annotation 类型的对象
- AnnotatedElement:该接口定义了与注解解析相关的解析方法
- 所有的类成分 Class、Method、Field、Constructor,都实现了 AnnotatedElement 接口,它们都拥有解析注解的能力
解析注解的技巧
- 注解在哪个成分上,我们就先获取到哪个成分对象
- 比如注解作用在成员方法上,则要获得该成员方法对应的 Method 对象,再来拿上面的注解
- 比如注解作用在类上,则要该类的 Class 对象,再来拿上面的注解
- 比如注解作用在成员变量上,则要获得该成员变量对应 Field 对象,再来拿上面的注解
解析注解案例
解析注解,具体需求如下:
- 定义注解 MyTest4,要求如下:
- 包含属性:String value()
- 包含属性:double aaa(),默认100
- 包含属性:String[] bbb()
- 限制注解使用的位置:类和成员方法上
- 指定注解的有效范围:一直到运行时
- 定义一个 Demo 类,在类中定义一个 test 方法,并在该类和其方法上使用 MyTest4 注解
- 定义 AnnotationTest3 测试类,解析 Demo 类中的全部注解
案例代码
MyTest4.java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest4 {
String value();
double aaa() default 100;
String[] bbb();
}
Demo.java
@MyTest4(value = "蜘蛛精", aaa=99.5, bbb = {"至尊宝", "黑马"})
@MyTest3
public class Demo {
@MyTest4(value = "孙悟空", aaa=199.9, bbb = {"紫霞", "牛夫人"})
public void test1(){
}
}
AnnotationTest3.java
import org.junit.Test;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* 目标:掌握注解的解析
*/
public class AnnotationTest3 {
@Test
public void parseClass(){
// 1、先得到Class对象
Class c = Demo.class;
// 2、解析类上的注解
// 判断类上是否包含了MyTest4注解
if(c.isAnnotationPresent(MyTest4.class)){
MyTest4 myTest4 =
(MyTest4) c.getDeclaredAnnotation(MyTest4.class);
System.out.println(myTest4.value());
System.out.println(myTest4.aaa());
System.out.println(Arrays.toString(myTest4.bbb()));
}
}
@Test
public void parseMethod() throws Exception {
// 1、先得到Class对象
Class c = Demo.class;
Method m = c.getDeclaredMethod("test1");
// 2、解析方法上的注解
// 判断方法对象上是否包含了某个注解
if(m.isAnnotationPresent(MyTest4.class)){
MyTest4 myTest4 =
m.getDeclaredAnnotation(MyTest4.class);
System.out.println(myTest4.value());
System.out.println(myTest4.aaa());
System.out.println(Arrays.toString(myTest4.bbb()));
}
}
}
执行结果
应用场景
模拟Junit框架
需求
- 定义若干个方法,只要加了 MyTest 注解,就可以在启动时被触发执行
分析
- 定义一个自定义注解 MyTest,只能注解方法,生命周期是一直都在
- 定义若干个方法,只要有 @MyTest 注解的方法就能在启动时被触发执行,没有这个注解的方法不能执行
代码实现
MyTest.java
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 MyTest {
}
AnnotationTest4.java
import java.lang.reflect.Method;
/**
* 目标:模拟Junit框架的设计
*/
public class AnnotationTest4 {
// 不加注解
public void test1(){
System.out.println("===test1====");
}
@MyTest
public void test2(){
System.out.println("===test2====");
}
@MyTest
public void test3(){
System.out.println("===test3====");
}
@MyTest
public void test4(){
System.out.println("===test4====");
}
public static void main(String[] args) throws Exception {
AnnotationTest4 a = new AnnotationTest4();
// 启动程序!
// 1、得到Class对象
Class c = AnnotationTest4.class;
// 2、提取这个类中的全部成员方法
Method[] methods = c.getDeclaredMethods();
// 3、遍历这个数组中的每个方法,看方法上是否存在@MyTest注解,存在触发该方法执行
for (Method method : methods) {
if(method.isAnnotationPresent(MyTest.class)){
// 说明当前方法上是存在@MyTest,触发当前方法执行
method.invoke(a);
}
}
}
}
执行结果
动态代理
概述、快速入门
什么是代理
- 代理思想就是被代理者没有能力,或者不愿意去完成某件事情,需要找个人(代理)代替自己去完成这件事
- 通俗地讲,对象如果嫌身上干的事太多的话,可以通过代理来转移部分职责
动态代理的作用
- 动态代理主要是对被代理对象的行为进行处理
- 对象有什么方法想被代理,代理就一定要有对应的方法
动态代理的开发步骤
- 必须定义接口,里面定义一些方法,用来约束被代理对象和代理对象都要完成的事情
- 定义一个实现类实现接口,这个实现类的对象代表被代理的对象
- 定义一个测试类,在里面创建被代理对象,然后为其创建一个代理对象返回(重点)
- 代理对象中,需要模拟首付款,真正触发被代理对象的行为,然后接受尾款操作
- 通过返回的代理对象进行方法的调用,观察动态代理的执行流程
如何创建代理对象
- Java 中代理的代表类是
java.lang.reflect.Proxy
,它提供了一个静态方法,用于为被代理对象产生一个代理对象返回
代码演示
明星找经纪人,可以类比被代理对象找代理对象
接口 Star.java,定义被代理对象和代理对象都要完成的事情
public interface Star {
String sing(String name);
void dance();
}
实现类 BigStar.java
public class BigStar implements Star{
private String name;
public BigStar(String name) {
this.name = name;
}
public String sing(String name){
System.out.println(this.name + "正在唱:" + name);
return "谢谢!谢谢!";
}
public void dance(){
System.out.println(this.name + "正在跳舞~~");
}
}
代理工具类 ProxyUtil.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtil {
public static Star createProxy(BigStar bigStar){
Star starProxy = (Star) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
new Class[]{Star.class}, new InvocationHandler() {
@Override // 回调方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 代理对象要做的事情,会在这里写代码
if(method.getName().equals("sing")){
System.out.println("准备话筒,收钱20万");
}else if(method.getName().equals("dance")){
System.out.println("准备场地,收钱1000万");
}
return method.invoke(bigStar, args);
}
});
return starProxy;
}
}
测试类 Test.java
public class Test {
public static void main(String[] args) {
BigStar s = new BigStar("杨超越");
Star starProxy = ProxyUtil.createProxy(s);
String rs = starProxy.sing("好日子");
System.out.println(rs);
starProxy.dance();
}
}
执行结果
应用案例、使用代理的好处
模拟企业业务功能开发,并完成每个功能的性能统计
需求
- 模拟某企业用户管理业务,需包含用户登录,用户删除,用户查询功能,并要统计每个功能的耗时
分析
- 定义一个 UserService 表示用户业务接口,规定必须完成用户登录,用户删除,用户查询功能
- 定义一个实现类 UserServiceImpl 实现 UserService,并完成相关功能,且统计每个功能的耗时
- 定义测试类,创建实现类对象,调用方法
代码实现
用户业务接口 UserService.java
/**
* 用户业务接口
*/
public interface UserService {
// 登录功能
void login(String loginName,String passWord) throws Exception;
// 删除用户
void deleteUsers() throws Exception;
// 查询用户,返回数组的形式
String[] selectUsers() throws Exception;
}
用户业务实现类 UserServiceImpl.java
/**
* 用户业务实现类(面向接口编程)
*/
public class UserServiceImpl implements UserService{
@Override
public void login(String loginName, String passWord) throws Exception {
if("admin".equals(loginName) && "123456".equals(passWord)){
System.out.println("您登录成功,欢迎光临本系统~");
}else {
System.out.println("您登录失败,用户名或密码错误~");
}
Thread.sleep(1000);
}
@Override
public void deleteUsers() throws Exception{
System.out.println("成功删除了1万个用户~");
Thread.sleep(1500);
}
@Override
public String[] selectUsers() throws Exception{
System.out.println("查询出了3个用户");
String[] names = {"张全蛋", "李二狗", "牛爱花"};
Thread.sleep(500);
return names;
}
}
代理工具类 ProxyUtil.java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class ProxyUtil {
public static UserService createProxy(UserService userService){
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
new Class[]{UserService.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(method.getName().equals("login") || method.getName().equals("deleteUsers")||
method.getName().equals("selectUsers")){
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;
}else {
Object rs = method.invoke(userService, args);
return rs;
}
}
});
return userServiceProxy;
}
}
测试类 Test.java
import java.util.Arrays;
/**
* 目标:使用动态代理解决实际问题,并掌握使用代理的好处
*/
public class Test {
public static void main(String[] args) throws Exception{
// 1、创建用户业务对象
UserService userService = ProxyUtil.createProxy(new UserServiceImpl());
// 2、调用用户业务的功能
userService.login("admin", "123456");
System.out.println("----------------------------------------------------");
userService.deleteUsers();
System.out.println("----------------------------------------------------");
String[] names = userService.selectUsers();
System.out.println("查询到的用户是:" + Arrays.toString(names));
System.out.println("----------------------------------------------------");
}
}
执行结果