今日內(nèi)容
1. Junit單元測試
2. 反射
3. 注解
Junit單元測試:
* 測試分類:
1. 黑盒測試:不需要寫代碼,給輸入值,看程序是否能夠輸出期望的值。
2. 白盒測試:需要寫代碼的。關(guān)注程序具體的執(zhí)行流程。
* Junit使用:白盒測試
* 步驟:
1. 定義一個測試類(測試用例)
* 建議:
* 測試類名:被測試的類名Test CalculatorTest
* 包名:xxx.xxx.xx.test cn.itcast.test
2. 定義測試方法:可以獨立運行
* 建議:
* 方法名:test測試的方法名 testAdd()
* 返回值:void
* 參數(shù)列表:空參
3. 給方法加@Test
4. 導(dǎo)入junit依賴環(huán)境
* 判定結(jié)果:
* 紅色:失敗
* 綠色:成功
* 一般我們會使用斷言操作來處理結(jié)果
* Assert.assertEquals(期望的結(jié)果,運算的結(jié)果);
* 補充:
* @Before:
* 修飾的方法會在測試方法之前被自動執(zhí)行
* @After:
* 修飾的方法會在測試方法執(zhí)行之后自動被執(zhí)行
/**
* 計算器類
*
* 測試分類:
* 1. 黑盒測試:不需要寫代碼,給輸入值,看程序是否能夠輸出期望的值。
* 2. 白盒測試:需要寫代碼的。關(guān)注程序具體的執(zhí)行流程。更有技術(shù)含量
*
* 單元測試的使用步驟:
* * Junit使用:白盒測試
* * 步驟:
* 1. 定義一個測試類(測試用例)
* * 建議:
* * 測試類名:被測試的類名Test CalculatorTest
* * 包名:xxx.xxx.xx.test cn.itcast.test
*
* 2. 定義測試方法:可以獨立運行
* * 建議:
* * 方法名:test測試的方法名 testAdd()
* * 返回值:void
* * 參數(shù)列表:空參
*
* 3. 給方法加@Test
* 4. 導(dǎo)入junit依賴環(huán)境
*
* * 判定結(jié)果:
* * 紅色:失敗
* * 綠色:成功
* * 一般我們會使用斷言操作來處理結(jié)果
* * Assert.assertEquals(期望的結(jié)果,運算的結(jié)果);
*
* * 補充:(注意哪怕是程序出了錯誤,before和after都也會執(zhí)行,而且執(zhí)行順序好像沒有規(guī)律)
* * @Before:
* * 修飾的方法會在測試方法之前被自動執(zhí)行
* * @After:
* * 修飾的方法會在測試方法執(zhí)行之后自動被執(zhí)行
*
*/
public class CalculatorTest {
@Before
public void init(){
System.out.println("init....");
}
@After
public void close0(){
System.out.println("close0...");
}
@After
public void close1(){
System.out.println("close1...");
}
@After
public void close2(){
System.out.println("close2...");
}
@Test
public void testAdd(){
Calculator calculator = new Calculator();
int result = calculator.add(1, 2);
System.out.println(result);
}
@Test
public void testSub(){
Calculator calculator = new Calculator();
int result = calculator.sub(3, 1);
//System.out.println(result);
Assert.assertEquals(2,result);
}
}
反射:框架設(shè)計的靈魂
* 框架:半成品軟件??梢栽诳蚣艿幕A(chǔ)上進行軟件開發(fā),簡化編碼
* 反射:將類的各個組成部分封裝為其他對象,這就是反射機制
* 好處:
1. 可以在程序運行過程中,操作這些對象。
2. 可以解耦,提高程序的可擴展性。
* 獲取Class對象的方式:
1. Class.forName("全類名"):將字節(jié)碼文件加載進內(nèi)存,返回Class對象
* 多用于配置文件,將類名定義在配置文件中。讀取文件,加載類
2. 類名.class:通過類名的屬性class獲取
* 多用于參數(shù)的傳遞
3. 對象.getClass():getClass()方法在Object類中定義著。
* 多用于對象的獲取字節(jié)碼的方式
* 結(jié)論:
同一個字節(jié)碼文件(*.class)在一次程序運行過程中,只會被加載一次,不論通過哪一種方式獲取的Class對象都是同一個。
* Class對象功能:
* 獲取功能:
1. 獲取成員變量們
* Field[] getFields() :獲取所有public修飾的成員變量
* Field getField(String name) 獲取指定名稱的 public修飾的成員變量
* Field[] getDeclaredFields() 獲取所有的成員變量,不考慮修飾符
* Field getDeclaredField(String name)
2. 獲取構(gòu)造方法們
* Constructor<?>[] getConstructors()
* Constructor<T> getConstructor(類<?>... parameterTypes)
* Constructor<T> getDeclaredConstructor(類<?>... parameterTypes)
* Constructor<?>[] getDeclaredConstructors()
3. 獲取成員方法們:
* Method[] getMethods()
* Method getMethod(String name, 類<?>... parameterTypes)
* Method[] getDeclaredMethods()
* Method getDeclaredMethod(String name, 類<?>... parameterTypes)
4. 獲取全類名
* String getName()
* Field:成員變量
* 操作:
1. 設(shè)置值
* void set(Object obj, Object value)
2. 獲取值
* get(Object obj)
3. 忽略訪問權(quán)限修飾符的安全檢查
* setAccessible(true):暴力反射
* Constructor:構(gòu)造方法
* 創(chuàng)建對象:
* T newInstance(Object... initargs)
* 如果使用空參數(shù)構(gòu)造方法創(chuàng)建對象,操作可以簡化:Class對象的newInstance方法
* Method:方法對象
* 執(zhí)行方法:
* Object invoke(Object obj, Object... args)
* 獲取方法名稱:
* String getName:獲取方法名
* 案例:
* 需求:寫一個"框架",不能改變該類的任何代碼的前提下,可以幫我們創(chuàng)建任意類的對象,并且執(zhí)行其中任意方法
* 實現(xiàn):
1. 配置文件
2. 反射
* 步驟:
1. 將需要創(chuàng)建的對象的全類名和需要執(zhí)行的方法定義在配置文件中
2. 在程序中加載讀取配置文件
3. 使用反射技術(shù)來加載類文件進內(nèi)存
4. 創(chuàng)建對象
5. 執(zhí)行方法
/**
* 反射的基本使用:
* 反射:將類的各個組成部分封裝為其他對象,這就是反射機制
* Java代碼在計算機中經(jīng)歷的三個階段
* 1. Source源碼階段(.java-->.class字節(jié)碼)
* 2. Class類對象階段(類加載器加載到內(nèi)存)
* 3. Runtime運行時階段 (創(chuàng)建對象)
*
* 獲取Class對象的3種方式
* 1. Class.forName("全類名"):將字節(jié)碼文件加載進內(nèi)存,返回Class對象
* * 多用于配置文件,將類名定義在配置文件中。讀取文件,加載類
* 2. 類名.class:通過類名的屬性class獲取
* * 多用于參數(shù)的傳遞
* 3. 對象.getClass():getClass()方法在Object類中定義著。
* * 多用于對象的獲取字節(jié)碼的方式
*
* 注意:同一個字節(jié)碼文件(*.class)在一次程序運行過程中,只會被加載一次,不論通過哪一種方式獲取的Class對象都是同一個。
*
*/
public class ReflectDemo1 {
public static void main(String[] args) throws ClassNotFoundException {
// 獲取Class對象的3種方式
//1.Class.forName("");
Class clz1 = Class.forName("com.ivyzh.reflect.domain.Person");
System.out.println(clz1);
//2.類名.class
Class clz2 = Person.class;
System.out.println(clz2);
//3. 對象.getClass()
Person person = new Person();
Class clz3 = person.getClass();
System.out.println(clz3);
System.out.println(clz1 ==clz2);
System.out.println(clz1 ==clz3);
/**
* 控制臺輸出結(jié)果:
class com.ivyzh.reflect.domain.Person
class com.ivyzh.reflect.domain.Person
class com.ivyzh.reflect.domain.Person
true
true
*/
}
}
/**
* 反射的功能使用:
* 1. 獲取成員變量們
* * Field[] getFields() :獲取所有public修飾的成員變量
* * Field getField(String name) 獲取指定名稱的 public修飾的成員變量
*
* * Field[] getDeclaredFields() 獲取所有的成員變量,不考慮修飾符
* * Field getDeclaredField(String name)
* 2. 獲取構(gòu)造方法們
* * Constructor<?>[] getConstructors()
* * Constructor<T> getConstructor(類<?>... parameterTypes)
*
* * Constructor<T> getDeclaredConstructor(類<?>... parameterTypes)
* * Constructor<?>[] getDeclaredConstructors()
* 3. 獲取成員方法們:
* * Method[] getMethods()
* * Method getMethod(String name, 類<?>... parameterTypes)
*
* * Method[] getDeclaredMethods()
* * Method getDeclaredMethod(String name, 類<?>... parameterTypes)
*
* 4. 獲取全類名
* * String getName()
*/
public class ReflectDemo2 {
public static void main(String[] args) throws Exception {
// 獲取Class對象的3種方式
//1.Class.forName("");
Class clzPerson = Class.forName("com.ivyzh.reflect.domain.Person2");
System.out.println(clzPerson);
// 1. 獲取成員變量們
Field[] fields = clzPerson.getFields();//獲取所有public修飾的成員變量
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
System.out.println("field-->"+field);
}
Field fielda1 = clzPerson.getField("a1");//獲取指定名稱的 public修飾的成員變量
System.out.println("fielda1-->"+fielda1);
System.out.println("---------------------------");
Field[] declaredFields = clzPerson.getDeclaredFields();
for (int i = 0; i < declaredFields.length; i++) {
Field declaredField = declaredFields[i];
System.out.println("declaredField-->"+declaredField);
}
Field declaredFieldd = clzPerson.getDeclaredField("d");
System.out.println("declaredFieldd-->"+declaredFieldd);
System.out.println("---------------------------");
// 1.1 設(shè)置成員變量們
Person2 person = new Person2();
Field fieldaa = clzPerson.getField("a");
fieldaa.set(person,10);
Field fieldName = clzPerson.getDeclaredField("name");
fieldName.setAccessible(true);//暴力反射,忽略訪問權(quán)限修飾符的安全檢查
fieldName.set(person,"zhangsan");
System.out.println("person:"+person);
// 1.2 獲取成員們的值
Object nameValue = fieldName.get(person);
System.out.println("nameValue:"+nameValue);
System.out.println("---------------------------");
// 2. 獲取Constructor
Constructor[] constructors = clzPerson.getConstructors();
for (int i = 0; i < constructors.length; i++) {
Constructor constructor = constructors[i];
System.out.println("constructor-->"+constructor);
}
Constructor[] declaredConstructors = clzPerson.getDeclaredConstructors();
for (int i = 0; i < declaredConstructors.length; i++) {
Constructor declaredConstructor = declaredConstructors[i];
System.out.println("declaredConstructor-->"+declaredConstructor);
}
System.out.println("---------------------------");
// 使用構(gòu)造器創(chuàng)建對象
Constructor constructor = clzPerson.getConstructor();//無參數(shù)構(gòu)造器
Object p1 = constructor.newInstance();
System.out.println("p1:"+p1);
//Constructor declaredConstructor = clzPerson.getDeclaredConstructor(String.class,int.class);
Constructor declaredConstructor = clzPerson.getDeclaredConstructor( int.class);
declaredConstructor.setAccessible(true);
//Object p2 = declaredConstructor.newInstance("lisi",20);
Object p2 = declaredConstructor.newInstance( 20);
System.out.println("p2:"+p2);
System.out.println("---------------------------");
// 3. 獲取Method
Method[] methods = clzPerson.getMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
System.out.println("method-->"+method);//父類的方法也會被獲取
}
Method[] declaredMethods = clzPerson.getDeclaredMethods();
for (int i = 0; i < declaredMethods.length; i++) {
Method declaredMethod = declaredMethods[i];
System.out.println("declaredMethod-->"+declaredMethod);//父類的方法不會被獲取
}
Method setName = clzPerson.getDeclaredMethod("setName", String.class);
Person2 person2 = new Person2();
setName.setAccessible(true);
setName.invoke(person2,"wangwu");
System.out.println("person2:"+person2);
}
}
/**
* 反射案例:
* 需求:寫一個"框架",不能改變該類的任何代碼的前提下,可以幫我們創(chuàng)建任意類的對象,并且執(zhí)行其中任意方法
* * 實現(xiàn):
* 1. 配置文件
* 2. 反射
* * 步驟:
* 1. 將需要創(chuàng)建的對象的全類名和需要執(zhí)行的方法定義在配置文件中
* 2. 在程序中加載讀取配置文件
* 3. 使用反射技術(shù)來加載類文件進內(nèi)存
* 4. 創(chuàng)建對象
* 5. 執(zhí)行方法
*/
public class ReflectDemo3 {
public static void main(String[] args) throws Exception {
Properties properties = new Properties();
ClassLoader classLoader = ReflectDemo3.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("pro.properties");
properties.load(is);
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
Class<?> clz = Class.forName(className);
Object instance = clz.newInstance();
Method declaredMethod = clz.getDeclaredMethod(methodName);
declaredMethod.setAccessible(true);
declaredMethod.invoke(instance);
}
}
pro.properties文件
className=com.ivyzh.reflect.domain.BenzCar
methodName=fix
注解:
* 概念:說明程序的。給計算機看的
* 注釋:用文字描述程序的。給程序員看的
* 定義:注解(Annotation),也叫元數(shù)據(jù)。一種代碼級別的說明。它是JDK1.5及以后版本引入的一個特性,與類、接口、枚舉是在同一個層次。它可以聲明在包、類、字段、方法、局部變量、方法參數(shù)等的前面,用來對這些元素進行說明,注釋。
* 概念描述:
* JDK1.5之后的新特性
* 說明程序的
* 使用注解:@注解名稱
?
* 作用分類:
①編寫文檔:通過代碼里標(biāo)識的注解生成文檔【生成文檔doc文檔】
②代碼分析:通過代碼里標(biāo)識的注解對代碼進行分析【使用反射】
③編譯檢查:通過代碼里標(biāo)識的注解讓編譯器能夠?qū)崿F(xiàn)基本的編譯檢查【Override】
* JDK中預(yù)定義的一些注解
* @Override :檢測被該注解標(biāo)注的方法是否是繼承自父類(接口)的
* @Deprecated:該注解標(biāo)注的內(nèi)容,表示已過時
* @SuppressWarnings:壓制警告
* 一般傳遞參數(shù)all @SuppressWarnings("all")
* 自定義注解
* 格式:
元注解
public @interface 注解名稱{
屬性列表;
}
* 本質(zhì):注解本質(zhì)上就是一個接口,該接口默認繼承Annotation接口
* public interface MyAnno extends java.lang.annotation.Annotation {}
* 屬性:接口中的抽象方法
* 要求:
1. 屬性的返回值類型有下列取值
* 基本數(shù)據(jù)類型
* String
* 枚舉
* 注解
* 以上類型的數(shù)組
2. 定義了屬性,在使用時需要給屬性賦值
1. 如果定義屬性時,使用default關(guān)鍵字給屬性默認初始化值,則使用注解時,可以不進行屬性的賦值。
2. 如果只有一個屬性需要賦值,并且屬性的名稱是value,則value可以省略,直接定義值即可。
3. 數(shù)組賦值時,值使用{}包裹。如果數(shù)組中只有一個值,則{}可以省略
* 元注解:用于描述注解的注解
* @Target:描述注解能夠作用的位置
* ElementType取值:
* TYPE:可以作用于類上
* METHOD:可以作用于方法上
* FIELD:可以作用于成員變量上
* @Retention:描述注解被保留的階段
* @Retention(RetentionPolicy.RUNTIME):當(dāng)前被描述的注解,會保留到class字節(jié)碼文件中,并被JVM讀取到
* @Documented:描述注解是否被抽取到api文檔中
* @Inherited:描述注解是否被子類繼承
* 在程序使用(解析)注解:獲取注解中定義的屬性值
1. 獲取注解定義的位置的對象 (Class,Method,Field)
2. 獲取指定的注解
* getAnnotation(Class)
//其實就是在內(nèi)存中生成了一個該注解接口的子類實現(xiàn)對象
public class ProImpl implements Pro{
public String className(){
return "cn.itcast.annotation.Demo1";
}
public String methodName(){
return "show";
}
}
3. 調(diào)用注解中的抽象方法獲取配置的屬性值
* 案例:簡單的測試框架
* 小結(jié):
1. 以后大多數(shù)時候,我們會使用注解,而不是自定義注解
2. 注解給誰用?
1. 編譯器
2. 給解析程序用
3. 注解不是程序的一部分,可以理解為注解就是一個標(biāo)簽
/**
* * 概念:說明程序的。給計算機看的
* * 注釋:用文字描述程序的。給程序員看的
*
* * 定義:注解(Annotation),也叫元數(shù)據(jù)。一種代碼級別的說明。它是JDK1.5及以后版本引入的一個特性,與類、接口、枚舉是在同一個層次。
* 它可以聲明在包、類、字段、方法、局部變量、方法參數(shù)等的前面,用來對這些元素進行說明,注釋。
* * 概念描述:
* * JDK1.5之后的新特性
* * 說明程序的
* * 使用注解:@注解名稱
*
* * 作用分類:
* ①編寫文檔:通過代碼里標(biāo)識的注解生成文檔【生成文檔doc文檔】
* ②代碼分析:通過代碼里標(biāo)識的注解對代碼進行分析【使用反射】
* ③編譯檢查:通過代碼里標(biāo)識的注解讓編譯器能夠?qū)崿F(xiàn)基本的編譯檢查【Override】
* * 注解的定義
* * JDK中預(yù)定義的一些注解
* * 自定義注解
* * 在程序使用(解析)注解:獲取注解中定義的屬性值
*
*
* * JDK中預(yù)定義的一些注解
* * @Override :檢測被該注解標(biāo)注的方法是否是繼承自父類(接口)的
* * @Deprecated:該注解標(biāo)注的內(nèi)容,表示已過時
* * @SuppressWarnings:壓制警告
* * 一般傳遞參數(shù)all @SuppressWarnings("all")
*/
public @interface AnnoDemo1 {
//1. 屬性的返回值類型有
int show1();// 基本數(shù)據(jù)類
String show2();//String
MyAnno show3();//注解
MyEnum show6();//枚舉
int[] show4();//以上類型的數(shù)組
MyAnno[] show5();//以上類型的數(shù)組
//2. 定義了屬性,在使用時需要給屬性賦值
String name() default "zhangsan";// 如果定義屬性時,使用default關(guān)鍵字給屬性默認初始化值,則使用注解時,可以不進行屬性的賦值
}
/**
** 元注解:用于描述注解的注解
* * @Target:描述注解能夠作用的位置
* * ElementType取值:
* * TYPE:可以作用于類上
* * METHOD:可以作用于方法上
* * FIELD:可以作用于成員變量上
* * @Retention:描述注解被保留的階段
* * @Retention(RetentionPolicy.RUNTIME):當(dāng)前被描述的注解,會保留到class字節(jié)碼文件中,并被JVM讀取到
* * @Retention(RetentionPolicy.CLASS):當(dāng)前被描述的注解,會保留到class字節(jié)碼文件中,但不會被JVM讀取到
* * @Retention(RetentionPolicy.SOURCE):當(dāng)前被描述的注解,不會保留到class字節(jié)碼文件中,但不會被JVM讀取到
* (三個階段:source、class、runtime)
* * @Documented:描述注解是否被抽取到api文檔中
* * @Inherited:描述注解是否被子類繼承
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnoDemo3 {
int value();
}
/**
* * 概念:說明程序的。給計算機看的
* * 注釋:用文字描述程序的。給程序員看的
*
* * 定義:注解(Annotation),也叫元數(shù)據(jù)。一種代碼級別的說明。它是JDK1.5及以后版本引入的一個特性,與類、接口、枚舉是在同一個層次。
* 它可以聲明在包、類、字段、方法、局部變量、方法參數(shù)等的前面,用來對這些元素進行說明,注釋。
* * 概念描述:
* * JDK1.5之后的新特性
* * 說明程序的
* * 使用注解:@注解名稱
*
* * 作用分類:
* ①編寫文檔:通過代碼里標(biāo)識的注解生成文檔【生成文檔doc文檔】
* ②代碼分析:通過代碼里標(biāo)識的注解對代碼進行分析【使用反射】
* ③編譯檢查:通過代碼里標(biāo)識的注解讓編譯器能夠?qū)崿F(xiàn)基本的編譯檢查【Override】
* * 注解的定義
* * JDK中預(yù)定義的一些注解
* * 自定義注解
* * 在程序使用(解析)注解:獲取注解中定義的屬性值
*
*
* * JDK中預(yù)定義的一些注解
* * @Override :檢測被該注解標(biāo)注的方法是否是繼承自父類(接口)的
* * @Deprecated:該注解標(biāo)注的內(nèi)容,表示已過時
* * @SuppressWarnings:壓制警告
* * 一般傳遞參數(shù)all @SuppressWarnings("all")
*/
public @interface MyAnno {
}
/**
* 獲取指定的注解
* * getAnnotation(Class)
*/
@Pro(className = "com.ivyzh.reflect.domain.BMWCar",methodName = "run")
public class ReflectTest {
public static void main(String[] args) throws Exception {
Pro annotation = ReflectTest.class.getAnnotation(Pro.class);
String className = annotation.className();
String methodName = annotation.methodName();
System.out.println(className);
System.out.println(methodName);
Class<?> clz = Class.forName(className);
Object car = clz.newInstance();
Method declaredMethod = clz.getDeclaredMethod(methodName);
declaredMethod.setAccessible(true);
declaredMethod.invoke(car);
}
}
Calulator
/**
* 計算器類
*/
public class Calculator {
@Check(a=5,b=3)
public int add(int a,int b){
return a+b;
}
@Check
public int sub(int a,int b){
return a+b;
}
@Check
public int mul(int a,int b){
return a*b;
}
@Check(a=10,b=0)
public int div(int a,int b){
return a/b;
}
public void show(){
System.out.println("show...");
}
}
CalulatorTest
/**
* 案例:簡單的測試框架
* 1.
* 2.
*/
public class CaculatorTest {
public static void main(String[] args) throws Exception {
Calculator calculator = new Calculator();
Method[] declaredMethods = calculator.getClass().getDeclaredMethods();
int bugNum = 0;
BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("bug.txt"));
for (int i = 0; i < declaredMethods.length; i++) {
Method declaredMethod = declaredMethods[i];
if(declaredMethod.isAnnotationPresent(Check.class)){
Check annotation = declaredMethod.getAnnotation(Check.class);
int a = annotation.a();
int b = annotation.b();
try {
Object result = declaredMethod.invoke(calculator,a,b);
System.out.println(a+" "+declaredMethod.getName()+" "+b+" result:"+result);
}catch (Exception e){
e.printStackTrace();
bugNum++;
bufferedWriter.write(declaredMethod.getName()+" 出現(xiàn)異常了");
bufferedWriter.newLine();
bufferedWriter.write("異常名稱:"+e.getCause().getClass().getSimpleName());
bufferedWriter.newLine();
bufferedWriter.write("異常原因:"+e.getCause().getMessage());
bufferedWriter.newLine();
bufferedWriter.write("----------------------");
bufferedWriter.newLine();
}
}
}
bufferedWriter.write("本次運行一共出現(xiàn) "+bugNum+" 次異常");
bufferedWriter.flush();
bufferedWriter.close();
}
}
Check
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Check {
int a() default 5;
int b() default 3;
}
END.