1. 程式人生 > 其它 >JAVA高階程式設計

JAVA高階程式設計

Java高階程式設計

目錄

程式、程序和執行緒的概念

程式(program)是為完成特定任務、用某種語言編寫的一組指令的集合。即指一段靜態的程式碼,靜態物件。

程序(process)是程式的一次執行過程,或是正在執行的一個程式。是一個動態的過程:有它自身的產生、存在和消亡的過程。——生命週期

​ 如:執行中的QQ,執行中的MP3播放器

程式是靜態的,程序是動態的。程序作為資源分配的單位,系統在執行時會為每個程序分配不同的記憶體區域

執行緒(thread):程序可進一步細化為執行緒,是一個程式內部的一條執行路徑。若一個程序同一時間並行執行多個執行緒,就是支援多執行緒的。執行緒作為排程和執行的單位,每個執行緒擁有獨立的執行棧和程式計數器(pc),執行緒切換的開銷小。

一個程序中的多個執行緒共享相同的記憶體單元/記憶體地址空間。它們從同一堆中分配物件,可以訪問相同的變數和物件。這就使得執行緒間通訊更簡便、高效。但多個執行緒操作共享的系統資源可能就會帶來安全的隱患。

一個Java應用程式java.exe,其實至少有三個執行緒:main()主執行緒,gc()垃圾回收執行緒,異常處理執行緒。當然如果發生異常,會影響主執行緒。

並行:多個CPU同時執行多個任務。

併發:一個CPU同時執行多個任務。

Thread類和執行緒的特點

Thread類的特點:

每個執行緒都是通過某個特定Thread物件的run()方法來完成操作的,經常把run()方法的主體稱為執行緒體。通過該Thread物件的start()方法來啟動這個執行緒,而非直接呼叫run()。

構造器:

Thread():建立新的Thread物件

  • Thread(String threadname):建立執行緒並指定執行緒例項名
  • Thread(Runnable target):指定建立執行緒的目標物件,它實現了Runnable介面中的run方法
  • Thread(Runnable target, String name):建立新的Thread物件

常見方法:

  • void start(): 啟動執行緒,並執行物件的run()方法
  • run(): 執行緒在被排程時執行的操作
  • String getName(): 返回執行緒的名稱
  • void setName(String name):設定該執行緒名稱
  • static Thread currentThread(): 返回當前執行緒。在Thread子類中就是this,通常用於主執行緒和Runnable實現類.
  • static void yield():執行緒讓步,暫停當前正在執行的執行緒,把執行機會讓給優先順序相同或更高的執行緒若佇列中沒有同優先順序的執行緒,忽略此方法
  • join() :當某個程式執行流中呼叫其他執行緒的 join() 方法時,呼叫執行緒將被阻塞,直到 join() 方法加入的 join 執行緒執行完為止,低優先順序的執行緒也可以獲得執行
  • static void sleep(long millis):(指定時間:毫秒) 令當前活動執行緒在指定時間段內放棄對CPU控制,使其他執行緒有機會被執行,時間到後重新排隊。會丟擲InterruptedException異常。
  • stop(): 強制執行緒生命期結束,不推薦使用
  • boolean isAlive():返回boolean,判斷執行緒是否還活著

執行緒的排程:

同優先順序執行緒組成先進先出佇列(先到先服務),使用時間片策略對高優先順序,使用優先排程的搶佔式策略

執行緒的優先順序等級

MAX_PRIORITY:10

MIN _PRIORITY:1

NORM_PRIORITY:5

涉及的方法:

ØgetPriority() :返回執行緒優先值

ØsetPriority(int newPriority) :改變執行緒的優先順序

執行緒建立時繼承父執行緒的優先順序,低優先順序只是獲得排程的概率低,並非一定是在高優先順序執行緒之後才被呼叫

Java中執行緒分為兩類,一種是守護執行緒,一種是使用者執行緒。它們在幾乎每個方面都是相同的,唯一的區別是判斷JVM何時離開。

守護執行緒是用來服務使用者執行緒的,通過在start()方法前呼叫thread.setDaemon(true)可以把一個使用者執行緒變成一個守護執行緒。

Java垃圾回收就是一個典型的守護執行緒。若JVM中都是守護執行緒,當前JVM將退出。

建立執行緒的方法

public class ThreadTest {
    public static void main(String[] args) {
        MyThread mt = new MyThread();
        mt.start();
        MyRunnable mr = new MyRunnable();
        Thread td = new Thread(mr);
        td.start();
	MyCallable mc = new MyCallable();
	FutureTask ft = new FutureTask(mc);
	Thread td2 = new Thread(ft);
	td2.start();
    }
}
//繼承Thread類
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("繼承Thread類建立執行緒類");
    }
}
//實現Runnable介面
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("實現Runnable介面建立執行緒類");
    }
}
//實現Callable介面
class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("實現Callable介面建立執行緒類");
        return 10;
    }
}
//執行緒池方式建立執行緒
ExecutorService executorService =Executors.newFixedThreadPool(5);
executorService.execute(new Runnable() {
    @Override
    public void run() {
        System.out.println("執行緒池方式建立執行緒!");
    }
});
executorService.shutdown();//關閉執行緒池

執行緒的生命週期

java語言使用Thread類及其子類的物件來表示一個執行緒,在其完整的生命週期中有以下五種狀態:

  • 新建(NEW):當一個Thread類或其子類的物件被宣告並建立時,新生的執行緒物件處於新建狀態。
  • 就緒(RUNNABLE之READY):處於新建狀態的執行緒執行start()後,將進入執行緒佇列等待CPU時間片,此時已經具備了執行條件,只是沒有分配到CPU資源。
  • 執行(RUNNABLE之RUNNING):就緒狀態的執行緒被排程並分配到CPU資源,進入到執行狀態,run()方法定義了執行緒的操作和功能。
  • 阻塞(BLOCKED):在某種特殊情況下,被掛起,讓出CPU資源並臨時中止自己的執行,進入阻塞狀態。阻塞於鎖。
  • 等待(WAITING):處於這種狀態的執行緒不會被分配CPU執行時間,它們要等待被顯式地喚醒,否則會處於無限期等待的狀態。
  • 超時等待(TIMED_WAITING):處於該狀態的執行緒不會被分配CPU執行時間,不過無須無限期等待被其他執行緒顯示地喚醒,在達到一定時間後它們會自動喚醒。呼叫Thread.sleep(long mills)方法,可以進入此狀態。但不釋放物件鎖。mills時間後,自動甦醒進入就緒狀態。
  • 死亡(TREMINATED):執行緒執行完成自己的全部工作或者被提前強制性中止或出現異常導致結束。進入死亡狀態。

執行緒的同步機制

同步程式碼塊:鎖為任意自定義物件,自己指定

synchronized(obj){

	//同步程式碼;

}

同步方法:所有靜態方法共用鎖類名.class,所有非靜態方法共用鎖this

public synchronized void show (String name){ 

	 //同步程式碼;

}

鎖釋放:

  • 當前執行緒的同步方法、同步程式碼塊執行結束。
  • 當前執行緒在同步方法、同步程式碼塊中遇到了break、return終止了執行。
  • 當前執行緒在同步方法、同步程式碼塊出現了未處理的error或exception。
  • 當前執行緒在同步方法、同步程式碼塊執行當前物件的wait()方法。執行緒暫停並釋放鎖

Lock鎖:

從JDK 5.0開始,Java提供了更強大的執行緒同步機制——通過顯式定義同步鎖物件來實現同步。同步鎖使用Lock物件充當。

java.util.concurrent.locks.Lock介面是控制多個執行緒對共享資源進行訪問的工具。鎖提供了對共享資源的獨佔訪問,每次只能有一個執行緒對Lock物件加鎖,執行緒開始訪問共享資源之前應先獲得Lock物件。

ReentrantLock 類實現了 Lock ,它擁有與 synchronized 相同的併發性和記憶體語義,在實現執行緒安全的控制中,比較常用的是ReentrantLock,可以顯式加鎖、釋放鎖。

class A {
    private final ReentrantLock lock = new ReentrantLock();
    public void m() {
        lock.lock();
        try {
            //保證執行緒安全的程式碼;
        } finally {
            lock.unlock();
        }
    }
}

synchronized 與 Lock 的對比

\1. Lock是顯式鎖(手動開啟和關閉鎖,別忘記關閉鎖),synchronized 是隱式鎖,出了作用域自動釋放

\2. Lock只有程式碼塊鎖,synchronized有程式碼塊鎖和方法鎖

\3. 使用Lock鎖,JVM將花費較少的時間來排程執行緒,效能更好。並且具有更好的擴充套件性(提供更多的子類).

優先使用順序:Lock->同步程式碼塊(已經進入了方法體,分配了相應資源)->同步方法(在方法體之外)

執行緒的通訊

wait() 與 notify() 和 notifyAll()

Ø wait():令當前執行緒掛起並放棄CPU、同步資源並等待,使別的執行緒可訪問並修改共享資源,而當前執行緒排隊等候其他執行緒呼叫notify()或notifyAll()方法喚醒,喚醒後等待重新獲得對監視器的所有權後才能繼續執行。

Ø notify():喚醒正在排隊等待同步資源的執行緒中優先順序最高者結束等待。

Ø notifyAll ():喚醒正在排隊等待資源的所有執行緒結束等待. l

這三個方法只有在synchronized方法或synchronized程式碼塊中才能使用,否則會報java.lang.IllegalMonitorStateException異常。

因為這三個方法必須有鎖物件呼叫,而任意物件都可以作為synchronized的同步鎖,因此這三個方法只能在Object類中宣告。

時間和日期API

  1. java.lang.System類

    System類提供的public static long currentTimeMillis()用來返回當前時間與1970年1月1日0時0分0秒之間以毫秒為單位的時間差。此方法適於計算時間差。

  2. java.util.Date類

    ​ 表示特定的瞬間,精確到毫秒

    構造器: Date():使用無參構造器建立的物件可以獲取本地當前時間。

​ Date(long date) 傳入一個long型的資料,獲取一個時間。

​ 常用方法

​ getTime():返回自 1970 年 1 月 1 日 00:00:00 GMT 以來此 Date 物件表示的毫秒數。

​ toString():把此 Date 物件轉換為以下形式的 String: dow mon dd hh:mm:ss zzz yyyy 其中dow 是一週中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat), zzz是時間標準。

​ 其它很多方法都過時了。

  1. java.text.SimpleDateFormat類

Date類的API不易於國際化,大部分被廢棄了,java.text.SimpleDateFormat類是一個不與語言環境有關的方式來格式化和解析日期的具體類。它允許進行格式化:日期à文字、解析:文字à日期

格式化:

Ø SimpleDateFormat() :預設的模式和語言環境建立物件

Ø public SimpleDateFormat(String pattern):該構造方法可以用引數pattern指定的格式建立一個物件,該物件呼叫:

Ø public String format(Date date):方法格式化時間物件date

解析:

Ø public Date parse(String source):從給定字串的開始解析文字,以生成一個日期。

	Date d1 = new Date();
	System.out.println(d1.getTime());//1628934687677
	SimpleDateFormat sf2 = new SimpleDateFormat();
	System.out.println(sf2.format(d1));//21-8-14 下午5:51
	SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS EEE zzzzzz");
	//2021-08-14 17:51:27:677 星期六 中國標準時間
	System.out.println(sf.format(d1));

4.java.util.Calendar(日曆)類

​ Calendar是一個抽象基類,主用用於完成日期欄位之間相互操作的功能。

獲取Calendar例項的方法:

​ 使用Calendar.getInstance()方法

​ 呼叫它的子類GregorianCalendar的構造器。

一個Calendar的例項是系統時間的抽象表示,通過get(int field)方法來取得想要的時間資訊。比如YEAR、MONTH、DAY_OF_WEEK、HOUR_OF_DAY 、MINUTE、SECOND

  • public void set(int field,int value)
  • public void add(int field,int amount)
  • public final Date getTime()
  • public final void setTime(Date date)
SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Calendar calendar = Calendar.getInstance();
// 從一個 Calendar 物件中獲取 Date 物件
Date date = calendar.getTime();
System.out.println(sf.format(date));//2021-08-14 18:00:48
// 使用給定的 Date 設定此 Calendar 的時間
date = new Date(234234235235L);
calendar.setTime(date);
calendar.set(Calendar.DAY_OF_MONTH, 8);
System.out.println("當前時間日設定為8後,時間是:" + 	sf.format(calendar.getTime()));//當前時間日設定為8後,時間是:1977-06-08 09:03:55
calendar.add(Calendar.HOUR, 2);
System.out.println("當前時間加2小時後,時間是:" + 	sf.format(calendar.getTime()));//當前時間加2小時後,時間是:1977-06-08 11:03:55
calendar.add(Calendar.MONTH, -2);
System.out.println("當前日期減2個月後,時間是:" + 	sf.format(calendar.getTime()));//當前日期減2個月後,時間是:1977-04-08 11:03:55
  1. LocalDate、LocalTime、LocalDateTime

它們的例項是不可變的物件,分別表示使用 ISO-8601日曆系統的日期、時間、日期和時間。它們提供了簡單的本地日期或時間,並不包含當前的時間資訊,也不包含與時區相關的資訊。

方法 描述

now() / * now(ZoneId zone) 靜態方法,根據當前時間建立物件/指定時區的物件

of() 靜態方法,根據指定日期/時間建立物件

getDayOfMonth()/getDayOfYear() 獲得月份天數(1-31) /獲得年份天數(1-366)

getDayOfWeek() 獲得星期幾(返回一個 DayOfWeek 列舉值)

getMonth() 獲得月份, 返回一個 Month 列舉值

getMonthValue() / getYear() 獲得月份(1-12) /獲得年份

getHour()/getMinute()/getSecond() 獲得當前物件對應的小時、分鐘、秒

withDayOfMonth()/withDayOfYear()/withMonth()/withYear() 將月份天數、年份天數、月份、年份修改為指定的值並返回新的物件

plusDays(), plusWeeks(), plusMonths(), plusYears(),plusHours() 向當前物件新增幾天、幾周、幾個月、幾年、幾小時

minusMonths() / minusWeeks()/minusDays()/minusYears()/minusHours() 從當前物件減去幾月、幾周、幾天、幾年、幾小時

  1. instant瞬時

方法 描述

now() 靜態方法,返回預設UTC時區的Instant類的物件

ofEpochMilli(long epochMilli) 靜態方法,返回在1970-01-01 00:00:00基礎上 加上指定毫秒數之後的Instant類的物件

atOffset(ZoneOffset offset) 結合即時的偏移來建立一個 OffsetDateTime

toEpochMilli() 返回1970-01-01 00:00:00到當前時間的毫秒數,即為時間戳

  1. java.time.format.DateTimeFormatter 類

該類提供了三種格式化方法:

預定義的標準格式。如:

ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME

本地化相關的格式。如:ofLocalizedDateTime(FormatStyle.LONG)

自定義的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”)

方 法 描 述

ofPattern(String pattern) 靜態方法,返回一個指定字串格式的物件

DateTimeFormatter format(TemporalAccessor t) 格式化一個日期、時間,返回字串

parse(CharSequence text) 將指定格式的字元序列解析為一個日期、時間。

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); // 24小時
        LocalDateTime localDateTime = LocalDateTime.now(ZoneId.systemDefault());
        String format = localDateTime.format(formatter);
        System.out.println(format);

8.其他相關api

ZoneId:

該類中包含了所有的時區資訊,一個時區的ID,如 Europe/Paris ZonedDateTime:

一個在ISO-8601日曆系統時區的日期時間,如 2007-12-

03T10:15:30+01:00 Europe/Paris。 其中每個時區都對應著ID,地區ID都為“{區 域}/{城市}”的格式,例如:

Asia/Shanghai等

Clock:

使用時區提供對當前即時、日期和時間的訪問的時鐘。

持續時間:Duration,用於計算兩個“時間”間隔

日期間隔:Period,用於計算兩個“日期”間隔

TemporalAdjuster : 時間校正器。有時我們可能需要獲取例如:將日期調整到“下一個工作日”等操作。

TemporalAdjusters : 該類通過靜態方法(firstDayOfXxx()/lastDayOfXxx()/nextXxx())提供了大量的常用TemporalAdjuster 的實現。

Comparable和Comparator實現元素的比較

自然排序:java.lang.Comparable

定製排序:java.util.Comparator

Comparable介面強行對實現它的每個類的物件進行整體排序。這種排序被稱為類的自然排序。實現 Comparable 的類必須實現 compareTo(Object obj) 方法,兩個物件即通過 compareTo(Object obj) 方法的返回值來比較大小。如果當前物件this大 於形參物件obj,則返回正整數,如果當前物件this小於形參物件obj,則返回負整數,如果當前物件this等於形參物件obj,則返回零。實現Comparable介面的物件列表(和陣列)可以通過 Collections.sort 或Arrays.sort進行自動排序。實現此介面的物件可以用作有序對映中的鍵或有序集合中的元素,無需指定比較器。

當元素的型別沒有實現java.lang.Comparable介面而又不方便修改程式碼,或者實現了java.lang.Comparable介面的排序規則不適合當前的操作,那麼可以考慮使用 Comparator 的物件來排序,強行對多個物件進行整體排序的比較。重寫compare(Object o1,Object o2)方法,比較o1和o2的大小:如果方法返回正整數,則表示o1大於o2;如果返回0,表示相等;返回負整數,表示o1小於o2。可以將 Comparator 傳遞給 sort 方法(如 Collections.sort 或 Arrays.sort),從而允許在排序順序上實現精確控制。還可以使用 Comparator 來控制某些資料結構(如有序 set或有序對映)的順序,或者為那些沒有自然順序的物件 collection 提供排序。

public class CompareTest {
    public static void main(String[] args) {
        Dog[] dogs = new Dog[4];
        dogs[0] = new Dog(18);
        dogs[1] = new Dog(10);
        dogs[2] = new Dog(15);
        dogs[3] = new Dog(9);
        Arrays.sort(dogs);
        System.out.println(Arrays.toString(dogs));//[age=9, age=10, age=15, age=18]
        Integer[] nums = new Integer[]{1, 3, 6, 2, 4, 5};
        Arrays.sort(nums, (o1, o2) -> o2 - o1);
        System.out.println(Arrays.toString(nums));//[6, 5, 4, 3, 2, 1]
    }
}
class Dog implements Comparable<Dog> {
    private int age;
    public Dog(int age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "age=" + age;
    }
    @Override
    public int compareTo(Dog dog) {
        return this.age - dog.age;
    }
}

Java.lang.System類的使用

System類代表系統,系統級的很多屬性和控制方法都放置在該類的內部。該類位於java.lang包。 由於該類的構造器是private的,所以無法建立該類的物件,也就是無法例項化該類。其內部的成員變數和成員方法都是static的,所以也可以很方便的進行呼叫。 l

成員變數:

System類內部包含in、out和err三個成員變數,分別代表標準輸入流(鍵盤輸入),標準輸出流(顯示器)和標準錯誤輸出流(顯示器)。

成員方法:

native long currentTimeMillis(): 該方法的作用是返回當前的計算機時間,時間的表達格式為當前計算機時間和GMT時間(格林威治時間)1970年1月1號0時0分0秒所差的毫秒數。

void exit(int status):該方法的作用是退出程式。其中status的值為0代表正常退出,非零代表異常退出。使用該方法可以在圖形介面程式設計中實現程式的退出功能等。

void gc(): 該方法的作用是請求系統進行垃圾回收。至於系統是否立刻回收,則取決於系統中垃圾回收演算法的實現以及系統執行時的情況。

String getProperty(String key): 該方法的作用是獲得系統中屬性名為key的屬性對應的值。系統中常見的屬性名以及屬性的作用如下表所示:

static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)

從指定源陣列中複製一個數組,複製從指定的位置開始,到目標陣列的指定位置結束。src - 源陣列srcpos - 源陣列中的起始位置dest - 目標陣列destPos - 目標陣列中的起始位置length - 要複製的陣列元素的數量

列舉類的使用

當需要定義一組常量時,強烈建議使用列舉類。

列舉類的屬性

  1. Ø 列舉類物件的屬性不應允許被改動, 所以應該使用 private final 修飾
  2. Ø 列舉類的使用 private final 修飾的屬性應該在構造器中為其賦值
  3. Ø 若列舉類顯式的定義了帶引數的構造器, 則在列出列舉值時也必須對應的

傳入引數

  • Ø 使用 enum 定義的列舉類預設繼承了 java.lang.Enum類,因此不能再繼承 其他類
  • Ø 列舉類的構造器只能使用 private 許可權修飾符
  • Ø 列舉類的所有例項必須在列舉類中顯式列出(, 分隔 ; 結尾)。列出的例項系統會自動新增 public static final 修飾
  • Ø 必須在列舉類的第一行宣告列舉類物件
/**
 * 自定義的列舉類
 */
class Season {
    //只有定義好的物件,切為靜態不可變物件
    public static final Season SPRING = new Season("春天", "春暖花開");
    public static final Season SUMMER = new Season("夏天", "風和日麗");
    public static final Season AUTUMN = new Season("秋天", "秋高氣爽");
    public static final Season WINTER = new Season("冬天", "冰凍三尺");
    //私有化屬性,屬性定義後不可變
    private final String NAME;
    private final String DESC;
    //帶參私有化構造方法
    private Season(String name, String desc) {
        this.NAME = name;
        this.DESC = desc;
    }
    //獲取屬性
    public String getNAME() {
        return this.NAME;
    }
    public String getDESC() {
        return this.DESC;
    }
}
/**
 * 用enum定義的列舉類
 * 可以繫結物件
 */
enum Weeks {
    Monday("星期一", 1),
    Tuesday("星期二", 2),
    Wednesday("星期三", 3),
    ThursDay("星期四", 4),
    Friday("星期五", 5),
    SaturDay("星期四", 6),
    Sunday("星期四", 0);
    private final String DAY;
    private final Integer DESC;
    public String getDAY() {
        return DAY;
    }
    public Integer getDESC() {
        return DESC;
    }
    private Weeks(String day, Integer desc) {
        this.DAY = day;
        this.DESC = desc;
    }
}

Enum類的主要方法:

Ø values()方法:返回列舉型別的物件陣列。該方法可以很方便地遍歷所有的

列舉值。

Ø valueOf(String str):可以把一個字串轉為對應的列舉類物件。要求字串必須是列舉類物件的“名字”。如不是,會有執行時異常:IllegalArgumentException。 Ø toString():返回當前列舉類物件常量的名稱

Enum類實現了Comparable介面。

註解(Annotation)的使用

Annotation 其實就是程式碼裡的特殊標記, 這些標記可以在編譯, 類載入, 執行時被讀取, 並執行相應的處理。通過使用 Annotation, 程式設計師可以在不改變原有邏輯的情況下, 在原始檔中嵌入一些補充資訊。程式碼分析工具、開發工具和部署工具可以通過這些補充資訊進行驗證或者進行部署。

Annotation 可以像修飾符一樣被使用, 可用於修飾包,類, 構造器, 方法, 成員變數, 引數, 區域性變數的宣告, 這些資訊被儲存在Annotation的 “name=value” 對中。

使用 Annotation 時要在其前面增加 @ 符號, 並把該 Annotation 當成一個修飾符使用。用於修飾它支援的程式元素。

自定義註解:

定義新的 Annotation 型別使用 @interface 關鍵字

自定義註解自動繼承了java.lang.annotation.Annotation介面

Annotation 的成員變數在 Annotation 定義中以無引數方法的形式來宣告。其方法名和返回值定義了該成員的名字和型別。我們稱為配置引數。型別只能是八種基本資料型別、String型別、Class型別、enum型別、Annotation型別、以上所有型別的陣列。可以在定義 Annotation 的成員變數時為其指定初始值, 指定成員變數的初始值可使用 default 關鍵字如果只有一個引數成員,建議使用引數名為value

如果定義的註解含有配置引數,那麼使用時必須指定引數值,除非它有預設

值。格式是“引數名 = 引數值”,如果只有一個引數成員,且名稱為value,可以省略“value=”。

沒有成員定義的 Annotation 稱為標記; 包含成員變數的 Annotation 稱為元資料Annotation。

注意:自定義註解必須配上註解的資訊處理流程才有意義。


JDK 的元 Annotation 用於修飾其他 Annotation 定義,JDK5.0提供了4個標準的meta-annotation型別,分別是:Retention、Target、Documented、Inherited

@Retention:

只能用於修飾一個 Annotation 定義, 用於指定該 Annotation 的生命週期, @Rentention 包含一個 RetentionPolicy 型別的成員變數, 使用@Rentention 時必須為該 value 成員變數指定值:

ØRetentionPolicy.SOURCE:在原始檔中有效(即原始檔保留),編譯器直接丟棄這種策略的註釋

ØRetentionPolicy.CLASS:在class檔案中有效(即class保留) , 當執行 Java 程式時, JVM 不會保留註解。這是預設值

ØRetentionPolicy.RUNTIME:在執行時有效(即執行時保留),當執行 Java 程式時, JVM 會保留註釋。程式可以通過反射獲取該註釋。

public enum RetentionPolicy{
			SOURCE,
			CLASS,
			RUNTIME
}

@Target:

用於修飾 Annotation 定義, 用於指定被修飾的 Annotation 能用於修飾哪些程式元素。 @Target 也包含一個型別為為 ElementType的成員變數。

    @Target(ElementType.TYPE)   //介面、類、列舉、註解
    @Target(ElementType.FIELD) //欄位、列舉的常量
    @Target(ElementType.METHOD) //方法
    @Target(ElementType.PARAMETER) //方法引數
    @Target(ElementType.CONSTRUCTOR)  //建構函式
    @Target(ElementType.LOCAL_VARIABLE)//區域性變數
    @Target(ElementType.ANNOTATION_TYPE)//註解
    @Target(ElementType.PACKAGE) //包
	   @Target(ElementType.TYPE_USE) //使用型別的語句中
       @Target(ElementType.TYPE_TYPE_PARAMETER) //資料變數的宣告中

@Documented:

用於指定被該元 Annotation 修飾的 Annotation 類將被javadoc 工具提取成文件。預設情況下,javadoc是不包括註解的。定義為Documented的註解必須設定Retention值為RUNTIME。

@Inherited:

被它修飾的 Annotation 將具有繼承性。如果某個類使用了被@Inherited 修飾的 Annotation, 則其子類將自動具有該註解。比如:如果把標有@Inherited註解的自定義的註解標註在類級別上,子類則可以繼承父類類級別的註解。實際應用中,使用較少。

public class AnnotationTest {
    public static void main(String[] args) throws NoSuchFieldException {
        UserMe user = new UserMe();
        //反射獲取獲取類註解
        TypeTest typeTest = user.getClass().getAnnotation(TypeTest.class);
        String[] values = typeTest.values();
        System.out.println(Arrays.toString(values));
        //反射獲取成員變數註解
        Field username = user.getClass().getDeclaredField("username");
        FieldTest fieldTest = username.getAnnotation(FieldTest.class);
        String value = fieldTest.value();
        user.setUsername(value);
        System.out.println(user.getUsername());
    }
}
/**
 * 定義註解,用於給成員變數賦值,並使用反射取值
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@interface FieldTest {
    String value();
}
/**
 * 定義註解,修飾一個類,並獲取註解的值
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface TypeTest {
    String[] values();
}
@TypeTest(values = {"hello", "world", "annotaition"})
class UserMe {
    @FieldTest("zhangsan")
    private String username;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
}

Java集合

Java 集合可分為 Collection 和 Map 兩種體系

ØCollection介面:單列資料,定義了存取一組物件的方法的集合

  • List:元素有序、可重複的集合
  • Set:元素無序、不可重複的集合

Ø Map介面:雙列資料,儲存具有對映關係“key-value對”的集合

Collection介面:

1、新增
	add(Object obj)
	addAll(Collection coll) 
2、獲取有效元素的個數 int size()
3、清空集合 void clear()
4、是否是空集合 boolean isEmpty()
5、是否包含某個元素
	boolean contains(Object obj):是通過元素的equals方法來判斷是否是同一個物件
	boolean containsAll(Collection c):也是呼叫元素的equals方法來比較的。拿兩個集合的元素挨個比較。
6、刪除
	boolean remove(Object obj) :通過元素的equals方法判斷是否是要刪除的那個元素。只會刪除找到的第一個元素
	boolean removeAll(Collection coll):取當前集合的差集
7、取兩個集合的交集
	boolean retainAll(Collection c):把交集的結果存在當前集合中,不影響c集合
8、集合是否相等boolean equals(Object obj)
9、轉成物件陣列Object[] toArray()
10、獲取集合物件的雜湊值 hashCode()
11、遍歷 iterator():返回迭代器物件,用於集合遍歷

Iterator迭代器介面:

提供一種方法訪問一個容器(container)物件中各個元素,而又不需暴露該物件的內部細節。迭代器模式,就是為容器而生。Collection介面繼承了java.lang.Iterable介面,該介面有一個iterator()方法,那麼所有實現了Collection介面的集合類都有一個iterator()方法,用以返回一個實現了Iterator介面的物件。 Iterator 僅用於遍歷集合,Iterator 本身並不提供承裝物件的能力。如果需要建立Iterator 物件,則必須有一個被迭代的集合。集合物件每次呼叫iterator()方法都得到一個全新的迭代器物件,預設遊標都在集合的第一個元素之前。

//hasNext():判斷是否還有下一個元素
while(iterator.hasNext()){
//next():①指標下移 ②將下移以後集合位置上的元素返回
System.out.println(iterator.next());
}

在呼叫it.next()方法之前必須要呼叫it.hasNext()進行檢測。若不呼叫,且下一條記錄無效,直接呼叫it.next()會丟擲NoSuchElementException異常。

Iterator iter = coll.iterator();//回到起點
while(iter.hasNext()){
	Object obj = iter.next();
	if(obj.equals("Tom")){
		iter.remove();
	} 
}
//Iterator可以刪除集合的元素,但是是遍歷過程中通過迭代器物件的remove方 法,不是集合物件的remove方法。 如果還未呼叫next()或在上一次呼叫 next 方法之後已經呼叫了 remove 方法,再呼叫remove都會報IllegalStateException。

List集合:

List集合類中元素有序、且可重複,集合中的每個元素都有其對應的順序索引。List容器中的元素都對應一個整數型的序號記載其在容器中的位置,可以根據序號存取容器中的元素。JDK API中List介面的實現類常用的有:ArrayList、LinkedList和Vector。

List除了從Collection集合繼承的方法外,List 集合裡添加了一些根據索引來操作集合元素的方法。

void add(int index, Object ele)//在index位置插入ele元素
boolean addAll(int index, Collection c)//從index位置開始將c中的所有元素新增進來
Object get(int index)//獲取指定index位置的元素
int indexOf(Object obj)//返回obj在集合中首次出現的位置
int lastIndexOf(Object obj)//返回obj在當前集合中末次出現的位置
Object remove(int index)//移除指定index位置的元素,並返回此元素
Object set(int index, Object ele)//設定指定index位置的元素為ele
List subList(int fromIndex, int toIndex)//返回從fromIndex到toIndex位置的子集合

Set集合:

Set 集合不允許包含相同的元素,如果試把兩個相同的元素加入同一個Set 集合中,則新增操作失敗。Set 判斷兩個物件是否相同不是使用 == 運算子,而是根據 equals() 方法。

Map集合:

​ Map與Collection並列存在。用於儲存具有對映關係的資料:key-value

​ Map 中的 key 和 value 都可以是任何引用型別的資料

​ Map 中的 key 用Set來存放,不允許重複,即同一個 Map 物件所對應的類,須重寫hashCode()和equals()方法,常用String類作為Map的“鍵”

​ key 和 value 之間存在單向一對一關係,即通過指定的 key 總能找到唯一的、確定的 value

​ Map介面的常用實現類:HashMap、TreeMap、LinkedHashMap和Properties。

​ 其中,HashMap是 Map 介面使用頻率最高的實現類

常用方法:

//新增、刪除、修改操作: 
Object put(Object key,Object value)//將指定key-value新增到(或修改)當前map物件中
void putAll(Map m)//將m中的所有key-value對存放到當前map
Object remove(Object key)//移除指定key的key-value對,並返回value
void clear()//清空當前map中的所有資料
//元素查詢的操作:
Object get(Object key)//獲取指定key對應的value
boolean containsKey(Object key)//是否包含指定的key
boolean containsValue(Object value)//是否包含指定的value
int size()//返回map中key-value對的個數
boolean isEmpty()//判斷當前map是否為空
boolean equals(Object obj)//判斷當前map和引數物件obj是否相等
//元檢視操作的方法:
Set keySet()//返回所有key構成的Set集合
Collection values()//返回所有value構成的Collection集合
Set entrySet()//返回所有key-value對構成的Set集合

List實現類之:ArrayList和Vector

ArrayList 是 List 介面的典型實現類、主要實現類。本質上,ArrayList是物件引用的一個”變長”陣列

ArrayList的JDK1.8之前與之後的實現區別?

JDK1.7:ArrayList像餓漢式,直接建立一個初始容量為10的陣列

JDK1.8:ArrayList像懶漢式,一開始建立一個長度為0的陣列,當新增第一個元素時再建立一個始容量為10的陣列

Arrays.asList(…) 方法返回的 List 集合,既不是 ArrayList 例項,也不是Vector 例項。 Arrays.asList(…) 返回值是一個固定長度的 List 集合。

Vector:Vector 是一個古老的集合,JDK1.0就有了。大多數操作與ArrayList相同,區別之處在於Vector是執行緒安全的。

在各種list中,最好把ArrayList作為預設選擇。當插入、刪除頻繁時,使用LinkedList;Vector總是比ArrayList慢,所以儘量避免使用。

List實現類之:LinkedList

對於頻繁的插入或刪除元素的操作,建議使用LinkedList類,效率較高

新增方法:

void addFirst(Object obj) 
void addLast(Object obj)  
Object getFirst()
Object getLast()
Object removeFirst()
Object removeLast()

Set實現類之:HashSet

HashSet 是 Set 介面的典型實現,大多數時候使用 Set 集合時都使用這個實現類。HashSet 按 Hash 演算法來儲存集合中的元素,因此具有很好的存取、查詢、刪除效能。 l

HashSet 具有以下特點:

​ 不能保證元素的排列順序

​ HashSet 不是執行緒安全的

​ 集合元素可以是 null

HashSet 集合判斷兩個元素相等的標準:兩個物件通過 hashCode() 方法比較相等,並且兩個物件的 equals() 方法返回值也相等。對於存放在Set容器中的物件,對應的類一定要重寫equals()和hashCode(Object obj)方法,以實現物件相等規則。即:“相等的物件必須具有相等的雜湊碼”。

向HashSet中新增元素的過程:

​ 當向 HashSet 集合中存入一個元素時,HashSet 會呼叫該物件的 hashCode() 方法來得到該物件的 hashCode 值,然後根據 hashCode 值,通過某種雜湊函式決定該物件在 HashSet 底層陣列中的儲存位置(這個雜湊函式會與底層陣列的長度相計算得到在陣列中的下標,並且這種雜湊函式計算還儘可能保證能均勻儲存元素,越是雜湊分佈,該雜湊函式設計的越好)。如果兩個元素的hashCode()值相等,會再繼續呼叫equals方法,如果equals方法結果為true,新增失敗;如果為false,那麼會儲存該元素,但是該陣列的位置已經有元素了,那麼會通過連結串列的方式繼續連結。如果兩個元素的 equals() 方法返回 true,但它們的 hashCode() 返回值不相等,hashSet 將會把它們儲存在不同的位置,但依然可以新增成功。

底層也是陣列,初始容量為16,當如果使用率超過0.75,(16*0.75=12)就會擴大容量為原來的2倍。(16擴容為32,依次為64,128....等)

Set實現類之:LinkedHashSet

LinkedHashSet 是 HashSet 的子類

LinkedHashSet 根據元素的 hashCode 值來決定元素的儲存位置,但它同時使用雙向連結串列維護元素的次序,這使得元素看起來是以插入順序儲存的,具有可預測的迭代順序。LinkedHashSet插入效能略低於 HashSet,但在迭代訪問 Set 裡的全部元素時有很好的效能。LinkedHashSet 不允許集合元素重複。

Set實現類之:TreeSet

TreeSet 是 SortedSet 介面的實現類,TreeSet 可以確保集合元素處於排序狀態。TreeSet底層使用紅黑樹結構儲存資料。特點:有序,查詢速度比List快。

自然排序:TreeSet 會呼叫集合元素的 compareTo(Object obj) 方法來比較元素之間的大小關係,然後將集合元素按升序(預設情況)排列。如果試圖把一個物件新增到 TreeSet 時,則該物件的類必須實現 Comparable 介面。向 TreeSet 中新增元素時,只有第一個元素無須比較compareTo()方法,後面新增的所有元素都會呼叫compareTo()方法進行比較。因為只有相同類的兩個例項才會比較大小,所以向 TreeSet 中新增的應該是同一個類的物件。對於 TreeSet 集合而言,它判斷兩個物件是否相等的唯一標準是:兩個物件通過 compareTo(Object obj) 方法比較返回值。當需要把一個物件放入 TreeSet 中,重寫該物件對應的 equals() 方法時,應保證該方法與 compareTo(Object obj) 方法有一致的結果:如果兩個物件通過equals() 方法比較返回 true,則通過 compareTo(Object obj) 方法比較應返回 0。否則,讓人難以理解。

定製排序:TreeSet的自然排序要求元素所屬的類實現Comparable介面,如果元素所屬的類沒有實現Comparable介面,或不希望按照升序(預設情況)的方式排列元素或希望按照其它屬性大小進行排序,則考慮使用定製排序。定製排序,通過Comparator介面來實現。需要重寫compare(T o1,T o2)方法。利用int compare(T o1,T o2)方法,比較o1和o2的大小:如果方法返回正整數,則表示o1大於o2;如果返回0,表示相等;返回負整數,表示o1小於o2。要實現定製排序,需要將實現Comparator介面的例項作為形參傳遞給TreeSet的構造器。此時,仍然只能向TreeSet中新增型別相同的物件。否則發生ClassCastException異常。使用定製排序判斷兩個元素相等的標準是:通過Comparator比較兩個元素返回了0。

Map實現類之:HashMap

HashMap是 Map 介面使用頻率最高的實現類。允許使用null鍵和null值,與HashSet一樣,不保證對映的順序。執行緒不安全。

所有的key構成的集合是Set:無序的、不可重複的。所以,key所在的類要重寫:equals()和hashCode()。所有的value構成的集合是Collection:無序的、可以重複的。所以,value所在的類要重寫:equals()。

一個key-value構成一個entry。所有的entry構成的集合是Set:無序的、不可重複的。HashMap 判斷兩個 key 相等的標準是:兩個 key 通過 equals() 方法返回 true,hashCode 值也相等。HashMap 判斷兩個 value相等的標準是:兩個 value 通過 equals() 方法返回 true。

HashMap的儲存結構:JDK 7及以前版本:HashMap是陣列+連結串列結構(即為鏈地址法)。JDK 8版本釋出以後:HashMap是陣列+連結串列+紅黑樹實現。當連結串列數量大於8時,結構轉為紅黑樹。

HashMap原始碼中的重要常量:

DEFAULT_INITIAL_CAPACITY : HashMap的預設容量,16

MAXIMUM_CAPACITY : HashMap的最大支援容量,2^30

DEFAULT_LOAD_FACTOR:HashMap的預設載入因子

TREEIFY_THRESHOLD:Bucket中連結串列長度大於該預設值,轉化為紅黑樹

UNTREEIFY_THRESHOLD:Bucket中紅黑樹儲存的Node小於該預設值,轉化為連結串列

MIN_TREEIFY_CAPACITY:桶中的Node被樹化時最小的hash表容量。(當桶 中Node的數量大到需要變紅黑樹時,若hash表容量小於 MIN_TREEIFY_CAPACITY時,此時應執行resize擴容操作這個MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的4倍。)

table:儲存元素的陣列,總是2的n次冪

entrySet:儲存具體元素的集

size:HashMap中儲存的鍵值對的數量

modCount:HashMap擴容和結構改變的次數。

threshold:擴容的臨界值,=容量*填充因子

loadFactor:填充因子

HashMap的儲存結構:

JDK 1.8之前

​ HashMap的內部儲存結構其實是陣列和連結串列的結合。當例項化一個HashMap時,系統會建立一個長度為Capacity的Entry陣列,這個長度在雜湊表中被稱為容量(Capacity),在這個陣列中可以存放元素的位置我們稱之為“桶”(bucket),每個bucket都有自己的索引,系統可以根據索引快速的查詢bucket中的元素。 每個bucket中儲存一個元素,即一個Entry物件,但每一個Entry物件可以帶一個引用變數,用於指向下一個元素,因此,在一個桶中,就有可能生成一個Entry鏈。而且新新增的元素作為連結串列的head。 l

新增元素的過程:

​ 向HashMap中新增entry1(key,value),需要首先計算entry1中key的雜湊值(根據key所在類的hashCode()計算得到),此雜湊值經過處理以後,得到在底層Entry[]陣列中要儲存的位置i。如果位置i上沒有元素,則entry1直接新增成功。如果位置i上已經存在entry2(或還有連結串列存在的entry3,entry4),則需要通過迴圈的方法,依次比較entry1中key和其他的entry。如果彼此hash值不同,則直接新增成功。如果hash值相同,繼續比較二者是否equals。如果返回值為true,則使用entry1的value去替換equals為true的entry的value。如果遍歷一遍以後,發現所有的equals返回都為false,則entry1仍可新增成功。entry1指向原有的entry元素。

HashMap的擴容:

​ 當HashMap中的元素越來越多的時候,hash衝突的機率也就越來越高,因為陣列的長度是固定的。所以為了提高查詢的效率,就要對HashMap的陣列進行擴容,而在HashMap陣列擴容之後,最消耗效能的點就出現了:原陣列中的資料必須重新計算其在新陣列中的位置,並放進去,這就是resize。那麼HashMap什麼時候進行擴容呢?當HashMap中的元素個數超過陣列大小(陣列總大小length,不是陣列中個數size)*loadFactor 時,就會進行陣列擴容,loadFactor 的預設值 (DEFAULT_LOAD_FACTOR)為0.75,這是一個折中的取值。也就是說,預設情況下,陣列大小(DEFAULT_INITIAL_CAPACITY)為16,那麼當HashMap中元素個數

超過16**0.75=12(這個值就是程式碼中的threshold值,也叫做臨界值)的時候,就把陣列的大小擴充套件為 2*16=32,即擴大一倍,然後重新計算每個元素在陣列中的位置,而這是一個非常消耗效能的操作,所以如果我們已經預知HashMap中元素的個數,那麼預設元素的個數能夠有效的提高HashMap的效能。

JDK 1.8

​ HashMap的內部儲存結構其實是陣列+連結串列+樹的結合。當例項化一個HashMap時,會初始化initialCapacity和loadFactor,在put第一對對映關係時,系統會建立一個長度為initialCapacity的Node陣列,這個長度在雜湊表中被稱為容量(Capacity),在這個陣列中可以存放元素的位置我們稱之為“桶”(bucket),每個bucket都有自己的索引,系統可以根據索引快速的查詢bucket中的元素。 每個bucket中儲存一個元素,即一個Node物件,但每一個Node物件可以帶一個引用變數next,用於指向下一個元素,因此,在一個桶中,就有可能生成一個Node鏈。也可能是一個一個TreeNode物件,每一個TreeNode物件可以有兩個葉子結點left和right,因此,在一個桶中,就有可能生成一個TreeNode樹。而新新增的元素作為連結串列的last,或樹的葉子結點。

​ 那麼HashMap什麼時候進行擴容和樹形化呢?當HashMap中的元素個數超過陣列大小(陣列總大小length,不是陣列中個數size)loadFactor 時,就會進行陣列擴容,loadFactor的預設值(DEFAULT_LOAD_FACTOR)為0.75,這是一個折中的取值。也就是說,預設情況下,陣列大小(DEFAULT_INITIAL_CAPACITY)為16,那麼當HashMap中元素個數超過160.75=12(這個值就是程式碼中的threshold值,也叫做臨界值)的時候,就把陣列的大小擴充套件為 2*16=32,即擴大一倍,然後重新計算每個元素在陣列中的位置,而這是一個非常消耗效能的操作,所以如果我們已經預知HashMap中元素的個數,那麼預設元素的個數能夠有效的提高HashMap的效能。 當HashMap中的其中一個鏈的物件個數如果達到了8個,此時如果capacity沒有達到64,那麼HashMap會先擴容解決,如果已經達到了64,那麼這個鏈會變成樹,結點型別由Node變成TreeNode型別。當然,如果當對映關係被移除後,下次resize方法時判斷樹的結點個數低於6個,也會把樹再轉為連結串列。

總結:JDK1.8相較於之前的變化:

1.HashMap map = new HashMap();//預設情況下,先不建立長度為16的陣列

2.當首次呼叫map.put()時,再建立長度為16的陣列

3.陣列為Node型別,在jdk7中稱為Entry型別

4.形成連結串列結構時,新新增的key-value對在連結串列的尾部(七上八下)

5.當陣列指定索引位置的連結串列長度>8時,且map中的陣列的長度> 64時,此索引位置上的所有key-value對使用紅黑樹進行儲存。

LinkedHashMap 是 HashMap 的子類

在HashMap儲存結構的基礎上,使用了一對雙向連結串列來記錄新增元素的順序與LinkedHashSet類似,LinkedHashMap 可以維護 Map 的迭代順序:迭代順序與 Key-Value 對的插入順序一致

Map實現類之:TreeMap

TreeMap儲存 Key-Value 對時,需要根據 key-value 對進行排序。TreeMap 可以保證所有的 Key-Value 對處於有序狀態。TreeSet底層使用紅黑樹結構儲存資料

TreeMap 的 Key 的排序:

自然排序:TreeMap 的所有的 Key 必須實現 Comparable 介面,而且所有的 Key 應該是同一個類的物件,否則將會丟擲 ClasssCastException

定製排序:建立 TreeMap 時,傳入一個 Comparator 物件,該物件負責對TreeMap 中的所有 key 進行排序。此時不需要 Map 的 Key 實現Comparable 介面TreeMap判斷兩個key相等的標準:兩個key通過compareTo()方法或者compare()方法返回0。

Map實現類之:Hashtable和Properties

Hashtable是個古老的 Map 實現類,JDK1.0就提供了。不同於HashMap,Hashtable是執行緒安全的。Hashtable實現原理和HashMap相同,功能相同。底層都使用雜湊表結構,查詢速度快,很多情況下可以互用。

與HashMap不同,Hashtable 不允許使用 null 作為 key 和 value。

與HashMap一樣,Hashtable 也不能保證其中 Key-Value 對的順序。

Hashtable判斷兩個key相等、兩個value相等的標準,與HashMap一致。

Properties 類是 Hashtable 的子類,該物件用於處理屬性檔案。由於屬性檔案裡的 key、value 都是字串型別,所以 Properties 裡的 key 和 value 都是字串型別。存取資料時,建議使用setProperty(String key,String value)方法和getProperty(String key)方法

	Properties pros = new Properties();
	pros.load(new FileInputStream("jdbc.properties"));
	String user = pros.getProperty("user");
	System.out.println(user);

ArrayList/LinkedList/Vector的異同,Vector和ArrayList的最大區別?

​ ArrayList和LinkedList的異同:二者都執行緒不安全,相對執行緒安全的Vector,執行效率高。此外,ArrayList是實現了基於動態陣列的資料結構,LinkedList基於連結串列的資料結構。對於隨機訪問get和set,ArrayList覺得優於LinkedList,因為LinkedList要移動指標。對於新增和刪除操作add(特指插入)和remove,LinkedList比較佔優勢,因為ArrayList要移動資料。

​ ArrayList和Vector的區別:Vector和ArrayList幾乎是完全相同的,唯一的區別在於Vector是同步類(synchronized),屬於強同步類。因此開銷就比ArrayList要大,訪問要慢。正常情況下,大多數的Java程式設計師使用ArrayList而不是Vector,因為同步完全可以由程式設計師自己來控制。Vector每次擴容請求其大小的2倍空間,而ArrayList是1.5倍。Vector還有一個子類Stack.

Collections集合操作方法

排序操作:(均為static方法)

	reverse(List)//反轉 List 中元素的順序
	shuffle(List)//對 List 集合元素進行隨機排序
	sort(List)//根據元素的自然順序對指定 List 集合元素按升序排序
	sort(List,Comparator)//根據指定的 Comparator 產生的順序對 List 集合元素進行排序
	swap(List,int, int)//將指定 list 集合中的 i 處元素和 j 處元素進行交換

查詢、替換:

	Object max(Collection)//根據元素的自然順序,返回給定集合中的最大元素
	Object max(Collection,Comparator)//根據 Comparator 指定的順序,返回給定集合中的最大元素
	Object min(Collection)
	Object min(Collection,Comparator)
	int frequency(Collection,Object)//返回指定集合中指定元素的出現次數
	void copy(List dest,List src)//將src中的內容複製到dest中 
	boolean replaceAll(List list,Object oldVal,Object newVal)//使用新值替換List 物件的所有舊值

Collections 類中提供了多個 synchronizedXxx() 方法,該方法可使將指定集合包裝成執行緒同步的集合,從而可以解決多執行緒併發訪問集合時的執行緒安全問題.

Java中的泛型

​ 所謂泛型(Generic),就是允許在定義類、介面時通過一個標識表示類中某個屬性的型別或者是某個方法的返回值及引數型別。這個型別引數將在使用時(例如,繼承或實現這個介面,用這個型別宣告變數、建立物件時)確定(即傳入實際的型別引數,也稱為型別實參)。Java泛型可以保證如果程式在編譯時沒有發出警告,執行時就不會產生ClassCastException異常。同時,程式碼更加簡潔、健壯。主要優點是能夠在編譯器而不是在執行期檢測錯誤.

//在集合中使用泛型
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("Tom1", 34);
map.put("Tom2", 44);
map.put("Tom3", 33);
map.put("Tom4", 32);
//新增失敗
//map.put(33, "Tom");
Set<Entry<String, Integer>> entrySet = map.entrySet();
Iterator<Entry<String, Integer>> iterator = entrySet.iterator();
while (iterator.hasNext()) {
    Entry<String, Integer> entry = iterator.next();
    System.out.println(entry.getKey() + "--->" + entry.getValue());
}

自定義泛型結構:

interface List 和 class GenTest<K,V>

其中,T,K,V不代表值,而是表示型別。這裡使用任意字母都可以。

常用T表示,是Type的縮寫。T只能是類,不能用基本資料型別填充。但可以使用包裝類填充。

  1. 泛型類可能有多個引數,此時應將多個引數一起放在尖括號內。比如:<E1,E2,E3>

  2. 泛型類的構造器如下:public GenericClass(){}。

    而下面是錯誤的:public GenericClass(){}

  3. 例項化後,操作原來泛型位置的結構必須與指定的泛型型別一致。

  4. 泛型不同的引用不能相互賦值。儘管在編譯時ArrayList和ArrayList是兩種型別,但是,在執行時只有一個ArrayList被載入到JVM中。

  5. 泛型如果不指定,將被擦除,泛型對應的型別均按照Object處理,但不等價於Object。經驗:泛型要使用一路都用。要不用,一路都不要用。

  6. 如果泛型結構是一個介面或抽象類,則不可建立泛型類的物件。

  7. jdk1.7,泛型的簡化操作:ArrayList flist = new ArrayList<>();

  8. 泛型的指定中不能使用基本資料型別,可以使用包裝類替換。

  9. 在類/介面上宣告的泛型,在本類或本介面中即代表某種型別,可以作為非靜態屬性的型別、非靜態方法的引數型別、非靜態方法的返回值型別。但在靜態方法中不能使用類的泛型。

  10. 異常類不能是泛型的

  11. 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];參考:ArrayList原始碼中:Object[] elementData,而非泛型引數型別陣列。

  12. 父類有泛型,子類可以選擇保留泛型也可以選擇指定泛型型別:

子類不保留父類的泛型:按需實現

​ 沒有型別 擦除

​ 具體型別

子類保留父類的泛型:泛型子類

​ 全部保留

​ 部分保留

結論:子類必須是“富二代”,子類除了指定或保留父類的泛型,還可以增加自己的泛型

public class GenericTest {
    public static void main(String[] args) {
        GenericClass<Integer, String> genericClass = new GenericClass<>(1, "zhangsan");
        GenericClass<Integer, String> lisi = genericClass.setInfo(3).setDesc("lisi");
	String name = lisi.getValue("name");
    }
}
/**
 * 泛型類
 *
 * @param <T> 泛型一
 * @param <K> 泛型二
 */
class GenericClass<T, K> {
    private T info;
    private K desc;
    public T getInfo() {
        return info;
    }
    public GenericClass<T,K> setInfo(T info) {
        this.info = info;
        return this;
    }
    public K getDesc() {
        return desc;
    }
    public GenericClass<T,K> setDesc(K desc) {
        this.desc = desc;
        return this;
    }
    public GenericClass(T info, K desc) {
        this.info = info;
        this.desc = desc;
    }
    //泛型方法
	public <V> V getValue(V valuev) {
        V value = valuev;
        return value;
    }
}

泛型萬用字元<?>的使用:

1.使用型別萬用字元:?

比如:List,Map

List<?>是List、List等各種泛型List的父類。

2.讀取List<?>的物件list中的元素時,永遠是安全的,因為不管list的真實型別是什麼,它包含的都是Object。

3.寫入list中的元素時,不行。因為我們不知道c的元素型別,我們不能向其中新增物件。唯一的例外是null,它是所有型別的成員。

另一方面,我們可以呼叫get()方法並使用其返回值。返回值是一個未知的型別,但是我們知道,它總是一個Object。

:允許所有泛型的引用呼叫

​ 萬用字元指定上限:上限extends:使用時指定的型別必須是繼承某個類,或者實 現某個介面,即<=

​ 萬用字元指定下限:下限super:使用時指定的型別不能小於操作的類,即>=

舉例:

(無窮小 , Number] 只允許泛型為Number及Number子類的引用呼叫 [Number , 無窮大) 只允許泛型為Number及Number父類的引用呼叫 只允許泛型為實現Comparable介面的實現類的引用呼叫

File類的使用

java.io.File類:檔案和檔案目錄路徑的抽象表示形式,與平臺無關。File 能新建、刪除、重新命名檔案和目錄,但 File 不能訪問檔案內容本身。如果需要訪問檔案內容本身,則需要使用輸入/輸出流。想要在Java程式中表示一個真實存在的檔案或目錄,那麼必須有一個File物件,但是Java程式中的一個File物件,可能沒有一個真實存在的檔案或目錄。File物件可以作為引數傳遞給流的構造器。

當硬碟中有一個真實的檔案或者目錄時,建立File物件時,會顯示賦值。當硬碟中沒有真實的檔案或者目錄時,除了指定目錄和路徑外,其他的屬性都是取成員變數的預設值。

public File(String pathname) 以pathname為路徑建立File物件,可以是絕對路徑或者相對路徑,如果pathname是相對路徑,則預設的當前路徑在系統屬性user.dir中儲存。

Ø 絕對路徑:是一個固定的路徑,從碟符開始

Ø 相對路徑:是相對於某個位置開始

public File(String parent,String child)以parent為父路徑,child為子路徑建立File物件。

public File(File parent,String child)根據一個父File物件和子檔案路徑建立File物件。

File類提供了一個常量:

public static final String separator根據作業系統,動態的提供分隔符。

File類的獲取功能:

public String getAbsolutePath():獲取絕對路徑

public String getPath() :獲取路徑

public String getName() :獲取名稱

public String getParent():獲取上層檔案目錄路徑。若無,返回null

public long length() :獲取檔案長度(即:位元組數)。不能獲取目錄的長度。 public long lastModified() :獲取最後一次的修改時間,毫秒值

public String[] list() :獲取指定目錄下的所有檔案或者檔案目錄的名稱陣列

public File[] listFiles():獲取指定目錄下的所有檔案或者檔案目錄的File陣列

File類的重新命名功能:

public boolean renameTo(File dest):把檔案重新命名為指定的檔案路徑

File類的判斷功能:

public boolean isDirectory():判斷是否是檔案目錄

public boolean isFile() :判斷是否是檔案

public boolean exists() :判斷是否存在

public boolean canRead() :判斷是否可讀

public boolean canWrite() :判斷是否可寫

public boolean isHidden() :判斷是否隱藏

File類的建立功能:

public boolean createNewFile() :建立檔案。若檔案存在,則不建立,返回false

public boolean mkdir() :建立檔案目錄。如果此檔案目錄存在,就不建立了。

如果此檔案目錄的上層目錄不存在,也不建立。

public boolean mkdirs():建立檔案目錄。如果上層檔案目錄不存在,一併創 建

注意事項:如果你建立檔案或者檔案目錄沒有寫碟符路徑,那麼,預設在項 目路徑下。

File類的刪除功能

public boolean delete():刪除檔案或者資料夾

刪除注意事項:Java中的刪除不走回收站。 要刪除一個檔案目錄,請注意 該檔案目錄內不能包含檔案或者檔案目錄

/**
 * 遞迴呼叫函式遍歷檔案下所有目錄和檔案
 * @param path
 */
public static void getSubFile(String path) {
    File file = new File(path);
    if (!file.exists()) {
        return;
    }
    if (file.isDirectory()) {
        System.out.println(file.getAbsolutePath());
        File[] list = file.listFiles();
        for (File f : list) {
            String absolutePath = f.getAbsolutePath();
            getSubFile(absolutePath);
        }
    } else {
        System.out.println(file.getName());
    }
}

Java的IO流

按操作資料單位不同分為:位元組流(8 bit),字元流(16 bit)

按資料流的流向不同分為:輸入流,輸出流

按流的角色的不同分為:節點流,處理流

Java的IO流都是從以上這四個抽象基類派生的,由這四個類派生出來的子類的名字都是基類的名字為字尾命名的。

節點流:直接從資料來源或者從目的地讀取資料

處理流:不直接連線到資料來源或者目的地,而是連線在已存在的流上,通過對數 據的處理提供更強大的讀寫能力。

InputStream 和 Reader 是所有輸入流的基類。

InputStream:

int read()從輸入流中讀取資料的下一個位元組。返回 0 到 255 範圍內的 int 位元組值。如果因為已經到達流末尾而沒有可用的位元組,則返回值 -1。

int read(byte[] b)從此輸入流中將最多b.length個位元組的資料讀入一個byte 陣列中。如果因為已經到達流末尾而沒有可用的位元組,則返回值 -1。否則以整數形式返回實際讀取的位元組數。

int read(byte[] b, int off,int len)將輸入流中最多 len 個數據位元組讀入 byte 陣列。嘗試讀取len個位元組,但讀取的位元組也可能小於該值。以整數形式返回實際讀取的位元組數。如果因為流位於檔案末尾而沒有可用的位元組,則返回值 -1。 l public void close() throws IOException關閉此輸入流並釋放與該流關聯的所有系統資源。

Reader:

int read()讀取單個字元。作為整數讀取的字元,範圍在0到65535之間 (0x00-0xffff)(2個位元組的Unicode碼),如果已到達流的末尾,則返回 -1 l

int read(char[] cbuf)將字元讀入陣列。如果已到達流的末尾,則返回 -1。否則返回本次讀取的字元數。 l

int read(char[] cbuf,int off,int len)將字元讀入陣列的某一部分。存到陣列cbuf中,從off處開始儲存,最多讀len個字元。如果已到達流的末尾,則返回 -1。否則返回本次讀取的字元數。 l

public void close() throws IOException關閉此輸入流並釋放與該流關聯的所有系統資源。

OutputStream 和 Writer是所有輸出流的基類。

outputStream:

void write(int b)將指定的位元組寫入此輸出流。write 的常規協定是:向輸出流寫入一個位元組。要寫入的位元組是引數 b 的八個低位。b 的24個高位將被忽略。 即寫入0~255範圍的。

void write(byte[] b)將 b.length 個位元組從指定的 byte 陣列寫入此輸出流。write(b) 的常規協定是:應該與呼叫 write(b, 0, b.length) 的效果完全相同。 l void write(byte[] b,int off,int len)將指定 byte 陣列中從偏移量 off 開始的 len 個位元組寫入此輸出流。 l

public void flush()throws IOException重新整理此輸出流並強制寫出所有緩衝的輸出位元組,呼叫此方法指示應將這些位元組立即寫入它們預期的目標。 l

public void close() throws IOException關閉此輸出流並釋放與該流關聯的所有系統資源。

Writer:

void write(int c)寫入單個字元。要寫入的字元包含在給定整數值的 16 個低位中,16 高位被忽略。 即寫入0 到 65535 之間的Unicode碼。 l

void write(char[] cbuf)寫入字元陣列。 l

void write(char[] cbuf,int off,int len)寫入字元陣列的某一部分。從off開始,寫入len個字元

void write(String str)寫入字串。 l

void write(String str,int off,int len)寫入字串的某一部分。

void flush()重新整理該流的緩衝,則立即將它們寫入預期目標。 l

public void close() throws IOException關閉此輸出流並釋放與該流關聯的所有系統資源。

System.in和System.out分別代表了系統標準的輸入和輸出裝置

預設輸入裝置是:鍵盤,輸出裝置是:顯示器

System.in的型別是InputStream

System.out的型別是PrintStream,是OutputStream的子類和FilterOutputStream 的子類

重定向:通過System類的setIn,setOut方法對預設裝置進行改變。

Ø public static void setIn(InputStream in)

Ø public static void setOut(PrintStream out)

實現將基本資料型別的資料格式轉化為字串輸出

列印流:PrintStream和PrintWriter

Ø提供了一系列過載的print()和println()方法,用於多種資料型別的輸出

Ø PrintStream和PrintWriter的輸出不會丟擲IOException異常

Ø PrintStream和PrintWriter有自動flush功能

Ø PrintStream 列印的所有字元都使用平臺的預設字元編碼轉換為位元組。

在需要寫入字元而不是寫入位元組的情況下,應該使用 PrintWriter 類。 Ø System.out返回的是PrintStream的例項

節點流或檔案流

FileInputStream&FileOutputStream&FileReader&FileWriter

讀取檔案

1.建立一個流物件,將已存在的一個檔案載入進流。

Ø FileReader fr = new FileReader(new File(“Test.txt”));

2.建立一個臨時存放資料的陣列。

Ø char[] ch = new char[1024];

3.呼叫流物件的讀取方法將流中的資料讀入到陣列中。

Ø fr.read(ch);

4.關閉資源。

Ø fr.close();

寫入檔案

1.建立流物件,建立資料存放檔案

Ø FileWriter fw = new FileWriter(new File(“Test.txt”));

2.呼叫流物件的寫入方法,將資料寫入流

Ø fw.write(“我愛你中國”);

3.關閉流資源,並將流中的資料清空到檔案中。

Ø fw.close();

class InputStreamTest {
    public static void inputTest() {
        try (FileInputStream inputStream = new FileInputStream("text.txt");
             FileOutputStream outputStream = new FileOutputStream("text1.txt", true);
        ) {
            byte[] b = new byte[8];
            while (inputStream.read(b) != -1) {
                outputStream.write(b);
            }
            outputStream.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
class ReaderTest {
    public static void readerTest() {
        try (FileReader reader = new FileReader("text.txt");
            FileWriter writer =new FileWriter("text2.txt",true);
        ) {
            char[] a = new char[8];
            while (reader.read(a) != -1) {
                writer.write(a);
            }
            writer.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

緩衝流

為了提高資料讀寫的速度,Java API提供了帶緩衝功能的流類,在使用這些流類時,會建立一個內部緩衝區陣列,預設使用8192個位元組(8Kb)的緩衝區。 l 緩衝流要“套接”在相應的節點流之上,根據資料操作單位可以把緩衝流分為:

BufferedInputStream 和 BufferedOutputStream

BufferedReader 和 BufferedWriter

當讀取資料時,資料按塊讀入緩衝區,其後的讀操作則直接訪問緩衝區。

當使用BufferedInputStream讀取位元組檔案時,BufferedInputStream會一次性從檔案中讀取8192個(8Kb),存在緩衝區中,直到緩衝區裝滿了,才重新從檔案中讀取下一個8192個位元組陣列。 向流中寫入位元組時,不會直接寫到檔案,先寫到緩衝區中直到緩衝區寫滿,BufferedOutputStream才會把緩衝區中的資料一次性寫到檔案裡。使用方法flush()可以強制將緩衝區的內容全部寫入輸出流。關閉流的順序和開啟流的順序相反。只要關閉最外層流即可,關閉最外層流也會相應關閉內層節點流.

flush()方法的使用:手動將buffer中內容寫入檔案。如果是帶緩衝區的流物件的close()方法,不但會關閉流,還會在關閉流之前重新整理緩衝區,關閉後不能再寫出。

public class BufferedTest {
    public static void main(String[] args) {
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            // 建立緩衝流物件:它是處理流,是對節點流的包裝 
            br = new BufferedReader(new FileReader("d:\\IOTest\\source.txt"));
            bw = new BufferedWriter(new FileWriter("d:\\IOTest\\dest.txt"));
            String str;
            while ((str = br.readLine()) != null) { // 一次讀取字元文字檔案的一行字元 
                bw.write(str); // 一次寫入一行字串 
                bw.newLine(); // 寫入行分隔符 
            }
            bw.flush(); // 重新整理緩衝區 
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 關閉IO流物件 
            try {
                if (bw != null) {
                    bw.close(); // 關閉過濾流時,會自動關閉它所包裝的底層節點流 
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (br != null) {
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

轉換流

轉換流提供了在位元組流和字元流之間的轉換

Java API提供了兩個轉換流:

Ø InputStreamReader:將InputStream轉換為Reader

Ø OutputStreamWriter:將Writer轉換為OutputStream

位元組流中的資料都是字元時,轉成字元流操作更高效。很多時候我們使用轉換流來處理檔案亂碼問題。實現編碼和解碼的功能。

InputStreamReader:

​ 實現將位元組的輸入流按指定字符集轉換為字元的輸入流。

OutputStreamWriter:

​ 實現將字元的輸出流按指定字符集轉換為位元組的輸出流.

public void testMyInput() throws Exception {
    FileInputStream fis = new FileInputStream("dbcp.txt");
    FileOutputStream fos = new FileOutputStream("dbcp5.txt");
    InputStreamReader isr = new InputStreamReader(fis, "GBK");
    OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");
    BufferedReader br = new BufferedReader(isr);
    BufferedWriter bw = new BufferedWriter(osw);
    String str = null;
    while ((str = br.readLine()) != null) {
        bw.write(str);
        bw.newLine();
        bw.flush();
    }
    bw.close();
    br.close();
}

物件流序列化物件

ObjectInputStream和OjbectOutputSteam

用於儲存和讀取基本資料型別資料或物件的處理流。它的強大之處就是可以把Java中的物件寫入到資料來源中,也能把物件從資料來源中還原回來。

序列化:用ObjectOutputStream類儲存基本型別資料或物件的機制

反序列化:用ObjectInputStream類讀取基本型別資料或物件的機制

ObjectOutputStream和ObjectInputStream不能序列化static和transient修飾的成員變數

物件的序列化:

物件序列化機制允許把記憶體中的Java物件轉換成平臺無關的二進位制流,從而允許把這種二進位制流持久地儲存在磁碟上,或通過網路將這種二進位制流傳輸到另一個網路節點。//當其它程式獲取了這種二進位制流,就可以恢復成原來的Java物件。

序列化的好處在於可將任何實現了Serializable介面的物件轉化為位元組資料,使其在儲存和傳輸時可被還原。序列化是 RMI(Remote Method Invoke – 遠端方法呼叫)過程的引數和返回值都必須實現的機制,而 RMI 是 JavaEE 的基礎。因此序列化機制是JavaEE 平臺的基礎。

如果需要讓某個物件支援序列化機制,則必須讓物件所屬的類及其屬性是可序列化的,為了讓某個類是可序列化的,該類必須實現如下兩個介面之一。否則,會丟擲NotSerializableException異常

ØSerializable

ØExternalizable

凡是實現Serializable介面的類都有一個表示序列化版本識別符號的靜態變數:

	private static final long serialVersionUID;

​ serialVersionUID用來表明類的不同版本間的相容性。簡言之,其目的是以序列化物件進行版本控制,有關各版本反序列化時是否相容。如果類沒有顯示定義這個靜態常量,它的值是Java執行時環境根據類的內部細節自動生成的。若類的例項變數做了修改,serialVersionUID 可能發生變化。故建議,顯式宣告。

簡單來說,Java的序列化機制是通過在執行時判斷類的serialVersionUID來驗證版本一致性的。在進行反序列化時,JVM會把傳來的位元組流中的serialVersionUID與本地相應實體類的serialVersionUID進行比較,如果相同就認為是一致的,可以進行反序列化,否則就會出現序列化版本不一致的異常。(InvalidCastException)

若某個類實現了 Serializable 介面,該類的物件就是可序列化的:

序列化:

建立一個ObjectOutputStream

呼叫ObjectOutputStream物件的 writeObject(物件)方法輸出可序列化物件, 注意寫出一次,操作flush()一次

反序列化:

建立一個 ObjectInputStream

呼叫 readObject() 方法讀取流中的物件

強調:如果某個類的屬性不是基本資料型別或 String 型別,而是另一個 引用型別,那麼這個引用型別必須是可序列化的,否則擁有該型別的Field 的類也不能序列化

ObjectOutputStream oos = new ObjectOutputStream(new 	FileOutputStream(“data.txt")); 
Person p = new Person("韓梅梅", 18, "中華大街", new Pet()); 
oos.writeObject(p); 
oos.flush(); 
oos.close(); 
//反序列化:將磁碟中的物件資料來源讀出。 
ObjectInputStream ois = new ObjectInputStream(new 	FileInputStream(“data.txt")); 
Person p1 = (Person)ois.readObject(); 
System.out.println(p1.toString()); 
ois.close();

Path、Paths和Files核心API

Paths 類提供的靜態 get() 方法用來獲取 Path 物件:

Østatic Path get(String first, String … more) : 用於將多個字串串連成路徑

Østatic Path get(URI uri): 返回指定uri對應的Path路徑

Path 常用方法

Ø String toString() : 返回呼叫 Path 物件的字串表示形式

Ø boolean startsWith(String path) : 判斷是否以 path 路徑開始

Ø boolean endsWith(String path) : 判斷是否以 path 路徑結束

Ø boolean isAbsolute() : 判斷是否是絕對路徑

Ø Path getParent() :返回Path物件包含整個路徑,不包含 Path 物件指定的檔案路徑

Ø Path getRoot() :返回呼叫 Path 物件的根路徑

Ø Path getFileName() : 返回與呼叫 Path 物件關聯的檔名

Ø int getNameCount() : 返回Path 根目錄後面元素的數量

Ø Path getName(int idx) : 返回指定索引位置 idx 的路徑名稱

Ø Path toAbsolutePath() : 作為絕對路徑返回呼叫 Path 物件

Ø Path resolve(Path p) :合併兩個路徑,返回合併後的路徑對應的Path物件

Ø File toFile(): 將Path轉化為File類的物件

java.nio.file.Files 用於操作檔案或目錄的工具類

Files常用方法:

Path copy(Path src, Path dest, CopyOption … how) : 檔案的複製

Path createDirectory(Path path, FileAttribute<?> … attr) : 建立一個目錄

Path createFile(Path path, FileAttribute<?> … arr) : 建立一個檔案

void delete(Path path) : 刪除一個檔案/目錄,如果不存在,執行報錯

void deleteIfExists(Path path) : Path對應的檔案/目錄如果存在,執行刪除

Path move(Path src, Path dest, CopyOption…how) : 將 src 移動到 dest 位置

long size(Path path) : 返回 path 指定檔案的大小

Files常用方法:用於判斷

Ø boolean exists(Path path, LinkOption … opts) : 判斷檔案是否存在

Ø boolean isDirectory(Path path, LinkOption … opts) : 判斷是否是目錄

Ø boolean isRegularFile(Path path, LinkOption … opts) : 判斷是否是檔案

Ø boolean isHidden(Path path) : 判斷是否是隱藏檔案

Ø boolean isReadable(Path path) : 判斷檔案是否可讀

Ø boolean isWritable(Path path) : 判斷檔案是否可寫

Ø boolean notExists(Path path, LinkOption … opts) : 判斷檔案是否不存在

Files常用方法:用於操作內容

SeekableByteChannel newByteChannel(Path path, OpenOption…how) : 獲取與指定檔案的連線,how 指定開啟方式。

DirectoryStream newDirectoryStream(Path path) : 開啟 path 指定的目錄

InputStream newInputStream(Path path, OpenOption…how):獲取 InputStream 物件

OutputStream newOutputStream(Path path, OpenOption…how) : 獲取 OutputStream 物件

網路通訊

通訊雙方地址:

IP:唯一標識網路上的計算機。IPV4:4個位元組組成,4個0-255。192.168.開頭的就是私有址址,範圍即為192.168.0.0--192.168.255.255,專門為組織機構內部使用。IPV6:128位(16個位元組),寫成8個無符號整數,每個整數用四個十六進位制位表示,數之間用冒號(:)分開

埠號:標識正在計算機上執行的程式。埠分類:公認埠:0~1023。被預先定義的服務通訊佔用(如:HTTP佔用埠80,FTP佔用埠21,Telnet佔用埠23) 註冊埠:1024~49151。分配給使用者程序或應用程式。(如:Tomcat佔用埠8080,MySQL佔用埠3306,Oracle佔用埠1521等)。 動態/私有埠:49152~65535。

網路通訊協議:TCP/IP協議

埠號與IP地址的組合得出一個網路套接字:Socket。

InetAddress類主要表示IP地址,兩個子類:Inet4Address、Inet6Address。

lInetAddress類沒有提供公共的構造器,而是提供瞭如下幾個靜態方法來獲取InetAddress例項

public static InetAddress getLocalHost()

public static InetAddress getByName(String host)

InetAddress提供瞭如下幾個常用的方法

public String getHostAddress():返回 IP 地址字串(以文字表現形式)。

public String getHostName():獲取此 IP 地址的主機名

public boolean isReachable(int timeout):測試是否可以達到該地址

網路協議:

TCP/IP協議簇

傳輸層協議中有兩個非常重要的協議:

  • 傳輸控制協議TCP(Transmission Control Protocol)
  • 使用者資料報協議UDP(User Datagram Protocol)。

TCP/IP 以其兩個主要協議:傳輸控制協議(TCP)和網路互聯協議(IP)而得名,實際上是一組協議,包括多個具有不同功能且互為關聯的協議。

lIP(Internet Protocol)協議是網路層的主要協議,支援網間互連的資料通訊。

lTCP/IP協議模型從更實用的角度出發,形成了高效的四層體系結構,即物理鏈路層、IP層、傳輸層和應用層。

TCP協議:

  1. 使用TCP協議前,須先建立TCP連線,形成傳輸資料通道
  2. 傳輸前,採用“三次握手”方式,點對點通訊,是可靠的
  3. TCP協議進行通訊的兩個應用程序:客戶端、服務端。
  4. 在連線中可進行大資料量的傳輸
  5. 傳輸完畢,需釋放已建立的連線(四次揮手),效率低

UDP協議:

  1. 將資料、源、目的封裝成資料包,不需要建立連線
  2. 每個資料報的大小限制在64K內
  3. 傳送不管對方是否準備好,接收方收到也不確認,故是不可靠的
  4. 可以廣播發送
  5. 傳送資料結束時無需釋放資源,開銷小,速度快

Socket:

利用套接字(Socket)開發網路應用程式早已被廣泛的採用,以至於成為事實上的標準。網路上具有唯一標識的IP地址和埠號組合在一起才能構成唯一能識別的識別符號套接字。通訊的兩端都要有Socket,是兩臺機器間通訊的端點。網路通訊其實就是Socket間的通訊。Socket允許程式把網路連線當成一個流,資料在兩個Socket間通過IO傳輸。一般主動發起通訊的應用程式屬客戶端,等待通訊請求的為服務端。

Socket分類:

流套接字(stream socket):使用TCP提供可依賴的位元組流服務

資料報套接字(datagram socket):使用UDP提供“盡力而為”的資料報服務。

Socket類的常用構造器:

public Socket(InetAddress address,int port)建立一個流套接字並將其連線到指定 IP 地址的指定埠號。

public Socket(String host,int port)建立一個流套接字並將其連線到指定主機上的指定埠號。 l

Socket類的常用方法:

public InputStream getInputStream()返回此套接字的輸入流。可以用於接收網路訊息

public OutputStream getOutputStream()返回此套接字的輸出流。可以用於傳送網路訊息

public InetAddress getInetAddress()此套接字連線到的遠端 IP 地址;如果套接字是未連線的,則返回 null。

public InetAddress getLocalAddress()獲取套接字繫結的本地地址。 即本端的IP地址

public int getPort()此套接字連線到的遠端埠號;如果尚未連線套接字,則返回 0。

public int getLocalPort()返回此套接字繫結到的本地埠。 如果尚未繫結套接字,則返回 -1。即本端的埠號。

public void close()關閉此套接字。套接字被關閉後,便不可在以後的網路連線中使用(即無法重新連線或重新繫結)。需要建立新的套接字物件。 關閉此套接字也將會關閉該套接字的 InputStream 和OutputStream。

public void shutdownInput()如果在套接字上呼叫 shutdownInput() 後從套接字輸入流讀取內容,則流將返回 EOF(檔案結束符)。 即不能在從此套接字的輸入流中接收任何資料。

public void shutdownOutput()禁用此套接字的輸出流。對於 TCP 套接字,任何以前寫入的資料都將被髮送,並且後跟 TCP 的正常連線終止序列。 如果在套接字上呼叫 shutdownOutput() 後寫入套接字輸出流,則該流將丟擲 IOException。 即不能通過此套接字的輸出流傳送任何資料。

基於Socket的TCP程式設計

客戶端Socket的工作過程包含以下四個基本的步驟:

  1. 建立 Socket:根據指定服務端的 IP 地址或埠號構造 Socket 類物件。若伺服器端響應,則建立客戶端到伺服器的通訊線路。若連線失敗,會出現異常。
  2. 開啟連線到 Socket 的輸入/出流: 使用 getInputStream()方法獲得輸入流,使用getOutputStream()方法獲得輸出流,進行資料傳輸。
  3. 按照一定的協議對 Socket 進行讀/寫操作:通過輸入流讀取伺服器放入線路的資訊(但不能讀取自己放入線路的資訊),通過輸出流將資訊寫入執行緒。
  4. 關閉 Socket:斷開客戶端到伺服器的連線,釋放線路。

客戶端程式可以使用Socket類建立物件,建立的同時會自動向伺服器方發起連線。

Socket的構造器是:

Socket(String host,int port)throws UnknownHostException,IOException:向伺服器(域名是host。埠號為port)發起TCP連線,若成功,則建立Socket物件,否則丟擲異常。

Socket(InetAddress address,int port)throws IOException:根據InetAddress物件所表示的IP地址以及埠號port發起連線。

客戶端建立socketAtClient物件的過程就是向伺服器發出套接字連線請求

Socket s = new Socket(“192.168.40.165”,9999);
OutputStream out = s.getOutputStream();
out.write(" hello".getBytes());
s.close();

伺服器程式的工作過程包含以下四個基本的步驟:

  1. 呼叫 ServerSocket(int port) :建立一個伺服器端套接字,並繫結到指定埠上。用於監聽客戶端的請求。
  2. 呼叫 accept():監聽連線請求,如果客戶端請求連線,則接受連線,返回通訊套接字物件。
  3. 呼叫該Socket類物件的 getOutputStream() 和 getInputStream ():獲取輸出流和輸入流,開始網路資料的傳送和接收。
  4. 關閉ServerSocket和Socket物件:客戶端訪問結束,關閉通訊套接字。

ServerSocket 物件負責等待客戶端請求建立套接字連線,類似郵局某個視窗中的業務員。也就是說,伺服器必須事先建立一個等待客戶請求建立套接字連線的ServerSocket物件。

所謂“接收”客戶的套接字請求,就是accept()方法會返回一個 Socket 物件

ServerSocket ss = new ServerSocket(9999);
Socket s = ss.accept ();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+”:”+str);
s.close();
ss.close();

UDP網路通訊

類 DatagramSocket 和 DatagramPacket 實現了基於 UDP 協議網路程式。

UDP資料報通過資料報套接字 DatagramSocket 傳送和接收,系統不保證UDP資料報一定能夠安全送到目的地,也不能確定什麼時候可以抵達。

DatagramPacket 物件封裝了UDP資料報,在資料報中包含了傳送端的IP地址和埠號以及接收端的IP地址和埠號。

UDP協議中每個資料報都給出了完整的地址資訊,因此無須建立傳送方和接收方的連線。如同發快遞包裹一樣。

DatagramSocket 類的常用方法

public DatagramSocket(int port)建立資料報套接字並將其繫結到本地主機上的指定埠。套接字將被繫結到萬用字元地址,IP 地址由核心來選擇。

public DatagramSocket(int port,InetAddress laddr)建立資料報套接字,將其繫結到指定的本地地址。本地埠必須在 0 到 65535 之間(包括兩者)。如果 IP 地址為 0.0.0.0,套接字將被繫結到萬用字元地址,IP 地址由核心選擇。

public void close()關閉此資料報套接字。

public void send(DatagramPacket p)從此套接字傳送資料報包。DatagramPacket 包含的資訊指示:將要傳送的資料、其長度、遠端主機的 IP 地址和遠端主機的埠號。

public void receive(DatagramPacket p)從此套接字接收資料報包。當此方法返回時,DatagramPacket的緩衝區填充了接收的資料。資料報包也包含傳送方的 IP 地址和傳送方機器上的埠號。 此方法在接收到資料報前一直阻塞。資料報包物件的 length 欄位包含所接收資訊的長度。如果資訊比包的長度長,該資訊將被截短。

public InetAddress getLocalAddress()獲取套接字繫結的本地地址。

public int getLocalPort()返回此套接字繫結的本地主機上的埠號。

public InetAddress getInetAddress()返回此套接字連線的地址。如果套接字未連線,則返回 null。

public int getPort()返回此套接字的埠。如果套接字未連線,則返回 -1。

DatagramPacket類的常用方法

public DatagramPacket(byte[] buf,int length)構造 DatagramPacket,用來接收長度為 length 的資料包。length 引數必須小於等於 buf.length。

public DatagramPacket(byte[] buf,int length,InetAddress address,int port)構造資料報包,用來將長度為 length 的包傳送到指定主機上的指定埠號。length引數必須小於等於 buf.length。

public InetAddress getAddress()返回某臺機器的 IP 地址,此資料報將要發往該機器或者是從該機器接收到的。

public int getPort()返回某臺遠端主機的埠號,此資料報將要發往該主機或者是從該主機接收到的。

public byte[] getData()返回資料緩衝區。接收到的或將要傳送的資料從緩衝區中的偏移量 offset 處開始,持續 length 長度。

public int getLength()返回將要傳送或接收到的資料的長度。

傳送端:

DatagramSocket ds = null;
try {
    ds = new DatagramSocket();
    byte[] by = "hello,atguigu.com".getBytes();
    DatagramPacket dp = new DatagramPacket(by, 0, by.length, 
    InetAddress.getByName("127.0.0.1"), 10000);
    ds.send(dp);
} catch (Exception e) {
	e.printStackTrace();
} finally {
    if (ds != null)
	ds.close();
}

接收端:在接收端,要指定監聽的埠。

DatagramSocket ds = null;
try {
    ds = new DatagramSocket(10000);
    byte[] by = new byte[1024];
    DatagramPacket dp = new DatagramPacket(by, by.length);
    ds.receive(dp);
    String str = new String(dp.getData(), 0, dp.getLength());
    System.out.println(str + "--" + dp.getAddress());
} catch (Exception e) {
	e.printStackTrace();
} finally {
    if (ds != null)
    ds.close();
}

Java反射機制

動態語言:在執行時程式碼可以根據一定條件改變自身結構。C# python js

靜態語言:在執行時結構不可變的語言。Java C++ C

載入完類之後,在堆記憶體的方法區中就產生了一個Class型別的物件(一個類只有一個Class物件),這個物件就包含了完整的類的結構資訊。我們可以通過這個物件看到類的結構。這個物件就像一面鏡子,透過這個鏡子看到類的結構,所以,我們形象的稱之為:反射(Reflection)。

Java反射機制提供的功能:

  1. 在執行時判斷任意一個物件所屬的類
  2. 在執行時構造任意一個類的物件
  3. 在執行時判斷任意一個類所具有的成員變數和方法
  4. 在執行時獲取泛型資訊
  5. 在執行時呼叫任意一個物件的成員變數和方法
  6. 在執行時處理註解
  7. 生成動態代理

java.lang.Class

代表一個類。

Class本身也是一個類。

Class 物件只能由系統建立物件。

一個載入的類在 JVM 中只會有一個Class例項。

一個Class物件對應的是一個載入到JVM中的一個.class檔案。

每個類的例項都會記得自己是由哪個 Class 例項所生成。

通過Class可以完整地得到一個類中的所有被載入的結構。

Class類是Reflection的根源,針對任何你想動態載入、執行的類,唯有先獲得相應的Class物件。

Class類的常用方法:

static Class forName(String name)返回指定類名name的Class 物件。

Object newInstance()呼叫預設建構函式,返回該Class物件的一個例項。

getName()返回此Class物件所表示的實體(類、介面、陣列類、基本型別或void)名稱。

Class getSuperClass() 返回當前Class物件的父類的Class物件

Class [] getInterfaces() 獲取當前Class物件的介面

ClassLoader getClassLoader() 返回該類的類載入器

Class getSuperclass() 返回表示此Class所表示的實體的超類的Class

Constructor[] getConstructors() 返回一個包含某些Constructor物件的陣列

Field[] getDeclaredFields() 返回Field物件的一個數組

Method getMethod(String name,Class … paramTypes)返回一個Method物件,此物件的形參型別為paramType

獲取Class類的例項:

1)前提:若已知具體的類,通過類的class屬性獲取,該方法最為安全可靠,程式效能最高

例項:Class clazz = String.class;

2)前提:已知某個類的例項,呼叫該例項的getClass()方法獲取Class物件

例項:Class clazz = “www.atguigu.com”.getClass();

3)前提:已知一個類的全類名,且該類在類路徑下,可通過Class類的靜態方法forName()獲取,可能丟擲ClassNotFoundException

例項:Class clazz = Class.forName(“java.lang.String”);

4)其他方式

ClassLoader cl = this.getClass().getClassLoader();

Class clazz4 = cl.loadClass(“類的全類名”);

ClassLoader類載入器:

類載入的作用:將class檔案位元組碼內容載入到記憶體中,並將這些靜態資料轉換成方法區的執行時資料結構,然後在堆中生成一個代表這個類的java.lang.Class物件,作為方法區中類資料的訪問入口。 l

類快取:標準的JavaSE類載入器可以按要求查詢類,但一旦某個類被載入到類載入器中,它將維持載入(快取)一段時間。不過JVM垃圾回收機制可以回收這些Class物件。

建立執行時類的物件:呼叫Class物件的newInstance()方法

要 求:

​ 1)類必須有一個無引數的構造器。

​ 2)類的構造器的訪問許可權需要足夠。

呼叫有參構造器物件:

​ 1)通過Class類的getDeclaredConstructor(Class … parameterTypes)取得本類的指定形參型別的構造器

​ 2)向構造器的形參中傳遞一個物件陣列進去,裡面包含了構造器中所需的各個引數。

​ 3)通過Constructor例項化物件。

//無參構造器創造物件
Class<User> userClass = User.class;
User user = userClass.newInstance();
//獲取指定的有參構造器創造物件
Constructor<User> declaredConstructor = userClass.getDeclaredConstructor(String.class, Integer.class, Double.class);
User user1 = declaredConstructor.newInstance("zhangsan",19,3.2);

反射獲取執行時類的完整結構

Field:屬性

	public Field[] getFields()// 返回此Class物件所表示的類或介面的public的Field。
    public Field[] getDeclaredFields() //返回此Class物件所表示的類或介面的全部Field。

Field方法中:

 	public int getModifiers()// 以整數形式返回此Field的修飾符
     public Class<?> getType() //得到Field的屬性型別
     public String getName() //返回Field的名稱

呼叫類中反射得到的屬性:

在反射機制中,可以直接通過Field類操作類中的屬性,通過Field類提供的set()和get()方法就可以完成設定和取得屬性內容的操作。

public Field getField(String name) //返回此Class物件表示的類或介面的指定的public的Field。 
public Field getDeclaredField(String name)//返回此Class物件表示的類或介面的指定的Field。 

在Field中:

public Object get(Object obj) //取得指定物件obj上此Field的屬性內容
public void set(Object obj,Object value) //設定指定物件obj上此Field的屬性內容

Method:方法

	public Method[] getDeclaredMethods()//返回此Class物件所表示的類或介面的全部方法
    public Method[] getMethods() //返回此Class物件所表示的類或介面的public的方法

Method類中:

public Class<?> getReturnType()//取得全部的返回值
public Class<?>[] getParameterTypes()//取得全部的引數
public int getModifiers()//取得修飾符
public Class<?>[] getExceptionTypes()//取得異常資訊

呼叫類中反射得到的方法:

getMethod(String name,Class…parameterTypes)// 方法取得一個Method物件,並設定此方法操作時所需要的引數型別。
Object invoke(Object obj, Object[] args)//進行呼叫,並向方法中傳遞要設定的obj物件的引數資訊。

說明:

1.Object 對應原方法的返回值,若原方法無返回值,此時返回null

2.若原方法若為靜態方法,此時形參Object obj可為null

3.若原方法形參列表為空,則Object[] args為null

4.若原方法宣告為private,則需要在呼叫此invoke()方法前,顯式呼叫

5.方法物件的setAccessible(true)方法,將可訪問private的方法。

Constructor:構造器

public Constructor[] getConstructors() 返回此 Class 物件所表示的類的所有public構造方法。

public Constructor[] getDeclaredConstructors()返回此 Class 物件表示的類宣告的所有構造方法。

Constructor類中:

取得修飾符: public int getModifiers();

取得方法名稱: public String getName();

取得引數的型別:public Class<?>[] getParameterTypes();

Superclass:父類

public Class<? Super T> getSuperclass()

返回表示此 Class 所表示的實體(類、介面、基本型別)的父類的Class。

Interface:介面

public Class<?>[] getInterfaces()

返回此物件所表示的類或介面實現的介面。

Annotation:註解

​ get Annotation(Class annotationClass) 獲取此類上面的指定型別的註解

​ getDeclaredAnnotations() 獲取此類宣告的所有註解

Method和Field、Constructor物件都有setAccessible()方法。

setAccessible啟動和禁用訪問安全檢查的開關。引數值為true則指示反射的物件在使用時應該取消Java語言訪問檢查。提高反射的效率。如果程式碼中必須用反射,而該句程式碼需要頻繁的被呼叫,那麼請設定為true。使得原本無法訪問的私有成員也可以訪問。引數值為false則指示反射的物件應該實施Java語言訪問檢查。

Java8新特性:Lambda表示式

Lambda 表示式:在Java 8 語言中引入的一種新的語法元素和操作符。這個操作符為 “->”,該操作符被稱為 Lambda 操作符或箭頭操作符。它將 Lambda 分為兩個部分:

左側:指定了 Lambda 表示式需要的引數列表

右側:指定了 Lambda 體,是抽象方法的實現邏輯,也即Lambda 表示式要執行的功能。

上述 Lambda 表示式中的引數型別都是由編譯器推斷得出的。Lambda表示式中無需指定型別,程式依然可以編譯,這是因為 javac 根據程式的上下文,在後臺推斷出了引數的型別。Lambda 表示式的型別依賴於上下文環境,是由編譯器推斷出來的。這就是所謂的“型別推斷”。

Java8新特性:函式式介面

​ 只包含一個抽象方法的介面,稱為函式式介面。你可以通過 Lambda 表示式來建立該介面的物件。(若 Lambda 表示式丟擲一個受檢異常(即:非執行時異常),那麼該異常需要在目標介面的抽象方法上進行宣告)。

​ 我們可以在一個介面上使用 @FunctionalInterface 註解,這樣做可以檢查它是否是一個函式式介面。同時 javadoc 也會包含一條宣告,說明這個介面是一個函式式介面。在java.util.function包下定義了Java 8 的豐富的函式式介面。

​ 在Java8中,Lambda表示式是物件,而不是函式,它們必須依附於一類特別的物件型別——函式式介面。Lambda表示式就是一個函式式介面的例項。這就是Lambda表示式和函式式介面的關係。也就是說,只要一個物件是函式式介面的例項,那麼該物件就可以用Lambda表示式來表示。

Java內建四大函式式介面:

Java8新特性:方法引用

當要傳遞給Lambda體的操作,已經有實現的方法了,可以使用方法引用!

l 方法引用可以看做是Lambda表示式深層次的表達。換句話說,方法引用就是Lambda表示式,也就是函式式介面的一個例項,通過方法的名字來指向一個方法,可以認為是Lambda表示式的一個語法糖。

要求:*實現介面的抽象方法的引數列表和返回值型別,必須與方法引用的方法的引數列表和返回值型別保持一致!*

格式:使用操作符 “::” 將類(或物件) 與 方法名分隔開來。

如下三種主要使用情況:

  1. 物件::例項方法名
  2. 類::靜態方法名
  3. 類::例項方法名
Consumer<String> con1 = x -> System.out.println(x);
Consumer<String> con = System.out::println;
con.accept("nihao");
BiPredicate<String, String> stringBiPredicate = (x, y) -> x.equals(y);
BiPredicate<String, String> biPredicate = String::equals;
boolean test = biPredicate.test("a", "b");

當函式式介面方法的第一個引數是需要引用方法的呼叫者,並且第二個引數是需要引用方法的引數(或無引數)時:ClassName::methodName。

構造器引用:

格式: ClassName::new

與函式式介面相結合,自動與函式式介面中方法相容。可以把構造器引用賦值給定義的方法,要求構造器引數列表要與介面中抽象方法的引數列表一致!且方法的返回值即為構造器對應類的物件。

Java8新特性:Stream API

Stream 是 Java8 中處理集合的關鍵抽象概念,它可以指定你希望對集合進行的操作,可以執行非常複雜的查詢、過濾和對映資料等操作。使用Stream API 對集合資料進行操作,就類似於使用 SQL 執行的資料庫查詢。也可以使用 Stream API 來並行執行操作。簡言之,Stream API 提供了一種高效且易於使用的處理資料的方式。

Stream 和 Collection 集合的區別:Collection 是一種靜態的記憶體資料結構,而Stream是有關計算的。前者是主要面向記憶體,儲存在記憶體中,後者主要是面向CPU,通過CPU實現計算。

Stream到底是什麼呢?是資料渠道,用於操作資料來源(集合、陣列等)所生成的元素序列。“集合講的是資料,Stream講的是計算!”

注意:

①Stream 自己不會儲存元素。

②Stream 不會改變源物件。相反,他們會返回一個持有結果的新Stream。

③Stream 操作是延遲執行的。這意味著他們會等到需要結果的時候才執行。

Stream操作三個步驟:

1資料來源建立一個Stream

2一個操作中間鏈對資料進行處理

3終止操作,一旦進行終止操作,就會執行中間鏈,並返回結果,之後不再使用。

Stream的建立:

1.Collection介面的兩個方法:

default Stream stream() : 返回一個順序流

default Stream parallelStream() : 返回一個並行流

2.Arrays 的靜態方法 stream() 可以獲取陣列流:

static Stream stream(T[] array): 返回一個流

過載形式,能夠處理對應基本型別的陣列:

public static IntStream stream(int[] array)

public static LongStream stream(long[] array)

public static DoubleStream stream(double[] array)

3.可以呼叫Stream類靜態方法 of(), 通過顯示值建立一個流。它可以接收任意數量的引數。

public static Stream of(T... values) : 返回一個流

4.無限流的建立:

可以使用靜態方法 Stream.iterate() 和 Stream.generate(), 建立無限流。

迭代

public static Stream iterate(final T seed, final UnaryOperator f)

生成

public static Stream generate(Supplier s)

Stream的中間操作:

多箇中間操作可以連線起來形成一個流水線,除非流水線上觸發終止操作,否則中間操作不會執行任何的處理!而在終止操作時一次性全部處理,稱為“惰性求值”。

Stream的終止操作:
終端操作會從流的流水線生成結果。其結果可以是任何不是流的值,例如:List、Integer,甚至是 void 。流進行了終止操作後,不能再次使用。

Java8新特性:Optional類

Optional 類(java.util.Optional) 是一個容器類,它可以儲存型別T的值,代表這個值存在。或者僅僅儲存null,表示這個值不存在。原來用 null 表示一個值不存在,現在 Optional 可以更好的表達這個概念。並且可以避免空指標異常。Optional類的Javadoc描述如下:這是一個可以為null的容器物件。如果值存在則isPresent()方法會返回true,呼叫get()方法會返回該物件。

Optional提供很多有用的方法,這樣我們就不用顯式進行空值檢測。 l 建立Optional類物件的方法:

Optional.of(T t) : 建立一個 Optional 例項,t必須非空;

Optional.empty() : 建立一個空的 Optional 例項

Optional.ofNullable(T t):t可以為null,判斷Optional容器中是否包含物件:

boolean isPresent() : 判斷是否包含物件

void ifPresent(Consumer<? super T> consumer):如果有值,就執行Consumer介面的實現程式碼,並且該值會作為引數傳給它。

獲取Optional容器的物件:

T get(): 如果呼叫物件包含值,返回該值,否則拋異常

T orElse(T other) :如果有值則將其返回,否則返回指定的other物件。

T orElseGet(Supplier<? extends T> other) :如果有值則將其返回,否則返回由Supplier介面實現提供的物件。

T orElseThrow(Supplier<? extends X> exceptionSupplier) :如果有值則將其返回,否則丟擲由Supplier介面實現提供的異常。

Java8其他新特性

(1) 時間和日期API

(2) 介面的靜態方法和預設方法

(3) 註解:重複註解、型別註解

(4) js編譯工具

(5) Jdk:並行流、反射獲取形參名、NIO改進