1. 程式人生 > 實用技巧 >前端異常處理

前端異常處理

  • 「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
億,這個數值也就是 232 對半分成正負值,32=4位元組*每個位元組8位)
short 2 位元組 -32768 ~ 32767
long 8 位元組 -9223372036854775808 ~ 9223372036854775807
byte 1 位元組 -128 ~ 127

在通常情況下,int 型別最常用。但如果想要表示整個地球的居住人口,那麼就需要使用 long 型別了。byteshort 型別主要用於特定的應用場合,例如,底層的檔案處理或者儲存空間很寶貴時的大陣列 (因為節約記憶體)

浮點型別

浮點型別用於表示有小數部分的數值。在 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.10.2二進位制 中同 1/3十進位制 中一樣,不屬於整數的範疇,所以只能用近似值來代替,由於精度的限制 0.10.2 使用單精度浮點數表示的實際值為:0.1000000014901161193847656250.20000000298023223876953125,把它們相加起來得到的結果與我們在一開始看到的非常相似:

在交易系統或者科學計算的場景中,如果需要更高的精度小數,可以使用具有 28 個有效位數的 decimal 或者直接使用分數,不過這些表示方法的開銷也隨著有效位數的增加而提高,我們應該按照需要選擇最合適的方法。

重新回到最開始的問題 — 0.10.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 (布林) 型別有兩個值:falsetrue (注意這兩個是布林型別的字面常量也是保留字),用來判斷邏輯條件是否成立。

物件型別

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 中,變數命名需要遵循以下硬性規定和強烈建議遵守的非硬性規定:

硬性規則

  1. 變數名必須是一個以字母開頭並由字母或數字構成的序列 (儘管 $ 是合法的,但不要在你自己的程式碼中使用這個字元,它只用在 Java 編譯器或其他工具生成的名字中)
  2. 每一個字元都有意義,且大小寫敏感;
  3. 不要使用 Java 中的保留字;

《阿里巴巴 Java 開發手冊》規則

  1. 【強制】 程式碼中的命名 (所有識別符號) 均不能以下劃線或美元符號開始,也不能以下劃線或美元符號結束;(反例: $name)
  2. 【強制] 程式碼中的命名嚴禁使用拼音與英文混合的方式,更不允許直接使用中文的方式;(反例:DaZhePromotion - 打折)
  3. 【強制】 杜絕完全不規範的縮寫,避免詞不達意;(反例:condition 縮寫成 condi)
  4. 【推薦】 為了達到程式碼自解釋的目的,任何自定義程式設計元素在命名時,使用盡量完整的單片語合來表達其意;(反例: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 型別,所以返回真

要點回顧

  1. Java 是一種強型別語言,任何一種資料都屬於 1 種基本型別或者物件型別 (類) 中的一種;
  2. 8 種基本資料型別;
  3. 為什麼引入變數、如何定義使用變數以及變數名的命名規範;
  4. 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:輸入年份判斷是不是閏年

提示:

閏年需要滿足:

  1. 能被 4 整除,並且不能被 100 整除;
  2. 或者能被 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);
}
}

自取資料

擴充套件閱讀推薦

  1. Java 基本資料型別 | 菜鳥教程 - https://www.runoob.com/java/java-basic-datatypes.html
  2. 浮點型別精度深度討論:
    1. 為什麼 0.1 + 0.2 = 0.3? - https://draveness.me/whys-the-design-decimal-and-rational/
    2. 為什麼 0.1 + 0.2 = 0.300000004? - https://draveness.me/whys-the-design-floating-point-arithmetic/#fn:2
  3. IEEE754標準: 浮點數在記憶體中的儲存方式 - https://www.jianshu.com/p/8ee02e9bb57d
  4. 為什麼Java中char型別不能完整表示一個字元? - https://fookwood.com/java-string-charset-char

推薦書籍

Java 核心技術·卷 I(原書第 11 版)

推薦理由: 這本書在知識體系完整充實的同時,又比《Thinking in Java》暴風式的知識洗禮來得輕鬆,新人入門書籍強烈推薦!

碼出高效:Java開發手冊

推薦理由: 阿里系出品。從最基礎的計算機基礎入手,到 Java 的方方面面,加上精美的配圖和通俗易懂的解釋,是非常適合新手閱讀的一本兒關於 Java 的技術書籍。

參考資料

  1. Introduction to Computer Science using Java - http://programmedlessons.org/Java9/index.html#part02
  2. Java 運運算元 | 菜鳥教程 - https://www.runoob.com/java/java-operators.html
  3. 《Java 核心技術 卷I》(第11版)
  • 本文已收錄至我的 Github 程式設計師成長系列 【More Than Java】,學習,不止 Code,歡迎 star:https://github.com/wmyskxz/MoreThanJava
  • 個人公眾號 :wmyskxz,個人獨立域名部落格:wmyskxz.com,堅持原創輸出,下方掃碼關注,2020,與您共同成長!

非常感謝各位人才能 看到這裡,如果覺得本篇文章寫得不錯,覺得 「我沒有三顆心臟」有點東西 的話,求點贊,求關注,求分享,求留言!

創作不易,各位的支援和認可,就是我創作的最大動力,我們下篇文章見!