初學JAVA時,總會對一些概念只知其一;不知其二,相互混淆,不明其設計的用意,如類、對象、重載、封裝、繼承、多態、覆蓋、抽象類、接口概念。爲便於理解和鞏固,本文將基於一個案例及其變形,展示各個概念的定義、設計用意、使用規範和注意事項。html
長文警告,建議先收藏後閱讀!java
要求設計一個矩形的面積計算器,輸入爲矩形的高(height)和寬(width),輸出爲矩形的面積(area)。安全
對JAVA的語法有最基本的瞭解後能夠寫出以下代碼:ide
class Rectangle{ //建立矩形類 public double height; //定義類的成員變量 public double width; public Rectangle() { //定義無參構造器--可省略 } public void calcuArea() { //定義類的方法--面積計算 System.out.println("面積爲:"+height*width); } } public class Test{ public static void main(String[] args) { Rectangle rec=new Rectangle(); //建立矩形對象--調用構造器 rec.height=1; //高度賦值 rec.width=2; //寬度賦值 rec.calcuArea(); //調用面積計算方法 } }
以上代碼包含如下概念:
一、類(Class):類是構造對象的模板或藍圖,其由成員變量和方法構成,前者記錄數據,後者記錄數據的操做過程。
二、對象(Object):對象是類的實例化,一個類能夠有多個對象。
三、構造器(Constructor):構造器是一種特殊的方法,用於對象實例化時的初始化操做。模塊化
注意:函數
思考:以上代碼雖然實現了基本功能,但其功能不夠完善,好比當用戶輸入的高度和寬度爲字符串時,代碼就會報錯,那怎麼才能在不給用戶輸入添加麻煩的狀況下實現功能呢?優化
要求設計一個矩形的面積計算器,輸入爲矩形的高和寬(數字或者字符串輸入,假定用戶輸入的字符串都是數值型字符串),輸出爲矩形的面積。this
class Rectangle{ public double height; public double width; //定義無參構造器--可省略 public Rectangle() { } //定義有參構造器1 public Rectangle(double height,double width) { this.height=height; //this指代當前對象,用於區分方法中的形參 this.weight=width; } //定義有參構造器2 public Rectangle(String height,String width) { this.height=Double.valueOf(height); this.weight=Double.valueOf(width); } public void calcuArea() { System.out.println("面積爲:"+height*width); } } public class Test{ public static void main(String[] args) { Rectangle rec=new Rectangle(1,2); //建立矩形對象--調用構造器1 rec.calcuArea(); //調用面積計算方法 Rectangle rec1=new Rectangle("1","2"); //建立矩形對象--調用構造器2 rec1.calcuArea(); //調用面積計算方法 } }
以上代碼包含如下概念:
重載(Overload):指類中多個方法具備相同的名字,但參數類型或返回類型不一樣的現象。重載的設定是爲了方便用戶操做,以相同的方法名實現特定的功能,同時匹配不一樣的參數類型以知足功能的擴展性和需求的多樣性。在上面的代碼中咱們實現了構造器方法的重載,完美解決了用戶輸入多樣性的問題,但重載並不侷限於構造器方法,它能夠適用於類中的任何方法。spa
注意:設計
思考:以上代碼雖然解決了以前提出的問題,可是仍存在一個巨大的安全隱患,即用戶能夠直接經過「rec.height」和「rec.width」對矩形的高和寬賦值,這會致使兩個咱們不肯意看到的情景。一是,當用戶輸入很是規數值(好比-1)時,計算的結果是沒有意義的;二是,後期更改程序時,難以調試,好比咱們後期要將height的變量類型改成String類型,那麼就必須更改每一處height賦值的地方(如代碼1.2)。那麼,如何改進呢?
要求設計一個矩形的面積計算器,輸入爲矩形的高和寬(數值輸入),且數值都應大於零,輸出爲矩形的面積。同時要避免2.3中所述的問題。
class Rectangle{ private double height; //私有化實例字段 private double width; public void setHeight(double height) { //定義更改器方法 if (height<0) { this.height =0; } else { this.height = height; } } public void setWidth(double width) { if (width<0) { this.width =0; } else { this.width = width; } } public double getHeight() { //定義訪問器方法 return height; } public double getWidth() { return width; } public void calcuArea() { System.out.println("面積爲:"+height*width); } } public class Test{ public static void main(String[] args) { Rectangle rec=new Rectangle(); rec.setHeight(-1); //設定高 rec.setWidth(2); //設定寬 rec.calcuArea(); //讀取高和寬 System.out.println("高爲:"+rec.getHeight()+" 寬爲:"+rec.getWidth()); } }
以上代碼包含如下概念:
封裝(Encapsulation):經過私有化類的成員變量,並建立相應的公有化的更改器(即設定成員變量的獨立方法,如setHeight)和訪問器(即讀取成員變量的獨立方法,如getHeight)實現對成員變量的封裝。
設計用意:
我們稍微加一點難度。
如今要求設計一個面積計算器,計算的對象不只僅是矩形,還包括平行四邊形。輸入仍然爲高和寬(假定都是規範的數值輸入),輸出爲面積。
爲了簡化代碼,前面已經實現過的內容(好比封裝)就再也不展開寫了。
class Rectangle{ //定義矩形類 public double height; public double width; public Rectangle(double height,double width) { this.height=height; this.width=width; } public void calcuArea() { System.out.println("矩形的面積爲:"+height*width); } } class Parallelogram{ //定義平行四邊形類 public double height; public double width; public Parallelogram(double height,double width) { this.height=height; this.width=width; } public void calcuArea() { System.out.println("平行四邊形的面積爲:"+height*width); } } public class Test { public static void main(String[] args) { Rectangle rec=new Rectangle(1, 2); rec.calcuArea(); Parallelogram par=new Parallelogram(1, 3); par.calcuArea(); } }
思考:以上代碼寫起來挺簡單,可是有個問題,就是代碼重複率過高!這才兩個類,要是有成百上千個這種類,不只寫的累死,後期維護也得累死。
有沒有什麼偷懶的辦法呢?固然有啦,就是後面要介紹的繼承。
class Quadrangle{ //定義父類--四邊形類 public double height; public double width; public String name; public Quadrangle(double height,double width,String name) { this.height=height; this.width=width; this.name=name; } public void calcuArea() { System.out.println(name+"面積爲:"+height*width); } } class Rectangle extends Quadrangle{ //定義子類--矩形類 public Rectangle(double height,double width,String name) { super(height,width,name); //super指調用父類的構造方法 } } class Parallelogram extends Quadrangle{ //定義子類--平行四邊形類 public Parallelogram(double height,double width,String name) { super(height,width,name); } } public class Test { public static void main(String[] args) { Rectangle rec=new Rectangle(1, 2,"矩形"); rec.calcuArea(); Parallelogram par=new Parallelogram(1, 3,"平行四邊形"); par.calcuArea(); } }
誒?咋一看,好像並無省事哦?
那是由於咱們這裏只有兩個子類,若是換成實現成百上千個這種子類,差距就會拉開了。我們來分析一下這是如何實現的。
以上代碼包含如下概念:
繼承(Inheritance):即基於已有的類(稱之爲父類或超類)來建立新的類(稱之爲子類),顧名思義,子類將繼承父類全部的成員變量及方法。繼承通常應用於類與類較類似的狀況下,好比本案例中,矩形類與平行四邊形類的成員變量和方法高度類似,能夠提取二者的共同代碼,構造一個四邊形類做爲父類,從而避免了重複代碼,也方便了後期功能的擴展及維護。
注意:
我們稍微再加一點難度。
如今要求設計一個面積計算器,計算的對象不只僅是矩形和平行四邊形,還包括梯形。矩形和平行四邊形的輸入爲高和寬,梯形的輸入爲高、上底長和下底長(假定都是規範的數值輸入),輸出都爲面積。
規定:矩形和平行四邊形的面積計算公式爲寬x高;梯形的面積計算公式爲(上底+下底)x 高/2
思考:咱們仍然能夠採用繼承來實現,可是梯形的面積計算方法與矩形和平行四邊形不一樣,如何以最簡潔的方法實現代碼?
具體代碼以下:
class Quadrangle{ //定義父類--四邊形類 public double height; public double width; public String name; public Quadrangle(double height,double width,String name) { this.height=height; this.width=width; this.name=name; } public void calcuArea() { System.out.println(name+"面積爲:"+height*width); } } class Rectangle extends Quadrangle{ //定義子類--矩形類 public Rectangle(double height,double width,String name) { super(height,width,name); } } class Parallelogram extends Quadrangle{ //定義子類--平行四邊形類 public Parallelogram(double height,double width,String name) { super(height,width,name); } } class Trapezoid extends Quadrangle{ //定義子類--梯形類 public double width_up; //自定義成員變量--上底寬 public Trapezoid(double height,double width_up,double width_down,String name) { super(height,width_down, name); this.width_up=width_up; } @Override public void calcuArea() { //覆蓋父類的面積計算方法 System.out.println(name+"面積爲:"+height*(width_up+width)/2); } } public class Test { public static void main(String[] args) { Rectangle rec=new Rectangle(1, 2,"矩形"); rec.calcuArea(); Parallelogram par=new Parallelogram(1, 3,"平行四邊形"); par.calcuArea(); Trapezoid tra=new Trapezoid(1, 1, 2,"梯形"); tra.calcuArea(); } }
以上代碼用到了如下概念:
覆蓋(Override):指在繼承中,父類的有些方法在子類中不適用,子類從新定義的手段。在本案例中,梯形類對calcuArea方法實現了覆蓋。
注意:
思考:Test類中的語句塊有點囉嗦,一樣是初始化加調用面積計算方法,三個對象實現了三次,那若是有成百上千個類豈不是要累死,這可否優化呢?
對Test類進行優化能夠獲得以下代碼:
public class Test { public static void main(String[] args) { //建立對象 ArrayList<Quadrangle> quadrangles = new ArrayList<Quadrangle>(); quadrangles.add(new Rectangle(1, 2,"矩形")); quadrangles.add(new Parallelogram(1, 3,"平行四邊形")); quadrangles.add(new Trapezoid(1, 1, 2,"梯形")); //循環執行各個對象的面積計算方法 for (Quadrangle qua : quadrangles) { qua.calcuArea(); } } }
以上代碼用到了如下概念:
多態(Polymorphism):指一個對象變量(如代碼中的qua)能夠指示多種實際類型的現象。因爲矩形類、平行四邊形類和梯形類都是繼承於四邊形父類,因此其方法名一致,能夠經過一個父類的對象變量來實現子類的自動匹配,從而簡化了代碼。
多態的優缺點
思考:這個缺點怎麼解決呢?好比在上述代碼中,沒法經過qua.width_up獲取只有梯形的纔有的成員變量。
解決方法:能夠經過instanceof判斷對象變量的實際類型以及對象類型轉換實現相應的操做,代碼以下:
public class Test { public static void main(String[] args) { //建立對象 ArrayList<Quadrangle> quadrangles = new ArrayList<Quadrangle>(); quadrangles.add(new Rectangle(1, 2,"矩形")); quadrangles.add(new Parallelogram(1, 3,"平行四邊形")); quadrangles.add(new Trapezoid(1, 1, 2,"梯形")); //循環執行各個對象的面積計算方法 for (Quadrangle qua : quadrangles) { if (qua instanceof Trapezoid) { //判斷對象類型 Trapezoid tra=(Trapezoid)qua; //對象類型轉換 //輸出梯形類的上底寬 System.out.println("梯形的上底寬爲:"+tra.width_up); } qua.calcuArea(); } } }
我們再加一點難度。
如今要求設計一個面積計算器,計算的對象包括平行四邊形、梯形和圓。平行四邊形的輸入爲高和寬,梯形的輸入爲高、上底長和下底長,圓的輸入爲直徑(假定都是規範的數值輸入),全部的輸出均爲面積。
規定:平行四邊形的面積計算公式爲寬x高;梯形的面積計算公式爲(上底+下底)x 高/2;圓的面積計算公式爲圓周率x半徑的平方。
那麼,基於繼承,怎樣設計最好?
思考:將可繼承的方法體(即有具體內容的方法)放在父類中以免子類中重複代碼的出現是繼承的一大優點,但其並不是是萬能的。好比在這個案例中,三個面積計算公式都不同,很難抽取出共同的方法體,但咱們又但願子方法中都有面積計算方法且儘量避免重複代碼的出現,怎麼辦呢?
聰明如你,確定想到了能夠用剛纔學到的多態知識實現,代碼以下:
class Geometry { //定義幾何圖形類 public double height; public String name; public final double PI=3.1415; public Geometry(double height,String name) { this.height=height; this.name=name; } public void calcuArea(Geometry geo) { //定義面積計算方法,用到多態 if (geo instanceof Parallelogram) { Parallelogram par=(Parallelogram)geo; System.out.println(par.name+"面積爲:"+par.height*par.width); } else if (geo instanceof Trapezoid) { Trapezoid tra=(Trapezoid)geo; System.out.println(tra.name+"面積爲:"+height*(tra.width_up+tra.width_down)/2); } else if (geo instanceof Cycle) { Cycle cyc=(Cycle)geo; System.out.println(name+"面積爲:"+PI*Math.pow(cyc.height/2,2)); } } } class Parallelogram extends Geometry{ //定義平行四邊形類 public double width; public Parallelogram(double height,double width,String name) { super(height,name); this.width=width; } } class Trapezoid extends Geometry{ //定義梯形類 public double width_up; public double width_down; public Trapezoid(double height,double width_up,double width_down,String name) { super(height, name); this.width_up=width_up; this.width_down=width_down; } } class Cycle extends Geometry{ //定義圓形類 public Cycle(double diameter,String name) { super(diameter,name); } } public class Test { public static void main(String[] args) { //建立對象 ArrayList<Geometry> geometries = new ArrayList<Geometry>(); geometries.add(new Parallelogram(1, 3,"平行四邊形")); geometries.add(new Trapezoid(1, 1, 2,"梯形")); geometries.add(new Cycle(2,"圓形")); //循環執行各個對象的面積計算方法 for (Geometry geo : geometries) { geo.calcuArea(geo); } } }
思考:以上代碼確實實現了咱們的需求,完成了繼承,避免了重複代碼的出現,可是總感受哪裏不對勁。仔細觀察能夠發現,每增長一個新的子類,咱們就必須得在父類方法中作相應的修改,才能使新增子類也具有面積計算方法。做爲一個堂堂正正的父類怎麼能跟着子類的需求而變化呢?那這個父類豈不是很沒「面子」?
因此,在後期功能拓展時,如何才能避免對上層結構的改動呢?
思考:既然在父類中難以提取通用的方法體,那咱們可不能夠只聲明方法,而不具體實現呢?固然能夠呀,咱們能夠用到前文提到的「覆蓋」,實現子類方法的定義(如代碼5.2),這樣就避免了對父類的修改。
可是,這個實例化後的父類(好比Geometry類)是什麼呢?有意義嗎?沒有意義的話怎麼才能避免其被實例化呢?
解決方法見代碼:
abstract class Geometry { //定義抽象類 public double height; public Geometry(double height) { this.height=height; } abstract public void calcuArea(); //定義抽象方法 } class Parallelogram extends Geometry{ public double width; public Parallelogram(double height,double width) { super(height); this.width=width; } @Override public void calcuArea() { System.out.println("平行四邊形面積爲:"+height*width); } } class Trapezoid extends Geometry{ public double width_up; public double width_down; public Trapezoid(double height,double width_up,double width_down) { super(height); this.width_up=width_up; this.width_down=width_down; } @Override public void calcuArea() { System.out.println("梯形面積爲:"+height*(width_up+width_down)/2); } } class Cycle extends Geometry{ public final double PI=3.1415; public Cycle(double diameter) { super(diameter); } @Override public void calcuArea() { System.out.println("圓形面積爲:"+PI*(height/2)*(height/2)); } } public class Test { public static void main(String[] args) { ArrayList<Geometry> geometry = new ArrayList<Geometry>(); geometry.add(new Parallelogram(1, 2)); geometry.add(new Trapezoid(1, 1, 2)); geometry.add(new Cycle(2)); for (Geometry geo : geometry) { geo.calcuArea(); } } }
任何封閉的幾何圖形都應該具備面積計算方法,但方法不一,難以提取出相同的實現代碼,因此將其抽象。
以上代碼用到了如下概念:
抽象方法(Abstrac Method):指使用abstract修飾的方法,沒有方法體,只有聲明。定義的是一種「規範」,就是告訴子類必需要給抽象方法提供具體的實現。
抽象類(Abstrac Class):指包含抽象方法的類。經過abstract方法定義規範,而後要求子類必須定義具體實現。抽象類每每用來表徵對問題領域進行分析、設計中得出的抽象概念,是對一系列看上去不一樣,可是本質上相同的具體概念的抽象。經過抽象類,咱們就能夠作到嚴格限制子類的設計,使子類之間更加通用。同時,經過在抽象類中定義封裝的更改器和訪問器,減小了子類的代碼重複。
抽象的意義:
到這裏,你有可能會有個疑問,既然子類都得經過覆蓋實現本身的面積計算方法,爲何咱們執意要用繼承呢?
由於,咱們須要用繼承來提供一個規範,規範咱們的成員變量和方法(即便沒有具體的實現,也要有一致的方法名),沒有規範,多態就無從談起。若是說繼承的基本用意是實現代碼的複用性,那抽象就是繼承的昇華,它追求更高的精神境界,即契約(或規範)。抽象要求全部使用它的「用戶」(即子類)都必須簽定一份合約,這份合約規定子類必須實現抽象類所規定的全部方法。抽象方法意義在於就算沒法實現出方法的內容,但還能夠定義出一組子型共同的協議。
注意:
我們再加一點難度。
在案例6.1的基礎上增長功能,要求軸對稱圖形輸出其繞垂直中心軸旋轉獲得的幾何體的體積,體積計算方法定義爲calcuVolume。
那麼,考慮到代碼的複用性和功能的擴展性,怎樣設計最優呢?
思考:平行四邊形不是軸對稱圖形,沒法獲得旋轉的幾何體;梯形旋轉後能夠獲得圓臺;圓形旋轉後能夠獲得球。分析三個圖形的特色能夠獲得如圖所示的結構:
爲此,能夠想到以下兩種方法:
方法一:將calcuVolume方法直接定義在具備軸對稱性的子類中。
方法二:將calcuVolume方法定義在Geometry抽象類中。
那麼,如何才能優雅地解決這個問題呢?
詳見以下代碼:
abstract class Geometry { //定義抽象類 public double height; public Geometry(double height) { this.height=height; } public abstract void calcuArea(); //定義抽象方法 } interface AxialSymmetry{ public static final double PI=3.1415; public abstract void calcuVolume(); } class Parallelogram extends Geometry{ public double width; public Parallelogram(double height,double width) { super(height); this.width=width; } @Override public void calcuArea() { System.out.println("平行四邊形的面積爲:"+height*width); } } class Trapezoid extends Geometry implements AxialSymmetry{ public double width_up; public double width_down; public Trapezoid(double height,double width_up,double width_down) { super(height); this.width_up=width_up; this.width_down=width_down; } @Override public void calcuArea() { System.out.println("梯形的面積爲:"+height*(width_up+width_down)/2); } @Override public void calcuVolume() { System.out.println("圓臺的體積爲:"+PI*height*(Math.pow(width_up/2,2)+Math.pow(width_down/2,2)+width_up*width_down/4)/3); } } class Cycle extends Geometry implements AxialSymmetry{ final double PI=3.1415; public Cycle(double diameter) { super(diameter); } @Override public void calcuArea() { System.out.println("圓形的面積爲:"+PI*(height/2)*(height/2)); } @Override public void calcuVolume() { System.out.println("球的體積爲:"+PI*Math.pow(height/2,3)*4/3); } } public class Test { public static void main(String[] args) { //實現類的多態 ArrayList<Geometry> geometry = new ArrayList<Geometry>(); geometry.add(new Parallelogram(1, 2)); geometry.add(new Trapezoid(1, 1, 2)); geometry.add(new Cycle(2)); for (Geometry geo : geometry) { geo.calcuArea(); } //實現接口的多態 ArrayList<AxialSymmetry> axialSymmetry = new ArrayList<AxialSymmetry>(); axialSymmetry.add(new Trapezoid(1, 1, 2)); axialSymmetry.add(new Cycle(2)); for (AxialSymmetry axi : axialSymmetry) { axi.calcuVolume(); } } }
以上代碼用到了如下概念:
接口(Interface):接口不是類,而是對但願符合這個接口的類的一組需求。能夠說接口是比抽象更抽象的概念。抽象類還提供某些具體實現,而接口不提供任何實現,接口中全部方法都是抽象方法。接口是徹底面向規範的,規定了一批類具備的公共方法規範。接口的意義在於全面地、專業地實現了規範和具體實現的分離,便於實現模塊化設計。
類與接口的關係:
類與接口的區別:
抽象類與接口的區別:
a.成員變量
b.方法
c.構造方法
注意:
好了,概念講完了。我們來作個練習,把以上全部概念都用起來!
要求:儘可能使用本所介紹的知識,設計一個幾何圖形計算器,能計算圖形的面積以及軸對稱圖形旋轉後的體積。
結合本文所學的知識,編寫以下代碼:
//定義幾何圖形的接口 interface Geometry { public static final double PI=3.1415; public abstract void calcuArea(); } //定義軸對稱圖形的接口 interface AxialSymmetry{ public abstract void calcuVolume(); } //定義四邊形抽象類 abstract class Quadrangle implements Geometry{ private double height; private double width; public void setHeight(double height) { this.height = height; } public void setWidth(double width) { this.width = width; } public double getHeight() { return height; } public double getWidth() { return width; } public Quadrangle(double height, double width) { setHeight(height); setWidth(width); } } //定義三邊形抽象類 abstract class Triangle implements Geometry{ private double height; private double width; private String name; public void setHeight(double height) { this.height = height; } public void setWidth(double width) { this.width = width; } public void setName(String name) { this.name = name; } public double getHeight() { return height; } public double getWidth() { return width; } public String getName() { return name; } public Triangle(double height, double width) { setHeight(height); setWidth(width); } @Override public void calcuArea() { System.out.println(getName()+"的面積爲:"+getHeight()*getWidth()/2); } } //定義矩形類 class Rectangle extends Quadrangle implements AxialSymmetry{ public Rectangle(double height, double width) { super(height, width); } @Override public void calcuArea() { System.out.println("矩形的面積爲:"+getHeight()*getWidth()); } @Override public void calcuVolume() { System.out.println("矩形旋轉獲得圓柱體體積爲:"+getHeight()*Math.pow(getWidth()/2, 2)*PI); } } // 定義平行四邊形類 class Parallelogram extends Quadrangle{ public Parallelogram(double height, double width) { super(height, width); } @Override public void calcuArea() { System.out.println("平行四邊形的面積爲:"+getHeight()*getWidth()); } } //定義梯形類 class Trapezoid extends Quadrangle implements AxialSymmetry{ private double width_up; public void setWidth_up(double width_up) { this.width_up = width_up; } public double getWidth_up() { return width_up; } public Trapezoid(double height, double width_up, double width) { super(height,width); setWidth_up(width_up); } @Override public void calcuArea() { System.out.println("梯形的面積爲:"+getHeight()*(getWidth_up()+getWidth())/2); } @Override public void calcuVolume() { System.out.println("梯形旋轉獲得的圓臺體積爲:"+PI*getHeight()*(Math.pow(getWidth_up()/2,2)+Math.pow(getWidth()/2,2)+getWidth_up()*getWidth()/4)/3); } } //定義等腰三角形類 class IsoscelesTriangle extends Triangle implements AxialSymmetry{ public IsoscelesTriangle(double height, double width) { super(height, width); setName("等腰三角形"); } @Override public void calcuVolume() { System.out.println("等腰三角形旋轉獲得的圓錐體體積爲:"+getHeight()*(PI*Math.pow(getWidth()/2, 2))/3); } } //定義非等腰三角形類 class NotIsoscelesTriangle extends Triangle{ public NotIsoscelesTriangle(double height, double width) { super(height, width); setName("非等腰三角形"); } } public class Test { public static void main(String[] args) { // 實例化 ArrayList<Object> objects=new ArrayList<Object>(); objects.add(new Rectangle(1, 2)); objects.add(new Parallelogram(1, 2)); objects.add(new Trapezoid(1, 1, 2)); objects.add(new IsoscelesTriangle(1, 2)); objects.add(new NotIsoscelesTriangle(1, 2)); // 多態 for (Object obj : objects) { if (obj instanceof Geometry) { Geometry geo=(Geometry)obj; geo.calcuArea(); } if (obj instanceof AxialSymmetry) { AxialSymmetry axi=(AxialSymmetry) obj; axi.calcuVolume(); } } } }
注意!以上代碼必定不是最優方案,只是爲了練習本文所學知識,所以,僅作參考。關於程序的模式設計問題之後再聊。
層次結構以下:
【1】《Head First Java(第二版·中文版)》
【2】《Java核心技術·卷 I(原書第11版)》
【3】菜鳥教程:https://www.runoob.com/java/j...
【4】速學堂:https://www.sxt.cn/Java_jQuer...