• java基础第十八篇之单元测试、注解和动态代理


    1:单元测试
    1)JUnit是一个Java语言的单元测试框架,这里的单元指的就是方法
    2)单元测试用来替换以前的main方法


    1.1 Junit测试的步骤
    1:在方法的上面加上 @Test
    2:将junit库添加到工程的构建路径
    3:选中方法--->右键--->JunitTest
    1.2 常用的Junit测试注解
    常用注解
    @Test,用于修饰需要执行的方法
    @Before,测试方法前执行的方法
    @After,测试方法后执行的方法
    1.3 测试有返回值的方法
    public int sum(int a, int b){

    int sum = a + b;
    return sum;
    }

    @Test
    public void testSum(){
    int result = sum(10, 10);
    //断言,如果条件成立,则程序正常,如果条件不成立,则程序直接结束
    //参1:期望的值 参2:实际得到的值
    assertEquals(20, result);

    xxxxxxxxxxxxxxxxxx
    }

    2:注解(Annotation)
    注解可以理解成一个符号(@注解的名字)
    JDK1.5及以后版本引入

    注解的作用:
    1. 编译检查:通过代码里标识注解,让编译器能够实现基本的编译检查
    2. 编写文档:通过代码里标识注解,辅助生成帮助文档对应的内容 (@Document)

    2.1 注解的分类
    JDK提供的注解
    1.@Deprecated 表示被修饰的方法已经过时。过时的方法不建议使用,但仍可以使用。
    一般过时的方法都有一个新的方法来替换
    2.@Override 类的重写
    3:@SuppressWarnings("all"),表示抑制警告,被修饰的类或方法如果存在编译警告,将被编译器忽略
    deprecation ,或略过时
    rawtypes ,忽略类型安全(没有加泛型)
    unused ,忽略不使用
    unchecked ,忽略安全检查(没有泛型,还添加数据)
    null,忽略空指针(空指针去调用方法 )

    package pack02_annotation;

    import java.io.Serializable;
    import java.util.ArrayList;

    @SuppressWarnings("all") //对整个类起作用
    public class Demo02JDKAnnotation implements Serializable{

    // @SuppressWarnings("unchecked") //对整个方法起作用
    // @SuppressWarnings({"unused", "rawtypes","unchecked", "null"})
    public static void main(String[] args) {
    //参数表示出现警告的原因
    int a = 123;

    ArrayList list = new ArrayList();

    list.add("hello");

    String str = null;

    System.out.println(str.length());
    }
    public static void method(){
    int a = 123;
    }

    }



    package pack01_junit;

    import static org.junit.Assert.assertEquals;
    import static java.lang.Math.*;
    import org.junit.Test;
    public class Demo02Junit {

    public int add(int a , int b){
    return a + b;
    }

    @Test
    public void testAdd(){
    int result = add(10, 20);
    //断言
    //参1:表示期望得到的值
    //参2:表示实际得到的值
    //如果两个值一致,程序正常结束,如果不一致程序直接终止
    assertEquals(31, result);
    }
    }


    自定义注解

    3:自定义注解
     定义注解使用关键字: @interface 

    public @interface MyAnnotation {

    }

    //使用注解
    @MyAnnotation
    @MyAnnotation1
    class Demo{
    public void func(){

    }
    }
    //------------------------------
    @MyAnnotation1
    @MyAnnotation2
    public void func(){

    }
    3.2 给注解添加属性
    2. 返回值类型:基本类型、字符串String、Class、注解、枚举,以及以上类型的一维数组
    public @interface MyAnnotation {
    //属性格式:修饰符 返回值类型 属性名() [default 默认值]
    //1修饰符:默认值 public abstract ,且只能是public abstract。
    public abstract String myString();
    public abstract int myInt() default 123;
    }


    //-----------------例子-------------------------------
    enum MyEnum{
    Red,Blue
    //public static final MyEnum Red = new MyEnum();
    //public static final MyEnum Blue = new MyEnum();
    }
    public @interface MyAnnotation { //反编译之后,其实是接口
    //给注解添加属性
    public abstract int myInt() default 123; //类似于该方法的返回值
    public abstract String myString();
    public abstract Class myClass();
    public abstract MyAnnotation3 myAnno();
    public abstract MyEnum myEnum();

    public abstract int[] myIntArray();


    }

    2.4 自定义注解:使用
    @注解类名( 属性名= 值 , 属性名 = 值 , .....)

    //-------------例子------------------------------
    public @interface MyAnnotation4 {
    public abstract String value();
    }

    //如果一个注解只有一个属性,并且名字为value, 则可以不用加属性名
    @MyAnnotation4("hello")
    class Demo2{

    }

     注解使用的注意事项:
     注解可以没有属性,如果有属性需要使用小括号括住。例如:@MyAnno1或@MyAnno1()
     属性格式:属性名=属性值,多个属性使用逗号分隔。例如:@MyAnno2(username="rose")
     如果属性名为value,且当前只有一个属性,value可以省略。
     如果使用多个属性时,k的名称为value不能省略
     如果属性类型为数组,设置内容格式为:{ 1,2,3 }。例如:arrs = {"baidu","baidu"}
     如果属性类型为数组,值只有一个{} 可以省略的。例如:arrs = "baidu"

    //当使用一个有属性的注解时,必须指定属性的值
    //一个类可以使用多个注解
    //同一个注解一个类只能被使用一次

    2.5 注解的解析
    1:获取注解的属性值
    JDK提供java.lang.reflect.AnnotatedElement接口允许在运行时通过反射获得注解。

    @interface MyAnnotation{
    }
    Class对象 //MyAnnotation.class
    Method : 判断方法上是否有这个注解,参数为注解的Class对象
    Class : 判断类上是否有这个注解,参数为注解的Class对象
    boolean isAnnotationPresent(Class annotationClass) 当前对象(方法,类)是否有注解

    Class :获取类上的注解, 参数表示要获取的注解的Class对象
    Method:获取方法上的注解, 参数表示要获取的注解的Class对象 //MyAnnotation.class
    public <T extends Annotation> T getAnnotation(Class<T> annotationClass) //获取注解对象

    3:元注解
    是对注解的注解
    JDK提供4种元注解:
     @Retention 用于确定被修饰的自定义注解生命周期(注解从生效到消失)
     RetentionPolicy.SOURCE 被修饰的注解只能存在源码中,字节码class没有。用途:提供给编译器使用。
     RetentionPolicy.CLASS 被修饰的注解只能存在源码和字节码中,运行时内存中没有。用途:JVM java虚拟机使用
     RetentionPolicy.RUNTIME 被修饰的注解存在源码、字节码、内存(运行时)。用途:通过反射获取属性值
    默认的声明周期是: RetentionPolicy.CLASS
    当我们自定义一个注解,需要为注解加声明周期:RetentionPolicy.RUNTIME


    3.2 注解的修改目标
     ElementType.TYPE 修饰类、接口
     ElementType.CONSTRUCTOR 修饰构造
     ElementType.METHOD 修饰方法
     ElementType.FIELD 修饰字段
     @Documented 使用javaDoc生成 api文档时,是否包含此注解
    @Inherited 如果父类使用该注解,子类会继承该注解

    //---------------------------------------------
    @Retention(RetentionPolicy.RUNTIME) //指定注解的声明周期
    @Target({ElementType.TYPE, ElementType.METHOD}) //指定注解的作用目标
    public @interface MyAnnotation2 {

    }

    package pack05_parse_annotation;

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;

    //元注解:是对注解的注解

    //自定义一个注解时要给该注解设置生命周期
    @Retention(RetentionPolicy.RUNTIME) //注解可以到内存中,就可以反射

    //给自定义的注解设置修饰的目标: 该注解既可以修饰类,也可以修饰方法
    //默认情况下,注解可以修饰一切
    @Target({ElementType.METHOD,ElementType.TYPE})
    public @interface MyAnnotation {
    public abstract String myString();
    }


    package pack05_parse_annotation;

    import java.lang.reflect.Method;

    import org.junit.Test;

    @MyAnnotation(myString="类上的注解属性值")
    public class UseAnnotation {

    @MyAnnotation(myString="方法上的注解属性值")
    public void func1(){
    System.out.println("func1方法");
    }


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

    @Test
    public void parseAnnoClass(){
    //1:获取类的CLass对象
    Class<?> clazz = UseAnnotation.class;

    //2:判断类上是否有@MyAnnotation注解
    boolean bl = clazz.isAnnotationPresent(MyAnnotation.class);
    if(bl){
    //3:获取注解
    MyAnnotation annotation = clazz.getAnnotation(MyAnnotation.class);
    //4:调用方法
    String value = annotation.myString();
    System.out.println(value);
    }
    }





    //在该方法中获取注解的属性值
    @Test
    public void parseAnno(){
    //1:获取类的Class对象
    Class<?> clazz = UseAnnotation.class;

    //2:因为不知道哪个方法有注解,所以需要获取所有的方法
    Method[] methods = clazz.getMethods();
    //3:遍历数组,判断哪个方法有注解
    for (Method method : methods) {
    //这里的参数要指定获取的是哪一个注解
    boolean bl = method.isAnnotationPresent(MyAnnotation.class);
    // System.out.println(method.getName()+":"+bl);
    if(bl){
    //表示该方法加了MyAnnotation注解
    //获取注解:参数要指定获取的是哪一个注解
    //本质上获取注解就是获取注解注解 接口的实现类对象
    MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
    //获取属性值:调用注解中的方法,拿到返回值,就得到属性值

    String value = annotation.myString();
    System.out.println(value);
    }
    }
    }
    }

    package pack07_test;

    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;

    public class TestDemo {

    public static void main(String[] args) throws Exception {
    //1:获取使用注解方法所在类Class对象
    Class<?> clazz = UseAnnotation.class;

    Object obj = clazz.newInstance();
    //2:获取所有的方法
    Method[] methods = clazz.getMethods();

    for (Method method : methods) {
    //判断哪个方法有注解
    boolean bl = method.isAnnotationPresent(MyTest.class);
    if(bl){
    //如果 哪个方法加了这个注解,就执行哪个方法
    //加了注解之后,还要获取属性值
    MyTest annotation = method.getAnnotation(MyTest.class);
    String value = annotation.value();
    //只有属性值是run 才能运行
    if(value.equals("run")){
    method.invoke(obj);
    }
    }
    }
    }

    }

    4:类加载器
    引导类加载器:BootstrapClassLoader // 加载的是核心类,加载 jdk/jre/lib/rt.jar
    扩展类加载器:ExtClassLoader //加载扩展类, jdk/jre/lib/ext/
    应用类加载器:AppClassLoader //加载应用类(HelloWorld TestDemo)

    //获取一个类的加载器
    TestDemo.class.getClassLoader()

    加载原则:
    全盘负责制: A类要使用B类,A类必须负责加载B类中所有的类
    TestDemo --->String 类
    父亲委托制:子类要使用某个类,先要委托父类先加载,如果父类没有加载成功,则子类才会加载
    盘负责委托机制保证一个class文件只会被加载一次,形成一个Class对象。

    class F
    {
    Demo demo;
    }

    class Zi extends Fu
    {
    Demo demo2();
    }

    new ZI();

    ///--------------------
    class A
    {
    String str;
    }

    class B
    {
    A a;
    }
    5:动态代理

    作用
    //1:在不改变一个类源码的情况下,去对类中的方法进行功能增强
    class Demo
    {
    public void method(){
    System.out.println("功能1");
    }
    }

    //---------------------------------
    public void method(){
    System.out.println("功能1");
    System.out.println("功能2");
    System.out.println("功能3");
    }

    //2:在不改变一个类源码的情况下,屏蔽类中的某些功能
    class Demo
    {
    public void method(){
    //System.out.println("功能1");
    //System.out.println("功能2");
    System.out.println("功能3");
    }
    }

    动态代理的特点:
    1:动态代理基于接口机制
    2: Proxy 代理类
    /*
    参1:表示类加载器
    参2:表示实现的接口
    参3: 接口
    */
    public static Object newProxyInstance(ClassLoader loader,
    Class<?>[] interfaces,
    InvocationHandler h)


    //-------------------------------------
    @Retention(RetentionPolicy.RUNTIME) //指定注解的声明周期
    @Target({ElementType.TYPE, ElementType.METHOD}) //指定注解的作用目标
    public @interface MyAnnotation2 {

    }


    //------------------------------------------------
    //1:反射判断哪个方法有注解
    Class<?> clazz = UseAnnotation.class;
    TreeMap<Integer, Method> tm = new TreeMap<Integer, Method>();
    //创建对象
    Object obj = clazz.newInstance();
    //2:获取所有的方法
    Method[] methods = clazz.getMethods();
    for (Method method : methods) {
    boolean bl = method.isAnnotationPresent(MyAnnotation.class);
    if(bl){
    //还要判断属性值是否是:run
    //获取属性值
    MyAnnotation annotation = method.getAnnotation(MyAnnotation.class);
    String value = annotation.value();
    //如果注解的属性值是run,则运行该方法
    if(value.equals("run")){
    // method.invoke(obj);

    }

    }
    }
    }


    //动态代理的步骤
    1:写一个接口 //List
    public interface Sing
    {
    public abstract void sing();
    }

    2:一个类实现接口 //ArrayList
    class Singer implements Sing
    {
    public void sing(){
    //唱歌
    }
    }

    package com.baidu_05;

    public interface RunnCode {
    public abstract void run();
    }


    package com.baidu_05;

    public class Demo01 implements RunnCode {

    @Override
    public void run() {
    // TODO Auto-generated method stub
    for(int i = 0; i < 10000; i++) {
    System.out.println("i=" + i);
    }
    }

    }


    package com.baidu_05;

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

    class MyInn3 implements InvocationHandler {

    RunnCode obj;


    public MyInn3(RunnCode obj) {
    super();
    this.obj = obj;
    }


    public MyInn3() {
    super();
    // TODO Auto-generated constructor stub
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    // TODO Auto-generated method stub
    long t1 = System.currentTimeMillis();
    Object result = method.invoke(obj, args);
    long t2 = System.currentTimeMillis();
    System.out.println("消耗了:" + (t2-t2) + "毫秒");
    return result;
    }

    }

    public class Test2 {
    public static void main(String[] args) {
    RunnCode rc = new Demo01();

    rc.run();

    rc = (RunnCode)Proxy.newProxyInstance(rc.getClass().getClassLoader(), rc.getClass().getInterfaces(), new MyInn3(rc));
    rc.run();
    }

    }

    3:动态代理
    3.1 必须创建一个被代理对象
    Sing singer = new Singer();
    3.2 开始动态代理
    //代理类也实现了Sing接口,并创建接口的实现类对象
    Sing singer = (Sing)Proxy.newProxyInstance()

    //创建一个InvocationHandler接口的实现类,并在invoke方法中,指定你要增强的方法

    t1
    func();
    t2

    package pack12_proxy;

    import java.lang.reflect.Proxy;
    import java.util.ArrayList;
    import java.util.List;

    public class Demo02Pproxy {
    public static void main(String[] args) {
    //创建被代理类对象
    List<String> list = new ArrayList<String>();

    list.add("hello");
    list.add("world");

    list = myProxy(list);

    // list.add("xxx");
    // list.set(0,"xxx");
    System.out.println(list.get(0));
    }

    private static List<String> myProxy(List<String> list) {
    @SuppressWarnings("unchecked")
    List<String> proxyList=(List<String>)Proxy.newProxyInstance(list.getClass().getClassLoader(),
    list.getClass().getInterfaces(), new MyInvocationHandler(list));
    return proxyList;
    }
    }


    package pack12_proxy;

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.util.List;

    public class MyInvocationHandler implements InvocationHandler {
    List<String> obj;


    public MyInvocationHandler() {
    super();
    // TODO Auto-generated constructor stub
    }

    public MyInvocationHandler(List<String> obj) {
    super();
    this.obj = obj;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    //屏蔽add,set,remove方法
    if(method.getName().equals("add")){
    throw new RuntimeException("你不能调用add方法");
    }
    if(method.getName().equals("set")){
    throw new RuntimeException("你不能调用set方法");
    }
    if(method.getName().equals("remove")){
    throw new RuntimeException("你不能调用remove方法");
    }
    //其他方法正常调用
    Object result = method.invoke(obj, args);
    return result;
    }

    }

    作业:


    public class UseAnnotation {

    @MyTest("run","first")
    public void func1(){
    System.out.println("func1方法");
    }

    @MyTest("run","third")
    public void func2(){
    System.out.println("func2方法");
    }

    @MyTest("aaa")
    public void func3(){
    System.out.println("func3方法");
    }
    @MyTest("run","second")
    public void func4(){
    System.out.println("func4方法");
    }


    @MyTest("run","four")
    public void fun5(){
    System.out.println("func2方法");
    }

    }

  • 相关阅读:
    你的程序够健壮么?我看未必。。。
    POJ 3415 Max Sum of Max-K-sub-sequence (线段树+dp思想)
    Android ARM汇编语言
    关于索引删除的策略IndexDeletionPolicy
    深度学习领域的一些大牛
    框架学习之道:PE框架简介
    PropertyPlaceholderConfigurer类的使用注意
    hdu 4622 Reincarnation (后缀自动机)
    总结showModalDialog在开发中的一些问题
    android端向服务器提交请求的几种方式
  • 原文地址:https://www.cnblogs.com/haizai/p/11179653.html
Copyright © 2020-2023  润新知