尚硅谷Java入门视频教程第五章——面向对象编程(中)
尚硅谷Java入門視頻教程第五章——面向對象編程(中)
- 第5章:面向對象編程(中)
- 5.1 面向對象特征之二:繼承性
- 5.2 方法的重寫(override/overwrite)
- 5.3 四種訪問權限修飾符
- 5.4 關鍵字:super
- 5.5 子類對象實例化過程
- 5.6 面向對象特征之三:多態性
- 5.6.1 多態性及其理解
- 5.6.2 關鍵字:instanceof
- 5.7 Object類的使用
- 5.7.1 ==操作符與equals方法
- 5.7.2 toString() 方法
- 5.8 包裝類的使用
- 5.8.1 單元測試方法
- 5.8.2 包裝類
第5章:面向對象編程(中)
5.1 面向對象特征之二:繼承性
面向對象的特征之二:繼承性 why?
為什么要有繼承?
多個類中存在相同屬性和行為時,將這些內容抽取到單獨一個類中,
那么多個類無需再定義這些屬性和行為,只要繼承那個類即可。
-
繼承性的好處:
① 減少了代碼的冗余,提高了代碼的復用性
② 便于功能的擴展
③ 為之后多態性的使用,提供了前提 -
繼承性的格式:
class A extends B{}
A:子類、派生類、subclass
B:父類、超類、基類、superclass - 體現:一旦子類A繼承父類B以后,子類A中就獲取了父類B中聲明的所有的屬性和方法。
特別的,父類中聲明為private的屬性或方法,子類繼承父類以后,仍然認為獲取了父類中私有的結構。只有因為封裝性的影響,使得子類不能直接調用父類的結構而已。 - 子類繼承父類以后,還可以聲明自己特有的屬性或方法:實現功能的拓展。
子類和父類的關系,不同于子集和集合的關系。
extends:延展、擴展 -
Java中關于繼承性的規定:
- 一個類可以被多個子類繼承。
- Java中類的單繼承性:一個類只能有一個父類
- 子父類是相對的概念。
- 子類直接繼承的父類,稱為:直接父類。間接繼承的父類稱為:間接父類
- 子類繼承父類以后,就獲取了直接父類以及所有間接父類中聲明的屬性和方法
-
java.lang.Object類初識
- 如果我們沒有顯式的聲明一個類的父類的話,則此類繼承于java.lang.Object類
- 所有的java類(除java.lang.Object類之外)都直接或間接的繼承于java.lang.Object類
- 意味著,所有的java類具有java.lang.Object類聲明的功能。
-
練習
package com.atguigu.exer;public class Circle {private double radius;//半徑public Circle() {radius = 1.0;}public void setRadiu(double radius) {this.radius = radius;}public double getRadius() {return this.radius;}//返回圓的面積 public double findArea() {return Math.PI * radius * radius;} }
根據下圖實現類。在CylinderTest類中創建Cylinder類的對象,設置圓柱的底面半徑和高,并輸出圓柱的體積。
Circle類:Cylinder類
package com.atguigu.exer;public class Cylinder extends Circle{private double length;public Cylinder() {length = 1.0;}public double getLength() {return length;}public void setLength(double length) {this.length = length;}//返回圓柱體的體積public double findVolume() {return Math.PI * getRadius() * getRadius() * getLength();//或//return findArea() * getLength();} }CylinderTest類
package com.atguigu.exer;public class CylinderTest {public static void main(String[] args) {Cylinder cy = new Cylinder();cy.setRadiu(2.1);cy.setLength(3.4);double area = cy.findArea();System.out.println("圓柱體的底面積為:" + area);double volume = cy.findVolume();System.out.println("圓柱體的體積為:" + volume);}}
5.2 方法的重寫(override/overwrite)
- 重寫:
子類繼承父類以后,可以對父類中同名同參數的方法,進行覆蓋操作。 - 應用:重寫以后,當創建子類對象以后,通過子類對象調用子父類中的同名同參數的方法時,實際執行的是子類重寫父類的方法。
- 重寫的規定:
方法的聲明:權限修飾符 返回值類型 方法名(形參列表) throws 異常的類型{//方法體 } 約定俗成:子類中的叫重寫的方法,父類中的叫被重寫的方法 - 子類重寫的方法的方法名和形參列表與父類被重寫的方法的方法名和形參列表相同
- 子類重寫的方法的權限修飾符不小于父類被重寫的方法的權限修飾符
特殊情況:子類不能重寫父類中聲明為private權限的方法 - 返回值類型:
父類被重寫的方法的返回值類型是void,則子類重寫的方法的返回值類型只能是void
父類被重寫的方法的返回值類型是A類型,則子類重寫的方法的返回值類型可以是A類或A類的子類
父類被重寫的方法的返回值類型是基本數據類型(比如:double),則子類重寫的方法的返回值類型必須是相同的基本數據類型(必須也是double)——與變量類型提升無關 - 子類重寫的方法拋出的異常類型不大于父類被重寫的方法拋出的異常類型(具體放到異常處理時候講)
實際開發中一般直接將父類方法的方法聲明與參數列表直接復制粘貼到子類方法中,或者使用Eclipse的提示功能進行重寫,防止手打出錯。
子類和父類中的同名同參數的方法要么都聲明為非static的(考慮重寫),要么都聲明為static的(不是重寫)。
- 面試題:區分方法的重載與重寫:
-
二者的概念:
① 重載:
同一個類中,方法名相同但參數列表不同(參數類型與數量)的方法之間構成重載。
構造器也可以重載。
② 重寫:
子類繼承父類以后,可以對父類中同名同參數的方法,進行覆蓋操作。
構造器不可以重寫。 -
重載與重寫的具體規則:
-
從編譯和運行的角度看:
① 重載:
不表現為多態性,編譯器根據方法不同的參數表,對同名方法的名稱做修飾。對于編譯器而言,這些同名方法就成了不同的方法。它們的調用地址在編譯期就綁定了。Java的重載是可以包括父類和子類的,即子類可以重載父類的同名不同參數的方法。所以:對于重載而言,在方法調用之前,編譯器就已經確定了所要調用的方法,這稱為 “早綁定”或“靜態綁定”
② 重寫:
表現為多態性(Person p = new Man;p.eat();),只有等到方法調用的那一刻,解釋運行器才會確定所要調用的具體方法,這稱為==“晚綁定”或“動態綁定”==
- 例題:對5.1節中的例題的Cylinder類進行修改,重寫Circle類中的findArea方法,使得其返回圓柱體的表面積。
Cylinder類:package com.atguigu.exer;public class Cylinder extends Circle{private double length;public Cylinder() {length = 1.0;}public double getLength() {return length;}public void setLength(double length) {this.length = length;}//返回圓柱體的體積public double findVolume() {return Math.PI * getRadius() * getRadius() * getLength();}//返回圓柱體的表面積@Overridepublic double findArea() {return Math.PI * getRadius() * getRadius() * 2 + 2 * Math.PI * getRadius() * getLength();} }
5.3 四種訪問權限修飾符
5.4 關鍵字:super
- super理解為:父類的
- super可以用來調用:屬性、方法、構造器
- super的使用:調用屬性和方法
- 可以在子類的方法或構造器中。通過使用"super.屬性"或"super.方法"的方式,顯式的調用
父類中聲明的屬性或方法。但是,通常情況下,我們習慣省略"super." - 特殊情況:當子類和父類中定義了同名的屬性時,我們要想在子類中調用父類中聲明的屬性,則必須顯式的
使用"super.屬性"的方式,表明調用的是父類中聲明的屬性。 - 特殊情況:當子類重寫了父類中的方法以后,我們想在子類的方法中調用父類中被重寫的方法時,則必須顯式的
使用"super.方法"的方式,表明調用的是父類中被重寫的方法。
Cylinder類中求圓柱體的體積時,體積=底面積*高,此時要用的findArea為父類Circle中定義的求圓的面積而不是子類Cylinder類重寫的求表面積,因此使用super關鍵字
package com.atguigu.exer1;public class Cylinder extends Circle{private double length;//高public Cylinder(){length = 1.0;}public double getLength() {return length;}public void setLength(double length) {this.length = length;}//返回圓柱的體積public double findVolume(){ // return Math.PI * getRadius() * getRadius() * getLength();return super.findArea() * getLength();}@Overridepublic double findArea() {//返回圓柱的表面積return Math.PI * getRadius() * getRadius() * 2 + 2 * Math.PI * getRadius() * getLength();}}- super調用構造器
- 我們可以在子類的構造器中顯式的使用"super(形參列表)"的方式,調用父類中聲明的指定的構造器
- "super(形參列表)"的使用,必須聲明在子類構造器的首行!
- 我們在類的構造器中,針對于"this(形參列表)"或"super(形參列表)"只能二選一,且必須二選一,不能同時出現
- 在構造器的首行,沒有顯式的聲明"this(形參列表)“或"super(形參列表)”,則默認調用的是父類中空參的構造器:super()
- 在類的多個構造器中,至少有一個類的構造器中使用了"super(形參列表)",調用父類中的構造器
(如果一個類中有n個構造器,則最多有 n - 1構造器中使用了"this(形參列表)”,另一個則為"super(形參列表)")
父類Circle:
package com.atguigu.exer;public class Circle {private double radius;//半徑// public Circle() { // radius = 1.0; // }public Circle(double radius){this.radius = radius;}public void setRadiu(double radius) {this.radius = radius;}public double getRadius() {return this.radius;}//返回圓的面積 public double findArea() {return Math.PI * radius * radius;} }子類Cylinder:
package com.atguigu.exer1;public class Cylinder extends Circle{private double length;//高public Cylinder(){length = 1.0;}public double getLength() {return length;}public void setLength(double length) {this.length = length;}//返回圓柱的體積public double findVolume(){ // return Math.PI * getRadius() * getRadius() * getLength();return super.findArea() * getLength();}@Overridepublic double findArea() {//返回圓柱的表面積return Math.PI * getRadius() * getRadius() * 2 + 2 * Math.PI * getRadius() * getLength();}}當Circle類中的空慘構造器注釋時,子類Cylinder的構造器會報錯。
原因分析:
在父類Circle中有非空參的構造器,因此系統不再提供空參的構造器,在子類Cylinder的構造器中沒有顯式的使用的構造器,因此默認使用super(),但是父類中沒有空參的,因此報錯。
當子類Cylinder的空參構造器注釋,系統在進行類的聲明時仍然會有一個默認的空參構造器,其中仍然會默認的調用super(),因此也會報錯。
5.5 子類對象實例化過程
- 思考:
- 為什么super(…)和this(…)調用語句不能同時在一個構造器中出現?
兩者在使用時都必須在構造器的第一行 - 為什么super(…)或this(…)調用語句只能作為構造器中的第一句出現?
- 子類對象實例化的全過程:
-
從結果上來看:(繼承性)
子類繼承父類以后,就獲取了父類中聲明的屬性或方法。
創建子類的對象,在堆空間中,就會加載所有父類中聲明的屬性。 -
從過程上來看:
當我們通過子類的構造器創建子類對象時,我們一定會直接或間接的調用其父類的構造器,進而調用父類的父類的構造器,…
直到調用了java.lang.Object類中空參的構造器為止。正因為加載過所有的父類的結構,所以才可以看到內存中有父類中的結構,子類對象才可以考慮進行調用。
明確:雖然創建子類對象時,調用了父類的構造器,但是自始至終就創建過一個對象,即為new的子類對象。
- 例題:
5.6 面向對象特征之三:多態性
5.6.1 多態性及其理解
-
多態性:
- 理解多態性:可以理解為一個事物的多種形態。
- 何為多態性:
對象的多態性:父類的引用指向子類的對象(或子類的對象賦給父類的引用) - 多態的使用:虛擬方法調用
- 子類中定義了與父類同名同參數的方法,在多態情況下,將此時父類的方法稱為虛擬方法,父類根據賦給它的不同子類對象,動態調用屬于子類的該方法。這樣的方法調用在編譯期是無法確定的。
在編譯期,只能調用父類中聲明的方法,但在運行期,我們實際執行的是子類重寫父類的方法。即,方法的調用是在運行時確定的——動態綁定
總結:編譯,看左邊;運行,看右邊。 - 多態性的使用前提: ① 類的繼承關系 ② 方法的重寫
- 對象的多態性,只適用于方法,不適用于屬性(編譯和運行都看左邊)
-
多態性的理解:
- 減少冗余代碼(重載方法)
- 增加方法的通用性
- 規范代碼操作
- 抽象類、接口的使用肯定體現了多態性(抽象類、接口不能實例化,因此在使用時只能創建其子類的對象)
定義了一個父類Preson以及它的兩個子類Man和Woman:
package com.atguigu.java4;public class Person {String name;int age;int id = 1001;public void eat(){System.out.println("人:吃飯");}public void walk(){System.out.println("人:走路");}} package com.atguigu.java4;public class Man extends Person{boolean isSmoking;int id = 1002;public void earnMoney(){System.out.println("男人負責掙錢養家");}public void eat(){System.out.println("男人多吃肉,長肌肉");}public void walk(){System.out.println("男人霸氣的走路");}} package com.atguigu.java4;public class Woman extends Person{boolean isBeauty;public void goShopping(){System.out.println("女人喜歡購物");}public void eat(){System.out.println("女人少吃,為了減肥");}public void walk(){System.out.println("女人窈窕的走路");} }多態性的說明:
package com.atguigu.java4;public class PersonTest {public static void main(String[] args) {Person p1 = new Person();p1.eat();Man man = new Man();man.eat();man.age = 25;man.earnMoney();//*************************************************System.out.println("*******************");//對象的多態性:父類的引用指向子類的對象Person p2 = new Man(); // Person p3 = new Woman();//多態的使用:當調用子父類同名同參數的方法時,實際執行的是子類重寫父類的方法 ---虛擬方法調用p2.eat();p2.walk();// p2.earnMoney();//不可以調用父類沒有聲明的方法System.out.println(p2.id);//1001(與屬性無關,屬性仍然是父類中的)} }- 多態小結
- 多態作用:
提高了代碼的通用性,常稱作接口重用 - 前提:
① 需要存在繼承或者實現關系
② 有方法的重寫 - 成員方法:
編譯時:要查看引用變量所聲明的類中是否有所調用的方法。
運行時:調用實際new的對象所屬的類中的重寫方法。 - 成員變量:
不具備多態性,只看引用變量所聲明的類
5.6.2 關鍵字:instanceof
- 問題引入:
有了對象的多態性以后,內存中實際上是加載了子類特有的屬性和方法的,但是由于變量聲明為父類類型,導致編譯時,只能調用父類中聲明的屬性和方法。子類特有的屬性和方法不能調用。
如何調用子類的屬性和方法?——向下轉型:使用強制類型轉換符
但是向下轉型存在一定風險,可能會導致運行錯誤(一個父類可能包含多個子類,子類之間進行向下轉型編譯時不報錯,但運行時會報錯),因此在轉換前應該對變量的具體類型進行判斷 -> instanceof關鍵字 - instanceof關鍵字的使用
① a instanceof A:判斷對象a是否是類A的實例。如果是,返回true;如果不是,返回false。
② 如果 a instanceof A返回true,則 a instanceof B也返回true。 其中,類B是類A的父類
③ 要求a所屬的類與類A必須是子父類的關系,否則編譯錯誤。
- 練習:
-
判斷JAVA文件的輸出結果:
package com.atguigu.exer;class Base {int count = 10;public void display() {System.out.println(this.count);} }class Sub extends Base {int count = 20;public void display() {System.out.println(this.count);} }public class FieldMethodTest {public static void main(String[] args) {Sub s = new Sub();System.out.println(s.count);//20s.display();//20Base b = s;//多態性//==:對于引用數據類型來講,比較的是兩個引用數據類型變量的地址值是否相同System.out.println(b == s);//trueSystem.out.println(b.count);//10b.display();//20} }若子類重寫了父類方法,就意味著子類里定義的方法徹底覆蓋了父類里的同名方法,系統將不可能把父類里的方法轉移到子類中:編譯看左邊,運行看右邊
對于實例變量(屬性)則不存在這樣的現象,即使子類里定義了與父類完全相同的實例變量,這個實例變量依然不可能覆蓋父類中定義的實例變量:編譯運行都看左邊 -
編寫InatanceTest類
package com.atguigu.exer;public class InstanceTest {public static void main(String[] args) {InstanceTest test = new InstanceTest();test.method(new Student());}public void method(Person e){//(1):虛擬方法調用String info = e.getInfo();System.out.println(info);//(2):instanceof判斷//方式一 // if(e instanceof Graduate){ // System.out.println("a graduated student"); // System.out.println("a student"); // System.out.println("a person"); // }else if(e instanceof Student){ // System.out.println("a student"); // System.out.println("a person"); // }else{ // System.out.println("a person"); // }//方式二if(e instanceof Graduate){System.out.println("a graduated student");}if(e instanceof Student){System.out.println("a student");}if(e instanceof Person){System.out.println("a person");}} }class Person {protected String name = "person";protected int age = 50;public String getInfo() {return "Name: " + name + "\n" + "age: " + age;} }class Student extends Person {protected String school = "pku";public String getInfo() {return "Name: " + name + "\nage: " + age + "\nschool: " + school;} }class Graduate extends Student {public String major = "IT";public String getInfo() {return "Name: " + name + "\nage: " + age + "\nschool: " + school + "\nmajor:" + major;} } -
定義三個類,父類GeometricObject代表幾何形狀,子類Circle代表圓形,MyRectangle代表矩形。定義一個測試類GeometricTest,編寫equalsArea方法測試兩個對象的面積是否相等(注意方法的參數類型,利用動態綁定技術),編寫displayGeometricObject方法顯示對象的面積(注意方法的參數類型,利用動態綁定技術)。
package com.atguiau.exer1;public class GeometricObject {protected String color;protected double weight;protected GeometricObject(String color, double weight) {super();this.color = color;this.weight = weight;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public double getWeight() {return weight;}public void setWeight(double weight) {this.weight = weight;}public double findArea() {return 0.0;}}
父類:子類1:
package com.atguiau.exer1;public class Circle extends GeometricObject {private double radius;protected Circle(double radius, String color, double weight) {super(color, weight);this.radius = radius;}public double getRadius() {return radius;}public void setRadius(double radius) {this.radius = radius;}@Overridepublic double findArea() {return Math.PI * radius * radius;}}子類2:
package com.atguiau.exer1;public class MyRectangle extends GeometricObject {private double width;private double height;public MyRectangle(String color, double weight, double width, double height) {super(color, weight);this.width = width;this.height = height;}public double getWidth() {return width;}public void setWidth(double width) {this.width = width;}public double getHeight() {return height;}public void setHeight(double height) {this.height = height;}@Overridepublic double findArea() {return width * height;} }測試類:
package com.atguiau.exer1;public class GeometricTest {public static void main(String[] args) {GeometricTest geomTest = new GeometricTest();Circle c1 = new Circle(1, "blue", 2);Circle c2 = new Circle(1, "white", 3);double area1 = geomTest.displayGeometricObject(c1);System.out.println("面積為" + area1);double area2 = geomTest.displayGeometricObject(c2);System.out.println("面積為" + area2);double area3 = geomTest.displayGeometricObject(new MyRectangle("yellow", 2, 1, 2));System.out.println("面積為" + area3);boolean isEquals = geomTest.equalsArae(c1, c2);System.out.println("面積是否相等:" + isEquals);}public boolean equalsArae(GeometricObject o1, GeometricObject o2) {return o1.findArea() == o2.findArea();}public double displayGeometricObject(GeometricObject o) {return o.findArea();} }
面試題:多態是編譯時行為還是運行時行為?如何證明?
運行時行為。
證明:
若此段代碼不運行起來,我們永遠不知道輸出結果是什么。
筆試題:判斷以下程序的輸出結果
package com.atguigu.exer;//考查多態的筆試題目: public class InterviewTest1 {public static void main(String[] args) {Base1 base = new Sub1();//多態性,父類只能調用父類中定義過的子類重寫的方法base.add(1, 2, 3);//sub_1Sub1 s = (Sub1)base;s.add(1,2,3);//sub_2} }class Base1 {public void add(int a, int... arr) {System.out.println("base1");} }class Sub1 extends Base1 {public void add(int a, int[] arr) {System.out.println("sub_1");}public void add(int a, int b, int c) {System.out.println("sub_2");} }① int... arr與int[] arr在java看來是相同的,因此子類Sub1中定義的add(int a, int[] arr)是對父類Base1中定義的add(int a, int… arr)的重寫(同名同參數的方法)。在調用base.add(1, 2, 3)時,父類的引用指向子類的對象,執行的應為子類的方法(sub_1)。add(int a, int b, int c)不能與前兩個看做同類型的,因此不會改變base.add(1, 2, 3)的執行結果。
② 但是,將base強制轉換為Sub1類型后,在執行時,只在Sub1類內部執行,此時,會優先執行參數數量精確匹配的方法(sub_2)。
5.7 Object類的使用
- java.lang.Object類
- Object類是所有Java類的根父類
- 如果在類的聲明中未使用extends關鍵字指明其父類,則默認父類為java.lang.Object類
- Object類中的功能(屬性、方法)就具有通用性。
屬性:無
方法:equals() / toString() / getClass() /hashCode() / clone() / finalize()
wait() 、 notify()、notifyAll()
finalize()是在系統立即回收前通知對象調用的方法,System.gc();//強制性釋放空間,子類重寫此方法,可在釋放對象前進行某些操作。但一般不提倡程序員主動調用此方法。 - Object類只聲明了一個空參的構造器
5.7.1 ==操作符與equals方法
- 面試題: == 和 equals() 區別
== 運算符的使用:
如果比較的是引用數據類型變量:比較兩個對象的地址值是否相同.即兩個引用是否指向同一個對象實體
equals()方法的使用:
equals()是一個方法,而非運算符
只能適用于引用數據類型
Object類中equals()的定義:
public boolean equals(Object obj) {return (this == obj);}說明:Object類中定義的equals()和==的作用是相同的:比較兩個對象的地址值是否相同.即兩個引用是否指向同一個對象實體
像String、Date、File、包裝類等都重寫了Object類中的equals()方法。重寫以后,比較的不是兩個引用的地址是否相同,而是比較兩個對象的"實體內容"是否相同。
通常情況下,我們自定義的類如果使用equals()的話,也通常是比較兩個對象的"實體內容"是否相同。那么,我們就需要對Object類中的equals()進行重寫。
重寫的原則:比較兩個對象的實體內容是否相同。
- Customer類中equals()方法的重寫:
重寫equals()方法的原則:
package com.atguigu.java1;public class Customer {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Customer() {super();}public Customer(String name, int age) {super();this.name = name;this.age = age;}//重寫的原則:比較兩個對象的實體內容(即:name和age)是否相同//手動實現equals()的重寫@Overridepublic boolean equals(Object obj) {System.out.println("Customer equals()....");//比較地址值是否相等if (this == obj) {return true;}//判斷類型是否一致if(obj instanceof Customer){Customer cust = (Customer)obj;//向下轉型,為了使用子類的方法//比較兩個對象的每個屬性是否都相同//age是基本數據類型,使用==; name是引用數據類型,用equalsreturn this.age == cust.age && this.name.equals(cust.name);}return false;} } 實際開發中:直接自動生成(Source -> Generate hasCode() and equals())package com.atguigu.java1;public class Customer {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Customer() {super();}public Customer(String name, int age) {super();this.name = name;this.age = age;}//自動生成的equals()@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;Customer other = (Customer) obj;if (age != other.age)return false;if (name == null) {if (other.name != null)return false;} else if (!name.equals(other.name))return false;return true;} } - 例題
5.7.2 toString() 方法
- Object類中toString()的使用:
-
大多數情況下,當我們輸出一個對象的引用時,實際上就是調用當前對象的toString()
當對象為null時,打印輸出此對象時顯示null,但如果使用toString()方法,會出現NullPointerException——println內部有保護機制
-
Object類中toString()的定義:
public String toString() {return getClass().getName() + "@" + Integer.toHexString(hashCode());}``` -
像String、Date、File、包裝類等都重寫了Object類中的toString()方法。使得在調用對象的toString()時,返回"實體內容"信息
-
自定義類也可以重寫toString()方法,當調用此方法時,返回對象的"實體內容"
package com.atguigu.java1;import java.util.Date;public class ToStringTest {public static void main(String[] args) {Customer cust1 = new Customer("Tom",21);System.out.println(cust1.toString());//com.atguigu.java1.Customer@15db9742-->Customer[name = Tom,age = 21]System.out.println(cust1);//com.atguigu.java1.Customer@15db9742-->Customer[name = Tom,age = 21]String str = new String("MM");System.out.println(str);//MMDate date = new Date(4534534534543L);System.out.println(date.toString());//Mon Sep 11 08:55:34 GMT+08:00 2113} } - toString()方法的重寫package com.atguigu.java1;public class Customer {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Customer() {super();}public Customer(String name, int age) {super();this.name = name;this.age = age;}//自動生成的equals()@Overridepublic boolean equals(Object obj) {if (this == obj)return true;if (obj == null)return false;if (getClass() != obj.getClass())return false;Customer other = (Customer) obj;if (age != other.age)return false;if (name == null) {if (other.name != null)return false;} else if (!name.equals(other.name))return false;return true;}//手動實現 // @Override // public String toString() { // return "Customer[name = " + name + ",age = " + age + "]"; // }//自動實現@Overridepublic String toString() {return "Customer [name=" + name + ", age=" + age + "]";} }
- 例題
定義兩個類,父類GeometricObject代表幾何形狀,子類Circle代表圓形。
寫一個測試類,創建兩個Circle對象,判斷其顏色是否相等;利用equals方法判斷其半徑是否相等;利用toString()方法輸出其半徑。
父類GeometricObject:package com.atguigu.exer3;public class GeometricObject {protected String color;protected double weight;public GeometricObject() {super();this.color = "White";this.weight = 1.0;}public GeometricObject(String color, double weight) {super();this.color = color;this.weight = weight;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public double getWeight() {return weight;}public void setWeight(double weight) {this.weight = weight;} } 子類Circle:package com.atguigu.exer3;public class Circle extends GeometricObject {private double radius;public Circle() {super();this.radius = 1.0;}public double getRadius() {return radius;}public Circle(double radius) {super();this.radius = radius;}public Circle(double radius, String color, double weight) {super(color, weight);this.radius = radius;}public void setRadius(double radius) {this.radius = radius;}//求圓的面積public double findArea() {return Math.PI * radius * radius;}//重寫equals方法,比較兩個圓的半徑是否相等,如相等,返回true。@Overridepublic boolean equals(Object obj) {if(this == obj){return true;}if(obj instanceof Circle) {Circle cir = (Circle)obj;return this.radius == cir.radius;}return false;}@Override//重寫toString方法,輸出圓的半徑。public String toString() {return "Circle[radius = " + this.radius + "]";}} 測試類 :package com.atguigu.exer3;public class CircleTest {public static void main(String[] args) {Circle c1 = new Circle(3.3);Circle c2 = new Circle(3.3, "White", 2.0);//color是字符串,需要用equals判斷是否相等System.out.println("顏色是否相等:" + c1.getColor().equals(c2.color));System.out.println("半徑是否相等:" + c1.equals(c2));System.out.println(c1);//Circle[radius = 3.3]System.out.println(c1.toString());//Circle[radius = 3.3]} }
5.8 包裝類的使用
5.8.1 單元測試方法
- 步驟:
- 選中當前工程 - 右鍵選擇:build path - add libraries - JUnit 4 - 下一步
(實際開發中只需在需要創建注解@Test后在進行導入即可) - 創建Java類,進行單元測試。
此時的Java類要求:① 此類是public的 ②此類提供公共的無參的構造器 - 此類中聲明單元測試方法。
此時的單元測試方法要求:方法的權限是public,沒有返回值,沒有形參 - 此單元測試方法上需要聲明注解:@Test,并在單元測試類中導入:import org.junit.Test;
- 聲明好單元測試方法以后,就可以在方法體內測試相關的代碼。
- 寫完代碼以后,左鍵雙擊單元測試方法名,右鍵:run as - JUnit Test
說明:
① 如果執行結果沒有任何異常:綠條
② 如果執行結果出現異常:紅條 package com.atguigu.java2;import java.util.Date;import org.junit.Test;public class JUnitTest {int num = 10;@Testpublic void testEquals(){String s1 = "MM";String s2 = "MM";System.out.println(s1.equals(s2));//ClassCastException的異常
// Object obj = new String("GG");
// Date date = (Date)obj;System.out.println(num);show();}public void show(){num = 20;System.out.println("show()....");}@Testpublic void testToString(){String s2 = "MM";System.out.println(s2.toString());}
}
5.8.2 包裝類
- 包裝類(Wrapper)的使用:
- 為什么要有包裝類?
java針對八種基本數據類型提供了相應的引用類型—包裝類(封裝類),使得基本數據類型的變量具有類的特征,就可以調用類中的方法,Java才是真正的面向對象
- 掌握的:基本數據類型、包裝類、String三者之間的相互轉換
記住紅色部分的方法即可。
簡易版:
基本數據類型 <----------> 包裝類:JDK 5.0新特性:自動裝箱與自動拆箱
基本數據類型、包裝類 ------> String:調用String重載的ValueOf(Xxx xxx)
String ------> 基本數據類型、包裝類:調用包裝類的parseXxx(String s)package com.atguigu.java2;import org.junit.Test;public class WrapperTest {//String類型 --->基本數據類型、包裝類:調用包裝類的parseXxx(String s)@Testpublic void test5(){String str1 = "123";//錯誤的情況: // int num1 = (int)str1; // Integer in1 = (Integer)str1;//可能會報NumberFormatException(String str1 = "123a")int num2 = Integer.parseInt(str1);System.out.println(num2 + 1);String str2 = "true1";boolean b1 = Boolean.parseBoolean(str2);System.out.println(b1);//false}//基本數據類型、包裝類--->String類型:調用String重載的valueOf(Xxx xxx)@Testpublic void test4(){int num1 = 10;//方式1:連接運算String str1 = num1 + "";//方式2:調用String的valueOf(Xxx xxx)float f1 = 12.3f;String str2 = String.valueOf(f1);//"12.3"Double d1 = new Double(12.4);String str3 = String.valueOf(d1);System.out.println(str2);//"12.3"System.out.println(str3);//"12.4"}/** JDK 5.0 新特性:自動裝箱 與自動拆箱*/@Testpublic void test3(){ // int num1 = 10; // //基本數據類型-->包裝類的對象 // method(num1);//自動裝箱:基本數據類型 --->包裝類int num2 = 10;Integer in1 = num2;//自動裝箱boolean b1 = true;Boolean b2 = b1;//自動裝箱//自動拆箱:包裝類--->基本數據類型System.out.println(in1.toString());int num3 = in1;//自動拆箱}public void method(Object obj){System.out.println(obj);}//包裝類--->基本數據類型:調用包裝類Xxx的xxxValue() // @Test // public void test2(){ // Integer in1 = new Integer(12); // // int i1 = in1.intValue(); // System.out.println(i1 + 1); // // // Float f1 = new Float(12.3); // float f2 = f1.floatValue(); // System.out.println(f2 + 1); // }//基本數據類型 --->包裝類:調用包裝類的構造器@Testpublic void test1(){// int num1 = 10; System.out.println(num1.toString()); // Integer in1 = new Integer(num1); // System.out.println(in1.toString()); // // Integer in2 = new Integer("123"); // System.out.println(in2.toString()); // // //報異常 Integer in3 = new Integer("123abc"); System.out.println(in3.toString()); // // Float f1 = new Float(12.3f); // Float f2 = new Float("12.3"); // System.out.println(f1); // System.out.println(f2); // // Boolean b1 = new Boolean(true); // Boolean b2 = new Boolean("TrUe"); // System.out.println(b2);//true // Boolean b3 = new Boolean("true123"); // System.out.println(b3);//false // Order order = new Order();System.out.println(order.isMale);//false//boolean的默認值為falseSystem.out.println(order.isFemale);//null//類的默認值為null}}class Order{boolean isMale;Boolean isFemale; }
面試題:
如下兩個題目輸出結果相同嗎?各是什么?
import org.junit.Test;public class InterviewTest {@Testpublic void test1() {Object o1 = true ? new Integer(1) : new Double(2.0);System.out.println(o1);// 1.0}@Testpublic void test2() {Object o2;if (true)o2 = new Integer(1);elseo2 = new Double(2.0);System.out.println(o2);// 1}不同。
test1()中使用到了三元運算符,其在編譯時會對表達式1和表達式2的類型進行統一,因此new Integer(1) 被自動類型提升為double類型的1.0。
test2()為if-else結構,不會進行自動類型提升。
判斷輸出結果。
import org.junit.Test;@Testpublic void test3() {Integer i = new Integer(1);Integer j = new Integer(1);System.out.println(i == j);//false//引用類型的變量使用==比較的是地址值//Integer內部定義了IntegerCache結構,IntegerCache中定義了Integer[],//保存了從-128~127范圍的整數。如果我們使用自動裝箱的方式,給Integer賦值的范圍在//-128~127范圍內時,可以直接使用數組中的元素,不用再去new了。目的:提高效率Integer m = 1;Integer n = 1;System.out.println(m == n);//trueInteger x = 128;//相當于new了一個Integer對象Integer y = 128;//相當于new了一個Integer對象System.out.println(x == y);//false}}① 對于i == j的判斷:
使用new即為在堆空間中創建新的對象,因此地址值不同。
② 對于m == n的判斷:
Integer內部定義了IntegerCache結構,IntegerCache中定義了Integer[],保存了從-128~127范圍的整數。如果我們使用自動裝箱的方式,給Integer賦值的范圍在-128~127范圍內時,可以直接使用數組中的元素,不用再去new了(目的:提高效率)。此時不論新創建幾個對象,其指向的內存地址值是相同的。
③ 對于x == y的判斷:
IntegerCache中定義的Integer[]數組范圍為-128~127,而128在范圍外,因此不會收到影響。在創建對象時不論是使用new還是自動裝箱的方式都需要在堆空間中創建新的對象,因此地址值不同。
- 練習題:
利用Vector代替數組處理:從鍵盤讀入學生成績(以負數代表輸入結束),找出最高分,并輸出學生成績等級。- 提示:數組一旦創建,長度就固定不變,所以在創建數組前就需要知道它的長度。而向量類java.util.Vector可以根據需要動態伸縮。
- 創建Vector對象:Vector v=new Vector();
- 給向量添加元素:v.addElement(Object obj); //obj必須是對象
- 取出向量中的元素:Object obj=v.elementAt(0);
注意第一個元素的下標是0,返回值是Object類型的。 - 計算向量的長度:v.size();
- 若與最高分相差10分內:A等;20分內:B等;30分內:C等;其它:D等
總結
以上是生活随笔為你收集整理的尚硅谷Java入门视频教程第五章——面向对象编程(中)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux 2.6.32 sdxc 补丁
- 下一篇: 浅谈组织块(OB)