JAVA基礎加強篇12——單元測試、反射、註解、動態代理

自在现实發表於2024-06-25

單元測試、反射、註解、動態代理 課程安排

單元測試
單元測試概述
單元測試
單元測試就是針對最小的功能單元編寫測試程式碼,Java 程式最小的功能單元是方法,因此,單元測試就是針對 Java 方法的測試,進而檢查方法的正確性。
目前測試方法是怎麼進行的,存在什麼問題
只有一個 main 方法,如果一個方法的測試失敗了,其他方法測試會受到影響。
無法得到測試的結果報告,需要程式設計師自己去觀察測試是否成功。
無法實現自動化測試。
JUnit 單元測試框架
JUnit 是使用 Java 語言實現的單元測試框架,它是開源的,Java 開發者都應當學習並使用 JUnit 編寫單元測試。
此外,幾乎所用的 IDE 工具都整合了 JUnit,這樣我們就可以直接在 IDE 中編寫並執行 JUint 測試,JUnit 目前最先版本是 5。
Junit 優點
JUnit 可以靈活的選擇執行哪些測試方法,可以一鍵執行全部測試方法。
JUnit 可以生成全部方法的測試報告。

總結
JUnit 單元測試是做什麼的?
測試類中方法的正確性的。
JUnit 單元測試的優點是什麼?
JUnit 可以選擇執行哪些測試方法,可以一鍵執行全部測試方法的測試。
JUnit 可以生成測試報告,如果測試良好則是綠色;如果測試失敗,則是紅色。
單元測試中的某個方法測試失敗了,不會影響其他測試方法的測試。
單元測試快速入門
步驟 單元測試快速入門
需求:使用單元測試進行業務方法預期結果、正確性測試的快速入門

分析:

將 JUint 的 jar 包匯入到專案中

IDEA 通常整合好可了 JUint 框架,一般需要要匯入。

如果 IDEA 沒有整合好,需要自己手工匯入如下 2 個 JUnit 的 jar 包到模板

編寫測試方法:該測試方法必須是公共的無引數無返回值的非靜態方法。

在測試方法上使用 @Test 註解:標註該方法是一個測試方法。

在測試方法中完成被測試方法的預期正確性測試。

選中測試方法,選中 “JUnit 執行”,如果 測試良好則是綠色;如果測試失敗,則是紅色。

業務方法

/**

  • @author : gxd
  • @date : 2022/7/22 17:27
  • 業務方法
    */
    public class UserService {
    public String loginName(String loginName,String password){
    if ("admin".equals(loginName) && "123456".equals(password)){
    return "登入成功";
    }else {
    return "使用者名稱或者密碼有問題";
    }
    }
    public void selectNames(){
    System.out.println(10/0);
    System.out.println("查詢全部使用者名稱稱成功~~~");
    }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
測試類

/**

  • @author : gxd
  • @date : 2022/7/22 17:32
  • 測試類
  • 目標:單元測試快速入門
  • 步驟 單元測試快速入門
  • 需求:使用單元測試進行業務方法預期結果、正確性測試的快速入門
  • 分析:
    1. 將 JUint 的 jar 包匯入到專案中
  • - IDEA 通常整合好可了 JUint 框架,一般需要要匯入。
    
  • - 如果 IDEA 沒有整合好,需要自己手工匯入如下 2 個 JUnit 的 jar 包到模板
    
    1. 編寫測試方法:該測試方法必須是公共的無引數無返回值的非靜態方法。
    1. 在測試方法上使用 @Test 註解:標註該方法是一個測試方法。
    1. 在測試方法中完成被測試方法的預期正確性測試。
    1. 選中測試方法,選中 “JUnit 執行”,如果 試良好.則是綠色;如果測試失敗,則是紅色。

/
public class TestUserService {
/
*
* 測試方法
* 注意點:
* 1、必須是公開的,無引數 無返回值的方法
* 2、測試方法必須使用 @Test 註解標記
*/
@Test
public void testLoginName(){
UserService userService = new UserService();
String rs = userService.loginName("admin","123456");

    //進行預期結構的正確性測試:斷言。
    /**
     * public static void assertEquals(String message, Object expected, Object actual)
     * 引數一:訊息提示
     * 引數二:你調的這個方法的 userService.loginName 的返回結果一樣 ,預期的結果
     * 引數三:實際的結果
     */
    Assert.assertEquals("您的登入業務可能出現問題","登入成功",rs);
}

@Test
public void testSelectNames(){
    UserService userService = new UserService();
    userService.selectNames();
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
總結
JUnit 單元測試的實現過程是什麼樣的?
必須匯入 JUnit 框架的 jar 包。
定義的測試方法必須是無引數無返回值,且公開的方法。
測試方法使用 @Test 註解標記。
JUint 測試某個方法,測試全部方法怎麼處理?成功的標誌是什麼?
測試某個方法直接右鍵該方法啟動測試。
測試全部方法,可以選擇類或者模組啟動。
紅色失敗,綠色成功。
單元測試常用註解
JUnit 常用註解(JUnit 4.xxxx版本)

開始執行的方法:初始化資源。
執行完畢之後的方法:釋放資源。
/**

  • @author : gxd
  • @date : 2022/7/22 17:32
  • 測試類
  • 目標:單元測試快速入門
  • 步驟 單元測試快速入門
  • 需求:使用單元測試進行業務方法預期結果、正確性測試的快速入門
  • 分析:
    1. 將 JUint 的 jar 包匯入到專案中
  • - IDEA 通常整合好可了 JUint 框架,一般需要要匯入。
    
  • - 如果 IDEA 沒有整合好,需要自己手工匯入如下 2 個 JUnit 的 jar 包到模板
    
    1. 編寫測試方法:該測試方法必須是公共的無引數無返回值的非靜態方法。
    1. 在測試方法上使用 @Test 註解:標註該方法是一個測試方法。
    1. 在測試方法中完成被測試方法的預期正確性測試。
    1. 選中測試方法,選中 “JUnit 執行”,如果 試良好.則是綠色;如果測試失敗,則是紅色。

*/
public class TestUserService {

//修飾例項方法的
@Before
public void before(){
    System.out.println("======before方法執行一次======");
}
@After
public void after(){
    System.out.println("======after方法執行一次======");
}

//修飾靜態方法
@BeforeClass
public static void beforeClass(){
    System.out.println("======beforeClass方法執行一次======");
}
@AfterClass
public static void afterClass(){
    System.out.println("======afterClass方法執行一次======");
}

/**
 * 測試方法
 * 注意點:
 *    1、必須是公開的,無引數 無返回值的方法
 *    2、測試方法必須使用 @Test 註解標記
 */
@Test
public void testLoginName(){
    UserService userService = new UserService();
    String rs = userService.loginName("admin","123456");

    //進行預期結構的正確性測試:斷言。
    /**
     * public static void assertEquals(String message, Object expected, Object actual)
     * 引數一:訊息提示
     * 引數二:你調的這個方法的 userService.loginName 的返回結果一樣 ,預期的結果
     * 引數三:實際的結果
     */
    Assert.assertEquals("您的登入業務可能出現問題","登入成功",rs);
}

@Test
public void testSelectNames(){
    UserService userService = new UserService();
    userService.selectNames();
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
JUnit 常用註解(JUnit 5.xxxx 版本)

開始執行的方法:初始化資源。
執行完畢之後的方法:釋放資源。
使用與(JUnit 4.xxxx版本) 一樣,只是名字換了。

反射
反射概述
反射概述
反射是指對於任何一個 Class 類,在“執行的時候”都可以直接得到這個類全部部分。
在執行時,可以直接得到這個類的構造器物件:Constructor
在執行時,可以直接得到這個類的成員變數物件:Field
在執行時,可以直接得到這個類的成員方法物件:Method
這種執行時動態獲取類資訊以及動態呼叫類中成分的能力稱為 Java 語言的反射機制。
反射的關鍵:
反射的第一步都是先得到編譯後的 Class 類物件,然後就可以得到 Class 的全部成分。
|

總結
反射的基本作用、關鍵?
反射是在執行時獲取類的位元組碼檔案物件:然後可以解析類中的全部成分。
反射的核心思想和關鍵就是:得到編譯以後的 class 檔案物件。
反射獲取類物件
反射的第一步:獲取 Class 類的物件

Student

/**

  • @author : gxd
  • @date : 2022/7/23 17:31
    */
    public class Student {
    }
    1
    2
    3
    4
    5
    6
    Test

/**

  • @author : gxd

  • @date : 2022/7/23 17:32

  • 目標:反射的第一步:獲取Class物件
    */
    public class Test {
    public static void main(String[] args) throws Exception {
    //1、Class 類中的一個靜態方法:forName(全限名:包名 + 類名)
    Class c = Class.forName("com.zwzl.d2_reflect_class.Student");
    System.out.println(c);//Student.class

     //2、類名.class
     Class c1 = Student.class;
     System.out.println(c1);
    
     //3、物件.getClass() 獲取物件對應類的Class物件
     Student s = new Student();
     Class c2 = s.getClass();
     System.out.println(c2);
    

    }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
總結
反射的第一步是什麼?
獲取 Class 類物件,如此才可以解析類的全部成分
獲取 Class 類的物件三種方式
方式一:Class c1 = Class.forName(“全類名”);
方式二:Class c2 = 類名.class;
方式三:Class c3 = 物件.getClass();
反射獲取構造器物件
使用反射技術獲取構造器物件並使用

使用反射技術獲取構造器物件並使用
反射的第一步是先得到類物件,然後從類物件中獲取類的成分物件。

Class 類中用於構造器的方法

Student

/**

  • @author : gxd

  • @date : 2022/7/23 17:54
    */
    public class Student {
    private String name;
    private int age;

    private Student() {
    System.out.println("無引數構造器執行!");
    }

    public Student(String name, int age) {
    System.out.println("有引數構造器執行!");
    this.name = name;
    this.age = age;
    }

    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;
    }

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
TestStudent1

/**

  • @author : gxd

  • @date : 2022/7/23 17:56

  • 目標:使用反射技術獲取構造器物件並使用

    • 反射的第一步是先得到類物件,然後從類物件中獲取類的成分物件。
    • Class 類中用於構造器的方法
      /
      public class TestStudent1 {
      /
      *
      • 1、getConstructors:
      • 獲取全部的構造器:只能獲取public修飾的構造器。
      • public Constructor<?>[] getConstructors()
        */
        @Test
        public void getConstructors(){
        //a、第一步:獲取類物件
        Class c = Student.class;
        //b、提取類中的全部的構造器物件(這裡只能拿public修飾的構造器)
        Constructor[] constructors = c.getConstructors();
        //c、遍歷構造器
        for (Constructor constructor : constructors) {
        System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
        }
        }

    /**

    • 2、getDeclaredConstructors():
    • 獲取全部的構造器:只要你敢寫,這裡就能拿到,無所謂許可權是否可及。
    • ublic Constructor<?>[] getDeclaredConstructors()
      */
      @Test
      public void getDeclaredConstructors(){
      //a、第一步:獲取類物件
      Class c = Student.class;
      //b、提取類中的全部的構造器物件
      Constructor[] constructors = c.getDeclaredConstructors();
      //c、遍歷構造器
      for (Constructor constructor : constructors) {
      System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());
      }
      }

    /**

    • 3、getConstructor(Class… parameterTypes)

    • 獲取某個構造器:只能拿public修飾的某個構造器

    • public Constructor getConstructor(Class<?>... parameterTypes)
      */
      @Test
      public void getConstructor() throws Exception {
      //a、第一步:獲取類物件
      Class c = Student.class;
      //b、定位單個構造器物件(按照引數定位無引數構造器,只能拿public修飾的某個構造器)
      Constructor constructor = c.getConstructor();
      System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());

      //c、定位某個有參構造器(只能拿public修飾的某個構造器)
      Constructor constructor1 = c.getConstructor(String.class, int.class);
      System.out.println(constructor1.getName() + "===>" + constructor1.getParameterCount());
      }

    /**

    • 4、getDeclaredConstructor

    • 獲取某個構造器:只要你敢寫,這裡就能拿到,無所謂許可權是否可及。

    • public Constructor getDeclaredConstructor(Class<?>... parameterTypes)
      */
      @Test
      public void getDeclaredConstructor() throws Exception {
      //a、第一步:獲取類物件
      Class c = Student.class;
      //b、定位單個構造器物件(按照引數定位無引數構造器)
      Constructor constructor = c.getDeclaredConstructor();
      System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());

      //c、定位某個有參構造器
      Constructor constructor1 = c.getDeclaredConstructor(String.class, int.class);
      System.out.println(constructor1.getName() + "===>" + constructor1.getParameterCount());
      }
      }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
使用反射技術獲取構造器物件並使用
獲取構造器的作用依然是初始化一個物件返回。
Constructor 類中用於建立物件的方法

/**

  • @author : gxd
  • @date : 2022/7/23 17:57
  • 目標:使用反射技術獲取構造器物件並使用
    • 獲取構造器的作用依然是初始化一個物件返回。
  • Constructor 類中用於建立物件的方法
    • public T newInstance(Object ... initargs):根據指定的構造器建立物件
    • public void setAccessible(boolean flag):設定為 true,表示取消訪問檢查,進行暴力反射
      */
      public class TestStudent2 {
      //1、呼叫構造器得到一個類的物件返回。
      @Test
      public void getDeclaredConstructor() throws Exception {
      Class c = Student.class;
      Constructor constructor = c.getDeclaredConstructor();
      System.out.println(constructor.getName() + "===>" + constructor.getParameterCount());

      //如果遇到了私有的構造器,可以暴力反射
      constructor.setAccessible(true);//許可權被開啟

      Student s = (Student) constructor.newInstance();
      System.out.println(s);

      System.out.println("-------------------------------------");

      Constructor constructor1 = c.getDeclaredConstructor(String.class, int.class);
      System.out.println(constructor1.getName() + "===>" + constructor1.getParameterCount());

      Student s1 = (Student) constructor1.newInstance("張三", 35);
      System.out.println(s1);
      }
      }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
總結
利用反射技術獲取構造器物件的方式
getDeclaredConstructors()
getDeclaredConstructor(Class<?>… parameterTypes)
反射得到的構造器可以做什麼?
依然是建立物件的
public newInstance(Object… initargs)
如果是非 public 的構造器,需要開啟許可權(暴力反射),然後再建立物件
setAccessible(boolean)
反射可以破壞封裝性,私有的也可以執行了。
反射獲取成員變數物件
使用反射技術獲取成員變數物件並使用

使用反射技術獲取成員變數物件並使用
反射的第一步是先得到類物件,然後從類物件中或缺類的成分物件。

Class 類中用於獲取成員變數的方法

blog.csdnimg.cn/18757b8d91e24ab986297a3abdf112c1.png#pic_center)

Student

/**

  • @author : gxd

  • @date : 2022/7/23 23:07
    */
    public class Student {
    private String name;
    private int age;
    public static String schoolName;
    public static final String COUNTTPY = "中國";

    public Student() {
    System.out.println("無引數構造器執行!");
    }

    public Student(String name, int age) {
    System.out.println("有引數構造器執行!");
    this.name = name;
    this.age = age;
    }

    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;
    }

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
FieldTest1

/**

  • @author : gxd
  • @date : 2022/7/23 23:07
  • 目標:使用反射技術獲取成員變數物件並使用
    • 反射的第一步是先得到類物件,然後從類物件中或缺類的成分物件。
  • -Class類中用於獲取成員變數的方法
    • public Field[] getFields():返回所有成員變數物件的陣列(只能拿public的)
    • public Field[] getDeclaredFields():返回所有成員變數物件的陣列,存在就能拿到
    • public Field getField(String name):返回單個成員變數物件(只能拿public的)
    • public Field getDeclaredField(String name):返回單個成員變數物件,存在就能拿到
      /
      public class FieldTest1 {
      /
      *
    • 1、獲取全部的成員變數
    • public Field[] getDeclaredFields()
    • 獲取所有的成員變數對應的Field物件,只要申明瞭就可以得到
      */
      @Test
      public void getDeclaredFields(){
      //a、定位Class類
      Class c = Student.class;
      //b、定位全部成員變數
      Field[] fields = c.getDeclaredFields();
      //c、遍歷一下
      for (Field field : fields) {
      System.out.println(field.getName() + "====>" + field.getType());
      }
      }
/**
 * 2、獲取某個成員變數物件,只要申明瞭就可以得到
 * public Field getDeclaredField(String name)
 * 引數:成員變數名
 */
@Test
public void getDeclaredField() throws Exception {
    //a、定位Class物件
    Class c = Student.class;
    //b、根據名稱定位某個成員變數
    Field field = c.getDeclaredField("name");
    System.out.println(field.getName() + "===>" + field.getType());
}

/**
 * 3、返回所有成員變數物件的陣列(只能拿public的)
 * public Field[] getFields()
 */
@Test
public void getFields(){
    Class c = Student.class;
    //(這裡只能拿public修飾的成員變數)
    Field[] fields = c.getFields();
    for (Field field : fields) {
        System.out.println(field.getName() + "===>" + field.getType());
    }
}

/**
 * 4、返回單個成員變數物件(只能拿public的)
 * public Field getField(String name)
 */
@Test
public void getField() throws Exception {
    Class c = Student.class;
    //(只能拿public修飾的某個成員變數)
    Field field = c.getField("schoolName");
    System.out.println(field.getName() + "===>" + field.getType());
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
使用反射技術獲取成員變數物件並使用
獲取成員變數的作用依然是在某個物件中取值、賦值
Field 類中用於取值、賦值的方法

/**

  • @author : gxd

  • @date : 2022/7/23 23:07

  • 目標:反射獲取成員變數:取值和賦值

  • Field的方法:給成員變數賦值和取值

    • public void set(Object obj, Object value):給物件注入某個成員變數資料
    • public Object get(Object obj):獲取物件的成員變數的值
    • public void setAccessible(boolean flag):暴力反射,設定為可以直接訪問私有型別的屬性。
    • public Class<?> getType():獲取屬性的型別,返回Class物件
    • public String getName():獲取屬性的名稱
      */
      public class FieldTest2 {

    @Test
    public void setField() throws Exception{
    //a、定位Class物件
    Class c = Student.class;
    //b、根據名稱定位某個成員變數
    Field field = c.getDeclaredField("name");

    field.setAccessible(true);//暴力開啟許可權
    
    //c、賦值
    Student s = new Student();
    field.set(s,"張三");//s.setName("張三");
    System.out.println(s);
    
    //d、取值
    String name = (String) field.get(s);//s.getName();
    System.out.println(name);
    

    }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
總結
利用反射技術獲取成員變數的方式
獲取類中成員變數物件的方法
getDeclaredFields()
getDeclaredField(String name)
反射得到成員變數可以做什麼?
依然是某個物件中取值和賦值。
void set(Object obj, Object value)
Object get(Object obj)
如果某成員變數是非public的,需要開啟許可權(暴力反射),然後再取值、賦值。
setAccessible(boolean)
反射獲取方法物件
使用反射技術獲取方法物件並使用

使用反射技術獲取方法物件並使用
反射的第一步是先得到類物件,然後從類物件中獲取類的成分物件。

Class類中用於獲取成員方法的方法

使用發射技術獲取方法物件並使用
獲取成員方法的作用依然是在某個物件進行執行此方法
Method 類中用於觸發執行的方法

dog

/**

  • @author : gxd

  • @date : 2022/7/25 8:53
    */
    public class Dog {
    private String name;

    public Dog() {
    }

    public Dog(String name) {
    this.name = name;
    }

    public void run(){
    System.out.println("狗跑的賊快~~~");
    }

    private void eat(){
    System.out.println("狗吃骨頭");
    }

    private String eat(String name){
    System.out.println("狗吃" + name);
    return "吃的很開心";
    }
    public static void inAddr(){
    System.out.println("在吉山區有一群單身狗!");
    }

    public String getName() {
    return name;
    }

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
MethodTest1

/**

  • @author : gxd
  • @date : 2022/7/25 9:17
  • 目標:使用反射技術獲取方法物件並使用
  • 使用反射技術獲取方法物件並使用
    • 反射的第一步是先得到類物件,然後從類物件中獲取類的成分物件。
    • Class類中用於獲取成員方法的方法
    • public Method[] getMethods():、返回所有成員方法物件的陣列(只能拿public修飾的)
    • public Method[] getDeclaredMethods():返回所有成員方法物件的陣列,存在就能拿到
    • public Method getMethod(String name, Class<?>... parameterTypes):返回的單個成員方法物件(只能拿public修飾的)
    • public Method getDeclaredMethod(String name, Class<?>... parameterTypes):返回單個成員方法物件,存在就能拿到
  • Method的方法執行:
  • public Object invoke(Object obj, Object... args)
  • 引數一:觸發的是哪個物件的方法執行。
  • 引數二:args:呼叫方法時傳遞的實際引數
    */
    public class MethodTest1 {
/**
 * 1、返回所有成員方法物件的陣列,存在就能拿到
 * public Method[] getDeclaredMethods()
 */
@Test
public void getDeclaredMethods(){
    //a、獲取類物件
    Class c = Dog.class;
    //b、提取全部方法:包括私有的
    Method[] methods = c.getDeclaredMethods();
    //c、遍歷全部方法
    for (Method method : methods) {
        System.out.println(method.getName() + ",返回型別:" + method.getReturnType() + ",個數:" + method.getParameterCount());
    }
}

/**
 * 2、返回單個成員方法物件,存在就能拿到
 * public Method getDeclaredMethod(String name, Class<?>... parameterTypes)
 */
@Test
public void getDeclaredMethod() throws Exception {
    //a、獲取類物件
    Class c = Dog.class;
    //b、提取單個方法物件
    Method method = c.getDeclaredMethod("eat");
    Method method1 = c.getDeclaredMethod("eat",String.class);

    //暴力開啟許可權了
    method.setAccessible(true);
    method1.setAccessible(true);

    //c、觸發方法的執行
    Dog dog = new Dog();
    //注意:方法如果是沒有結果回來的,那麼返回的是 null。
    Object rs = method.invoke(dog);
    System.out.println(rs);

    Object rs1 = method1.invoke(dog, "骨頭");
    System.out.println(rs1);
}

/**
 * 3、返回所有成員方法物件的陣列(只能拿public修飾的全部方法)
 * public Method[] getMethods()
 */
@Test
public void getMethods(){
    Class c = Dog.class;
    //(只能拿public修飾的全部方法)
    Method[] methods = c.getMethods();
    for (Method method : methods) {
        System.out.println(method.getName() + ",返回型別:" + method.getReturnType() + ",個數:" + method.getParameterCount());
    }
}

/**
 * 4、返回的單個成員方法物件(只能拿public修飾的單個方法)
 * public Method getMethod(String name, Class<?>... parameterTypes)
 */
@Test
public void getMethod() throws Exception {
    Class c = Dog.class;
    //(只能拿public修飾的單個方法)
    Method method = c.getMethod("run");
    System.out.println(method.getName() + ",返回型別:" + method.getReturnType() + ",個數:" + method.getParameterCount());

    Method method1 = c.getMethod("eat",String.class);
    System.out.println(method1.getName() + ",返回型別:" + method1.getReturnType() + ",個數:" + method1.getParameterCount());
}

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
總結
利用反射技術獲取成員物件的方法
獲取類中成員方法物件
getDeclaredMethods()
getDeclaredMethodString name, Class<?>… parameterTypes)
反射得到成員方法可以做什麼?
依然是在某個物件中觸發該方法執行。
Object invoke(Object obj, Object… args)
如果某成員方法是非 public 的,需要開啟許可權(暴力反射),然後再觸發執行
setAccessible(boolean)
反射的作用-繞過編譯階段為集合新增資料
反射的作用-繞過編譯階段為集合新增資料
反射是作用在執行時的技術,此時集合的泛型將不能產生約束了,此時是可以為集合存入其他任意型別的元素的。

泛型只是在編譯階段可以約束結合只能操作某種資料型別,在編譯成 Class 檔案進入執行階段的時候,其真實型別都是 ArrayList 了,泛型相當於被擦除了。

/**

  • @author : gxd

  • @date : 2022/7/25 10:27

  • 目標:反射的作用-繞過編譯階段為集合新增資料

    • 反射是作用在執行時的技術,此時集合的泛型將不能產生約束了,此時是可以為集合存入其他任意型別的元素的。
  • -泛型只是在編譯階段可以約束結合只能操作某種資料型別,在編譯成 Class 檔案進入執行階段的時候,其真實型別都是 ArrayList 了,泛型相當於被擦除了。
    */
    public class ReflectTet {
    public static void main(String[] args) throws Exception {
    ArrayList list1 = new ArrayList<>();
    ArrayList list2 = new ArrayList<>();
    System.out.println(list1.getClass());
    System.out.println(list2.getClass());
    System.out.println(list1.getClass() == list2.getClass());//ArrayList.class

     System.out.println("-----------------------------------");
    
     ArrayList<Integer> list3 = new ArrayList<>();
     list3.add(15);
     list3.add(85);
     //list3.add("張三");
    
     Class c = list3.getClass();//ArrayList.class  ===> public boolean add(E e)
     //定位c類中的add方法
     Method method = c.getDeclaredMethod("add", Object.class);
     boolean rs = (boolean)method.invoke(list3,"張三");
     System.out.println(rs);
     System.out.println(list3);
     
     //不用反射也可以做到
     ArrayList list4 = list1;
     list4.add("asdfsd");
     System.out.println(list4);
    

    }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
總結
反射為何可以給約定了泛型的集合存入其他型別的元素?
編譯成Class檔案進入執行階段的時候,泛型會自動擦除。
反射是作用在執行時的技術,此時已經不存在泛型了。
反射的作用-通用框架的底層原理
案例 反射做通用框架
需求:給你任意一個物件,在不清楚物件欄位的情況,可以把物件的欄位名稱和對應值儲存到檔案中去。

分析:

定義一個方法,可以接收任意類的物件。
每次收到一個物件後,需要解析這個物件的全部成員變數名稱。
這個物件可能是任意的,那麼怎麼樣才可以知道這個物件的全部成員變數名稱呢?
使用反射獲取物件的 Class 類物件,然後獲取全部成員變數資訊。
遍歷成員變數資訊,然後提取本成員變數在物件中的具體值。
存入成員變數名稱和值到檔案中去即可。
實體類:Student

/**

  • @author : gxd

  • @date : 2022/7/25 11:09
    */
    public class Student {
    private String name;
    private char sex;
    private int age;
    private String className;
    private String hobby;

    public Student() {
    }

    public Student(String name, char sex, int age, String className, String hobby) {
    this.name = name;
    this.sex = sex;
    this.age = age;
    this.className = className;
    this.hobby = hobby;
    }

    public String getName() {
    return name;
    }

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

    public char getSex() {
    return sex;
    }

    public void setSex(char sex) {
    this.sex = sex;
    }

    public int getAge() {
    return age;
    }

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

    public String getClassName() {
    return className;
    }

    public void setClassName(String className) {
    this.className = className;
    }

    public String getHobby() {
    return hobby;
    }

    public void setHobby(String hobby) {
    this.hobby = hobby;
    }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
實體類:Teacher

/**

  • @author : gxd

  • @date : 2022/7/25 11:11
    */
    public class Teacher {
    private String name;
    private char sex;
    private double salary;

    public Teacher() {
    }

    public Teacher(String name, char sex, double salary) {
    this.name = name;
    this.sex = sex;
    this.salary = salary;
    }

    public String getName() {
    return name;
    }

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

    public char getSex() {
    return sex;
    }

    public void setSex(char sex) {
    this.sex = sex;
    }

    public double getSalary() {
    return salary;
    }

    public void setSalary(double salary) {
    this.salary = salary;
    }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
工具類:MybatisUtil

/**

  • @author : gxd
  • @date : 2022/7/25 11:14
  • 通用框架工具類
    /
    public class MybatisUtil {
    /
    *
    • 儲存任意型別的物件

    • @param obj
      */
      public static void save(Object obj){
      try (
      PrintStream ps = new PrintStream(new FileOutputStream("junit-reflect-annotation-proxy-app/src/data.txt",true))
      ){
      //1、提取這個物件的全部成員變數:只有反射可以解決
      Class c = obj.getClass();//c.getSimpleName()獲取當前類 c.getName獲取全限名:包名+類名
      ps.println("=" + c.getSimpleName() + "=");

       //2、提取它的全部成員變數
       Field[] fields = c.getDeclaredFields();
       //3、獲取成員變數的資訊
       for (Field field : fields) {
           String name = field.getName();
           //提取本成員變數在 obj 物件中的值(取值)
           field.setAccessible(true);
           String value = field.get(obj) + "";
           ps.println(name + "=" + value);
       }
      

      } catch (Exception e) {
      e.printStackTrace();
      }
      }
      }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
主程式:ReflectTest

/**

  • @author : gxd
  • @date : 2022/7/25 11:14
  • 目標: 反射的作用-通用框架的底層原理
  • 案例 反射做通用框架*
  • 需求:給你任意一個物件,在不清楚物件欄位的情況,可以把物件的欄位名稱和對應值儲存到檔案中去。
  • 分析:
    1. 定義一個方法,可以接收任意類的物件。
    1. 每次收到一個物件後,需要解析這個物件的全部成員變數名稱。
    1. 這個物件可能是任意的,那麼怎麼樣才可以知道這個物件的全部成員變數名稱呢?
    1. 使用反射獲取物件的 Class 類物件,然後獲取全部成員變數資訊。
    1. 遍歷成員變數資訊,然後提取本成員變數在物件中的具體值。
    1. 存入成員變數名稱和值到檔案中去即可。
      */
      public class ReflectTest {
      public static void main(String[] args) {
      Student s = new Student();
      s.setName("張三");
      s.setAge(22);
      s.setSex('男');
      s.setClassName("大四");
      s.setHobby("學習");
      MybatisUtil.save(s);

      Teacher t = new Teacher();
      t.setName("老師");
      t.setSex('男');
      t.setSalary(10000);
      MybatisUtil.save(t);
      }
      }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
總結
反射的作用?
可以在執行時得到一個類的全部成分然後操作。
也可以破壞封裝性。(很突出)
也可以破壞泛型的約束性。(很突出)
更重要的用途是適合:做Java高階框架
註解
註解的概述
註解概述、作用
Java 註解(Annotation)又稱 Java 標註,是 JDK5.0 引入的一種註釋機制。

Java 語言中的類、構造器、方法、成員變數、引數等都可以被註解進行標註。

註解的作用是什麼呢?
對 Java 中類、方法、成員變數做標記,然後進行特殊處理,至於到底做何種處理有業務需求來決定。
例如:JUint 框架中,標記了註解 @Test 的方法就可以被當成測試方法執行,而沒有標記的就不能當成測試方法執行。
總結
註解的作用
對 Java 中類、方法、成員變數做標記,然後進行特殊處理。
例如:JUint 框架中,標記了註解 @Test 的方法就可以被當成測試方法執行,而沒有標記的就不能當成測試方法執行
自定義註解
自定義註解—格式
自定義註解就是自己做一個註解來使用。

自定義註解:MyBook

/**

  • @author : gxd
  • @date : 2022/7/25 14:33
    */
    public @interface MyBook {
    String name();
    String[] authors();
    double price();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    主程式:AnnotationTest1

/**

  • @author : gxd

  • @date : 2022/7/25 15:29

  • 目標:學會自定義註解。掌握其自定義格式和語法。
    */
    @MyBook(name = "《精通JavaSE》",authors = {"自我自律","zwzl"},price = 100)
    public class AnnotationTest1 {
    @MyBook(name = "《精通JavaSE》",authors = {"自我自律","zwzl"},price = 100)
    private AnnotationTest1(){
    }

    @MyBook(name = "《精通JavaSE1》",authors = {"自我自律","zwzl"},price = 100)
    public static void main(String[] args) {
    @MyBook(name = "《精通JavaSE2》",authors = {"自我自律","zwzl"},price = 100)
    int age = 21;
    }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
特殊屬性
value 屬性,如果只有一個 value 屬性的情況下,使用 value 屬性的時候可以省略 value 名稱 不寫!
但是如果有多個屬性,且多個屬性沒有預設值,那麼 value 名稱是不能省略的。
總結
自定義註解

元註解
元註解
元註解:就是註解註解的註解。
元註解有兩個:
@Target:約束自定義註解只能在哪些地方使用。
@retention:申明註解的生命週期
MyTest

/**

  • @author : gxd
  • @date : 2022/7/25 16:35
    */
    @Target({ElementType.METHOD,ElementType.FIELD})//元註解
    @Retention(RetentionPolicy.RUNTIME)//一直或者,在執行階段這個註解也不消失
    public @interface MyTest {
    }
    1
    2
    3
    4
    5
    6
    7
    8
    AnnotationTest2

/**

  • @author : gxd

  • @date : 2022/7/25 16:36

  • 目標:認識元註解

  • @MyTest //只能註解方法和成員變數,有@Target在@MyTest註解裡面控制
    */
    public class AnnotationTest2 {
    @MyTest
    private String name;

    @MyTest
    public void test(){

    }

    public static void main(String[] args) {

    }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Target 中可使用的值定義在 ElementType 列舉類中,常用值如下

TYPE:類、介面
FIELD:成員變數
METHOD:成員方法
PARAMETER:方法引數
CONSTRUCTOR:構造器
LOCAL_VARIABLE:區域性變數
@Retention 中可使用的值定義在 RetentionPolicy 列舉類彙總,常用值如下

SOURCE:註解只作用在原始碼階段,生成的位元組碼檔案中不存在
CLASS:註解作用在原始碼階段,位元組碼檔案階段,執行階段不存在,預設值。
RUNTIME:註解作用在原始碼階段,位元組碼檔案階段,執行階段(開發常用)
總結
元註解是什麼?
註解註解的註解
@Target 約束自定義註解可以標記的範圍。
@Rentention 用來約束自定義註解的存活範圍。
註解解析
註解的解析
註解的操作中經常需要進行解析,註解的解析就是判斷是否存在註解,存在註解就解析出內容。
與註解解析相關的介面
Annotation:註解的頂級介面,註解都是 Annotation 型別的物件

AnnotatedElement:該介面定義了註解解析相關的解析方法

所有的類成分 Class,Method,Filed,Constructor,都實現了 AnnotatedElement 介面他們都擁有解析註解的能力。

解析註解的技巧
註解在哪個成分上,我們就先拿哪個成分物件。
比如註解作用成員方法,則要獲得該成員方法對應的 Method 物件,再來拿上面的註解
比如註解作用在類上,則要該類的 Class 物件,再來拿上面的註解
比如註解作用在成員變數上,則要獲得該成員變數對應的 Field 物件,再來拿上面的註解
案例 註解解析的案例
需求:註解解析的案例

分析:

定義註解 Book,要求如下:
包含屬性:String value() 書名
包含屬性:double price() 價格,預設值為 100
包含屬性:String[] authors() 多位作者
限制註解使用的位置:類和成員方法上
指定註解的有效範圍:RUNTIME
定義 BookStore 類,在類和成員方法上使用 Book 註解
定義 AnnotationTest1 測試類獲取 Book 註解上的資料
NewBook:

/**

  • @author : gxd
  • @date : 2022/7/25 22:07
    */

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NewBook {
String value();
double price() default 100;
String[] author();
}
1
2
3
4
5
6
7
8
9
10
11
12
AnnotationTest3 和 BookStore:

/**

  • @author : gxd

  • @date : 2022/7/25 22:10

  • 目標:學會註解解析

  • 註解的解析

    • 註解的操作中經常需要進行解析,註解的解析就是判斷是否存在註解,存在註解就解析出內容。
  • 與註解解析相關的介面

    • Annotation:註解的頂級介面,註解都是 Annotation 型別的物件
    • AnnotatedElement:該介面定義了註解解析相關的解析方法
  • - public Annotation[] getDeclaredAnnotations():獲得當前物件上使用的所有註解,返回註解陣列。
    
  • - public <A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass):根據註解型別獲得對應註解物件
    
  • - public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass):判斷當前物件是否使用了指定的註解,如果使用了則返回true,否則false
    
  • -所有的類成分 Class,Method,Filed,Constructor,都實現了 AnnotatedElement 介面他們都擁有解析註解的能力。

  • 解析註解的技巧

    • 註解在哪個成分上,我們就先拿哪個成分物件。
    • 比如註解作用成員方法,則要獲得該成員方法對應的 Method 物件,再來拿上面的註解
    • 比如註解作用在類上,則要該類的 Class 物件,再來拿上面的註解
    • 比如註解作用在成員變數上,則要獲得該成員變數對應的 Field 物件,再來拿上面的註解
  • 案例 註解解析的案例

  • 需求:註解解析的案例

  • 分析:

    1. 定義註解 Book,要求如下:
    • 包含屬性:String value() 書名
    • 包含屬性:double price() 價格,預設值為 100
    • 包含屬性:String[] authors() 多位作者
    • 限制註解使用的位置:類和成員方法上
    • 指定註解的有效範圍:RUNTIME
    1. 定義 BookStore 類,在類和成員方法上使用 Book 註解
    1. 定義 AnnotationTest1 測試類獲取 Book 註解上的資料
      */
      public class AnnotationTest3 {
      @Test
      public void parseClass(){
      //a、先得到類的物件
      Class c = BookStore.class;
      //b、判斷這個類上面是否存在這個註解
      if (c.isAnnotationPresent(NewBook.class)){
      //c、直接獲取該註解物件
      NewBook newBook = (NewBook) c.getDeclaredAnnotation(NewBook.class);
      System.out.println(newBook.value());
      System.out.println(newBook.price());
      System.out.println(Arrays.toString(newBook.author()));
      }
      }

    @Test
    public void parseMethod() throws Exception {
    //a、先得到類的物件
    Class c = BookStore.class;
    Method method = c.getDeclaredMethod("test");
    //b、判斷這個方法上面是否存在這個註解
    if (method.isAnnotationPresent(NewBook.class)){
    //c、直接獲取該註解物件
    NewBook newBook = method.getDeclaredAnnotation(NewBook.class);
    System.out.println(newBook.value());
    System.out.println(newBook.price());
    System.out.println(Arrays.toString(newBook.author()));
    }
    }
    }

@NewBook(value = "《法外狂徒張三》",price = 100,author = {"張三","羅老師"})
class BookStore{
@NewBook(value = "《三少爺的劍》",price = 66,author = {"古龍","熊耀華"})
public void test(){
}
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
總結
註解解析的方式

註解的應用場景一:JUnit框架
案例 模擬JUnit框架
需求:

定義若干個方法,只要加了 @MyTest 註解,就可以在啟動時被觸發執行
分析:

定義一個自定義註解 @MyTest,只能註解方法,存活範圍是一直都在。
定義若干個方法,只要有 @MyTest 註解的方法就能在啟動時被觸發執行,沒有這個主機的方法不能執行。
MyTest:

/**

  • @author : gxd
  • @date : 2022/7/25 16:35
    */
    @Target({ElementType.METHOD})//元註解
    @Retention(RetentionPolicy.RUNTIME)//一直或者,在執行階段這個註解也不消失
    public @interface MyTest {
    }
    1
    2
    3
    4
    5
    6
    7
    8
    AnnotationTest4

/**

  • @author : gxd

  • @date : 2022/7/25 22:57
    */
    public class AnnotationTest4 {
    @MyTest
    public void test1(){
    System.out.println("=test1執行=");
    }

    public void test2(){
    System.out.println("=test2執行=");
    }

    @MyTest
    public void test3(){
    System.out.println("=test3執行=");
    }

    /**

    • 啟動選單,有註解的才被呼叫
      */
      public static void main(String[] args) throws Exception {
      AnnotationTest4 t = new AnnotationTest4();
      //a、獲取類物件
      Class c = AnnotationTest4.class;
      //b、提取全部方法
      Method[] methods = c.getDeclaredMethods();
      //c、遍歷方法,看是否有 @MyTest 註解,有就跑它
      for (Method method : methods) {
      if (method.isAnnotationPresent(MyTest.class)){
      //跑它
      method.invoke(t);
      }
      }
      }
      }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
動態代理
動態代理概述、快速入門
什麼是代理?
代理指:某些場景下物件會找一個代理物件,來輔助自己完成一些工作,如:歌星(經紀人),買房的人(房產中介)。
代理主要幹什麼,他是如何工作的?

代理主要是針對物件的行為額外做一些輔助操作。

如何建立代理物件
Java 中代理的代表類是:java.lang.reflect.Proxy。

Proxy 提供了一個靜態方法,用於為物件產生一個代理物件返回。

介面:Skill

/**

  • @author : gxd
  • @date : 2022/7/25 23:21
    */
    public interface Skill {
    void jump();//跳舞
    void sing();//唱歌
    }
    1
    2
    3
    4
    5
    6
    7
    8
    實體類:Star

/**

  • @author : gxd

  • @date : 2022/7/25 23:21
    */
    public class Star implements Skill{
    private String name;

    public Star() {
    }

    public Star(String name) {
    this.name = name;
    }

    @Override
    public void jump() {
    System.out.println(name + "開始跳舞!");
    }

    @Override
    public void sing() {
    System.out.println(name + "開始唱歌!");
    }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
代理類:StarAgentProxy

/**

  • @author : gxd
  • @date : 2022/7/25 23:32
  • 代理類
    /
    public class StarAgentProxy {
    /
    *
    • 設計一個方法來返回一個明星物件的代理物件。
      /
      public static Skill getProxy(Star s){
      /
      *
      • public static Object newProxyInstance(ClassLoader loader,
      •                                       Class<?>[] interfaces,
        
      •                                       InvocationHandler h)
        
      • 引數一:定義代理類的類載入器
      • 引數二:代理類要實現的介面列表
      • 引數三:將方法呼叫分派到的處理程式。(代理物件的核心處理程式)
        */
        return (Skill) Proxy.newProxyInstance(s.getClass().getClassLoader(),
        s.getClass().getInterfaces(), new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("收首付款……");
        //真正讓明星去唱歌和跳舞…
        //method 正在呼叫的方法物件 , args 代表這個方法的引數。
        Object rs = method.invoke(s, args);
        System.out.println("收尾款,把楊超月接回來……");
        return rs;
        }
        });
        }
        }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
主程式:Test

/**

  • @author : gxd
  • @date : 2022/7/25 23:25
  • 目標:學習開發一個動態代理的物件出來,理解動態代理的執行流程
    */
    public class Test {
    public static void main(String[] args) {
    //1、建立一個物件(楊超月),物件的了必須實現介面
    Star star = new Star("楊超月");
    //為楊超月物件,生成一個代理物件(經紀人)
    Skill skill = StarAgentProxy.getProxy(star);
    skill.jump();//走代理的
    skill.sing();
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    Java 中如何生成代理,並指定代理幹什麼事

總結
代理是什麼?

一個物件,用來對被代理物件的行為額外做一些輔助工作。
在 Java 中實現動態代理的步驟是什麼樣的?

必須存在介面

被代理物件需要實現介面。

使用 Proxy 類提供的方法,的物件的代表物件。

透過代理物件呼叫方法,執行流程是什麼樣的?

先走向代理
代理可以為方法額外做一些輔助工作。
開發真正觸發物件的方法的執行。
回到代理中,有代理負責返回結果給方法的呼叫者。
動態代理的應用案例:做效能分析、代理的好處小結
案例 模擬企業業務功能開發,並完成每個功能的效能統計
需求:

模擬企業使用者管理業務,需包含使用者登入,使用者刪除,使用者查詢功能,並要統計每個功能的耗時。
分析:

定義一個 UserService 表示使用者業務介面,規定必須完成使用者登入,使用者刪除,使用者查詢功能。
定義一個實現類 UserServiceImpl 實現 UserService ,並完成相關功能,且統計每個功能的耗時。
定義測試列,建立實現類物件,呼叫方法。
本案例存在哪些問題?

業務物件的每個方法都要進行效能統計,存在大量重複的程式碼。
解決方案,使用動態代理
介面:UserService

/**

  • @author : gxd
  • @date : 2022/7/26 0:13
    */
    public interface UserService {
    String login(String loginName,String passWord);
    void deleteUsers();
    String selectUsers();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    實現類:UserServiceImpl

/**

  • @author : gxd

  • @date : 2022/7/26 0:14
    */
    public class UserServiceImpl implements UserService{
    @Override
    public String login(String loginName, String passWord) {
    String rs = "登入名和密碼錯誤!";
    if ("admin".equals(loginName) && "123456".equals(passWord)){
    rs = "登入成功!";
    }
    try {
    Thread.sleep(1000);
    } catch (Exception e) {
    e.printStackTrace();
    }
    return rs;
    }

    @Override
    public void deleteUsers() {
    try {
    System.out.println("您正在刪除使用者資料中……");
    Thread.sleep(2500);
    } catch (Exception e) {
    e.printStackTrace();
    }
    }

    @Override
    public String selectUsers() {
    String rs = "查詢了10000個使用者資料!";
    try {
    Thread.sleep(3000);
    } catch (Exception e) {
    e.printStackTrace();
    }
    return rs;
    }
    }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
代理工具類:ProxyUtil

/**

  • @author : gxd
  • @date : 2022/7/26 0:25
  • 代理工具類
    /
    public class ProxyUtil {
    /
    *
    • 透過一個靜態方法,為使用者業務物件返回一個代理物件
      */
      public static T getProxy(T userService){
      return (T) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
      userService.getClass().getInterfaces(), new InvocationHandler() {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      long startTime = System.currentTimeMillis();
      Object rs = method.invoke(userService, args);
      long endTime = System.currentTimeMillis();
      System.out.println(method.getName() + "方法耗時:" + (endTime - startTime) / 1000.0 + "s");
      return rs;
      }
      });
      }
      }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
主程式:Test

/**

  • @author : gxd
  • @date : 2022/7/26 0:11
  • 目標:掌握使用動態代理解決問題,理解使用動態代理的優勢。
  • 案例 模擬企業業務功能開發,並完成每個功能的效能統計
  • 需求:
    • 模擬企業使用者管理業務,需包含使用者登入,使用者刪除,使用者查詢功能,並要統計每個功能的耗時。
  • 分析:
    • 定義一個 UserService 表示使用者業務介面,規定必須完成使用者登入,使用者刪除,使用者查詢功能。
    • 定義一個實現類 UserServiceImpl 實現 UserService ,並完成相關功能,且統計每個功能的耗時。
    • 定義測試列,建立實現類物件,呼叫方法。
      */
      public class Test {
      public static void main(String[] args) {
      UserService userService = ProxyUtil.getProxy(new UserServiceImpl());
      System.out.println(userService.login("admin","123456"));
      System.out.println(userService.selectUsers());
      userService.deleteUsers();
      }
      }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
最佳化的關鍵步驟
必須有介面,實現類要實現介面(代理通常是基於介面實現的)。

建立一個實現類的物件,該物件為業務物件,緊接著為業務物件做一個代理物件。

動態代理的優點
可以在不改變方法原始碼的情況下,實現對方法功能的增強,提高了程式碼的複用。
簡化了程式設計工作、提高了開發效率,同時提高了軟體系統的可擴充套件性。
可以為被代理物件的所有方法做代理。
非常的靈活,支援任意介面型別的實現類物件做代理,也可以直接為介面本身做代理。
————————————————

                        版權宣告:本文為博主原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處連結和本宣告。

原文連結:https://blog.csdn.net/chu_dianxia/article/details/126058856

相關文章