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

}

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容