反射, 注解, 动态代理

news2025/5/15 23:31:33

文章目录

    • 单元测试
      • 什么是单元测试
      • 咱们之前是如何进行单元测试的? 有啥问题 ?
      • 现在使用方法进行测试
      • 优点
      • Junit单元测试的使用步骤
      • 删除不需要的jar包
      • 总结
    • 反射
      • 认识反射、获取类
        • 什么是反射
        • 反射具体学什么?
        • 反射第一步:或者Class对象
      • 获取类中的成分、并对其进行操作
        • 获取类中构造器
        • 获取类的成员变量
        • 获取类的成员方法
      • 作用、应用场景
        • 使用反射做一个简易版的框架
    • 注解
      • 注解概述
        • 注解概述-自定义注解
        • 注解概述-注解的原理
        • 注解的作用
      • 元注解
        • 元注解是什么
      • 注解的解析
        • 如何解析注解?
        • 解析注解的案例
        • 总结
      • 作用、应用场景
    • 动态代理(设计模式)
      • 程序为什么需要代理?代理长什么样?
        • 什么是代理
        • 如何为Java对象创建一个代理对象?
        • 代码
        • 代码运行流程
      • 解决实际问题、掌握使用代理的好处

这些是源码、框架、架构师层面的技术

单元测试

什么是单元测试

就是针对最小的功能单元:方法,编写测试代码对其进行正确性测试。也就是测试 类中方法的正确性的。

咱们之前是如何进行单元测试的? 有啥问题 ?

  • 只能在main方法编写测试代码,去调用其他方法进行测试。
  • 无法实现自动化测试,一个方法测试失败,可能影响其他方法的测试。(报错就无法继续进行了)
  • 无法得到测试的报告,需要程序员自己去观察测试是否成功。(一个一个写main方法进行测试很麻烦)
    在这里插入图片描述

现在使用方法进行测试

可以用来对方法进行测试,它是第三方公司开源出来的(很多开发工具已经集成了Junit框架,比如IDEA)

优点

  • 可以灵活的编写测试代码,可以针对某个方法执行测试,也支持一键完成对全部方法的自动化测试,且各自独立。
  • 不需要程序员去分析测试的结果,会自动生成测试报告出来。如果测试良好则是绿色;如果测试失败,则是红色。
  • 单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。

在这里插入图片描述

Junit单元测试的使用步骤

需求

  • 某个系统,有多个业务方法,请使用Junit单元测试框架,编写测试代码,完成对这些方法的正确性测试。
    具体步骤
  1. 将Junit框架的jar包导入到项目中(注意:IDEA集成了Junit框架,不需要我们自己手工导入了)
  2. 为需要测试的业务类,定义对应的测试类,并为每个业务方法,编写对应的测试方法(必须:公共、无参、无返回值)
  3. 测试方法上必须声明@Test注解,然后在测试方法中,编写代码调用被测试的业务方法进行测试;
  4. 开始测试:选中测试方法,右键选择“JUnit运行” ,如果测试通过则是绿色;如果测试失败,则是红色

业务

// StringUtil.java
/**
 * 字符串工具类
 */
public class StringUtil {
    public static void printNumber(String name){
        if(name == null){
            System.out.println("参数为null!请注意");
            return;
        }
        System.out.println("名字长度是:" + name.length());
    }

    /**
     * 求字符串的最大索引
     */
    public static int getMaxIndex(String data){
        if(data == null || "".equals(data)) {
            return -1;
        }
        return data.length() - 1;
    }
}

测试

// StringUtilTest.java
import org.junit.Assert;
import org.junit.Test;

// 测试类:junit单元测试框架,对业务类中的业务方法进行正确性测试。
public class StringUtilTest {
    // 测试方法:必须是公开public,无参,无返回值。
    // 测试方法必须加上@Test注解(Junit框架的核心步骤)
    @Test
    public void testPrinNumber() {
        // 测试步骤:
        StringUtil.printNumber("张三abc"); // 5
        // 测试用例
        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);

        // 做断言:断言结果是否与预期结果一致
        // assertEquals("手写错误提示",预期结果,实际结果);
        Assert.assertEquals("本轮测试失败,业务获取的最大索引有问题!请检查",6, index);
        Assert.assertEquals("本轮测试失败,业务获取的最大索引有问题!请检查",-1, index2);
        Assert.assertEquals("本轮测试失败,业务获取的最大索引有问题!请检查",-1, index3);
    }
}

在这里插入图片描述

删除不需要的jar包

在这里插入图片描述
在这里插入图片描述

总结

1 Junit单元测试是做什么的?

  • 测试类中方法的正确性的。

2 Junit单元测试的优点是什么?

  • JUnit可以选择执行哪些测试方法,可以一键执行全部测试方法的测试。
  • JUnit可以生测试报告,如果测试良好则是绿色;如果测试失败,则是红色。
  • 单元测试中的某个方法测试失败了,不会影响其他测试方法的测试。

3 JUnit单元测试的实现过程是什么样的?

  • 必须导入Junit框架的jar包。
  • 定义的测试方法必须是无参数无返回值,且公开的方法。
  • 测试方法使用@Test注解标记。

4 JUnit测试某个方法,测试全部方法怎么处理?成功的标志是什么

  • 测试某个方法直接右键该方法启动测试。
  • 测试全部方法,可以选择类或者模块启动。
  • 红色失败,绿色通过。

反射

认识反射、获取类

什么是反射

反射就是:加载类,并允许以编程的方式解剖类中的各种成分(成员变量、方法、构造器等)。
比如idea中当你.一个对象为什么会有提示,这就是用了反射。反射可以解剖类的所有成员变量和方法。
在这里插入图片描述

反射具体学什么?

学习获取类的信息、操作它们
1、反射第一步:加载类,获取类的字节码:Class对象
2、获取类的构造器:Constructor对象
3、获取类的成员变量:Field对象
4、获取类的成员方法:Method对象

全部认识完后,再看反射的应用场景
在这里插入图片描述

反射第一步:或者Class对象

1、反射第一步:加载类,获取类的Class对象

在这里插入图片描述
获取Class对象的三种方式

  1. Class c1 = 类名.class
  2. 调用Class提供方法:public static Class forName(String package);
  3. Object提供的方法: public Class getClass(); Class c3 = 对象.getClass();
public class ReflectDemo1 {
    public static void main(String[] args) throws Exception {
        // 目标:掌握反射第一步操作:或者类的Class对象。(获取类本身)。
        // 1、获取类本身:类.class
        Class c1 = Student.class;
        System.out.println(c1);// class com.itheima.demo2reflect.Student

        // 2、获取类本身:Class.forName("类的全类名")
        Class c2 = Class.forName("com.itheima.demo2reflect.Student");
        System.out.println(c2);// class com.itheima.demo2reflect.Student

        // 3、获取类本身:对象.getClass()
        Student s = new Student();
        Class c3 = s.getClass();
        System.out.println(c3);// class com.itheima.demo2reflect.Student

        System.out.println(c1 == c2); // true
        System.out.println(c2 == c3); // true
    }
}
// student类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private String name;
    private int age;
    private String hobby;
}

获取类中的成分、并对其进行操作

获取类中构造器

Class提供了从类中获取构造器的方法。

方法说明
Constructor<?>[] getConstructors()获取全部构造器(只能获取public修饰的)
Constructor<?>[] getDeclaredConstructors()获取全部构造器(只要存在就能拿到)
Constructor<T> getConstructor(Class<?>… parameterTypes)获取某个构造器(只能获取public修饰的)
Constructor<T> getDeclaredConstructor(Class<?>… parameterTypes)获取某个构造器(只要存在就能拿到)

获取类构造器的作用:依然是初始化对象返回

Constructor提供的方法说明
T newInstance(Object… initargs)调用此构造器对象表示的构造器,并传入参数,完成对象的初始化并返回
public void setAccessible(boolean flag)设置为true,表示禁止检查访问控制(暴力反射)
获取类的成员变量

Class提供了从类中获取成员变量的方法。

方法说明
public Field[] getFields()获取类的全部成员变量(只能获取public修饰的)
public Field[] getDeclaredFields()获取类的全部成员变量(只要存在就能拿到)
public Field getField(String name)获取类的某个成员变量(只能获取public修饰的)
public Field getDeclaredField(String name)获取类的某个成员变量(只要存在就能拿到)

获取到成员变量的作用:依然是赋值、取值。

方法说明
void set(Object obj, Object value):赋值
Object get(Object obj)取值
public void setAccessible(boolean flag)设置为true,表示禁止检查访问控制(暴力反射)
获取类的成员方法

Class提供了从类中获取成员方法的API。

方法说明
Method[] getMethods()获取类的全部成员方法(只能获取public修饰的)
Method[] getDeclaredMethods()获取类的全部成员方法(只要存在就能拿到)
Method getMethod(String name, Class<?>… parameterTypes)获取类的某个成员方法(只能获取public修饰的)
Method getDeclaredMethod(String name, Class<?>… parameterTypes)获取类的某个成员方法(只要存在就能拿到)

成员方法的作用:依然是执行

Method****提供的方法说明
public Object invoke(Object obj, Object… args)触发某个对象的该方法执行。
public void setAccessible(boolean flag)设置为true,表示禁止检查访问控制(暴力反射)
public class Dog {
    private String name;
    private int age;
    private String hobby;
    private Dog() {
        System.out.println("无参数构造器执行了~~");
    }
    private Dog(String name) {
        System.out.println("1个参数有参数构造器执行了~~");
        this.name = name;
    }
    public Dog(String name, int age) {
        System.out.println("2个参数有参数构造器执行了~~");
        this.name = name;
        this.age = age;
    }

    private void eat(){
        System.out.println("狗吃骨头!");
    }

    public String eat(String name){
        System.out.println("狗吃" + name);
        return "狗说:谢谢!谢谢!汪汪汪!";
    }

    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 String getHobby() {
        return hobby;
    }

    public void setHobby(String hobby) {
        this.hobby = hobby;
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", hobby='" + hobby + '\'' +
                '}';
    }
}
import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class ReflectDemo2 {
    @Test
    public void getClassInfo(){
        // 目标:获取类的信息。
        // 1、反射第一步:或者Class对象,代表拿到类。
        Class c1 = Student.class;
        System.out.println(c1.getName()); // 类名的全类名 com.itheima.demo2reflect.Student
        System.out.println(c1.getSimpleName()); // 类名 Student
    }

    // 2、获取类的构造器对象并对其进行操作。
    @Test
    public void getConstructorInfo() throws Exception {
        // 目标:获取类的构造器对象并对其进行操作。
        // 1、反射第一步:或者Class对象,代表拿到类。
        Class c1 = Dog.class;
        // 2、获取构造器对象。
        Constructor[] cons = c1.getDeclaredConstructors();
        for (Constructor con : cons) {
            System.out.println(con.getName() + "(" + con.getParameterCount() + ")");
        }
        // com.itheima.demo2reflect.Dog(0)
        // com.itheima.demo2reflect.Dog(1)
        // com.itheima.demo2reflect.Dog(2)
        System.out.println("----------------------------------------------------------------");
        // 3、获取单个构造器
        Constructor con = c1.getDeclaredConstructor(); // 无参数构造器
        System.out.println(con.getName() + "(" + con.getParameterCount() + ")");
        //com.itheima.demo2reflect.Dog(0)

        Constructor con2 = c1.getDeclaredConstructor(String.class, int.class); // 2个参数的有参数构造器
        System.out.println(con2.getName() + "(" + con2.getParameterCount() + ")");
        // com.itheima.demo2reflect.Dog(2)

        // 4、获取构造器的作用依然是创建对象:创建对象。
        // 我们完全可以new Dog() 这里之所以不用new Dog(),是为了之后的学习打下基础。
        // 暴力反射:暴力反射可以访问私有的构造器、方法、属性。
        con.setAccessible(true); // 绕过访问权限,直接访问!
        Dog d1 = (Dog) con.newInstance();
        System.out.println(d1);
        // 无参数构造器执行了~~
        // Dog{name='null', age=0, hobby='null'}

        Dog d2 = (Dog)con2.newInstance("小黑", 3);
        System.out.println(d2);
        // 2个参数有参数构造器执行了~~
        // Dog{name='小黑', age=3, hobby='null'}
    }

    // 3、获取类的成员变量对象并对其进行操作。
    @Test
    public void getFieldInfo() throws Exception {
        // 目标:获取类的成员变量对象并对其进行操作。
        // 1、反射第一步:或者Class对象,代表拿到类。
        Class c1 = Dog.class;
        // 2、获取成员变量对象。
        Field[] fields = c1.getDeclaredFields();
        for (Field field : fields) {
            System.out.println(field.getName() + "(" + field.getType().getName() + ")");
        }
        // name(java.lang.String)
        // age(int)
        // hobby(java.lang.String)
        // 3、获取单个成员变量对象。
        Field field = c1.getDeclaredField("hobby");
        System.out.println(field.getName() + "(" + field.getType().getName() + ")");
        // hobby(java.lang.String)

        Field field2 = c1.getDeclaredField("age");
        System.out.println(field2.getName() + "(" + field2.getType().getName() + ")");
        // age(int)

        // 4、获取成员变量的目的依然是取值和赋值。
        Dog d = new Dog("泰迪", 3);// 2个参数有参数构造器执行了~~
        field.setAccessible(true); // 绕过访问权限,直接访问!
        field.set(d, "社交");  //  相当于 d.setHobby("社交");
        System.out.println(d);// Dog{name='泰迪', age=3, hobby='社交'}

        String hobby = (String) field.get(d); // 相当于 d.getHobby();
        System.out.println(hobby);// 社交

    }

    // 4、获取类的成员方法对象并对其进行操作。
    @Test
    public void getMethodInfo() throws Exception {
        // 目标:获取类的成员方法对象并对其进行操作。
        // 1、反射第一步:或者Class对象,代表拿到类。
        Class c1 = Dog.class;
        // 2、获取成员方法对象。
        Method[] methods = c1.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method.getName() + "(" + method.getParameterCount() + ")");
        }
        // eat(0)
        // eat(1)
        // setHobby(1)
        // getHobby(0)
        // getAge(0)
        // setAge(1)
        // 3、获取单个成员方法对象。
        Method m1 = c1.getDeclaredMethod("eat");// 获取是无参数的eat方法
        Method m2 = c1.getDeclaredMethod("eat", String.class);// 获取是有参数的eat方法
        System.out.println(m1.getName() + "(" + m1.getParameterCount() + ")");// getName(0)
        System.out.println(m2.getName() + "(" + m2.getParameterCount() + ")");// toString(0)
        System.out.println("--------------------------------------");
        // 4、获取成员方法的目的依然是调用方法。
        Dog d = new Dog("泰迪", 3);// 2个参数有参数构造器执行了~~
        m1.setAccessible(true); // 绕过访问权限,直接访问!
        Object rs1 = m1.invoke(d); // 唤醒对象d的eat方法执行,相当于 d.eat(); //狗吃骨头!
        System.out.println(rs1); // null

        Object rs2 = m2.invoke(d, "牛肉"); // 唤醒对象d的eat带String参数的方法执行,相当于 d.eat("牛肉");
        // 狗吃牛肉
        System.out.println(rs2);// 狗说:谢谢!谢谢!汪汪汪!
    }
}

作用、应用场景

反射的作用?

  • 基本作用:可以得到一个类的全部成分然后操作。
  • 可以破坏封装性。
  • 可以绕过泛型的约束
  • 最重要的用途是:适合做Java的框架,基本上,主流的框架都会基于反射设计出一些通用的功能。

在这里插入图片描述

import java.lang.reflect.Method;
import java.util.ArrayList;

public class ReflectDemo3 {
    public static void main(String[] args) throws Exception {
        // 3、可以绕过泛型的约束。
        ArrayList<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("令狐冲");
        list.add("东方不败");
//        list.add(9.9);// 报错
//        list.add(true);

        Class c1 = list.getClass(); // c1 == ArrayList.class
        // 这是编译后,类名.class
        // 获取 ArrayList 类的add方法
        Method add = c1.getDeclaredMethod("add", Object.class);
        // 编译好后的文件已经把泛型擦除了, 所以这里可以添加任意类型的数据。
        // 触发list集合对象的add方法执行。
        add.invoke(list, 9.9); // 翻墙
        add.invoke(list, true); // 翻墙

        System.out.println(list);
        // [张无忌, 令狐冲, 东方不败, 9.9, true]
    }
}

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

需求:
对于任意一个对象,该框架都可以把对象的字段名和对应的值,保存到文件中去。
在这里插入图片描述
实现步骤

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

反射有啥作用?
可以在运行时得到一个类的全部成分然后操作。
可以破坏封装性。(很突出)
也可以破坏泛型的约束性。(很突出)
更重要的用途是适合:做Java高级框架
基本上主流框架都会基于反射设计一些通用技术功能。

// 框架
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Field;

public class SaveObjectFrameWork {
    // 保存任意对象的静态方法
    public static void saveObject(Object obj) throws Exception {

        PrintStream ps = new PrintStream(new FileOutputStream("day-15\\src\\com\\itheima\\demo2reflect\\obj.txt", true));
        // obj 可能是学生  老师  狗
        // 只有反射可以直到对象有多少个字段:
        // 1. 获取Class对象
        Class c = obj.getClass();
        String simpleName = c.getSimpleName();
        ps.println("==============" + simpleName + "====================");
        // 2. 获取Class对象的所有字段。
        Field[] fields = c.getDeclaredFields();
        // 3. 遍历字段
        for (Field field : fields) {
            // 4. 获取字段的值
            // 4.1 获取字段名称
            String fieldName = field.getName();
            // 4.2 获取字段的值
            field.setAccessible(true); // 暴力反射
            Object fieldValue = field.get(obj) + "";
            // 5. 打印到文件中去
            ps.println(fieldName + "=" + fieldValue);
       }
       ps.close();
    }
}
// 编写入口方法
public class ReflectDemo4 {
    public static void main(String[] args) throws Exception {
        // 目标:搞清楚反射的应用:做框架的通用技术。
        Dog d = new Dog("小黑", 3);
        SaveObjectFrameWork.saveObject(d);

        // 创建学生对象
        Student s = new Student("小明", 18, "爱问问题");
        SaveObjectFrameWork.saveObject(s);

        // 创建老师对象
        Teacher t = new Teacher("小红", 19, "java、前端、动漫", 3000, "422期", '女', "12345678901");
        SaveObjectFrameWork.saveObject(t);

    }
}

==============Dog====================
name=小黑
age=3
hobby=null
==============Student====================
name=小明
age=18
hobby=爱问问题
==============Teacher====================
name=小红
age=19
hobby=java、前端、动漫
salary=3000.0
className=422期
sex=女
phone=12345678901

注解

注解概述

就是Java代码里的特殊标记,比如:@Override、@Test等,作用是:让其他程序根据注解信息来决定怎么执行该程序。
注意:注解可以用在类上、构造器上、方法上、成员变量上、参数上、等位置处。
在这里插入图片描述

注解概述-自定义注解

自定义注解
就是自己定义注解。

public @interface 注解名称 {
 	public 属性类型 属性名() default 默认值 ;
}
@MyBook(name = "赵丽颖", age = 18, address = {"北京", "上海"})
public class AnnotationDemo1 {
    @MyBook(name = "王菲", age = 52, address = {"北京", "香港"})
    public static void main( String[] args ) {
        // 目标:自定义注解。
        int a;
    }
}

// 自定义注解
public @interface MyBook {
    String name();
    int age() default 18;
    String[] address();
}

特殊属性名: value
如果注解中只有一个value属性,使用注解时,value名称可以不写!!

public  @interface A {
    String value(); // 特殊属性value,在使用时如果只有一个value属性,value名称可以不写
    String hobby() default "打篮球"; // 当这个有默认值,value名称也可以不写
}

// @A(value = "delete")
//@A("delete") // 特殊属性value,在使用时如果只有一个value属性,value名称可以不写
//@A(value = "delete", hobby = "打篮球")
@A("delete")
public class AnnotationDemo1 {
    public static void main( @A("delete") String[] args ) {
        // 目标:自定义注解。
        @A("delete")
        int a;
    }
}
注解概述-注解的原理

在这里插入图片描述
注解本质是一个接口,Java中所有注解都是继承了Annotation接口的。
@注解(…):其实就是一个实现类对象,实现了该注解以及Annotation接口。

注解的作用
  • 对Java中类、方法、成员变量做标记,然后进行特殊处理。
  • 例如:JUnit框架中,标记了注解@Test的方法就可以被当成测试方法执行,而没有标记的就不能当成测试方法执行

元注解

元注解是什么

指的是:注解(动词)注解(名词)的注解(名词)。

在这里插入图片描述
元注解是什么?
注解注解的注解
各自的作用是什么?
@Target约束自定义注解可以标记的范围。
@Retention用来约束自定义注解的存活范围。

//@MyTest1
public class AnnotationDemo2 {

    @MyTest1
    private int age;

//    @MyTest1
    public AnnotationDemo2(){
    }

    @MyTest1
    public static void main(String[] args) {
        // 目标:搞清楚元注解的作用。
    }
    @MyTest1
    public void getAgeTest(){
    }
}

// 元注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.FIELD}) // 表示注解的作用目标为方法,成员变量
@Retention(RetentionPolicy.RUNTIME) // 表示注解的保留策略: 编译器运行时(一直活着)
public @interface MyTest1 {  }

注解的解析

注解的解析是什么
就是判断类上、方法上、成员变量上是否存在注解,并把注解里的内容给解析出来。

如何解析注解?
  • 指导思想:要解析谁上面的注解,就应该先拿到谁。
  • 比如要解析类上面的注解,则应该先获取该类的Class对象,再通过Class对象解析其上面的注解。
  • 比如要解析成员方法上的注解,则应该获取到该成员方法的Method对象,再通过Method对象解析其上面的注解。
  • Class 、 Method 、 Field , Constructor、都实现了AnnotatedElement接口,它们都拥有解析注解的能力。
AnnotatedElement 接口提供了解析注解的方法说明
public Annotation[] getDeclaredAnnotations()获取当前对象上面的注解。
public T getDeclaredAnnotation(Class annotationClass)获取指定的注解对象
public boolean isAnnotationPresent(Class annotationClass)判断当前对象上是否存在某个注解
解析注解的案例
  1. 定义注解MyTest4,要求如
    • 包含属性:String value()
    • 包含属性:double aaa(),默认值为 100
    • 包含属性:String[] bbb()
    • 限制注解使用的位置:类和成员方法上
    • 指定注解的有效范围:一直到运行时
  2. 定义一个类叫:Demo,在类中定义一个test1方法,并在该类和其方法上使用MyTest4注解
  3. 定义AnnotationTest3测试类,解析Demo类中的全部注解。
// 第一步
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD, ElementType.TYPE}) // 表示注解的作用目标为方法,成员变量
@Retention(RetentionPolicy.RUNTIME) // 表示注解的保留策略: 编译器运行时(一直活着)
public @interface MyTest2 {
    String value();
    double height() default 169.5;
    String[] address();
}

// 第二步
@MyTest2(value = "刘亦菲", address = {"北京", "上海", "深圳"})
public class Demo {
    @MyTest2(value = "欧阳娜娜", address = {"湖南", "湖北"})
    public void go(){
    }
}

// 第三步
import org.junit.Test;
import java.lang.reflect.Method;
import java.util.Arrays;

public class AnnotationDemo3 {
    // 目标:解析注解
    @Test
    public void parseClass() throws Exception {
        // 1.获取类对象
        Class c1 = Demo.class;
        // 2、使用isAnnotationPresent判断这个类上是否陈列了注解MyTest2
        if (c1.isAnnotationPresent(MyTest2.class)) {
            // 3、获取注解对象
            MyTest2 myTest2 = (MyTest2) c1.getDeclaredAnnotation(MyTest2.class);

            // 4、获取注解属性值
            String[] address = myTest2.address();
            double height = myTest2.height();
            String value = myTest2.value();

            // 5、打印注解属性值
            System.out.println(Arrays.toString(address));
            System.out.println(height);
            System.out.println(value);
            // [北京, 上海, 深圳]
            // 169.5
            // 刘亦菲
        }
    }

    @Test
    public void parseMethod() throws Exception {
        // 1.获取类对象
        Class c1 = Demo.class;
        // 2、获取方法对象
        Method method = c1.getMethod("go");
        // 3、使用isAnnotationPresent判断这个方法上是否陈列了注解MyTest2
        if (method.isAnnotationPresent(MyTest2.class)) {
            // 4、获取注解对象
            MyTest2 myTest2 = method.getDeclaredAnnotation(MyTest2.class);

            // 5、获取注解属性值
            String[] address = myTest2.address();
            double height = myTest2.height();
            String value = myTest2.value();

            // 6、打印注解属性值
            System.out.println(Arrays.toString(address));
            System.out.println(height);
            System.out.println(value);
            // [湖南, 湖北]
            // 169.5
            // 欧阳娜娜
        }
    }
}

总结

什么是注解的解析?注解解析的步骤是啥?

方法
Annotation[] getDeclaredAnnotations()
T getDeclaredAnnotation(Class<T> annotationClass)
boolean isAnnotationPresent(Class<Annotation> annotationClass)

作用、应用场景

使用注解开发出一个简易版的Junit框架

需求

  • 定义若干个方法,只要加了MyTest注解,就会触发该方法执行。
    分析
  1. 定义一个自定义注解MyTest,只能注解方法,存活范围是一直都在。
  2. 定义若干个方法,部分方法加上@MyTest注解修饰,部分方法不加。
  3. 模拟一个junit程序,可以触发加了@MyTest注解的方法执行。
import java.lang.reflect.Method;
public class AnnotationDemo4 {
    // 目标:搞清楚注解的应用场景:模拟junit框架。有MyTest注解的方法就执行,没有的就不执行。
    public static void main(String[] args) throws Exception {
        AnnotationDemo4 ad = new AnnotationDemo4();
        // 1、获取类对象
        Class c = AnnotationDemo4.class;
        // 2、获取所有方法
        Method[] methods = c.getMethods();
        // 3、遍历所有方法,判断方法上是否有MyTest注解,有就执行,没有就不执行。
        for (Method method : methods) {
            // 4、判断方法上是否有MyTest注解
            if (method.isAnnotationPresent(MyTest.class)) {
                // 获取到这个方法的注解
                MyTest myTest = method.getDeclaredAnnotation(MyTest.class);
                int count = myTest.count();
                // 5、有就执行这个method方法
                for (int i = 0; i < count; i++) {
                    method.invoke(ad);
                }
                // test3方法执行了
                // test3方法执行了
                // test1方法执行了
                // test4方法执行了
            }
        }
    }
    // 测试方法:public 无参 无返回值
    @MyTest
    public void test1(){
        System.out.println("test1方法执行了");
    }

    public void test2(){
        System.out.println("test2方法执行了");
    }

    @MyTest(count = 2)
    public void test3(){
        System.out.println("test3方法执行了");
    }

    @MyTest
    public void test4(){
        System.out.println("test4方法执行了");
    }
}

// 注解。
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 {
    int count() default 1; // 表示注解的属性
}

动态代理(设计模式)

程序为什么需要代理?代理长什么样?

什么是代理

在这里插入图片描述
歌手自己准备场地话筒收钱
在这里插入图片描述
当歌手忙不过来时。
在这里插入图片描述
歌手自己找个代理,让代理帮自己准备场地,准备话筒,并为歌手安排行程等等。
在这里插入图片描述

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

java.lang.reflect.Proxy类:提供了为对象产生代理对象的方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
参数一:用于指定用哪个类加载器,去加载生成的代理类
参数二:指定接口,这些接口用于指定生成的代理长什么,也就是有哪些方法
参数三:用来指定生成的代理对象要干什么事情
代码
// 接口:StarService
package com.itheima.demo4proxy;
// 明星行为接口
public interface StarService {
    void sing(String name);
    String dance();
}

// Star.java
package com.itheima.demo4proxy;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Star implements StarService{
    private String name;

    @Override
    public void sing(String name) {
        System.out.println(this.name + "表演唱歌:" + name);
    }

    @Override
    public String dance() {
        System.out.println(this.name + "表演跳舞:魅力四射!" );
        return "谢谢!谢谢!";
    }
}

// ProxyUtil.java
package com.itheima.demo4proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 代理工具类:中介公司,专门负责创建代理对象并返回给别人使用
 */
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代表正在被代理的方法的参数
                        String methodName = method.getName();
                        if("sing".equals(methodName)){
                            System.out.println("准备话筒,收钱20万!");
                        }else if("dance".equals(methodName)){
                            System.out.println("准备场地,收钱100万!");
                        }
                        // 真正干活(把真正的明星对象叫过来正式干活)
                        // 找真正的明星对象来执行被代理的行为:method方法
                        Object result = method.invoke(s, args);
                        return result;
                    }
                });
        return proxy;
    }
}
package com.itheima.demo4proxy;

import java.lang.reflect.Proxy;

public class Test {
    public static void main(String[] args) {
        // 目标:创建代理对象。
        // 1、准备一个明星对象:设计明星类。
        Star star = new Star("章若楠");
        // 2、为章若楠创建一个专属与她的代理对象。
        StarService proxy = ProxyUtil.createProxy(star);
        proxy.sing("《红昭愿》");
        System.out.println(proxy.dance());
    }
}

代码运行流程

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Java提供了什么API帮我们创建代理?
Proxy的newProxyInstance方法。

newIProxyInstance方法在创建代理时,需要接几个参数,每个参数的含义是什么?

  1. 参数一:用于执行用哪个类加载器去加载生成的代理类。
  2. 参数二:用于指定代理类需要实现的接口: 明星类实现了哪些接口,代理类就实现哪些接口
  3. 参数三:用于指定代理类需要如何去代理(代理要做的事情)。

通过invokehandler的invoke方法指定代理干的事时,这个invoke会被谁调用?要接哪几个参数?
谁去调用触发谁的方法。
参数一: proxy接收到代理对象本身(暂时用处不大)
参数二: method代表正在被代理的方法
参数三: args代表正在被代理的方法的参数

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

使用代理优化用户管理类

场景
某系统有一个用户管理类,包含用户登录,删除用户,查询用户等功能,系统要求统计每个功能的执行耗时情况,以便后期观察程序性能。

需求
现在,某个初级程序员已经开发好了该模块,请观察该模块的代码,找出目前存在的问题,并对其进行改造。

package com.itheima.demo05proxy2;
/**
 *  用户业务接口
 */
public interface UserService {
    // 登录功能
    void login(String loginName,String passWord) throws Exception;
    // 删除用户
    void deleteUsers() throws Exception;
    // 查询用户,返回数组的形式。
    String[] selectUsers() throws Exception;
    String[] selectUsers2() throws Exception;
}

package com.itheima.demo05proxy2;
/**
 * 用户业务实现类(面向接口编程)
 */
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;
    }

    @Override
    public String[] selectUsers2() throws Exception {
        System.out.println("查询出了3000个用户");
        String[] names = {"张全蛋2", "李二狗2", "牛爱花2"};
        Thread.sleep(2500);

        return names;
    }
}
package com.itheima.demo05proxy2;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyUtil {
    public static <T> T createProxy(T obj) {
        T proxy = (T) Proxy.newProxyInstance(ProxyUtil.class.getClassLoader(),
                obj.getClass().getInterfaces(), new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        long start = System.currentTimeMillis(); // 记录开始时间 1970年1月1日0时0分0秒 走到此刻的总毫秒值
                        // 真正调用业务对象被代理的方法
                        Object result = method.invoke(obj, args);

                        long end = System.currentTimeMillis();
                        System.out.println(method.getName() + "方法耗时:"+(end-start)/1000.0+"秒");
                        return result;
                    }
                });
        return proxy;
    }
}
package com.itheima.demo05proxy2;

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

        userService.deleteUsers();

        String[] names = userService.selectUsers();
        System.out.println("查询到的用户是:" + Arrays.toString(names));

        String[] names2 = userService.selectUsers2();
        System.out.println("查询到的用户是:" + Arrays.toString(names2));
    }
}
您登录成功,欢迎光临本系统~
login方法耗时:1.007秒
成功删除了1万个用户~
deleteUsers方法耗时:1.506秒
查询出了3个用户
selectUsers方法耗时:0.515秒
查询到的用户是:[张全蛋, 李二狗, 牛爱花]
查询出了3000个用户
selectUsers2方法耗时:2.514秒
查询到的用户是:[张全蛋2, 李二狗2, 牛爱花2]

在这里插入图片描述

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

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

相关文章

vue vite 无法热更新问题

一、在vue页面引入组件CustomEmployeesDialog&#xff0c;修改组件CustomEmployeesDialog无法热更新 引入方式&#xff1a; import CustomEmployeesDialog from ../dialog/customEmployeesDialog.vue 目录结构&#xff1a; 最后发现是引入import时&#xff0c;路径大小写与目…

深度学习中的查全率与查准率:如何实现有效权衡

&#x1f4cc; 友情提示&#xff1a; 本文内容由银河易创AI&#xff08;https://ai.eaigx.com&#xff09;创作平台的gpt-4-turbo模型辅助生成&#xff0c;旨在提供技术参考与灵感启发。文中观点或代码示例需结合实际情况验证&#xff0c;建议读者通过官方文档或实践进一步确认…

Windows玩游戏的时候,一按字符键就显示桌面

最近打赛伯朋克 2077 的时候&#xff0c;不小心按错键了&#xff0c;导致一按字符键就显示桌面。如下&#xff1a; 一开始我以为是输入法的问题&#xff08;相信打游戏的人都知道输入法和奔跑键冲突的时候有多烦&#xff09;&#xff0c;但是后来解决半天发现并不是。在网上搜…

Gemini 2.5 Flash和Pro预览版价格以及上下文缓存的理解

Gemini 2.5 Flash和Pro预览版价格 Gemini 2.5 Flash 预览版就是 Google 的最新 AI 大模型&#xff0c;能处理巨量内容。可以免费体验&#xff0c;但有次数和功能上的限制&#xff1b;付费层级才开放全部高级功能。价格也比传统 API 略有不同&#xff0c;尤其在“思考预算”“上…

vue2 头像上传+裁剪组件封装

背景&#xff1a;最近在进行公司业务开发时&#xff0c;遇到了头像上传限制尺寸的需求&#xff0c;即限制为一寸证件照&#xff08;宽295像素&#xff0c;高413像素&#xff09;。 用到的第三方库&#xff1a; "vue-cropper": "^0.5.5" 完整组件代码&…

AI-02a5a5.神经网络-与学习相关的技巧-权重初始值

权重的初始值 在神经网络的学习中&#xff0c;权重的初始值特别重要。实际上&#xff0c;设定什么样的权重初始值&#xff0c;经常关系到神经网络的学习能否成功。 不要将权重初始值设为 0 权值衰减&#xff08;weight decay&#xff09;&#xff1a;抑制过拟合、提高泛化能…

【springcloud学习(dalston.sr1)】Eureka单个服务端的搭建(含源代码)(三)

该系列项目整体介绍及源代码请参照前面写的一篇文章【springcloud学习(dalston.sr1)】项目整体介绍&#xff08;含源代码&#xff09;&#xff08;一&#xff09; 这篇文章主要介绍单个eureka服务端的集群环境是如何搭建的。 通过前面的文章【springcloud学习(dalston.sr1)】…

Node.js数据抓取技术实战示例

Node.js常用的库有哪些呢&#xff1f;比如axios或者node-fetch用来发送HTTP请求&#xff0c;cheerio用来解析HTML&#xff0c;如果是动态网页的话可能需要puppeteer这样的无头浏览器。这些工具的组合应该能满足大部分需求。 然后&#xff0c;可能遇到的难点在哪里&#xff1f;…

windows10 安装 QT

本地环境有个qt文件&#xff0c;这里是5.14.2 打开一个cmd窗口并指定到该文件根目录下 .\qt-opensource-windows-x86-5.14.2.exe --mirror https://mirrors.ustc.edu.cn/qtproject 执行上面命令 记住是文件名&#xff0c;记住不要傻 X的直接复制&#xff0c;是你的文件名 点击…

WordPress 和 GPL – 您需要了解的一切

如果您使用 WordPress&#xff0c;GPL 对您来说应该很重要&#xff0c;您也应该了解它。查看有关 WordPress 和 GPL 的最全面指南。 您可能听说过 GPL&#xff08;通常被称为 WordPress 的权利法案&#xff09;&#xff0c;但很可能并不完全了解它。这是有道理的–这是一个复杂…

C++书本摆放 2024年信息素养大赛复赛 C++小学/初中组 算法创意实践挑战赛 真题详细解析

目录 C++书本摆放 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、运行结果 五、考点分析 六、 推荐资料 1、C++资料 2、Scratch资料 3、Python资料 C++书本摆放 2024年信息素养大赛 C++复赛真题 一、题目要求 1、编程实现 中科智慧科技…

RabbitMQ 核心概念与消息模型深度解析(一)

一、RabbitMQ 是什么 在当今分布式系统盛行的时代&#xff0c;消息队列作为一种至关重要的中间件技术&#xff0c;扮演着实现系统之间异步通信、解耦和削峰填谷等关键角色 。RabbitMQ 便是消息队列领域中的佼佼者&#xff0c;是一个开源的消息代理和队列服务器&#xff0c;基于…

论文阅读笔记——双流网络

双流网络论文 视频相比图像包含更多信息&#xff1a;运动信息、时序信息、背景信息等等。 原先处理视频的方法&#xff1a; CNN LSTM&#xff1a;CNN 抽取关键特征&#xff0c;LSTM 做时序逻辑&#xff1b;抽取视频中关键 K 帧输入 CNN 得到图片特征&#xff0c;再输入 LSTM&…

LabVIEW在电子电工教学中的应用

在电子电工教学领域&#xff0c;传统教学模式面临诸多挑战&#xff0c;如实验设备数量有限、实验过程存在安全隐患、教学内容更新滞后等。LabVIEW 作为一款功能强大的图形化编程软件&#xff0c;为解决这些问题提供了创新思路&#xff0c;在电子电工教学的多个关键环节发挥着重…

Vue3 怎么在ElMessage消息提示组件中添加自定义icon图标

1、定义icon组件代码&#xff1a; <template><svg :class"svgClass" aria-hidden"true"><use :xlink:href"iconName" :fill"color"/></svg> </template><script> export default defineComponen…

生活破破烂烂,AI 缝缝补补(附提示词)

写在前面&#xff1a;​【Fire 计算器】已上线&#xff0c;快算算财富自由要多少​ 现实不总温柔&#xff0c;愿你始终自渡。 请永远拯救自己于水火之中。 毛绒风格提示词&#xff08;供参考&#xff09;&#xff1a; 1. 逼真毛绒风 Transform this image into a hyperrealist…

张 。。 通过Token实现Loss调优prompt

词编码模型和 API LLM不匹配,采用本地模型 理性中性案例(针对中性调整比较合理) 代码解释:Qwen2模型的文本编码与生成过程 这段代码展示了如何使用Qwen2模型进行文本的编码和解码操作。 模型加载与初始化 from transformers import AutoModelForCausalLM, AutoTokenizer

JVM学习专题(一)类加载器与双亲委派

目录 1、JVM加载运行全过程梳理 2、JVM Hotspot底层 3、war包、jar包如何加载 4、类加载器 我们来查看一下getLauncher&#xff1a; 1.我们先查看getExtClassLoader() 2、再来看看getAppClassLoader(extcl) 5、双亲委派机制 1.职责明确&#xff0c;路径隔离​&#xff…

PyTorch API 9 - masked, nested, 稀疏, 存储

文章目录 torch.randomtorch.masked简介动机什么是 MaskedTensor&#xff1f; 支持的运算符一元运算符二元运算符归约操作查看与选择函数 torch.nested简介构造方法数据布局与形状支持的操作查看嵌套张量的组成元素填充张量的相互转换形状操作注意力机制 与 torch.compile 的配…

进程相关面试题20道

一、基础概念与原理 1.进程的定义及其与程序的本质区别是什么&#xff1f; 答案&#xff1a;进程是操作系统分配资源的基本单位&#xff0c;是程序在数据集合上的一次动态执行过程。核心区别&#xff1a;​ 动态性&#xff1a;程序是静态文件&#xff0c;进程是动态执行实例…