反射
1. Junit單元測試
測試分類
- 黑盒測試:不需要寫代碼,輸入值看結果
- 白盒測試:需要寫代碼,關注程序的執行流程(如:Junit測試)
Junit測試
實現步驟:
- 定義一個測試類(測試用例)
- 測試類名:被測試的類名Test
- 包名:xxx.xxx.xx.test
- 定義測試方法:可以獨立運行
- 方法名:test即將測試的方法名
- 返回值:void
- 參數列表:空參
- 給方法加註解:@Test
- 導入Junit的依賴環境
判定結果:
-
紅色:失敗
-
綠色:失敗
-
一般情況下,使用斷言操作來操作結果
Assert.assertEquals(期望值,result);
@Before&@After
- @Before:修飾的方法會在測試方法之前被自動執行
- @After:修飾的方法會在測試方法之後自動被執行
實例代碼
public class Calcutor {
public int add(int a,int b){
return a+b;
}
public int del(int a,int b){
return a-b;
}
}
Junit測試類實例代碼
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class CalcutorTest {
@Before
public void init(){
System.out.println("執行初始化方法");
}
@Test
public void testadd(){
Calcutor calcutor = new Calcutor();
int result = calcutor.add(3,4);
Assert.assertEquals(7,result);
}
@After
public void after(){
System.out.println("執行的最後方法");
}
}
2.反射
反射概述
反射是框架設計的靈魂
框架:半成品軟件。可以在框架的基礎上進行軟件開發,簡化編碼
反射機制:將類的各個組成部分(成員變量、成員方法等)封裝爲其他對象。
反射優點:
- 可以在程序運行過程中,操作這些對象
- 可以解耦,提高程序的擴展性
反射詳解
獲取class對象的3種方式
Class.forName("全類名")
應用:多用於配置文件,將類名定義在配置文件中。讀取文件,加載類
類名.class
應用:多用於參數的傳遞
對象.getClass()
(常用)
應用:多用於對象獲取字節碼
結論:同一字節碼文件.class文件
在程序運行過程中,只會加載一次,三種方式獲取的class對象都是同一個。
實例代碼
public class Example {
public static void main(String[] args) throws Exception {
//1.Class.forName("全類名");
Class cls1 = Class.forName("AFan.Test.Calcutor");
System.out.println(cls1); // class AFan.Test.Calcutor
//2.類名.class
Class cls2 = Calcutor.class;
System.out.println(cls2); // class AFan.Test.Calcutor
//3.對象.getClass()
Calcutor calcutor = new Calcutor();
Class cls3 = calcutor.getClass();
System.out.println(cls3); // class AFan.Test.Calcutor
System.out.println(cls1 == cls2); //true
System.out.println(cls1 == cls3); //true
/*結論:三種方式指向的是同一對象*/
}
}
Class對象的常用方法
將類的各個部分轉化爲對象
- 獲取成員變量們
方法聲明 | 功能描述 |
---|---|
Field[] getFields() | 獲取所有public修飾的成員變量 |
Field getFields(String name) | 獲取所有public修飾的name變量名的成員變量 |
Filed[] getDeclaredFields() | 獲取所有成員變量,不考慮修飾符 |
Field getDeclaredField(String name) | 獲取name變量名的成員變量 |
- 獲取構造方法們
方法聲明 | 功能描述 |
---|---|
Constructor<?>[] getConstructors() | 獲取所有public修飾的構造器 |
Constructor getConstructor(類<?>… parameterTypes) | 獲取public修飾的指定的構造器 |
Constructor<?>[] getDeclaredConstructors() | 獲取所有構造器 |
Constructor getDeclaredConstructor(類<?>… parameterTypes) | 獲取指定的構造器 |
- 獲取成員方法們
方法聲明 | 功能描述 |
---|---|
Method[] getMethods() | 獲取所有public修飾的成員方法 |
Method getMethod(String name,類<?>… parameterTypes) | 獲取public修飾的指定的成員方法 |
Method[] getDeclaredMethods() | 獲取所有成員方法 |
Method getDeclaredMethod(String name,類<?>… parameterTypes) | 獲取指定的常用方法 |
- 獲取類名
方法聲明 | 功能描述 |
---|---|
String getName() | 獲取類名 |
操作class對象的方法
- Field:操作成員變量們
方法聲明 | 功能描述 |
---|---|
void set(Object obj,Object value) | 設置值 |
Object get(Object obj) | 獲取值 |
void setAccessible(true) | 忽略訪問權限修飾符的安全檢查 |
- Constructor:創建對象
方法聲明 | 功能描述 |
---|---|
T newInstance(Object obj) | 創建對象 |
- Method:方法對象
方法聲明 | 功能描述 |
---|---|
Object invoke(Object obj,Object …args) | 創建方法對象 |
String getName() | 獲取方法名 |
反射實例代碼
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Example {
public static void main(String[] args) throws Exception {
//獲取class對象
Class personClass = Person.class;
//獲取構造方法的對象
Constructor personClassConstructor = personClass.getConstructor(String.class, int.class);
Object person = personClassConstructor.newInstance("afan", 20);
System.out.println(person);
//獲取成員變量
Field a = personClass.getField("a");
a.set(person,"我是a");
System.out.println(a.get(person));
//獲取成員方法的對象
Method method = personClass.getMethod("eat", String.class);
method.invoke(person,"大米飯");
}
}
反射案例
需求:寫一個"框架",不能改變該類的任何代碼的前提下,可以幫我們創建任意類的對象,並且執行其中任意方法
實現步驟:
- 將需要創建的對象的全類名和需要執行的方法定義在配置文件中
- 在程序中加載讀取配置文件
- 使用反射技術來加載類文件進內存
- 創建對象,執行方法
實例代碼
className = AFan.Person
methodName = eat
public class Person {
private String name;
private int age;
public Person(){
}
public Person(String name,int age) {
this.name = name;
this.age = age;
}
public void eat(){
System.out.println("eat···");
}
}
import java.io.InputStream;
import java.util.Properties;
public class Example {
public static void main(String[] args) throws Exception {
//創建Properties對象
Properties pro = new Properties();
//加載配置文件,轉換爲一個集合
InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("pro.properties");
pro.load(in);
//獲取全類名和方法
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//創建對象,執行方法
Class proClass = Class.forName(className);
Object obj = proClass.getConstructor().newInstance();
proClass.getMethod(methodName).invoke(obj);
}
}
3.註解
註解介紹
作用:用來對包、類、方法、局部變量、方法參數等元素進行說明,註釋。
使用註解:@註解名稱
註解與註釋的區別:
- 註解:說明程序,給計算機看的
- 註釋:用文字描述程序的,給程序員看的
作用分類:
- 編寫文檔:生成javadoc (API)文檔
- 代碼分析:使用反射
- 編譯檢查:Override
JDK預定義的一些註解
註解 | 功能描述 |
---|---|
@Override | 檢測被該註解標註的方法是否繼承自父類(接口) |
@Deprecated | 表示該方法已過時 |
@SuppressWarnings | 壓制警告 |
@SuppressWarnings(“all”) | 壓制所有警告 |
自定義註解
- 格式:
public @interface 類名{
//屬性列表(返回值類型 方法名() )
}
- 本質:註解本質還是一個接口,該接口默認繼承Annotation接口
public interface MyAnno extends java.lang.annotation.Annotation {}
- 屬性:接口中可以定義的抽象方法
- 屬性的返回值類型:基本數據類型、String、枚舉、註解、數組
- 定義了屬性,在使用時需要給屬性賦值
- 如果定義屬性時,使用default關鍵字給屬性默認初始化值,則使用註解時,可以不進行屬性的賦值
- 如果只有一個屬性需要賦值,並且屬性的名稱是value。則value可以省略,直接定義值即可。
- 數組賦值時,值使用{}包裹。如果數組中只有一個值,則{}省略
元註解
定義:描述註解的註解
元註解 | 功能描述 |
---|---|
@Target(ElementType e) | 描述註解能夠作用的位置 |
@Retention | 描述註解被保留的階段 |
@Documented | 描述註解是否被抽取到API文檔中 |
@Inherited | 描述註解是否被子類繼承 |
注意:
@Target中ElementType的取值:
- TYPE:作用在類上
- METHOD:作用在方法上
- FIELD:作用在成員變量上
@Retention(RetentionPolicy.RUNTIME):當前被描述的註解,會保留到class字節碼文件中,並被JVM讀取到
解析註解
作用:獲取註解中定義的屬性值
實例代碼
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Anna {
String className();
String methodName();
}
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
@Anna(className = "AFan.Person",methodName = "eat")
public class Example {
public static void main(String[] args) throws Exception {
//獲取註解定義的位置的對象
Class<Example> exampleClass = Example.class;
//獲得指定的註解
Anna an = exampleClass.getAnnotation(Anna.class);
String className = an.className();
String methodName = an.methodName();
Class newclass = Class.forName(className);
Constructor constructor = newclass.getConstructor();
Object p = constructor.newInstance();
Method method = newclass.getMethod(methodName);
method.invoke(p);
}
}