總結: JDK 包含了 JRE,同時還包含了編譯 java 源碼的編譯器 javac,還包含了不少 java 程序調試和分析的工具。簡單來講:若是你須要運行 java 程序,只需安裝 JRE 就能夠了,若是你須要編寫 java 程序,就須要安裝 JDK。java
Java中數據類型分爲基本數據類型和引用數據類型2種web
基本類型和引用類型比較,== 的做用效果是不一樣的。算法
基本類型:比較的是值是否相同設計模式
引用類型:比較的是引用是否相同數組
int x = 10;
int y = 10;
String a = "panda";
String b = "panda";
String c = new String("panda");
// true 基本類型比較值是否相同
System.out.println(x == y);
// true 引用類型比較引用是否相同,這裏引用相同
System.out.println(a == b);
// false 引用不一樣
System.out.println(a == c);
// true 引用不一樣,String重寫了equals,使其用值比較
System.out.println(a.equals(c));
複製代碼
12345678910111213緩存
equals 本質上就是 ==,Object類中定義的 equals 方法以下安全
public boolean equals(Object obj) {
return (this == obj);
}
123
複製代碼
總結:== 對於基本類型比較的是值,對於引用類型比較的是引用;而 equals 默認狀況下是引用比較,只是不少類重寫了 equals 方法,好比 String、Integer 等把它變成了值比較,因此通常狀況下 equals 比較的是值是否相等。服務器
不正確,兩個對象的 hashCode() 相同,equals() 不必定 true。好比在 map 中,hashCode() 相等,只能說明這兩個鍵值對的哈希值相同,不表明這兩個鍵值對相等。markdown
String str1 = "通話";
String str2 = "重地";
// str1: 1179395 | str2: 1179395
System.out.println(String.format("str1: %d | str2: %d",str1.hashCode(),str2.hashCode()));
// false
System.out.println(str1.equals(str2));
123456
複製代碼
String 是字符串常量,每次操做都會生產新的對象,適用於少許字符串操做的狀況;StringBuffer、StringBuilder 是字符串變量,StringBuffer 是線程安全的,而 StringBuilder 是非線程安全的,但 StringBuilder 的性能卻高於 StringBuffer,因此在單線程環境下推薦使用 StringBuilder,多線程環境下推薦使用 StringBuffer。數據結構
不同,由於內存的分配方式不同。String str=「donkey」,java 虛擬機會將其分配到常量池中;而 String str=new String(「donkey」) 則會被分到堆內存中。
使用 StringBuilder 或者 stringBuffer 的 reverse() 方法
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append("abcdefg");
System.out.println(stringBuffer.reverse()); // gfedcba
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("abcdefg");
System.out.println(stringBuilder.reverse()); // gfedcba
123456
複製代碼
String str = " app le ";
// indexOf(): 返回指定字符的索引
System.out.println(str.indexOf("a")); // 1
// charAt(): 返回指定索引處的字符
System.out.println(str.charAt(5)); // l
// replace(): 字符串替換
System.out.println(str.replace("pp", "cc")); // " acc le "
// trim(): 去除字符串兩端空白
System.out.println(str.trim()); // "app le"
// split(): 分割字符串,返回一個分割後的字符串數組
String[] arr = str.split(" ");
// getBytes(): 返回字符串的 byte 類型數組
byte[] bytes = str.getBytes();
// length(): 返回字符串長度
System.out.println(str.length()); // 8
// toLowerCase(): 將字符串轉成小寫字母
System.out.println(str.toLowerCase()); // " app le "
// toUpperCase(): 將字符串轉成大寫字符
System.out.println(str.toUpperCase()); // " APP LE "
// substring(): 截取字符串
System.out.println(str.substring(2)); // "pp le "
// equals(): 字符串比較
System.out.println("apple".equals(str)); // false
1234567891011121314151617181920212223
複製代碼
擁有抽象方法(指沒有方法體的方法,同時抽象方法還必須使用關鍵字abstract 作修飾)的類就是抽象類,抽象類要使用 abstract 關鍵字聲明。
不須要,抽象類不必定非要有抽象方法,以下代碼能夠正常運行
public abstract class elephant {
String str = "apple";
public void test01(){
System.out.println("aaaa");
}
}
123456
複製代碼
不能,定義抽象類就是讓其餘類繼承的,若是定義爲 final 該類就不能被繼承,這樣彼此就會產生矛盾,因此 final 不能修飾抽象類。
接口能夠理解爲一種特殊的類,裏面所有是由全局常量和公共的抽象方法所組成。接口是解決Java沒法使用多繼承的一種手段,可是接口在實際中更多的做用是制定標準。
字節流和字符流的區別:字節流按 8 位傳輸,以字節爲單位輸入輸出數據;字符流按 16 位傳輸,以字符爲單位輸入輸出數據。
// Files.exists():檢測文件路徑是否存在
Path path1 = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test");
System.out.println(Files.exists(path1, new LinkOption[]{LinkOption.NOFOLLOW_LINKS})); // true
// Files.createFile():建立文件
Path path2 = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\a.txt");
try {
Path newFilw = Files.createFile(path2);
} catch (FileAlreadyExistsException e){
System.out.println("exists");
} catch (IOException e) {
System.out.println("other wrong");
}
// Files.createDirectory():建立文件夾
Path path3 = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\newDirectory");
try {
Path newDirectory = Files.createDirectory(path3);
} catch (FileAlreadyExistsException e) {
System.out.println("exists");
} catch (IOException e){
System.out.println("other wrong");
}
// Files.delete():刪除一個文件或目錄
try {
Files.delete(path2);
} catch (IOException e) {
e.printStackTrace();
}
// Files.copy():複製文件
Path source = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\b.txt");
Path target = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\newb.txt");
try {
Files.copy(source,target);
} catch (FileAlreadyExistsException e) {
System.out.println("targetFile already exists");
} catch (IOException e){
System.out.println("other wrong");
}
// Files.move():移動文件
Path source1 = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\b.txt");
Path target1 = Paths.get("C:\\Users\\e-yangfangchao\\Desktop\\test\\newDirectory\\a.txt");
try {
Files.move(source1,target1);
} catch (FileAlreadyExistsException e) {
System.out.println("targetFile1 already exists");
} catch (IOException e){
System.out.println("other wrong");
}
// Files.size():查看文件個數
// Files.read():讀取文件
// Files.write():寫入文件
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950
複製代碼
在 Map 中作插入、刪除和定位元素這類操做,HashMap 是最好的選擇。假如你須要對一個有序的 key 集合進行遍歷,TreeMap 是更好的選擇。
HashMap概述: HashMap 是基於哈希表的Map接口的非同步實現。此實現提供全部可選的映射操做,並容許使用 null 值和 null 鍵。此類不保證映射的順序,特別是它不保證該順序恆久不變。
HashMap的數據結構: HashMap 其實是一個「鏈表散列」的數據結構,即數組和鏈表的結合體。
當咱們往 HashMap 中 put 元素時,首先根據 key 的 hashcode 從新計算 hash 值,根據 hash 值獲得這個元素在數組中的位置(下標),若是該數組在該位置上已經存放了其餘元素,那麼在這個位置上的元素將以鏈表的形式存放,新加入的放在鏈頭,最早加入的放入鏈尾。若是數組中該位置沒有元素,就直接將該元素放到數組的該位置上。
Jdk1.8 中對 HashMap 的實現作了優化,當鏈表中的節點數據超過八個以後,該鏈表會轉爲紅黑樹來提升查詢效率,從原來的 O(n) 到 O(logn)。
HashSet 實現 Set 接口,由哈希表(其實是一個 HashMap 實例)支持。它不保證 set 的迭代順序;特別是它不保證該順序恆久不變;此類容許使用 null 元素;HashSet 中不容許有重複元素。
HashSet 是基於 HashMap 實現的,HashSet 中的元素都存放在 HashMap 的 key 上面,而 value 中的值都是統一的一個 private static final Object PRESENT = new Object()
。HashSet跟HashMap同樣,都是一個存放鏈表的數組。
ArrayList 底層基於動態數組,隨機訪問元素效率高,向集合尾部添加元素效率高,刪除或者在其餘位置添加元素效率低(須要移動數組);LinkedList 基於鏈表的動態數組,數據添加和刪除效率高,只須要改變指針指向便可,可是訪問數據的平均效率低,須要對鏈表進行遍歷。
// List 轉 數組
String[] strArr = {"apple","pear","banana","peach"};
List<String> list = Arrays.asList(strArr);
// 數組 轉 List
String[] arr = (String[]) list.toArray();
12345
複製代碼
Queue 中 remove() 和 poll() 都是用來從隊列頭部刪除一個元素,在隊列元素爲空的狀況下,remove() 方法會拋出 NoSuchElementException 異常,poll() 方法只會返回 null。
迭代器是一種設計模式,它是一個對象,它能夠遍歷並選擇序列中的對象,而開發人員不須要了解該序列的底層結構。迭代器一般被稱爲「輕量級」對象,由於建立它的代價小。
Iterator 功能比較簡單,而且只能單向移動
使用方法 iterator() 要求容器返回一個 Iterator。第一次調用 Iterator 的 next() 方法時,它返回序列的第一個元素。注意:iterator() 方法是 java.lang.Iterable 接口,被 Collection 繼承。
使用 hasNext() 檢查序列中是否還有元素
使用 next() 得到序列中的下一個元素
使用 remove() 將迭代器新返回的元素刪除
Iterator 是 Java 迭代器最簡單的實現,爲 List 設計的 ListIterator 具備更多的功能,它能夠從兩個方向遍歷 List,也能夠從 List 中插入和刪除元素。
public static void main(String[] args) {
// List
ArrayList<String> list = new ArrayList<>();
list.add("apple");
list.add("pear");
list.add("banana");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
String s = iterator.next();
if ("apple".equals(s)){
iterator.remove();
}
}
list.forEach(item -> System.out.println(item));
// Map<key,value>
Map<String,String> map=new HashMap<>();
map.put("pig","豬");
map.put("cat","貓");
map.put("dog","狗");
Iterator<String> iterator1 = map.keySet().iterator();
Iterator<String> iterator2 = map.values().iterator();
while (iterator1.hasNext()){
System.out.println(iterator1.next());
}
while (iterator2.hasNext()){
System.out.println(iterator2.next());
}
}
複製代碼
1234567891011121314151617181920212223242526272829
普通解釋:
專業術語:
進程是程序運行和資源分配的基本單位,一個程序至少有一個進程,一個進程至少有一個線程。進程在執行過程當中擁有獨立的內存單元,而多個線程共享內存資源,減小切換次數,從而效率更高。線程是進程的一個實體,是 cpu 調度和分派的基本單位,是比程序更小的能獨立運行的基本單位。同一進程中的多個線程之間能夠併發執行。
守護線程(即 daemon thread),是個服務線程,準確地來講就是服務其餘的線程。它可以自我結束。若是 JVM 中沒有一個正在運行的非守護線程,這個時候,JVM 會退出。JVM 中的垃圾回收線程就是典型的守護線程,若是說沒有守護線程,JVM 就永遠不會退出了
① 繼承 Thread 類建立線程
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("run task");
}
}
public class Demo {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
123456789101112
複製代碼
② 實現 Runnable 接口建立線程
public class MyThread implements Runnable{
@Override
public void run() {
System.out.println("run task");
}
}
public class Demo {
public static void main(String[] args) {
MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
}
}
12345678910111213
複製代碼
③ 經過 Callable 和 Future 建立線程
public class MyThread implements Callable {
@Override
public Object call() {
System.out.println("run!");
return "run task success";
}
}
public class Demo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyThread myThread = new MyThread();
FutureTask futureTask = new FutureTask<String>(myThread);
Thread thread = new Thread(futureTask);
thread.start();
// 得到子線程執行結束後的返回值
System.out.println(futureTask.get()); // run task success
}
}
1234567891011121314151617
複製代碼
線程一般都有五種狀態,建立、就緒、運行、阻塞和死亡。
若是線程調用了對象的 wait() 方法,那麼線程便會處於該對象的等待池中,等待池中的線程不會去競爭該對象的鎖。
當有線程調用了對象的 notifyAll() 方法(喚醒全部 wait 線程)或 notify() 方法(只隨機喚醒一個 wait 線程),被喚醒的的線程便會進入該對象的鎖池中,鎖池中的線程會去競爭該對象鎖。也就是說,調用了 notify 後只有一個線程會由等待池進入鎖池,而 notifyAll 會將該對象等待池內的全部線程移動到鎖池中,等待鎖競爭。
優先級高的線程競爭到對象鎖的機率大,倘若某線程沒有競爭到該對象鎖,它還會留在鎖池中,惟有線程再次調用 wait() 方法,它纔會從新回到等待池中。而競爭到對象鎖的線程則繼續往下執行,直到執行完了 synchronized 代碼塊,它會釋放掉該對象鎖,這時鎖池中的線程會繼續競爭該對象鎖。
每一個線程都是經過某個特定 Thread 對象所對應的方法 run() 來完成其操做的,方法 run() 稱爲線程體。經過調用 Thread 類的 start() 方法來啓動一個線程。
線程池有5種狀態:Running、ShutDown、Stop、Tidying、Terminated。
線程池各個狀態切換框架圖:
接收的參數不同
submit(Runnable task) submit(Runnable task,T result) submit(Callable task) execute(Runnable command) 1234
submit() 有返回值,而 execute() 沒有。用到返回值的例子,好比說我有不少個作 validation 的task,我但願全部的 task 執行完,而後每一個 task 告訴我它的執行結果,是成功仍是失敗,若是是失敗,緣由是什麼。
submit()方便 Exception 處理,若是你在你的 task 裏會拋出 checked 或者 unchecked exception,而你又但願外面的調用者可以感知這些 exception 並作出及時的處理,那麼就須要用到submit,經過捕獲 Future.get 拋出的異常。
線程安全在三個方面體現:
在 Java 中,鎖共有 4 種狀態,級別從低到高依次爲:無狀態鎖,偏向鎖,輕量級鎖和重量級鎖狀態,這幾個狀態會隨着競爭狀況逐漸升級。鎖能夠升級但不能降級。
鎖升級的圖示過程:
死鎖是指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。是操做系統層面的一個錯誤,是進程死鎖的簡稱,最先在 1965 年由 Dijkstra 在研究銀行家算法時提出的,它是計算機操做系統乃至整個併發程序設計領域最難處理的問題之一。
死鎖的四個必要條件:
這四個條件是死鎖的必要條件,只要系統發生死鎖,這些條件必然成立,而只要上述條件之 一不知足,就不會發生死鎖。
理解了死鎖的緣由,尤爲是產生死鎖的四個必要條件,就能夠最大可能地避免、預防和 解除死鎖。因此,在系統設計、進程調度等方面注意如何不讓這四個必要條件成立,如何確 定資源的合理分配算法,避免進程永久佔據系統資源。此外,也要防止進程在處於等待狀態的狀況下佔用資源。所以,對資源的分配要給予合理的規劃。
線程局部變量是侷限於線程內部的變量,屬於線程自身全部,不在多個線程間共享。Java 提供ThreadLocal 類來支持線程局部變量,是一種實現線程安全的方式。可是在管理環境下(如 web 服務器)使用線程局部變量的時候要特別當心,在這種狀況下,工做線程的生命週期比任何應用變量的生命週期都要長。任何線程局部變量一旦在工做完成後沒有釋放,Java 應用就存在內存泄露的風險。
synchronized 能夠保證方法或者代碼塊在運行時,同一時刻只有一個方法能夠進入到臨界區,同時它還能夠保證共享變量的內存可見性。
Java 中每個對象均可以做爲鎖,這是 synchronized 實現同步的基礎:
synchronized 是和 if、else、for、while 同樣的關鍵字,ReentrantLock 是類,這是兩者的本質區別。既然 ReentrantLock 是類,那麼它就提供了比 synchronized 更多更靈活的特性,能夠被繼承、能夠有方法、能夠有各類各樣的類變量,ReentrantLock 比 synchronized 的擴展性體如今幾點上:
另外,兩者的鎖機制其實也是不同的:ReentrantLock 底層調用的是 Unsafe 的 park 方法加鎖,synchronized 操做的應該是對象頭中 mark word。
Atomic 包中的類基本的特性就是在多線程環境下,當有多個線程同時對單個(包括基本類型及引用類型)變量進行操做時,具備排他性,即當多個線程同時對該變量的值進行更新時,僅有一個線程能成功,而未成功的線程能夠向自旋鎖同樣,繼續嘗試,一直等到執行成功。
Atomic 系列的類中的核心方法都會調用 unsafe 類中的幾個本地方法。咱們須要先知道一個東西就是Unsafe 類,全名爲:sun.misc.Unsafe,這個類包含了大量的對 C 代碼的操做,包括不少直接內存分配以及原子操做的調用,而它之因此標記爲非安全的,是告訴你這個裏面大量的方法調用都會存在安全隱患,須要當心使用,不然會致使嚴重的後果,例如在經過 unsafe 分配內存的時候,若是本身指定某些區域可能會致使一些相似 C++ 同樣的指針越界到其餘進程的問題。