JAVA高階程式設計
Java高階程式設計
目錄- 程式、程序和執行緒的概念
- Thread類和執行緒的特點
- 建立執行緒的方法
- 執行緒的生命週期
- 執行緒的同步機制
- 執行緒的通訊
- 時間和日期API
- Comparable和Comparator實現元素的比較
- Java.lang.System類的使用
- 列舉類的使用
- 註解(Annotation)的使用
- Java集合
- List實現類之:ArrayList和Vector
- List實現類之:LinkedList
- Set實現類之:HashSet
- Set實現類之:LinkedHashSet
- Set實現類之:TreeSet
- Map實現類之:HashMap
- Map實現類之:TreeMap
- Map實現類之:Hashtable和Properties
- ArrayList/LinkedList/Vector的異同,Vector和ArrayList的最大區別?
- Collections集合操作方法
- Java中的泛型
- File類的使用
- Java的IO流
- 節點流或檔案流
- 緩衝流
- 轉換流
- 物件流序列化物件
- Path、Paths和Files核心API
- 網路通訊
- 基於Socket的TCP程式設計
- UDP網路通訊
- Java反射機制
- java.lang.Class
- 反射獲取執行時類的完整結構
- Java8新特性:Lambda表示式
- Java8新特性:函式式介面
- Java8新特性:方法引用
- Java8新特性:Stream API
- Java8新特性:Optional類
- Java8其他新特性
程式、程序和執行緒的概念
程式(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
-
java.lang.System類
System類提供的public static long currentTimeMillis()用來返回當前時間與1970年1月1日0時0分0秒之間以毫秒為單位的時間差。此方法適於計算時間差。
-
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是時間標準。
其它很多方法都過時了。
- 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
- 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() 從當前物件減去幾月、幾周、幾天、幾年、幾小時
- 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到當前時間的毫秒數,即為時間戳
- 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 - 要複製的陣列元素的數量
列舉類的使用
當需要定義一組常量時,強烈建議使用列舉類。
列舉類的屬性
- Ø 列舉類物件的屬性不應允許被改動, 所以應該使用 private final 修飾
- Ø 列舉類的使用 private final 修飾的屬性應該在構造器中為其賦值
- Ø 若列舉類顯式的定義了帶引數的構造器, 則在列出列舉值時也必須對應的
傳入引數
- Ø 使用 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
其中,T,K,V不代表值,而是表示型別。這裡使用任意字母都可以。
常用T表示,是Type的縮寫。T只能是類,不能用基本資料型別填充。但可以使用包裝類填充。
-
泛型類可能有多個引數,此時應將多個引數一起放在尖括號內。比如:<E1,E2,E3>
-
泛型類的構造器如下:public GenericClass(){}。
而下面是錯誤的:public GenericClass
(){} -
例項化後,操作原來泛型位置的結構必須與指定的泛型型別一致。
-
泛型不同的引用不能相互賦值。儘管在編譯時ArrayList
和ArrayList 是兩種型別,但是,在執行時只有一個ArrayList被載入到JVM中。 -
泛型如果不指定,將被擦除,泛型對應的型別均按照Object處理,但不等價於Object。經驗:泛型要使用一路都用。要不用,一路都不要用。
-
如果泛型結構是一個介面或抽象類,則不可建立泛型類的物件。
-
jdk1.7,泛型的簡化操作:ArrayList
flist = new ArrayList<>(); -
泛型的指定中不能使用基本資料型別,可以使用包裝類替換。
-
在類/介面上宣告的泛型,在本類或本介面中即代表某種型別,可以作為非靜態屬性的型別、非靜態方法的引數型別、非靜態方法的返回值型別。但在靜態方法中不能使用類的泛型。
-
異常類不能是泛型的
-
不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];參考:ArrayList原始碼中:Object[] elementData,而非泛型引數型別陣列。
-
父類有泛型,子類可以選擇保留泛型也可以選擇指定泛型型別:
子類不保留父類的泛型:按需實現
沒有型別 擦除
具體型別
子類保留父類的泛型:泛型子類
全部保留
部分保留
結論:子類必須是“富二代”,子類除了指定或保留父類的泛型,還可以增加自己的泛型
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