安安静静学JAVA(十四)

单元测试

导入jar包

  • junit-4.12.jar
  • hamcrest-core-1.3.jar

@Test

单元测试方法的注意事项

  • 公共
  • 无参
  • 无返回

其他注解

  • Junit4.xxxx版本

    • @Before:用来修饰方法,该方法会在每一个测试方法执行之前执行一次。
    • @After:用来修饰方法,该方法会在每一个测试方法执行之后执行一次。
    • @BeforeClass:用来静态修饰方法,该方法会在所有测试方法之前执行一次。
    • @AfterClass:用来静态修饰方法,该方法会在所有测试方法之后执行一次。
  • Junit5.xxxx版本

    • @BeforeEach:用来修饰方法,该方法会在每一个测试方法执行之前执行一次。
    • @AfterEach:用来修饰方法,该方法会在每一个测试方法执行之后执行一次。
    • @BeforeAll:用来静态修饰方法,该方法会在所有测试方法之前执行一次。
    • @AfterAll:用来静态修饰方法,该方法会在所有测试方法之后执行一次。

断言

  • 预习判断某个条件一定成立,如果条件不成立,则直接奔溃。

  • Assert

    • Assert.assertFalse(exists);
    • Assert.assertTrue(exists);
    • Assert.assertEquals方法的参数 (String message, double expected, double actual)
      message: 消息字符串
      expected: 期望值
      actual: 实际值
public class Demo03 {

    @Test
    public void test01() {

        int result = fun(5);

        // 断言
        // 断言是相等的
        /*
            assertEquals(Object expected,    // 期望
                        Object actual)       // 实际
         */
        Assert.assertEquals( 120, result);
    }

    public int fun(int num) {
        if (num == 1) {
            return 1;
        }
        return num * fun(num - 1);
    }


    // =====================================================

    @Test
    public void test02() {
        int[] arr = {10, 20, 30, 40, 50};
        boolean exists = isNumberExists(arr, 30);

        // Assert.assertFalse(exists);

        // Assert.assertTrue(exists);

        Assert.assertEquals(true, exists);
    }

    public boolean isNumberExists(int[] arr, int num) {

        for (int i : arr) {
            if (num == i) {
                return true;
            }
        }
        // 循环结束, 才能确定是不存在, 返回false
        return false;
    }



}

反射

类加载器:

  • 应用程序类加载器(Application ClassLoader)
  • 扩展类加载器(Extension ClassLoader)(- JKD1.9之后为PlatformClassLoader)
  • 启动类加载器(Bootstrap ClassLoader)
/**
 * 类加载器
 */
public class Demo01 {
    public static void main(String[] args) {
        ClassLoader loader = Demo01.class.getClassLoader();
        System.out.println(loader);
        ClassLoader parent = loader.getParent();
        System.out.println(parent);
        ClassLoader grandParent = parent.getParent();
        System.out.println(grandParent);
    }
}

- 双亲委派模型

什么是反射

  • 反射是一种机制/功能,利用该机制/功能可以在 = =程序运行= = 过程中对类进行解剖并操作类中的构造方法,成员方法,成员属性。
  • ​ 反射乃框架之灵魂
  • ​ 反射就是把Java的各种成分(字段,方法)映射成相应的Java类.

反射应用

  • 开发工具中写代码时的提示
  • 各种框架的设计

获取字节码对象

  • 类名.class
  • 对象名.getClass()
  • Class.forName(“全限定类名”)
  • 代码展示
/**
 * 获取字节码对象的3种方式
 *
 *  1. 对象.getClass()
 *  2. 类名.class
 *  3. Class.forName()
 *   "com.itheima._02reflect.demo03_field.Student"  -> 类的全限定类名/类的全路径类名
 */
public class Demo02 {
    public static void main(String[] args) throws ClassNotFoundException {

        Student s = new Student();
        Class class1 = s.getClass();

        Class class2 = Student.class;

        Class class3 = Class.forName("com.itheima._02reflect.demo03_field.Student");

        System.out.println(class1 == class2);
        System.out.println(class1 == class3);

        System.out.println(class1.hashCode());
        System.out.println(class2.hashCode());
        System.out.println(class3.hashCode());

    }
}

获取构造方法

  • Constructor
  • getConstructor(Class … ): 参数的类型

使用构造方法创建对象

  • newInstance(参数)

代码展示


/*
   Class:
        参数是确定   构造方法的参数列表是什么类型
        不确定个数
        Constructor getConstructor(Class ... a)     : 获取指定的构造方法


   Constructor:
        newInstance(Object ... objs) : 创建对象




 */
public class Demo04 {
    public static void main(String[] args) throws Exception {

        Class aClass = Class.forName("com.itheima._02reflect.demo03_field.Student");

        // Student() -> 空参构造
        Constructor c1 = aClass.getConstructor();
        System.out.println(c1);
        // 通过无参构造创建对象
        Student s1 = (Student) c1.newInstance();
        System.out.println(s1);

        // Student(String name)
        Constructor c2 = aClass.getConstructor(String.class);
        System.out.println(c2);
        Student s2 = (Student) c2.newInstance("张三");
        System.out.println(s2);

        // Student(int age)
        Constructor c3 = aClass.getConstructor(int.class);
        System.out.println(c3);
        Student s3 = (Student) c3.newInstance(23);
        System.out.println(s3);

        // Student(String name, int age)
        Constructor c4 = aClass.getConstructor(String.class, int.class);
        System.out.println(c4);
        Student s4 = (Student) c4.newInstance("高圆圆", 40);
        System.out.println(s4);

    }
}

获取成员变量

  • Field
  • getField(String) : 变量名
/*
    成员变量(Field)
    Class:
        获取指定的成员变量, 可以通过变量名, 来指定获取哪一个成员变量
        Field getField(String fieldName)
    Field:
        set()


    s.gender = "男";

 */
public class Demo01 {
    public static void main(String[] args) throws Exception {
        // 获取字节码对象
        Class class1 = Class.forName("com.itheima._02reflect.demo03_field.Student");
        // 获取空参构造
        Constructor c = class1.getConstructor();
        // 通过空参构造创建对象
        Student s = (Student) c.newInstance();

        // 获取指定的成员变量
        Field f = class1.getField("gender");
        // 修改成员变量的值, 缺: 修改的对象 ,  新值
        /*
        set(Object obj,         // 对象
            Object value)       // 新值
         */
        f.set(s, "男");

        // s.gender = "男";

        System.out.println(s);


    }
}

修改成员变量

  • set(对象, 新值)

获取成员方法

  • Method
  • getMethod(String name, Class …) : 方法名, 方法的参数类型
  • 代码展示
/*
    成员方法

    Class
        通过方法名, 和方法的参数列表来确定一个成员方法
        方法名:  通过String来确定是什么名字
        参数列表:  可变参数  Class... a
        getMethod(String methodName, Class... a)

    Method:

        public Object invoke(Object obj,     // 调用方法的对象
                     Object... args)         // 调用方法传入的参数

    s.eat()
    s.eat(实参)

 */
public class Demo01 {
    public static void main(String[] args) throws Exception {
        // 获取字节码对象
        Class class1 = Class.forName("com.itheima._02reflect.demo03_field.Student");
        // 获取空参构造
        Constructor c = class1.getConstructor();
        // 通过空参构造创建对象
        Student s = (Student) c.newInstance();

        // 获取空参的eat方法
        Method eat1 = class1.getMethod("eat");
        // 调用
        int o = (int) eat1.invoke(s);
        System.out.println(o);

        Method eat2 = class1.getMethod("eat", String.class, int.class, boolean.class);

        eat2.invoke(s, "烤冷面", 2, true);

    }
}

调用成员方法

  • invoke(对象, 参数)

暴力反射

  • getDeclaredXxx

  • setAccessible(true)

  • 代码展示

/*

    暴力反射

    获取非公共成员变量的方法 : public Field getDeclaredField(String name)


 */
public class Demo02 {
    public static void main(String[] args) throws Exception {
        // 获取字节码对象
        Class class1 = Class.forName("com.itheima._02reflect.demo03_field.Student");
        // 获取空参构造
        Constructor c = class1.getConstructor();
        // 通过空参构造创建对象
        Student s = (Student) c.newInstance();

        // 获取私有成员变量
        Field name = class1.getDeclaredField("name");
        // 去除私有权限
        name.setAccessible(true);
        // 修改
        name.set(s, "高圆圆");
        System.out.println(s);
    }
}

反射+多态+Properties

public class Apple extends Fruit {
    @Override
    public void juice() {
        System.out.println("榨出了一杯苹果汁儿!~");
    }
}
public abstract class Fruit {
    public abstract void juice();
}
public class FruitTest {
    public static void main(String[] args) throws Exception {
        // 改进2: 全限定类名通过配置文件获取
        Properties p = new Properties();
        // 通过类加载器, 直接动态获取到src路径, 从src路径下获取到指定文件
        InputStream is = FruitTest.class.getClassLoader().getResourceAsStream("fruit.properties");
        // 调用加载, load方法, 方法是用来加载输入流关联配置文件中的信息
        p.load(is);
        // 关流
        is.close();
        // 获取Properties中className对应的值
        String className = p.getProperty("className");

        // 改进1: 通过反射创建对象
        Class class1 = Class.forName(className);
        // 获取空参构造并创建对象
        Fruit f = (Fruit) class1.getConstructor().newInstance();

        juicer(f);
    }

    // 下面方法用来模拟榨汁机
    public static void juicer(Fruit f) {
        f.juice();
    }
}
//config.properties文件下的内容 用全限定类名通过配置文件获取下面内容
className=com.itheima._02reflect.test.Orange

注解

内置注解

  • @Override:描述方法的重写.
  • @Deprecated:标记过时
  • @SuppressWarnings(all):压制警告.
  • @FunctionalInterface:函数式接口

自定注解

  • @interface

    • 本质上就是一个接口,接口中可以定义变量(常量)和方法(抽象),注解中的方法叫注解属性
    • 语法: @interface 注解名{}
  • 属性

    • 属性值的范围

      • ​ 1.基本类型
      • ​ 2.String
      • ​ 3.枚举类型
      • ​ 4.注解类型
      • ​ 5.Class类型
      • ​ 6.以上类型的一维数组类型
    • 注意事项

      • 一旦注解有属性了,使用注解的时候,属性必须有值
    • 注解属性赋值
      在这里插入图片描述在这里插入图片描述

元注解

  • @Target
    在这里插入图片描述
// 元注解
@Target({ElementType.TYPE, ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
public @interface MyAnnotation {

    // 属性 -> 必须赋值
    // 可以指定默认值, 如果指定了默认值, 可以不在使用的时候赋值
    String a() default "呵呵";
    int b() default 100;


    // 如果注解中只有一个属性需要赋值, 且属性名为value, 那么可以省略  `value =`
    // 如果给数组赋值时, 数组中只有一个值, 那么可以省略大括号
    // 给数组赋值时的格式:   数组名 = {值1, 值2 ...}
    String[] value() default "哈哈";

}
// (属性名=值, 属性名=值 ... )
@MyAnnotation
public class MyClass {

    @MyAnnotation
    public int num;

    @MyAnnotation
    public MyClass() {
    }

    @MyAnnotation
    public void fun() {

    }

}

  • @Retention
    在这里插入图片描述
// 如果没有写任何保留策略, 默认保留在源文件阶段
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

    String a();
    String b();

}
public class MyClass {

    @MyAnnotation(a = "哈哈", b = "嘿嘿")
    public void fun1() {

    }

    public void fun2() {

    }

}

注解解析

在这里插入图片描述

public class Test01 {

    @Test
    public void test1() throws Exception {

        // 获取到MyClass的字节码对象
        Class class1 = MyClass.class;
        // 反射获取fun1方法
        Method fun1 = class1.getMethod("fun1");
        // 判断是否有指定的注解
        if (fun1.isAnnotationPresent(MyAnnotation.class)) {
            // 获取指定的注解
            MyAnnotation anno = fun1.getAnnotation(MyAnnotation.class);

            System.out.println(anno.a());
            System.out.println(anno.b());

        }

    }
}

MyTest

public class MyClass {
    @MyTest
    public void fun1() {
        System.out.println("fun1()");
    }
    public void fun2() {
        System.out.println("fun2()");
    }
    public void fun3() {
        System.out.println("fun3()");
    }
    @MyTest
    public void fun4() {
        System.out.println("fun4()");
    }
    public void fun5() {
        System.out.println("fun5()");
    }
}

// 指定这个注解只能使用在方法上
@Target(ElementType.METHOD)
// 指定保留策略, 为所有阶段RUNTIME
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
public class MyTestDemo {
    public static void main(String[] args) throws Exception{

        // 1. 获取MyClass的字节码对象
        Class class1 = MyClass.class;
        // 获取空参构造
        Constructor c = class1.getConstructor();
        // 创建对象
        MyClass mc = (MyClass) c.newInstance();
        // 2. 获取所有方法
        Method[] methods = class1.getMethods();
        // 3. 遍历方法, 判断是否有MyTest注解
        for (Method method : methods) {
            // 判断是否有MyTest注解
            if (method.isAnnotationPresent(MyTest.class)) {
                // 4. 如果有, 调用这个方法
                method.invoke(mc);
            }
        }
    }
}

动态代理

作用:在不改变原有类的状态下,对对象功能进行增强。

静态代理

public class Demo01 {
    public static void main(String[] args) {

        /*DemoLogin dl = new DemoLogin();
        dl.login();*/

        // 创建被代理对象
        DemoLogin dl = new DemoLogin();
        // 创建代理类对象
        LoginProxy lp = new LoginProxy();
        // 向代理类注入被代理类的对象
        lp.setDemoLogin(dl);
        // 调用方法
        lp.login();
    }
}
public class DemoLogin implements LoginAble {
    @Override
    public void login() {
        System.out.println("用户登录");
    }
}
public interface LoginAble {
    void login();
}
// 和被代理类实现同一个接口
public class LoginProxy implements LoginAble {




    private DemoLogin demoLogin;

    // 注入
    public void setDemoLogin(DemoLogin demoLogin) {
        this.demoLogin = demoLogin;
    }

    @Override
    public void login() {
        // 前置增强
        System.out.println("前置增强");
        // 原本的登录功能
        demoLogin.login();
        // 后置增强
        System.out.println("后置增强");
    }
}

动态代理

/**
 *
 *  动态代理的目标:
 *      1. 动态代理类是动态生成的类, 就需要用到某个方法来生成
 *              Proxy.newProxyInstance()
 *      2. 对需要增强的方法, 进行拦截, 重新指定方法的功能 -> 前置和后置增强
 *              InvocationHandler
 *
 *  动态代理的类, 和被代理的类, 一定实现同一个接口
 *
 */
public class Demo02 {
    public static void main(String[] args) {

        // 创建被代理对象
        UserServiceImpl userService = new UserServiceImpl();
        // 创建间接的代理对象
        TargetProxy targetProxy = new TargetProxy();
        // 注入被代理对象
        targetProxy.setTarget(userService);
        // 获取真正的动态代理类
        UserService proxy = (UserService) targetProxy.getProxy();
        // 调用方法
        proxy.add();
        proxy.delete();
        proxy.update();
        proxy.query();


    }
}

/*
    CRUD
    C: Create
    R: Retrieve
    U: Update
    D: Delete
 */
public interface UserService {

    void add();
    void delete();
    void update();
    void query();

}
public class UserServiceImpl implements UserService{
    @Override
    public void add() {
        System.out.println("添加了一个用户");
    }

    @Override
    public void delete() {
        System.out.println("删除了一个用户");
    }

    @Override
    public void update() {
        System.out.println("修改了一个用户");
    }

    @Override
    public void query() {
        System.out.println("查询了一个用户");
    }
}
/**
 * 动态代理类是JDK动态生成的类: $Proxy0
 *
 * TargetProxy可以理解为简介的代理类, 它不是真正的动态代理类, 只用于生成动态代理类
 *
 */
// TargetProxy实现InvocationHandler
public class TargetProxy implements InvocationHandler {

    // 被代理对象
    private Object target;

    // set方法注入
    public void setTarget(Object target) {
        this.target = target;
    }

    /**
     * 此方法用于返回真正的动态代理类
     *
     * newProxyInstance(ClassLoader loader,
     *                      建议使用被代理对象获取类加载器, 但是使用其他类获取类加载器也可以
    *                   Class<?>[] interfaces,
     *                      方法是用于生成真正的动态代理类, 动态代理类会实现和被代理类相同的接口
     *                      这里传入的就是被代理类实现的接口
    *                   InvocationHandler h
     *                      动态代理类和拦截和加强的功能产生联系
     *
     *
     */
    public Object getProxy() {
        // proxy是真正的动态代理类
        Object proxy = Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this);
        return proxy;
    }


    // 拦截的方法
    /*
    invoke(Object proxy,    // 真正的动态代理类
    Method method,          // 被拦截的方法, 对它进行增强
    Object[] args)          // 被拦截方法的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("前置增强了方法: " + method.getName());

        // 被拦截的方法由谁调用?? 被代理对象调用!!!
        Object o = method.invoke(target, args);

        System.out.println("后置增强");

        return o;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值