2020年java反射課堂筆記

反射

1. Junit單元測試

測試分類

  1. 黑盒測試:不需要寫代碼,輸入值看結果
  2. 白盒測試:需要寫代碼,關注程序的執行流程(如:Junit測試)

Junit測試

實現步驟:

  1. 定義一個測試類(測試用例)
    • 測試類名:被測試的類名Test
    • 包名:xxx.xxx.xx.test
  2. 定義測試方法:可以獨立運行
    • 方法名:test即將測試的方法名
    • 返回值:void
    • 參數列表:空參
  3. 給方法加註解:@Test
  4. 導入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.反射

反射概述

反射是框架設計的靈魂

框架:半成品軟件。可以在框架的基礎上進行軟件開發,簡化編碼

反射機制:將類的各個組成部分(成員變量、成員方法等)封裝爲其他對象。

反射優點:

  1. 可以在程序運行過程中,操作這些對象
  2. 可以解耦,提高程序的擴展性

反射詳解

獲取class對象的3種方式

  1. Class.forName("全類名")

應用:多用於配置文件,將類名定義在配置文件中。讀取文件,加載類

  1. 類名.class

應用:多用於參數的傳遞

  1. 對象.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對象的常用方法

將類的各個部分轉化爲對象

  1. 獲取成員變量們
方法聲明 功能描述
Field[] getFields() 獲取所有public修飾的成員變量
Field getFields(String name) 獲取所有public修飾的name變量名的成員變量
Filed[] getDeclaredFields() 獲取所有成員變量,不考慮修飾符
Field getDeclaredField(String name) 獲取name變量名的成員變量
  1. 獲取構造方法們
方法聲明 功能描述
Constructor<?>[] getConstructors() 獲取所有public修飾的構造器
Constructor getConstructor(類<?>… parameterTypes) 獲取public修飾的指定的構造器
Constructor<?>[] getDeclaredConstructors() 獲取所有構造器
Constructor getDeclaredConstructor(類<?>… parameterTypes) 獲取指定的構造器
  1. 獲取成員方法們
方法聲明 功能描述
Method[] getMethods() 獲取所有public修飾的成員方法
Method getMethod(String name,類<?>… parameterTypes) 獲取public修飾的指定的成員方法
Method[] getDeclaredMethods() 獲取所有成員方法
Method getDeclaredMethod(String name,類<?>… parameterTypes) 獲取指定的常用方法
  1. 獲取類名
方法聲明 功能描述
String getName() 獲取類名

操作class對象的方法

  1. Field:操作成員變量們
方法聲明 功能描述
void set(Object obj,Object value) 設置值
Object get(Object obj) 獲取值
void setAccessible(true) 忽略訪問權限修飾符的安全檢查
  1. Constructor:創建對象
方法聲明 功能描述
T newInstance(Object obj) 創建對象
  1. 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,"大米飯");
    }
}

反射案例

需求:寫一個"框架",不能改變該類的任何代碼的前提下,可以幫我們創建任意類的對象,並且執行其中任意方法

實現步驟:

  1. 將需要創建的對象的全類名和需要執行的方法定義在配置文件中
  2. 在程序中加載讀取配置文件
  3. 使用反射技術來加載類文件進內存
  4. 創建對象,執行方法

實例代碼

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

  • 屬性:接口中可以定義的抽象方法
    1. 屬性的返回值類型:基本數據類型、String、枚舉、註解、數組
    2. 定義了屬性,在使用時需要給屬性賦值
      1. 如果定義屬性時,使用default關鍵字給屬性默認初始化值,則使用註解時,可以不進行屬性的賦值
      2. 如果只有一個屬性需要賦值,並且屬性的名稱是value。則value可以省略,直接定義值即可。
      3. 數組賦值時,值使用{}包裹。如果數組中只有一個值,則{}省略

元註解

定義:描述註解的註解

元註解 功能描述
@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);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章