5.1.1 繼承的實現
面向對象的三大特徵:繼承、封裝、多態
繼承讓我們更加容易實現類的擴展。 比如,我們定義了人類,再定義Boy類就只需要擴展人類即可。實現了代碼的重用,不用再重新發明輪子(don’t reinvent wheels)。
從英文字面意思理解,extends的意思是“擴展”。
在我們編程中,如果新定義一個Student類,發現已經有Person類包含了我們需要的屬性和方法,那麼Student類只需要繼承Person類即可擁有Person類的屬性和方法。
package cn.nls.oo2;
/**
* 測試繼承
* @author
*
*/
public class TestExtends {
public static void main(String[] args) {
Student stu = new Student();
stu.name="大哥";
stu.height=177;
stu.rest();//休息一會!
Student stu2 = new Student("小弟",222,"高數");
/*測試instanceof*/
System.out.println(stu2 instanceof Student);//true
System.out.println(stu2 instanceof Person);//true
System.out.println(stu2 instanceof Object);//true
System.out.println(new Person() instanceof Student);//false
}
}
class Person /*extends Object*/{
String name;
int height;
public void rest() {
System.out.println("休息一會!");
}
}
class Student extends Person{
//String name;
//int height;
String major;
public void study() {
System.out.println("學習兩小時!");
}
//public void rest() {
// System.out.println("休息一會!");
//}
public Student(String name,int height,String major) {
this.name = name;
this.height= height;
this.major = major;
}
public Student() {
}
}
instanceof是二元運算符,左邊是對象,右邊是類;當對象是右面類或子類所創建對象時,返回true;否則,返回false。
5.1.3 繼承使用要點
可以使用ctrl+T方便的查看類的繼承層次結構
1.父類也稱作超類、基類、派生類等。
2.Java中只有單繼承,沒有像C++那樣的多繼承。多繼承會引起混亂,使得繼承鏈過於複雜,系統難於維護。
3.Java中類沒有多繼承,接口有多繼承。
4.子類繼承父類,可以得到父類的全部屬性和方法 (除了父類的構造方法),但不見得可以直接訪問(比如,父類私有的屬性和方法)。
5.如果定義一個類時,沒有調用extends,則它的父類是:java.lang.Object。
5.1.4 方法的重寫override
子類通過重寫父類的方法,可以用自身的行爲替換父類的行爲。方法的重寫是實現多態的必要條件。
方法的重寫需要符合下面的三個要點:
1.“==”: 方法名、形參列表相同。
2.“≤”:返回值類型和聲明異常類型,子類小於等於父類。
3.“≥”: 訪問權限,子類大於等於父類。
package cn.nls.oo2;
/**
* 測試重寫(override)/覆蓋
* @author
*
*/
public class TestOverride {
public static void main(String[] args) {
Horse h = new Horse();
h.run();
}
}
class Vehicle{
public void run() {
System.out.println("跑....");
}
public void stop() {
System.out.println("停止!");
}
public Person whoIsPsg() {
return new Person();
}
}
class Horse extends Vehicle{
public void run() {//重寫
System.out.println("四蹄翻飛,嘚嘚嘚。。。");
}
public Student whoIsPsg() {//重寫,返回類型student,比Person小,即小於父類類型,可以
return new Student();
}
}
5.2.1 Object
Object類是所有Java類的根基類,也就意味着所有的Java對象都擁有Object類的屬性和方法。如果在類的聲明中未使用extends關鍵字指明其父類,則默認繼承Object類。
package cn.nls.oo2;
public class TestObject {
public static void main(String[] args) {
//Object obj;
TestObject to = new TestObject();
System.out.println(to.toString());
Person2 p2 = new Person2("狗子",66);
System.out.println(p2.toString());
}
public String toString() {//toString的重寫
return "測試Object對象";
}
}
class Person2{
String name;
int age;
public String toString() {
return name+",年齡:"+age;
}
public Person2(String name,int age) {
this.name = name;
this.age = age;
}
}
5.2.3 ==和equals方法
==”代表比較雙方是否相同。如果是基本類型則表示值相等,如果是引用類型則表示地址相等即是同一個對象。
Object類中定義有:public boolean equals(Object obj)方法,提供定義“對象內容相等”的邏輯。比如,我們在公安系統中認爲id相同的人就是同一個人、學籍系統中認爲學號相同的人就是同一個人。
Object 的 equals 方法默認就是比較兩個對象的hashcode,是同一個對象的引用時返回 true 否則返回 false。但是,我們可以根據我們自己的要求重寫equals方法。
package cn.nls.oo2;
public class TestEquals {
public static void main(String[] args) {
Object obj;
String str;
User u1 = new User(1999,"高琪","123456");
User u2 = new User(1999,"高息","123456");
System.out.println(u1==u2);//false,false
System.out.println(u1.equals(u2));//false,重寫後true
String str1 = new String("sxt");
String str2 = new String("sxt");
System.out.println(str1==str2);//false
System.out.println(str1.equals(str2));//true
}
}
class User{
int id;
String name;
String pwd;
public User(int id, String name, String pwd) {
super();
this.id = id;
this.name = name;
this.pwd = pwd;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
User other = (User) obj;
if (id != other.id)
return false;
return true;
}
}
JDK提供的一些類,如String、Date、包裝類等,重寫了Object的equals方法,調用這些類的equals方法, x.equals (y) ,當x和y所引用的對象是同一類對象且屬性內容相等時(並不一定是相同對象),返回 true 否則返回 false。
5.3 super關鍵字
super是直接父類對象的引用。可以通過super來訪問父類中被子類覆蓋的方法或屬性。
使用super調用普通方法,語句沒有位置限制,可以在子類中隨便調用。
若是構造方法的第一行代碼沒有顯式的調用super(...)或者this(...);那麼Java默認都會調用super(),含義是調用父類的無參數構造方法。這裏的super()可以省略。
package cn.nls.oo2;
public class TestSuper01 {
public static void main(String[] args) {
new ChildClass().f();
}
}
class FatherClass {
public int value;
public void f() {
value = 100;
System.out.println("FatherClass.value="+value);//FatherClass.value=100
}
}
class ChildClass extends FatherClass{
public int value;
public void f() {
super.f();//調用父類對象的普通方法
value = 200;
System.out.println("ChildClass.value="+value);//ChildClass.value=200
System.out.println(value);//200
System.out.println(super.value);//100,調用父類對象的成員變量
}
}
構造方法調用順序:
構造方法第一句總是:super(…)來調用父類對應的構造方法。所以,流程就是:先向上追溯到Object,然後再依次向下執行類的初始化塊和構造方法,直到當前子類爲止。
注:靜態初始化塊調用順序,與構造方法調用順序一樣,不再重複。
package cn.nls.oo2;
public class TestSuper02 {
public static void main(String[] args) {
System.out.println("開始創建一個ChildClass對象....");
new ChildClass02();
}
}
class FatherClass02{
public FatherClass02() {
System.out.println("創建FatherClass02");
}
}
class ChildClass02 extends FatherClass02{
public ChildClass02() {
//super();
System.out.println("創建ChildClass02");
}
}
5.4封裝
我們程序設計要追求“高內聚,低耦合”。 高內聚就是類的內部數據操作細節自己完成,不允許外部干涉;低耦合是僅暴露少量的方法給外部使用,儘量方便外部調用。
編程中封裝的具體優點:
1. 提高代碼的安全性。
2. 提高代碼的複用性。
3. “高內聚”:封裝細節,便於修改內部代碼,提高可維護性。
4. “低耦合”:簡化外部調用,便於調用者使用,便於擴展和協作。
5.4.2 封裝的實現-使用訪問控制符
Java是使用“訪問控制符”來控制哪些細節需要封裝,哪些細節需要暴露的。 Java中4種“訪問控制符”分別爲private、default、protected、public,它們說明了面向對象的封裝性,所以我們要利用它們儘可能的讓訪問權限降到最低,從而提高安全性。
下面詳細講述它們的訪問權限問題。其訪問權限範圍如表5-1所示。
表5-1 訪問權限修飾符
1. private 表示私有,只有自己類能訪問
2. default表示沒有修飾符修飾,只有同一個包的類能訪問
3. protected表示可以被同一個包的類以及其他包中的子類訪問
4. public表示可以被該項目的所有包中的所有類訪問
代碼1:
package cn.nls.oo2;
/**
* 測試封裝
* @author
*
*/
public class TestEncapsulation {
public static void main(String[] args) {
Human h = new Human();
//h.age=10;//age不可見
h.name = "高琪";
h.Height = 8;
}
}
/*
class Human{
private int age;
String name;
void sayAge() {
System.out.println(age);
}
}*/
class Boy extends Human{
void sayHello() {
//System.out.println(age);//報錯,子類無法使用父類的私有屬性和方法
}
}
代碼2:
package cn.nls.oo;
import cn.nls.oo2.Human;
public class TestEncapsulation2 {
public static void main(String[] args) {
Human h = new Human();
//h.age=10;//age不可見
//h.name = "高琪"; //name爲default屬性,不能被不同包的類訪問
//h.Height = 8;//protected屬性,同包可見,不同包不可見
h.sayAge();
}
}
class Girl extends Human{
void sayGood() {
System.out.println(Height);
}
}
代碼3:
package cn.nls.oo2;
public class Human {
private int age;
String name; //,默認default,可以被本包下面的類訪問
protected int Height;
public void sayAge() {
System.out.println(age);
}
}
5.4.3 封裝的使用細節
類的屬性的處理:
1. 一般使用private訪問權限。
2. 提供相應的get/set方法來訪問相關屬性,這些方法通常是public修飾的,以提供對屬性的賦值與讀取操作(注意:boolean變量的get方法是is開頭!)。
3. 一些只用於本類的輔助性方法可以用private修飾,希望其他類調用的方法用public修飾。
javaBean封裝實例
package cn.nls.oo2;
/**
* 仍然測試測試封裝
* @author
*
*/
public class Person4 {
private int id;
private String name;
private int age;
private boolean man;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setAge(int age) {
if(age>=1&&age<=130) {
this.age = age;
}else {
System.out.println("請輸入正常的年齡!");
}
}
public int getAge() {
return this.age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public boolean isMan() {
return man;
}
public void setMan(boolean man) {
this.man = man;
}
}
5.5 多態 (polymorphism)
多態指的是同一個方法調用,由於對象不同可能會有不同的行爲。
多態的要點:
1. 多態是方法的多態,不是屬性的多態(多態與屬性無關)。
2. 多態的存在要有3個必要條件:繼承,方法重寫,父類引用指向子類對象。
3. 父類引用指向子類對象後,用該父類引用調用子類重寫的方法,此時多態就出現了。
package cn.nls.oo2;
/**
* 測試多態
* @author
*
*/
public class TestPolym {
public static void main(String[] args) {
Animal a = new Animal();
animalCry(a);//叫了一聲!
Dog d = new Dog();
animalCry(d);//旺旺旺
}
static void animalCry(Animal a) {
a.shout();
}
}
class Animal{
public void shout() {
System.out.println("叫了一聲!");
}
}
class Dog extends Animal{
public void shout() {
System.out.println("旺旺旺");
}
}
class Cat extends Animal{
public void shout() {
System.out.println("喵喵喵!");
}
}
展示了多態最爲多見的一種用法,即父類引用做方法的形參,實參可以是任意的子類對象,可以通過不同的子類對象實現不同的行爲方式。
由此,我們可以看出多態的主要優勢是提高了代碼的可擴展性,符合開閉原則。但是多態也有弊端,就是無法調用子類特有的功能,比如,我不能使用父類的引用變量調用Dog類特有的seeDoor()方法。
5.6 對象的轉型(casting)
父類引用指向子類對象,我們稱這個過程爲向上轉型,屬於自動類型轉換。
向上轉型後的父類引用變量只能調用它編譯類型的方法,不能調用它運行時類型的方法。這時,我們就需要進行類型的強制轉換,我們稱之爲向下轉型!
在向下轉型過程中,必須將引用變量轉成真實的子類類型(運行時類型)否則會出現類型轉換異常ClassCastException。
package cn.nls.oo2;
/**
* 測試多態
* @author
*
*/
public class TestPolym {
public static void main(String[] args) {
Animal a = new Animal();
animalCry(a);//叫了一聲!
Dog d = new Dog();
animalCry(d);//旺旺旺
Animal dd = new Dog();//自動向上轉型
animalCry(dd);
Dog dd2 = (Dog)dd;//強制向下轉型
//dd.seeDoor();//報錯
dd2.seeDoor();//
//Animal c = new Cat();
//Dog dd3 = (Dog)c;//類型轉化錯誤
//dd3.seeDoor();//報錯
}
static void animalCry(Animal a) {
a.shout();
}
}
class Animal{
public void shout() {
System.out.println("叫了一聲!");
}
}
class Dog extends Animal{
public void shout() {
System.out.println("旺旺旺");
}
public void seeDoor() {
System.out.println("看門!!");
}
}
class Cat extends Animal{
public void shout() {
System.out.println("喵喵喵!");
}
}
5.7 final關鍵字
final關鍵字的作用:
1. 修飾變量: 被他修飾的變量不可改變。一旦賦了初值,就不能被重新賦值。
1 |
|
2. 修飾方法:該方法不可被子類重寫。但是可以被重載!
1 |
|
3. 修飾類: 修飾的類不能被繼承。比如:Math、String等。
1 |
|
5.8 抽象方法和抽象類
抽象方法
使用abstract修飾的方法,沒有方法體,只有聲明。定義的是一種“規範”,就是告訴子類必須要給抽象方法提供具體的實現。
·抽象類
包含抽象方法的類就是抽象類。通過abstract方法定義規範,然後要求子類必須定義具體實現。通過抽象類,我們就可以做到嚴格限制子類的設計,使子類之間更加通用。
抽象類的使用要點:
1. 有抽象方法的類只能定義成抽象類
2. 抽象類不能實例化,即不能用new來實例化抽象類。
3. 抽象類可以包含屬性、方法、構造方法。但是構造方法不能用來new實例,只能用來被子類調用。
4. 抽象類只能用來被繼承。
5. 抽象方法必須被子類實現。
package cn.nls.oop;
/**
* 抽象類的意義就在於:爲子類提供統一的、規範的模板。子類必須實現相關的抽象方法
* @author
*
*/
public abstract class Animal {
//第一:沒有實現。第二:子類必須實現
abstract public void shout();
public void run() {
System.out.println("跑跑");
}
public static void main(String[] args) {
Animal a = new Dog();
}
}
class Dog extends Animal{
@Override
public void shout() {
System.out.println("汪汪汪");
}
}