2017年11月2日 星期四

Day1-5 - 宣告類別成員

1.3 Develop code that declares, initializes, and uses primitives, arrays, enums, and objects as static, instance, and local variables. Also, use legal identifiers for variable names.

1.4 Develop code that declares both static and non-static methods, and - if appropriate - use method names that adhere to the JavaBeans naming standards. Also develop code that declares and uses a variable-length argument list.

method和non-local variable統稱為members
可以使用non-access modifier(strictfp, final, abstract)修飾members

存取修飾子

- Class只能使用public or private (原因:protected是同package class+subclass能夠存取,但什麼情況下一個A package的class能夠extends B package的class,但 B package class又只限B package內的class存取使用?因此一般來說定義protected沒有必要,就和定義default class一樣。)
- members可使用四種access control等級
- access control有兩個議題:

  1. class內的method是否可以access另一個class member(dot operator)
  2. subclass是否可以extends parent-class的member(subclass宣告了擁有superclass的所有member)

- 假如A class不能夠被B class存取,代表A的所有member不能被B class存取(即使A member有設定為public的variable or class)
- this 這個參考一定是指到此刻正在執行的object

Ex:

Bicycle.java
applyBreak() { }
makeStop() {
  applyBreak();//同class呼叫method
|}

MountainBike.java extends Bicycle.java
doThings() {
Bicycle bk = new Bicycle();
bk.applyBreak();//透過class的reference呼叫method
}
doMore() {
  applyBreak();//繼承的method (沒有dot等於暗中使用this.applyBreak())
}

Driver.java
doDriverStuff() {
Bicycle car = new Bicycle();
car.applyBreak();//透過class的reference呼叫method

MountainBike mb = new MountainBike();
mb.applyBreak();//透過class的reference呼叫method
}


私有成員(Private Members)
- 假如一個subclass嘗試繼承supercalass的 private class將會有錯誤訊息跳出
- subclass可以重新定義superclass的method(這不是override),只是一個名稱恰好相同的method
- 雖然可以將instance variable標示為public,但實務上盡量將variable保持private or protected。developer應該透過setter methods(設值函數) and getter methods(取值函數)操作變數。如此可以透過method檢查設定的值是否有問題

保護(Protected)和預設(Default)成員
- protected和default兩者的members都能夠被同一個package的class存取,最大的差別在於,protected的members能夠被subclass存取(使用繼承方法,而非建立實體使用dot存取),即使在不同的package內
- 若package外的subclass使用superclass的reference access members,protected 等級就會像default等級一樣,無法使用。
- protected會顧及到父子關係

  • protected (pacakge+child
  • default (package)
Ex:

package A;
public class Parent {
  protected int x = 1;


package B;
import A.Parent;
class Child extends Parent {
  public void test() {
    System.out.println("x is " + x); // 因為是subclass,且x為protected,在不同的package還是可以繼承
    Parent p = new Parent();
    System.out.println("x is " + p.x); //錯誤,因為對p來說已經是不同package之間去存取protected的變數
  }
}

package B;
class Other {
  public void test() {
    Child cd = new Child();
    System.out.println("child x is " + cd.x);//錯誤,這個x對於其他package的class而言已經變成private了
  }
}

Local Variables 和 Access Modifiers
- access modifier不可應用在local variables上。
- 只有final可以用在local variables上

非存取的成員修飾子

Final Method
- 避免修改
- 防止method被副寫也消滅OO優勢

Final Arguments

public Human getAge(int birth, final int growIndex) {}

- method arguments規則與local variables一樣,能夠使用final modifier/
- 上述例子表growIndex不能在method內被修改

Abstract Methods
- 一個abstract method需要以分號作為結尾,意即沒有method body
Ex1:
public class Human {//錯誤,沒有宣告abstract的class內不應該有abstract method
    public abstract void attack();
}

Ex2:
public abstract class Human {//正確,abstract class可以沒有包含abstract method
    void heal() {...}//
}
以下三個原因表示heal()不是一個abstract method

  1. heal()沒有abstract
  2. heal()有大括號
  3. heal()有提供實作程式碼

- 任何extends abstract class的subclass都必須要implement所有superclass的abstract class,除非subclass也是abstract的
- 第一個concrete subclass必須要implement所有superclass(所有inheritance tree)的abstract class
- abstract目的是:superclass不知道subclass在此method需要有怎樣的行為; final目的是:superclass知道關於所有subclass在此method需要有如何的行為(因此abstract和final為對立的)
- private不能被subclass看到,無法繼承,因此也不能與abstract共用
- abstract無法和static一起共用

Synchronized method
- 意指該method一次只能被一個thread存取
- Synchronized可以加上final
public synchronized void increment() {c++;}

Native 函式
- native只能應用在mehod上,不能用在class, variable上
- native的宣告必須以分號作為結尾(就像abstract method一樣)
public native int intMethod(int i);

Strictfp 函式
- 目的 : 強迫floating point遵循IEEE754標準,不論在哪個平台運行都可以預測floating point的行為;缺點是當底層平台支援更精準的運算,strictfp將無法使用此優點
- strictfp可以用來修飾class及method
- strictfp不能用來當作變數的宣告

Variable Argument Lists(簡稱var-args) method
- 以下皆為var-args

  • variable-length argument lists
  • variable arguments
  • var-args
  • varargs
  • variable arity parameter

- 定義argument及parameters

  • argument:call method時,放在括號內的東西 ex: name.doMethod("someString", 25);
  • parameter: method's signature. ex: public void doMethod(String s, int i) { .. }

- var-args宣告規則

  1. type: 一個var-arg parameter並須指定可以接收的argument type。可以是primitive type(基本型別),也可以是object type
  2. 語法: type + (...)省略符號 + 空白 + 接收parameter的array名稱
  3. 帶的parameter可以是0到n個
  4. 其他參數: 使用var-arg的method可以同時擁有其他的parameter
  5. 限制: var-arg必須是method signature上的最後一個parameter,而且一個函式只能有一個var-arg
  6. 一般來說overloading的var-args method會是最後一個選擇
合法的:
 void average( double... numbers ){ }
 void average( char c, double... numbers ){ } //可以有其他parameter
 void average( Ant... ant){ } //使用object Ant type
非法的:
 void average( double  numbers...){ } //語法錯誤
 void average( double... numbers,  String... times){ } //不能有一組以上的var-args
 void average( double... numbers, char k ){ } //var-args必須是最後一個

Constructor宣告
- 建立新的obejct,最少會有一個constructor會被呼叫
- constructor不能有return type
- 可以有access modifiers,var-args
- constructor的名字必須和class名字相同
- constructor不能被標示成static(因為和object initialization有關係), final, abstract (因為不能被override)

// 非法建構子
void Foo2() { } // 不能有回傳type
Foo2(short s); // abstract method
Foo2(int... x, int t) { } // 錯誤的 var-arg 語法

variable的宣告

兩種type的variable

  • Primitives: 共八種:char, boolean, (byte, short, int, long)整數, (double, float)浮點數,強型別,宣告後無法更改
  • Reference variables: refer to 一個object。可為任何宣告class,也可以是宣告class的subclass (多型)

基本資料的宣告和範圍
- Java的六種數字型別(byte, short, int, long, double, float)都是某種數量的8-bit bytes組成,且帶正負號
- 以下範圍,正數個數之所以比負數個數少1,是因為0被歸類到正數
- VM決定boolean的值需要幾個bits表示
- 一個char包含16 bites萬國碼字元(0-2^16-1=65535); ASCII只需要8 bits表示,但需要其他空間表示非英文字元


Bits
Bytes
Minimum Range
Maximum Range
byte
8
1
-2^7
2^7-1
short
16
2
-2^15
2^15-1
int
32
4
-2^31
2^31-1
long
64
8
-2^63
2^63-1
float
32
4
n/a
                    n/a
double
64
8
n/a
                    n/a

Instance Variables
- 被定義在object class內,method之外,只有當一個class被initialization的時候才會進行initial的動作。
- instance variable是每個獨一無二的object的資料欄位(fields)
- instance variable = field = property, attribute
以下幾點注意

  • 可以使用任何四種Aaccess等級
  • 可以被標示為final/transient
  • 不可被標示為abstract/synchronized/strictfp/native/static(class variables)

Local/Automatic/Stack/Method variables
- local variable 只宣告在method裡
- 所有的modifier只有final允許出現在local variable
- local variable只存在於stack,而不是在heap
- local宣告的object在stack內為local declared reference variable,沒有local object
- shadowing範例如下

int count = 9

void logIn(){
    int count = 10
    println(x)  
}
logIn();//顯示10

println(x);// 顯示 9

- 使用shadowing的原因:要將parameter設定到instance variable上,如:

int size = 27;
public void setSize(int size) {
  this.size = size; // 使用this解決naming collision
}

陣列的宣告
- Array本身是Heap裡面的一個object
- 宣告方式: 儲存的type+中括號+變數  ex: Thread[] threads; //增加程式可讀性,thread是(Thread[])的object reference
- 多維陣列 (multidimensional arrays),意指array的array

  • Thread[][][] threads; //三維array(建議寫法)
  • Thread[] threads[]; //二維array,合法但不是好的寫法

- Declare array的同時不能declare array大小,這不合法。 ex: int[5] count; //錯誤,必須要等到初始化array object,JVM才會分配記憶體,這時候才需要考慮array大小。

Final 變數
- 沒有final object, 只有final reference
- final的object reference variable可以修改這個object的資料,但不能再把這個reference拿去pointer其他object。

  • Final v.s. Class : final class不能被extends
  • Final v.s. Method: final method不能被override
  • Final v.s. Variable: final variable一旦被initial,就不能再assign新值。(assign value必須在constructor執行之前發生)

Transient 變數(只能在instance variable上)
- 若instance variable標示為transient,表示JVM需要在serialize(序列化)時要忽略此變數
- serialized: 可以flattern(平坦化) object的status(instance variable)到一個I/O上。也就是可以將object存到檔案上,或讓它在網路上傳送,並在另一端JVM回復成原來的object(reinflating, deserializing)。

Volatile 變數(只能在instance variable上)
- 當一個thread存取帶有Volatile變數時,必須將thread對於此variable的copy和memory裡面主要的copy進行同步化。
- 為了保證資料是thread-safe,應該使用synchronized,而不是volatile modifier。

Static 變數和函式
- 如果class內的某些,method和variable與建立的instance無關,則可以使用static建立method/variable
- static method不能直接存取自己class的instance variable,因為他沒有和特定的instance有關係
- 建立任何class的instance之前,static member已經存在
- 不論有多少個instance,static member只會有一份
- 意即: 此type的所有instance都share同一份static variable
- 可以被標示為static:

  1. Method
  2. Variable(非區域)
  3. 包含在另一個class內的class(nested class),但不是在method內
  4. Initialization blocks

- 不可被標示為static:

  1. Constructor
  2. Class (Unless it is nested)
  3. Interface
  4. method local inner classes
  5. Inner class method and instance variable
  6. local variable (重要) local就是宣告在method內部的變數

Enums 的宣告
- Java5.0開始可以對variable的值加以限制,從某些定義好的清單中選擇一個來指派給variable(清單項目為enums),可以協助減少程式內的錯誤
Ex:

public enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY 
}

取得方法:
Day day = Day.MONDAY;

- 對enum來說,清單中的值都是常數,根據Sun的程式碼慣例:[應使用大寫字元和底線做為區隔(ex: MIN_HEIGHT)。]
- enum可以被宣告成獨立的class或class member,但它不能被宣告在method內
- emum只能宣告成public或default access level,就像non-inner class
- enum的後面不一訂要加分號

寫法一 (在class外,被當作是一個獨立class來宣告)
enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY 
}; //分號非必須
class Week{
    Day date;
}
Week wk = new Week();
wk.date = Day.Monday;

寫法二(在class內,被當作是一個class member)
class Week{
    enum Day {
    SUNDAY, MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY 
    } 
    Day date;
}
Week wk = new Week();
wk.date = Week.Day.Monday;

- SUNDAY, MONDAY, TUESDAY, WEDNESDAY,..這些都是Week的instance
- 可以將Day的enum想成一個Day的陣列,有順序性
- 其實對於SUNDAY, MONDAY, TUESDAY,..都是public static final,所以都是常數不可變


宣告enum 的contrcutor, method, variable
- 可以對enum增加Constructor, instance variable, method, constant specific class body(常數特定類別本體)
- 比如星期,對SUNDAY來說是7, MONDAY是1...所以還需要有常數,最簡單的方法是將每一個enum的值當作可以擁有instance variable的object。如此藉由initial enum的時候傳入constructor的arguments,可以指派每個object對應的值。
- 每個enum都有一個static method: value()

enum Day {//沒有括號
    SUNDAY(7), 
    MONDAY(1), 
    TUESDAY(2), 
    WEDNESDAY(3),
    THURSDAY(4), 
    FRIDAY(5), 
    SATURDAY(6);

    Day (int order) { //constructor
      this.order = order;
    }
    private int order; //instance variable
     
    public int getOrder() { //method
      return order;
    }
}
    Class Week {
    Day day;
     
     public static void main(String[] args){
        Week wk = new Week();
        wk.day = Day.Sunday;
        Week wk2 = new Week();
        wk2.day = Day.Thursday;
        
        System.out.println(wk.day.getOrder());//return 7
        for (Day d: Day.values())//對Day來講其實就是一個array
          System.out.prinln(d + " " + d.getOrder());
     }
    }

印出結果:
7
SUNDAY(7)
MONDAY(1)
TUESDAY(2)
WEDNESDAY(3)
THURSDAY(4)
FRIDAY(5)
SATURDAY(6)

- 關於enum constructor:

  • 不可以直接呼叫enum的constructor(enum的constructor是根據定義在constant後面的argument直接被自動呼叫
  • enum的constructor可以接受多個argument
  • 可以在enum定義constant specific class body
- 關於constant specific class body,想像以下情境:假設一星期只有禮拜六是開心的,其餘都是不開心的,如果不想在getMood()內使用if/else程式碼,最好是讓SATURDAY可以多載getMood()這個function並回傳"Happy"

enum Day {

    SUNDAY(7),
    MONDAY(1),
    TUESDAY(2),
    WEDNESDAY(3),
    THURSDAY(4),
    FRIDAY(5),
    SATURDAY(6){//開啟一段程式碼,定義constant specific class body
        public String getMood(){
           return "Happy";
        }

    };//下面還有程式碼就必須加上這個分號

    Day (int order) { //constructor
      this.order = order;
    }

    private int order; //instance variable

    public int getOrder() { //method
      return order;
    }

    public String getMood(){ //這個method被constant overloading
      return "Unhappy"; //這是default值
    }
}

沒有留言:

張貼留言