1. 程式人生 > 其它 >Java學習筆記:2022年1月6日(補充)

Java學習筆記:2022年1月6日(補充)

Java學習筆記:2022年1月6日(補充)

摘要:這篇筆記主要記錄了2022年1月6日下午的筆記,主要內容為Java語言中的基礎操作,以及基礎知識點,瞭解這些後基本上就可以使用Java寫演算法了。


@

目錄
1.語句和語句塊

​ Java語言是由很多語句指令構成的,這些語句指令通常是用分號結尾,Java語言中存在的最多的就是變數宣告語句以及變數賦值語句,還有迴圈語句,這些語句總體上構成了所有的Java程式碼。

for(int i = 0; i < 10 ; i++){
	System.out.println("See you Cowboy Bebop!");
}

​ 以上的語句中就包含了變數宣告語句,賦值語句,輸出語句以及迴圈語句,我們注意到在for迴圈後邊跟著一個大括號,這個大括號裡邊的東西通常被成為迴圈體,實際上它也可以被稱為語句塊,在Java中,被大括號括起來的部分就是一個語句塊,他們可以被看做一個單獨的個體,而大括號括起來的範圍可以被稱之為作用域,任何在大括號中被宣告的變數在大括號之外或者是在自己的作用域之外都無法使用,這是Java的一個特性,這一點會在之後的Java執行時詳解中進行詳細闡述。

2.迴圈語句
1.for迴圈

​ for迴圈是最基本的迴圈語句,其使用方法為for(第一部分;第二部分;第三部分){迴圈體},在整個迴圈語句中,第一部分是最先執行的,其次是第二部分,然後是迴圈體部分,最後才是第三部分被執行。其中第一部分通常是一些變數初始化以及定義,第二部分為迴圈條件,第三部分可以寫在迴圈語句中也可以寫在迴圈體中,第三部分通常被用來當做控制迴圈的相關變數的變化區,如下:

for(int i = 0; i < 10 ; i++){
	System.out.println("See you Cowboy Bebop!");
}//第三部分通常為第一部分定義的變數的自增,而第二部分則是使用第一部分定義的變數根據第三部分的控制進而控制整個迴圈體的迴圈與否

​ 如個位所見的是第二部分通常是一個布林表示式,其結果就是真或假,當結果為真時,這個迴圈會繼續向下進行,當結果為假,這個迴圈會停止並結束。

for(int i = 0; i < 10 ; ){
	System.out.println("See you Cowboy Bebop!");
	i++;
}//上邊的程式碼和這個程式碼是等效的,第三部分實際上是最後執行的,將i++這個自增語句寫在第三部分相當於將它寫在迴圈體的末尾

​ 因此我們其實可以通過這個特性更加靈活的改變for迴圈的細節,比如我們可以不再第三部分寫標誌位自增語句,而是寫在迴圈體中的不同位置,這樣可以靈活的讓for迴圈得到原本以外的我們所需要的性質。

2.while迴圈

​ 實際上while迴圈可以理解為一個最簡單的迴圈,因為它只有一個while,while就有“當...的時候”的意思,因此它被拿來當迴圈也是很好理解的,while迴圈的用法是while(迴圈條件){迴圈體}。它的小括號中是和for迴圈中第二部分一樣的控制迴圈進行與否的布林表示式,大括號內則是迴圈體,通常while迴圈的進行也需要一個標誌位進行控制,這個標誌位的宣告充滿了靈活性,通常是根據我們的需求進行設定。

while(true){//true代表永遠為真,當while迴圈的迴圈條件為true時,這個迴圈通常沒有辦法停止
	int 1 = 0;//在迴圈體內部進行標誌位的宣告
	i++;
	if(i>10)
		break;
}

​ 如上邊程式碼所示,我們在while迴圈中需要自己書寫停止機制,當然i變數的宣告可以在迴圈體外邊也可以在迴圈體裡邊,這些就比較靈活了。但是我們需要注意的是這裡使用到了一個重要的語句break,這個是強行停止語句,通常來講while迴圈的迴圈控制語句部分如果沒有一個寫好的布林表示式,我們通常使用它來進行迴圈的停止,break會導致迴圈立即停止並跳出迴圈,break下邊的語句將不再執行。

​ 和break類似的語句還有一個continue語句,此語句可以立即結束當前迴圈,或者說可以跳過當前迴圈,也就是像break一樣,直接忽略之後的所有語句,但是和break語句不同的地方在於,continue語句並不會終止迴圈,它會讓迴圈體直接開始下一輪迴圈,也就是立即終止本輪迴圈並直接開始下一輪迴圈。

​ 需要注意的是在for迴圈中使用continue並不會影響到第三部分的執行,儘管第三部分是在最後執行,但是它仍然不真的屬於迴圈體,在for迴圈中使用continue會忽略掉迴圈體中的語句,但是對於在最後執行的第三部分,for迴圈仍然會執行。

for (int i = 0; i < 10; i++,System.out.println(i)) {
	if(i == 3)
		continue;
}//輸出結果為1,2,3,4,5,6,7,8,9,10。可見i等於3時的countnue語句並沒有影響到第三部分的執行,這是因為for的迴圈體和第三部分不是一個區域。

3.do while迴圈

​ do while迴圈和while迴圈實際上有不小的差別,其使用方式為do{迴圈體}while(迴圈條件);。是不是很奇怪,do while迴圈中的迴圈體或者說迴圈語句會被強制執行一次,也就是說無論如何,這個迴圈中的迴圈體都會被至少執行一次。

int i = 0;
do{
    if(i>10);
    	break;
    System.out.println(i);
    i++;
}while(false);

​ 上邊的程式碼輸出結果為0,即使while迴圈的迴圈條件上來就是0,它也會被強制執行一遍,這就是do while迴圈。這裡有一個重點,在Java中我們難免會計算一些數學相關的式子,如等差數列求和之類的,對於這些式子,通常有更加簡便的數學公式,不使用迴圈就可以直接一步得到,對於這種情況,我們儘量使用公式,而不是使用迴圈增加計算機負擔。

3.Switch擊穿問題(面試點)

​ 在Java中,存在一種選擇語句,我們稱之為switch語句,這種語句的使用方法如下:

switch(x){
	case 1:
		System.out.println(1);
		break;
	case 2:
		System.out.println(2);
		break;
	case 3:
		System.out.println(3);
		break;
	case 4:
		System.out.println(4);
		break;
	case 5:
		System.out.println(5);
		break;
	default:
		System.out.println(6);
		break;
}

​ switch語句是一種分支語句,在特定的情況下使用效果非常好,它的使用方法是在switch中指定一個已經存在的變數,這裡為x,當語句執行到這裡時,這個語句就會將x變數中的數值和他下邊case中列舉出來的數值進行對比,一旦對比成功,語句就會從對比成功的位置開始執行。注意這句話,語句就會從對比成功的位置開始執行,這裡就是switch擊穿問題的關鍵所在,我們看到在每個case後邊都有一個break語句在進行“兜底”,這個break語句實際上就是防止擊穿的機制,如果沒有break的話,在某次匹配成功之後,這個語句就會一直向下執行,進而將下邊的不符合匹配的答案也進行輸出。如:

switch(x){
            case 1:
                System.out.println(1);
                break;
            case 2:
                System.out.println(2);
                //break;這裡將break註釋了
            case 3:
                System.out.println(3);
                break;
            case 4:
                System.out.println(4);
                break;
            case 5:
                System.out.println(5);
                break;
            default:
                System.out.println(6);
                break;
        }
/*
	switch語法就是選中一個變數,然後在其作用域和case進行對比,若對比成功則執行下面的語句,執行到break就會強行停止。
	如果忘記寫break,就會發生switch擊穿,就會繼續向下執行所有的東西,也就是說一旦匹配到,就會開始執行,直到break為止,它會不顧所有的一直往下執行,即使不匹配的也會執行,直到break為止。
*/

​ 上邊語句的執行結果就變成了2,3。語句在2處匹配成功,然後就開始向下執行,因為沒有了break進行兜底,程式便直接忽略了case 3這個匹配語句並直接執行了它的語句塊,直到遇見3語句塊中的break才會停止。switch擊穿就是在switch語句中,由於某處忽略了break語句,導致程式執行一直向下執行,不能按照設計好的邏輯正常執行。有點類似剎不住車了。另外一點需要注意的是case中可以進行匹配的變數型別為char、byte、 short 或 int 的常量表達式,也就是一些算式也是可以的,同時還可以匹配列舉型常量。從 Java SE 7開始, case 標籤還可以是字串字面量。字串字面量就是字串,case可以進行字串的匹配,但是其他更復雜的引用型別就不可以了。switch可選型別目前只有這幾種,這個也是一個面試題。

4.break和continue

​ break只能打斷當前包裹自己的for迴圈,也就是打算一層迴圈。

​ continue是終止本次迴圈,或者說跳過本次迴圈,直接進行下一次迴圈。

5.大數型別

​ 當需要真正意義上的大數精確運算時,需要大數型別,有BigInteger和BigDecimal,BigInteger專門收納大數整數,BigDecimal專門收納大數小數。他們的實現機制都不是普通的計算機邏輯運算,而是基於字串的模擬運算,這兩個類中提供相關的運算操作函式,總體上講還是非常方便的。

6.陣列

​ 之前已經提到過,在Java中陣列是一種非常重要的型別,需要注意的是,陣列是引用型別,而非是基本型別,首先根據陣列不是定長的就可以判斷出陣列不是基本型別這一事實。陣列的定義方式有多種:

int[] a = new int[10];
int[] b = {1,2,3,4,5,6,7,10};
int[] c = new int[]{1,3,4,5,6,7,10};

​ 基本型別的特點是單個變數空間佔用不變,我們在不停的宣告不同大小的同名數組時,不會報錯,說明它的大小可以自如的變化。陣列的大小可以動態變化,而基本型別的大小是固定的,因此可以知道陣列不是基本型別,而是屬於引用型別。關於引用型別,之後在Java執行時中會進行更加詳細的解釋。

int[] a = new int[10];//注意這種方式宣告的陣列不是空陣列,每個上邊都有0
int[] b = {1,2,3,4};
int[] c = new int[]{2,3,4,5};

​ 注意陣列a的宣告方式,它並沒有為陣列賦初值,但在java中,只要宣告陣列,Java程式就會為這個陣列賦予初值0,這是和C語言中不同的地方。

int[] a = new int[10];
System.out.println(Arrays.toString(a));

​ 然而對於其他的變數來說,Java並不會為它們賦初值,只有陣列型別會被賦予初值。在這裡先簡要提一句:Java執行時中,基本型別的變數的控制代碼和其值是相鄰的,它們同位於棧區,而引用型別的控制代碼位於棧區,但是其控制代碼的值並沒有挨著它,和他挨著的是一個地址,這個地址指向堆區中一個地址,這個地址是引用型別的值真實所在的位置。

​ 陣列的定址方式如下:陣列的索引從0開始,陣列記錄的是它開頭第一個字元的地址,在裡邊每個元素都有一個首地址,第一個數的地址就是首地址,所以是首地址+0,其定址方式就是首地址+n*單位。獲取第二個n就是1,因為首地址就是第一個的地址,啥也不用加,所以是0,從零開始。陣列中第n個元素就是索引n-1。陣列的定址方式實際上在之前我詳細記載過,實際上就是因為陣列上的元素長度都一樣,只要獲取陣列元素的長度資訊,就可以使用首地址+下標*元素長度的方式進行定址。這個定址過程可以直接得到地址資訊,非常的快速。

7.二維陣列和三維陣列

​ 同C語言一樣,Java中也可以有二維陣列和三維陣列,上圖為這兩種陣列的宣告和初始化方式。對於這兩種陣列我們可以理解為:儲存陣列的陣列,也就是說陣列的元素同為陣列型別,根據上面我們所定義的陣列,陣列元素在記憶體上佔用的長度是一定的,可是在二維陣列和三維陣列中,陣列中的陣列元素的長度可以是不一樣的,用這種方式定義的陣列叫做不規則陣列,也就是子陣列長度不同的陣列。這是為什麼呢?這是因為陣列中的子陣列並不是儲存的這個陣列,而是儲存了這個子陣列的地址,也就是說二維陣列中,是儲存了多個子陣列地址的陣列,而三維陣列中的子陣列,則同為儲存了字陣列的陣列,這個概念非常類似C語言中的重指標,實際上它們的實現方式也類似於重指標。

​ 在變數宣告中,等號左側是控制代碼右側是值,基本型別的值和控制代碼是在一起的,引用型別是指向自己值的地址,引用型別的等於號相當於把自己的指向換掉,java中不存在指標概念,只存在引用型別概念,但實際上引用型別的底層實現就是指標。我們可以理解為:引用型別本身相當於一個指標,或者說有著類似指標的功能。因此我們可以知道,在二維陣列中,每個陣列元素實際上是一個指向另一個數組的引用型別,這個引用型別的值其實是個地址,而這些地址是等長的,因此他們完全可以被儲存在一個數組中,他們指向的陣列的長度是否都等長實際上和父陣列沒有關係,因此可以實現不規則陣列。如下是二維陣列的記憶體示意圖:

​ 三維陣列和二維陣列在記憶體上的儲存方式是同理的,三維陣列的子陣列同二維陣列的子陣列一樣,是元素為另一個數組地址的陣列,根據這個道理,我們實際上可以宣告出更多維度的陣列,但實際上這可能不太實用。

​ 三維陣列和二維陣列非常類似作業系統中的多級索引,或者樹狀列表,它們的實現原理實際上就和多級索引以及樹狀列表類似。

8.引用型別的淺拷貝和深拷貝

​ 字串型別的底層是用字元陣列實現的,它也是引用型別,和陣列相當類似,對於引用型別,存在兩種型別的拷貝,一種名曰淺拷貝,另一種名曰深拷貝。當我們希望拷貝一個引用型別到另一個變數中去時,使用“=”,如:

String a = "See you Cowboy Bebop!";
String b = a;

​ 這個過程實際上是將a的地址拷貝給了b,b只不過是指向了a指向的字串,此時二者指向同一個字串,當我們通過a、b中任意一個變數修改這個字串時,使用另一個變數訪問字串,會發現使用另一個變數輸出出來的字串也被改動過了,實際上淺拷貝就是拷貝指向,會讓其他的引用型別變數指向同一個地址上的值。這種拷貝我們稱之為淺拷貝

​ 另外一種拷貝方式我們稱之為深拷貝,深拷貝實際上就是新建一個地址,然後將原變數指向的地址上的值真正的複製到這個地址上,然後再讓新變數指向這個新地址,這樣我們修改其中一個引用型別變數的值,另外一個不會受到影響,因為二者都有一個自己的真實值。對於深拷貝,我們通常可以自己寫,對於String型別的深拷貝,可以使用構造方法進行實現。這點目前還沒有深入學習,在以後的學習中我會進行更加深入的研究。

9.排序

​ 在Java中,提供一個現成的快速排序方法,使用Arry類進行呼叫,呼叫方法為Arry.sort();這個方法是一個快速排序,在使用Java寫演算法時可以直接用,非常方便。

10.筆記原文

​ 下面附上我的筆記原文:

大括號括起來的部分就是塊,在主類中和主方法並行的大括號是合法的,裡邊可以寫一些操作,這是合法的。
while迴圈是一個常用的迴圈語句,while本身就有當...時的意思,因此也就是說當while中的語句為真的時候,就迴圈
do while迴圈第一次執行不需要判斷,也就是會一定會至少執行一次。
import java.util.*
public class Wt{
	public static void main(String[] aaa){
		for(int a = 0, b = 9;a < 10; a++){
			System.out.println("================");
		}
	}
}
和
import java.util.*
public class Wt{
	public static void main(String[] aaa){
		for(int a = 0, b = 9;a < 10; System.out.println("================"),a++){
			
		}
	}
}
二者是一樣的。
但是迴圈體內的語句是先於for引數內的第三個位置執行的。
在計算機中,有規律的計算都能歸納為公式,千萬不要盲目的用for迴圈,比如從1加到1萬,用高斯定理即可

switch擊穿 面試題 !!!!
這是一個重要的面試題
switch是一種分支,
switch(x){
case 1:
System.out.println(1);
break;
case 2:
System.out.println(2);
break;
case 3:
System.out.println(3);
break;
case 4:
System.out.println(4);
break;
case 5:
System.out.println(5);
break;
default:
System.out.println(6);
break;
}
switch語法就是選中一個變數,然後在其作用域和case進行對比,若對比成功則執行下面的語句,執行到break就會強行停止。
如果忘記寫break,就會發生switch擊穿,就會繼續向下執行所有的東西,也就是說一旦匹配到,就會開始執行,直到break為止,它會不顧所有的一直往下執行,即使不匹配的也會執行,知道break為止。

case 標籤可以是: 
•
型別為 char、byte、 short 或 int 的常量表達式。 
•
列舉常量。 
•
從 Java SE 7開始, case 標籤還可以是字串字面量。

switch可選型別目前只有這幾種,這個也是一個面試題。
字串字面量就是字串,case可以進行字串的匹配,但是其他更復雜的引用型別就不可以了。
break只能打斷當前包裹自己的for迴圈,也就是打算一層迴圈。
continue是終止本次迴圈,或者說跳過本次迴圈,直接進行下一次迴圈。

當需要真正意義上的大數精確運算時,需要大數型別,有BigInteger和BigDecimal,BigInteger專門收納大數整數,BigDecimal專門收納大數小數。
陣列時引用型別,不是基本型別,哪怕是int型別陣列。
int[] a = new int[10];
基本型別的特點是單個變數空間佔用不變,我們在不停的宣告不同大小的同名數組時,不會報錯,說明它的大小可以自如的變化。
陣列的大小可以動態變化,而基本型別的大小是固定的,因此可以知道陣列不是基本型別,而是屬於引用型別。

陣列的宣告方式:
int[] a = new int[10];//注意這種方式宣告的陣列不是空陣列,每個上邊都有0
int[] b = {1,2,3,4};
int[] c = new int[]{2,3,4,5};
基本型別的控制代碼和值都是相鄰的,而引用型別的值通常不相鄰,通常是控制代碼與一個指標相鄰,指標指向連續的空間、
陣列的索引從0開始,陣列記錄的是它開頭第一個字元的地址,在裡邊每個元素都有一個首地址,第一個數的地址就是首地址,所以是首地址+0,其定址方式就是首地址+n*單位。獲取第二個n就是1,因為首地址就是第一個的地址,啥也不用加,所以是0,從零開始。陣列中第n個元素就是索引n-1

二維陣列

二維陣列,三維陣列,這些語法都是沒問題的。
不規則陣列也是合法的
int[][] b = {
{0,0,0},
{89,0},
{0,0,0,0,0}
}
用這種方式定義的陣列叫做不規則陣列,也就是子陣列長度不同的陣列
陣列宣告的方式很靈活很多遍,同時宣告過的數組裡邊的子陣列也可以重新宣告
等號左側是控制代碼右側是值,基本型別的值和控制代碼是在一起的,引用型別是指向自己值的地址,引用型別的等於號相當於把自己的指向換掉,java中沒有為指標。
在java中引用型別的等於號相當於在切換自己的指向,引用型別並沒有和自己的值在一起,引用型別的值是指向一個儲存值得地址的,引用型別本身相當於一個指標,或者說有著類似指標的功能。
多維陣列和作業系統中的多級索引類似,可能實現起來就是一個東西。
在多維陣列中,陣列元素中存取的值其實就是存的地址

陣列名下存在著地址,這個地址指向它的值實體,二維陣列的值實體是一個連續的空間,這空間之中連續排布這一串地址,這些地址則是指向了其他的連續的空間,這些空間中排布的就是數值了。
這個可以理解為空間上的矩陣,也可以理解為多級索引,總體上就是地址的多級指向,類似C語言中的重指標,即指向指標的指標,最終像樹一樣存取了多個數值。其結構非常類似於樹,在整個結構中存取值的其實是葉子結點部分。
對於二維陣列的遍歷,也是O(1)的時間複雜度,因為都是隨機存取,都有地址
對於二維陣列中a[3][4]中,a[0] = new一個新的的行為,其實就是新開闢一個空間,然後切換a[0]的指向,C++中的指標切換指向,其實也是這個意思。只要是引用型別的控制代碼等於什麼,一定是在切換它的指向。其他語言也是一樣的。
引用型別是拿過地址,基本型別是直接拷貝複製。
其實也就是說只要這個型別是基本型別,那麼給它賦值就是直接複製,而如果它是引用型別,就要引用地址。
這一點其實在C++中最為明顯,C語言可以用重指標實現多維陣列,Java中雖然沒有指標,但是其原理十分相似。
字串陣列,字串也都是運用型別,實際上,一維字串陣列類似於二維陣列,其元素都是引用型別
注意陣列的修改方式與規則,其實就是根據元素究竟是什麼型別而定。
引用型別直接等於號是淺拷貝,淺拷貝就是改變地址指向,深拷貝則是直接再申請一個新空間,將數值拷貝後讓新變數指向它
Java中的Arry.sort(),提供一個快速排序的功能,是一個優化的快速排序,打演算法時會經常用到這個,很有用!