javaSE

CH_song發表於2024-08-18

JDK的安裝與解除安裝

解除安裝

首先在控制面版上解除安裝---》需要在環境變數上刪除配置。

安裝

直接下載install版的jdk 直接安裝即可

配置環境變數(當前電腦在任何位置都能使用jdk中bin目錄的命令)

我的電腦--》屬性--》高階屬性設定--》環境變數--》path 新增jdk安裝目錄中的bin檔案路徑

C:\Program Files\Java\jdk-17\bin

環境變數配置

配置JAVA_HOME給其他軟體使用java提供一個視窗。

在系統變數中新建一個變數

​ 變數名:JAVA_HOME

​ 變數值::\Program Files\Java\jdk-17

jdk jre jvm

jdk:java開發工具包

jre:java執行時環境

jvm:java虛擬機器

jdk包含jre包含jvm

idea自定義快捷模板

File-->Setting-->Editor-->Live Templates

image-20240716115559576

HelloWorld案例常見錯誤

當cmd命令列視窗的字元編碼與.java原始檔的字元編碼不一致,如何解決?

1.在Notepad++等編輯器中,修改原始檔的字元編碼:

2.在EditPlus中可以將Java原始檔另存為ANSI編碼方式(中文作業系統下即為GBK字符集)

3.在使用javac命令式,可以指定原始檔的字元編碼 --> javac -encoding utf-8 Review01.java

4.一個java檔案內有幾個類就會生成幾個class檔案

5.一個java檔案內有多個類如果採用動態編譯則使用第一個類

6.如果一個類被public修飾則類名必須和它一致

一個java檔案中只能有一個類被public修飾

7.java是嚴格區分大小寫

8.java標點符號必須是英文半形

基本語法

  • 大小寫敏感:Java 是大小寫敏感的,這就意味著識別符號 Hello 與 hello 是不同的。
  • 類名:對於所有的類來說,類名的首字母應該大寫。如果類名由若干單片語成,那麼每個單詞的首字母應該大寫,例如 MyFirstJavaClass
  • 方法名:所有的方法名都應該以小寫字母開頭。如果方法名含有若干單詞,則後面的每個單詞首字母大寫。
  • 原始檔名:原始檔名必須和類名相同。當儲存檔案的時候,你應該使用類名作為檔名儲存(切記 Java 是大小寫敏感的),檔名的字尾為 .java。(如果檔名和類名不相同則會導致編譯錯誤)。
  • 主方法入口:所有的 Java 程式由 public static void main(String[] args) 方法開始執行。

註釋和列印語句

作用: 對程式碼進行解釋說明
分類: 單行 //
多行 /* * /
文件 /** * /
注意:
1. 多行和文件註釋不能巢狀使用
1. 註釋只存在於編譯時
編譯 .java--> .class
反編譯 .class --> .java

關鍵字

被Java語言賦予了特殊含義,用做專門用途的字串(或單詞)

java識別符號

識別符號:java中對類 方法 變數等命名時採用的字元序列

命名規則:

​ 1.字母 數字 下劃線_ 美元符$ 組成

​ 2.數字不能開頭

​ 3.不能使用java中的關鍵字和保留字
​ 保留字:在當前的k版本內沒有意義但是以後可能會使用
​ 4.嚴格區分大小寫

命名規範:

​ 對類命名:大駝峰 例: UserTest

​ 對變數 方法命名: 小駝峰 例: userName

​ 對包命名:全部字母小寫 例: chs

​ 對常量命名:全部字母大寫如果由多個單片語成 中間使用下劃線連線 例: PI

兩大資料型別

基本資料型別:四類八種

​ 整數型別: byte short int long

​ 浮點: float double
​ 字元: char
​ 布林: boolean true false

引用資料型別:只要不是基本資料型別那麼一定是引用資料型別

整數型別:

image-20240713094623346

計算公式 [2(bit-1)~(2(bit-1))-1]

定義long型別的變數,賦值時需要以"l"或"L"作為字尾。

​ 當賦值的值沒有超過int界時,可以不用加 "L"

Java程式中變數通常宣告為int型,除非不足以表示較大的數,才使用long。

Java的整型常量預設為 int 型

浮點型別:

image-20240713103639683

  • 浮點型常量有兩種表示形式:

    • 十進位制數形式。如:5.12 512.0f .512 (必須有小數點)

    • 科學計數法形式。如:5.12e2 512E2 100E-2

      例:5e2==>5*10^2

  • float:單精度,尾數可以精確到7位有效數字。很多情況下,精度很難滿足需求。

  • double:雙精度,精度是float的兩倍。通常採用此型別。

  • 定義float型別的變數,賦值時需要以"f"或"F"作為字尾。

  • Java 的浮點型常量預設為double型

如果需要精確數字計算或保留指定位數的精度,需要使用BigDecimal類

字元型別:

char 型資料用來表示通常意義上“字元”(佔2位元組).

char型別是可以進行運算的。因為它都對應有Unicode碼,可以看做是一個數值.

a:97 A:65 0:48

布林型別:

boolean型別資料只有兩個值:true、false,無其它 (一個位元組)

計算機儲存單位

位元組(Byte):是計算機用於計量儲存容量基本單位,一個位元組等於8 bit。

位(bit):是資料儲存的最小單位。二進位制數系統中,每個0或1就是一個位,叫做bit(位元),其中8 bit 就稱為一個位元組(Byte)。

8 bit = 1 Byte

1024 Byte = 1 KB

1024 KB = 1 MB

1024 MB = 1 GB

1024 GB = 1 TB

變數

變數: 在程式執行時值可以發生改變的量
作用: 記錄程式執行時產生的資訊

注意:
方法內變數是區域性變數 它在使用前必須進行初始化賦值就是初始化
在同一個方法內不能宣告同名的區域性變數

字串可以與變數名進行拼接中間使用+連線

底層整數儲存原理:所有資料在底層都是以2進位制形式儲存

byte 8bit [-128,127]
二進位制最高位是符號位 0正數 1負數

36

原始碼:0010 0100

反碼: 0010 0100

補碼: 0010 0100

正整數三碼合一

-36:

原始碼:最高位是1其餘位是10進位制轉為2進位制的值 1010 0100

反碼: 符號位不變其餘位1變00變1 1101 1011

補碼: 反碼+1 1101 1100

負整數

所有資料在底層是以補碼的形式儲存

資料型別轉換

自動轉換 (小轉大)

如果有多個資料型別的值做運算結果是最高型別

強制型別轉換(大轉小)

公式:小的資料型別 變數名=(小的資料型別) 大的資料型別的值;

byte<short==char<int<long<flaot<double;

當強制型別轉換大轉小超出小的範圍時會以2進位制形式儲存

例:

byte num=(byte)400; ====> num=-112

解:

原始碼:1 1001 0000 因為byte佔8bit,所以只取最後8位

補碼:1001 0000

反碼:1000 1111

原始碼:1111 0000

-112

特殊的型別轉換

byte short char三個型別的【變數】進行運算結果都是int型別

有時強制型別轉換會小轉大 (double)100

運算子

按照功能分為:算術運算子、賦值運算子、比較(或關係)運算子、邏輯運算子、位運算子、條件運算子、Lambda運算子

分類 運算子
算術運算子(7個) +、-、*、/、%、++、--
賦值運算子(12個) =、+=、-=、*=、/=、%=、>>=、<<=、>>>=、&=、|=、^=等
比較(或關係)運算子(6個) >、>=、<、<=、==、!=
邏輯運算子(6個) &、|、^、!、&&、||
位運算子(7個) &、|、^、~、<<、>>、>>>
條件運算子(1個) (條件表示式)?結果1:結果2
Lambda運算子(1個) 後面講解
  • 按照運算元個數分為:一元運算子(單目運算子)、二元運算子(雙目運算子)、三元運算子 (三目運算子)
分類 運算子
一元運算子(單目運算子) 正號(+)、負號(-)、++、--、!、~
二元運算子(雙目運算子) 除了一元和三元運算子剩下的都是二元運算子
三元運算子 (三目運算子) (條件表示式)?結果1:結果2

算術運算子

image-20240715085043345

“+”號的兩種用法
  • 第一種:對於+兩邊都是數值的話,+就是加法的意思
  • 第二種:對於+兩邊至少有一邊是字串的話,+就是拼接的意思
符號:=
  • 當“=”兩側資料型別不一致時,可以使用自動型別轉換或使用強制型別轉換原則進行處理。
  • 支援連續賦值

賦值運算子: +=、 -=、*=、 /=、%= (不會更改資料型別)

賦值運算子 符號解釋
+= 將符號左邊的值右邊的值進行相加操作,最後將結果賦值給左邊的變數
-= 將符號左邊的值右邊的值進行相減操作,最後將結果賦值給左邊的變數
*= 將符號左邊的值右邊的值進行相乘操作,最後將結果賦值給左邊的變數
/= 將符號左邊的值右邊的值進行相除操作,最後將結果賦值給左邊的變數
%= 將符號左邊的值右邊的值進行取餘操作,最後將結果賦值給左邊的變數

比較(關係)運算子

image-20240715093703313

  • 比較運算子的結果都是boolean型,也就是要麼是true,要麼是false。

  • > < >= <= :只適用於基本資料型別(除boolean型別之外)

    == != :適用於基本資料型別和引用資料型別

  • 比較運算子“==”不能誤寫成“=

  • instanceof 檢查是否式類的物件 返回結果為true|false

    用法: a instanceof 型別 s

邏輯運算子

image-20240715094053851

  • & 和 &&:表示"且"關係,當符號左右兩邊布林值都是true時,結果才能為true。否則,為false。

  • | 和 || :表示"或"關係,當符號兩邊布林值有一邊為true時,結果為true。當兩邊都為false時,結果為false

  • ! :表示"非"關係,當變數布林值為true時,結果為false。當變數布林值為false時,結果為true。

  • ^ :當符號左右兩邊布林值不同時,結果為true。當兩邊布林值相同時,結果為false。

  • 區分“&”和“&&”: (&&:如果前面的表示式有一個false,就不再執行後面的表示式了)

    • 相同點:如果符號左邊是true,則二者都執行符號右邊的操作

    • 不同點:& : 如果符號左邊是false,則繼續執行符號右邊的操作

    ​ && :如果符號左邊是false,則不再繼續執行符號右邊的操作

    • 建議:開發中,推薦使用 &&
  • 區分“|”和“||”:(||:如果前面的表示式有一個ture,就不再執行後面的表示式了)

    • 相同點:如果符號左邊是false,則二者都執行符號右邊的操作

    • 不同點:| : 如果符號左邊是true,則繼續執行符號右邊的操作

      ​ || :如果符號左邊是true,則不再繼續執行符號右邊的操作

    • 建議:開發中,推薦使用 ||

位運算子

image-20240715102753252

位運算子的運算過程都是基於二進位制的補碼運算

左移:<<

運算規則:在一定範圍內,資料每向左移動一位,相當於原資料*2。(正數、負數都適用)

【注意】當左移的位數n超過該資料型別的總位數時,相當於左移(n-總位數)位

image-20240715104159115

右移:>>

運算規則:在一定範圍內,資料每向右移動一位,相當於原資料/2。(正數、負數都適用)

image-20240715104137002

無符號右移:>>>

運算規則:往右移動後,左邊空出來的位直接補0。(正數、負數都適用)

image-20240715104125640

條件運算子(可巢狀使用)

條件運算子格式:

(條件表示式)? 表示式1:表示式2

運算子優先順序

優先順序 運算子說明 Java運算子
1 括號 ()[]{}
2 正負號 +-
3 單元運算子 ++--~
4 乘法、除法、求餘 */%
5 加法、減法 +-
6 移位運算子 <<>>>>>
7 關係運算子 <<=>=>instanceof
8 等價運算子 ==!=
9 按位與 &
10 按位異或 ^
11 按位或 `
12 條件與 &&
13 條件或 `
14 三元運算子 ? :
15 賦值運算子 =+=-=*=/=%=
16 位賦值運算子 &=、`

開發建議:

  1. 不要過多的依賴運算的優先順序來控制表示式的執行順序,這樣可讀性太差,儘量使用()來控制表示式的執行順序。
  2. 不要把一個表示式寫得過於複雜,如果一個表示式過於複雜,則把它分成幾步來完成。例如:
    ​ (num1 + num2) * 2 > num3 && num2 > num3 ? num3 : num1 + num2;

分支語句

if-else條件判斷結構

單結構分支

if(條件表示式){
  	語句塊;
}

雙分支條件判斷

if(條件表示式) { 
  	語句塊1;
}else {
  	語句塊2;
}

多分支條件判斷:if...else if...else

if (條件表示式1) {
  	語句塊1;
} else if (條件表示式2) {
  	語句塊2;
}
...
}else if (條件表示式n) {
 	語句塊n;
} else {
  	語句塊n+1;
}

switch-case選擇結構

switch(表示式){
    case 常量值1:
        語句塊1;
        //break;
    case 常量值2:
        語句塊2;
        //break; 
    // ...
   [default:
        語句塊n+1;
        break;
   ]
}

使用注意點:

  • switch(表示式)中表示式的值必須是下述幾種型別之一:byte,short,char,int,列舉 (jdk 5.0),String (jdk 7.0)

  • case子句中的值必須是常量,不能是變數名或不確定的表示式值或範圍;

  • 同一個switch語句,所有case子句中的常量值互不相同;

  • break語句用來在執行完一個case分支後使程式跳出switch語句塊;

    如果沒有break,程式會順序執行到switch結尾;

  • default子句是可選的。同時,位置也是靈活的。當沒有匹配的case時,執行default語句。

5.2.2 應用舉例

利用case的穿透性

在switch語句中,如果case的後面不寫break,將出現穿透現象,也就是一旦匹配成功,不會在判斷下一個case的值,直接向後執行,直到遇到break或者整個switch語句結束,執行終止

switch的新寫法

在 Java 14 中,新的 switch 表示式主要改變了兩個方面:

  • 支援箭頭表示式返回;
  • 支援 yied 返回值。
/*
 * 需求:指定一個月份,輸出該月份對應的季節。一年有四季:
 * 		3,4,5	春季
 * 		6,7,8	夏季
 * 		9,10,11	秋季
 * 		12,1,2	冬季
 */
  // 改進版
        switch(month) {
            case 1:
            case 2:
            case 12:
                System.out.println("冬季");
                break;
            case 3:
            case 4:
            case 5:
                System.out.println("春季");
                break;
            case 6:
            case 7:
            case 8:
                System.out.println("夏季");
                break;
            case 9:
            case 10:
            case 11:
                System.out.println("秋季");
                break;
            default:
                System.out.println("你輸入的月份有誤");
                break;
        }

   //改進版2 (一個case後面可以有多個常量值)

     int month =6;
        switch(month){
            case 1,2,12:System.out.println("冬季"); break;
            case 3,4,5:System.out.println("春季"); break;
            case 6,7,8:System.out.println("夏季") ;break;
            case 9,10,11:System.out.println("秋季"); break;
        }

    //改進版3  (switch 可以有返回值)可以省略break;
 		/*
        統計月份對應的季節
          3 4 5 春天
          6 7 8 夏天
          9 10 11 秋天
          12 1 2 冬天
         */
        int month = 3;
        String seasonName =  switch (month) {
           case 3,4,5 ->{
               yield  "春天";   //yield 屬性 就是返回的值
           }
           case 6,7,8 -> "夏天";
           case 9,10,11 ->"秋天";
           case 12,1,2->"冬天";
           default -> "您輸入的月份有誤";
        };
        System.out.println("seasonName = " + seasonName);

總結:switch和if分支

if適用範圍判斷和等值判斷

switch只使用等值判斷 但是等值判斷的效率比If快很多

如何獲取一個隨機數

Math.random()

1、Math類的random()的呼叫,會返回一個[0,1)範圍的一個double型值

2、Math.random() * 100 ---> [0,100)
(int)(Math.random() * 100) ---> [0,99]
(int)(Math.random() * 100) + 5 ----> [5,104]

3、如何獲取[a,b]範圍內的隨機整數呢?(int)(Math.random() * (b - a + 1)) + a

class MathRandomTest {
	public static void main(String[] args) {
		double value = Math.random();
		System.out.println(value);

		//[1,6]
		int number = (int)(Math.random() * 6) + 1; //
		System.out.println(number);
	}
}

Random類

藉助java.util.Random類來產生一個隨機數發生器,也是最常用的一種,建構函式有兩個,Random()Random(long seed)

  • 第一個就是以當前時間為預設種子
  • 第二個是以指定的種子值進行。

[n,m] 公式: rand.nextInt(m-n+1)+n;

public static void main(String[] args) {
    Random rand = new Random();
    for (int i = 0; i < 10; i++) {
        System.out.println(rand.nextInt(100) + 1);  //[1,100]
    }
}

迴圈語句

  • 理解:迴圈語句具有在某些條件滿足的情況下,反覆執行特定程式碼的功能。

  • 迴圈結構分類:

    • for 迴圈
    • while 迴圈
    • do-while 迴圈
  • 迴圈結構四要素

    • 初始化部分
    • 迴圈條件部分
    • 迴圈體部分
    • 迭代部分

for迴圈

語法格式

for (①初始化部分; ②迴圈條件部分; ④迭代部分){
         	③迴圈體部分;
}

執行過程:①-②-③-④-②-③-④-②-③-④-.....-②

說明:

  • for(;;)中的兩個;不能多也不能少
  • ①初始化部分可以宣告多個變數,但必須是同一個型別,用逗號分隔
  • ②迴圈條件部分為boolean型別表示式,當值為false時,退出迴圈
  • ④可以有多個變數更新,用逗號分隔
for(int i=0,j=0,i<=5;i++){
 //迴圈體
}

while迴圈

語法格式

①初始化部分
while(②迴圈條件部分){
    ③迴圈體部分;
    ④迭代部分;
}

執行過程:①-②-③-④-②-③-④-②-③-④-...-②

說明:

  • while(迴圈條件)中迴圈條件必須是boolean型別。
  • 注意不要忘記宣告④迭代部分。否則,迴圈將不能結束,變成死迴圈。
  • for迴圈和while迴圈可以相互轉換。二者沒有效能上的差別。實際開發中,根據具體結構的情況,選擇哪個格式更合適、美觀。
  • for迴圈與while迴圈的區別:初始化條件部分的作用域不同。
class WhileTest1 {
	public static void main(String[] args) {
		
		int i = 1;
		while(i <= 5){
			System.out.println("Hello World!");
			i++;
		}
	}
}

do-while迴圈

語法格式

①初始化部分;
do{
	③迴圈體部分
	④迭代部分
}while(②迴圈條件部分); 

執行過程:①-③-④-②-③-④-②-③-④-...-②

說明:**

  • 結尾while(迴圈條件)中迴圈條件必須是boolean型別
  • do{}while();最後有一個分號
  • do-while結構的迴圈體語句是至少會執行一次,這個和for和while是不一樣的
  • 迴圈的三個結構for、while、do-while三者是可以相互轉換的。
輸出1~200能被5整除的數,5個為一行
public class DoWhileExam {
    public static void main(String[] args) {
        int i=1;
        int count=0;
        do{
            if(i%5==0){
                count++;
                System.out.print(i+"\t");
                if(count%5==0){
                    System.out.println();
                }
            }
            i++;

        }while (i<=200);
    }
}

對比三種迴圈結構

如何選擇

  • 遍歷有明顯的迴圈次數(範圍)的需求,選擇for迴圈
  • 遍歷沒有明顯的迴圈次數(範圍)的需求,選擇while迴圈
  • 如果迴圈體語句塊至少執行一次,可以考慮使用do-while迴圈
  • 本質上:三種迴圈之間完全可以互相轉換,都能實現迴圈的功能

"無限"迴圈

最簡單"無限"迴圈格式:while(true) , for(;;)

關鍵字break和continue和return的使用

適用範圍			在迴圈結構中使用的作用						相同點

break		switch-case
			迴圈結構			一旦執行,就結束(或跳出)當前迴圈結構		    此關鍵字的後面,不能宣告語句

continue	迴圈結構			一旦執行,就結束(或跳出)當次迴圈結構		    此關鍵字的後面,不能宣告語句

return      任何地方             一旦執行,結束當前方法					此關鍵字的後面,不能宣告語句

break:如果存在標籤,跳出標籤所在層的迴圈

continue:如果存在標籤,結束標籤所在層的本次迴圈

image-20240717102424981

買可樂

/*
1. 輸出1~100內的數 沒有 3的倍數 以及個位是3的

2. 20元買可樂  可樂3元一瓶  瓶子可以換1塊錢  最多買幾瓶?
  思路一: 一次購買當前錢數的最大瓶數
      總錢數  單價  單次購買的可樂數  剩餘錢數
        20    3        6            2
        8     3        2            2
        4     3        1            1
        2
  思路二: 一次購買一瓶
      總錢數  單價  單次購買的可樂數  剩餘錢數
        20    3        1           17
        18    3        1           15
        16    3        1           13
        ....
 */
class LoopExer3{
    public static void main(String[] args) {
        int money  =20;
        int price = 3;

        int count = 0;

        while (money>=price){
            //買一瓶可樂
            count++;
            money = money-3+1;

        }
        System.out.println("count = " + count);


    }
}


class LoopExer2{
    public static void main(String[] args) {
        //總錢數
        int money = 20;
        //單價
        int price = 3;
        //todo 定義變數 記錄每次購買的瓶子數
        int count = 0;
        //迴圈購買 總錢數>=單價
        while (money>=price){
            //單次購買的可樂數
            int bottle = money / price;
            //todo 將每次購買的可樂數 加到一起
            count+=bottle;

            //剩餘錢數
            int freeMoney = money % price;
            //更改總錢數 = 可樂數 + 剩餘錢數
            money = bottle+freeMoney;
        }

        System.out.println("count = " + count);


    }
}
public class Test {
    public static void main(String[] args) {

        for(int i = 1;i<=100;i++){
            if(i%3==0|| i%10==3){
                continue;
            }
            System.out.print(i+"\t");


        }

    }
}

陣列

  • 陣列(Array),是多個相同型別資料按一定順序排列的集合,並使用一個名字命名,並透過編號的方式對這些資料進行統一管理。
  • ​ 儲存相同型別資料的有序集合.
  • 陣列中的概念
    • 陣列名
    • 下標(或索引)
    • 元素
    • 陣列的長度

陣列的宣告

//推薦
元素的資料型別[] 一維陣列的名稱;

//不推薦
元素的資料型別  一維陣列名[];

陣列的宣告,需要明確:

(1)陣列的維度:在Java中陣列的符號是[],[]表示一維,[][]表示二維。

(2)陣列的元素型別:即建立的陣列容器可以儲存什麼資料型別的資料。元素的型別可以是任意的Java的資料型別。例如:int、String、Student等。

(3)陣列名:就是代表某個陣列的識別符號,陣列名其實也是變數名,按照變數的命名規範來命名。陣列名是個引用資料型別的變數,因為它代表一組資料。

陣列的初始化

靜態初始化

1.
資料型別[] 陣列名 = new 資料型別[]{元素1,元素2,元素3,...};
或
資料型別[] 陣列名;
陣列名 = new 資料型別[]{元素1,元素2,元素3,...};

2.
資料型別[] 陣列名 = {元素1,元素2,元素3...};//必須在一個語句中完成,不能分成兩個語句寫
  • 如果陣列變數的初始化和陣列元素的賦值操作同時進行,那就稱為靜態初始化。

  • 靜態初始化,本質是用靜態資料(編譯時已知)為陣列初始化。此時陣列的長度由靜態資料的個數決定。

動態初始化

陣列儲存的元素的資料型別[] 陣列名字 = new 陣列儲存的元素的資料型別[長度];

或

陣列儲存的資料型別[] 陣列名字;
陣列名字 = new 陣列儲存的資料型別[長度];
  • [長度]:陣列的長度,表示陣列容器中可以最多儲存多少個元素。

  • 注意:陣列有定長特性,長度一旦指定,不可更改。和水杯道理相同,買了一個2升的水杯,總容量就是2升是固定的。

陣列元素的引用

​ 每一個儲存到陣列的元素,都會自動的擁有一個編號,從0開始,這個自動編號稱為陣列索引(index)或下標,可以透過陣列的索引/下標訪問到陣列中的元素。

陣列名[索引/下標]

陣列的下標範圍?

Java中陣列的下標從[0]開始,下標範圍是[0, 陣列的長度-1],即[0, 陣列名.length-1]

陣列元素下標可以是整型常量或整型表示式。如a[3] , b[i] , c[6*i];

陣列遍歷

​ 1.普通for :透過下標,遍歷陣列元素展示資料

​ 2.增強for:for(陣列元素型別 變數名: 陣列名){

​ 變數名(陣列內的每個值);}

陣列元素的預設值

image-20240717134445183

Java虛擬機器的記憶體劃分

為了提高運算效率,就對空間進行了不同區域的劃分,因為每一片區域都有特定的處理資料方式和記憶體管理方式。

image-20240717152650784

區域名稱 作用
虛擬機器 用於儲存正在執行的每個Java方法的區域性變數表等。區域性變數表存放了編譯期可知長度的各種基本資料型別、物件引用,方法執行完,自動釋放。
記憶體 儲存物件(包括陣列物件),new來建立的,都儲存在堆記憶體。
方法區(元空間) 儲存已被虛擬機器載入的類資訊、常量、(靜態變數)、即時編譯器編譯後的程式碼等資料。
本地方法棧 當程式中呼叫了native的本地方法時,本地方法執行期間的記憶體區域
程式計數器 程式計數器是CPU中的暫存器,它包含每一個執行緒下一條要執行的指令的地址

陣列的擴容與縮容陣列的擴容與縮容

陣列的擴容

題目:現有陣列 int[] arr = new int[]{1,2,3,4,5}; ,現將陣列長度擴容1倍,並將10,20,30三個資料新增到arr陣列中,如何操作?

public class ArrTest1 {
    public static void main(String[] args) {

        int[] arr = new int[]{1,2,3,4,5};
        int[] newArr = new int[arr.length << 1];

        for(int i = 0;i < arr.length;i++){
            newArr[i] = arr[i];
        }

        newArr[arr.length] = 10;
        newArr[arr.length + 1] = 20;
        newArr[arr.length + 2] = 30;

        arr = newArr;

        //遍歷arr
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

陣列的縮容

題目:現有陣列 int[] arr={1,2,3,4,5,6,7}。現需刪除陣列中索引為4的元素。

public class ArrTest2 {
    public static void main(String[] args) {

        int[] arr = {1, 2, 3, 4, 5, 6, 7};
        //刪除陣列中索引為4的元素
        int delIndex = 4;
        //方案1:
        /*//建立新陣列
        int[] newArr = new int[arr.length - 1];

        for (int i = 0; i < delIndex; i++) {
            newArr[i] = arr[i];
        }
        for (int i = delIndex + 1; i < arr.length; i++) {
            newArr[i - 1] = arr[i];
        }

        arr = newArr;
        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }*/

        //方案2:
        for (int i = delIndex; i < arr.length - 1; i++) {
            arr[i] = arr[i + 1];
        }
        arr[arr.length - 1] = 0;

        for (int i = 0; i < arr.length; i++) {
            System.out.println(arr[i]);
        }
    }
}

陣列元素排序

氣泡排序(依次前後兩個元素進行比較,根據排序規則確定較大(或較小)的元素向後移動(或向後移動)。最後直到最大(或最小)元素移動到最後一位(或第一位))

  • 時間複雜度:平均 O(n²),最壞 O(n²),最好 O(n)
  • 穩定排序,通常用於教學和簡單場景。
//冒泡
public class ArraySort{
    public static void main(String[] args) {
        int[] arr ={3,9,-1,10,-2,6};
        //最佳化冒泡演算法
        boolean flag = false;
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length-i-1; j++) {
                if(arr[j]>arr[j+1]){
                    flag=true;
                    int temp  = arr[j+1];
                    arr[j+1]=arr[j];
                    arr[j]=temp;
                }
            }
            System.out.println(Arrays.toString(arr));
            if(!flag){
                break;
            }
        }
    }
}

選擇排序(首先選擇第“i”個元素下標當作是最小(或是最大)依次去跟後面的元素比較記錄下最小(或最大)元素的下標,然後第“i”個元素於記錄的下標元素交換位置)

  • 時間複雜度:O(n²)
  • 簡單易懂,不穩定,適合小規模資料。
//選擇排序
class ArraySort2 {
    public static void main(String[] args) {
        int[] arr = {3, 9, -1, 10, -2, 6};
        for (int i = 0; i < arr.length; i++) {
            int minIndex = i;
            for (int j = i + 1; j < arr.length; j++) {
                if (arr[minIndex] > arr[j]) {
                    //記錄最小數字的下標
                    minIndex = j;
                }
            }
            //最小和第一個元素交換位置
            if (minIndex != i) {   //最佳化  如果最小值的下標沒有變化了,就說明已經排好序了
                int temp = arr[minIndex];
                arr[minIndex] = arr[i];
                arr[i] = temp;
            }
            else break;
            System.out.println(Arrays.toString(arr));
        }
    }
}

插入排序(把n個待排序的元素看成為一個有序表和一個無序表,開始時有序表中只包含一個元素,無序表中包含有n-1個元素,排序過程中每次從無序表中取出第一個元素,把它的排序碼依次與有序表元素的排序碼進行比較,將它插入到有序表中的適當位置,使之成為新的有序表)

  • 時間複雜度:平均 O(n²),最壞 O(n²),最好 O(n)
  • 簡單且適合小規模資料,穩定排序。
//插入排序
class ArraySort3 {
    public static void main(String[] args) {
        int[] arr = {3, 9, -1, 10, -2, 6};
        for (int i = 1; i < arr.length; i++) {
            //記錄待插入的數
            int insertVal = arr[i];
            //記錄待插入的數,插入的前一個有序元素的下標
            int insertIndex = i - 1;
            //防止下標越界和(定位該元素插入有序列表的下標數)
            while (insertIndex >= 0 && insertVal < arr[insertIndex]) {
                arr[insertIndex + 1] = arr[insertIndex];  //將比待插入大的元素都後移一位
                insertIndex--;
            }
            //迴圈結束後得到了待插入元素插入的位置
            arr[insertIndex+1]=insertVal;
            System.out.println(Arrays.toString(arr));
        }
    }
}

快速排序(透過一趟排序將要排序的資料分割成獨立的兩部分,其中一部分的所有資料都比另外一部分的所有資料都要小,然後再按此方法對這兩部分資料分別進行快速排序,整個排序過程可以遞迴進行,以此達到整個資料變成有序序列)

  • 時間複雜度:平均 O(n log n),最壞 O(n²)
  • 原地排序,適合大多數情況,效率高。
//快速排序
class ArraySort4 {
    public static void main(String[] args) {
        int[] arr = {3, 9, -1, 10, -2, 6};
        //int[] arr = {3, 6, 8, 10, 1, 2, 1};
        quickSort(arr, 0, arr.length - 1);
        System.out.println(Arrays.toString(arr));
    }
    public static void quickSort(int[] arr, int low, int high) {
        if (arr == null || arr.length == 0){
            return;
        }

        if (low >= high){
            return;
        }

        int middle = low + (high - low) / 2;   // 這樣永遠都是一段陣列的"最中間值(向下取整)"   例子  low =0    high =3   mid = 0+3/2 =1
        int pivot = arr[middle];
        int l = low, r = high; //記錄當前開始下標和當前結束下標
        while (l <= r){
            //從當前開始下標開始查詢比中間值   大的值的下標
            while (arr[l] < pivot){
                l++;
            }
            //從當前結束下標開始查詢比中間值   小的值的下標
            while (arr[r] > pivot){
                r--;
            }
            //如果找到的下標滿足當前開始下標小於等於當前結束下標,就互相交換值 ,並當前開始下標後移1,當前結束下標前移1
            if (l <= r){
                int temp = arr[l];
                arr[l] = arr[r];
                arr[r] = temp;
                l++;
                r--;
            }
        }
        if (low < r){   //如果開始下標小於當前結束下標,向左遞迴(開始下標不變,結束下標變成,當前結束下標 r)   【1,3,4]   ,2,1】
            quickSort(arr, low, r);
        }
        if (high > l){  //如果結束下標大於當前開始下標,向右遞迴(結束下標不變,開始下標變成,當前開始下標 l)   【1,3,    [4,2,1】
            quickSort(arr, l, high);
        }
    }
}

歸併排序(是利用歸併的思想實現的排序方法,該演算法採用經典的分治策略(分治法將問題分成一些小的問題然後遞迴求解,而治的階段則將分的階段得到的各答案"修補"在一起,即分而治之))

  • 時間複雜度:O(n log n)
  • 穩定排序,適合處理大資料量,尤其是連結串列。

二維陣列

分類

  • 二維陣列:
    • 所謂二維陣列其實就是多個一維陣列作為元素,儲存在一個一維陣列中
  • 三維陣列
    • 所謂三維陣列其實就是多個二維陣列作為元素,儲存在一個一維陣列中
  • 四維陣列
    • 所謂三維陣列其實就是多個三維陣列作為元素,儲存在一個一維陣列中

宣告與初始化

宣告

//推薦
元素的資料型別[][] 二維陣列的名;

//不推薦
元素的資料型別  二維陣列名[][];
//不推薦
元素的資料型別[]  二維陣列名[]

靜態初始化

int[][] arr = {{1,2,3},{4,5,6},{7,8,9,10}};//宣告與初始化必須在一句完成

int[][] arr = new int[][]{{1,2,3},{4,5,6},{7,8,9,10}};

int[][] arr;
arr = new int[][]{{1,2,3},{4,5,6},{7,8,9,10}};

arr = new int[3][3]{{1,2,3},{4,5,6},{7,8,9,10}};//錯誤,靜態初始化右邊new 資料型別[][]中不能寫數字

動態初始化

/**動態1*/
//(1)確定行數和列數
元素的資料型別[][] 二維陣列名 = new 元素的資料型別[m][n];
例:int[][] arr = new int[3][2];
	//其中,m:表示這個二維陣列有多少個一維陣列。或者說一共二維表有幾行
	//其中,n:表示每一個一維陣列的元素有多少個。或者說每一行共有一個單元格

//此時建立完陣列,行數、列數確定,而且元素也都有預設值

//(2)再為元素賦新值
二維陣列名[行下標][列下標] = 值;


/**動態2*/
//(1)先確定總行數
元素的資料型別[][] 二維陣列名 = new 元素的資料型別[總行數][];

//此時只是確定了總行數,每一行裡面現在是null

//(2)再確定每一行的列數,建立每一行的一維陣列
二維陣列名[行下標] = new 元素的資料型別[該行的總列數];

//此時已經new完的行的元素就有預設值了,沒有new的行還是null

//(3)再為元素賦值
二維陣列名[行下標][列下標] = 值;

遍歷二維陣列(多位也一樣)

普通for

for(int i=0; i<二維陣列名.length; i++){ //二維陣列物件.length
    for(int j=0; j<二維陣列名[i].length; j++){//二維陣列行物件.length
        System.out.print(二維陣列名[i][j]);
    }
    System.out.println();
}

增強for

for(一維陣列型別[] 變數名1:二維陣列變數){
    for(資料型別 變數名2 : 變數名1){
        變數名(陣列內的每個值);
    }
}

空指標異常:

public class TestNullPointerException {
    public static void main(String[] args) {
        //定義陣列
        int[][] arr = new int[3][];

        System.out.println(arr[0][0]);//NullPointerException
    }
}

因為此時陣列的每一行還未分配具體儲存元素的空間,此時arr[0]是null,此時訪問arr[0][0]會丟擲NullPointerException 空指標異常。

物件導向思想程式設計

方法(對功能進行封裝,實現程式碼的複用)

  • 方法是類或物件行為特徵的抽象,用來完成某個功能操作。在某些語言中也稱為函式過程
  • 將功能封裝為方法的目的是,可以實現程式碼重用,減少冗餘,簡化程式碼
  • Java裡的方法不能獨立存在,所有的方法必須定義在類裡。
方法必須宣告在類中 且不能巢狀使用
    類{
    方法1(){
        
    }
    方法2(){
        
    }
}

定義方法:

    修飾符   返回值型別
public static void 方法名([形參列表]){
   //方法的內容
}

//public static 是方法的修飾符 公共的 靜態的
//返回值型別:表示方法執行的結果的資料型別,方法執行後將結果返回到呼叫者
//方法名:給方法起一個名字,見名知意,能準確代表該方法功能的名字
//形參列表:表示完成方法體功能時需要外部提供的資料列表
//方法體:方法體必須有{}括起來,在{}中編寫完成方法功能的程式碼

方法呼叫:

public class Method {
    public static void main(String[] args) {
        System.out.println("呼叫方法");
        eat();
    }
    public static void eat(){
        System.out.println("吃飯了!!!");
    }
}

方法的形參和返回值

形參和實參:

方法的形參:
        1.方法的宣告處
        2.規定方法實參的數量和型別
方法的實參:
        方法呼叫者傳遞的實際的值
            
例:
public class MethodTest3 {
    //方法的宣告
    public static void sum(int a,int b){//形參
        System.out.println(a+b);
    }

    public static void main(String[] args) {
      
        sum(10,30);//實參
    }

}

方法的返回值:

有的時候需要方法執行的結果作為下次執行的條件就需要帶返回值的方法
public static 返回值型別 方法名([形參列表]){
        方法體的功能程式碼
}
返回值型別:  
      void:沒有返回值
   資料型別:必須透過 return 關鍵字 返回一個該型別匹配的值 
   
- return語句的作用是結束方法的執行,並將方法的結果返回去
- 如果返回值型別不是void,方法體中必須保證一定有 return 返回值; 語句,並且要求該返回值結果的型別與宣告的返回值型別一致或相容。
- 如果返回值型別為void時,方法體中可以沒有return語句,如果要用return語句提前結束方法的執行,那麼return後面不能跟返回值,直接寫return ; 就可以。
- return語句後面就不能再寫其他程式碼了,否則會報錯:Unreachable code
    
例:    
public class MethodTest4 {
    //方法的宣告
    public static int sum(int a,int b){//形參 
        return a+b;
    }

    public static void main(String[] args) {
      
       int result =  sum(10,30);//實參
        System.out.println("result = "+ result);
    }

}

注意:
方法只宣告不呼叫 不會執行"
方法與方法是兄弟關係
class 類{
方法a(){}
方法b(){}
}
方法執行完畢 回到方法呼叫處

方法的過載(在同一類中使用相同的方法名錶示不同細節實現的方法)

新:本類或者父子類中有相同的方法名 不同的形參列表(數量 順序 型別) 就是過載

要求: 兩同一不同

​ 兩同:同一類中同一方法實現

​ 不同:引數列表不同: 數量、型別、順序

//找兩個數的最大值
public class MethodsOverload {
    public static void main(String[] args) {
        System.out.println((char)Compare('a','b'));
        System.out.println(Compare(10,2));
        System.out.println(Compare(6.1,5.1));
    }
    public static int Compare(int a, int b){
        return a>b?a:b;
    }
    public static int Compare(double a, double b){
        return (char)(a>b?a:b);
    }
    public static int Compare(char a, char b){
        return a>b?a:b;
    }
}

可變引數

當定義一個方法時,形參的型別可以確定,但是形參的個數不確定,那麼可以考慮使用可變引數

宣告可變引數:

public 返回值型別 方法名(【非可變引數部分的形參列表,】引數型別... 形參名){  

}

可變引數的特點

(1)一個方法最多隻能有一個可變引數(語法規定)

(2)如果一個方法包含可變引數,那麼可變引數必須是形參列表的最後一個

(3)在宣告它的方法中,可變引數當成陣列使用

(4)可變引數中實參的數量[0,n]

public class MethodVariableParams {
    public static void main(String[] args) {
        System.out.println(joins('-',"c","h","s"));
    }
    //字元於字串拼接
    public static String joins(char c,String ...s){
        String temp="";
        for (int i = 0; i < s.length; i++) {
            if(i==s.length-1) temp+=s[i];
            else  temp+=s[i]+c;
        }
        return temp;
  }

方法的值傳遞(畫圖)

基本型別資料的值:傳遞的是值的副本

陣列作為引數:傳遞的是地址值