你必須知道的Java基礎知識

程式設計師自由之路 發表於 2020-09-08

你必須知道的Java基礎知識

本篇部落格主要記錄Java中物件導向的概念和Java語法的基礎知識。

物件導向

什麼是物件導向

物件導向是一種優秀的軟體設計思想,是相對於程式導向、面向切面等設計思想的一種軟體設計理念。它的核心思想是運用更加貼近人類思維的方式去設計軟體,將軟體中的各個元件抽象成相應的類,再將這些類組裝成我們所需的軟體系統。

這裡舉個例子:假如用物件導向的方式設計一個電腦,我們會設計CPU類、硬碟類、顯示器類、記憶體類等等,然後將這些類組合在一起設計成Computer類。

而程式導向的思想在解決問題時會將問題分解成一個個小的函式,然後按照某種順序去執行這些方法,當這些方法執行完畢,問題也就解決了。

三大基本特徵和五項基本原則

物件導向的三大基本特徵是:封裝、繼承和多型。正是基於這些特徵,物件導向的開發語言才能擁有更好的可重用性、擴充套件性和維護性

  • 封裝:將物件的實現細節隱藏起來,然後通過一些公共的方法向外部提供該物件的功能;
  • 繼承:繼承是軟體複用的一種重要手段,子類繼承父類之後將直接獲得父類的屬性和方法;
  • 多型:物件可以賦給父類物件或者它實現的介面,但是執行時依然表現出子類或實現類的特徵。

物件導向的五大原則如下:

  • 單一職責原則(SRP):一個類專注於實現一個功能;
  • 開閉原則(OCP):物件或實體應該對擴充套件開放,對修改封閉;
  • 里氏替換原則(LSP):子類可以替換父類並且出現在父類能夠出現的任何地方(這個原則就需要我們面向介面程式設計);
  • 依賴倒置原則(DIP):高層次的模組不應該依賴於低層次的模組,他們都應該依賴於抽象,抽象不應該依賴於具體實現,具體實現應該依賴於抽象;
  • 介面隔離原則(ISP):使用多個專門的介面比使用單個介面要好的多:在實際程式設計中,為了減少介面的定義,將許多方法都放在一個介面中,最後發現,維護和實現介面的時候會花費很多精力,介面所定義的操作相當於對客戶端的一種承諾,這種承諾當然是越少越好,越精練越好,過多的承諾帶來的就是你的大量精力和時間去維護。不要把不相干的方法放在一個介面中定義。

另外還有迪米特原則合成服用原則,也需要我們在設計系統和介面時考慮到。

迪米特法則:又叫最少知識法則,這個法則主張一個類要儘可能少地知道其他類地細節,並且儘可能少地和其他類進行通訊互動,只和於自己有密切關係地類進行互動。
合成服用原則:合成服用原則又稱為組合/聚合服用原則(Composite/Aggregate Reuse Principle):儘量採用組合(contains-a)、聚合(has-a)的方式而不是繼承(is-a)的關係來達到軟體的複用目的。

合成服用原則最主要地原因是繼承會破環封裝:基類的很多內部細節都是對派生類可見的,而有些細節可能是不想讓子類知道的。關於繼承、組合還有聚合的概念可能比較容易搞混,這邊也提下。

繼承:子類擁有父類的某些屬性和方法,子類可以替換父類;
組合:是整體與部分的關係,整體離不開部分,部分離開了整體沒有意義,如飛機翅膀與飛機的關係;
聚合:也是整體與部分的關係,但整體可以分離部分,部分也可以離開整體,如火車與車廂的關係,還有就是聚合支付的列子等。

過載和重寫的區別

過載:是指在一個類裡面存在多個方法名相同,但是方法引數不同的方法。

重寫:是指子類在繼承父類時,重新改寫了父類的某個方法。子類中的這個方法的方法名和方法引數和父類中的完全一樣。

Java基礎語法

Java語言是最流行的程式語言,從剛“出生”,Java語言就號稱“一次編譯,到處執行”。

Java語言能跨平臺的關鍵是JVM能跨平臺,JVM遮蔽了作業系統的底層差異,讓位元組碼檔案在每個平臺上都可以執行。

除了Java,JVM還支援的語言有Kotlin、Groovy、JRuby、Jython、Scala等。另外,隨著Oracle對Graal虛擬機器的研發,JVM平臺將會支援所有的語言,包括JS和C++等語言。

進行Java開發的第一步是下載一個合適版本的JDK,然後配置JAVA_HOME這個環境變數。但是如果你只是需要執行Java程式,那你只需要安裝一個JRE即可。JDK和JRE的關係如下:

JDK包含:Java編譯器、JRE以及常用的Java類庫;
JRE包含:JVM、類載入器、位元組碼校驗器以及核心類庫等。

然後就是編譯執行Java程式

# -d後面的引數指定生成的位元組碼檔案的生產路徑,預設當前目錄;
javac -d destDir HelloWorld.java;
# 執行Java程式;
# classpath的作用是提供了一系列路徑讓Java程式去在這些路徑下尋找類,找不到就報錯。在Windows上用;號分割,在Linux上用:號分隔
java -classpath %CLAss_PATH%;.;dir1;dir2 HelloWorld;

三種基本的程式結構

  • 順序結構
  • 選擇結構
  • 迴圈結構

使用這三種結構能編寫出任意功能的程式。

Java中的註釋

  • 單行註釋://
  • 多行註釋:/**/
  • 文件註釋:/** */ 使用javadoc可以生成api文件

Java中的關鍵字

Java中一共有48個關鍵字,2個保留字和三個直接量

  • 訪問控制:private、protected、public;
  • 類,方法和變數修飾符:abstract、class、extends、final、implements、interface、native、new、static、strictfp、synchronized、transient、volatile;
  • 程式控制:break、continue、return、do、while、if、else、for、instanceof、switch、case、default;
  • 錯誤處理:try、cathc、throw、throws、finally;
  • 包相關:import、package;
  • 基本型別:boolean、byte、char、double、float、int、long、short、null、true、false;
  • 變數引用:super、this、void;
  • 保留字:goto、const;
  • 直接量:true、false、null。

重要關鍵字說明:

  • native: 用來宣告一個方法是由與機器相關的語言(如C/C++/FORTRAN語言)實現的;
  • strictfp:用來宣告FP-strict(雙精度或單精度浮點數)表示式;
  • transient:宣告不用序列化的屬性;
  • volatile:表明兩個或多個變數必須同步地發生變化。

資料型別

Java中的資料型別分為基本資料型別和引用資料型別。

資料型別

1. 取值範圍

8種基本數字型別從大類上分的話分別是布林型,字元型,整形和浮點型,對應bool,byte,char,short,int,long,float和double型別。

  • byte:一個位元組,8個bit位,-2^7 ~ 2^7-1
  • char : 2個位元組,16個bit位,0 ~ 2^16-1 ;
  • short:2個位元組,16個bit位,-2^15 ~ 2^15-1
  • int:4個位元組,32個bit位,-2^31 ~ 2^31-1
  • long:8個位元組,64個bit位,-2^63 ~ 2^63-1
  • float:4個位元組,32個bit位
  • double:8個位元組,64個bit位。

上面的float和double都是浮點型別資料,在計算機中,浮點數用來近似表示任意某個實數。浮點數分為雙精度浮點數和單精度浮點數。單精度佔4個位元組,雙精度佔8個位元組,表示的範圍更大。

對於double和float型別的資料,正數除以0得到正無窮,負數除以0得到負無窮,0.0除以0達到NaN。但是整數除以0會得到除0異常。

在要求精確計算的場合,不建議使用浮點型資料。因為浮點數計算的結果不是很精確,是近似的結果。這種情況應該使用BigDecimal。這邊舉個BigDecimal使用的簡單列子:

BigDecimal bigDecimal1 = new BigDecimal(String.valueOf(1.1));
BigDecimal bigDecimal2 = new BigDecimal(String.valueOf(2.223));
String str1 = bigDecimal1.add(bigDecimal2).toString();
//減法
bigDecimal1 = new BigDecimal(String.valueOf(2.2));
bigDecimal2 = new BigDecimal(String.valueOf(10.1));
String str2 = bigDecimal1.subtract(bigDecimal2).toString();
//乘法
bigDecimal1 = new BigDecimal(String.valueOf(1.1));
bigDecimal2 = new BigDecimal(String.valueOf(2.234));
String str3 = bigDecimal1.multiply(bigDecimal2).toString();
//除法
bigDecimal1 = new BigDecimal(String.valueOf(4.4));
bigDecimal2 = new BigDecimal(String.valueOf(3));
//除法需要設定精度和四捨五入的方式
String str4 = bigDecimal1.divide(bigDecimal2,3, RoundingMode.HALF_UP).toString();
//結果
System.out.println("加法結果:" + Double.valueOf(str1));
System.out.println("減法結果:" + Double.valueOf(str2));
System.out.println("乘法結果:" + Double.valueOf(str3));
System.out.println("除法結果:" + Double.valueOf(str4));

2. 基本資料型別之間的轉換

char---------------|
byte-->short-->int-->long-->float-->double

對於上面的轉換關係做下說明:char可以自動轉換成int,byte可以自動轉換成short,short可以自動轉換成int。這些轉換過程都是自動完成的。

short a = 3;
int b = a;

那麼將int賦值給short,或者將double賦值給int可不可以呢?答案是可以的,但是最後的資料可能不是我們預期的值。因為int佔的位元組數比short長,short盛放不下,會做資料擷取。

  • 整數之間的強制轉換:比如int轉換為short會縮短位數;
  • double向int轉換:會截掉小數部分。

3. 表示式型別自動提升

整個表示式的型別會提升為表示式中最高等級的型別。

short aa = 1;
//這個表示式會報錯,aa+2會自動提升為int
short bb = aa+2; 

包裝類型別

包裝型別是相對於基本資料型別來講的。Java中有8種基本資料型別,每個基本資料型別都有相對的包裝型別。比如int的包裝型別是Integer。包裝型別都是是引用型別,而且都是不可變類。

關於包裝型別,有兩個概念需要我們瞭解:

  • 自動裝箱:將基本資料型別賦值給包裝型別的過程,實現原理是編譯器層面new了一個包裝類再賦值給相應的變數(如果是是將包裝類快取過的數值賦值給包裝類的化,不會new新的包裝了,而是直接使用快取的物件);
  • 自動拆箱:包裝型別直接賦值給基本資料型別,實現原理類似,也是編譯器層面呼叫了包裝類的getValue方法再賦值給對應的基本數值型別。

比較有趣的是,包裝型別在載入的過程中都會快取某些值的類。比如Integer會快取-128到127的類。

//不會使用快取的資料
Integer num1 = new Integer(1); 
//會使用快取的資料,實際上是呼叫了Integer.valueOf方法
Integer num2 = 1;

通過設定-XX:AutoBoxCacheMax=?這個引數,可以調整Integer快取的最大值。當然其他包裝型別也有類似的行為,Byte、Short和Long都快取了-128到127的類,Character快取了0到127的類,但是這些類不能像Integer那樣修改快取的最大值。

關於基本資料型別和包裝型別的使用,介紹一個阿里巴巴開發規範中的原則:

所有的POJO類屬性必須使用包裝資料型別;RPC方法的返回值和引數必須使用包裝型別;所有的區域性變數儘量使用基本資料型別。
定義布林型的類變數,變數名不要以is打頭。

Java中的值傳遞和引用傳遞

基本資料型別,儲存的是資料本身的值,按值傳遞;引用型別的變數儲存的是物件在記憶體中的地址,按引用傳遞(引用傳遞可以看成是一種特殊的值傳遞)。

一個問題

Java中的bool變數到底佔用多大的記憶體呢?網上的各種說法都有?歡迎大家留言討論。

公眾號推薦

歡迎大家關注我的微信公眾號「程式設計師自由之路」

你必須知道的Java基礎知識