1. 程式人生 > >java基礎第十八篇之單元測試、註解和動態代理

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