java构造函数重载继承_Java基础-继承 - 写代码换盆的个人空间 - OSCHINA - 中文开源技术交流社区...
訪問權(quán)限
Java 中有三個(gè)訪問權(quán)限修飾符:private、protected 以及 public,如果不加訪問修飾符,表示包級可見。
可以對類或類中的成員(字段和方法)加上訪問修飾符。
類可見表示其它類可以用這個(gè)類創(chuàng)建實(shí)例對象。
成員可見表示其它類可以用這個(gè)類的實(shí)例對象訪問到該成員;
protected 用于修飾成員,表示在繼承體系中成員對于子類可見,但是這個(gè)訪問修飾符對于類沒有意義。
設(shè)計(jì)良好的模塊會隱藏所有的實(shí)現(xiàn)細(xì)節(jié),把它的 API 與它的實(shí)現(xiàn)清晰地隔離開來。模塊之間只通過它們的 API 進(jìn)行通信,一個(gè)模塊不需要知道其他模塊的內(nèi)部工作情況,這個(gè)概念被稱為信息隱藏或封裝。因此訪問權(quán)限應(yīng)當(dāng)盡可能地使每個(gè)類或者成員不被外界訪問。
如果子類的方法重寫了父類的方法,那么子類中該方法的訪問級別不允許低于父類的訪問級別。這是為了確保可以使用父類實(shí)例的地方都可以使用子類實(shí)例去代替,也就是確保滿足里氏替換原則。
字段決不能是公有的,因?yàn)檫@么做的話就失去了對這個(gè)字段修改行為的控制,客戶端可以對其隨意修改。例如下面的例子中,AccessExample 擁有 id 公有字段,如果在某個(gè)時(shí)刻,我們想要使用 int 存儲 id 字段,那么就需要修改所有的客戶端代碼。
public class AccessExample {
public String id;
}
可以使用公有的 getter 和 setter 方法來替換公有字段,這樣的話就可以控制對字段的修改行為。
public class AccessExample {
private int id;
public String getId() {
return id + "";
}
public void setId(String id) {
this.id = Integer.valueOf(id);
}
}
但是也有例外,如果是包級私有的類或者私有的嵌套類,那么直接暴露成員不會有特別大的影響。
public class AccessWithInnerClassExample {
private class InnerClass {
int x;
}
private InnerClass innerClass;
public AccessWithInnerClassExample() {
innerClass = new InnerClass();
}
public int getValue() {
return innerClass.x; // 直接訪問
}
}
抽象類與接口
1. 抽象類
抽象類和抽象方法都使用 abstract 關(guān)鍵字進(jìn)行聲明。如果一個(gè)類中包含抽象方法,那么這個(gè)類必須聲明為抽象類。
抽象類和普通類最大的區(qū)別是,抽象類不能被實(shí)例化,只能被繼承。
public abstract class AbstractClassExample {
protected int x;
private int y;
public abstract void func1();
public void func2() {
System.out.println("func2");
}
}
public class AbstractExtendClassExample extends AbstractClassExample {
@Override
public void func1() {
System.out.println("func1");
}
}
// AbstractClassExample ac1 = new AbstractClassExample(); // 'AbstractClassExample' is abstract; cannot be instantiated
AbstractClassExample ac2 = new AbstractExtendClassExample();
ac2.func1();
2. 接口
接口是抽象類的延伸,在 Java 8 之前,它可以看成是一個(gè)完全抽象的類,也就是說它不能有任何的方法實(shí)現(xiàn)。
從 Java 8 開始,接口也可以擁有默認(rèn)的方法實(shí)現(xiàn),這是因?yàn)椴恢С帜J(rèn)方法的接口的維護(hù)成本太高了。在 Java 8 之前,如果一個(gè)接口想要添加新的方法,那么要修改所有實(shí)現(xiàn)了該接口的類,讓它們都實(shí)現(xiàn)新增的方法。
接口的成員(字段 + 方法)默認(rèn)都是 public 的,并且不允許定義為 private 或者 protected。從 Java 9 開始,允許將方法定義為 private,這樣就能定義某些復(fù)用的代碼又不會把方法暴露出去。
接口的字段默認(rèn)都是 static 和 final 的。
public interface InterfaceExample {
void func1();
default void func2(){
System.out.println("func2");
}
int x = 123;
// int y; // Variable 'y' might not have been initialized
public int z = 0; // Modifier 'public' is redundant for interface fields
// private int k = 0; // Modifier 'private' not allowed here
// protected int l = 0; // Modifier 'protected' not allowed here
// private void fun3(); // Modifier 'private' not allowed here
}
public class InterfaceImplementExample implements InterfaceExample {
@Override
public void func1() {
System.out.println("func1");
}
}
// InterfaceExample ie1 = new InterfaceExample(); // 'InterfaceExample' is abstract; cannot be instantiated
InterfaceExample ie2 = new InterfaceImplementExample();
ie2.func1();
System.out.println(InterfaceExample.x);
3. 比較
從設(shè)計(jì)層面上看,抽象類提供了一種 IS-A 關(guān)系,需要滿足里式替換原則,即子類對象必須能夠替換掉所有父類對象。而接口更像是一種 LIKE-A 關(guān)系,它只是提供一種方法實(shí)現(xiàn)契約,并不要求接口和實(shí)現(xiàn)接口的類具有 IS-A 關(guān)系。
從使用上來看,一個(gè)類可以實(shí)現(xiàn)多個(gè)接口,但是不能繼承多個(gè)抽象類。
接口的字段只能是 static 和 final 類型的,而抽象類的字段沒有這種限制。
接口的成員只能是 public 的,而抽象類的成員可以有多種訪問權(quán)限。
4. 使用選擇
使用接口:
需要讓不相關(guān)的類都實(shí)現(xiàn)一個(gè)方法,例如不相關(guān)的類都可以實(shí)現(xiàn) Comparable 接口中的 compareTo() 方法;
需要使用多重繼承。
使用抽象類:
需要在幾個(gè)相關(guān)的類中共享代碼。
需要能控制繼承來的成員的訪問權(quán)限,而不是都為 public。
需要繼承非靜態(tài)和非常量字段。
在很多情況下,接口優(yōu)先于抽象類。因?yàn)榻涌跊]有抽象類嚴(yán)格的類層次結(jié)構(gòu)要求,可以靈活地為一個(gè)類添加行為。并且從 Java 8 開始,接口也可以有默認(rèn)的方法實(shí)現(xiàn),使得修改接口的成本也變的很低。
super
訪問父類的構(gòu)造函數(shù):可以使用 super() 函數(shù)訪問父類的構(gòu)造函數(shù),從而委托父類完成一些初始化的工作。應(yīng)該注意到,子類一定會調(diào)用父類的構(gòu)造函數(shù)來完成初始化工作,一般是調(diào)用父類的默認(rèn)構(gòu)造函數(shù),如果子類需要調(diào)用父類其它構(gòu)造函數(shù),那么就可以使用 super() 函數(shù)。
訪問父類的成員:如果子類重寫了父類的某個(gè)方法,可以通過使用 super 關(guān)鍵字來引用父類的方法實(shí)現(xiàn)。
public class SuperExample {
protected int x;
protected int y;
public SuperExample(int x, int y) {
this.x = x;
this.y = y;
}
public void func() {
System.out.println("SuperExample.func()");
}
}
public class SuperExtendExample extends SuperExample {
private int z;
public SuperExtendExample(int x, int y, int z) {
super(x, y);
this.z = z;
}
@Override
public void func() {
super.func();
System.out.println("SuperExtendExample.func()");
}
}
SuperExample e = new SuperExtendExample(1, 2, 3);
e.func();
SuperExample.func()
SuperExtendExample.func()
重寫與重載
1. 重寫(Override)
存在于繼承體系中,指子類實(shí)現(xiàn)了一個(gè)與父類在方法聲明上完全相同的一個(gè)方法。
為了滿足里式替換原則,重寫有以下三個(gè)限制:
子類方法的訪問權(quán)限必須大于等于父類方法;
子類方法的返回類型必須是父類方法返回類型或?yàn)槠渥宇愋汀?/p>
子類方法拋出的異常類型必須是父類拋出異常類型或?yàn)槠渥宇愋汀?/p>
使用 @Override 注解,可以讓編譯器幫忙檢查是否滿足上面的三個(gè)限制條件。
下面的示例中,SubClass 為 SuperClass 的子類,SubClass 重寫了 SuperClass 的 func() 方法。其中:
子類方法訪問權(quán)限為 public,大于父類的 protected。
子類的返回類型為 ArrayList,是父類返回類型 List 的子類。
子類拋出的異常類型為 Exception,是父類拋出異常 Throwable 的子類。
子類重寫方法使用 @Override 注解,從而讓編譯器自動檢查是否滿足限制條件。
class SuperClass {
protected List func() throws Throwable {
return new ArrayList<>();
}
}
class SubClass extends SuperClass {
@Override
public ArrayList func() throws Exception {
return new ArrayList<>();
}
}
在調(diào)用一個(gè)方法時(shí),先從本類中查找看是否有對應(yīng)的方法,如果沒有再到父類中查看,看是否從父類繼承來。否則就要對參數(shù)進(jìn)行轉(zhuǎn)型,轉(zhuǎn)成父類之后看是否有對應(yīng)的方法。總的來說,方法調(diào)用的優(yōu)先級為:
this.func(this)
super.func(this)
this.func(super)
super.func(super)
/*
A
|
B
|
C
|
D
*/
class A {
public void show(A obj) {
System.out.println("A.show(A)");
}
public void show(C obj) {
System.out.println("A.show(C)");
}
}
class B extends A {
@Override
public void show(A obj) {
System.out.println("B.show(A)");
}
}
class C extends B {
}
class D extends C {
}
public static void main(String[] args) {
A a = new A();
B b = new B();
C c = new C();
D d = new D();
// 在 A 中存在 show(A obj),直接調(diào)用
a.show(a); // A.show(A)
// 在 A 中不存在 show(B obj),將 B 轉(zhuǎn)型成其父類 A
a.show(b); // A.show(A)
// 在 B 中存在從 A 繼承來的 show(C obj),直接調(diào)用
b.show(c); // A.show(C)
// 在 B 中不存在 show(D obj),但是存在從 A 繼承來的 show(C obj),將 D 轉(zhuǎn)型成其父類 C
b.show(d); // A.show(C)
// 引用的還是 B 對象,所以 ba 和 b 的調(diào)用結(jié)果一樣
A ba = new B();
ba.show(c); // A.show(C)
ba.show(d); // A.show(C)
}
2. 重載(Overload)
存在于同一個(gè)類中,指一個(gè)方法與已經(jīng)存在的方法名稱上相同,但是參數(shù)類型、個(gè)數(shù)、順序至少有一個(gè)不同。
應(yīng)該注意的是,返回值不同,其它都相同不算是重載。
class OverloadingExample {
public void show(int x) {
System.out.println(x);
}
public void show(int x, String y) {
System.out.println(x + " " + y);
}
}
public static void main(String[] args) {
OverloadingExample example = new OverloadingExample();
example.show(1);
example.show(1, "2");
}
總結(jié)
以上是生活随笔為你收集整理的java构造函数重载继承_Java基础-继承 - 写代码换盆的个人空间 - OSCHINA - 中文开源技术交流社区...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 伊对如何注销账号(汉典伊字的基本解释)
- 下一篇: java输出链表的值_[剑指offer]