前端異常處理
- 「MoreThanJava」 宣揚的是 「學習,不止 CODE」,本系列 Java 基礎教程是自己在結合各方面的知識之後,對 Java 基礎的一個總回顧,旨在 「幫助新朋友快速高質量的學習」。
- 當然 不論新老朋友 我相信您都可以 從中獲益。如果覺得 「不錯」 的朋友,歡迎 「關注 + 留言 + 分享」,文末有完整的獲取連結,您的支援是我前進的最大的動力!
Part 1. 資料型別
假設您遇到了以下撕碎的紙片,您覺得會是什麼意思?
在不瞭解上下文的情況下,很難說出 MIX
的含義,它可能是羅馬數字 1009
,也可以是英語單詞 mix
或者別的什麼東西。
在不知道上下文的情況下,一串字母沒有任何意義。
與一串字母一樣,一串 01
的含義取決於如何使用。而決定這一串資料如何使用的方案被稱為其 資料型別 (跟檔案型別有些類似)。
8 種基本資料型別
Java 是一種強型別語言。這意味著必須為每一條資料宣告一種型別。在 Java 中共有 8
種基本型別 (primitive type),其中 4
種整型、2
種浮點型別、1
種字元型別、1
種表示真值的 boolean
(布林) 型別。
整型
整型被用來表示沒有小數部分的數值,允許是負數。Java 提供了 4
種整型,具體內容如下:
型別 | 儲存需求 | 取值範圍 |
---|---|---|
int | 4 位元組 | -2147483648 ~ 2147483647 (剛超過 20 32=4位元組*每個位元組8位 ) |
short | 2 位元組 | -32768 ~ 32767 |
long | 8 位元組 | -9223372036854775808 ~ 9223372036854775807 |
byte | 1 位元組 | -128 ~ 127 |
在通常情況下,int
型別最常用。但如果想要表示整個地球的居住人口,那麼就需要使用 long
型別了。byte
和 short
型別主要用於特定的應用場合,例如,底層的檔案處理或者儲存空間很寶貴時的大陣列 (因為節約記憶體)。
浮點型別
浮點型別用於表示有小數部分的數值。在 Java 中有兩種浮點型別,具體內容如下:
型別 | 儲存需求 | 取值範圍 |
---|---|---|
float | 4 位元組 | 大約 ±3.40282347E + 38F (大約有效數為 6 ~ 7) |
double | 8 位元組 | 大約 ±1.7976931486231580E + 308 (大約有效數為 15 位) |
double
表示這種型別的數值精度是 float
型別的兩倍 (也有人稱 double
為雙精度數值)。
浮點數精度問題
問一個問題:0.1 + 0.2 = ?
先別奇怪,在 IDEA 中嘗試著輸出一下這句話就知道了:
System.out.println(0.1 + 0.2);
// 輸出:0.30000000000000004
0.1 + 0.2
為什麼會等於 0.30000000000000004
?而不是我們想象中的 0.3
?
這不是因為它們在計算時出現了錯誤,而是因為浮點數計算標準的要求。
首先我們要明確一點:程式設計中的浮點數並不能和數學中的小數看做同一個東西。
- 程式設計中的浮點數的精度往往都是有限的,單精度的浮點數使用
32
位表示,而雙精度的浮點數使用64
位表示; - 數學中的小數系統可以通過引入無限序列....可以表示任意的實數;
請考慮使用 十進位制 表示 1/3
:
0.3333333333333333....
如果想要完整地表達 1/3
的精度,那麼小數點之後的 3
需要無限地寫下去。如果需要讓你在一張紙上表達清晰,顯然由於紙張大小的限制你無法無限地寫下去...
0.1
和 0.2
在 二進位制 中同 1/3
在 十進位制 中一樣,不屬於整數的範疇,所以只能用近似值來代替,由於精度的限制 0.1
和 0.2
使用單精度浮點數表示的實際值為:0.100000001490116119384765625
和 0.20000000298023223876953125
,把它們相加起來得到的結果與我們在一開始看到的非常相似:
在交易系統或者科學計算的場景中,如果需要更高的精度小數,可以使用具有 28
個有效位數的 decimal
或者直接使用分數,不過這些表示方法的開銷也隨著有效位數的增加而提高,我們應該按照需要選擇最合適的方法。
重新回到最開始的問題 — 0.1
和 0.2
相加不等於 0.3
的原因包括以下兩個:
- 使用二進位製表達十進位制的小數時,某些數字無法被有限位的二進位制小數表示;
- 單精度和雙精度的浮點數只包括
7
位或者15
位的有效小數位,儲存需要無限位表示的小數時只能儲存近似值;
在使用單精度和雙精度浮點數時也應該牢記它們只有 7
位和 15
位的有效位數。
char 型別
char
用來表示單個字元。在 Java 中 char
型別的資料使用 16
位來儲存和表示,而許多程式語言則僅用 8
位。
char
型別的字面量值需要用 單引號 括起來。例如:'A'
是編碼值為 65
的 字元常量,它與 "A"
不同,"A"
是僅包含字元 A
的 字串 (String 型別)。
強烈建議:不要在程式中使用 char
型別,除非您確實需要處理 UTF-16
程式碼單元。
(更多相關資料放入了下面的自取資料,感興趣可以去閱讀一下更多 char 型別的東西,不感興趣跳過即可...)
boolean 型別
boolean
(布林) 型別有兩個值:false
和 true
(注意這兩個是布林型別的字面常量也是保留字),用來判斷邏輯條件是否成立。
物件型別
Java 中的所有資料都屬於「基本資料型別」或「物件」中的一種。
雖然只有八種基本資料型別,但 Java 有許多滿足您需求的相關型別的物件供您使用,例如,表示字串的 String
型別。
我們會在之後的內容中更多地討論物件 (因為 Java 是一種面向物件的程式語言),現在,您需要了解以下資訊:
- 基本資料型別使用少量的固定位元組數 (下面會詳細介紹);
- 只有
8
種基本資料型別,您無法建立新的原始資料型別; - 一個物件是一個較大的資料塊,可能使用很多位元組的記憶體;
- 物件型別的資料被稱為 類;
- Java 中已經封裝了足夠多的類用來滿足您各類的需求,您也可以發明新的類來滿足程式的特定需求;
Part 2. 變數
計算機記憶體中有數以十億計的位元組用於儲存機器指令和資料。
程式執行時,某些記憶體用於儲存機器指令,而另外一些則用於儲存資料。後來,當另一個程式執行時,以前儲存的機器指令中的某些位元組現在可以用來儲存資料,而之前儲存的資料中的某些位元組現在可以儲存機器指令。
計算機先驅 約翰·馮·諾依曼 (John von Neumann) 的想法是:使用相同的儲存器來儲存指令和資料。
回想一下,資料型別是一種使用位模式來表示值的方案。
可以把 變數 視為一個由一個或多個位元組組成的小盒子,該盒子可以使用特定的資料型別儲存值。
要將值儲存在記憶體中,以後再取回它,則程式必須為每個變數指定一個名稱,如 className
/ payAmount
(變數名採用小駝峰命名法)。
變數隨執行程式的需要而變化。當正在執行的程式不再需要變數時,該記憶體部分可用於其他目的。
宣告變數
在 Java 中,每個變數都必須有一個型別 (type)。在宣告變數時,需要先指定變數的型別,然後是變數名:
double salary;
int vacationDays;
long earthPopluation;
boolean finished;
可以看到,每個變數都以分號 (;
) 結束。由於宣告是一條完整的 Java 語句,而所有的 Java 語句都以分號結束,所以這裡的分號是必須的。
變數命名
在 Java 中,變數命名需要遵循以下硬性規定和強烈建議遵守的非硬性規定:
硬性規則
- 變數名必須是一個以字母開頭並由字母或數字構成的序列 (儘管
$
是合法的,但不要在你自己的程式碼中使用這個字元,它只用在 Java 編譯器或其他工具生成的名字中); - 每一個字元都有意義,且大小寫敏感;
- 不要使用 Java 中的保留字;
《阿里巴巴 Java 開發手冊》規則
- 【強制】 程式碼中的命名 (所有識別符號) 均不能以下劃線或美元符號開始,也不能以下劃線或美元符號結束;(反例:
$name
) - 【強制] 程式碼中的命名嚴禁使用拼音與英文混合的方式,更不允許直接使用中文的方式;(反例:
DaZhePromotion
- 打折) - 【強制】 杜絕完全不規範的縮寫,避免詞不達意;(反例:
condition
縮寫成condi
) - 【推薦】 為了達到程式碼自解釋的目的,任何自定義程式設計元素在命名時,使用盡量完整的單片語合來表達其意;(反例:
int a;
的隨意命名方式)
變數初始化
宣告一個變數之後,必須用賦值語句對變數進行顯式初始化,千萬不要使用未初始化的變數的值。例如,Java 編譯器認為下面的語句序列是錯誤的:
int amount;
System.out.println(amount); // ERROR -- variable not initialized
要相對一個已經宣告過的變數進行賦值,就需要將變數名放在等號 (=
) 左側,再把一個適當取值放在等號的右側:
int amount;
amount = 12;
也可以將變數的宣告和初始化放在同一行中。例如:
int amount = 12;
最後,在 Java 中可以將宣告放在程式碼中的任何地方。
但讓變數儘可能地靠近變數第一次使用的地方,這是一種良好的程式編寫風格。
變數使用範例
我們來使用變數完成兩個數的加減乘除:
int num1 = 2;
int num2 = 3;
System.out.println(num1 + num2);
System.out.println(num1 - num2);
System.out.println(num1 * num2);
System.out.println(num1 / num2);
Part 3. 運運算元
計算機的最基本用途之一就是執行數學運算,作為一門計算機語言,Java 也提供了一套豐富的運運算元來操縱變數。我們可以把運運算元分成以下幾組:
- 算術運運算元;
- 關係運算子;
- 位運運算元;
- 邏輯運運算元;
- 賦值運運算元;
- 其他運運算元;
算術運運算元
算術運運算元用在數學表示式中,它們的作用和在數學中的作用一樣。下表列出了所有的算術運運算元。
表格中的例項假設整數變數 A
的值為 10
,變數 B
的值為 20
:
例項
下面的簡單示例程式演示了算術運運算元。複製並貼上下面的 Java 程式並儲存為 Test.java
檔案,然後編譯並執行這個程式:
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 25;
int d = 25;
System.out.println("a + b = " + (a + b));
System.out.println("a - b = " + (a - b));
System.out.println("a * b = " + (a * b));
System.out.println("b / a = " + (b / a));
System.out.println("b % a = " + (b % a));
System.out.println("c % a = " + (c % a));
System.out.println("a++ = " + (a++));
System.out.println("a-- = " + (a--));
// 檢視 d++ 與 ++d 的不同
System.out.println("d++ = " + (d++));
System.out.println("++d = " + (++d));
}
}
執行結果:
a + b = 30
a - b = -10
a * b = 200
b / a = 2
b % a = 0
c % a = 5
a++ = 10
a-- = 11
d++ = 25
++d = 27
i++ 與 ++i 到底有什麼不同?
實際上,不管是前置 ++
,還是後置 ++
,都是先將變數的值加 1
,然後才繼續計算的。
二者之間真正的區別是:前置 ++
是將變數的值加 1
後,使用增值後的變數進行運算的,而後置 ++
是首先將變數賦值給一個臨時變數,接下來對變數的值加 1
,然後使用那個臨時變數進行運算。
關係運算子
下表為Java支援的關係運算子。
表格中的例項整數變數 A
的值為 10
,變數 B
的值為 20
:
例項
下面的簡單示例程式演示了關係運算子。複製並貼上下面的 Java 程式並儲存為 Test.java
檔案,然後編譯並執行這個程式:
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println("a == b = " + (a == b));
System.out.println("a != b = " + (a != b));
System.out.println("a > b = " + (a > b));
System.out.println("a < b = " + (a < b));
System.out.println("b >= a = " + (b >= a));
System.out.println("b <= a = " + (b <= a));
}
}
執行結果:
a == b = false
a != b = true
a > b = false
a < b = true
b >= a = true
b <= a = false
位運運算元
Java定義了位運運算元,應用於整數型別 (int),長整型 (long),短整型 (short),字元型 (char),和位元組型 (byte) 等型別。
位運運算元作用在所有的位上,並且按位運算。假設 a = 60,b = 13;
它們的二進位制格式表示將如下:
A = 0011 1100
B = 0000 1101
-----------------
A&B = 0000 1100
A | B = 0011 1101
A ^ B = 0011 0001
~A = 1100 0011
下表列出了位運運算元的基本運算,假設整數變數 A
的值為 60
和變數 B
的值為 13
:
例項
下面的簡單示例程式演示了關係運算子。複製並貼上下面的 Java 程式並儲存為 Test.java
檔案,然後編譯並執行這個程式:
public class Test {
public static void main(String[] args) {
int a = 60; /* 60 = 0011 1100 */
int b = 13; /* 13 = 0000 1101 */
int c = 0;
c = a & b; /* 12 = 0000 1100 */
System.out.println("a & b = " + c);
c = a | b; /* 61 = 0011 1101 */
System.out.println("a | b = " + c);
c = a ^ b; /* 49 = 0011 0001 */
System.out.println("a ^ b = " + c);
c = ~a; /*-61 = 1100 0011 */
System.out.println("~a = " + c);
c = a << 2; /* 240 = 1111 0000 */
System.out.println("a << 2 = " + c);
c = a >> 2; /* 15 = 1111 */
System.out.println("a >> 2 = " + c);
c = a >>> 2; /* 15 = 0000 1111 */
System.out.println("a >>> 2 = " + c);
}
}
執行結果:
a & b = 12
a | b = 61
a ^ b = 49
~a = -61
a << 2 = 240
a >> 2 = 15
a >>> 2 = 15
邏輯運運算元
下表列出了邏輯運運算元的基本運算,假設布林變數 A
為真,變數 B
為假:
例項
下面的簡單示例程式演示了關係運算子。複製並貼上下面的 Java 程式並儲存為 Test.java
檔案,然後編譯並執行這個程式:
public class Test {
public static void main(String[] args) {
boolean a = true;
boolean b = false;
System.out.println("a && b = " + (a && b));
System.out.println("a || b = " + (a || b));
System.out.println("!(a && b) = " + !(a && b));
}
}
執行結果:
a && b = false
a || b = true
!(a && b) = true
短路邏輯運運算元
當使用與邏輯運運算元時,在兩個運算元都為 true
時,結果才為 true
,但是當得到第一個操作為 false
時,其結果就必定是 false
,這時候就不會再判斷第二個操作了。
事實上,如果所有的邏輯表示式都有一部分不必計算,那將獲得潛在的效能提升。
例項:
public class LuoJi {
public static void main(String[] args) {
int a = 5;//定義一個變數;
boolean b = (a < 4) && (a++ < 10);
System.out.println("使用短路邏輯運運算元的結果為" + b);
System.out.println("a的結果為" + a);
}
}
執行結果:
使用短路邏輯運運算元的結果為false
a的結果為5
賦值運運算元
下面是 Java 語言支援的賦值運運算元:
例項
下面的簡單示例程式演示了關係運算子。複製並貼上下面的 Java 程式並儲存為 Test.java
檔案,然後編譯並執行這個程式:
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 0;
c = a + b;
System.out.println("c = a + b = " + c);
c += a;
System.out.println("c += a = " + c);
c -= a;
System.out.println("c -= a = " + c);
c *= a;
System.out.println("c *= a = " + c);
a = 10;
c = 15;
c /= a;
System.out.println("c /= a = " + c);
a = 10;
c = 15;
c %= a;
System.out.println("c %= a = " + c);
c <<= 2;
System.out.println("c <<= 2 = " + c);
c >>= 2;
System.out.println("c >>= 2 = " + c);
c >>= 2;
System.out.println("c >>= 2 = " + c);
c &= a;
System.out.println("c &= a = " + c);
c ^= a;
System.out.println("c ^= a = " + c);
c |= a;
System.out.println("c |= a = " + c);
}
}
執行結果:
c = a + b = 30
c += a = 40
c -= a = 30
c *= a = 300
c /= a = 1
c %= a = 5
c <<= 2 = 20
c >>= 2 = 5
c >>= 2 = 1
c &= a = 0
c ^= a = 10
c |= a = 10
其他運運算元
條件運運算元 (?
條件運運算元也被稱為三元運運算元。該運運算元有 3
個運算元,並且需要判斷布林表示式的值。該運運算元的主要是決定哪個值應該賦值給變數。
variable x = (expression) ? value if true : value if false
例項
下面的簡單示例程式演示了關係運算子。複製並貼上下面的 Java 程式並儲存為 Test.java
檔案,然後編譯並執行這個程式:
public class Test {
public static void main(String[] args) {
int a, b;
a = 10;
// 如果 a 等於 1 成立,則設定 b 為 20,否則為 30
b = (a == 1) ? 20 : 30;
System.out.println("Value of b is : " + b);
// 如果 a 等於 10 成立,則設定 b 為 20,否則為 30
b = (a == 10) ? 20 : 30;
System.out.println("Value of b is : " + b);
}
}
執行結果:
Value of b is : 30
Value of b is : 20
instanceof 運運算元
該運運算元用於操作物件例項,檢查該物件是否是一個特定型別(型別別或介面型別)。
instanceof
運運算元使用格式如下:
( Object reference variable ) instanceof (class/interface type)
如果運運算元左側變數所指的物件,是操作符右側類或介面 (class/interface) 的一個物件,那麼結果為真。
下面是一個例子:
String name = "James";
boolean result = name instanceof String; // 由於 name 是 String 型別,所以返回真
要點回顧
- Java 是一種強型別語言,任何一種資料都屬於
1
種基本型別或者物件型別 (類) 中的一種; 8
種基本資料型別;- 為什麼引入變數、如何定義使用變數以及變數名的命名規範;
- Java 中的運運算元以及使用例項;
練習
獲取使用者輸入 Scanner
java.util.Scanner
是 Java5 的新特徵,我們可以通過 Scanner
類來獲取使用者的輸入。
下面是建立 Scanner 物件的基本語法:
Scanner scanner = new Scanner(System.in);
在下面的示例中,我們將使用該類的 nextLine()
方法,該方法用於讀取字串:
import java.util.Scanner; // Import the Scanner class
class MyClass {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in); // Create a Scanner object
System.out.println("Enter username");
String userName = scanner.nextLine(); // Read user input
System.out.println("Username is: " + userName); // Output user input
}
}
輸入型別
在上面的示例中,我們使用了 nextLine()
用於讀取字串的方法。要閱讀其他型別,請檢視下錶:
練習 1:輸入圓的半徑計算周長和麵積
參考答案:
import java.util.Scanner;
public class Tester {
public static void main(String[] args) {
System.out.println("請輸入圓的半徑:");
Scanner scanner = new Scanner(System.in);
int radius = scanner.nextInt();
// 計算周長和麵積
double area = radius * radius * 3.14;
double perimeter = 2 * 3.14 * radius;
System.out.println("該圓的面積為:" + area);
System.out.println("該圓的周長為:" + perimeter);
}
}
練習 2:輸入年份判斷是不是閏年
提示:
閏年需要滿足:
- 能被
4
整除,並且不能被100
整除; - 或者能被
400
整除;
參考答案:
import java.util.Scanner;
public class Tester {
public static void main(String[] args) {
System.out.println("請輸入年份:");
Scanner scanner = new Scanner(System.in);
int year = scanner.nextInt();
boolean leapYear;
boolean divisbleBy4 = year % 4 == 0;
boolean notDivisbleBy100 = year % 100 != 0;
boolean divisibleBy400 = year % 400 == 0;
leapYear = (divisbleBy4 && notDivisbleBy100) || divisibleBy400;
System.out.println("該年份是否是閏年:" + leapYear);
}
}
自取資料
擴充套件閱讀推薦
- Java 基本資料型別 | 菜鳥教程 - https://www.runoob.com/java/java-basic-datatypes.html
- 浮點型別精度深度討論:
- 為什麼 0.1 + 0.2 = 0.3? - https://draveness.me/whys-the-design-decimal-and-rational/
- 為什麼 0.1 + 0.2 = 0.300000004? - https://draveness.me/whys-the-design-floating-point-arithmetic/#fn:2
- IEEE754標準: 浮點數在記憶體中的儲存方式 - https://www.jianshu.com/p/8ee02e9bb57d
- 為什麼Java中char型別不能完整表示一個字元? - https://fookwood.com/java-string-charset-char
推薦書籍
Java 核心技術·卷 I(原書第 11 版)
推薦理由: 這本書在知識體系完整充實的同時,又比《Thinking in Java》暴風式的知識洗禮來得輕鬆,新人入門書籍強烈推薦!
碼出高效:Java開發手冊
推薦理由: 阿里系出品。從最基礎的計算機基礎入手,到 Java 的方方面面,加上精美的配圖和通俗易懂的解釋,是非常適合新手閱讀的一本兒關於 Java 的技術書籍。
參考資料
- Introduction to Computer Science using Java - http://programmedlessons.org/Java9/index.html#part02
- Java 運運算元 | 菜鳥教程 - https://www.runoob.com/java/java-operators.html
- 《Java 核心技術 卷I》(第11版)
- 本文已收錄至我的 Github 程式設計師成長系列 【More Than Java】,學習,不止 Code,歡迎 star:https://github.com/wmyskxz/MoreThanJava
- 個人公眾號 :wmyskxz,個人獨立域名部落格:wmyskxz.com,堅持原創輸出,下方掃碼關注,2020,與您共同成長!
非常感謝各位人才能 看到這裡,如果覺得本篇文章寫得不錯,覺得 「我沒有三顆心臟」有點東西 的話,求點贊,求關注,求分享,求留言!
創作不易,各位的支援和認可,就是我創作的最大動力,我們下篇文章見!