1. 程式人生 > >面向物件---方法傳參與構造器

面向物件---方法傳參與構造器

方法詳解

方法是類或物件的行為特徵的抽象,方法是類或物件的重要組成部分。Java裡的方法不能單獨存在,在邏輯上要麼屬於類,要麼屬於物件。

方法的所屬性:

Java語言是靜態的,一個類定義完成後,只要不在重新編譯這個類檔案,該類和該類的物件所擁有的方法是固定的,永遠都不會改變。執行方法時必須使用類或物件來作為排程者。
注意:同一個類中的方法之間相互排程時實際上還是this或者來作為排程者,只不過有時侯會省略,但實際上還是存在。

方法的引數傳遞機制:

Java中方法傳遞方式只有一種:值傳遞。所謂值傳遞就是將實際引數值的副本傳入方法內,而引數本身不會受到任何影響。

package org.westos.practice;

public class valuetransform {

    public static  void swap(int a,int b){
        int tmp;
        tmp=a;
        a=b;
        b=tmp;
        System.out.println("a="+a+";"+"b="+b); //a=9;b=6

    }

    public static void main(String[] args) {
        int a=6;
        int b=9;
        System.out.println("a="+a+";"+"b="+b); //a=6;b=9
        swap(a,b);
        System.out.println("a="+a+";"+"b="+b); //a=6;b=9
    }

}

可以看到最終a,b的值並沒有交換,只是傳入及交換函式swap的a,b的兩個副本值發生交換。

引用資料型別傳參:

public class DataWrap {
    int a;
    int b;
}

測試類,交換a,b的值

package org.westos.practice;

public class DataWrapTest {
    public static void main(String[] args) {
        DataWrap dw=new DataWrap();
        dw.a=6;
        dw.b=9;
        swap(dw);
        System.out.println("a="+dw.a+";b="+dw.b);
    }

    private static void swap(DataWrap dw) {
        int tmp=dw.a;
        dw.a=dw.b;
        dw.b=tmp;
        System.out.println("a="+dw.a+";b="+dw.b);
    }
}
/*a=9;b=6
a=9;b=6*/

其中dw作為一個實際引數傳入swap函式,其實是採用值傳遞的方式。複製了dw的副本傳入swap,而dw只是一個引用變數,而並沒有複製實際物件。所以傳入之後相當於在main棧中有一個dw指向實際物件,同時在swap棧中也有一個dw指向實際物件。所以在swap函式中操作的是實際物件,而並非副本。

形參個數可變的方法:

JDK1.5之後,java允許定義形參個數可變的引數,從而允許為方法指定數量不確定的形參。

例項如下:

package org.westos.practice;

public class Varages {
    public static void test(int a,String... books){
        //books被當做陣列處理
        for (String tmp:books){
            System.out.println(tmp);
        }
        //輸出整形變數a的值
        System.out.println(a);
    }

    public static void main(String[] args) {
        test(5,"Java","c++","python");
    }
}

/*Java
c++
python
5*/

可以看出形參個數可變的引數本質就是一個數組引數

public static void test(int a,String... books)
public static void test(int a,String[] books)

這兩段程式碼效果完全一樣,但是呼叫的時候存在差別,形參個數可變的這種方法呼叫更加簡潔。但是它只能放在形參最後傳入,而陣列可以放在任意位置傳入。

  • 類名作為形參
    先定義一個類,在測試類中再定義一個需要類傳入的方法;

Animal.class

package org.westos.practice;

public class Animal {

    public void eat(){
        System.out.println("我是普通類的方法");
    }
}
  • 抽象類作為形參
    同上

AbsAnimal.class

package org.westos.practice;

public abstract class AbsAnimal {

    public abstract void show();
}

Cat類實現該抽象類;
Cat.class

package org.westos.practice;

public class Cat  extends AbsAnimal{
    @Override
    public void show() {
        System.out.println("我是抽象類子類的重寫方法");
    }
}
  • 介面類作為引數傳遞

MyInterface.class

package org.westos.practice;

public interface MyInterface {
    void intershow();
}

Dog類實現該抽象類;
Dog.class

package org.westos.practice;

public class Dog implements MyInterface {
    @Override
    public void intershow() {
        System.out.println("我是介面子類實現的方法");
    }
}

測試類中:

package org.westos.practice;

public class MyTest {
    public static void main(String[] args) {

        //如果方法需要傳入一個類,你就傳入這個類的物件
        methodClass(new Animal());

        //如果方法需要傳入一個抽象類,你就傳入這個抽象類子類的物件
        //Cat類實現了抽象類的方法
        //AbsAnimal absan= new Cat();
        methodAbsClass(new Cat());

        //如果方法需要傳入一個介面類,你就傳入這個介面類實現的子類物件
        //Dog類實現了介面方法
        methodInterClass(new Dog());
    }

    public static void methodClass(Animal an){
        an.eat();
    }

    public static void methodAbsClass(AbsAnimal absan){
        absan.show();
    }

    public static void methodInterClass(MyInterface inface){
        inface.intershow();
    }
}

成員變數和區域性變數

注意事項:

  1. 成員變數又可分為類變數(被static修飾)和例項變數(沒有被static修飾)。
    類變數隨著類的建立而建立,銷燬而銷燬;這個類普遍相同的屬性,如人都有兩個眼睛,一個嘴巴。
    例項變數隨著物件的建立而建立,銷燬而銷燬,對於同一個類,不同物件屬性不同,如每個人的身高體重。
  2. 在一個方法中同名的區域性變數會覆蓋成員變數,不過可以通過this或指定類名作為呼叫者來訪問被覆蓋的成員變數。不過大部分時候應該避免這種重名情況。
  3. 在程式中使用區域性變數,應該儘可能的縮小區域性變數的範圍,區域性變數的作用範圍越小,它在記憶體中停留的時間就越短,程式執行效能就越好。

深入構造器

構造器是一個特殊的方法,這個方法用於建立例項時執行初始化。構造器是建立物件的重要途徑。

使用構造器執行初始化

當建立一個物件時系統為這個物件的例項變數進行預設初始化,這種預設的初始化把所有基本型別的例項變數設為0或false。把所有引用型別的例項變數設為null。如果想改變這種預設的初始化,想讓系統建立物件時就為該物件的例項變數顯式指定初始值,就可以通過構造器來實現。

注意:如果程式設計師沒有為java提供任何構造器,則系統會為這個類提供一個無引數的構造器,這個構造器為空,不做任何事,但無論如何如何java類至少包含一個構造器。

package org.westos.practice1;

public class ConstructorTest {
    public String name;
    public int count;

    //提供自定義的構造器,該構造器包含兩個引數
    public ConstructorTest(String name,int count){
        //構造器裡的tjis代表它進行初始化的物件
        //將傳入的兩個引數賦值給this代表物件的name和count
        this.name=name;
        this.count=count;
    }

    public static void main(String[] args){
        //系統將會對該物件執行自定義的初始化
        ConstructorTest tc=new ConstructorTest("java",3);

        System.out.println(tc.name);
        System.out.println(tc.count);
    }
}

注意:雖然說構造器是建立物件的途徑,但實際上當程式設計師呼叫構造器的時候,系統先為該物件分配記憶體空間,併為這個物件執行預設初始化,這個物件已經產生了,這些操作在構造器執行之前就都完成了。也就是說,當系統開始執行構造器之前,系統已經建立了一個物件,只是這個物件還不能被外部程式訪問,只能在該構造器中用this來引用。當構造器執行完之後,這個物件會作為構造起的返回值被返回,通常會賦給另一個引用變數,從而讓外部程式可以訪問該物件。

通常把構造器設定成public訪問許可權,從而允許系統中的任何位置的類都能建立該類的物件。

構造器過載

構造器過載和之前的方法過載一樣,每個過載的構造器使用與類相同的名字。只是需要傳入的引數個數和型別不一樣;

例項如下:

package org.westos.practice1;

public class Constructer {
    String name;
    int age;
    
    //預設的構造器,由於還要定義其他過載構造器,所以得讓他顯式,而不被覆蓋
    public Constructer(){
        
    }
    public  Constructer(String name){
        this.name=name;
    }
    public  Constructer(int age){
        this.age=age;
    }
    public static void main(String[] args) {

        Constructer ct=new Constructer();
        System.out.println(ct.name);
        System.out.println(ct.age);
/*null
0*/
        String name="Liming";
        Constructer ct1=new Constructer(name);
        System.out.println(ct1.name);
        System.out.println(ct1.age);
/*Liming
0*/
        int age=4;
        Constructer ct2=new Constructer(age);
        System.out.println(ct2.name);
        System.out.println(ct2.age);
/*null
4*/
    }
}

本例使用方法過載一共建立了三個物件。