1. 程式人生 > >Java_面向物件相關(總覽及突破)

Java_面向物件相關(總覽及突破)

Java面向物件重難點與細節部分總結

物件的建立

設有類class A{},產生物件的程式碼為new A();,這句的含義是根據類模板產生一個物件,並在計算機記憶體中為為此物件開闢一塊新的獨立記憶體空間。new 操作是棧中建立物件,複製到堆中以類為模板產生物件,實質上就是將類中的屬性複製到生成的物件中,這些屬性雖然在類中定義,但實際上是為物件服務,因此稱他們為物件屬性。而方法在呼叫時,系統會為方法開闢一個棧空間,用於存放方法中的區域性變數和形式引數,並且方法在執行的時候還可以訪問複製到物件中的屬性,其效果就像方法也被複制到物件中一樣。方法執行完畢後,棧空間被釋放。雖然方法在類中定義但由於可以訪問物件屬性,故實質還是為物件服務,因而成為物件方法。那麼究竟什麼能成為類方法呢,下面介紹。

插播:這也就涉及到了Java中的記憶體分配

堆在應用程式生命週期中一直存在,棧在方法呼叫完畢後就釋放

記憶體中的分配方法(一部分)。

棧區 方法區(一說歸在棧區)
物件(使用new關鍵字產生的) 堆中物件的引用(地址) 靜態變數和常量池
null 方法執行時用的區域性變數和形參 包括(基本型別)常量和字串常量

簡單資料型別根據初始化的位置在方法中還是程式中決定存放在棧中還是方法區中

插播2:C++ new細節
C艹中用關鍵字new方式產生的物件在堆中,用A a;產生的物件在方法棧中。還有寫法A* a=new A();


兩種區別:
分配釋放方式不同,存放位置也不同
1) 編譯器安排程式碼,自動分配釋放,
1.1)全域性物件 (全域性外部變數,名空間內外部變數,某檔案靜態變數,類靜態變數,函式靜態變數,匿名名空間變數),記憶體為全域性(態)資料區
1.2)函式內非靜態變數物件,記憶體為棧區
程式碼:

A a;      // 自動分配,並在合適的時候釋放
a.print(); //操作

2)程式設計師寫出程式碼,主動分配釋放記憶體。
記憶體包括,兩部分
2.1)有名物件 指標變數 a ,和 上面一樣,自動分配。
2.2)無名物件 *a ,記憶體在堆區,這部分主動分配釋放
程式碼

A *a=new A(); //分配記憶體
a->print();        //操作 
delete a;         //釋放

物件*a 是無名物件,通過有名物件 指標 a 進行操作;

前面我們通過new A();有了物件,接下來還想要對它進行控制。A a;的作用是產生一個A類的宣告,此時並沒有任何此類的物件產生,也沒有為物件分配記憶體。這是與C艹不同的,C艹中此時已經有物件了。 Java中我們需要將A a;與物件連在一起才能通過a控制生成的物件,使用a=new A();。更直接的方法是A a=new A();一步到位。

A a=new A();過程是先產生物件再將物件賦予宣告a。當a被賦予物件後,就從概念‘宣告’升級為概念‘引用’。

static

static修飾的方法有如下特點:

  • static方法是類方法,但可以被所有物件所訪問,引用這個方法時,可以使用物件名做字首,也可以使用類名做字首。
  • static方法內部的程式碼,只能訪問類中的static屬性或方法,不能訪問類中的非static屬性或方法(因為那是物件方法),但非 static方法(物件方法)可以訪問static資料成員。
  • main方法是特殊的靜態方法,是Application程式入口點,必須寫成public static void main(String args[])的形式
  • static修飾的方法是類方法,修飾的屬性是類屬性。其他的方法都稱為物件方法,為物件服務。static修飾的類由於只能訪問類的static屬性而不能訪問物件中的屬性,故稱為【真!類方法】
  • static方法呼叫時,也開闢棧空間存放區域性變數
  • static類“物件共同擁有”,可以直接通過[類名.方法]呼叫方法,不必例項化物件。

引數傳遞

借用我老師的一句話:Java中所有的引數傳遞都是值傳遞,也就是拷貝傳遞。就算是物件的“引用傳遞”也是值傳遞,只是拷貝的是地址。

//普通資料型別作為引數傳遞是值傳遞,物件"引用傳遞"是拷貝地址傳遞,歸根結底也是值傳遞。
//就算是靜態類函式修改靜態類屬性,一樣是拷貝
public class X {
    private static int a;
     public static void main(String [] args) {
        modify(a); 
        System.out.println(a);
    }
     public static void modify(int a) { a++; }
 }
本程式的輸出為0,因為a++是對形式引數進行自增,而不是對物件屬性a進行自增。

看下面的一個例子

class IntClass{
    int value;
}
public class RunIntClass {
    public static IntClass getInstance(){
        //在方法中產生物件
        IntClass s= new IntClass();//2
        s.value=8;
        return s;
    }

    public static void main(String args[]){
        IntClass a;
        a = getInstance(); //1
        System.out.println(a.value); //3
    }
}

/**
*在main方法中使用的物件是在getInstance方法當中產生並放到堆中(用new長生的放在堆中),
*再通過引用s將物件地址返回賦值給a,此時getInstance方法所在的佔空間被釋放,s也被釋放但
*物件在堆中還沒消失,現在由a指向這個物件。
*/
//程式輸出:8

使用C艹編寫上面的RunIntClass函式時,如果將IntClass s= new IntClass();換為IntClass s;程式會出錯i,因為這種方式產生的物件在方法棧中,若僅僅將引用傳回,隨著棧空間的釋放物件也就消失了。

如果用String去修改上面的程式,觀察是否能夠呼叫?【因為String很特別,可以直接賦值】

public class RunIntClassUseString {
    public static String getInstance(){
        //在方法中產生物件
        String s="abc";

        //String s= new String();//2
        //s="abc";
        return s;
    }

    public static void main(String args[]){
        String a;//此時a是null不是“”,此句分配了一個記憶體空間,沒存入任何物件,相當於什麼也沒做。
        //String a="";會分配一個記憶體空間並存入一個字串物件!
        //但C艹中使用string a;已經分配好空間建立了物件.類比前面C艹A a;的栗子
        //Java這時再跟一句a=new string("abc")將會把棧中的物件複製進堆。
        a = getInstance(); //1
        System.out.println(a); //3

    }
}

靜態程式碼塊

一個類中可以使用不包含在任何方法體中的靜態程式碼塊。當類被裝載時,靜態程式碼塊被執行且只被執行一次。靜態程式碼塊經常用來對類中定義的屬性進行初始化

class Test
{ static int value ;
  static 
  {  value = 3;
     System.out.println("value="+value);  }
  public static void main(String[] args){   }
}
//程式輸出結果為3

多型:過載與覆蓋

過載:類中定義了多個同名而不同內容引數的成員方法,成這些方法為過載方法 overloading
覆蓋:子類對父類引數相同,返回型別相同的同名方法重新進行定義這種多型成為覆蓋 overriding

方法名稱相同,引數名稱相同,返回型別相同:覆蓋
方法名稱相同,引數名稱不同:過載
方法名稱相同,引數名稱相同,返回型別不同:編譯不能通過

覆蓋——注意

  • 子類的訪問修飾符許可權應等於或大於父類
  • static方法不能覆蓋非靜態方法,也不能被非static方法覆蓋,但是static方法可以覆蓋static方法
  • 方法前有final修飾符,此方法不能在子類方法中進行覆蓋
  • 在JDK中,很多父類的方法被子類重新覆蓋,賦予了不同的含義,如Object類中的boolean equals(Object obj)方法
  • 抽象類中如果存在抽象方法,則具體子類必須對抽象方法進行覆蓋

抽象類

  • 抽象類不可以有物件,抽象類存在就是為了被繼承。所以自編的父類多數時候都應為抽象類,不然則認為設計有問題。(父類上面還有父類時可不作為抽象類)
  • 抽象類中不可以有private成員
  • 如果子類還是抽象類,則子類不可以定義和父類重名的抽象方法。
  • 如果把抽象類寫成abstract public double area(){}這不叫抽象類,有了{}就是實現,空著也是實現。
  • 在抽象類中,非抽象方法可以呼叫抽象方法。
  • abstract不能與final並列修飾同一個類(產生邏輯矛盾);
  • abstract 不能與private ,static(因為static修飾的方法必然被直接呼叫),final或native並列修飾同一個方法

介面

  • 就是特殊的抽象類(純虛類) 所有方法都是abstract
  • 通俗地講就是不同類的相同行為的集合

定義介面要注意幾點

  • 介面定義用關鍵字interface,而不是用class,interface前的修飾符要麼為public,要麼為預設。
  • 介面定義的資料成員全是final static(靜態常量)。即使沒有修飾符,其效果也等效,訪問級別要麼為public,要麼為預設。
  • 介面中沒有構造方法;所有成員方法都是抽象方法(與抽象類有所不同)。即使沒有修飾符,其效果完全等效,訪問級別要麼為public,要麼為預設。注:方法前不能修飾為final。
  • 介面具有繼承性,可通過extends關鍵字宣告介面的父介面

此文為總覽及突破, 細節部分見log2000計劃之類與物件系列博文

visitor tracker
訪客追蹤外掛