【Java从入门到大牛】Java高级技术

news2024/11/27 10:19:28

🔥 本文由 程序喵正在路上 原创,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);
    }
}

写完后,有几种测试方式:

  1. 将鼠标移动到某个测试方法名称处,右键选择执行,会进行单个测试方法的测试
  2. 将鼠标移动到类名处,右键选择执行,这个方式会进行这个类中所有测试方法的测试
  3. 在模块名处右键选择 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);
    }
}

执行结果

在这里插入图片描述

反射

认识反射

什么是反射 ?

  • 反射,指的是允许以编程方式访问已加载类的成分,包括成员变量、成员方法、构造器等

反射学什么 ?

学习如何获取这些信息,并操作它们

  1. 反射第一步,获取类:Class
  2. 获取类的构造器:Constructor
  3. 获取类的成员变量:Field
  4. 获取类的成员方法: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 的框架,基本上,主流的框架都会基于反射设计出一些通用的功能

案例

使用反射做一个简易版的框架

需求:对于任意一个对象,该框架都可以把对象的字段名和对应的值,保存到文件中去

实现步骤:

  1. 定义一个方法,可以接收任意对象
  2. 每收到一个对象后,使用反射获取该对象的 Class 对象,然后获取全部的成员变量
  3. 遍历成员变量,然后提取成员变量在该对象中的具体值
  4. 把成员变量名和其值,写出到文件中去即可

代码实现

准备一个学生类和教师类进行测试

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);
    }
}

执行结果

2

注解

概述

注解(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 注解,就可以在启动时被触发执行

分析

  1. 定义一个自定义注解 MyTest,只能注解方法,生命周期是一直都在
  2. 定义若干个方法,只要有 @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);
            }
        }
    }
}

执行结果

在这里插入图片描述

动态代理

概述、快速入门

什么是代理

  • 代理思想就是被代理者没有能力,或者不愿意去完成某件事情,需要找个人(代理)代替自己去完成这件事
  • 通俗地讲,对象如果嫌身上干的事太多的话,可以通过代理来转移部分职责

动态代理的作用

  • 动态代理主要是对被代理对象的行为进行处理
  • 对象有什么方法想被代理,代理就一定要有对应的方法

动态代理的开发步骤

  1. 必须定义接口,里面定义一些方法,用来约束被代理对象和代理对象都要完成的事情
  2. 定义一个实现类实现接口,这个实现类的对象代表被代理的对象
  3. 定义一个测试类,在里面创建被代理对象,然后为其创建一个代理对象返回(重点)
  4. 代理对象中,需要模拟首付款,真正触发被代理对象的行为,然后接受尾款操作
  5. 通过返回的代理对象进行方法的调用,观察动态代理的执行流程

如何创建代理对象

  • 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("----------------------------------------------------");
    }
}

执行结果

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1259639.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

探索 V8 引擎的内部:深入理解 JavaScript 执行的本质

🤍 前端开发工程师(主业)、技术博主(副业)、已过CET6 🍨 阿珊和她的猫_CSDN个人主页 🕠 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 🍚 蓝桥云课签约作者、已在蓝桥云…

nodejs+vue+mysql皮具行李箱包包网上商城购物网站

本系统可分为两个大的模块,即前台用户模块和后台管理员模块,前台用户模块用户可以进行浏览查询皮具的各种信息,添加购物车,下订单等各种操作。后台管理员模块管理员可以进行皮具的处理,还有处理订单,皮具分…

数据结构--->单链表

文章目录 链表链表的分类 单链表单链表的存储结构单链表主要实现的接口函数单链表尾插动态申请新节点单链表头插单链表的尾删单链表的头删在指定位置之前插入单链表查找插入 在指定位置之后插删除指定位置元素删除指定位置之后的元素顺序输出链表销毁单链表 顺序表和单链表的区…

excel单元格内换行按什么快捷键

如果我们使用excel软件的时候,因为一些日常的操作太过繁琐想要简化自己的操作步骤的话,其实是有很多快捷方式在其中的。那么对excel单元格内换行按什么快捷键这个问题,据小编所知我们可以在表格中使用Alt Enter来进行换行。详细内容就来看下…

【云备份】数据管理模块

文章目录 1. 数据管理模块要管理什么数据?2. 数据管理模块如何管理数据?3. 数据管理模块的具体实现BackupInfo 数据信息类NewBackupInfo —— 获取各项属性信息 DataManager 数据管理类构造函数析构函数insert —— 新增update —— 修改GetOneByURL——…

C语言——标识符

一、标识符是什么 标识符是C程序的最基本组成部分,例如:变量名称、函数名称、数据类型等等,都是一个标识符。标识符的要求是:必须由字母(区分大小写)、数字、下划线组成。而且,标识符的第一个字…

JDBC编程基础

JDBC编程基础 JDBC介绍创建JDBC项目的步骤1.引入依赖2.注册驱动3.获取数据库连接4.获取sql执行对象 JDBC 常用 API 详解sql执行对象PreparedStatement作用 事务管理结果集对象 JDBC项目demo测试 JDBC介绍 每个数据库都会提供一组API来支持程序员实现自己客户端,自己…

SQL Server:流程控制语言详解

文章目录 一、批处理、脚本和变量局部变量和全局变量1、局部变量2、全局变量 二、顺序、分支和循环结构语句1、程序注释语句2、BEGIN┅END语句块3、IF┅ELSE语句4、CASE语句5、WHILE语句6、BREAK和CONTINUE语句BREAK语句CONTINUE语句 三、程序返回、屏幕显示等语句1、RETURN语句…

通义灵码,你的智能编码助手,免费公测啦!

目录 ​编辑 1、介绍 2、安装 3、功能介绍 行/函数级实时续写 自然语言生成代码 单元测试生成 代码注释生成 代码解释 研发智能问答 多编程语言、多编辑器全方位支持 4、视频 🍃作者介绍:双非本科大三网络工程专业在读,阿里云专家…

TopK问题(用堆解决)

我们继续来延续我们上面的TopK问题,TopK问题一般是在解决有很多数的情况下,我们的k是个和小的值,然后我们是要找到最小或者最大的K个数,这类问题我们也称之为TopK问题,面对这种的问题,如果数字不是很大的情…

java--子类中访问其他成员的特点

1.在子类方法中访问其他成员(成员变量、成员方法),是依照就近原则的。 ①先子类局部范围找。 ②然后子类成员范围找。 ③然后父类成员范围找,如果父类范围还没有找到则报错。 2.如果父类中,出现了重名的成员,会优先使用子类的…

linux 磁盘管理、分区管理常用命令

文章目录 基础命令挂载新硬盘/分区添加内存交换分区swaplvm分区管理模式 基础命令 查看目录文件大小 du -sh /backup du -sh /backup/* du -sh *查看磁盘挂载信息 df -lhT查看某个目录挂载在哪个分区,以及分区的磁盘使用情况 df [目录] #例如:df /ho…

【Linux】cd 命令使用

cd(英文全拼:change directory)命令用于改变当前工作目录的命令,切换到指定的路径。 ~ 也表示为 home 目录 的意思。. 则是表示目前所在的目录。.. 则表示目前目录位置的上一层目录。 语法 cd [目录] 命令选项及作用 执行令 …

平均模式恒流控制的LED驱动器:FP7122,打造舒适照明环境的绝佳选择

目录 一、 FP7122概述 二、 FP7122特点 三、 FP7122应用 近年来,随着LED照明技术的迅猛发展,LED驱动器在家庭照明、商业照明以及植物灯等领域扮演着至关重要的角色。其中,平均模式恒流控制的LED驱动器已经成为人们追求舒适照明环境的首选。…

齐活!Spring工程整合Redis实战汇总

🎈个人公众号:🎈 :✨✨✨ 可为编程✨ 🍟🍟 🔑个人信条:🔑 知足知不足 有为有不为 为与不为皆为可为🌵 🍉本篇简介:🍉本篇记录Spring工程整合Redis实战汇总操作&#xff0…

经典神经网络——GoogLeNet模型论文详解及代码复现

论文地址:[1409.4842] Going Deeper with Convolutions (arxiv.org) 一、GoogLeNet概述 创新点 我认为,这篇文章最大的创新点是引入了一个名为Inception块的结构,能够增加神经网络模型大小的同时,减缓参数量的爆炸式增长&#x…

Java第二十章 ——多线程

本文主要讲了java中多线程的使用方法、线程同步、线程数据传递、线程状态及相应的一些线程函数用法、概述等。 在这之前,首先让我们来了解下在操作系统中进程和线程的区别:   进程:每个进程都有独立的代码和数据空间(进程上下文…

服务化通信OPC实操

实操也是基于视频进行一些笔记,没得写就少写了 准备 Nuget包准备:OPCfoundation 一般都是使用Ua,当然也是有: 客户端链接服务器参数:IP Port 认证 登录用户名 Session 的实例化创建 进行使用: 因为Ses…

NAS层协议学习(三)

消息结构 每个NAS消息包含一个协议鉴别符和一个消息标识。协议鉴别符是一个 4 位值,指示正在使用的协议,即对于 EPS NAS 消息是 EMM 或 ESM。消息标识指示发送的特定消息。 EMM 消息还包含一个安全标头,指示消息是否受到完整性保护和/或加密…

AI视觉识别有哪些工业应用

AI视觉识别,主要是利用人工智能算法对图像或视频数据进行分析和处理,以提取关键信息并执行筛选、判断、预警等任务。AI视觉识别涵盖多种应用,如人脸识别、目标检测和识别、图像分割、行为识别、视频分析等。本篇就简单介绍一下AI视觉识别的应…