黑馬程序員Java零基礎(chǔ)視頻教程_下部(Java入門,含斯坦福大學(xué)練習(xí)題+力扣算



Map加入集合也是無序的
put方法的作用添加和覆蓋
- 如果在添加數(shù)據(jù)的時候,鍵值不存在,則會直接添加到Map集合中,此時返回值是null。
- 如果鍵值存在,則直接覆蓋,但是會返回原有的鍵值的值。
remove方法不能單獨(dú)刪除value
Map集合第一種遍歷方式(健找值):
將Map集合中的key單獨(dú)拿出來,放到set集合中。利用的是keySet方法,返回值就是一個Set集合。然后再用Iterator,增強(qiáng)for或者lambda來遍歷就行。然后利用get方法獲取遍歷到的key中value的值。
Map集合第二種遍歷方式(鍵值對):

這里涉及到泛型嵌套,既泛型中再去指定泛型的泛型。
利用entrySet方法,將Map集合保存為一個Set集合,但是這次Set和的泛型指定為Map.Entry類型。
再用Iterator、增強(qiáng)for、lambda表達(dá)式遍歷,利用getKey與getValue方法分別得到鍵值對的值。
lambda表達(dá)式的底層其實是增強(qiáng)for循環(huán),


HashMap的底層原理與HashSet一樣,也是hash表的形式(數(shù)組+鏈表+紅黑樹)。
但是HashMap只比較健的hash值,只保證健的唯一

LinkedHashMap



hashMap默認(rèn)的空參構(gòu)造方法,只是加載加載因子用的
HashMap剛創(chuàng)建的時候,數(shù)組啥的是不存在的,只有當(dāng)添加元素的時候才會建立 。


觸發(fā)擴(kuò)容機(jī)制之后,會將原有的數(shù)據(jù)移動到或者復(fù)制到新的數(shù)組中
要存入的元素,先計算出hash值,再與數(shù)組長度進(jìn)行與運(yùn)算,就是當(dāng)前元素應(yīng)存入的位置,如果當(dāng)前位置沒有元素,就是數(shù)組當(dāng)前位置為null,則直接存入數(shù)組中。

當(dāng)添加的元素hash值與當(dāng)前數(shù)組中的元素相同時


當(dāng)hash值相同,并且key值也相同時,會執(zhí)行覆蓋
hash值相同,key值不同,還是回掛在鏈表的下面

...:表示可變參數(shù);底層是一個數(shù)組
且一個方法的形參中只能由一個可變參數(shù);
如果參數(shù)列表中除了可變參數(shù),還有其他類型的參數(shù)。則可變參數(shù)要寫在最后,不然方法調(diào)用時識別不出,其他類型變量的值。

Collections

addAll返回值是collection只能給單列集合批量添加

// copy方法不能直接創(chuàng)建集合進(jìn)行復(fù)制,而是先將新建的集合批量加入,并指定長度之后才能復(fù)制 // 否則會下標(biāo)越界
shift+F6:批量改名



雙列集合不能直接使用stream流,要先通過entrySet等轉(zhuǎn)為單列集合。


對stream流中數(shù)據(jù)進(jìn)行修改,只會影響流中的數(shù)據(jù),對原來集合或者數(shù)組中的數(shù)據(jù)沒有影響。
concat方法:
當(dāng)合并兩個流時,如果兩個流的數(shù)據(jù)類型不一致,則會選擇兩個流數(shù)據(jù)類型共同的父類作為流輸出。


收集器如果是Collector.toList()則,集合里面有重復(fù)數(shù)據(jù)也會保留;如果是Collector.toSet則,集合里面不會有重復(fù)的數(shù)據(jù)

如果將集合收集到map里面,需要指定鍵的規(guī)則和值的規(guī)則,而且鍵要保證不能重復(fù)。
Lambda表達(dá)式的方式書寫鍵值規(guī)則:

new Function(鍵的規(guī)則(l流當(dāng)中數(shù)據(jù)的類型,鍵的類型),值的規(guī)則(l流當(dāng)中數(shù)據(jù)的類型,值的類型))
將集合中的數(shù)據(jù)封裝到對象中,組成對象集合。
stream().map()中,map是用來進(jìn)行類型轉(zhuǎn)換的。


異常:程序中可能出現(xiàn)的問題


編譯時異常:java文件通過javac進(jìn)行編譯時出現(xiàn)的異常
作用在于提醒程序員檢查本地信息,如日期解析異常。
與運(yùn)行時異常:字節(jié)碼運(yùn)行時出現(xiàn)的異常,代碼出錯出現(xiàn)的異常


invoke:調(diào)用
異常的作用:
1.用來查詢bug的關(guān)鍵參考信息;
2.作為方法內(nèi)部的一種特殊返回值,以便通知調(diào)用者底層的執(zhí)行情況。

此時,調(diào)用者可以選擇自己在后臺進(jìn)行處理,或者打印到控制臺。
如果程序出現(xiàn)異常,那么虛擬機(jī)就會創(chuàng)建一個相應(yīng)異常的對象,如果此時使用try-catch去捕獲這個異常,則會執(zhí)行catch中的代碼。當(dāng)catch里面的代碼執(zhí)行完畢,就繼續(xù)執(zhí)行try-catch下面其他的代碼。
如果要捕獲多個異常,那么他們共同的父類也要捕獲的話,要寫在子異常的下面。
如果try中的一行代碼出了問題,那么這行代碼以下的代碼都不會執(zhí)行。直接進(jìn)行捕獲或者JVM去創(chuàng)建異常對象


System.err.println(要打印的語句);
將要打印的語句以紅色字體打印在控制臺

throws:寫在方法上
throw new:寫在方法里,并且結(jié)束方法運(yùn)行

運(yùn)行時異常用throws在方法上進(jìn)行拋出時,可以省略不寫。
拋出:方法將產(chǎn)生的異常傳給調(diào)用者。
捕獲:方法調(diào)用者將方法拋出的異常拿到。
自定義異常類,包括一個無參的構(gòu)造方法,一個有參的message提示方法
其中,如果是運(yùn)行時異常,要繼承RuntimeException
編譯時異常要繼承Exception
這樣,在catch里面捕獲就行
public class 異常類名{
public 異常類構(gòu)造方法(){
}
public 異常類構(gòu)造方法(參數(shù)列表){
}
}

IO流

程序在讀寫IO流中的數(shù)據(jù)

輸出流:程序->文件
輸入流:文件->程序



文件輸出流,就是把程序中的數(shù)據(jù)寫入到文件
步驟:
1.創(chuàng)建文件輸出流對象
既通過 FileOutputStream fos = new FileOutputStream("指定文件存放的路徑");建立程序與文件的傳輸通道。
2.寫數(shù)據(jù)
寫入數(shù)據(jù)就相當(dāng)于數(shù)據(jù)在這條通道上進(jìn)行行駛,再到終點(diǎn)。
3.釋放資源
就是將這條通道關(guān)閉。
FileOutputStream
創(chuàng)建輸出流對象,參數(shù)可以是用字符串表示的路徑,或者new一個File對象再把路徑給File對象也可以。
如果指定路徑中的文件不存在,則會創(chuàng)建新文件,但是要保證這個文件所在的文件夾存在,不像Linux一樣可以多級創(chuàng)建。
如果文件已經(jīng)存在,則會覆蓋文件內(nèi)容。


FileOutputStream底層,在創(chuàng)建對象的時候會new一個File對象和一個boolean的append參數(shù),append默認(rèn)時false(也就是默認(rèn)是覆蓋的),改為true之后就不會覆蓋,而是續(xù)寫了。
這樣再次執(zhí)行程序?qū)懭霑r,文件中的內(nèi)容就不會被覆蓋了。
FileInputStream
1.創(chuàng)建字節(jié)輸入流對象
FileInputStream fis = new FileInputStream("文件路徑");
2.讀取數(shù)據(jù)
int i = fis.read();
返回的是一個int型的變量,可以強(qiáng)轉(zhuǎn)成char
read方法更像是有個指針,一次讀一個
3.釋放資源
fis.close();





GBK字母存儲

GBK漢字的存儲

一個字節(jié)不夠,三個字節(jié)太多,所以用兩個字節(jié)就可以,可以標(biāo)識65535個漢字;為了與英文區(qū)分開。


UTF-8:可變字符長度(1-4字節(jié)),其中中文用三個字節(jié)表示;ASCII用一個字節(jié)。是unicode字符集的編碼方式


解碼時,是先將前面的固定格式去除,再去拼接為漢字就是兩個字節(jié)的二進(jìn)制位。再去查詢Unicode編碼表。

如果一開始用Unicode去編碼,然后再用GBK去解碼,此時解碼時查詢的是GBK編碼表,GBK用兩個字節(jié)表示一個漢字,而Unicode用三個字節(jié),所以讀完兩個字節(jié)之后剩下的一個字節(jié)就不讀了,所以一個漢字會出現(xiàn)讀不全的問題。
字符流


除了對象名不同,其他跟字節(jié)流差不多


FileReader fr = new FileReader("D:\\My Work National\\IJ_IDEA\\Day05\\a.txt"); int ch = 0; while ((ch = fr.read()) != -1){ System.out.print((char)ch); } fr.close();
java中的換行其實是兩個轉(zhuǎn)義字符\r\n


字符流底層原理

字符流有緩沖區(qū),字節(jié)流沒有

緩沖流

緩沖流只是一個中間態(tài)的東西,還是要關(guān)聯(lián)四大基本流來進(jìn)行操作
釋放資源的時候,緩沖流在底層會先釋放基本流資源。不需要自己關(guān)。

基本流會先讀取數(shù)據(jù),放入緩沖區(qū)當(dāng)中;
變量b會去讀緩沖區(qū)中的數(shù)據(jù),再把讀到的數(shù)據(jù)放入當(dāng),輸出流的緩沖區(qū)當(dāng)中。

newLine方法底層會去判斷使用的操作系統(tǒng)是什么樣的,然后輸出相應(yīng)的換行。

轉(zhuǎn)換流
字節(jié)流轉(zhuǎn)換為字符流
通常用于字節(jié)流轉(zhuǎn)字符流之后,用字符流里面的方法。
多線程
提高程序的運(yùn)行效率。
應(yīng)用場景:聊天軟件,拷貝、遷移大文件,加載大量資源文件,后臺服務(wù)程序。

并發(fā):同一時刻,多個指令在單個CPU上交替執(zhí)行。
并行:同一時刻,多個指令在多個CPU上同時進(jìn)行。
并發(fā)和并行

線程的啟動,new對象,用對象的引用去調(diào)用start()方法
繼承Thread父類
public class MyTread extends Thread{ @Override public void run() { for (int i = 0; i < 100; i++) { System.out.println(getName() + "Hello World"); } } } main方法中 /*MyTread m1 = new MyTread(); m1.setName("線程1"); m1.start(); MyTread m2 = new MyTread(); m2.setName("線程2"); m2.start();*/
實現(xiàn)Runable接口
public class MyRunable implements Runnable{ @Override public void run() { for (int i = 0; i < 100; i++) { //Thread.currentThread.getName 獲取當(dāng)前線程的線程名 System.out.println(Thread.currentThread().getName()+"你好!"); } } } //main方法中 //創(chuàng)建對象 MyRunable myRunable = new MyRunable(); //創(chuàng)建線程對象 Thread t1 = new Thread(myRunable,"線程1"); t1.start(); Thread t2 = new Thread(myRunable,"線程2"); t2.start();
Callable接口如果指定泛型,則call方法的返回值就是這個泛型的返回類型。如果不指定默認(rèn)是Object類型。
public class MyCallable implements Callable<Integer> { @Override public Integer call() throws Exception { int sum = 0; for (int i = 0; i < 100; i++) { sum += i; } return sum; } }


JVM虛擬機(jī)在啟動的時候,會默認(rèn)創(chuàng)建多條線程。
其中一條就是main線程,作用是調(diào)用main方法,執(zhí)行里面的代碼
關(guān)于優(yōu)先級:
不指定優(yōu)先級默認(rèn)就是5
優(yōu)先級的取值范圍:1~10,越大優(yōu)先級越高
守護(hù)線程
當(dāng)把一個線程定義為守護(hù)線程之后,當(dāng)其他非守護(hù)線程結(jié)束之后守護(hù)線程也會陸續(xù)結(jié)束,但不是立馬,因為JVM跟線程之間也有通信時間。
設(shè)置一個程序為守護(hù)進(jìn)程:
Thread t1 = new Thread(程序,設(shè)置程序名);
t1.setDaemon(true);
所謂守護(hù) 線程,是指在程序運(yùn)行的時候在后臺提供一種通用服務(wù)的線程,比如垃圾回收線程就是一個很稱職的守護(hù)者,并且這種線程并不屬于程序中不可或缺的部分。 因此,當(dāng)所有的非守護(hù)線程結(jié)束時,程序也就終止了,同時會殺死進(jìn)程中的所有守護(hù)線程。
如果其他線程的優(yōu)先級太小,會導(dǎo)致設(shè)置的守護(hù)進(jìn)程先執(zhí)行完了,而其他非守護(hù)進(jìn)程還在執(zhí)行。
線程的生命周期

線程在就緒狀態(tài),會不斷地跟其他線程搶奪執(zhí)行權(quán),當(dāng)搶奪到之后,程序進(jìn)入運(yùn)行狀態(tài),期間如果被sleep,則會進(jìn)入阻塞狀態(tài),直到sleep的時間到了,會進(jìn)入就緒繼續(xù)上面的步驟,直到run方法中的代碼執(zhí)行完。

鎖對象是唯一的,什么都行,Object的也行,但是必須是唯一的
唯一:一把鎖對應(yīng)一道門
public class PayTicket implements Runnable{ static private int ticket = 1; static Object o = new Object(); @Override public void run() { while (true){ //這個鎖一般定義為當(dāng)前類的字節(jié)碼文件。 synchronized (PayTicket.class) { if (ticket <= 100) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "正在賣第" + ticket + "張票"); } else { break; } ticket++; } } } }

線程同步四種方式:
- 一、通過Object的wait和notify
- 二、通過Condition的awiat和signal
- 三、通過一個阻塞隊列
- 四、通過兩個阻塞隊列
StringBuffer和StringBuilder的區(qū)別:
StringBuffer是線程安全的,因為每個方法都加了sychronized同步鎖。
如果程序是單線程的,不需要考慮數(shù)據(jù)安全性問題,則可以用StringBuilder;
如果是多線程的,需要考慮數(shù)據(jù)的安全,則可以用StringBuffer。

Lock鎖

public void run() { //synchronized (PayTicketWithThread.class){ while (true) { lock.lock(); if (ticket == 100) { break; } else { ticket++; System.out.println(Thread.currentThread().getName() + "正在賣第" + ticket + "張票"); } //如果unlock釋放鎖加在這里,則當(dāng)ticket==100成立時,直接break跳出程序,沒有執(zhí)行釋放鎖的操作 lock.unlock(); } //} }



public class Disk { /** * * 用于控制生產(chǎn)者和消費(fèi)者的執(zhí)行 * * */ //是否有面條;有:1;沒有:0;默認(rèn):0 public static int foodFlag = 0; //面條總數(shù) public static int count = 10; //鎖對象 public static Object lock = new Object(); }
public class Foodie extends Thread{ @Override public void run() { //先寫循環(huán) while(true){ synchronized (Disk.lock){ if (Disk.count == 0){ break; }else { //判斷是否有面條,0表示沒有則去等待 if (Disk.foodFlag == 0){ try { Disk.lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { //面條-1 Disk.count--; System.out.println("還有"+Disk.count+"碗"); //吃完之后喚醒廚師 Disk.lock.notify(); //修改面條改為沒有0 Disk.foodFlag = 0; } } } } } }
public class Cook extends Thread{ @Override public void run() { while (true){ synchronized (Disk.lock){ if (Disk.count == 0){ break; }else { if (Disk.foodFlag == 1){ try { //如果桌上有面條,則lock要等待 Disk.lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else { //如果沒有,則生產(chǎn)一碗面條 Disk.foodFlag = 1; //喚醒消費(fèi)者去吃 Disk.lock.notify(); } } } } } }
Cook c = new Cook(); Foodie f = new Foodie(); c.setName("廚師"); f.setName("吃貨"); c.start(); f.start();

線程池

將跟連wifi一樣,指定最大連接數(shù)


//獲取線程池對象
ExcutorService pool = Excutors.newCachedThreadPool(可以指定線程的數(shù)量);
//提交任務(wù)
pool.submit(new 類名);
//關(guān)閉資源
pool.shutdown

這時,cpu會創(chuàng)建臨時線程處理任務(wù)7和任務(wù)8

此時任務(wù)10會拒絕服務(wù)

主要是第一個拒絕策略。
