今日內(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í)行
public class refilectdemo {
public static void main(String[] args) throws ClassNotFoundException {
//forName將字節(jié)碼文件加載到內(nèi)存中返回Class對象 獲取字節(jié)碼文件的三種方式
Class<?> aClass = Class.forName("JavaWeb開發(fā).day_01基礎(chǔ)加強.Junit.CalcClass");
System.out.println(aClass);
//第二種方式 多用于傳參
Class aClass1 = CalcClass.class;
System.out.println(aClass1);
//第三種方式
CalcClass calcClass = new CalcClass();
Class aClass2 = calcClass.getClass();
// ClassLoader classLoader = aClass.getClassLoader();
// System.out.println("類加載器 :"+classLoader);
System.out.println(aClass2);
System.out.println(aClass==aClass1); //內(nèi)存地址相同 屬于同一個對象
System.out.println(aClass==aClass2);
System.out.println(aClass1==aClass2);
}
}
反射:框架設(shè)計的靈魂
* 框架:半成品軟件〗墒兀可以在框架的基礎(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í)行方法
創(chuàng)建一個框架
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
public class ReflectTest {
public static void main(String[] args) throws Exception {
//創(chuàng)建一個properties對象
Properties pro = new Properties();
//創(chuàng)建一個類加載器
ClassLoader classLoader = ReflectTest.class.getClassLoader();
//調(diào)用getResourceAsStream方法獲取資源對應(yīng)的字節(jié)流
InputStream is = classLoader.getResourceAsStream("pro.properties");
System.out.println(is);
//調(diào)用load方法獲取里面的鍵值對
pro.load(is);
//獲取配置文件中鍵值對
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//將對象加入內(nèi)存中
Class aClass = Class.forName(className);
//創(chuàng)建一個新的對象
Object o = aClass.getDeclaredConstructor().newInstance();
//獲取方法
Method method = aClass.getMethod(methodName);
//執(zhí)行方法
method.invoke(o);
}
}
Class類總結(jié)
Class類也是類的一種兽埃,與class關(guān)鍵字是不一樣的侥钳。
手動編寫的類被編譯后會產(chǎn)生一個Class對象,其表示的是創(chuàng)建的類的類型信息柄错,而且這個Class對象保存在同名.class的文件中(字節(jié)碼文件)舷夺,比如創(chuàng)建一個Shapes類,編譯Shapes類后就會創(chuàng)建其包含Shapes類相關(guān)類型信息的Class對象售貌,并保存在Shapes.class字節(jié)碼文件中给猾。
每個通過關(guān)鍵字class標(biāo)識的類,在內(nèi)存中有且只有一個與之對應(yīng)的Class對象來描述其類型信息颂跨,無論創(chuàng)建多少個實例對象敢伸,其依據(jù)的都是用一個Class對象。
Class類只存私有構(gòu)造函數(shù)毫捣,因此對應(yīng)Class對象只能有JVM創(chuàng)建和加載
Class類的對象作用是運行時提供或獲得某個對象的類型信息详拙,這點對于反射技術(shù)很重要(關(guān)于反射稍后分析)帝际。
獲取Class類的幾種方式 和 類加載的初始化
import java.util.*;
/*因為staticFinal屬于編譯期靜態(tài)常量,在編譯階段通過常量傳播優(yōu)化的方式將Initable類的常量
staticFinal存儲到了一個稱為NotInitialization類的常量池中饶辙,在以后對Initable類常量staticFinal的
引用實際都轉(zhuǎn)化為對NotInitialization類對自身常量池的引用蹲诀,所以在編譯期后,對編譯期常量的引用都將
在NotInitialization類的常量池獲取,這也就是引用編譯期靜態(tài)常量不會觸發(fā)Initable類初始化的重要原因
但在之后調(diào)用了Initable.staticFinal2變量后就觸發(fā)了Initable類的初始化弃揽,注意staticFinal2雖然被static和final修飾脯爪,但其值在編譯期并不能確定,
因此staticFinal2并不是編譯期常量矿微,使用該變量必須先初始化Initable類痕慢。
Initable2和Initable3類中都是靜態(tài)成員變量并非編譯期常量,引用都會觸發(fā)初始化涌矢。
**/
class Initable {
//編譯期靜態(tài)常量
static final int staticFinal = 47;
//非編期靜態(tài)常量
static final int staticFinal2 =
ClassInitialization.rand.nextInt(1000);
static {
System.out.println("Initializing Initable");
}
}
class Initable2 {
//靜態(tài)成員變量
static int staticNonFinal = 147;
//jing
static {
System.out.println("Initializing Initable2");
}
}
class Initable3 {
//靜態(tài)成員變量
static int staticNonFinal = 74;
static {
System.out.println("Initializing Initable3");
}
}
public class ClassInitialization {
public static Random rand = new Random();
public static void main(String[] args) throws Exception {
//字面常量獲取方式獲取Class對象
Class initable = Initable.class;
System.out.println("After creating Initable ref");
//不觸發(fā)類初始化
System.out.println(Initable.staticFinal);
//會觸發(fā)類初始化
System.out.println(Initable.staticFinal2);
//會觸發(fā)類初始化
System.out.println("============");
System.out.println(Initable2.staticNonFinal);
//forName方法獲取Class對象
// Class initable3 = Class.forName("cn.Initable3");
System.out.println("After creating Initable3 ref");
System.out.println(Initable3.staticNonFinal);
}
}
注解:
* 概念:說明程序的掖举。給計算機看的
* 注釋:用文字描述程序的。給程序員看的
* 定義:注解(Annotation)娜庇,也叫元數(shù)據(jù)塔次。一種代碼級別的說明函荣。它是JDK1.5及以后版本引入的一個特性洋魂,與類官觅、接口恩袱、枚舉是在同一個層次资铡。它可以聲明在包变泄、類亮元、字段袖外、方法汁掠、局部變量略吨、方法參數(shù)等的前面,用來對這些元素進行說明调塌,注釋晋南。
* 概念描述:
* JDK1.5之后的新特性
* 說明程序的
* 使用注解:@注解名稱
package JavaWeb開發(fā).day_01基礎(chǔ)加強.annotate;
/**
* @author grey //作者
* @since 1.5 //JDK版本
* @version 1.0 //版本
*/
public class annotationDemo {
/**
* 求和
* @param a 整數(shù)
* @param b 整數(shù)
* @return a和b的和
*/
public int method(int a,int b){
return a+b;
}
}
?
? * 作用分類:
? ①編寫文檔:通過代碼里標(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")
* @FunctionalInterface 函數(shù)式接口
* 自定義注解
* 格式:
元注解
public @interface 注解名稱{
屬性列表;
}
* 本質(zhì):注解本質(zhì)上就是一個接口羔砾,該接口默認(rèn)繼承Annotation接口
* public interface MyAnno extends java.lang.annotation.Annotation {}
* 屬性:接口中的抽象方法
* 要求:
1. 屬性的返回值類型有下列取值
* 基本數(shù)據(jù)類型
* String
* 枚舉
* 注解
* 以上類型的數(shù)組
2. 定義了屬性负间,在使用時需要給屬性賦值
1. 如果定義屬性時,使用default關(guān)鍵字給屬性默認(rè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)簽
判斷程序出現(xiàn)異常數(shù)量案例
@Pro(className = "cn.Calculator")
public class CheckTest {
public static void main(String[] args) throws Exception {
////獲取注解定義的位置的對象
Class<CheckTest> aClass = CheckTest.class;
//獲取指定的注解
Pro pro = aClass.getAnnotation(Pro.class);
//獲取被修飾類的名稱
String className = pro.className();
//將class對象加載到內(nèi)存中
Class<?> aClass1 = aClass.forName(className);
//創(chuàng)建一個classname對象
Object o = aClass1.getDeclaredConstructor().newInstance();
//獲取所有的方法并存入內(nèi)存中
Method[] methods = aClass1.getDeclaredMethods();
//創(chuàng)建一個輸出流對象 將錯誤信息存入.txt文件
BufferedWriter fw=new BufferedWriter(new FileWriter("Bug.txt"));
int count=0; //計算出錯數(shù)量
for (Method method : methods) {
if (method.isAnnotationPresent(Check.class)){
try{
method.invoke(o); //執(zhí)行方法
}catch (Exception e){
count++;
fw.write("哎呀谣殊!"+method.getName()+"方法出現(xiàn)異常了");
fw.newLine();
fw.write("異常名稱: "+e.getCause().getClass().getSimpleName());
fw.newLine();
fw.write("異常詳情: "+e.getCause().getMessage());
fw.newLine();
}
}
}
fw.write("共有 "+count+"個異常");
fw.flush();
fw.close();
}
}