close

26   多變的變數

變數(Variable)基本上可當作資料(Data)、欄位(Field)、屬性(Attribute),甚至是參數(Parameter)、引數(argument)來看,視使用場合而定。

變數有各種分類:

(1)   基本資料型別變數與參考資料型別變數

(2)   物件變數與類別變數(實體變數與靜態變數)

(3)   區域變數與全域變數

(4)   區塊變數-外部區塊變數與內部區塊變數

 

 

[26-1基本資料型別變數與參考資料型別變數]

基本資料型別可分為字元(Characters)、整數(Integers)、浮點數(Floating Point Numbers)、與布林值(Booleans)四類,並可再細分為八種資料型態,都有其固定長度。參考資料型別(Reference Types)包含三類:內建物件之陣列(矩陣)、字串,與自行設計之建構物件(含類別及介面)等。基本資料型別屬實值型別,變數存放的是實際的值,參考資料型別存放的是參考,也就是物件的位址。可參考「8參考資料型別和基本資料型別大不同」。此二種資料型別已在前面章節說明過,不再贅述。

 

 

[26-2 物件變數與類別變數(實體變數與靜態變數) ]

宣告於類別中之全域變數(後述),如果須實體化建立物件才可使用,就稱為物件變數(實體變數),不須實體化建立物件即可使用,則稱為類別變數(靜態變數)

 

[26-2-1物件變數(實體變數) ]

一般宣告於類別(class),但不在方法(method)內的非static全域變數稱為實體(Instance)變數,必須建立物件才可使用,也就是物件變數。同一個類別所建立的多個物件,各自保有其變數之識別欄位(field)及內容值,會隨物件之存在及消滅。宣告時如無指定初期值,則會依資料型別自動賦予初期值(參見基本資料型別章節)。操作各物件變數時須指定"物件名稱.變數名稱"格式,如下式之" a1.x "" a2.x "" a3.x "等。

物件變數特徵:

為非靜態全域變數。

※須建立物件才可使用。

※各物件之變數擁有自己的記憶體,不受其他物件變動之影響。

※變數之使用須前置"物件名稱."

 

圖26-1 

 

class A {

  int x = 0;  //實體變數

}

 

class Amain {

  public static void main(String args){

    A a1 = new A();

    A a2 = new A();

    A a3 = new A();

    a1.x = 5;

    a2.x = 10;

    a3.x = 15;

  }

}

 

[26-2-2類別變數(靜態變數) ]

類別變數顧名思義是不須實體化(建立物件)即可取用之變數,但也因為如此在記憶體上是唯一的,在任何物件上進行變更,都是指同一個變數。類別變數必須前置static,也稱為靜態變數。物件變數則因擁有各自的記憶體,修改物件各自的變數將不會影響其他物件。類別變數除非是在同一類別(如主類別)內,應在該變數前置類別名稱,以資區別。

類別變數特徵:

為靜態全域變數。

※不一定要建立物件才可使用。

※各物件之變數為同一記憶體,不受其他物件變動之影響。

※變數之使用須前置"物件名稱.",或"類別名稱.",同一類別內使用可省略。

 

[26-2-2-1他類別取用]

由其他類別來取用時需前置類別名稱。

[無前置類別名稱-編譯錯誤]

class Mynumber53a {

  int m;       //物件變數

static int n;   //類別變數

}

 

public class cla3a {

public static void main(String[] args) {              

    n = 10;

    System.out.println("n = "+ n);

  }                                          

}

執行結果:C:\js>javac cla3a.java

cla3a.java:8: error: cannot find symbol

    n = 10;

    ^

  symbol:   variable n

  location: class cla3a

cla3a.java:9: error: cannot find symbol

    System.out.println("n = "+ n);

                               ^

  symbol:   variable n

  location: class cla3a

2 errors

 

[前置類別名稱]

class Mynumber53b {

int m;       //物件變數

static int n;   //類別變數

}

 

public class cla3b {

public static void main(String[] args) {              

    Mynumber53b.n = 10;  //不須建立物件,但前置類別名稱

    System.out.println("n = "+ Mynumber53b.n);

  }                                          

}

執行結果:C:\js>java cla3b

n = 10

 

[26-2-2-2同類別取用]

變數在自身類別內宣告時,可標示所屬類別之名稱或直接使用。

[無前置類別名稱]

class cla4a {

  static int a = 100;

  public static void main(String[] args) {

    a = a + 1;

    System.out.println("a = "+a);  }  }

執行結果:C:\js>java cla4a

a = 101

 

[前置類別名稱]

class cla4b {

  static int a;       

  public static void main(String[] args) {

    cla4b.a = 5;

    System.out.println("cla4b = "+ cla4b.a);  }  }

執行結果:C:\js>java cla4b

cla4b = 5

 

[混用]

class cla4c {

  static int a = 200;       

  public static void main(String[] args) {

    print4c();

  }

  static void print4c(){

    cla4c.a = a + 2;

    System.out.println("cla4c = "+ cla4c.a);

  }

}

執行結果:C:\js>java cla4c

cla4c = 202

 

註:

靜態方法所宣告之變數皆屬於靜態變數。

只有靜態方法能被其他靜態方法引用。

 

[26-2-2-3靜態變數是唯一的]

建立物件的靜態變數也是唯一的,不因建立物件之多寡而有多個變數,即多個物件共用一個記憶體位置,任何物件都是變更同一記憶體位置變數的內容。

[範例]

class Mynumber55 {

  static int x = 5 ;

  int y = 20 ;

}

 

public class static1 {

  public static void main(String[] args) {

    System.out.println("Mynumber55.x = " + Mynumber55.x) ; // x=5

 

    Mynumber55 M = new Mynumber55( ) ;

    M.x = 10 ;                        

    System.out.println("M.x = " + M.x) ; // x=10

    System.out.println("Mynumber55.x = " + Mynumber55.x) ; // x=10

 

    Mynumber55 N = new Mynumber55( ) ;

    N.x = 15 ;

    System.out.println("N.x = " + N.x) ; // x=15

    System.out.println("M.x = " + M.x) ; // x=15

    System.out.println("Mynumber55.x = " + Mynumber55.x) ; // x=15

 

    System.out.println("M.y = " + M.y) ; // M.y=20

    M.y = 25 ;

    System.out.println("M.y = " + M.y) ; // M.y=25

    System.out.println("N.y = " + N.y) ; // N.y=20

 

    N.y = 33 ;

    System.out.println("N.y = " + N.y) ; // N.y=33

    System.out.println("M.y = " + M.y) ; // M.y=25

  }

}

 

圖26-2

 

 

執行結果:C:\js>java static1

Mynumber55.x = 5

M.x = 10

Mynumber55.x = 10

N.x = 15

M.x = 15

Mynumber55.x = 15

M.y = 20

M.y = 25

N.y = 20

N.y = 33

M.y = 25

 

[範例]

class Planet{

  String planettype = "岩石行星-水星、金星、地球、火星";//物件變數

  static String Pluto = "1930年發現冥王星";//類別變數

  public void planetprint () {

    System.out.println(planettype);

    System.out.println(Pluto);

  }

}

 

public class PlanetMain{

  public static void main(String[] args){

    Planet planetw = new Planet();//(1)

    Planet planeti = new Planet();//(2)

    planetw.planettype = "水行星-木星、土星";//(3)

    planetw.Pluto = "2006年冥王星被降等為矮行星";//(4)

    planeti.planettype = "冰行星-天王星、海王星";//(5)

    planeti.Pluto = "2005年發現鬩神星,質量比冥王星大";//覆蓋(6)

    planetw.planetprint();

    planeti.planetprint();

  }

}

執行結果:C:\js>java PlanetMain

水行星-木星、土星

2005年發現鬩神星,質量比冥王星大

冰行星-天王星、海王星

2005年發現鬩神星,質量比冥王星大

 

變數值變化

階段

變數

(1)初始化

planetw.planettype

岩石行星-水星、金星、地球、

火星

planetw.Pluto

1930年發現冥王星

(2)初始化

planeti.planettype

岩石行星-水星、金星、地球、

火星

planeti.Pluto

1930年發現冥王星

(3)指定

planetw.planettype

水行星-木星、土星

(4)指定

(同一變數)

planetw.Pluto

2006年冥王星被降等為矮行星

planeti.Pluto

(5)指定

planeti.planettype

冰行星-天王星、海王星

(6)指定

(同一變數)

planeti.Pluto

2005年發現鬩神星,質量比

冥王星大

planetw.Pluto

 

 

[26-3區域變數及全域變數]

[26-3-1區域變數]

方法(method)的參數及定義於方法中變數,都屬於區域變數(Local variable),存活於方法被叫用起至執行結束為止。

區域變數因存放於Stack記憶體中,故又稱為Stack變數(Static變數)

區域變數必須被指定初始值,否則會產生編譯錯誤。

宣告在類別內、方法外的為全域變數。

區域變數特徵:

*宣告於方法內。

*宣告時必須給定初始值。

存活於方法被叫用起至執行結束為止。

*不得被其他方法取用。

 

[區域變數不指定初始值-錯誤]

class NonPlanet{

  String nonplanettype;

  public void nonplanetprint1() {

    Stringasteroid;

    System.out.println(nonplanettype);

    System.out.println(asteroid);

  }

}

執行結果:C:\js>javac NonPlanet.java

NonPlanet.java:6: error: variable asteroid might not have been initialized

    System.out.println(asteroid);

                       ^

1 error

區域變數必須指定初始值。

 

[存取非自身方法的區域變數-錯誤]

class NonPlanet{

  String nonplanettype = "行星以外天體包含小行星、彗星、外緣天體、衛星與環等";//全域變數

  public void nonplanetprint1() {

    Stringasteroid = "小行星大小依序為穀神星、智神星、灶神星";//區域變數

    System.out.println(nonplanettype + "…1"); //可存取全域變數

    System.out.println(asteroid + "…1");//可存取本方法區域變數

  }

  public void nonplanetprint2() {

    System.out.println(nonplanettype + "…2");//可存取全域變數

    System.out.println(asteroid + "…2");//不可存取非本方法區域變數

  }

}

 

public class NonPlanetMain{

  public static void main(String[] args){

    NonPlanet nonplanet = new NonPlanet();

    nonplanet.nonplanetprint1();

    nonplanet.nonplanetprint2();  }  }

執行結果:C:\js>javac NonPlanetMain.java

NonPlanetMain.java:10: error: cannot find symbol

    System.out.println(asteroid + "…2");//不可存取非本方法區域變數

                       ^

  symbol:   variable asteroid

  location: class NonPlanet

1 error

不可存取非本方法區域變數。

 

[刪除非本方法區域變數的存取-正確]

class NonPlanet{

  String nonplanettype = "行星以外天體包含小行星、彗星、外緣天體、衛星與環等";//全域變數

  public void nonplanetprint1() {

    Stringasteroid = "小行星大小依序為穀神星、智神星、灶神星";//區域變數

    System.out.println(nonplanettype + "…1"); //可存取全域變數

    System.out.println(asteroid + "…1");//可存取本方法區域變數

  }

  public void nonplanetprint2() {

    System.out.println(nonplanettype + "…2");//可存取全域變數

  }

}

 

public class NonPlanetMain{

  public static void main(String[] args){

    NonPlanet nonplanet = new NonPlanet();

    nonplanet.nonplanetprint1();

    nonplanet.nonplanetprint2();

  }

}

執行結果:C:\js>java NonPlanetMain

行星以外天體包含小行星、彗星、外緣天體、衛星與環等…1

小行星大小依序為穀神星、智神星、灶神星…1

行星以外天體包含小行星、彗星、外緣天體、衛星與環等…2

 

[區域變數-方法參數]

方法(method)參數也是區域變數的一種,故不能適用於自身方法之外。

 

[- 內行星與外行星(Inferior Planets-Superior Planets)]

[錯誤]

class InsuPlanet1{

String inferior = "水星、金星";

  String superior = "火星、木星、土星、天王星、海王星";

  public void Insuprint1(String in, String su){//insu為方法參數(區域變數)

    System.out.println("內行星-" + in + "…1"); //自身可存取

    System.out.println("外行星-" + su + "…1");//自身可存取

  }

public void Insuprint2() {

    System.out.println("內行星-" + in + "…2"); //非自身不可存取

    System.out.println("外行星-" + su + "…2");//非自身不可存取

  }

}

 

public class InsuPlanetMain1{

  public static void main(String[] args){

    InsuPlanet1 insu = new InsuPlanet1();

    insu.Insuprint1(insu.inferior, insu.superior);

    insu.Insuprint2();

  }

}

執行結果:C:\js>javac InsuPlanetMain1.java

InsuPlanetMain1.java:9: error: cannot find symbol

    System.out.println("內行星-" + in + "2"); //非自身不可存取

                                ^

  symbol:   variable in

  location: class InsuPlanet1

InsuPlanetMain1.java:10: error: cannot find symbol

    System.out.println("外行星-" + su + "2");//非自身不可存取

                                ^

  symbol:   variable su

  location: class InsuPlanet1

2 errors

非自身方法不可存取區域變數。

 

[正確]

class InsuPlanet2{

  String inferior = "水星、金星";

  String superior = "火星、木星、土星、天王星、海王星";

  public void Insuprint2(String in, String su){//insu為方法參數(區域變數)

    System.out.println("內行星-" + in); //自身可存取

    System.out.println("外行星-" + su); //自身可存取

  }

}

 

public class InsuPlanetMain2{

  public static void main(String[] args){

    InsuPlanet2 insu = new InsuPlanet2();

    insu.Insuprint2(insu.inferior, insu.superior);

  }

}

執行結果:C:\js>java InsuPlanetMain2

內行星-水星、金星

外行星-火星、木星、土星、天王星、海王星

 

[26-3-2全域變數]

宣告在類別內、方法外的為全域變數(Global variable),全域變數為所有方法皆可存取使用。定義在方法內的為區域變數,只有在該方法內才可存取使用。全域變數宣告可不指定初始值,會被自動設定為預先決定的初始值。

區域變數特徵:

*宣告於類別的方法之外。

*宣告時不必須給予初始值,但會被設定為預先決定的初始值。

存活於類別(物件)存活期間。

*可被類別內所有方法取用。

 

[全域變數的存取]

class Comet{

  String cometsource1 = "彗星的誕生地1-歐特雲";//全域變數

  String cometsource2 = "彗星的誕生地2-古柏帶";//全域變數

  public void cometprint1(){

    System.out.println("方法一存取");

    System.out.println(cometsource1);

    System.out.println(cometsource2);

  }

public void cometprint2(){

String coms1 = cometsource1;

String coms2 = cometsource2;

System.out.println("方法二存取");

  System.out.println(coms1);

  System.out.println(coms2);

  }

}

 

public class CometMain{

  public static void main(String[] args){

    Comet cs = new Comet();

    cs.cometprint1();

cs.cometprint2();

  }

}

執行結果:C:\js>java CometMain

方法一存取

彗星的誕生地1-歐特雲

彗星的誕生地2-古柏帶

方法二存取

彗星的誕生地1-歐特雲

彗星的誕生地2-古柏帶

 

[26-4區塊變數-外部區塊變數及內部區塊變數]

Java程式是以區塊為基礎單位所組成。以{  }所涵蓋的範圍稱為區塊,外層的稱為外部區塊,內層的稱為內部區塊。

 

[26-4-1外部區塊及內部區塊]

[範例]

class Block1 {

  //全域變數

  int a = 1 ;

 

public void bmethod1()

  //外部區塊

{int b = 2 ;

 

//內部區塊1

{int c = 3;

       System.out.println("內部區塊1變數c = " + c) ;

      }

 

  //內部區塊2

{int d = 4;

       System.out.println("內部區塊2變數d = " + d) ;

      }

 

System.out.println("外部區塊變數b = " + b) ;

 

}

 

}

 

public class BlockMain1{

  public static void main(String[] args){

    Block1 b1 = new Block1();

    b1. bmethod1();

  }

}

執行結果:C:\js>java BlockMain1

內部區塊1變數c = 3

內部區塊2變數d = 4

外部區塊變數b = 2

 

[26-4-2區塊變數之關係]

[不相隸屬的2個區塊,可宣告相同名稱之變數]

不相隸屬的2個區塊,可宣告相同名稱之變數,但視為不同之變數,如下例之內部區塊變數1與內部區塊變數2之變數c

class Block2 {

  //全域變數

  int a = 11 ;

 

public void bmethod2()

  //外部區塊

{int b = 22 ;

 

//內部區塊1

{int c = 33;

       System.out.println("內部區塊1變數c = " + c) ;

      }

 

  //內部區塊2

{int c = 44;

       System.out.println("內部區塊2變數c = " + c) ;

      }

 

System.out.println("外部區塊變數b = " + b) ;

 

}

 

}

 

public class BlockMain2{

  public static void main(String[] args){

    Block2 b2 = new Block2();

    b2. bmethod2();

  }

}

執行結果:C:\js>java BlockMain2

內部區塊1變數c = 33

內部區塊2變數c = 44

外部區塊變數b = 22

 

[外部區塊與內部區塊不得宣告相同名稱之變數(編譯錯誤) ]

class Block3 {

  //全域變數

  int a = 111 ;

 

public void bmethod3()

  //外部區塊

{int b = 222 ;

 

//內部區塊1

{int b = 333;

       System.out.println("內部區塊1變數b = " + b) ;

      }

 

  //內部區塊2

{int c = 444;

       System.out.println("內部區塊2變數c = " + c) ;

      }

 

System.out.println("外部區塊變數b = " + b) ;

 

}

 

}

 

public class BlockMain3{

  public static void main(String[] args){

    Block3 b3 = new Block3();

    b3. bmethod3();

  }

}

編譯結果:C:\js>javac BlockMain3.java

BlockMain3.java:10: error: variable b is already defined in method bmethod3()

    {int b = 333;

         ^

1 error

 

[內部區塊可取用外部區塊之宣告變數]

class Block4 {

  //全域變數

  int a = 1111 ;

 

public void bmethod4()

  //外部區塊

{int b = 2222;

 

//內部區塊1

{int c = 3333;

       System.out.println("外部區塊變數b = " + b) ;

      }

 

  //內部區塊2

{int d = 4444;

       System.out.println("內部區塊2變數d = " + d) ;

      }

 

System.out.println("外部區塊變數b = " + b) ;

 

}

 

}

 

public class BlockMain4{

  public static void main(String[] args){

    Block4 b4 = new Block4();

    b4. bmethod4();

  }

}

執行結果:C:\js>java BlockMain4

外部區塊變數b = 2222

內部區塊2變數d = 4444

外部區塊變數b = 2222

 

[外部區塊不可取用內部區塊之宣告變數(編譯錯誤)]

因外部區塊存取內部區塊宣告之變數時,內部區塊已然執行結束,內部區塊變數不復存在,故外部區塊不可取用內部區塊宣告之變數,會編譯錯誤。

class Block5 {

  //全域變數

  int a = 11111;

 

public void bmethod5()

  //外部區塊

{int b = 22222;

 

//內部區塊1

{int c = 33333;

       System.out.println("外部區塊變數c = " + c) ;

      }

 

  //內部區塊2

{int d = 44444;

       System.out.println("內部區塊2變數d = " + d) ;

      }

 

System.out.println("內部區塊1變數c = " + c) ;

 

}

 

}

 

public class BlockMain5{

  public static void main(String[] args){

    Block5 b5 = new Block5();

    b5. bmethod5();

  }

}

執行結果:C:\js>javac BlockMain5.java

BlockMain5.java:19: error: cannot find symbol

    System.out.println("內部區塊1變數c = " + c) ;

                                       ^

  symbol:   variable c

  location: class Block5

1 error

[區塊關係整理]

區塊

宣告同名變數

外部區塊宣告

之變數

內部區塊宣告

之變數

外部區塊

不得與內部區塊宣告相同名稱之變數

 

可以取用

 

不可以取用

內部區塊

不得與外部區塊宣告相同名稱之變數

 

可以取用

 

可以取用

平行區塊

(不相隸屬)

可以宣告相同名稱之變數,但視為不同變數

 

-

 

-

 

 

[26-5主方法內的變數及靜態方法內的變數]

主方法因冠有static,和靜態方法一樣,其內宣告之變數皆為靜態變數。主方法、靜態方法內之同名變數皆可存取靜態全域變數,反之如全域變數非為靜態變數,則會編譯錯誤。

 

[26-5-1主方法、靜態方法內宣告之同名變數與全域變數之關係]

[靜態全域變數-可存取]

public class SGVariable{

  static int a = 9;

  static int b = 7;

  public static void main(String[] args){

    a = 99;  //和全域變數為同一變數

    System.out.println("a = " + a);

    smethod();

  }

  public static void smethod(){

    b = 77;  //和全域變數為同一變數

    System.out.println("b = " + b);

  }

}

執行結果:C:\js>java SGVariable

a = 99

b = 77

 

[非靜態全域變數-編譯錯誤]

public class NSGVariable{

  int a = 9;

  int b = 7;

  public static void main(String[] args){

    a = 99;  //和全域變數非為同一變數,為區域變數

    System.out.println("a = " + a);

    smethod();

  }

  public static void smethod(){

    b = 77;  //和全域變數非為同一變數,為區域變數

    System.out.println("b = " + b);

  }

}

執行結果:C:\js>javac NSGVariable.java

NSGVariable.java:5: error: non-static variable a cannot be referenced from a static context

    a = 99;  //和全域變數非為同一變數,為區域變數

    ^

NSGVariable.java:6: error: non-static variable a cannot be referenced from a static context

    System.out.println("a = " + a);

                                ^

NSGVariable.java:10: error: non-static variable b cannot be referenced from a static context

    b = 77;  //和全域變數非為同一變數,為區域變數

    ^

NSGVariable.java:11: error: non-static variable b cannot be referenced from a static context

    System.out.println("b = " + b);

                                ^

4 errors

 

 

 

[26-6參數與引數]

參數與()引數也是變數。

主方法的args及緊接一般方法名稱之後的()所宣告的變數,一般稱為參數(也有人稱為引數),其所傳遞的值則稱為引數,當然args本身即是引數之意義。方法之參數為區域變數之一,已如本章前述區域變數章節所說明者。下面僅就主方法進行說明。

 

[26-6-1方法參數]

方法之參數為區域變數之一,已如本章前述區域變數章節所說明者。

 

[26-6-2命令列執行時引數]

命令列執行時輸入之引數(下例之apple orange pear),即是主方法之args

下例為本系列"15 方法(2)-兼論靜態資料"曾舉過之例子。

class MainArg1{

  public static void main(String[ ] args){

    int i ;

    if (args.length<3){

          System.out.println("請輸入3個命令列引數。") ;

          System.exit(1);

         }

    else {

          for(i = 0 ; i < args.length ; i++)

          System.out.println((i+1) + "" + args[i]) ;

         }

  }

}

執行結果:

C:\js>java MainArg1

請輸入3個命令列引數。

 

C:\js>java MainArg1 apple orange pear

1apple

2orange

3pear

 

 

[26-7變數的其他修飾子]

變數尚有finalprivate修飾子,及superthis關鍵字,請參閱本系列"21 繼承及相關之修飾子、關鍵字"

 

[26-7-1 final修飾子]

加上final修飾子的欄位變數,其值不可以被更改。

 

[26-7-2 private修飾子]

父類別private欄位變數,子類別繼承後無法使用,也無法將父類別建立物件後使用。

 

[26-7-3 super關鍵字]

super關鍵字可以引用父類別欄位變數。

 

[26-7-4 this關鍵字]

this關鍵字是指所屬類別的欄位變數。

arrow
arrow

    祈泊 發表在 痞客邦 留言(3) 人氣()