目錄
一、類的繼承
在subroutine.java中:
class Parent{
Parent(){
System.out.println("調用父類的Parent()構造方法");
}
}
class SubParent extends Parent{
SubParent(){
System.out.println("調用子類的subParent()構造方法");
}
}
public class subroutine extends SubParent {
subroutine(){
System.out.println("調用子類的subroutine()構造方法");
}
public static void main(String[] args) {
subroutine obj = new subroutine();
}
}
運行結果爲:
在實例化子類對象時,父類無參構造方法將被自動調用,但又參構造方法不能被自動調用。只能依賴於super關鍵字顯式調用父類的構造方法。創建一個子類對象,將包含一個父類子對象。當實例化子類對象時,父類對象也相應被實例化。
(以下具體代碼)
class text{
text(){
System.out.println("call the constructor of text()");
}
public void doSomething() {
System.out.println("doSomething");
}
protected text dolt() {
return new text();
}
}
public class text2 extends text{
text2(){
super();
System.out.println("call the constructor of text2()");
}
public void doSomething() {
System.out.println("doSomething in class text2");
}
public void doNew() {
System.out.println("doNew here");
}
protected text2 dolt() {
return new text2();
}
public static void main(String[] args) {
text2 obj = new text2();
obj.doSomething(); //執行text2類中的方法
obj.doNew();
obj.dolt(); //執行text2類中的方法
}
}
結果爲:
call the constructor of text() | 創建父類對象 |
call the constructor of text2() | 創建子類對象 |
doSomething in class text2 | 調用text2中的重寫方法 |
doNew here | 調用text2中新添加的方法 |
call the constructor of text() | 調用text2中的dolt()方法,創建對象,先創建一個父類對象 |
call the constructor of text2() | 再創建一個子類對象 |
1. super關鍵字:
可以在子類的構造方法中調用super()語句調用父類的構造方法;也可以在子類中使用super關鍵字調用父類的成員方法(僅public、protected)。
2. 重寫(覆蓋)父類的成員方法:
將父類成員方法的名稱保留,
1)重寫成員方法的實現內容 | |
2)更改成員方法的存儲權限: | 只能從小範圍改到大範圍 |
3)修改成員方法的返回類型: | 遵循:重寫的返回值類型必須是父類該方法的返回值類型的子類 |
重寫方法的調用: |
||
1)用實例化對象是調用不到父類的 | 在內存中仍爲子類對象 | |
2)只能用super關鍵字 |
1)且只能在類方法中調用 2)在main(類外部)是調用不到的 |
|
(最初overrride的設計意圖之一) |
class Father{
Father(){}
public void doSth() {
System.out.println("doFather");
}
}
public class overrideDemo extends Father {
overrideDemo(){}
public void doSth() {
System.out.println("doOverride");
}
public void callFatherdoSth() {
super.doSth(); //用super關鍵字在子類類方法中調用父類重寫方法
}
public static void main(String[] args) {
overrideDemo obj = new overrideDemo();
obj.doSth(); //調用子類方法
obj.callFatherdoSth(); //通過子類中的方法,調用父類重寫方法
//不是用實例化對象調用的
//子類方法中用super關鍵字調用了重寫方法
}
}
結果爲:
如果要調用父類的重寫方法,就在子類中加一個方法,該方法用super關鍵字調用父類重寫方法。若多重繼承,也可以這樣調用,在每個子類中都加上這樣的方法就可以了。
如果要調用
3. 重構:
子類與父類的成員方法:返回值相同、方法名稱相同、參數類型及個數相同;僅方法實現內容不同。
二、Object類
在Java中,所有類都直接或間接繼承了java.lang.Object類。Object類是所有類的父類,是java類層中最高層類。
1)在Object類中主要包括equals()、toString()、clone()、finalize()等方法。由於所有類都是Object類的子類,所以任何類都可以重寫Object類中的方法。
2)Object類中的getClass()、notify()、notifyAll()、wait()等方法不能被重寫,因爲這些方法被定義爲final類型。
1. getclass()方法
返回對象執行時的Class,然後使用此實例調用getName()方法,可以獲得類的名稱。還可與toString()方法聯合使用
2. toString()方法
將一個對象返回爲欸字符串形式,返回類型爲String,返回一個String實例。實際中通常根據需要重寫toString方法。
當對象轉換爲欸字符串或與字符串連接時,將自動調用重寫的toString()方法。
public class ObjectInstance{
public String toString(){ //toString()返回一個String實例
return "在" + getClass().getName() + "類中重寫toString()方法";
}
public static void main(String[] args){
ObjectInstance obj = new ObjectInstance();
System.out.println(obj); //自動調用重寫的toString()方法
}
} //輸出爲:“在ObjectInstance類中重寫toString()方法”
3. equals()方法
“==”比較的時兩個引用是否相等,而equals()方法比較的是實際內容。
而在比較兩個自定義類的實例化對象時,因爲equals()方法的默認實現是使用“==”運算符比較兩個對象的引用地址,而不是比較對象的內容,因此通常需要在自定義類中重寫equals()方法。
class V_one{
public String name;
V_one(){}
V_one(String str){name = str;}
}
class V_two{
public String name;
V_two(){}
V_two(String str){name = str;}
public Boolean equals(V_two v) {
return name.equals(v.name);
}
}
public class OverWriteEquals {
public static void main(String[] args) {
String str1 = new String("123");
String str2 = new String("123");
V_one one1 = new V_one("ab");
V_one one2 = new V_one("ab");
V_two two1 = new V_two("cd");
V_two two2 = new V_two("cd");
System.out.println(str1.equals(str2)); //true
System.out.println(one1.equals(one2)); //false
System.out.println(two1.equals(two2)); //true,因爲調用了重寫的equals()方法
}
}
三、對象類型的轉換
向上轉型(將子類對象視爲父類對象,自動)。子類對象都可以被看作是一個父類對象。
向下轉型,往往會報錯,要顯式類型轉換。在執行向下轉型操作時,如果父類對象不是子類對象的實例,就會發生ClassCastException異常。
四、使用instanceof操作符判斷對象類型
就像上面說的,此時,就要在執行向下轉型之前,要習慣判斷父類對象是否爲子類對象的實例。
可以使用instanceof操作符判斷是否一個類實現了某個接口(接口在後文),也可以用它來判斷一個實例對象是否屬於一個類。
myobject instanceof ExampleClass |
myobject:某類的對象引用
ExampleClass:某個類
返回值爲布爾值
class AA{
//……
}
class BB extends AA{
//……
}
class CC{
//……
}
public class DD extends AA{
public static voiid main(String[] args){
AA a = new AA();
if(a instanceof DD){
DD d = (DD)a; //向下轉型操作
}
if(a instanceof BB){
BB b = (BB)a; //向下轉型操作
}
if(a instanceof CC){ //實例對象a不屬於CC類
//……
}
}
}
五、方法的重載
方法的重載在就是在同一個類中允許同時存在一個以上的同名方法,只要這些方法的參數個數或類型不同即可。但只有返回類型不同也不行,還需要通過參數以及參數的類型來設置。(常規略)
定義不定長參數方法
不定長方法語法:
返回值 方法名(參數數據類型 ...參數名稱) //3個英文句號
public class UncertainLengthAdd {
public static int add(int...a) {
int sum = 0;
for(int i=0;i<a.length;i++) {
sum += a[i];
}
return sum;
}
public static void main(String[] args) {
System.out.println(add(1,2,3)); //6
System.out.println(add(1,2,3,4,5,6)); //21
}
}
六、多態
如果定義一個四邊形類,讓它處理所有繼承該類的對象,根據”向上轉型“原則可以使每個繼承四邊形類的對象作爲draw()方法的參數,然後再draw()方法中作一些限定就可以根據不同圖形類對象繪製相應的圖形,從而以更爲通用的四邊形類來取代具體的正方形類和平行四邊形類。這樣處理就能夠很好地解決代碼冗餘的問題,同時也易於維護。使得無需在所有子類中定義執行相同功能的方法。
在多態機制中,並不需要將弗雷初始化對象,需要的只是子類對象。會把子類對象都向上轉型爲父類對象來調用父類方法。
創建Quadrangle類,再分別創建兩個內部類Square和Parallelogramgle,它們都繼承了Quadrangle類。編寫draw()方法,該方法接受Quadrangle類的對象作爲參數,即使用這兩個內部類的父類作爲方法參數。在住方法中分別以兩個內部類的實例對象作爲參數執行draw()方法。
//定義一個正方形類,繼承四邊形類
class Square extends Quadrangle{
public Square() {
System.out.println("正方形");
}
}
//定義一個平行四邊形類,繼承四邊形類
class Parallelogramgle extends Quadrangle{
public Parallelogramgle() {
System.out.println("平行四邊形");
}
}
public class Quadrangle {
//實例化保存四邊形對象的數組對象
private Quadrangle[] qtest = new Quadrangle[6];
private int nextIndex = 0;
public void draw(Quadrangle q) {
if(nextIndex<qtest.length) {
qtest[nextIndex] = q;
System.out.println(nextIndex);
nextIndex++;
}
}
public static void main(String[] args) {
//實例化兩個四邊形對象,用於調用draw()方法
Quadrangle q = new Quadrangle();
q.draw(new Square()); //以正方形對象爲參數調用draw()方法
q.draw(new Parallelogramgle()); //以平行四邊形對象爲參數調用draw()方法
}
}
結果爲:
七、抽象類與接口
1、抽象類
一般將父類定義爲抽象類,需要使用這個父類進行繼承與多態處理。在多態機制中,並不需要將父類初始化對象,需要的只是子類對象。所以java中,抽象類不可以實例化對象,其子類可以。
抽象類語法:------抽象類關鍵字abstract
public abstract class Test{
abstract void testAbstract(); //定義抽象方法
}
1、使用absract關鍵自定義的類稱爲抽象類,使用這個關鍵字定義的方法稱爲抽象方法。
2、抽象方法沒有方法體。抽象方法只能定義在抽象類中。
3、承載抽象方法的抽象類必須被繼承。
4、繼承抽象類的所有子類必須重寫抽象類中的所有抽象方法。保證相同的方法名稱、參數列表和相同返回值類型。子類可以創建出非抽象方法,或者抽象方法。
---->當子類中的一部分需要一個方法,而另一部分不需要這一方法時(如果在父類抽象類中定義該方法,那麼不需要這個方法的子類也必須重寫該方法)
---->解決方法:接口
2. 接口
1、接口是抽象類的延伸,可看作純粹的抽象類。
2、接口中所有方法都沒有方法體。
3、可將上述(一部分子類需要一部分不需要的)方法封裝到一個接口中,使需要這些方法的那一部分類實現該接口,同時所有類都繼承父類抽象類。
4、接口使用interface關鍵字進行定義:
public interface drawTest{
void draw(); //接口內的方法,省略abstract關鍵字
}
5、一個類實現一個接口可以使用implements關鍵字:
public class 子類名 extends 父類抽象類名 implements 接口名{
……//
}
6、在接口中定義的方法必須被定義爲 public 或 abstract 形式,其他修飾權限不被java編譯器認可。即使不將該方法聲明爲public形式,它也是public。
7、在接口中定義的任何字段都自動是 static 和 final 的。
3. 接口與繼承
Java中不允許多重繼承,即不允許一個子類同時繼承一個或多個父類(即同時extends多個類)。
實現方法:使用接口可以實現多重繼承(即可以同時implements多個接口,同時實現多個接口)。
多重繼承語法:
class 類名 implements 接口1,接口2,...,接口n
在定義一個接口時使該接口繼承另外一個接口:
interface intf1{
...//
}
interface intf2 extends intf1{
...//
}
1、一個類只能繼承一個類:class A extends B{}
2、一個類可以實現多個接口:class A implements B,C,...{}
3、一個接口可以繼承多個接口:interface intf1 extends intf2,intf3,...{}
4、在繼承類的同時也可以繼承接口:class A extends B implements intf1,intf2,...{}
這就是選擇用接口而不選擇抽象類的原因。
(具體代碼示例:)
interface drawTest{ //定義接口
public void draw(); //定義方法,沒有方法體
}
class ParallelogramgleUseInterface extends QuadrangleUseInterface implements drawTest{
public void draw() { //因爲該類實現了接口,所以需要覆蓋draw()方法
System.out.println("平行四邊形.draw()");
}
public void doAnything() { //覆蓋父類方法
//……
}
}
class SquareUseInterface extends QuadrangleUseInterface implements drawTest{
public void draw() {
System.out.println("正方形.draw()");
}
public void doAnything() {
//……
}
}
public class QuadrangleUseInterface {
public void doAnything() {
//……
}
public static void main(String[] args) {
//接口也可以進行向上轉型操作
drawTest[] d = {new SquareUseInterface(), new ParallelogramgleUseInterface()};
for(int i=0;i<d.length;i++) {
d[i].draw(); //調用draw()方法
}
}
}
結果爲: