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 "等。
物件變數特徵:
※為非靜態全域變數。
※須建立物件才可使用。
※各物件之變數擁有自己的記憶體,不受其他物件變動之影響。
※變數之使用須前置"物件名稱."。
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
}
}
執行結果: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){//in、su為方法參數(區域變數)
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){//in、su為方法參數(區域變數)
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
1:apple
2:orange
3:pear
[26-7變數的其他修飾子]
變數尚有final、private修飾子,及super、this關鍵字,請參閱本系列"21 繼承及相關之修飾子、關鍵字"。
[26-7-1 final修飾子]
加上final修飾子的欄位變數,其值不可以被更改。
[26-7-2 private修飾子]
父類別private欄位變數,子類別繼承後無法使用,也無法將父類別建立物件後使用。
[26-7-3 super關鍵字]
以super關鍵字可以引用父類別欄位變數。
[26-7-4 this關鍵字]
this關鍵字是指所屬類別的欄位變數。
留言列表