其他分享
首页 > 其他分享> > 13-JUnit单元测试、反射、注解、动态代理、JDK8新特性

13-JUnit单元测试、反射、注解、动态代理、JDK8新特性

作者:互联网

今日内容

第一章 Junit单元测试

1.1 Junit单元测试

Junit的概念

Junit的使用步骤

执行测试方法

Junit单元测试的注意事项

1.2 Junit其他注解

/**
 * Created by PengZhiLin on 2021/8/15 9:11
 */
public class TestDemo {

    @BeforeClass
    public static void bc(){
        System.out.println("@BeforeClass修饰的方法...");
    }

    @Before
    public void b(){
        System.out.println("@Before修饰的方法...");
    }

    @Test
    public void test1(){
        System.out.println("测试方法test1...");
    }

    @Test
    public void test2(){
        System.out.println("测试方法test2...");
    }

    @After
    public void a(){
        System.out.println("@After修饰的方法...");
    }

    @AfterClass
    public static void ac(){
        System.out.println("@AfterClass修饰的方法...");
    }

}

1.3 Junit断言

第二章 反射

2.1 类加载器

类的加载

image-20210815095014594

类的加载时机

  1. 创建类的实例。

  2. 类的静态变量,或者为静态变量赋值。

  3. 类的静态方法。

  4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。

  5. 初始化某个类的子类对象。

  6. 直接使用java.exe命令来运行某个主类。

    以上六种情况的任何一种,都可以导致JVM将一个类加载到方法区。

    /**
     * Created by PengZhiLin on 2021/8/15 10:11
     */
    class Student extends Person {
    
    }
    
    public class Test {
        public static void main(String[] args) throws Exception {
            //1. 创建类的实例。
            //new Person();
    
            //2. 类的静态变量,或者为静态变量赋值。
            //System.out.println(Person.num);
    
            //3. 类的静态方法。
            //Person.method();
    
            //4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
            //Class.forName("com.itheima.demo4_类加载时机.Person");
    
            //5. 初始化某个类的子类对象。
            new Student();
    
            //6. 直接使用java.exe命令来运行某个主类。
            //
            //   以上六种情况的任何一种,都可以导致JVM将一个类加载到方法区。
        }
    }
    
    
    /**
     * Created by PengZhiLin on 2021/8/15 10:11
     */
    public class Person {
        static {
            System.out.println("Person 静态代码块...");
        }
    
        static int num = 10;
    
        public static void method(){
            System.out.println("静态method...");
        }
    }
    
    

类加载器

类加载器:是负责将磁盘上的某个class文件读取到内存并生成Class的对象。

/**
 * Created by PengZhiLin on 2021/8/15 10:16
 */
public class Test {
    public static void main(String[] args) {
        // 需求:获取Person类的类加载器
        // 1.获取Person类的字节码对象
        // 2.使用字节码对象调用getClassLoader()方法
        ClassLoader classLoader = Person.class.getClassLoader();
        System.out.println("Person类的类加载器:"+classLoader);

        // 需求:获取String类的类加载器
        ClassLoader classLoader1 = String.class.getClassLoader();
        System.out.println("String类的类加载器:"+classLoader1);
        // API中说明:一些实现可能使用null来表示引导类加载器。 如果此类由引导类加载器加载,则此方法将在此类实现中返回null
    }
}

2.2 扩展类加载器结合Properties的使用


/**
 * Created by PengZhiLin on 2021/8/15 10:27
 */
public class Utils {

    public static String value1;
    public static String value2;
    public static String value3;
    public static String value4;

    static {
        try {
            // 1.创建Properties对象
            Properties pro = new Properties();

            // 2.调用load方法加载配置文件中的键值对
            //pro.load(new FileInputStream("day13\\src\\db.properties"));
            // 这种方式获得的流,路径已经到了src路径
            InputStream is = Utils.class.getClassLoader().getResourceAsStream("db.properties");
            pro.load(is);

            // 3.获取数据
            value1 = pro.getProperty("k1");
            value2 = pro.getProperty("k2");
            value3 = pro.getProperty("k3");
            value4 = pro.getProperty("k4");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

/**
 * Created by PengZhiLin on 2021/8/15 10:23
 */
public class Test {
    public static void main(String[] args) throws Exception{
        System.out.println(Utils.value1);
        System.out.println(Utils.value2);
        System.out.println(Utils.value3);
        System.out.println(Utils.value4);
    }
}

2.3 反射的概述

反射的引入

 通过反射技术对类进行了解剖得到了类的所有成员。

反射的概念

 反射是一种机制,利用该机制可以在程序运行过程中对类进行解剖并操作类中的所有成员(成员变量,成员方法,构造方法)

使用反射操作类成员的前提

要获得该类字节码文件对象,就是Class对象

反射在实际开发中的应用

* 开发IDE(集成开发环境),比如IDEA,Eclipse
* 各种框架的设计和学习 比如Spring,Hibernate,Struct,Mybaits....

2.4 获取Class对象

2.5 Class类常用方法

String getSimpleName(); 获得类名字符串:类名
String getName();  获得类全名:包名+类名
T newInstance() ;  创建Class对象关联类的对象---相当于调用该类的空参构造方法

2.6 反射之操作构造方法

Constructor类概述

Constructor类概述
    * 类中的每一个构造方法都是一个Constructor类的对象

通过反射获取类的构造方法

Class类的方法 
1. Constructor getConstructor(Class... parameterTypes)
        * 根据参数类型获得对应的Constructor对象。
        * 只能获得public修饰的构造方法
 2. Constructor getDeclaredConstructor(Class... parameterTypes)---推荐
        * 根据参数类型获得对应的Constructor对象
    	* 可以是public、protected、(默认)、private修饰符的构造方法。
 3. Constructor[] getConstructors()
        获得类中的所有构造方法对象,只能获得public的
 4. Constructor[] getDeclaredConstructors()---推荐
        获得类中的所有构造方法对象
    	可以是public、protected、(默认)、private修饰符的构造方法。
/**
 * Created by PengZhiLin on 2021/8/15 10:58
 */
public class Person {

    String name;
    int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person(String name) {
        this.name = name;
    }

    private Person(int age) {
        this.age = age;
    }
}

/**
 * Created by PengZhiLin on 2021/8/15 10:58
 */
public class Test {
    public static void main(String[] args) throws Exception {
        // 获取Person类的字节码对象
        Class<Person> c = Person.class;

        // 需求:获取Person类的构造方法
        // 获取第1个构造方法
        Constructor<Person> con1 = c.getDeclaredConstructor();
        System.out.println("con1:" + con1);

        // 获取第2个构造方法
        Constructor<Person> con2 = c.getDeclaredConstructor(String.class, int.class);
        System.out.println("con2:" + con2);

        // 获取第3个构造方法
        Constructor<Person> con3 = c.getDeclaredConstructor(String.class);
        System.out.println("con3:" + con3);

        // 获取第4个构造方法
        Constructor<Person> con4 = c.getDeclaredConstructor(int.class);
        System.out.println("con4:" + con4);

        // 需求:获取所有的构造方法
        Constructor<?>[] arr = c.getDeclaredConstructors();
        for (Constructor<?> con : arr) {
            System.out.println(con);
        }
    }
}

通过反射执行构造方法

Constructor对象常用方法
1. T newInstance(Object... initargs)
 	根据指定的参数创建对象
    参数:被执行的构造方法需要的实际参数
2. void setAccessible(true)
   设置"暴力反射"——是否取消权限检查,true取消权限检查,false表示不取消

示例代码

/**
 * Created by PengZhiLin on 2021/8/15 10:58
 */
public class Test {
    public static void main(String[] args) throws Exception {
        // 获取Person类的字节码对象
        Class<Person> c = Person.class;

        // 需求:获取Person类的构造方法
        // 获取第1个构造方法
        Constructor<Person> con1 = c.getDeclaredConstructor();
        System.out.println("con1:" + con1);

        // 获取第2个构造方法
        Constructor<Person> con2 = c.getDeclaredConstructor(String.class, int.class);
        System.out.println("con2:" + con2);

        // 获取第3个构造方法
        Constructor<Person> con3 = c.getDeclaredConstructor(String.class);
        System.out.println("con3:" + con3);

        // 获取第4个构造方法
        Constructor<Person> con4 = c.getDeclaredConstructor(int.class);
        System.out.println("con4:" + con4);

        // 需求:获取所有的构造方法
        Constructor<?>[] arr = c.getDeclaredConstructors();
        for (Constructor<?> con : arr) {
            System.out.println(con);
        }

        // 通过反射执行构造方法
        // 执行第1个构造方法
        Person p1 = con1.newInstance();
        System.out.println("p1:" + p1);

        // 执行第2个构造方法
        Person p2 = con2.newInstance("黑马程序员", 18);
        System.out.println("p2:" + p2);

        // 执行第3个构造方法
        Person p3 = con3.newInstance("传智播客");
        System.out.println("p3:" + p3);

        // 执行第4个构造方法
        con4.setAccessible(true);// 取消权限检查
        Person p4 = con4.newInstance(19);
        System.out.println("p4:" + p4);
    }
}

2.7 反射之操作成员方法

Method类概述

Method类概述
    * 每一个成员方法都是一个Method类的对象。

通过反射获取类的成员方法

Class类中与Method相关的方法
* Method getMethod(String name,Class... args);
    * 根据方法名和参数类型获得对应的成员方法对象,只能获得public的

* Method getDeclaredMethod(String name,Class... args);----->推荐
    * 根据方法名和参数类型获得对应的成员方法对象,包括public、protected、(默认)、private的
      参数1:要获取的方法的方法名
      参数2:要获取的方法的形参类型的Class对象

* Method[] getMethods();
    * 获得类中的所有成员方法对象,返回数组,只能获得public修饰的且包含父类的

* Method[] getDeclaredMethods();----->推荐
    * 获得类中的所有成员方法对象,返回数组,只获得本类的,包括public、protected、(默认)、private的
/**
 * Created by PengZhiLin on 2021/8/15 11:17
 */
public class Person {

    public void method1() {
        System.out.println("无参数无返回值的成员方法...");
    }

    public void method2(int num) {
        System.out.println("有参数无返回值的成员方法...,参数num的值:" + num);
    }

    public int method3() {
        System.out.println("无参数有返回值的成员方法...");
        return 100;
    }

    private int method4(String name, int age) {
        System.out.println("有参数有返回值的成员方法...参数name的值:" + name + "参数age的值:" + age);
        return 200;
    }
}


/**
 * Created by PengZhiLin on 2021/8/15 11:17
 */
public class Test {
    public static void main(String[] args) throws Exception{
        // 获取Person类的字节码对象
        Class<Person> c = Person.class;

        // 需求: 获取成员方法对象
        // 获得第1个成员方法
        Method m1 = c.getDeclaredMethod("method1");

        // 获得第2个成员方法
        Method m2 = c.getDeclaredMethod("method2", int.class);

        // 获得第3个成员方法
        Method m3 = c.getDeclaredMethod("method3");

        // 获得第4个成员方法
        Method m4 = c.getDeclaredMethod("method4", String.class, int.class);
        System.out.println(m1);
        System.out.println(m2);
        System.out.println(m3);
        System.out.println(m4);

        // 需求:获取所有成员方法对象
        Method[] arr = c.getDeclaredMethods();
        for (Method method : arr) {
            System.out.println(method);
        }
    }
}

通过反射执行成员方法

Method对象常用方法
*  Object invoke(Object obj, Object... args)
    * 参数1:调用该方法的对象
    * 参数2:调用该法时传递的实际参数
      返回值:该方法执行完毕后的返回值
          
*  void setAccessible(true)
    设置"暴力访问"——是否取消权限检查,true取消权限检查,false表示不取消

示例代码

/**
 * Created by PengZhiLin on 2021/8/15 11:17
 */
public class Test {
    public static void main(String[] args) throws Exception {
        // 获取Person类的字节码对象
        Class<Person> c = Person.class;

        // 需求: 获取成员方法对象
        // 获得第1个成员方法
        Method m1 = c.getDeclaredMethod("method1");

        // 获得第2个成员方法
        Method m2 = c.getDeclaredMethod("method2", int.class);

        // 获得第3个成员方法
        Method m3 = c.getDeclaredMethod("method3");

        // 获得第4个成员方法
        Method m4 = c.getDeclaredMethod("method4", String.class, int.class);
        System.out.println(m1);
        System.out.println(m2);
        System.out.println(m3);
        System.out.println(m4);

        // 需求:获取所有成员方法对象
        Method[] arr = c.getDeclaredMethods();
        for (Method method : arr) {
            System.out.println(method);
        }
        System.out.println("--------------");

        // 通过反射执行成员方法
        // 通过反射创建Person对象
        Person p = c.newInstance();
        //Person p = c.getDeclaredConstructor().newInstance();

        // 通过反射执行第1个成员方法
        m1.invoke(p);// 等价于:p.method1();

        // 通过反射执行第2个成员方法
        m2.invoke(p, 10);// 等价于:p.method2(10);

        // 通过反射执行第3个成员方法
        Object res1 = m3.invoke(p);// 等价于: Object res1 = p.method3();
        System.out.println("res1:" + res1);

        // 通过反射执行第4个成员方法
        m4.setAccessible(true);// 取消权限检查
        Object res4 = m4.invoke(p, "张三", 19);
        System.out.println("res4:"+res4);// 等价于: Object res2 = p.method4("张三", 19);
    }
}

2.8 反射之操作成员变量【自学】

Field类概述

Field类概述
    * 每一个成员变量都是一个Field类的对象。

通过反射获取类的成员变量

Class类中的方法
* Field getField(String name);
    *  根据成员变量名获得对应Field对象,只能获得public修饰
        参数:属性名
* Field getDeclaredField(String name);----->推荐
    *  根据成员变量名获得对应Field对象,包括public、protected、(默认)、private的
        参数:属性名
            
* Field[] getFields();
    * 获得所有的成员变量对应的Field对象,只能获得public的
* Field[] getDeclaredFields();---->推荐
    * 获得所有的成员变量对应的Field对象,包括public、protected、(默认)、private的
/**
 * Created by PengZhiLin on 2021/8/15 11:50
 */
public class Person {
    public String name;
    private int age;
}

/**
 * Created by PengZhiLin on 2021/8/15 11:52
 */
public class Test {
    public static void main(String[] args)throws Exception {
        // 获得Person类的字节码对象
        Class<Person> c = Person.class;

        // 通过反射获取类的成员变量
        // 获得第1个成员变量
        Field f1 = c.getDeclaredField("name");

        // 获得第2个成员变量
        Field f2 = c.getDeclaredField("age");
        System.out.println(f1);
        System.out.println(f2);

        // 获取所有的成员变量
        Field[] arr = c.getDeclaredFields();
        for (Field field : arr) {
            System.out.println(field);
        }

    }
}

通过反射访问成员变量

Field对象常用方法
给对象的属性赋值的方法
	void  set(Object obj, Object value) ----->推荐
    参数1: 给哪个对象的属性赋值---该类的对象
    参数2: 给属性赋的值
        
    void setInt(Object obj, int i) 	
    void setLong(Object obj, long l)
    void setBoolean(Object obj, boolean z) 
    void setDouble(Object obj, double d) 
    ...
获取对象属性的值的方法
    Object get(Object obj)  ----->推荐
    int	getInt(Object obj) 
    long getLong(Object obj) 
    boolean getBoolean(Object ob)
    double getDouble(Object obj) 
    ...

void setAccessible(true);暴力反射,设置为可以直接访问私有类型的属性。 ----->推荐
Class getType(); 获取属性的类型,返回Class对象。

setXxx方法都是给对象obj的属性设置使用,针对不同的类型选取不同的方法。

getXxx方法是获取对象obj对应的属性值的,针对不同的类型选取不同的方法。

示例代码

/**
 * Created by PengZhiLin on 2021/8/15 11:52
 */
public class Test {
    public static void main(String[] args) throws Exception {
        // 获得Person类的字节码对象
        Class<Person> c = Person.class;

        // 通过反射获取类的成员变量
        // 获得第1个成员变量
        Field f1 = c.getDeclaredField("name");

        // 获得第2个成员变量
        Field f2 = c.getDeclaredField("age");
        System.out.println(f1);
        System.out.println(f2);

        // 获取所有的成员变量
        Field[] arr = c.getDeclaredFields();
        for (Field field : arr) {
            System.out.println(field);
        }

        // 通过反射得到Person类的对象
        Person p = c.newInstance();

        // 通过反射访问成员变量
        // 访问第1个成员变量
        f1.set(p, "张三");
        System.out.println("p对象的name属性值:" + f1.get(p));

        // 访问第2个成员变量
        f2.setAccessible(true);// 取消权限检查
        f2.set(p,18);
        System.out.println("p对象的age属性值:" + f2.get(p));
        
        // 获取成员变量的类型
        System.out.println(f1.getType());
        System.out.println(f2.getType());
    }
}

第三章 注解

3.1 注解概述

注解概述

注解的作用

3.2 JDK提供的三个基本的注解

​ @Override:描述方法的重写.

​ @SuppressWarnings:压制\忽略警告.

​ @Deprecated:标记过时

/**
 * Created by PengZhiLin on 2021/8/15 12:07
 */
public class Test {
    public static void main(String[] args) {
        @SuppressWarnings("all")
        int num = 10;
        
        method1();
    }
    
    @Override
    public String toString() {
        return super.toString();
    }
        
    @Deprecated
    public static void method1(){
        
    }
}

3.3 自定义注解

}


- 注解属性
- 格式: `数据类型 属性名();`

- 属性类型

  ​	1.基本类型

  ​	2.String

  ​	3.Class类型 

  ​	4.注解类型

  ​	5. 枚举类型

  ​	6.以上类型的一维数组类型  

- 示例代码

```java
// 有属性的注解
public @interface MyAnnotation2 {
  //​	1.基本类型(4类8种)
    int a();
    byte b();

    //​	2.String
    String str();

    //​	3.Class类型
    Class c();

    //​	4.注解类型
    MyAnnotation1 ma();

    //​	5. 枚举类型
    Sex sex();
        
    //​	6.以上类型的一维数组类型
    int[] arr1();
    String[] arr2();
    Class[] arr3();
    MyAnnotation1[] arr4();
    Sex[] arr5();
    
}


/**
 * Created by PengZhiLin on 2021/8/15 12:22
 */
// 无属性的注解
public @interface MyAnnotation1 {
}

3.4 使用注解

如果一个注解没有属性,那么就不需要给注解属性赋值,直接使用即可     @注解名
如果一个注解中有属性,那么使用注解的时候一定要给注解所有属性赋值   @注解名(属性名=值,属性名2=值2,...) 
如何给注解属性赋值:
        @注解名(属性名=值,属性名2=值2,...) 

3.5 使用注解的注意事项

3.6 元注解

什么是元注解

​ 定义在注解上的注解(修饰注解的注解)

常见的元注解

​ @Target:表示该注解作用在什么上面(位置),默认注解可以在任何位置. 值为:ElementType的枚举值

​ METHOD:方法

​ TYPE:类 接口

​ FIELD:字段

​ CONSTRUCTOR:构造方法声明

​ LOCAL_VARIABLE:局部变量

​ ....

/**
 * Created by PengZhiLin on 2021/8/15 14:49
 */
@Target(value={ElementType.TYPE,ElementType.METHOD})
public @interface MyAnnotation1 {
}

​ @Retention:定义该注解保留到那个代码阶段, 值为:RetentionPolicy类型,默认只在源码阶段保留

​ SOURCE:只在源码上保留(默认)

​ CLASS:在源码和字节码上保留

​ RUNTIME:在所有的阶段都保留

.java (源码阶段) ----编译---> .class(字节码阶段) ----加载内存--> 运行(RUNTIME)

案例:

//@Retention(value= RetentionPolicy.SOURCE) // 该注解只能保留到源码阶段
//@Retention(value= RetentionPolicy.CLASS) // 该注解只能保留到字节码阶段
@Retention(value= RetentionPolicy.RUNTIME) // 该注解只能保留到运行阶段
public @interface MyAnnotation2 {
}

3.7 注解解析

java.lang.reflect.AnnotatedElement接口: Class、Method、Field、Constructor等实现了AnnotatedElement

// 有属性的注解
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String name();
    int age();
}

/**
 * Created by PengZhiLin on 2021/8/15 14:59
 */
public class Test {
    public static void main(String[] args) throws Exception {
        // 需求:获取method1方法上注解的属性值
        // 1.获取method1方法上的注解
        // 1.1 获取Test类的字节码对象
        Class<Test> c = Test.class;

        // 1.2 获取Test类中method1方法对应的Method对象
        Method m1 = c.getDeclaredMethod("method1");

        // 1.3 根据Method对象调用getAnnotation方法获得注解对象
        MyAnnotation annotation = m1.getAnnotation(MyAnnotation.class);

        // 2.根据method1方法上的注解获取属性值
        System.out.println(annotation.name());
        System.out.println(annotation.age());

        // 需求: 执行Test类中含有MyAnnotation注解的所有方法
        // 1.获取Test类中所有的方法
        Method[] arr = c.getDeclaredMethods();

        // 2.通过反射创建Test对象
        Test test = c.newInstance();

        // 3.循环遍历所有的方法
        for (Method m : arr) {
            // 3.判断遍历出来的方法上是否有MyAnnotation注解
            boolean flag = m.isAnnotationPresent(MyAnnotation.class);

            // 4.如果有,就执行
            if (flag){
                m.invoke(test);
            }
        }
    }

    @MyAnnotation(name = "itheima", age = 100)
    public void method1() {
        System.out.println("method1方法....");
    }

    @MyAnnotation(name = "itheima", age = 100)
    public void method2() {
        System.out.println("method2方法....");
    }

    public void method3() {
        System.out.println("method3方法....");
    }
}

3.8 完成注解的MyTest案例

需求

​ 在一个类(测试类,TestDemo)中有三个方法,其中两个方法上有@MyTest,另一个没有.还有一个主测试类(MainTest)中有一个main方法. 在main方法中,让TestDemo类中含有@MyTest方法执行. 自定义@MyTest, 模拟单元测试.

思路分析

  1. 定义两个类和一个注解

  2. 在MainTest的main()方法里面:

    //1.获得TestDemo字节码对象
    //2.反射获得TestDemo里面的所有的方法
    //3.遍历方法对象的数组. 判断是否有@MyTest(isAnnotationPresent)
    //4.有就执行(method.invoke())

代码实现

/**
 * Created by PengZhiLin on 2021/8/15 15:16
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}

/**
 * Created by PengZhiLin on 2021/8/15 15:16
 */
public class TestDemo {

    @MyTest
    public void test1(){
        System.out.println("测试方法test1...");
    }

    public void test2(){
        System.out.println("测试方法test2...");
    }

    @MyTest
    public void test3(){
        System.out.println("测试方法test3...");
    }
}


/**
 * Created by PengZhiLin on 2021/8/15 15:17
 */
public class MainTest {
    public static void main(String[] args) throws Exception{
        // 1.获得TestDemo类的字节码对象
        Class<TestDemo> c = TestDemo.class;

        // 2.获得TestDemo类中的所有方法
        Method[] arr = c.getDeclaredMethods();

        // 通过反射创建TestDemo对象
        TestDemo td = c.newInstance();

        // 3.循环遍历所有的方法
        for (Method m : arr) {

            // 4.判断方法上是否有@MyTest注解
            boolean flag = m.isAnnotationPresent(MyTest.class);

            // 5.如果就,就执行该方法
            if (flag){
                m.invoke(td);
            }
        }


    }
}

第四章 动态代理

代理模式概述

为什么要有“代理”?生活中就有很多代理的例子,例如,我现在需要出国,但是我不愿意自己去办签证、预定机票和酒店(觉得麻烦 ,那么就可以找旅行社去帮我办,这时候旅行社就是代理,而我自己就是被代理了。

代理模式的定义:被代理者没有能力或者不愿意去完成某件事情,那么就需要找个人代替自己去完成这件事,这个人就是代理者, 所以代理模式包含了3个角色: 被代理角色 代理角色 抽象角色(协议)

静态代理:

/**
 * Created by PengZhiLin on 2021/8/15 15:46
 */
public interface FindHappy {
    void happy();
}



/**
 * Created by PengZhiLin on 2021/8/15 15:46
 */
public class JinLian implements FindHappy {
    @Override
    public void happy() {
        System.out.println("金莲正在happy...");
    }
}

/**
 * Created by PengZhiLin on 2021/8/15 15:46
 */
public class YanPoXi implements FindHappy {
    @Override
    public void happy() {
        System.out.println("阎婆惜正在happy...");
    }
}


/**
 * Created by PengZhiLin on 2021/8/15 15:48
 */
public class WangPo implements FindHappy {

    FindHappy findHappy;

    public WangPo(FindHappy findHappy) {
        this.findHappy = findHappy;
    }

    @Override
    public void happy() {
        System.out.println("王婆以做衣服的名义开好房间...");
        findHappy.happy();
        System.out.println("王婆打扫战场...");
    }
}

/**
 * Created by PengZhiLin on 2021/8/15 15:46
 */
public class Test {
    public static void main(String[] args) {
        // 创建金莲对象---被代理者
        JinLian jinLian = new JinLian();
        //jinLian.happy();

        // 创建王婆对象---代理者
        WangPo wp = new WangPo(jinLian);
        wp.happy();

        System.out.println("---------------");
        // 创建阎婆惜对象---被代理者
        YanPoXi ypx = new YanPoXi();

        // 创建王婆对象---代理者
        WangPo wp1 = new WangPo(ypx);
        wp1.happy();
    }
}

动态代理介绍

动态代理相关api介绍

Java.lang.reflect.Proxy类可以直接生成一个代理对象

案例演示

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

/**
 * Created by PengZhiLin on 2021/8/15 15:59
 */
public class Test {
    public static void main(String[] args) {
        // 1.创建金莲对象
        JinLian jinLian = new JinLian();

        // 程序运行到这里,直接通到反射生成一个代理对象
        // 2.获得被代理类的类加载器
        ClassLoader classLoader = jinLian.getClass().getClassLoader();
        // 3.获得被代理类实现的所有接口的Class对象
        Class<?>[] interfaces = jinLian.getClass().getInterfaces();
        // 4.处理接口---用来监听代理对象的
        InvocationHandler iv = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 只要代理对象调用方法,就会来到这里执行
                // 参数1: 代理对象(慎用)
                // 参数2: 代理对象调用的方法
                // 参数3: 代理对象调用方法时传递的实际参数
                // 返回值: 代理对象调用的方法的返回值

                if (method.getName().equals("happy")){
                    System.out.println("王婆以做衣服的名义开好房间...");
                    //jinLian.happy();
                    method.invoke(jinLian,args);
                    System.out.println("王婆打扫战场...");
                }

                if (method.getName().equals("toString")){
                    System.out.println("toString...");
                }
                return null;
            }
        };

        // 5.动态生成一个代理对象
        FindHappy proxy = (FindHappy)Proxy.newProxyInstance(classLoader,interfaces,iv);

        // 6.使用代理对象
        proxy.happy();
        System.out.println("-----------");
        proxy.toString();

    }
}

第五章JDK8新特性

5.1 方法引用

方法引用概述

方法引用基本使用

/**
 * Created by PengZhiLin on 2021/8/15 16:37
 */
public class Test {

    public static void printStr(){
        System.out.println("Hello World...");
        System.out.println("Hello World...");
        System.out.println("Hello World...");
    }

    public static void main(String[] args) {
        // 创建并启动线程
       /* new Thread(()->{
            System.out.println("Hello World...");
            System.out.println("Hello World...");
            System.out.println("Hello World...");
        }).start();*/

        /*new Thread(()->{
            Test.printStr();
        }).start();*/

        new Thread(Test::printStr).start();

    }
}

5.2 方法引用的分类

构造方法引用

/**
 * Created by PengZhiLin on 2021/8/15 16:43
 */
public class Test1_引用构造方法 {
    public static void main(String[] args) {
         /*
            引入构造方法的格式: 类名::new
         */
        //创建集合
        ArrayList<String> list = new ArrayList<>();
        list.add("杨紫");
        list.add("迪丽热巴");
        list.add("陈钰琪");

        // 需求: 使用Stream流把集合中的元素转换为Person对象,打印输出
        //list.stream().map((String name)->{return new Person(name);}).forEach(p->System.out.println(p));
        // map方法中的Lambda表达式大括号中就是调用Person类的构造方法,可以使用方法引用
        list.stream().map(Person::new).forEach(p->System.out.println(p));
    }
}

静态方法引用

/**
 * Created by PengZhiLin on 2021/8/15 16:50
 */
public class Test2_引用静态方法 {
    public static void main(String[] args) {
          /*
            引用静态方法的格式: 类名::方法名
         */
        //创建集合
        ArrayList<String> list = new ArrayList<>();
        list.add("110");
        list.add("111");
        list.add("112");

        // 需求: 使用Stream流把集合中的元素转换为int值,打印输出
        //list.stream().map((String str)->{return Integer.parseInt(str);}).forEach(i-> System.out.println(i+1));
        // map方法的Lambda表达式大括号中就是调用Integer的parseInt方法,可以使用方法引用
        list.stream().map(Integer::parseInt).forEach(i-> System.out.println(i+1));
    }
}

有参数成员方法引用

没有参数成员方法引用

5.3 Base64

Base64概述

Base64编码和解码的相关方法

案例演示

public class Test1 {
    public static void main(String[] args) {
        // 使用基本型的编码器和解码器对数据进行编码和解码:
        // 1.获取编码器
        Base64.Encoder encoder = Base64.getEncoder();

        // 2.对字符串进行编码
        String str = "name=中国?password=123456";
        String str1 = encoder.encodeToString(str.getBytes());

        // 3.打印输出编码后的字符串
        System.out.println("编码后的字符串:"+str1);

        // 4.获取解码器
        Base64.Decoder decoder = Base64.getDecoder();

        // 5.对编码后的字符串进行解码
        byte[] bys = decoder.decode(str1);
        String str2 = new String(bys);

        // 6.打印输出解码后的字符串
        System.out.println("解码后的字符串:"+str2);
    }
}
public class Test2 {
    public static void main(String[] args) {

        // 使用URL型的编码器和解码器对数据进行编码和解码:
        // 1.获取编码器
        Base64.Encoder encoder = Base64.getUrlEncoder();

        // 2.对字符串进行编码
        String str = "name=中国?password=123456";
        String str1 = encoder.encodeToString(str.getBytes());

        // 3.打印输出编码后的字符串
        System.out.println("编码后的字符串:"+str1);

        // 4.获取解码器
        Base64.Decoder decoder = Base64.getUrlDecoder();

        // 5.对编码后的字符串进行解码
        byte[] bys = decoder.decode(str1);
        String str2 = new String(bys);

        // 6.打印输出解码后的字符串
        System.out.println("解码后的字符串:"+str2);
    }
}

public class Test3 {
    public static void main(String[] args) {
        // 使用MIME型的编码器和解码器对数据进行编码和解码:
        // 1.获取编码器
        Base64.Encoder encoder = Base64.getMimeEncoder();

        // 2.对字符串进行编码
        String str = "";
        for (int i = 0; i < 100; i++) {
            str += i;
        }
        System.out.println("编码前的字符串:"+str);

        String str1 = encoder.encodeToString(str.getBytes());

        // 3.打印输出编码后的字符串
        System.out.println("编码后的字符串:"+str1);

        // 4.获取解码器
        Base64.Decoder decoder = Base64.getMimeDecoder();

        // 5.对编码后的字符串进行解码
        byte[] bys = decoder.decode(str1);
        String str2 = new String(bys);

        // 6.打印输出解码后的字符串
        System.out.println("解码后的字符串:"+str2);
    }
}

总结

必须练习:
	1.Junit必须掌握--->@Test,@Before,@After,@BeforeClass,@AfterClass
	2.反射获取构造方法\成员方法\成员变量,并使用--->必须掌握,特别反射操作成员方法
    3.使用注解(带有属性\不带有属性)=-->必须掌握
    4.动态代理---->难点\重点\必须掌握
    5.建议方法引用理解,Base64
     
        
- 能够使用Junit进行单元测试
    1.下载Junit的jar包
	2.把Junit的jar包拷贝到模块下的lib文件夹中,并添加到classpath路径中
	3.编写测试方法
	4.在测试方法上书写@Test注解
	5.运行测试
        
- 能够通过反射技术获取Class字节码对象
      1.1 类名.class
	  1.2 对象名.getClass();
	  1.3 Class.forName("类的全路径");

- 能够通过反射技术获取构造方法对象,并创建对象。
    Constructor getDeclaredConstructor(Class... parameterTypes)
    Constructor[] getDeclaredConstructors()	
    T newInstance(Object... initargs)
    void setAccessible(true);暴力反射
    
- 能够通过反射获取成员方法对象,并且调用方法。--------特别
    	 Method getDeclaredMethod(String name,Class...args);
		 Method[] getDeclaredMethods();		 
		 Object invoke(Object obj, Object... args)
         void setAccessible(true);暴力反射   
             
- 能够通过反射获取属性对象,并且能够给对象的属性赋值和取值。
      Field getDeclaredField(String name);
		Field[] getDeclaredFields();
		void setAccessible(true);暴力反射
		void  set(Object obj, Object value)
		Object get(Object obj)
            
- 能够说出注解的作用 
   作为配置\编译检查
            
- 能够自定义注解和使用注解
    格式:
		public @interface 注解名{
			属性
		}
	属性格式:
		数据类型 属性名();
		
	数据类型:
		1.基本类型
		2.String类型
		3.Class类型
		4.枚举类型
		5.注解类型
		6.以上类型的一维数组类型

    使用注解:
		无属性的注解: @注解名
        有属性的注解: @注解名(属性名=属性值,...)
    注意事项:
		1.如果注解属性是数组类型,并且数组的值只有一个,那么大括号可以省略
        2.如果注解只有一个属性,并且属性名为value,那么给属性赋值的时候value属性名可以省略
        3.注解属性有默认值,可以不给注解属性赋值  格式: 数据类型 注解名() default 属性值;

- 能够说出常用的元注解及其作用
      @Target:表示该注解作用在什么上面(位置),默认注解可以在任何位置
      @Retention:定义该注解保留到那个代码阶段, 值为:RetentionPolicy类型,==默认只在源码阶段保留==
          
- 能够解析注解并获取注解中的数据
java.lang.reflect.AnnotatedElement接口: Class、Method、Field、Constructor等实现了该接口
     - T getAnnotation(Class<T> annotationType):得到指定类型的注解引用。没有返回null。
     - boolean isAnnotationPresent(Class<?extends Annotation> annotationType):
                        判断指定的注解有没有。  
    获取注解的属性值:  注解对象.属性名();

- 能够完成注解的MyTest案例
   1.获取类的字节码对象
   2.获取该类的所有方法
   3.循环遍历所有的方法
   4.判断遍历出来的方法是否包含指定的注解,如果包含,就执行该方法
    
- 能够说出动态代理模式的作用
    作用: 为了增强被代理类的方法
        
- 能够使用Proxy的方法生成代理对象
Java.lang.reflect.Proxy类可以直接生成一个代理对象
 - public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)生成一个代理对象
                    - 参数1:ClassLoader loader 被代理对象的类加载器
                    - 参数2:Class<?>[] interfaces 被代理对象的要实现的接口
                    - 参数3:InvocationHandler h (接口)执行处理类
                    - 返回值: 代理对象
                    - 前2个参数是为了帮助在jvm内部生成被代理对象的代理对象,第3个参数,用来监听代理对象调用方法,帮助我们调用方法

- InvocationHandler中的Object invoke(Object proxy, Method method, Object[] args)方法:调用代理类的任何方法,此方法都会执行
                  - 参数1:代理对象(慎用)
                  - 参数2:当前执行的方法
                  - 参数3:当前执行的方法运行时传递过来的参数
                  - 返回值:当前方法执行的返回值     
                      
- 能够使用四种方法的引用
 总结:使用方法引用的步骤
    1.分析要写的Lambda表达式的大括号中是否就是调用另一个方法
    2.如果是,就可以使用方法引用替换,如果不是,就不能使用方法引用
    3.确定引用的方法类型(构造方法,成员方法,静态方法,类的成员方法)
    4.按照对应的格式去引用:
        构造方法: 类名::new
        成员方法(有参数): 对象名::方法名
        静态方法: 类名::方法名
        类的成员方法\成员方法(无参数):  类名::方法名
            
- 能够使用Base64对基本数据、URL和MIME类型进行编解码
    static Base64.Decoder getDecoder() 基本型 base64 解码器。
    static Base64.Encoder getEncoder() 基本型 base64 编码器。

    static Base64.Decoder getUrlDecoder() Url型 base64 解码器。
    static Base64.Encoder getUrlEncoder() Url型 base64 编码器。
	
    static Base64.Decoder getMimeDecoder() Mime型 base64 解码器。
    static Base64.Encoder getMimeEncoder() Mime型 base64 编码器。

     Encoder编码器:  encodeToString(byte[] bys)编码
     Decoder解码器:  decode(String str) 解码

标签:13,String,单元测试,System,JDK8,println,注解,public,out
来源: https://www.cnblogs.com/ofanimon/p/16188330.html