JavaSEday15Junit、反射和註解

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案例**************
            

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章