> JDK8 > > 複習用 ## Java前置知識 1. JavaSE Java Standard Edition 標準版 支持面向桌面級應用(如Windows下的應用程式)的 Java平臺,提供了完整的Java核心API 此版本以前稱為 J2SE 2. JavaEE Java Enterpris ...
JDK8
複習用
Java前置知識
-
JavaSE
Java Standard Edition 標準版
支持面向桌面級應用(如Windows下的應用程式)的 Java平臺,提供了完整的Java核心API
此版本以前稱為 J2SE
-
JavaEE
Java Enterprise Edition 企業版
一套用於企業環境下的應用程式的應用方案(包含:Servlet、Jsp),主要針對 Web應用程式的開發
此版本以前稱為 J2EE
-
JavaME
Java Micro Edition 小型版
支持 Java程式 運行在移動端的版本,對 JavaAPI 有所精簡,並且加入了針對移動端的支持
此版本以前稱為 J2ME
-
JDK
Java Development Kit Java開發工具包
JDK = JRE + Java開發工具(java、javac、javap、javadoc等)
-
JRE
Java Runtime Environment Java運行環境
JRE = JVM + Java核心類庫
-
JVM
Java Virtual Machine Java虛擬機
JVM 是一個虛擬的電腦,具有指令集並使用不同的存儲區域,負責執行指令,管理數據、記憶體、寄存器
對於不同的平臺有不同的虛擬機,屏蔽了底層運行平臺的差別,實現了“一次編譯,到處運行”
-
Java運行簡單的大致流程
Java文件 通過 javac 編譯為 Class文件,再通過 java指令 交給 JVM虛擬機 執行
-
Java中特殊的轉義字元
\r
: 游標定位到行首public class Main { public static void main(String[] args) { System.out.println("aaaaaaa\rbb"); } }
-
在 IDEA 中 會清空這一行並把游標放到這一行的首位
上方代碼在IDEA中輸出:bb
-
在 windows終端 中,只會幫游標定位到該行的行首
上方代碼在Windows終端中輸出:bbaaaaa
-
-
Java程式 中
+
號的使用- 當
+
號 的 左邊和右邊 都是數值型時,視為加法運算 - 當
+
號 的 左邊和右邊 有一方是字元串時,視為拼接運算 - 運算順序:從左往右
System.out.println(100 + 98); // 輸出: 198 System.out.println("100" + 98); // 輸出:10098 System.out.println(100 + 3 + "hello"); // 輸出:103hello System.out.printIn("hello" + 100 + 3); // 輸出:hello1003
-
當
+
號 的 左邊和右邊 有一方是char字元時,視為字元編碼值的運算由於 Java 中 char 底層存儲的是 對應字元的Unicode碼值,列印時會對照Unicode表列印出對應的字元
因此,對char字元的運算相當於對char字元對應的Unicode碼值進行運算
但當有一方是字元串時,仍然視為拼接運算
System.out.println(1 + 'a'); // 98 System.out.println('a' + 1); // 98 System.out.println('a' + 'b'); // 195 (97+98=195) System.out.println('a' + "bc"); // abc
- 當
-
JavaAPI
- API : Application Programming Interface 應用程式編程介面
- JavaAPI : Java提供的基本編程介面
- JavaAPI文檔 : Java語言提供了大量的基礎類,Oracle公司也為這些基礎類提供了相應的AP文檔用於告訴開發者如何使用這些類,以及這些類里包含的方法
- switch語句 中表達式的值,必須是:byte、short、int、char、這4個基本數據類型的包裝類對象、String對象、枚舉對象
Java變數
不論使用哪種高級程式語言編寫程式,變數都是程式的基本組成單位
-
變數三要素:
類型、名稱、值
-
變數相當於記憶體中一個存儲空間的表示,即 聲明變數時就是先開闢了一定空間的空房間,變數相當於門牌號,通過 門牌號 可以找到目標房間,從而放入、取出房間中的填充物(也就是變數值),一個房間是有最大容量限制的,超過這個容量就會導致溢出
- 變數類型的不同,其占用記憶體空間的大小也不同
- 此區域有自己的變數名、數據類型,且可以在同一類型內不斷變化
- 變數必須先聲明後使用
- 變數在同一個作用域內,不能重名
-
數據類型
-
基本數據類型
-
數值型
-
整數類型
- byte : 1個位元組
- short : 2個位元組
- int : 4個位元組
- long : 8個位元組
-
浮點數類型
- float : 4個位元組
- double : 8個位元組
浮點數計算陷阱:
2.7 和 8.1/3 的值在電腦中會不相等
因為在二進位方面 8.1/3 是無限小數,double的精度太低,導致在記憶體中存儲的 2.7 和 8.1/3 的值在二進位層面不相等
-
-
字元型
- char : 2個位元組(Java中可以使用2個char來表示1個字元,用於彌補單個char存儲空間小的限制)
-
布爾型
- boolean : 1個位元組
-
-
引用數據類型
- 類
- 介面
- 數組
-
-
字元類型
-
字元類型(char)可以表示單個字元,占用兩個位元組(可以存放漢字),多個字元使用字元串(String類)
-
String類底層仍然是多個char
-
Java中的char本質存儲的是Unicode表中,對應字元的整數位值,因此一些特殊符號(
\n
、\t
)也可以賦值給char,使對應的char在列印時具備對應特殊符號的功能
-
-
布爾類型
- 布爾類型也叫boolean類型,booolean類型數據只允許取值true和false,不允許賦予null值
- boolean類型占1個位元組
- boolean類型適於邏輯運算(if、while、do-while、for等)
-
基本數據類型的相互轉換
-
自動類型轉換
當java程式在進行賦值或者運算時,精度小的類型自動轉換為精度大的數據類型,這個就是自動類型轉換
-
註意事項
-
有多種類型的數據混合運算時,系統首先自動將所有數據轉換成容量最大的那種數據類型,然後再進行計算
-
當我們把精度(容量)大的數據類型賦值給精度(容量)小的數據類型時,就會報錯,反之就會進行自動類型轉換
-
(byte,short)和char之間不會相互自動轉換
flowchart LR byte & short x--無法自動轉換---x char -
byte、short、char他們三者可以計算,在計算時首先轉換為int類型
-
boolean不參與轉換
-
自動提升原則:表達式結果的類型自動提升為操作數中最大的類型
-
-
-
強制類型轉換
自動類型轉換的逆過程,將容量大的數據類型轉換為容量小的數據類型
使用時要加上強制轉換符(),但可能造成精度降低或溢出,格外要註意
-
數據類型從大—>小(或多態的向下轉型)時,就需要使用到強制轉換
-
強轉符號只針對於最近的操作數有效,實際中經常會使用小括弧提升優先順序
-
char類型可以保存int的常量值,但不能保存int的變數值,需要強轉
char c1 = 100; //ok int m = 100; //ok char c2 = m; //錯誤 char c3 = (char)mi; //ok System.out.printIn(c3):/100對應的字元
-
byte、short、char類型在進行運算時,會被當做int類型處理
-
易錯題
short s = 12; //ok s = s - 9; // 錯誤 int -> short byte b = 10;//ok b = b + 11; // 錯誤 int -> byte b = (byte)(b+11); //正確,使用了強轉 char c = 'a'; // ok int i = 16; // ok float d = .314F; // ok double result = c i d; // ok float -> double byte b = 16;// ok short s = 14;// ok short t = s + b; // 錯誤 int -> short
-
-
基本數據類型 和 String類 的轉換
-
轉 String類
int aInt = 1; String aStr = 1 + "";
-
轉 基本數據類型
String aStr = "1"; int aInt = Integer.parseInt(aStr);
-
String類 轉 char
String類底層維護的就是char數組,因此只需要取出數組中的某個索引位置的值即可
String str = "Hello world!"; char e = str.charAt(1); // 數組索引下標從0開始
-
註意事項
-
在將String類型轉成基本數據類型時,要確保String類型能夠轉成有效的數據
比如我們可以把"123",轉成一個整數,但是不能把"hello"轉成一個整數
-
若String類無法轉換為數值基本類型,會拋出異常:java.lang.NumberFormatException
-
String類 轉 boolean 時,除了字元串為
true
時,得到true
的boolean值,其他情況均會得到false
的boolean值String str1 = "Hello world!"; boolean b1 = Boolean.parseBoolean(str1); System.out.println(b1); // 輸出 : false String str2 = null; boolean b2 = Boolean.parseBoolean(str2); System.out.println(b2); // 輸出 : false
-
-
-
Java運算符
-
運算符是一種特殊的符號,用以表示數據的運算、賦值和比較等
Java中存在的運算符:算術運算符、關係運算符 [比較運算符]、邏輯運算符、位運算符、賦值運算符、三元運算符
-
測試算術運算符
運算符 運算 範例 結果 + 正號 + 7 7 - 負號 - 7 -7 + 加號 9 + 9 18 - 減號 9 - 9 0 * 乘號 9 * 9 81 / 除號(保留整數部分) 10 / 3 3 % 取模(保留小數部分) 10 % 3 1 ++ 自增(前):先運算後取值 a=2;b=++a; a=3;b=3; ++ 自增(後):先取值後運算 a=2;b=a++; a=3;b=2; -- 自減(前):先運算後取值 a=2;b=--a; a=1;b=1; -- 自減(後):先取值後運算 a=2;b=a--; a=1;b=2; + 字元串相加(拼接) "hello" + "world" "helloworld" - 註意:
-
取模 的 本質:
a % b = a - a / b * b
-
自增、自減 在前在後 的簡化理解:看作一個整體
例如下麵的例子
-
將
x++
看作一個整體,因為自增在後,所以此時x++
整體的值為 5 ,x
的值為6 -
將
++y
看作一個整體,因為自增在前,所以此時++y
整體的值為6,y
的值為6
int x = 5; int y = 5; if (x++==6 & ++y==6){ // F & T -> F x=11; } System.out.print("x -> " + x + "\t"); // 6 System.out.println("y -> " + y); // 6
-
-
- 註意:
-
關係運算符(比較運算符)
關係運算符的 結果 都是 boolean類型,即 都是 true 或 false
常用於 條件判斷 中
運算符 運算 範例 結果 == 相等於 8 == 7 fasle != 不等於 8 != 7 true < 小於 8 < 7 false > 大於 8 > 7 true <= 小於等於 8 <= 7 false >= 大於等於 8 >= 7 true instanceof 檢查左邊(對象)是否右邊(類)相等 或 子類關係 "hello world" instanceof String true -
邏輯運算符、位運算符
用於連接多個條件(多個關係表達式,關係表達式也就是前面關係運算符所連接的一串表達式)
最終的結果也是一個 boolean 值
-
& : 邏輯與
左邊、右邊同為 true 時,結果為 true,否則為 false
-
&& : 短路與
在滿足 邏輯與 的基礎上,添加了短路機制:當左邊為 false 時,不會計算右邊,直接返回 false
-
| : 邏輯或
左邊、右邊同為 false 時,結果為 false,否則為 true
-
|| : 短路或
在滿足 短路或 的基礎上,添加了短路機制:當左邊為 true 時,不會計算右邊,直接返回 true
-
^ : 邏輯異或
左邊、右邊 不同 時,返回 true
左邊、右邊 相同 時,返回 false
-
! : 取反
對 ! 右側的 boolean值進行取反
true –> false
false –> true
a b a & b a && b a | b a || b a ^ b !a true true true true true ture false false true false false false true true true false false true false false true true true true false false false false false false false true -
複習題
//題1 public static void test01(){ int x = 5; int y = 5; if (x++==6 & ++y==6){ // F & T -> F x=11; } System.out.print("x -> " + x + "\t"); // 6 System.out.println("y -> " + y); // 6 } //題2 public static void test02(){ int x = 5; int y = 5; if (x++==5 | ++y==5){ // T | F -> T x=11; } System.out.print("x -> " + x + "\t"); // 11 System.out.println("y -> " + y); // 6 } //題3 public static void test03(){ int x = 5; int y = 5; if (x++==6 && ++y==6){ // F && (短路不運算) x=11; } System.out.print("x -> " + x + "\t"); // 6 System.out.println("y -> " + y); // 5 } //題4 public static void test04(){ int x = 5; int y = 5; if (x++==5 || ++y==5){ // T || (短路不運算) x=11; } System.out.print("x -> " + x + "\t"); // 11 System.out.println("y -> " + y); // 5 }
-
-
賦值運算
賦值運算符就是將某個運算後的值,賦給指定的變數
-
基本賦值運算符
int a = 1; // 此處的 = 號,便是基本賦值運算符,可以理解為: 有一個存儲int的4位元組空間,空間名稱為a,現在將 1 這個值存放到這個空間中
-
複合賦值運算符
int a = 10; int b = 20; a += b; // 等價於: a = a + b; a -= b; // 等價於: a = a - b; a *= b; // 等價於: a = a * b; a /= b; // 等價於: a = a / b; a %= b; // 等價於: a = a % b;
-
賦值運算符的特點
-
運算順序從右往左
-
賦值運算符
- 左邊 只能是變數
- 右邊 可以是變數、表達式、常量值
-
複合賦值運算符會進行類型轉換
byte b = 3; b += 2; // 等價 b = (byte)(b + 2); b++; // b = (byte)(b+1);
-
-
-
三元運算符
-
基本語法 :
條件表達式 ? 表達式1 : 表達式2
當條件表達式為 true 時,返回 表達式1 的 結果
當條件表達式為 false 時,返回 表達式2 的 結果
-
舉例 :
int a = 10; int b = 20; int res = a > b ? a++ : --b; // res -> 21 ; a -> 10 ; b -> 21 ;
-
細節
-
三元運算符返回基本數據類型時,會自動轉換為表達式中精度最高的類型
int a = 10; double b = 10.1; System.out.println(true ? a : b); // 輸出 : 10.0
-
-
-
運算符優先順序
所謂優先順序就是表達式運算中的運算順序
下表中的運算符中,上一行運算符總優先於下一行
只有單目運算符、賦值運算符是從右向左運算的
運算方向 運算符 . () {} ; , 右 -> 左 ++ -- ~ !(data type) 左 -> 右 * / % 左 -> 右 + - 左 -> 右 << >> >>> 位移 左 -> 右 < > <= >= instanceof 左 -> 右 == != 左 -> 右 & 左 -> 右 ^ 左 -> 右 | 左 -> 右 && 左 -> 右 || 左 -> 右 xx ? xx : xx(三元運算符) 右 -> 左 = *= /= %= 右 -> 左 += -= <<= >>= 右 -> 左 >>>= &= ^= |= -
標識符的命名規範
Java對各種變數、方法和類等命名時使用的字元序列稱為標識符
凡是自己可以起名字的地方都叫標識符-
標識符的命名規則(必須遵守)
-
26個英文字母大小寫(Java中區分大小寫)、0-9、_ 、$ 組成
最好不要用 $
-
數字 不能 開頭
-
不可以 使用 保留字、關鍵字,但能夠包含 保留字、關鍵字
-
長度 不受限制
-
不能包含 空格
-
-
標識符的命名規範(約定俗成)
-
包名
多單片語成時所有字母都小寫
com.zhumei.study
-
類名、介面名
多單詞時使用大駝峰
XxxYyyZzz
-
變數名、方法名
多單詞時使用小駝峰
xxxYxxZzz
-
常量名
字母全大寫
多單詞時使用下劃線分隔
XXX_YYY_ZZZ
-
-
Java流程式控制制
-
順序控制
沒什麼好說的,就是從上往下按代碼順序執行
-
分支控制
-
單分支 if
flowchart TD A[Start] --> B{條件表達式} B -->|true| C[代碼塊] C --> D[end] B -->|false| Dif (條件表達式) { // 條件表達式的值為 ture 則執行此代碼塊中的代碼(false則不執行) 代碼; }
-
雙分支 if - else
flowchart TD A[Start] --> B{條件表達式} B -->|true| C[代碼塊1] C --> E[end] B -->|false| D[代碼塊2] D --> Eif (條件表達式) { // 條件表達式的值為 ture 則執行此代碼塊中的代碼1(false則不執行) 代碼1; } else{ // 條件表達式的值為 false 則執行此代碼塊中的代碼2(true則不執行) 代碼2; }
-
多分枝 if - else if - … - else
flowchart TD A[Start] --> B{條件表達式1} B -->|true| C[代碼塊1] C --> D[end] B -->|false| E{條件表達式2} E -->|true| F[代碼塊2] F --> D E -->|false| G{條件表達式n} G -->|true| H[代碼塊n] G -->|false| I[代碼n+1] H --> D I --> Dif (條件表達式1) { 代碼1; // 條件表達式1為true則執行代碼1,並不再繼續執行與條件表達式1處於同一分支的表達式與其對應的代碼 } else if (條件表達式2){ 代碼2; // 條件表達式2為true則執行代碼2,並不再繼續執行與條件表達式2處於同一分支的表達式與其對應的代碼 } else if (條件表達式N){ 代碼n; // 條件表達式n為true則執行代碼n,並不再繼續執行與條件表達式n處於同一分支的表達式與其對應的代碼 } else{ 代碼n+1; // 若所有的表達式都為false,則執行此代碼塊 }
-
嵌套分支
在 一個分支結構 中又 完整的 嵌套了 另一個完整的分支結構
建議不超過3層,嵌套層數與可讀性呈負相關,層數越多可讀性越差
if (條件表達式a){ 代碼1; if (條件表達式b){ 代碼2; }else if (條件表達式c){ 代碼3; }else { 代碼4 } }
-
switch分支
- 在Java中:
- switch表達式的值可以是變數(byte、short、int、char、這4個基本數據類型的包裝類對象、String對象、枚舉對象)
- case後的值不能為變數,必須是常量
- default語句是可選的,若沒有default語句又沒有匹配上對應的case中的常量,就表示不會執行switch中的任何代碼(switch中的表達式是一定會執行的)
- 在case中的代碼不寫break,會導致case穿透
switch(表達式){ case 常量1: 代碼1; break; case 常量2: 代碼2; break; case 常量n: 代碼n; break; default: 代碼default; break; }
- 在Java中:
-
-
迴圈控制
-
for迴圈
-
for迴圈的四要素:
- 迴圈變數初始化
- 迴圈條件
- 迴圈操作(需要迴圈的代碼)
- 迴圈變數迭代
-
迴圈變數初始化、迴圈條件、迴圈變數迭代,都可以不放在for迴圈的括弧中
-
迴圈條件是一個返回布爾值的表達式,若在for迴圈中沒有寫迴圈條件,則預設為true
-
退出for迴圈的方式:
- 迴圈條件結果為false
- 執行break語句
- 執行return語句
for(迴圈變數初始化;迴圈條件;迴圈變數迭代){ 需要迴圈的代碼; }
-
-
while迴圈
while迴圈和for迴圈一樣,也有相同的迴圈四要素,只不過放的位置和for迴圈不一樣
簡單來說,可以把while迴圈看作另一種寫法的for迴圈
不必糾結寫法上的細枝末節,重點在於知道執行的代碼在哪種情況下被迴圈了幾次
迴圈變數初始化; while(迴圈條件){ 需要迴圈的代碼; 迴圈變數迭代; }
-
do…while迴圈
和while迴圈相比,do…while迴圈會先執行一次尋妖迴圈的代碼,再進行
迴圈變數初始化; do{ 需要迴圈的代碼; 迴圈變數迭代; }while(迴圈條件);
-
多重迴圈控制
將一個迴圈放在另一個迴圈中,外層迴圈和內層迴圈均可使用for迴圈、while迴圈、do…while迴圈
一般建議多重迴圈不超過3層,層數越多,代碼可讀性越差
for (int i = 0; i < 10; i++) { // 外層迴圈 for (int j = 0; j < 20; j++) { // 內層迴圈 System.out.print("*"); // 需要xun } System.out.println(); // 表示換行 }
- 經典題
-
列印 九九乘法表
for (int i = 1; i <= 9; i++) { // 表示輸出多少行 for (int j = 1; j <= i; j++) { // 表示每行輸出多少列 System.out.print(j + "*" + i + "=" + j * i); System.out.print("\t"); } System.out.println(); // 換行 }
-
列印 實心 / 空心 三角形
//1. 實心三角 for (int i = 1; i <= 6; i++) { // 表示列印多少行 for (int j = 1; j <= 6-i; j++) { // 每一列要列印的空格 System.out.print(" "); } for (int j = 1; j <= (2*i-1) ; j++) { // 每一列要列印的*號數量 System.out.print("*"); } System.out.println(); } //2. 空心三角 for (int i = 1; i <= 6; i++) { // 表示列印多少行 for (int j = 1; j <= 6-i; j++) { // 每一列要列印的空格 System.out.print(" "); } for (int j = 1; j <= (2*i-1) ; j++) { // 每一列要列印的*號數量 //當要列印的是 最後一行 或者 這一列第一個和最後一個 時,輸出* if (i==6||j==1||j==(2*i-1)){ System.out.print("*"); }else { // 其餘輸出空格 System.out.print(" "); } } System.out.println();// 換行 }
-
列印 空心 / 實心 菱形
//3. 實心菱形 for (int i = 1; i <= 12; i++) {// 控制列印的行數 if (i <= 6) { //列印菱形的上半部分 for (int j = 1; j <= (6 - i); j++) { System.out.print(" "); } for (int j = 1; j <= (2 * i - 1); j++) { System.out.print("*"); } } else { //列印菱形的下半部分 for (int j = 1; j <= (i - 6); j++) { System.out.print(" "); } for (int j = 1; j <= (2 * (12 - i) - 1); j++) { System.out.print("*"); } } System.out.println(); // 換行 } //4. 空心菱形 for (int i = 1; i <= 12; i++) {// 控制列印的行數 if (i <= 6) { //列印菱形的上半部分 //1. 先列印空格 for (int j = 1; j <= (6 - i); j++) { System.out.print(" "); } //2. 列印邊框和內部空格 for (int j = 1; j <= (2*i-1) ; j++) { if (j == 1 || j == (2 * i - 1)){ System.out.print("*"); }else { System.out.print(" "); } } } else { //列印菱形的下部分 //1. 列印空格 for (int j = 1; j <= (i - 6); j++) { System.out.print(" "); } //2. 列印邊框和內部空格 for (int j = 1; j <= (2 * (12 - i) - 1); j++) { if (j == 1 || j == (2 * (12 - i) - 1)){ System.out.print("*"); }else { System.out.print(" "); } } } System.out.println(); // 換行 }
-
- 經典題
-
break跳轉控制語句
break語句用於終止某個語句塊的執行,一般使用在switch或者迴圈[for,while,do-while]中
//使用方式1 for (int i = 0; i < 10; i++) { if (i == 5) { break; // 退出最近的某個迴圈體 } } //使用方式2 label1: for (int i = 0; i < 10; i++) { label2: for (int j = 0; j < 10; j++) { if (j == 5) { break label1; // 退出了label1標簽所標識的for迴圈 } System.out.println("j -> " + j); } }
-
continue跳轉控制語句
continue語句用於結束本次迴圈,繼續執行下一次迴圈。
//使用方式1 for (int i = 0; i < 10; i++) { //當i=5時,不會執行後續的輸出語句,而是進入下一次迴圈 if (i == 5) { continue; // 結束本次迴圈,進入下一次迴圈 } System.out.println("i -> " + i); } //使用方式2 label1: for (int i = 0; i < 4; j++) { label2: for (int j = 0; j < 10; i++) { if (i == 5) { continue label1; // 執行下一次 label1 標簽所指的For迴圈(for i的迴圈) } System.out.println("j = " + j + "\t"); } }
-
return跳轉控制語句
return 使用在方法,表示跳出所在的方法,意味著所在方法算為執行完畢
return寫在主方法中,則會退出Java程式
-
易錯陷阱題
-
求出1-1/2+1/3-1/4..1/100的和
易錯點:
-
1/2 在Java中等於0,需要使用1.0/2才能得到0.5
所以在做
/
號時,除數與被除數必須有一個為浮點型
double res = 0.0; double temp = 0.0; for (double i = 1.0; i <= 100; i++) { if (i%2==0){ //符號為負 temp = -(1/i); }else { //符號為正 temp = 1/i; } res += temp; } System.out.println(res);
-
-
-
Java數組
-
簡介
數組可以存放多個同一類型的數據
數組也是一種數據類型,是引用類型
當數組存儲基本數據類型時,預設值為基本數據類型對應的預設值
當數組存儲引用數據類型(如:包裝類對象、String類對象)時,預設值為null
數據就是在記憶體空間中連續存儲的同一類型數據
數組索引從0開始
註意:數組是引用數據類型,變數中存儲的是數組在堆中的地址
-
數組的幾種初始化方式
-
方式一
double[] a = new double[5]; // 直接分配記憶體空間 a[0] = 1d; double b[]; // 先聲明 b = new double[5]; // 再分配記憶體空間 b[0] = 2d; // double[] a 和 double a[] 是一樣的,建議使用 double[]
-
方式二
double[] c = {1.0,2.0,3.0}; // 靜態初始化
-
-
數組反轉
有很多方法,比如:
- 開闢一個同樣長度的數組,便利舊數組,在新數組中從後往前放值
- 數組第一個和最後一個進行交換位置,第二個和倒數第二個,直到數組完全反轉
Integer[] arrayInter = {1, 2, 3, 4, 5, 6, 7}; for (int i = 0; i < arrayInter.length / 2; i++) { Integer temp = arrayInter[i]; arrayInter[i] = arrayInter[arrayInter.length - 1 - i]; arrayInter[arrayInter.length - 1 - i] = temp; } for (int i = 0; i < arrayInter.length; i++) { System.out.print(arrayInter[i] + "\t"); }
-
數組排序/查找
有多種數組排序和查找的演算法,這裡演示冒泡排序
-
冒泡排序
冒泡排序(Bubble Sorting)的基本思想是:通過對待排序序列從後向前(從下標較大的元素開始),依次比較相鄰元素的值,若發現逆序則交換,使值較大的元素逐漸從前移向後部,就象水底下的氣泡一樣逐漸向上冒。
舉例:(從小到大)
int[] arrayInt = {41, 23, 65, 21, 78, 45}; int temp = 0; for (int i = 0; i < arrayInt.length - 1; i++) { for (int j = 0; j < arrayInt.length - 1 - i; j++) { if (arrayInt[j] > arrayInt[j+1]){ temp = arrayInt[j]; arrayInt[j] = arrayInt[j+1]; arrayInt[j+1] = temp; } } } for (int i = 0; i < arrayInt.length; i++) { System.out.print(arrayInt[i] + "\t"); }
-
二分查找
在演算法筆記中
-
-
二維數組
-
幾種定義方式
int[][] a
、int[] a[]
、int a[][]
都可以,但建議使用int[][] a
-
方式一
int[][] a = new int[2][3]; int[][] b; b = new int[2][3];
-
方式二
int[][] a = new int[10][]; a[1] = new int[1]; a[2] = new int[2]; a[3] = new int[3];
-
方式二
int[][] a = { {1,2,3,4}, {1,2,3}, {1,2}, {1} };
-
-
易錯點
int[] x,y[]; // x是一維數組,y是二維數組
// 初學時會有點搞混匿名內部類和數組靜態初始化的代碼 //1. 數組的靜態初始化 String[] arrayStr = new String[]{"a","b"}; //2. 匿名內部類 String arrayStr = new String(){...}; // 註意: 這裡編譯會報錯,因為String為final類 (為了通過編譯報錯來確定這是創建匿名內部類的方式)
-
Java面向對象
-
Java對象在記憶體中的存在形式(簡化)
class Cat { private String color; private String name; private int age; public Cat(String color, String name, int age) { this.color = color; this.name = name; this.age = age; } } public static void main(String[] args) { Cat cat = new Cat(new String("blue"), "tom", 6); }
-
JVM記憶體結構的簡單分析
- 棧:存儲基本數據類型(局部變數)
- 堆:存儲對象
- 方法區:常量池,類文件
-
Java創建對象的流程(簡化)
- 先載入對應類的信息(載入屬性和方法信息,且只會載入一次)
- 在堆中分配空間,進行預設初始化
- 將對象在堆中的地址賦值給棧中的局部變數
-
成員方法傳參機制
本質:都是值傳遞,即 拷貝一份副本然後將副本值傳遞給目標方法
方法返回值也是同理,本質都是值拷貝,返回的是基本數據類型時拷貝的就是其值,返回的是引用數據類型時拷貝的是就是其在堆記憶體中的地址
-
方法遞歸調用
在演算法筆記中有較為詳細的介紹
- 使用遞歸的重要知識點
-
執行一個方法時,就會創建一個新的受保護的獨立空間(棧空間)
方法的局部變數是獨立的,不會相互影響
-
如果方法中使用的是引用類型變數(比如數組,對象),就會共用該引用類型的數據。
-
遞歸必須向退出遞歸的條件逼近,否則就是無限遞歸,出現 StackOverflowError 棧溢出,即棧空間被撐爆了
-
當一個方法執行完畢,或者遇到return就會返回,遵守誰調用,就將結果返回給誰
-
- 使用遞歸的重要知識點
-
方法重載
java 中允許同一個類中,多個同名方法的存在,但要求 形參列表不一致
- 重載的好處
- 減輕了 起名 、記名 的麻煩
- 重載的要求
- 方法名 必須 相同
- 形參列表 必須 不同(類型、個數、順序 至少一個不同)(形參名無要求)
- 返回類型 沒有要求
- 重載的好處
-
方法重寫
方法重寫(覆蓋)就是子類有一個方法,和父類的某個方法的名稱、返回類型、參數一樣,那麼我們就說子類的這個方法重寫了父類的方法
- 達成方法重寫的要求
- 子類方法的 形參列表、方法名 必須 要和父類方法的 形參列表、方法名 完全一致
- 子類方法的 返回類型,必須 要和 父類方法的返回類型一致 或 是父類方法返回類型的子類
- 子類方法 不能 縮小父類方法的訪問許可權(public > protected > 預設 > private)
- 達成方法重寫的要求
-
可變參數
java 允許將同一個類中多個同名同功能但參數個數不同的方法,通過可變參數的形式,封裝成一個方法
簡單點就是說方法可以傳入無限多個指定類型的參數值
public String study(String... arrayStr){ String str = ""; for (int i = 0; i < arrayStr.length; i++) { str += arrayStr[i]; } return str; }
- 一些細節
- 可變參數的實參可以是 0個 或 任意多個
- 可變參數的本質 就是 數組
- 可變參數 和 普通參數 一起放在形參列表時,必須保證 可變參數類型 放在最後
- 一個形參列表中只能出現一個可變參數
- 一些細節
-
作用域
-
Java中作用域的分類
-
全局變數:也就是屬性,作用域為整個類體
全局變數(屬性)可以不賦值,就可以直接使用,因為有預設值
-
局部變數:除了屬性之外的其他變數,作用域為定義它的代碼塊中
局部變數沒有預設值,必須賦值後使用
-
-
一些細節
-
屬性和局部變數可以重名,訪問時遵循就近原則
-
在同一個作用域中,比如在同一個成員方法中,兩個局部變數,不能重名
-
生命周期不同
-
屬性生命周期較長,伴隨著對象的創建而創建,伴隨著對象的銷毀而銷毀
-
局部變數,生命周期較短,伴隨著它的代碼塊的執行而創建,伴隨著代碼塊的結束而銷毀
即:在一次方法調用過程中
-
-
作用域範圍不同
- 全局變數/屬性:可以被本類使用,或其他類使用(通過對象調用)
- 局部變數:只能在本類中對應的方法中使用
-
修飾符不同
- 全局變數/屬性可以加修飾符
- 局部變數不可以加修飾符
-
-
-
構造器
構造方法又叫構造器(constructor),是類的一種特殊的方法,它的主要作用是完成對新對象的初始化
-
特點
- 方法名 和 類名 相同
- 沒有返回值
- 在創建對象時,系統會自動的調用該類的構造器完成對象的初始化
-
一些細節
-
一個類可以定義多個不同的構造器,即構造器重載
-
構造器是用來完成對象初始化的,不是用於創建對象的
-
在創建對象時,系統自動的調用該類的構造方法
-
如果程式員沒有定義構造器,系統會自動給類生成一個預設無參構造器(預設構造器)
-
一旦定義了自己的構造器,預設的構造器就覆蓋了
即:不能再使用預設的參構造器,除非顯式的定義一下
-
-
-
this關鍵字
Java虛擬機會給每個對象分配this,代表當前對象
即:每個實例都有一個隱藏的變數this,存儲了該對象在堆記憶體中的地址
-
JVM記憶體圖(簡化)
Cat cat = new Cat( new String("blue"), // 堆中String對象的地址 "tom", // 常量池中的地址 6 // 基本數據類型的值 );
-
一些註意事項
-
this 不能在類定義的外部使用,只能在類定義的方法中使用
-
this 關鍵字可以用來訪問本類的屬性、方法、構造器
-
this 用於區分當前類的屬性和局部變數
-
訪問成員方法的語法:this.方法名(參數列表)
-
訪問構造器語法:this(參數列表)
即只能在構造器中訪問另外一個構造器,且必須放在第一條語句
this() 和 super() 都需要放在構造器的第一行,所以無法同時使用
-
在哪個對象的方法中調用,this就代表哪個對象
看的不是編譯類型,而是運行類型。
後期框架中使用了大量的設計模式,導致了很難判斷this到底是指的哪個對象,最好的辦法是使用IDEA的debug
-
-
-
訪問修飾符
java 提供四種訪問控制修飾符號,用於控制方法和屬性(成員變數)的訪問許可權(範圍)
-
四種訪問修飾符
訪問級別 訪問修飾符 同類 同包 子類 不同包 公開 public √ √ √ √ 受保護 protected √ √ √ × 預設 沒有修飾符 √ √ × × 私有 private √ × × × -
實際開發中,當某個方法需要 A類 的實例傳入,但 A類 的 構造方法的protected
此時,就可以使用匿名內部類的方式
package com.test; // 註意和 MainApp類 在不同包下 public class A { protected A(){} }
package com.zhumei; // 註意和 A類 在不同包下 import com.test.A; public class MainApp { public static void main(String[] args) { MainApp mainApp = new MainApp(); mainApp.test01(new A(){}); // new A(){} 相當於創建了一個 MainApp$1 extends A 類(繼承了A類),這樣就突破了protected修飾符導致的無法使用不同包下類的構造器 } public void test01(A a){ System.out.println(a.getClass()); } }
-
使用修飾符的註意事項
-
修飾符可以用來修飾類中的屬性,成員方法以及類
-
只有 預設修飾符 和 public修飾符 才能修飾類
-
子類繼承了父類的方法、欄位後,調用父類的方法、欄位需要遵守父類設定的訪問修飾符的規則
即:父類設置為private的欄位、方法,子類無法通過正常方式進行訪問使用(可以使用反射爆破的方式強行使用)
-
-
-
面向對象三大特征
-
封裝
封裝(encapsulation)就是把抽象出的數據[屬性]和對數據的操作(方法)封裝在一起
數據被保護在內部,程式的其它部分只有通過被授權的操作(方法),才能對數據進行操作
- 封裝的好處
- 隱藏具體的實現細節
- 可以對封裝的數據進行校驗
- 封裝的實現步驟
- 將數據私有化(private)
- 提供一個公共的(public)set方法,對外提供修改數據的方式,也可對外界提供的數據進行校驗
- 提供一個公共的(public)get方法,對外提供獲取數據的方式,也可對外界進行許可權判斷來不同的值
- 封裝的好處
-
繼承
繼承可以解決代碼復用,讓我們的編程更加靠近人類思維
當多個類存在相同的屬性和方法時,可以從這些類中抽象出父類
在父類中定義這些相同的屬性和方法,所有的子類不需要重新定義這些屬性和方法,只需要通過 extends 來聲明繼承父類即可
-
super關鍵字
super代表父類的引用,用於訪問父類的屬性、方法、構造器(但受到父類訪問修飾符的限定)
註意:
- super的訪問不限於直接父類,而是所有繼承的類,使用super若在父類中找不到會去找父類的父類,直到找到為止 或 所有的父類均沒有而報錯
- 若在父類中找到了,但由於修飾符關係無法直接訪問,則會直接報錯,而不會繼續向更上層的父類進行尋找
-
一些細節:
-
子類會擁有父類的所有信息(欄位、方法),但是會受到父類訪問修飾符的影響,對父類私有、預設訪問修飾符修飾的欄位和方法都無法直接使用
可以通過父類提供的其他可訪問方法來間接獲取、修改、使用欄位或方法
-
final類無法被繼承
-
子類必須調用父類的構造器,完成父類的初始化
當創建子類對象時,不管使用子類的哪個構造器,預設情況 下總會去調用父類的無參構造器
如果父類沒有提供無參構造器,則必須在子類的構造器中用 super 去指定使用父類的哪個構造器完成對父類的初始化工作,否則編譯不通過
-
-
如果希望指定去調用父類的某個構造器,則顯式的調用一下 : super(參數列表)
-
super(參數列表)在使用時必須放在構造器第一行
super() 和 this() 都只能放在構造器第一行,因此這兩個方法不能共存在一個構造器
-
this 和 super
public class MainApp { public static void main(String[] args) { B b = new B(); } }
class A{ public A(){ System.out.println("A -> null"); // 無參構造器 } public A(String str){ System.out.println("A -> String"); // 有參構造器 } } class B extends A{ public B(){ this("str"); System.out.println("B -> null"); // 無參構造器 } public B(String str){ super(str); System.out.println("B -> String"); // 有參構造器 } }
輸出:
A -> String
B -> String
B -> null
-
-
java 所有類都是 Object 類的子類, Object 是所有類的基類
-
父類構造器的調用不限於直接父類,將一直往上追溯直到 Object 類(頂級父類)
-
子類最多只能繼承一個父類(指直接繼承),即 java 中是單繼承機制
-
不能濫用繼承,子類和父類之間必須滿足 is-a 的邏輯關係
-
-
創建繼承類實例時,JVM記憶體分析(簡化)
class GrandPa{ String name = "大頭爺爺"; protected int age = 70; } class Father extends GrandPa{ String name = "大頭爸爸"; private String job = "程式員"; public String getJob(){ return this.job; } } class Son extends Father{ String name = "大頭兒子"; }
public class MainApp { public static void main(String[] args) { Son son = new Son(); // 研究創建一個有繼承關係的類的實例時,JVM中的記憶體分佈圖(簡化) } }
創建實例的流程(簡化):
-
首先會載入類信息進入到記憶體中
先從頂級父類開始載入,比如這裡會先載入 Object類,然後是 GrandPa類,接著是 Father類,最後是 Son類
-
在堆中開闢一個空間用於存儲son實例
這個空間會分成幾個部分,用於存儲從不同類中繼承而來的信息
比如這裡主要會划出3個關鍵的部分:Son類、Father類、GrandPa類,每個部分都相互獨立,即使有相同的欄位名也不會造成衝突
-
將堆中的地址賦值給在棧中的變數
-
-
註意:
若父類方法中有獲取欄位值的行為,該值只會去父類本身或父類的父類去找,並不會從子類中向上找
public class MainApp { public static void main(String[] args) { B b = new B(); b.study1(); // 輸出的是10 } } class A { public int number = 10; public void study() { System.out.println(this.number); // this.number 會去本類(A類),以及Object類中尋找 (若A類中沒有的話) } } class B extends A { public int number = 120; }
-
-
多態
方法或對象具有多種形態
比如說你可以是大學生的同時,也可以是高中生的課外輔導班老師,具體是什麼身份要看在哪一種場景中做什麼事情
多態是面向對象的第三大特征,多態是建立在封裝和繼承基礎之上的
-
實現多態的三大條件
- 必須在繼承體系下
- 子類必須對父類中的方法進行重寫
- 通過父類的引用調用重寫的方法
-
方法的多態
重寫和重載就是多態的體現
調用的方法名是同一個,但是會根據 調用方的對象不同、傳入的參數不同 導致最終調用的方法也不同
-
重載:在調用方法時,傳入不同的參數,就會調用不同的方法
-
重寫:在調用方式時,根據對象的不同,就會調用不同的方法
-
-
對象的多態
一個編譯類型能夠表達多個不同的運行類型,真正運行時根據真正的運行類型來確定運行的結果,這就體現了多態
class A { public String study() { return "a"; } } class B extends A { @Override public String study() { return "b"; } } public class MainApp { public static void main(String[] args) { //這就是多態的體現,編譯類型A既可以表達A實例對象也可以表達B實例對象 A a = new B(); // A是編譯類型,B是運行類型 String str = a.study(); } }
-
多態的向上轉型
父類的引用指向了子類的對象
即:子類的對象地址,保存在了父類類型的變數中
通過向上轉型,子類可以調用父類中的所有成員(但要遵守訪問許可權的限制),但是不能調用子類的特有成員
最終的運行效果,仍然是看子類的具體實現,從運行類型開始查找然後調用,與之前繼承的實例方法調用一致
class A { public String study() {return "a";} } class B extends A { @Override public String study() {return "b";} } public class MainApp { public static void main(String[] args) { // 父類的引用指向了子類的對象 A a = new B(); // A是編譯類型,B是運行類型 } }
-
多態的向下轉型
將父類的引用交給子類的類型
即:原本父類變數中保存的是子類對象的地址,現在將地址交給子類變數
只能強轉父類的引用,不能強轉父類的對象
要求父類的引用必須指向的是當前目標類型的對象
當向下轉型後,可以調用子類類型中所有的成員
class A { public String study() {return "a";} } class B extends A { @Override public String study() {return "b";} } public class MainApp { public static void main(String[] args) { // 父類的引用指向了子類的對象 A a = new B(); // A是編譯類型,B是運行類型 // 父類的引用交給子類的類型(向下轉型) B b = (B)a; // B是編譯類型,B是運行類型 } }
-
-
Java的動態綁定機制
- 當調用對象方法時,該方法會和該對象的記憶體地址(運行類型)綁定
- 當調用對象屬性時,沒有動態綁定機制,哪裡聲明哪裡使用
public class MainApp { public static void main(String[] args) { A a = new B(); System.out.println(a.study()); // 動態綁定機制,先去 子類(B類) 中尋找sutdy方法,沒找到再去父類中找 System.out.println(a.number); // 直接在編譯類型A類(或A類的父類)中尋找對應的欄位,欄位(屬性)沒有動態綁定機制 } } class A { public int number = 10; public String study() { return "a"; } } class B extends A { public int number = 120; @Override public String study() { return "b"; } }
-
多態的常見應用
-
多態數組
public class MainApp { public static void main(String[] args) { Person[] person = new Person[3]; person[0] = new Student(); // 編譯類型 -> Person ; 運行類型 -> Student person[1] = new Teacher(); // 編譯類型 -> Person ; 運行類型 -> Teacher person[2] = new Student(); // 編譯類型 -> Person ; 運行類型 -> Student for (int i = 0; i < person.length; i++) { String name = person[i].name(); System.out.println(name); } } } interface Person{ String name(); } class Student implements Person{ @Override public String name() {return "學生";} } class Teacher implements Person{ @Override public String name() {return "教師";} }
-
多態參數
public class MainApp { public static void main(String[] args) { test(new Teacher()); test(new Student()); } public static void test(Person person){ String name = person.name(); // name方法的具體運行邏輯是根據運行類型決定的 System.out.println(name); } } interface Person{ String name(); } class Student implements Person{ @Override public String name() { return "學生"; } } class Teacher implements Person{ @Override public String name() { return "教師"; } }
-
-
-
-
類變數、類方法 (static)
-
類變數
類變數也叫靜態變數/靜態屬性,是該類的所有對象共用的變數
-