day15 基礎加強(junit&反射&註解)
一.Junit單元測試**********
1.什麼是單元測試
可以取代main方法獨立運行
2.Junit的使用步驟
i.下載(http://www.junit.org)(現在不需要下載,大多數開發工具中自帶)
ii.具體使用步驟
a.編寫一個被測試類(業務類)
b.編寫一個測試類(使用Junit)
c.編寫一個Junit測試方法,加上@Test
iii.運行測試
右鍵-->Run XXX() (右鍵點擊測試方法或者類名)
3.單元測試中其他四個註解
@Before 在每個測試方法執行之前執行
@After 在每個測試方法執行之後執行
@BeforeClass 在所有測試方法執行之前執行
@AfterClass 在所有測試方法執行之後執行
注意:@BeforeClass和@AfterClass必須修飾靜態方法
二.反射********************
1.類的加載
.java--->.class--->加載--->JVM
類什麼時候加載到內存???
當我們第一次使用到該類時,該類纔會被JVM加載到內存
當類被加載到內存後,JVM會幹這麼一件事:
在堆區中創建一個對象,用來保存類中的所有信息
該對象稱爲Class對象,也叫字節碼文件對象
2.什麼是反射
反射是一種運行時機制,在運行時獲取到某個類的字節碼文件對象
從而解剖它,取出其中成員,進而使用他們
3.反射在實際開發中的應用
a.開發IDE
b.開發框架
4.反射中萬物皆對象的概念
Class -- 字節碼文件對象
Field -- 成員變量
Method -- 成員方法
Constructor -- 構造方法
newInstance -- 創建對象
invoke -- 調用/執行
體驗一下,反射中的語法:
正常語法:
類名 對象名 = new 構造方法(參數);
對象名.成員方法(參數);
對象名.成員變量 = 值;
反射語法:
類名 對象名 = 構造方法.newInstance(參數);
成員方法.invoke(對象名,參數);
成員變量.set(對象名.值); 5.反射的第一步獲取字節碼文件對象(Class對象)
public class TestGetClass {
public static void main(String[] args) throws ClassNotFoundException {
//所有反射的第一步,獲取Class對象
//a.通過類的靜態成員,class
Class clazz1 = Cat.class;
System.out.println(clazz1);
//b.通過類的對象,獲取Class對象
Cat cc = new Cat();
Class clazz2 = cc.getClass();
System.out.println(clazz2);
//c。通過Class的方法靜態方法
Class clazz3 = Class.forName("com.itheima.demo02_getClass.Cat");
System.out.println(clazz3);
//注意:以上是三種獲取Class對象的方法,不是所有三個Class對象
//實際以上三種獲取的是同一個CLass對象
System.out.println(clazz1 == clazz2);
System.out.println(clazz1 == clazz3);
System.out.println(clazz2 == clazz3);
}
}
6.Class對象中的三個常用方法
public String getSimpleName(); 獲取類名
pubic String getName(); 獲取全限定類名(包名.類名)
pubic Object newInstance(); 創建Class對象所代表的類的對象
public class TestGetClass02 {
public static void main(String[] args) throws Exception {
//所有反射的第一步,獲取Class對象
Class clazz1 = Cat.class;
System.out.println(clazz1);
//調用class1中三個方法
String name = clazz1.getName();
System.out.println(name);
String simpleName = clazz1.getSimpleName();
System.out.println(simpleName);
//創建對象
Object obj = clazz1.newInstance();
System.out.println(obj);
}
}
7.通過反射獲取構造方法&&使用構造方法創建對象*************
public Constructor getConstructor(Class<T>... 參數的類型.class); 獲取"public" 某個構造方法
a.如果是私有構造怎麼獲取和使用
public Constructor getDeclaredConstructor(Class...); 獲取任意修飾符的某個構造方法
私有構造不能直接使用,必須先設置暴力權限
構造方法對象.setAccessible(true);
public class ConstructorDemo {
public static void main(String[] args) throws Exception {
//1.獲取Class對象
Class cc = Cat.class;
//2.解剖出構造方法
Constructor con1 = cc.getConstructor(int.class);//獲取age構造
Constructor con2 = cc.getConstructor();//獲取空參數構造
Constructor con3 = cc.getConstructor(int.class, String.class);
//3.使用解剖出的構造
Object obj = con1.newInstance(10);//new Cat(10);
System.out.println(obj);
Object obj2 = con2.newInstance();
System.out.println(obj2);
Object obj3 = con3.newInstance(10, "旺財");
System.out.println(obj3);
System.out.println("==============");
//4.如果是非公有
Constructor con4 = cc.getDeclaredConstructor(String.class);
//設置暴力訪問權限
con4.setAccessible(true);
Object obj4 = con4.newInstance("旺財");
System.out.println(obj4);
}
}
除外獲取某個構造,也可以獲取所有構造
public Constructor[] getConstructors(); 獲取所有的public修飾的構造方法
public Constructor[] getDeclaredConstructors(); 獲取所有的構造方法,包括private修飾的
8.通過反射獲取成員方法&&調用成員方法*******
public Method getMethod(String methodName, Class... args); 獲取"public"某個成員方法
a.如果是私有的成員方法怎麼獲取和調用?
public Method getDeclaredMethod(String methodName, Class... args); 獲取任意修飾符某個成員方法
私有成員方法調用之前先設置暴力訪問權限
成員方法對象.setAccessible(true);
public class MethodDemo {
public static void main(String[] args) throws Exception {
//1.獲取Class對象
Class cc = Cat.class;
//2.獲取成員方法
Method m1 = cc.getMethod("eat", String.class);
Method m2 = cc.getMethod("eat", String.class,String.class);
//3.調用成員方法
Cat jf = new Cat();
m1.invoke(jf,"魚");
m2.invoke(jf, "魚", "魚湯");
//4.如果是私有的
Method m3 = cc.getDeclaredMethod("climb");
//設置暴力權限
m3.setAccessible(true);
m3.invoke(jf);
}
}
除了獲取單個成員方法之外,也可以獲取全部的成員方法
public Method[] getMethods() (瞭解)
獲取所有的public修飾的成員方法,包括父類中。
public Method[] getDeclaredMethods() (瞭解)
獲取當前類中所有的方法,包含私有的,不包含父類中
9.通過反射獲取成員屬性(瞭解即可)
因爲實際開發中,所有屬性都會有set/get方法,我們只需要反射獲取方法即可
public Field getField(String fieldName); 獲取"public"屬性
public Field getDeclaredField(String fieldName); 獲取"任意修飾符"屬性
public class FieldDemo {
public static void main(String[] args) throws Exception {
//1.獲取Class對象
Class cc = Cat.class;
//2.獲取屬性
// Field field1 = cc.getField("age");
Field field1 = cc.getDeclaredField("age");
System.out.println(field1);
//3.獲取屬性的值和設置屬性值
Cat jf = new Cat();
field1.setAccessible(true);
field1.set(jf,10);
System.out.println(jf);
Object obj = field1.get(jf);
System.out.println(obj);
}
}
三.註解
1.JDK1.5新特性——註解
註解:一種標記,主要是給編譯器或者JVM看的
2.註解的兩個作用
a.編譯檢查
b.作爲框架的配置文件(之前學了個.properties文件(屬性集))
3.常用的兩個註解介紹
@Override 方法重寫註解
@FunctionalInterface 函數式接口註解
@Deprecated 過時方法/類註解
4.自定義註解*************
類:class
接口:interface
枚舉:enum
註解:@interface
格式:
public @interface 註解名{
}
使用:
@註解名(在哪都能用)
5.給自定義註解添加屬性***********
格式:
public @interface 註解名{
數據類型 屬性名();
數據類型 屬性名() default 默認值;
}
註解中屬性的數據類型,必須是以下幾種之一:
a.八大基本類型(包裝類不行)
b.String,enum,Class,其他註解
c.以上12種類型的一維數組
6.自定義註解練習
public @interface Book {
//屬性
//基本類型
int price() default 20;
//引用類型
String name();
//一維數組
String[] authors();
}
@Book(price = 10,name = "紅樓夢",authors = "曹雪芹")
public class Demo {
@Book(name = "西遊記",authors = "吳承恩")
public void show() {
}
}
7.使用註解時的注意事項
注意:
1.使用註解必須保證每個屬性都有值
2.沒有默認的值必須賦值,有默認的值可以不賦值,也可以重新賦值
8.自定義註解中的特殊屬性名value
a.如果註解中只有一個屬性,且名字叫value,那麼使用註解給該屬性賦值時,可以省略value
b.如果註解中除了value屬性之外,還有其他屬性,並且其他屬性都有默認值,那麼使用該註解時,如果需要給value賦值,那麼value可以省略
總結:如果使用註解時,只給value的屬性賦值時,那麼value可省略
9.註解的註解——元註解
元註解:修飾我們的註解
兩個元註解的介紹
@Retention 作用:修飾我們註解,代表我們註解的生命週期
聲明週期只能有以下幾種:
RetentionPolicy.SOURCE
表示我們的註解只在源碼階段存在
RetentionPolicy.CLASS(默認值)
表示我們的註解在源碼和字節碼階段存在
RetentionPolicy.RUNTIME
表示我們的註解在源碼階段和字節碼階段和內存中都存在
@Target 作用:修飾我們的註解,代表我們註解的可作用目標(默認任何地方都能使用)
作用目標只能有以下幾種(常用):
ElementType.TYPE 作用目標:類和接口
ElementType.FIELD 作用目標:成員變量
ElementType.METHOD 作用目標:成員方法
ElementType.PARAMETER 作用目標:方法參數
ElementType.CONSTRUCTOR 作用目標:構造方法
ElementType.LOCAL_VARIABLE 作用目標:局部變量
10.註解的解析**************
什麼是註解解析:
通過代碼,獲取出某個註解的所有屬性值
解析步驟:
a.獲取註解所在類的Class對象
b.獲取註解所在的目標對象
c.判斷該目標對象是否存在我們要的註解
d.從目標對象上獲取我們要的註解
e.從註解上獲取屬性值即可
/**
* 自定義註解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Student {
int age();
String name();
}
public class Dog {
int age;
String name;
public Dog() {
}
public Dog(int age, String name) {
this.age = age;
this.name = name;
}
@Student(age = 111, name = "張三")
public void eat() {
}
}
public class AnnotationTestDemo {
public static void main(String[] args) throws NoSuchMethodException {
// 解析步驟:
// a.獲取註解所在類的Class對象
Class clazz = Dog.class;
// b.獲取註解所在的目標對象
Method eatMethod = clazz.getMethod("eat");
// c.判斷該目標對象是否存在我們要註解
boolean b = eatMethod.isAnnotationPresent(Student.class);
if (b) {
//有註解
//d.從目標對象上獲取我們要的註解
Student studentAnnotation = eatMethod.getAnnotation(Student.class);
//e.從註解上獲取屬性值即可
int age = studentAnnotation.age();
String name = studentAnnotation.name();
System.out.println(age+"...."+name);
}else{
System.out.println("沒有此註解...");
}
}
}
11.綜合練習:模擬Junit的@Test註解***************
/**
* 自定義註解:模擬Junit的@Test註解
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyTest {
}
public class Test {
@MyTest
public void test01() {
System.out.println("1111111111");
}
public void test02() {
System.out.println("1111111111");
}
public void test03() {
System.out.println("1111111111");
}
@MyTest
public void test04() {
System.out.println("1111111111");
}
}
public class TestDemo {
public static void main(String[] args) throws Exception {
//右鍵模擬@Tets是做不到...
//我們使用main方法模擬,當運行main方法時,要求Test類中
//被@Mytest修飾的註解自動運行
//1.獲取我們註解所在的類
Class testClass = Test.class;
//2.獲取我們註解的作用的目標對象
Method[] methods = testClass.getMethods();
//3.遍歷數組
for (Method method : methods) {
//4.判斷該目標上是否有我們要的註解
boolean b = method.isAnnotationPresent(MyTest.class);
//5.判斷
if (b) {
method.invoke(new Test());
}
}
}
}
總結:
1.能夠使用Junit進行單元測試
a.編寫被測試類(業務類)
b.編寫測試類,測試類中使用@Test修飾的普通方法即可
c.右鍵運行該@Test修飾的方法即可
2.能夠通過反射技術獲取Class字節碼對象
類名.class
對象名.getClass();
Class.forName("全限定類名");
3.能夠通過反射技術獲取構造方法對象,並創建對象
Class cc = Dog.class;
Constructor con = cc.get[Declared]Constructor(Class... argsType)
Object 對象名 = con.newInstance(實際參數);
4.能夠通過反射獲取成員變量方法對象,並且調用方法
Class cc = Dog.class;
Method method = cc.get[Declared]Method(String methodName, Class... argsType)
method.invoke(new Dog(), 實際參數);
5.能夠說出註解的作用
6.能夠自定義註解和使用註解
格式:
public @interface 註解名{
數據類型 屬性名();
數據類型 屬性名() default 默認值;
}
使用:
@註解名(屬性名=屬性值,屬性名=屬性值,...)
7.能夠說出常用的元註解及其作用
@Target
@Retension
8.能夠解析註解並獲取註解中的數據**********
a.獲取Class對象
b.獲取目標對象
c.判斷目標對象是否有該註解
d.如果有取出該註解
e.從註解中取出屬性值
9.能夠完成註解的MyTest案例**************