Java常用類的使用

Java常用類

1. Optional

在我們的開發中,NullPointerException可謂是隨時隨處可見,為了避免空指針異常,我們常常需要進行 一
些防禦式的檢查,所以在程式碼中常常可見if(obj != null) 這樣的判斷。幸好在JDK1.8中,java為我們提供了
一個Optional類,Optional類能讓我們省掉繁瑣的非空的判斷。下面先說一下Optional中為我們提供的方法。

image-20220831233206208

反面示例:

interface IMessage{
    void echo(String msg);
}
class Factory{
    public static IMessage getInstance1(){
        return (msg -> System.out.println("msg = " + msg)); // 正常
        
    }
}
public class Demo {
    public static void main(String[] args) {
        IMessage message = Factory.getInstance1();
        if(message!=null){ // 不為null才調用
            message.echo("你好,小弟弟");
        }
    }
}

image-20220901231635533

正面示例:

interface IMessage {
    void echo(String msg);
}

class Factory {
    public static Optional<IMessage> getInstance2() {
        return Optional.of((msg -> System.out.println(msg))); // 保存到 Optional中 如果保存的為null的話,還是會發生空指針異常
    }
}

public class Demo {
    public static void main(String[] args) {
        IMessage message = Factory.getInstance2().get();// 取出 Optional中的數據
        message.echo("你好,小弟弟");
    }
}

這裡想要表達的意思就是,保存到Optional中的數據為null,只會在賦值是出現空指針異常,而不會等到調用是才出現,增強了業務的健壯性。

2. ThreadLocal

​ 從名字我們就可以看到ThreadLocal 叫做本地執行緒變數,意思是說,ThreadLocal 中填充的的是當前執行緒的變數,該變數對其他執行緒而言是封閉且隔離的,ThreadLocal 為變數在每個執行緒中創建了一個副本,這樣每個執行緒都可以訪問自己內部的副本變數。

​ 從字面意思很容易理解,但是實際角度就沒那麼容易了,作為一個面試常問的點,使用場景也是很豐富。

  • 1、在進行對象跨層傳遞的時候,使用ThreadLocal可以避免多次傳遞,打破層次間的約束。
  • 2、執行緒間數據隔離
  • 3、進行事務操作,用於存儲執行緒事務資訊。
  • 4、資料庫連接,Session會話管理。

1. 常用方法

image-20220901124936453

2. ThreadLocal怎麼用?

下面我先舉一個反面例子,加深大家的理解。

啟動三個執行緒,遍歷values數組,然後看他們的輸出結果。

@Data
class Message {
    public String content;
}
class MessagePrint { // 輸出結果
    public static void print() {
        System.out.println("【MessagePrint】" + Resource.message.content);
    }
}

/**
 * 中間類
 */
class Resource {
    static Message message;
}
/**
 * 測試
 * @author jiejie
 * @date 2022/09/01
 */
public class Demo1 {
    public static void main(String[] args) {
        String[] values = {"你好,弟弟", "你好,妹妹", "你好,姐姐"};
        for (String value : values) {
            new Thread(() -> {
                Resource.message = new Message();
                Resource.message.setContent(value);
                MessagePrint.print();
            }).start();
        }
    }

結果:

image-20220901124138229

可以看到,輸出的結果怎麼是一樣的呢,我們不是遍歷輸出的嗎。這就要談到我們的執行緒安全的問題,簡單來說,多個執行緒同時對某個對象進行賦值就會存在執行緒安全的問題,其實相對於這個問題,我們可以加鎖來解決這個問題。今天我們使用另外一個方法,就是上面提到的ThreadLocal。請看:

修改Resource:

/**
 * 中間類
 */
class Resource {
	
    private static ThreadLocal<Message> threadLocal = new ThreadLocal<>();

    public static Message getMessage() {
        return threadLocal.get();
    }

    public static void setMessage(Message message) {
        threadLocal.set(message);
    }

    public static void removeMessage() {
        threadLocal.remove();
    }
}
class MessagePrint { // 輸出

    public static void print() {
        System.out.println("【MessagePrint】" + Resource.getMessage().content);
    }
}
public class Demo1 {
    public static void main(String[] args) {
        String[] values = {"你好,弟弟", "你好,妹妹", "你好,姐姐"};

        for (String value : values) {
            new Thread(() -> {
                    Resource.setMessage(new Message());
                    Resource.getMessage().setContent(value);
                    MessagePrint.print();
            }).start();
        }
    }
}

程式執行結果:

image-20220901124719338

可以看到,我們的目的實現了,有沒有發現輸出的順序與values數組的順序並不一致,這是由於我們執行緒啟動的順序決定的。

我們這裡就體現了執行緒間對於變數的隔離。

3. 定時任務

在開發過程中,經常性需要一些定時或者周期性的操作。而在Java中則使用Timer對象完成定時計劃任務功能。

定時計劃任務功能在Java中主要使用的就是Timer對象,它在內部使用多執行緒的方式進行處理,所以Timer對象一般又和多執行緒技術結合緊密。

由於Timer是Java提供的原生Scheduler(任務調度)工具類,不需要導入其他jar包,使用起來方便高效,非常快捷。

image-20220902103135845

參數說明:

  • task:所要執行的任務,需要extends TimeTask override run()
  • time/firstTime:首次執行任務的時間
  • period:周期性執行Task的時間間隔,單位是毫秒
  • delay:執行task任務前的延時時間,單位是毫秒 很顯然,通過上述的描述,我們可以實現: 延遲多久後執行一次任務;指定時間執行一次任務;延遲一段時間,並周期性執行任務;指定時間,並周期性執行任務;

1. timer

  1. 實現TimerTask(需要執行什麼任務)的run方法, 明確要做什麼
    可以繼承實現, 也可用匿名內部類
  2. new 一個Timer
  3. 調用Timer實例的 schedule 或 scheduleAtFixedRate 方法
    將TimerTask放入Timer,並指定開始時間 和 間隔時間

簡單用法

public class Demo {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("【定時任務】定時執行");
            }
        }, 1000, 2000);
    }
}

執行結果:

【定時任務】定時執行
【定時任務】定時執行
【定時任務】定時執行

1.2 schedule和scheduleAtFixedRate有什麼區別?

scheduleAtFixedRate:每次執行時間為上一次任務開始起向後推一個period間隔,也就是說下次執行時間相對於上一次任務開始的時間點,因此執行時間不會延後,但是存在任務並發執行的問題(簡單來說,就是當任務阻塞,下次任務開始的時間不會受阻塞影響,而推遲下次任務執行時間。)。

schedule:每次執行時間為上一次任務結束後推一個period間隔,也就是說下次執行時間相對於上一次任務結束的時間點,因此執行時間會不斷延後(回受阻塞影響)。

1.3:如果執行task發生異常,是否會影響其他task的定時調度?

如果TimeTask拋出RuntimeException,那麼Timer會停止所有任務的運行!

1.4 Timer的一些缺陷?

​ 前面已經提及到Timer背後是一個單執行緒,因此Timer存在管理並發任務的缺陷:所有任務都是由同一個執行緒來調度,所有任務都是串列執行,意味著同一時間只能有一個任務得到執行,而前一個任務的延遲或者異常會影響到之後的任務。 其次,Timer的一些調度方式還算比較簡單,無法適應實際項目中任務定時調度的複雜度。

2 JDK對定時任務調度的執行緒池支援:ScheduledExecutorService

​ 由於Timer存在的問題,JDK5之後便提供了基於執行緒池的定時任務調度:ScheduledExecutorService。 設計理念:每一個被調度的任務都會被執行緒池中的一個執行緒去執行,因此任務可以並發執行,而且相互之間不受影響。

我們直接看例子:

image-20220902125525163

執行結果:

【定時任務】定時執行Fri Sep 02 12:54:02 CST 2022
【定時任務】定時執行Fri Sep 02 12:54:04 CST 2022
【定時任務】定時執行Fri Sep 02 12:54:06 CST 2022

3 定時任務大哥:Quartz

雖然ScheduledExecutorService對Timer進行了執行緒池的改進,但是依然無法滿足複雜的定時任務調度場景。因此OpenSymphony提供了強大的開源任務調度框架:Quartz。Quartz是純Java實現,而且作為Spring的默認調度框架,由於Quartz的強大的調度功能、靈活的使用方式、還具有分散式集群能力,可以說Quartz出馬,可以搞定一切定時任務調度!

3.1 核心:

任務 Job

我們想要調度的任務都必須實現 org.quartz.job 介面,然後實現介面中定義的 execute( ) 方法即可,類似於TimerTask

觸發器 Trigger

Trigger 作為執行任務的調度器。我們如果想要凌晨1點執行備份數據的任務,那麼 Trigger 就會設置凌晨1點執行該任務。其中 Trigger 又分為 SimpleTriggerCronTrigger 兩種

調度器 Scheduler

Scheduler 為任務的調度器,它會將任務 Job 及觸發器 Trigger 整合起來,負責基於 Trigger 設定的時間來執行 Job

3.2 使用Quartz

導入依賴:

		<!--quartz-->
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.3.2</version>
        </dependency>

創建任務類:

public class TestJob implements Job{

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        System.out.println("【定時任務】定時執行"+new Date());
    }
}

測試

class TestScheduler {
    public static void main(String[] args) throws SchedulerException {
        // 獲取默認任務調度器
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        // 定義Job(任務)實例
        JobDetail testJob = JobBuilder.newJob(TestJob.class)
                .withIdentity("測試任務").build();
        // 定義觸發器
        Trigger simpleTrigger = TriggerBuilder.newTrigger()
                .withIdentity("測試任務的觸發器")
                .startNow()
                .withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(1))
                .build();
        // 使用觸發器調度任務的執行
        scheduler.scheduleJob(testJob, simpleTrigger);
        scheduler.start();
    }
}

image-20220902132836415

經過上面的簡單使用,我們再來了解下它的結構吧

image-20220902153106561

圖中可知,還有一種觸發器 CronTrigger,下面簡單使用一下吧。

Cron表達式用法

測試類 任務類不變,修改測試類即可

class TestScheduler {
    public static void main(String[] args) throws SchedulerException {
        // 獲取默認任務調度器
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        // 定義Job(任務)實例
        JobDetail testJob = JobBuilder.newJob(TestJob.class)
                .withIdentity("測試任務").build();
        // 定義觸發器
        CronTrigger cronTrigger = TriggerBuilder.newTrigger()
                .withIdentity("name", "group")
                .withSchedule(CronScheduleBuilder.cronSchedule("0/2 * * ? * *")).build();
        // 使用觸發器調度任務的執行
        scheduler.scheduleJob(testJob, cronTrigger);
        scheduler.start();
    }
}

4 Arrays

Arrays類位於 java.util 包中,主要包含了操縱數組的各種方法。

常用方法:

image-20220902195539079

1 排序

升序排序

public class Demo {
    public static void main(String[] args) {
        int[] arr = {1, 2, 19, 4, 5, 6, 7, 8, 9};
        System.out.printf("【未排序前的數組:】%s \n", Arrays.toString(arr)); // Arrays.toString(arr) 數組轉換成字元串
        Arrays.sort(arr);
        System.out.printf("【排序後的數組:】%s", Arrays.toString(arr));
    }
}
// 執行結果
//【未排序前的數組:】[1, 2, 19, 4, 5, 6, 7, 8, 9] 
//【排序後的數組:】[1, 2, 4, 5, 6, 7, 8, 9, 19]

2 判斷是否相等

需要一模一樣的數組,執行結果才會為True

判斷大小關係:大於(1)、等於(0)、小於(-1)

public class Demo2 {
    public static void main(String[] args) {
        int[] arrA = {1, 5, 7};
        int[] arrB = {7, 5, 1};
        System.out.println("【判斷是否相等 -未排序】" + Arrays.equals(arrA, arrB));
        Arrays.sort(arrA);
        Arrays.sort(arrB);
        System.out.println("【判斷是否相等 -排序後】" + Arrays.equals(arrA, arrB));
        
        System.out.println("【判斷數組的大小關係 -排序後】" + Arrays.compare(arrA, arrB));
        System.out.println("【判斷數組的大小關係 -隨機數組】" + Arrays.compare(new int[]{1, 2, 3}, arrB));
        System.out.println("【判斷數組的大小關係 -隨機數組】" + Arrays.compare(new int[]{7, 8, 9}, arrB));
    }
}
// 執行結果
//【判斷是否相等 -未排序】false
//【判斷是否相等 -排序後】true
//【判斷數組的大小關係 -排序後】0
//【判斷數組的大小關係 -隨機數組】-1
//【判斷數組的大小關係 -隨機數組】1

3 二分查找

二分查找在大數據量的場景下,性能非常好。

下面舉個栗子:

  • 試想一下,要是我們的arrA數組的數據量非常大,達到了百萬級別甚至更多,我們想要在裡面找到某個數據,會遍曆數組很多次,時間複雜度O(n)
public class Demo3 {
    public static void main(String[] args) {
		int[] arrA = {1, 3, 5, 7, 9};// 模擬數組
        int key = 9;// 需要查找的數據
        int index = search(arrA, key);
        System.out.println("index = " + index);
    }
	
    // 普通 循環查詢,未查找到返回-1
    private static int search(int[] arrA, int key) {
        for (int i = 0; i < arrA.length; i++) {
            if (arrA[i] == key) {
                return i;
            }
        }
        return -1;
    }
}

然後我們再來看看,二分查找。

注意,使用二分查找的前提下,要保證數組的有序性(先排序)

  • Arrays.binarySearch(數組,key);(Arrays提供的二分查找)
   /**
     * 二分查找
     */
    private static int twoPoints(int[] arrA, int key) {
        Arrays.sort(arrA);
        System.out.println("【排序後的數組:】"+Arrays.toString(arrA));
        // 開始索引
        int start = 0;
        // 結束索引
        int end = arrA.length - 1;
        while (start <= end) {
            // 位運算 這裡以5為例 101(5) >>>1 10(2) mid(中間的下標)
            int mid = start+end >>> 1;
            int midVal = arrA[mid];
            // 用中間值的key比較,中間值小的話,說明我們需要查找的數據在右邊,中間值+1賦值start
            if (midVal < key) {
                start = mid + 1;
            }// 用中間值的key比較,中間值打的話,說明我們需要查找的數據在右邊,中間值-1賦值end
            else if (midVal > key) {
                end = mid - 1;
            } else {
                return mid;
            }
        }
        // 未找到
        return -1;
    }

image-20220902215515188

4 比較器

4.1 比較器出現的原因

​ 在Java項目開發的機制之中,比較器是一種最為常見的功能,同時在整個的Java類集實現架構之中,比較器都有著非常重要的地位,但是首先應該知道為什麼要使用比較器?﹒通過之前的講解應該已經發現在Java裡面提供有一個所謂的Arrays類,這個Arrays類提供有大量的數組有關的操作方法,而其中,可以發現這樣的一個方法定義:

public static void sort(Object[] a) 

發現Arrays類也可以直接實現對象數組的排序處理,於是下面就按照此方法的規則進行程式的編寫。範例:實現一個對象數組的排序﹐

@AllArgsConstructor
@Data
class Book{
    String name;
    double price;
}
public class 比較器 {
    public static void main(String[] args) {
       Book[] books= new Book[] {
               new Book("Java入門到入土",89.0),
               new Book("Python入門到入土",78.0),
           	   new Book("前端入門到入土",68.0)
       };
        Arrays.sort(books);
    }
}

執行程式: 可以發現,程式執行報錯了。類轉換異常

Exception in thread "main" java.lang.ClassCastException: class look.word.arrays.Book cannot be cast to class java.lang.Comparable (look.word.arrays.Book is in unnamed module of loader 'app'; java.lang.Comparable is in module java.base of loader 'bootstrap')
	at java.base/java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320)
	at java.base/java.util.ComparableTimSort.sort(ComparableTimSort.java:188)
	at java.base/java.util.Arrays.sort(Arrays.java:1041)
	at look.word.arrays.比較器.main(比較器.java:26)

​ 在程式執行的過程之中出現有一個「ClassCastException」異常,這種異常所描述的就是對象轉換異常,這裡面直接提示給用戶「不能夠將Book類的對象實例轉為Comparable」。那麼為什麼現在會出現這樣的異常呢?

​ 如果說現在給定的是一個整型數組,那麼如果要想確定數組之中的元素彼此之間的大小關係,直接利用各種關係運算符即可,但是問題是此時所給出的是一個對象數組,對象數組裡面所包含的內容一個個堆記憶體的資訊,那麼請問堆記憶體的資訊如何進行大小關係的比較呢?。

​ 很明顯,堆記憶體無法直接進行大小關係的比較,如果要的進行排序處理,嚴格意義上來講應該使用是堆記憶體中屬性的內容來進行大小關係確認,而這個屬性內容的確認就必須採用比較器來支援,而在Java裡面支援有兩種比較器:Comparable、Comparator

4.2 Comparable

要想自定義的類,實現可以比的效果,可以實現我們的Comparable介面,實現其compareTo方法。定義比較規則。

示例:

@AllArgsConstructor
@Data
class Book implements Comparable<Book>{
    String name;
    double price;
    
    @Override
    public int compareTo(Book book) {
        if (this.price > book.price){
            return 1;
        } else if (this.price < book.price) {
            return -1;
        }
        return 0;
    }
}

執行程式:

可以看到,這次程式執行沒有出錯,並且是升序排序的。如若修改排序規則,修改每次判斷的返回值即可。

  • String類就實現了Comparable這個介面,也就是說String類支援排序。
【Book類繼承比較器比較後的結果】
 [Book(name=前端入門到入土, price=68.0), 
 Book(name=Python入門到入土, price=78.0),
 Book(name=Java入門到入土, price=89.0)]

4.3 Comparator

需要使用 Comparator 是一個帶有@FunctionalInterface,也就是說它是一個函數式介面,可以使用Lambda表達式,也可以使用匿名類部類的方式去定義我們的比較規則。

image-20220903093113311

示例程式碼:

@AllArgsConstructor
@Data
class books {
    String name;
    double price;
}
public class 比較器2 {
    public static void main(String[] args) {
        books[] books = new books[]{
                new books("Java入門到入土", 89.0),
                new books("Python入門到入土", 78.0),
                new books("前端入門到入土", 68.0)
        };
        Comparator<books> comparator = (books1, books2) -> {
            if (books1.price > books2.price) {
                return 1;
            } else if (books1.price < books2.price) {
                return -1;
            }
            return 0;
        };
        Arrays.sort(books, comparator.reversed()); //  comparator.reversed() 是反轉的意思
        System.out.println("【books類繼承比較器比較後的結果】\n" + Arrays.toString(bookss));
    }
}

程式執行結果:

【books類繼承比較器比較後的結果】
[books(name=Java入門到入土, price=89.0), 
 books(name=Python入門到入土, price=78.0), 
 books(name=前端入門到入土, price=68.0)]

​ Comparator 除了基本的排序支援之外,其內部實際上也存在有大量的數據排序的處理操作,例如: reversed(),如果現在使用的是Comparable介面實現這樣的反轉那麼必須進行大量系統源程式碼的修改,法來進行配置,所以靈活度更高。

總結:請解釋兩種比較器的區別?

  • java.Jang.Comparable:是在類定義的時候實現的介面,該介面只存在有一個compareTo()方法用於確定大小關係;
  • java.utilComparator:是屬於挽救的比較器,除了可以實現排序的功能之外,在JDK 1.8之後的版本裡面還提供有更多方便的數組操作的處理功能。

5 StringBuffer

定義一個 StringBuffer類對象,然後通過append()方法向對象中添加26個小寫字母,要求每次只添加一次,共添加26次,然後按照逆序的方式輸出,並且可以刪除前5個字元。

傳統面向過程做法

/**
 * 傳統面向過程實現
 **/
public class Demo {
    public static void main(String[] args) {
        StringBuffer buffer = new StringBuffer(26);
        for (int i = 'a'; i <= 'z'; i++) {
            buffer.append((char) i);
        }
        System.out.println("【初始的數據】" + buffer);
        System.out.println("【逆序輸出】" + buffer);
        System.out.println("【刪除前5個】" + buffer.delete(0, 5));
    }
}

雖然這個時候已經完成了所對應的處理功能,但是如果僅僅是以一位所謂的初學者的角度來講肯定是沒有問題的,但問題是現在需要討論的不是能否實現的問題了,而是屬於如何實現更好的問題.如果按照面向對象的設計形式以上的操作程式碼明顯是不合理的,主要體現在對於當前給定的程式模型應該以介面的設計先行而後定義具體的操作子類

範例:通過面向對象的方式進行程式的開發

interface IContent { // 介面先行
    String content();
    String reverse();
    String delete(int start, int end);
}

class StringContent implements IContent {
    private StringBuffer buffer = new StringBuffer(26);
    public StringContent() {
        for (int i = 'a'; i <= 'z'; i++) {
            buffer.append((char) i);
        }
    }
    public String content() {
        return buffer.toString();
    }
    public String reverse() {
        return buffer.reverse().toString();
    }
    public String delete(int start, int end) {
        return buffer.delete(start, end).toString();
    }
}
class Factory { // 工廠獲取實例
    private Factory() {}
    public static StringContent getInstance() {
        return new StringContent();}
}
public class Demo2 {
    public static void main(String[] args) {
        StringContent content = Factory.getInstance();
        System.out.println("【初始的數據】" + content.content());
        System.out.println("【逆序輸出】" + content.reverse());
        System.out.println("【刪除前5個】" + content.delete(0, 5));
    }
}

在面向對象的設計結構之中所有的程式程式碼必須首先定義出公共的處理標準(定義介面),隨後再依據此標準進行項目的具體實現,對於介面子類的獲取也需要考慮到工廠設計模式。

6 反射

1. 出現的原因

​ Java編程開發之所以會存在有反射機制,最為重要的原因是可以使Java編寫程式碼更加的靈活,而這種靈活如果要想徹底的領悟,那麼也需要通過大量的苦練才可以得到,當你已經可以熟練使用反射之後,那麼就可以設計出更加結構性強,且可重用性更高的程式程式碼,在Java裡面存在有很多的開發框架,而之所以可以提供大量開發框架,主要的原因也在於反射機制。

Java的反射機制指的是通過「反方向」的操作來實現類的相關處理,那麼既然要有「反」則一定會有「正」,按照傳統的開發的行為模式來講,如果要想進行一個類的操作,那麼是需要根據類進行對象的實例化,隨後再通過實例化對象實現類中方法的調用處理,現在給出如下程式碼。

範例:觀察傳統的類的使用行為

class Book{
    public void read(){
        System.out.println("認真學習java書籍!");
    }
}

public class Demo {
    public static void main(String[] args) {
        Book book = new Book(); // 實例化對象
        book.read(); // 調用實例的方法
    }
}
// 執行結果: 認真學習java書籍!

以上的處理操作是一種正向的處理行為,但是如果是反向操作,則就意味著可以根據實例化對象獲取相關的資訊來源,在Java裡面所有的類實際上都屬於Object子類,那麼在Object類中就提供有一個重要的方法,這個方法可以獲取「反」的資訊:

2. 反射的入口

java.lang.Class

對每一種對象,JVM 都會實例化一個 java.lang.Class 的實例,java.lang.Class 為我們提供了在運行時訪問對象的屬性和類型資訊的能力。Class 還提供了創建新的類和對象的能力。最重要的是,Class 是調用其他反射 API 的入口,我們必須先獲得一個 Class 實例才可以進行接下來的操作。

獲取類Class對象的四種方式:

  • 調用運行時類本身的.class屬性
Class<String> stringClass = String.class;
  • 通過運行時類的對象獲取
 Class<? extends String> aClass = new String().getClass();
  • 通過Class的靜態方法獲取:體現反射的動態性
		try {
            Class<?> aClass1 = Class.forName("java.lang.String");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
  • 通過類的載入器
		ClassLoader classLoader = this.getClass().getClassLoader(); // 獲取類載入器
        Class<?> aClass1 = classLoader.loadClass("java.lang.String");

3. 獲取成員變數

java.lang.Class也提供了許多獲取成員變數的方法:

  • public Field[] getDeclaredFields() 獲取所有的屬性(包括私有的)
  • public Field[] getFields() 獲取所有public 修飾的屬性

當然每個成員變數有類型java.lang.reflect.Field 為我們提供了獲取當前對象的成員變數的類型,和重新設值的方法。

提供了兩個方法獲去變數的類型:

  • field.getModifiers(): 獲取屬性的修飾符

  • Field.getName():獲取屬性的的名稱

  • Field.getType():返回這個屬性的類型

  • Field.getGenericType():如果當前屬性有簽名屬性類型就返回,否則就返回 Field.getType()

@Data
class Books{
    String name;
    public Integer age;
    private Integer gender;
    protected String salt;
}
public class 屬性 {
    public static void main(String[] args) {
        Class<Books> booksClass = Books.class;
        // 獲取所有屬性 包括私有的
        Field[] fields = booksClass.getDeclaredFields();
        for (Field field : fields) {
            // 設置訪問無障礙
            field.setAccessible(true);
            System.out.print("【修飾符】" + Modifier.toString(field.getModifiers()));
            System.out.print("【\t變數的類型】" + field.getType());
            System.out.print("【\t屬性的的名稱】 = " +  field.getName()+"\n");
        }
    }
}

4. 調用方法

  • Class.getMethods(): 獲取所有的方法
  • Class.getMethod(“方法名”,”方法的參數列表數據類型”);
  • Class.getParmenterTypes(); // 獲取參數列表
  • Class.getReturnType();//返回類型
class Book1 {
    public void init() {
        System.out.println("初始圖書" );
    }
    public void read(String name) {
        System.out.println("讀了" + name);
    }
}
public class 方法 {
    public static void main(String[] args) throws Exception {
        Class<Book1> bookClass = Book1.class; // 獲取Class對象
        Method init = bookClass.getMethod("init"); // 獲取指定方法
        init.invoke(bookClass.newInstance(),null); // 執行實例無參方法
        Method read = bookClass.getMethod("read", String.class); // 獲取指定方法
        read.invoke(bookClass.newInstance(),"java圖書"); // 執行實例有參方法
        Method[] methods = bookClass.getMethods();
        for (Method method : methods) { // 遍歷所有方法
            System.out.println("method = " + method);
        }
    }
}