Java官方筆記4類和對象

創建類

定義類Bicycle:

public class Bicycle {

    // the Bicycle class has
    // three fields
    public int cadence;
    public int gear;
    public int speed;

    // the Bicycle class has
    // one constructor
    public Bicycle(int startCadence, int startSpeed, int startGear) {
        gear = startGear;
        cadence = startCadence;
        speed = startSpeed;
    }

    // the Bicycle class has
    // four methods
    public void setCadence(int newValue) {
        cadence = newValue;
    }

    public void setGear(int newValue) {
        gear = newValue;
    }

    public void applyBrake(int decrement) {
        speed -= decrement;
    }

    public void speedUp(int increment) {
        speed += increment;
    }
}

創建子類MountainBike繼承類Bicycle:

public class MountainBike extends Bicycle {

    // the MountainBike subclass has
    // one field
    public int seatHeight;

    // the MountainBike subclass has
    // one constructor
    public MountainBike(int startHeight, int startCadence,
                        int startSpeed, int startGear) {
        super(startCadence, startSpeed, startGear);
        seatHeight = startHeight;
    }   

    // the MountainBike subclass has
    // one method
    public void setHeight(int newValue) {
        seatHeight = newValue;
    }   
}

可以看出定義類的語法如下:

class MyClass {
    // field, constructor, and 
    // method declarations
}

複雜點的:

class MyClass extends MySuperClass implements YourInterface {
    // field, constructor, and
    // method declarations
}

定義了類MyClass繼承類MySuperClass,並實現接口YourInterface。類只能有一個父類,但是能實現多個接口。

定義方法

方法定義示例:

public double calculateAnswer(double wingSpan, int numberOfEngines,
                              double length, double grossTons) {
    //do the calculation here
}

方法簽名,方法名+參數類型,比如上面的方法簽名是:

calculateAnswer(double, int, double, double)

1個類中可以有多個同名的,但是不同參數的方法,也就是方法重載:

public class DataArtist {
    ...
    public void draw(String s) {
        ...
    }
    public void draw(int i) {
        ...
    }
    public void draw(double f) {
        ...
    }
    public void draw(int i, double f) {
        ...
    }
}

但是方法重載應謹慎使用,它會降低代碼可讀性。

類構造器

類有個默認的無參數的構造器,也可以自定義:

public Bicycle(int startCadence, int startSpeed, int startGear) {
    gear = startGear;
    cadence = startCadence;
    speed = startSpeed;
}

構造器跟類同名,沒有return。類可以有多個不同參數列表的構造器。

調用

參數定義的叫做Parameters ,實際傳入的叫做Arguments

基本數據類型

public double computePayment(
                  double loanAmt,
                  double rate,
                  double futureValue,
                  int numPeriods) {
    double interest = rate / 100.0;
    double partial1 = Math.pow((1 + interest), 
                    - numPeriods);
    double denominator = (1 - partial1) / interest;
    double answer = (-loanAmt / denominator)
                    - ((futureValue * partial1) / denominator);
    return answer;
}

類對象

public Polygon polygonFrom(Point[] corners) {
    // method body goes here
}

可變參數

public Polygon polygonFrom(Point... corners) {
    int numberOfSides = corners.length;
    double squareOfSide1, lengthOfSide1;
    squareOfSide1 = (corners[1].x - corners[0].x)
                     * (corners[1].x - corners[0].x) 
                     + (corners[1].y - corners[0].y)
                     * (corners[1].y - corners[0].y);
    lengthOfSide1 = Math.sqrt(squareOfSide1);

    // more method body code follows that creates and returns a 
    // polygon connecting the Points
}

使用...來表示可變參數。

方法中的基本數據類型,return後消失:

public class PassPrimitiveByValue {

    public static void main(String[] args) {

        int x = 3;

        // invoke passMethod() with 
        // x as argument
        passMethod(x);

        // print x to see if its 
        // value has changed
        System.out.println("After invoking passMethod, x = " + x);

    }

    // change parameter in passMethod()
    public static void passMethod(int p) {
        p = 10;
    }
}

方法中的引用類型,return後還有作用到原來的對象:

public class RefType {
    public void moveCircle(Circle circle, int deltaX, int deltaY) {
        // code to move origin of circle to x+deltaX, y+deltaY
        circle.setX(circle.getX() + deltaX);
        circle.setY(circle.getY() + deltaY);

        // code to assign a new reference to circle
        circle = new Circle(0, 0);
    }

    public static void main(String[] args) {
        Circle circle = new Circle(1, 1);
        RefType refType = new RefType();
        refType.moveCircle(circle, 2, 2);
        System.out.println(circle.getX());
    }
}

運行結果爲3。moveCircle裏面的circle,set方法調用會影響到引用的對象。雖然最後new了一個新對象,但是賦值給的是方法內部的這個circle,return後消失,外面的circle不受影響。可以這麼理解,外面和裏面的兩個circle,引用的都是同一個對象。

對象

創建對象:

Point originOne = new Point(23, 94);
Rectangle rectOne = new Rectangle(originOne, 100, 200);
Rectangle rectTwo = new Rectangle(50, 100);

變量聲明:

type name;

如果是基本數據類型,則會先分配內存空間。如果是類引用,則不會,在new的時候纔會分配內存空間。

字段:

objectReference.fieldName

方法:

objectReference.methodName(argumentList);

請記住,一個對象能有多個引用。

類的高級用法

方法退出有3種情況:

  1. 代碼執行完

  2. throw異常

  3. return

返回類型如果是類,那麼可以return子類。返回類型如果是接口,那麼可以return接口實現類。

this關鍵字表示當前類,或用來調用其他構造方法:

public class Rectangle {
    private int x, y;
    private int width, height;

    public Rectangle() {
        this(0, 0, 1, 1);
    }
    public Rectangle(int width, int height) {
        this(0, 0, width, height);
    }
    public Rectangle(int x, int y, int width, int height) {
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
    }
    ...
}

調用其他構造方法必須放在第一行。

範圍控制

top級別:public、package-private

member級別:public、private、protected、package-private

其中注意protected,等於package-private + 其他包中的子類(繼承了當前類,但是在其他包裏面)

怎麼理解?在自己的地盤(package)隨便玩,但是到了其他地盤,必須有父類保護(protected)

第3列的Subclass指的是其他包的子類。

訪問控制有2個用途:

  1. 在用別人的類時,看哪些可以使用

  2. 定義自己的類時,決定哪些允許別人用

static關鍵字,創建類字段:

public class Bicycle {

    private int cadence;
    private int gear;
    private int speed;
    private int id;
    private static int numberOfBicycles = 0;

    public Bicycle(int startCadence, int startSpeed, int startGear){
        gear = startGear;
        cadence = startCadence;
        speed = startSpeed;

        // increment number of Bicycles
        // and assign ID number
        id = ++numberOfBicycles;
    }

    // new method to return the ID instance variable
    public int getID() {
        return id;
    }
        ...
}

可以直接用類名訪問:

Bicycle.numberOfBicycles

static關鍵字

創建類方法:

public static int getNumberOfBicycles() {
    return numberOfBicycles;
}

創建常量:

static final double PI = 3.141592653589793;

注意,基本數據類型或string的常量,會在編譯的時候直接替換。如果依賴外部包的常量值變化了,比如PI變成了3.9,那麼當前代碼需要重新編譯。

關於static,有一個很重要的點是:static只能訪問static,也就是class級別只能訪問class級別,如果想訪問member級別,必須實例化對象後通過引用來訪問。

static塊

對於字段初始化來說,如果想寫多行代碼來初始化(比如寫個for循環來填充複雜數組),instance級別可以在構造函數來做,而對於class級別呢?就可以使用static塊:

public class MyClass {
    public static int x;
    public static int y;

    static {
        x = 10;
        y = 20;
        System.out.println("靜態塊執行");
    }

    public static void main(String[] args) {
        System.out.println("x=" + x);
        System.out.println("y=" + y);
    }
}

static塊是用來給class級別字段做值初始化的,它們會按照在類中出現的順序依次執行,並且它們在構造函數之前執行。

也可以定義static方法後賦值:

class Whatever {
    public static varType myVar = initializeClassVariable();

    private static varType initializeClassVariable() {

        // initialization code goes here
    }
}

member級別字段初始化塊,也就是不帶static的:

{
    // whatever code is needed for initialization goes here
}

會在每個constructor中執行。也可以使用final方法:

class Whatever {
    private varType myVar = initializeInstanceVariable();

    protected final varType initializeInstanceVariable() {

        // initialization code goes here
    }
}

爲什麼這裏必須要用final?因爲在實例初始化期間調用非final方法會報錯,這是爲了提高代碼健壯性、可讀性和可維護性,初始化本來就是一個確定的事情,那麼就用final限定清楚。

嵌套類

class OuterClass {
    ...
    class InnerClass {
        ...
    }
    static class StaticNestedClass {
        ...
    }
}

InnerClass能訪問OutClass所有成員(可以理解爲跟method類似),StaticNestedClass則不能。

什麼情況需要用嵌套類?

  • 這個類只會被另外1個類使用,那麼可以定義爲嵌套類(helper classes),優化包結構

  • 封裝,嵌套類可以訪問內部private成員

  • 可讀性,小的嵌套類,方便閱讀

必須先實例化OuterClass再實例化InnerClass:

OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();

而static嵌套類跟普通類一樣:

StaticNestedClass staticNestedObject = new StaticNestedClass();

注意,static嵌套類,跟其他頂層類是一樣的,意味着它不能直接訪問所在類的字段,而必須通過實例化對象引用才能訪問。(也許是爲了方便在一個文件裏面寫多個類,才設計了static嵌套類)

以下是示例:

OuterClass.java

public class OuterClass {

    String outerField = "Outer field";
    static String staticOuterField = "Static outer field";

    class InnerClass {
        void accessMembers() {
            System.out.println(outerField);
            System.out.println(staticOuterField);
        }
    }

    static class StaticNestedClass {
        void accessMembers(OuterClass outer) {
            // Compiler error: Cannot make a static reference to the non-static
            //     field outerField
            // System.out.println(outerField);
            System.out.println(outer.outerField);
            System.out.println(staticOuterField);
        }
    }

    public static void main(String[] args) {
        System.out.println("Inner class:");
        System.out.println("------------");
        OuterClass outerObject = new OuterClass();
        OuterClass.InnerClass innerObject = outerObject.new InnerClass();
        innerObject.accessMembers();

        System.out.println("\nStatic nested class:");
        System.out.println("--------------------");
        StaticNestedClass staticNestedObject = new StaticNestedClass();
        staticNestedObject.accessMembers(outerObject);

        System.out.println("\nTop-level class:");
        System.out.println("--------------------");
        TopLevelClass topLevelObject = new TopLevelClass();
        topLevelObject.accessMembers(outerObject);
    }
}

TopLevelClass.java

public class TopLevelClass {

    void accessMembers(OuterClass outer) {
        // Compiler error: Cannot make a static reference to the non-static
        //     field OuterClass.outerField
        // System.out.println(OuterClass.outerField);
        System.out.println(outer.outerField);
        System.out.println(OuterClass.staticOuterField);
    }
}

一個變量引用的例子,這個例子展示了不同級別的同名變量,是如何取值的:

public class ShadowTest {

    public int x = 0;

    class FirstLevel {

        public int x = 1;

        void methodInFirstLevel(int x) {
            System.out.println("x = " + x);
            System.out.println("this.x = " + this.x);
            System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
        }
    }

    public static void main(String... args) {
        ShadowTest st = new ShadowTest();
        ShadowTest.FirstLevel fl = st.new FirstLevel();
        fl.methodInFirstLevel(23);
    }
}
x = 23
this.x = 1
ShadowTest.this.x = 0  // 注意這種在嵌套類取OuterClass同名字段的方式

Local Class,定義在method裏面的嵌套類:

public class LocalClassExample {

    static String regularExpression = "[^0-9]";

    public static void validatePhoneNumber(
        String phoneNumber1, String phoneNumber2) {

        final int numberLength = 10;

        // Valid in JDK 8 and later:

        // int numberLength = 10;

        class PhoneNumber {

            String formattedPhoneNumber = null;

            PhoneNumber(String phoneNumber){
                // numberLength = 7;
                String currentNumber = phoneNumber.replaceAll(
                  regularExpression, "");
                if (currentNumber.length() == numberLength)
                    formattedPhoneNumber = currentNumber;
                else
                    formattedPhoneNumber = null;
            }

            public String getNumber() {
                return formattedPhoneNumber;
            }

            // Valid in JDK 8 and later:

//            public void printOriginalNumbers() {
//                System.out.println("Original numbers are " + phoneNumber1 +
//                    " and " + phoneNumber2);
//            }
        }

        PhoneNumber myNumber1 = new PhoneNumber(phoneNumber1);
        PhoneNumber myNumber2 = new PhoneNumber(phoneNumber2);

        // Valid in JDK 8 and later:

//        myNumber1.printOriginalNumbers();

        if (myNumber1.getNumber() == null)
            System.out.println("First number is invalid");
        else
            System.out.println("First number is " + myNumber1.getNumber());
        if (myNumber2.getNumber() == null)
            System.out.println("Second number is invalid");
        else
            System.out.println("Second number is " + myNumber2.getNumber());

    }

    public static void main(String... args) {
        validatePhoneNumber("123-456-7890", "456-7890");
    }
}

Local Class只能訪問方法裏面的final變量,或者“看似final”的變量(值不會發生變化)。否則會報錯:local variables referenced from an inner class must be final or effectively final。但是能直接方法參數列表的parameters。

Anonymous Class,定義在method裏面的沒有名字的嵌套類:

public class HelloWorldAnonymousClasses {

    interface HelloWorld {
        public void greet();
        public void greetSomeone(String someone);
    }

    public void sayHello() {

        class EnglishGreeting implements HelloWorld {
            String name = "world";
            public void greet() {
                greetSomeone("world");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hello " + name);
            }
        }

        HelloWorld englishGreeting = new EnglishGreeting();

        HelloWorld frenchGreeting = new HelloWorld() {  // 匿名類
            String name = "tout le monde";
            public void greet() {
                greetSomeone("tout le monde");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Salut " + name);
            }
        };

        HelloWorld spanishGreeting = new HelloWorld() {  // 匿名類
            String name = "mundo";
            public void greet() {
                greetSomeone("mundo");
            }
            public void greetSomeone(String someone) {
                name = someone;
                System.out.println("Hola, " + name);
            }
        };
        englishGreeting.greet();
        frenchGreeting.greetSomeone("Fred");
        spanishGreeting.greet();
    }

    public static void main(String... args) {
        HelloWorldAnonymousClasses myApp =
            new HelloWorldAnonymousClasses();
        myApp.sayHello();
    }
}

它的語法跟constructor類似,new後面跟上implements接口或extends類的名字,但它是一個表達式,所以最後要加上分號。

請記住,Local Class和Anonymous Class,都是應以在method裏面的。

參考資料:

Classes and Objects https://dev.java/learn/classes-objects/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章