Java學習日誌(二十五): Junit單元測試,反射技術

JavaEE學習日誌持續更新----> 必看!JavaEE學習路線(文章總彙)

Junit單元測試

Junit單元測試:單獨執行某一個方法

使用步驟:

  1. 導入Junit的jar包
  2. 在方法上添加一個@Test註解,並導入test註解所在的包
  3. 點擊方法左邊的綠色三角/右鍵選擇方法,選擇run執行方法
    點擊類左邊的綠色三角/右鍵選擇方法,選擇run執行全部被@Test修飾的方法

Junit的注意事項
1.沒有被@Test註解修飾的方法,不能使用Junit單獨執行
2.Junit只能執行public void 修飾的空參數方法

Junit相關的註解:

  • @Test,用於修飾需要執行的測試方法
  • @Before,修飾的方法會在測試方法之前被自動執行,用於獲取資源
  • @After,修飾的方法會在測試方法執行之後自動被執行,用於釋放資源

注意:
@Before和@After修飾的方法,不能單獨執行

代碼示例:

public class Demo03 {
    @Test
    public void test01() {
        System.out.println("test01");
    }

    @Test
    public void test02() {
        System.out.println("test02");
    }

    @Test
    public void test03() {
        System.out.println("test03");
    }

    @Before
    public void myBefore() {
        System.out.println("myBefore");
    }

    @After
    public void myAfter() {
        System.out.println("myAfter");
    }
}

反射

類加載器

在這裏插入圖片描述

反射的概念與原理

看圖就完事了
在這裏插入圖片描述

獲取class文件對象的方式

獲取class文件對象的方式(重點):

  1. 可以使用Object類中的方法,獲取class文件對象
    Class<?> getClass() 返回此 Object的運行時類。
  2. 可以使用數據類型.class屬性,獲取class文件對象
    java爲每種數據類型,都賦予了一個class屬性
    基本數據類型(4類8種):int.class,double.class
    引用數據類型(類,接口,數組):Person.class,String.class,ArrayList<String>.class
  3. 可以使用class類中的靜態方法,獲取class文件對象
    static Class<?> forName​(String className) 返回與具有給定字符串名稱的類或接口關聯的類對象。
    參數:String className:全類名(包名+類名:cn.itcast.demo02.Reflect.Person)
    選中類名,右鍵copyReference即可拷貝包名+類名

注意:class文件對象是由類加載器創建的,無論用哪種方法獲取,都是同一個class文件對象,class文件對象只有一個

代碼示例:Person類

public class Person {
    private String name;
    private int age;

    private Person(String name) {
        this.name = name;
        System.out.println("Person類的私有構造方法");
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
        System.out.println("Person類的滿參數構造方法");
    }

    public Person() {
        System.out.println("Person類的空參數構造方法");
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    private void method(){
        System.out.println("私有方法");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

測試類

public class Demo01 {
    public static void main(String[] args) throws ClassNotFoundException {
        //1.可以使用Object類中的方法,獲取class文件對象
        Person p = new Person();
        Class c1 = p.getClass();
        System.out.println(c1);//class cn.itcast.demo02.Reflect.Person
        //2.可以使用數據類型.class屬性,獲取class文件對象
        /*int.class;
        double.class;
        String.class;*/
        Class c2 = Person.class;
        System.out.println(c2);//class cn.itcast.demo02.Reflect.Person
        //3.可以使用class類中的靜態方法,獲取class文件對象
        Class c3 = Class.forName("cn.itcast.demo02.Reflect.Person");
        System.out.println(c3);//class cn.itcast.demo02.Reflect.Person
        //class文件對象只有一個
        System.out.println(c1==c2);//true
        System.out.println(c1==c3);//true
        System.out.println(c2==c3);//true
    }
}

Class類中的常用方法

Class類中的常用方法

  • String getSimpleName():獲得簡單類名,只是類名,沒有包
  • String getName():獲取全類名,包含包名+類名

在Person類中加入一個靜態代碼塊

static {
    System.out.println("靜態代碼塊");
}

代碼示例:獲取class文件對象,會執行類中的靜態代碼塊

public class Demo02 {
    public static void main(String[] args) throws ClassNotFoundException {
        //獲取class文件對象,會執行類中的靜態代碼塊-->mysql-->JDBC
        Class clazz = Class.forName("cn.itcast.demo02.Reflect.Person");
        String simpleName = clazz.getSimpleName();
        System.out.println(simpleName);//Person
        String name = clazz.getName();
        System.out.println(name);//cn.itcast.demo02.Reflect.Person

    }
}

獲取類中的構造方法並創建對象

使用反射技術獲取類中的構造方法,並使用獲取到的構造方法創建對象

獲取構造方法

實現步驟

  1. 獲取類的class文件對象
  2. 使用class文件對象中的方法getConstructor/Constructors獲取類中的構造方法
  3. 使用Constructor類中的方法newInstance創建對象(實例化對象)

Person類中的三個構造方法:
public Person() :空參構造
private Person(String name) :私有構造
public Person(String name, int age) :滿參構造

成員方法

  • 構造器<?>[] getConstructors() 返回一個包含構造器對象的數組,構造器對象反映了此類對象所表示的類的所有公共構造函數
  • 構造器<?>[] getDeclaredConstructors() 返回構造器對象的數組,構造器對象反映由此類對象表示的類聲明的所有構造函數(包括私有)
  • 構造器<T> getConstructor​(類<?>... parameterTypes) 返回一個 構造器對象,該對象反映此 類對象所表示的類的指定公共構造函數
    參數:類<?>… parameterTypes:可變參數,構造方法參數的class文件對象
  • 構造器<T> getDeclaredConstructor​(類<?>... parameterTypes) 返回一個構造器對象,該對象反映此類對象所表示的類或接口的指定構造函數(包括私有)。

注意:如果類中沒有指定的構造方法,會拋出NoSuchMethodException異常

代碼示例:獲取類中的構造方法

public class Demo03GetConstructor {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
        //1.獲取類的class文件對象
        Class<?> clazz = Class.forName("cn.itcast.demo02.Reflect.Person");
        //2.使用class文件對象中的方法getConstructor/Constructors獲取類中的構造方法
        //構造器<?>[] getConstructors()
        Constructor<?>[] cons = clazz.getConstructors();
        for (Constructor<?> con : cons) {
            System.out.println(con);
        }
        System.out.println("--------------------");
        //構造器<?>[] getDeclaredConstructors()
        Constructor<?>[] declaredCons = clazz.getDeclaredConstructors();
        for (Constructor<?> con : declaredCons) {
            System.out.println(con);
        }
        System.out.println("--------------------");
        //構造器<T> getConstructor​(類<?>... parameterTypes)
        //public Person()
        Constructor<?> con1 = clazz.getConstructor();
        System.out.println(con1);//public cn.itcast.demo02.Reflect.Person()
        //public Person(String name, int age)
        Constructor<?> con2 = clazz.getConstructor(String.class, int.class);
        System.out.println(con2);

        //構造器<T> getDeclaredConstructor​(類<?>... parameterTypes)
        //private Person(String name)
        Constructor<?> con3 = clazz.getDeclaredConstructor(String.class);
        System.out.println(con3);

    }
}

newInstance方法創建對象

java.lang.reflect.Constructor :描述構造方法的類
使用Constructor類中的方法newInstance創建對象(實例化對象)

成員方法
T newInstance​(Object... initargs) :實例化對象,創建對象

參數:Object... initargs:可變參數,構造方法創建對象實際使用的參數
返回值:T:創建好的對象,類型使用Object類型

私有的構造方法,沒有權限訪問,執行的時候有權限檢查,會拋出
IllegalAccessException非法訪問異常

可以使用Constructor的父類中的方法解決這個問題

Constructor extends AccessibleObject

void setAccessible​(boolean flag) 將此反射對象的 accessible標誌設置爲指示的布爾值。

  • true:表示反射對象在使用時應禁止檢查Java語言訪問控制
  • false:表示反射對象在使用時應強制檢查Java語言訪問控制

代碼示例:

public class Demo03GetConstructor {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        //1.獲取類的class文件對象
        Class<?> clazz = Class.forName("cn.itcast.demo02.Reflect.Person");
        //2.使用class文件對象中的方法getConstructor/Constructors獲取類中的構造方法
        //構造器<?>[] getConstructors()
        Constructor<?>[] cons = clazz.getConstructors();
        for (Constructor<?> con : cons) {
            System.out.println(con);
        }
        System.out.println("--------------------");
        //構造器<?>[] getDeclaredConstructors()
        Constructor<?>[] declaredCons = clazz.getDeclaredConstructors();
        for (Constructor<?> con : declaredCons) {
            System.out.println(con);
        }
        System.out.println("--------------------");
        //構造器<T> getConstructor​(類<?>... parameterTypes)
        //public Person()
        Constructor<?> con1 = clazz.getConstructor();
        System.out.println(con1);//public cn.itcast.demo02.Reflect.Person()
        //public Person(String name, int age)
        Constructor<?> con2 = clazz.getConstructor(String.class, int.class);
        System.out.println(con2);

        //構造器<T> getDeclaredConstructor​(類<?>... parameterTypes)
        //private Person(String name)
        Constructor<?> con3 = clazz.getDeclaredConstructor(String.class);
        System.out.println(con3);

        //3.使用Constructor類中的方法newInstance創建對象(實例化對象)
        /*
            java.lang.reflect.Constructor<T> :描述構造方法的類
            T newInstance​(Object... initargs) :實例化對象,創建對象
            參數:Object... initargs:可變參數,構造方法創建對象實際使用的參數
            返回值:T:創建好的對象,類型使用Object類型

         */
        //public Person()
        Object obj1 = con1.newInstance();//此方法相當於new Person();
        System.out.println(obj1);//Person{name='null', age=0}
        /*Person p = (Person)obj1;
        System.out.println(p);////Person{name='null', age=0}*/

        //public Person(String name, int age)
        Object obj2 = con2.newInstance("老王",18);//此方法相當於new Person("老王",18);
        System.out.println(obj2);
        /*
            私有的構造方法,沒有權限訪問,執行的時候有權限檢查,會拋出
            IllegalAccessException非法訪問異常。
            可以使用Constructor的父類中的方法解決這個問題
                Constructor extends AccessibleObject
                void setAccessible​(boolean flag) 將此反射對象的 accessible標誌設置爲指示的布爾值。
                值true表示反射對象在使用時應禁止檢查Java語言訪問控制。
                值false表示反射對象在使用時應強制檢查Java語言訪問控制

            注意:暴力反射不建議使用,破壞了類的封裝性
         */
        //private Person(String name)
        con3.setAccessible(true);//取消con3構造方法的權限檢查-->暴力反射
        Object obj3 = con3.newInstance("老張");
        System.out.println(obj3);
    }
}

簡便寫法–>直接創建空參構造

使用反射技術獲取構造方法並創建對象的簡便方式:

Class類中的成員方法
T newInstance() 直接獲取空參構造並創建對象返回,省略了獲取Constructor的過程

使用前提:

  1. 類中必須有空參構造
  2. 空參數構造方法的修飾符不能是private,建議使用public

代碼示例:

public class Demo04 {
    public static void main(String[] args) throws Exception {
        //獲取class文件對象
        Class<?> clazz = Class.forName("cn.itcast.demo02.Reflect.Person");
        //直接使用newInstance方法獲取對象
        Object obj = clazz.newInstance();
        System.out.println(obj);
        //取代了
        Object obj2 = clazz.getDeclaredConstructor().newInstance();
        System.out.println(obj2);

    }
}

獲取類中的成員方法並執行

獲取成員方法

使用反射技術獲取類中的成員方法,並執行
執行步驟

  1. 獲取類的class文件對象
  2. 使用class文件對象中的方法getMethod/getMethods獲得成員方法
  3. 使用Method類中的方法invoke執行獲取到的成員方法

Person類中的成員方法

  • public String getName()
  • public void setName(String name)
  • private void method()

Class類中的方法:

  • 方法[] getMethods() 獲取類中所有公共的成員方法,包括父類和接口中的

  • 方法[] getDeclaredMethods() 返回一個包含 方法對象的數組, 方法對象反映此 類對象表示的類或接口的所有已聲明方法,包括public,protected,default(package)訪問和私有方法,但不包括繼承的方法。

  • 方法 getMethod​(String name, 類<?>... parameterTypes)獲取指定的公共成員方法

  • 方法 getDeclaredMethod​(String name, 類<?>... parameterTypes) 返回 方法對象,該對象反映此 類對象表示的類或接口的指定聲明方法。

      參數:
      String name:方法名稱
      類<?>... parameterTypes:成員方法參數列表的class文件對象
    

注意:類中沒有指定的成員方法,會拋出NoSuchMethodException

代碼示例:

public class Demo05 {
    public static void main(String[] args) throws NoSuchMethodException {
        //1.獲取類的class文件對象
        Class clazz = Person.class;
        //2.使用class文件對象中的方法getMethod/getMethods獲得成員方法
        /*
            方法[] getMethods() 獲取類中所有公共的成員方法,包括父類和接口中的
            方法[] getDeclaredMethods() 返回一個包含 方法對象的數組, 方法對象反映此 類對象表示的類或接口的所有已聲明方法,包括public,protected,default(package)訪問和私有方法,但不包括繼承的方法。

         */
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("------------------------------------");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method);
        }
        System.out.println("------------------------------------");
        /*
            方法 getMethod​(String name, 類<?>... parameterTypes)獲取指定的公共成員方法
            方法 getDeclaredMethod​(String name, 類<?>... parameterTypes) 返回 方法對象,該對象反映此 類對象表示的類或接口的指定聲明方法。
            參數:String name:方法名稱
                 類<?>... parameterTypes:成員方法參數列表的class文件對象
            注意:類中沒有指定的成員方法,會拋出NoSuchMethodException

         */
        //public String getName()
        Method getNameMethod = clazz.getMethod("getName");
        System.out.println(getNameMethod);
        //public void setName(String name)
        Method setNameMethod = clazz.getMethod("setName", String.class);
        System.out.println(setNameMethod);
        //private void method()
        Method privateMethod = clazz.getDeclaredMethod("method");
        System.out.println(privateMethod);
    }
}

invoke方法執行獲取到的成員方法

java.lang.reflect.Method :描述成員方法的類

Method類中的invoke方法
Object invoke​(Object obj, Object... args) 執行成員方法

參數:
    Object obj:invoke方法需要對象支持,執行的是哪個類的方法,就傳遞哪個對象,執行的是Person類的方法,傳遞Person對象(clazz.newInstance())。
    Object... args:成員方法執行的時候,需要的實際參數。
返回值:
    Object:方法的返回值,如果方法有返回值則返回對應的值,如果沒有返回值,返回null。

注意:私有方法沒有權限執行,有權限檢查,會拋出IllegalAccessException
使用暴力反射,取消權限檢查。

代碼示例:

public class Demo05 {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        //1.獲取類的class文件對象
        Class clazz = Person.class;
        //2.使用class文件對象中的方法getMethod/getMethods獲得成員方法
        /*
            方法[] getMethods() 獲取類中所有公共的成員方法,包括父類和接口中的
            方法[] getDeclaredMethods() 返回一個包含 方法對象的數組, 方法對象反映此 類對象表示的類或接口的所有已聲明方法,包括public,protected,default(package)訪問和私有方法,但不包括繼承的方法。

         */
        Method[] methods = clazz.getMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("------------------------------------");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method method : declaredMethods) {
            System.out.println(method);
        }
        System.out.println("------------------------------------");
        /*
            方法 getMethod​(String name, 類<?>... parameterTypes)獲取指定的公共成員方法
            方法 getDeclaredMethod​(String name, 類<?>... parameterTypes) 返回 方法對象,該對象反映此 類對象表示的類或接口的指定聲明方法。
            參數:String name:方法名稱
                 類<?>... parameterTypes:成員方法參數列表的class文件對象
            注意:類中沒有指定的成員方法,會拋出NoSuchMethodException

         */
        //public String getName()
        Method getNameMethod = clazz.getMethod("getName");
        System.out.println(getNameMethod);
        //public void setName(String name)
        Method setNameMethod = clazz.getMethod("setName", String.class);
        System.out.println(setNameMethod);
        //private void method()
        Method privateMethod = clazz.getDeclaredMethod("method");
        System.out.println(privateMethod);
        System.out.println("----------------------------------");
        //3.使用Method類中的方法invoke執行獲取到的成員方法
        /*
            java.lang.reflect.Method :描述成員方法的類
            Object invoke​(Object obj, Object... args) 執行成員方法
            參數:Object obj:invoke方法需要對象支持,執行的是哪個類的方法,就傳遞哪個對象
                    執行的是Person類的方法,傳遞Person對象(clazz.newInstance())
                 Object... args:成員方法執行的時候,需要的實際參數
            返回值:Object:方法的返回值
                    如果方法有返回值則返回對應的值,如果沒有返回值,返回null

         */
        Object obj = clazz.newInstance();
        //public String getName()
        Object v1 = getNameMethod.invoke(obj);//此方法相當於執行了getName方法
        System.out.println("v1:"+v1);//v1:null
        ////public void setName(String name)
        Object v2 = setNameMethod.invoke(obj,"老王");//此方法相當於執行了setName方法,給成員遍歷賦值
        System.out.println("v2:"+v2);//v2:null 方法沒有返回值
        System.out.println(obj);
        /*
            私有方法沒有權限執行,有權限檢查,會拋出IllegalAccessException
            使用暴力反射,取消權限檢查
         */
        privateMethod.setAccessible(true);
        //private void method()
        privateMethod.invoke(obj);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章