第 9 章 物件的建構.

Slides:



Advertisements
Similar presentations
软件编程基础 一、程序的编辑 Java 源程序是以 Java 为后缀的简单的文本文件,可以用各种 Java 集成开发环境中的源代码编辑器来编写,也可以用其他文 本编辑工具,如 Windows 中的记事本或 DOS 中的 EDIT 软件等。 利用文字编辑器编写下列程序 public class Hello.
Advertisements

第五章 类的继承和派生 Inheritance/extends/derive. 教学目标 理解继承的概念和作用 派生类的定义 理解访问修饰符 protected 方法的重写 继承下的构造函数的使用 继承下的 finalize 方法的使用 理解超类和子类的关系.
第一章 Java 程序设计技术 概述  什么是 Java 语言  一个简单的 Java 程序  程序的编译和运行  常见错误  使用 Java 核心 API 文档.
第 7 章 配合 例子源代码一起使用 Power point 制作:耿祥义 张跃平 普通高等教育 “ 十一五 ” 国家级规划教材 JSP 与 JavaBean.
Power point 制作 耿祥义 张跃平 配合 例子源代码一起使用. 第 5 章 JSP 与 JavaBean JavaBean 是一个可重复使用的软件组件, 是遵循一定标准、用 Java 语言编写的一 个类,该类的一个实例称为一个 JavaBean ,简称 bean.
Java 程序设计(第二版) 普通高等教育 “ 十一五 ” 国家级规划教材 辛运帷等 编著 徐小平 主讲.
系統分析與設計 楊子青 H-1 H 、物件導向技術 n 物件導向的基本概念 – 物件、類別 – 封裝、繼承 – 同名異式 ( 多型 ) 、超荷 ( 過載 ) n 物件導向分析與設計及塑模工具 n UML 塑模工具.
第 2 章 醫療新紀元 第二單元 醫療消費面面觀 第 2 章 醫療新紀元 為什麼要有全民健康保險? 許多沒有保險的國人生病時,無法支付 龐大的醫療費用,而無法就醫、延誤治 療,甚至全家可能陷入財務上的困境。 為了照顧全體民眾的健康,民國 84 年起 ,政府便開辦全民健康保險,規定所有 人民都必須加入。
第6章 对象和类.
Java语言的特点 Java开发环境的搭建
单元二:面向对象程序设计 任务二:借书卡程序设计.
第四章 类、对象和接口.
第三讲 面向对象(上).
JAVA 编 程 技 术 主编 贾振华 2010年1月.
病歷複印本申辦 病歷影本 [什麼人可以申請] [到那申請] [何時可申請] [申辦流程]
开远市第一中学 2014年高考志愿填报指导会 2014年6月26日.
<<會計資訊系統課程講義>> 統一塑模語言(UML)語法精要 -- 物件導向概念、需求分析及系統分析
6. 6 Overloading methods and constructors 6
这是一个数字的 乐园 这里埋藏着丰富的 宝藏 请跟我一起走进数学的 殿堂.
问卷调查的规范与技术 问卷调查的规范与技术.
七(7)中队读书节 韩茜、蒋霁制作.
四資二甲 第三週作業 物件導向程式設計.
负 债 第九章 主讲老师:潘煜双 方正为人,勤慎治学.
第一章 面向对象程序设计.
设计模式可以帮助我们改善系统的设计,增强 系统的健壮性、可扩展性,为以后铺平道路。
程設一.
第二章 JAVA语言基础.
類別與物件 Class & Object.
JAVA程序设计 (03) JAVA Programming
第9章 单例模式 Website:
1 Department of Computing.
第5章 Java中类、对象、接口 及包的概念 5.1 类的基本概念 5.2 类的继承概念 5.3 抽象类和接口 5.4 包.
程式設計實作.
第5章 面向对象程序设计 本章要点 5.1 面向对象程序设计概述 5.2 Java语言的面向对象程序设计 5.3 方法的使用和对象数组
H、物件導向技術 物件導向的基本概念 物件、類別 封裝、繼承 同名異式(多型) 、超荷(過載) 物件導向分析與設計及塑模工具 UML塑模工具.
物件導向程式設計 (Object-Oriented rogramming)
Classes Lecturer: 曾學文.
CHAPTER 9 建構方法 ROBERT.
第六章 类的扩展与继承.
程式敘述執行順序的轉移 控制與重複、方法 Lecturer:曾學文.
程式設計實作.
CH09 套件 物件導向程式設計(II).
Java软件设计基础 5. 继承与多态.
Java程序设计 第9章 继承和多态.
中国矿大计算机学院杨东平 第5章 接口和包 中国矿大计算机学院杨东平
2019/1/16 Java语言程序设计-类与对象 教师:段鹏飞.
第5讲 使用类和对象编程(三) 内部类 实例 程序控制结构 选择语句.
Ch02-基礎語法.
C/C++/Java 哪些值不是头等程序对象
* 單元:電腦與問題解決 主題:Java物件導向程式設計-類別與物件 台南縣國立善化高中 蕭嘉民 老師
第16章 虛擬與多形 16-1 虛擬函數 16-2 純虛擬函數與抽象類別 16-3 多形 16-4 虛擬繼承與虛擬解構子.
简单工厂模式.
JAVA 编 程 技 术 主编 贾振华 2010年1月.
第二章 Java基本语法 讲师:复凡.
Java程式初體驗大綱 大綱 在學程式之前及本書常用名詞解釋 Hello Java!程式 在Dos下編譯、執行程式
Interfaces and Packages
Object-Oriented Programming in C++ 第二章 类和对象
第二章 Java语法基础.
辅导课程十一.
第二章 Java基本语法 讲师:复凡.
方法進階及物件導向基礎 Lecturer: 楊昌樺.
第6單元 6-1 類別的繼承 (Class Inheritance) 6-2 抽象類別 (Abstract Class)
JAVA 程式設計與資料結構 第三章 物件的設計.
第2章 Java语言基础.
對於成員(member)存取權的限制 成員的資料被毫無限制的存取,任誰都可以指定任意值給成員,Java語言為了防止這種現象的產生,規定:有一種成員的資料不能任由類別外部的任何人隨意存取。
05 方法. 05 方法 5.1 方法 在一個較大型的程式中,通常會將具有特定功能或經常重複使用的程式,撰寫成獨立的小單元,稱為「方法」(Method),並賦予方法一個名稱,當程式需要時就可以呼叫此方法來執行該段特定程式。(此種重複使用的程式小單元在其他語言中可能稱為程序、副程式或函式, Visual.
第二章 Java基础语法 北京传智播客教育
輸出執行結果到螢幕上 如果要將執行結果的文字和數值都「輸出」到電腦螢幕時,程式要怎麼寫? class 類別名稱 {
第6章 继承和多态 伍孝金
Summary
Presentation transcript:

第 9 章 物件的建構

本章重點 9 - 1 建構方法 (Cons t ructor) 9 - 2 封裝與資訊隱藏 9 - 3 static 共享成員變數 9 - 4 綜合演練

前言 以前一章範例程式中所用的汽車類別為例, 每次建立物件後, 都還要在 main() 方法中另行設定物件的成員變數:

前言 如果可以將建立物件與設定物件狀態的動作結合在一起, 會有以下優點: 避免忘記設定物件初始狀態, 這種錯誤看起來雖然是小事, 但往往在程式出錯時也最容易忽略。由於編譯器無法猜測物件在產生後是否需要進行任何初始化的動作, 因此無法像是警告您變數尚未設定初值的方式提出警示。

前言 更接近自然界的物件。舉例來說, 小嬰孩必定在出生前就決定了膚色與髮色, 而不會是先出生, 然後才顯現膚色或是髮色;相同的道理, 舞台劇上場的演員也不會先出場, 然後請觀眾等一下, 在台上上妝之後才開始演出。既然如此, 程式中的各個物件也應該在產生的同時就設定好初始狀態, 直接參與程式的執行。 為了解決上述的問題, Java 這一類物件導向的程式語言便提供一個特別的機制, 來幫物件設定初始狀態, 而這也就是本章的主題。

9 - 1 建構方法 (Cons t ructor) 建構方法就是物件導向程式語言對於物件初始化的解決方案。顧名思義, 建構方法是一個方法 (method), 比較特別的是:它是在建立物件時由系統自動呼叫, 以建構物件初始的狀態, 因此名之為建構方法。也因為在物件產生時會自動呼叫建構方法, 因此使用 new 運算子時, 才必須在類別的名稱之後加上一對小括號, 這對小括號的意義就是呼叫建構方法。

建構方法 (Cons t ructor) 9-1-1 預設建構方法 (Defau l t Constr ucor) 9-1-2 自行定義建構方法 無參數的建構方法 具有參數的建構方法 9-1-3 建構方法的多重定義 (Overloading) 9-1-4 this 保留字

9-1-1 預設建構方法 (Defau l t Constr ucor) 建構方法的名稱與類別名稱相同, 如果類別之中並未定義任何建構方法,則 Java 編譯器會自動幫類別定義一個預設建構方法, 例如:

預設建構方法 (Defau l t Constr ucor) 在上面這個例子中, Test 類別就沒有定義任何的建構方法, 因此 Java 編譯器便會自動定義一個預設的建構方法, 此時就如同以下的程式:

預設建構方法 (Defau l t Constr ucor) 第 5 〜 6 行就是一個建構方法, 但這個建構方法什麼事情都沒有做, 單純只是為了類別必須定義有建構方法而存在。也正因為如此, 如果類別在產生物件的時候並不需要進行任何初始化的動作, 撰寫時就可以省略定義建構方法, 讓 Java 編譯器自動替我們產生。

9-1-2 自行定義建構方法 如果需要對新建立的物件進行任何初始化設定, 那麼就可以自行定義建構方法。定義建構方法時除了要依循一般方法的定義規則外, 還有以下幾點需要注意: 建構方法不能傳回任何值, 因此不需也不能註明傳回值型別, 連 void 也不可加上, 如果加上傳回型別, 反而會造成編譯錯誤。 建構方法一定要和類別同名, 而無法使用其他名稱來為建構方法命名。

無參數的建構方法 最簡單的建構方法就是在方法的主體區塊中對物件進行初始化的設定,例如:

無參數的建構方法

無參數的建構方法 在程式第14 行一樣是用 new 運算子建立物件, 由於 Java 會自動呼叫第5 〜 8行的建構方法, 所以成員變數 x 與 y 都被設為指定的值, 並不需要在產生物件之後另外指定。

具有參數的建構方法 建構方法也可以接受參數, 讓物件的建構時更具彈性。建構方法若能接受參數, 在建立物件時, 就可以透過跟隨在 new 運算子以及類別名稱之後的那一對小括號傳入參數。例如:

具有參數的建構方法

具有參數的建構方法 要特別注意的是, 一旦定義了建構方法之後, 使用 new 運算子產生物件時就必須依據建構方法的定義, 傳入相同數量以及型別的參數, 就像是呼叫一般的方法一樣, 否則編譯時就會產生錯誤, 例如:

具有參數的建構方法 編譯後的錯誤訊息告訴我們, 編譯器找不到僅需要單一個整數的建構方法。

9-1-3 建構方法的多重定義(Overloading) 建構方法也和一般的方法一樣, 可以使用多重定義的方式, 定義多種版本的建構方法, 以便能夠依據不同的場合, 為新產生的物件進行最適當的初始設定。編譯器會依據所傳入參數的個數以及資料型別, 選擇符合的建構方法, 就像是編譯器選擇多重定義的一般方法時一樣。 舉例來說, 底下的類別就同時定義有多個版本的建構方法:

建構方法的多重定義(Overloading)

建構方法的多重定義(Overloading)

建構方法的多重定義(Overloading)

建構方法的多重定義(Overloading) 在此範例中, Test 類別擁有 3 種版本的建構方法, 如此就可以依據需要,使用適當的建構方法。 有一點需要特別注意:當我們為類別定義需要參數的建構方法時, Java編譯器就不會替我們建立無參數的預設建構方法, 因此這時候最好也自行為類別加上一個不需參數的建構方法。因為當程式產生物件時, 未必每次都需要將物件設定為特定狀態, 此時這個不需參數的建構方法就可以提供一種預設狀態給物件, 撰寫程式時就可以享有一定程度的方便性。

建構方法的多重定義(Overloading) 因此, 建議在為類別設計建構方法時, 先定義一個不具參數的建構方法,可以將物件設定為預設的狀態。接著, 再根據不同的情況, 定義特殊的版本。

9-1-4 this 保留字 由於建構方法主要的用途是設定物件的初始狀態, 傳入的參數大多與類別的成員變數相關, 因此在為這些參數命名時通常都會和成員變數相同, 這時就和一般的方法一樣, 參數的名稱會遮蔽掉 (Shadowing) 成員變數。如果需要存取成員時, 就可以使用上一章介紹過的 this 保留字, 以表示目前執行此方法的物件。例如:

this 保留字

this 保留字

this 保留字 除了在參數名稱與成員名稱相同的情況派上用場外, this 保留字還有一個很大的妙用。如果在建構方法中所需要進行的設定, 有一部份在另外一個版本的建構方法中完全重複, 而想要在直接呼叫該版本的建構方法時, 並不能直接使用類別名稱呼叫該建構方法:

this 保留字

this 保留字

this 保留字 在第 6 行中, 本來想要利用另一個只需單一參數的建構方法設定成員變數 x 的值, 但是從編譯後的錯誤訊息說明了編譯器找不到一個叫做 Test()的方法。換句話說, 雖然建構方法和一般方法很類似, 但在 Java 中卻是被視為不同的元素, 不能以呼叫其它方法的方式呼叫建構方法。因此要呼叫其他版本的建構方法, 必須使用 this 保留字, 例如:

this 保留字

this 保留字

this 保留字 第 6 行的敘述, 就是使用 this 呼叫其他版本建構方法的方式, 在 this 之後的小括號可以放入要傳遞給其他版本建構方法的參數, 編譯器就是透過這裡的參數個數與型別來找尋適當的其他版本。 為了類別特性的一致性, 建議您可以多利用 this 保留字, 將重複的設定動作集中在適當的建構方法中, 並且在其他需要同樣建構方式的建構方法中呼叫該建構方法, 避免因為在不同的建構方法中的疏忽, 而使得產生的物件行為或是特性不一致。

9 - 2 封裝與資訊隱藏 學會建構方法的用法後, 即可在建立物件時, 一併完成物件的初始化, 不必再於 main() 方法中直接修改物件的成員變數。但我們現在僅只是『不必』直接存取成員變數, 對物件導向程式設計方法, 則是要求『不能』直接修改物件的成員變數。以術語來說就是資訊隱藏 (Information Hiding), 亦即類別外部 (例如 main() 方法) 不能看到、接觸到物件內部的資訊 (屬性)。

封裝與資訊隱藏 那外部要如何得知或改變物件的屬性呢?那就必須透過類別公開給外部的方法, 對應到生活中實際接觸的物件也是如此, 例如要讓車子前進, 必透過車子提供給外部的油門;要轉彎, 可使用方向盤;而要讓行進中的車子停止, 則要使用剎車。油門、方向盤、剎車就是車子提供給我們操作車子的方法, 使用這些方法, 就會改變車子的狀態與屬性 (速度、方向、位置、油量等等)。

封裝與資訊隱藏 因此當我們設計類別時, 就必須提供必要的方法, 讓外部能正常操作物件;而透過這種程式設計方式, 只要我們的類別有公開能操作物件的方法,其他人就算完全不知道類別內部是如何設計、運作, 也可透過這些公開的介面來使用我們所設計好的類別, 達到程式碼重複使用、提高軟體開發效率的目的。

封裝與資訊隱藏 就好比大部份的駕駛人不會瞭解車子內部是如何設計與運作, 但只要會使用車子公開的油門、方向盤、剎車. . .等介面, 就會開車。而將類別的屬性、操作屬性的方法包裝在一起, 只對外公開必要的介面, 即稱為封裝(Encapsulation)。

封裝與資訊隱藏 9-2-1 類別成員的存取控制 9-2-2 為成員變數撰寫存取方法 9-2-3 傳回成員物件的資訊

9-2-1 類別成員的存取控制 為了讓外部不能任意存取封裝在類別內的屬性或方法, 我們必須在類別之中, 使用存取控制字符 (Access Mocifier) 來限制外部對類別成員變數的存取。以下可以使用的存取限制字符:

類別成員的存取控制 其中 protected 存取控制字符會在第 11、13 章做進一步的說明, 本章先說明 private 及 public。private 存取控制字符, 就如其英文字面含意一樣, 是指該成員變數是類別所私有, 除了類別中的方法以外, 對於其他的類別來說,這個成員變數都好像看不到一樣, 無法使用。例如:

類別成員的存取控制

類別成員的存取控制

類別成員的存取控制 編譯時就會有錯誤訊息, 表示 i 是 Test 類別中的 private 成員, 所以不能在Test 類別以外的地方存取。只要把第 21 行刪除, 程式就可以編譯並正確執行。 也就是說, 凡是被標示為 private 的成員變數, 除非是透過類別所定義的方法, 否則就無法存取該成員。

類別成員的存取控制 這正好一開始所提到的資訊隱藏特性, 在上述的例子中, main() 方法不能直接存取物件的成員變數值, 只能透過類別所提供的 show()、modifyMember() 方法來顯示或修改成員變數, 至於怎麼顯示或修改, main() 方法則不需去瞭解。 如果沒有特別標示存取控制字符, Java 就會採用預設控制 (Default Access),也就是只有同一個套件 (Package) 的類別可以存取此成員變數。

類別成員的存取控制 我們會在第13 章正式介紹套件, 目前只需先記得, 如果編譯好的 .class 檔案都位於同一個資料夾, 那麼這些類別就會被視為是在同一個套件中。 這也正是為什麼先前的範例程式全都沒有標示存取控制字符, 但在 main() 方法中可任意存取個別成員變數的原因, 因為同一個檔案中的類別在編譯後都會在同一個資料夾下。 另外, 存取限制字符也可以應用在方法上, 用來限制哪些方法可以被外界呼叫, 而哪些方法只是提供給類別中的其他方法呼叫。在第 11 章還會針對存取限制字符做進一步的討論。

9-2-2 為成員變數撰寫存取方法 為了實作資訊隱藏這個物件導向程式設計的基本觀念, 在設計類別時, 就要注意應盡量避免暴露類別內部的實作細節, 讓類別的使用者可不依賴內部實作細節亦可撰寫程式。這樣的好處之一, 即是若日後類別需變更內部的實作方式, 但只要它仍提供相同的操作方法, 則使用者也不必修改使用到該類別的程式。 因此為了隱藏成員變數, 我們就需適時地為成員變數加上存取限制。一般而言, 可使用下列的原則:

為成員變數撰寫存取方法 除非必要, 最好所有成員變數都加上 private 存取限制。 如果使用此類別的程式需要透過成員來完成某件事, 就由類別提供方法來完成。 如果需要修改或取得成員的值, 就提供專門存取成員的方法。通常用來取得成員值的方法會命名為 getXXX, 其中 XXX 就是成員變數的名稱;相對的, 用來設定成員值的方法就稱為setXXX。這樣一來, 可以將更改成員的動作侷限在此方法中, 往後對於除錯或要為更改成員而引發的影響加上處理工作時就會比較方便。

為成員變數撰寫存取方法 相同的道理, 對於類別中所定義的方法, 加上存取限制的通則如下: 如果是要提供給外界呼叫的方法, 請明確的標示為 public 存取限制。像是剛剛所提到的 get、set 方法, 就是最好的例子。 如果只是供類別中其他的方法呼叫的方法, 請明確的標示為 private 存取限制, 以避免被類別外部的程式呼叫。

為成員變數撰寫存取方法 對於建構方法, 除非有特別的用途, 否則應該都標示為 public, 因為若是標示為 private, 則 new 運算子就無法呼叫建構方法, 而不能設定物件的初始狀態了。 以下面的程式為例:

為成員變數撰寫存取方法

為成員變數撰寫存取方法

為成員變數撰寫存取方法 在程式中第 10〜13 行就為 Test 類別的 private 的成員 x、y 提供了一組存取的方法, 並分別取名為 getX、setX 與 getY、setY, 同時也特別將這2組存取方法都標示為 public 存取限制。 回頭看前一章的汽車類別範例, 只要加上合適的建構方法, 並將成員變數設為 private, 就可讓它將資訊隱藏起來, 我們只能透過它所公開的方法來控制物件:

為成員變數撰寫存取方法

為成員變數撰寫存取方法

為成員變數撰寫存取方法

為成員變數撰寫存取方法 以這種方式撰寫程式時, main() 方法的內容看起來會比較簡捷, 因為其中大多是直接呼叫類別所提供的方法, 我們也比較能看出 main() 方法是在做什麼事。

9-2-3 傳回成員物件的資訊 透過前面幾個例子, 相信大家都已對資訊隱藏有初步的認識, 原則就是將成員變數設為 private, 並提供必要的公開方法。但如果類別的成員變數是另一個類別的物件, 則在設計與此成員物件相關的公開方法時, 要注意是否要將這個私有成員物件, 也公開到外部。 舉例來說, 在平面幾何中, 一個圓的可以由一個圓心座標和半徑來定義。因此我們可以先設計一個代表座標點的類別 Point, 然後再於圓的類別中用Point 的物件代表圓心座標, 例如:

傳回成員物件的資訊

傳回成員物件的資訊

傳回成員物件的資訊 在 Circle 類別中的 getP() 方法, 其用途是讓外界取得圓心座標, 但如果直接傳回私有成員變數 p 的參照, 那麼外界就可取得此物件的參照, 並透過此參照呼叫 Point 類別的 setx()、 sety() 方法, 如此一來就變成外界可直接修改私有成員物件了, 請參考以下的例子:

傳回成員物件的資訊

傳回成員物件的資訊

傳回成員物件的資訊

傳回成員物件的資訊

傳回成員物件的資訊 在第 30 行為 Circle 類別定義了一個取得圓心座標的 getp() 方法, 此方法直接將成員變數 p 傳回, 也就是傳回圓心物件的參照。因此當 main() 方法在第 52 行用 getp() 方法取得圓心後, 可在第 53 行用它呼叫 Point 類別的 setx() 方法來變更 X 軸座標。由執行結果也可看到圓心的 X 軸座標的確被更改了。

傳回成員物件的資訊 雖然這樣改變圓心座標的方式看似合理, 但對 Circle 類別而言, 卻失去『資訊隱藏』的特性, 因為外界不需透過它, 即可任意變更其圓心座標。雖然成員變數 p 確實是 private, 但因為 getp() 方法是將其參照傳回, 所以外界即可透過此參照直接存取到私有的物件。 如果要保護 Circle 類別的內容, 則可修改 getp() 方法, 讓它變成傳回另一個座標值相同的 Point 物件, 而非傳回私有的 Point 物件參照, 如此一來外界仍能取得一個代表圓心座標的 Point 物件, 但該物件並非 Circle 類別的成員, 因而可達到『資訊隱藏』的目的。修改後的程式如下:

傳回成員物件的資訊

傳回成員物件的資訊

傳回成員物件的資訊

傳回成員物件的資訊

傳回成員物件的資訊 這個範例與前一個範例有 2 個主要不同之處: 第 25 〜28 行為 Point 類別定義了另一個建建構方法, 此建構方法是用現有的點物件為參數, 並複製參數物件的座標值給新物件。 第 35〜37 行的 getp() 方法, 改成傳回一個新建立的 Point 物件, 而非私有的成員變數 p, 但建立新物件時則是以 p 為參數, 所以傳回的物件之座標會與圓心相同。

傳回成員物件的資訊 經過上述的修改後, 第 57、58 行取得並修改圓心的座標時, 即不會動到Circle 物件實際的圓心。由執行結果可發現, 程式第 59 行顯示圓的資訊時, 其圓心座標並未被修改。 這兩個範例程式中, 都為類別定義了一個 toSt ring()方法, 這個方法會將物件的資訊以字串的形式傳回, 這種設計方式, 讓我們需要輸出物件資訊時, 可以有較彈性的用法, 因為我們可利用 "+" 運算子將多個字串組合在一起。

9-3 static 共享成員變數 前面的類別範例所建立的物件, 都能擁有個自己成員變數, 以表現物件屬性間的差異。但在某些情況下, 我們可能會想讓所有物件的某個屬性都是相同的, 此時就可使用 static 共享成員變數來表現這個物件的共通屬性。 以我們示範過的汽車類別為例, 假如該類別代表的是某一款特定的車型,所有物件代表同型車的多個個體。

static 共享成員變數 載油量當然會隨車子所加的油量、行駛的里程而隨時變動;但耗油率我們可能想將之設為固定值, 即所有物件的耗油率都相同。此時若單只是在建構物件時, 將耗油率設為相同的數值, 並不方便, 也不安全, 因為如果寫程式時不小心寫錯了, 就會造成耗油率不一致等問題。 因此 Java 就提供 static 的成員變數, 來解決這個問題。

static 共享成員變數 9-3-1 stat ic 存取控制 9-3-2 使用類別名稱存取 static 成員變數 解開main() 方法之謎 9 - 3 -5 final 存取控制

9-3-1 stat ic 存取控制 當我們將類別的成員變數加上 static 存取控制字符, 就表示所有屬於此類別的物件, 都會共享這個成員, 而非每一個物件擁有自己的一份成員。舉例來說:

stat ic 存取控制

stat ic 存取控制 在程式 18 〜20 行雖然分別為物件的成員變數 y 設定不同的值, 但由於該成員變數為 static , 所以其實這 3 個物件的成員變數 y 是同一份, 每次設定值時, 都是設定同一個 y, 因此最後一次呼叫建構方法時將之設為 60 之後, 不論是透過 a、b、c 參照來取得成員變數 y 的值, 都是60 了。反之, 成員變數 x 則因為不是 static, 所以個別物件都享有自己的一份。

9-3-2 使用類別名稱 存取 static成員變數 stat ic 成員變數除了可用如同一般成員變數的方式存取外, 也可以透過類別名稱存取之。以剛剛的 Test 類別, 我們可以用如下的方式存取成員變數 y:

使用類別名稱存取 static成員變數

使用類別名稱存取 static成員變數 在第 21 行以『類別名稱.成員變數名稱』的方式即存取到 y, 並將它設為 100, 所以之後顯示物件內容時, 其 y 值都是 100。 此外, 我們甚至可在未建立物件的情況下, 也能使用類別的 static 成員變數:

使用類別名稱存取 static成員變數

使用類別名稱存取 static成員變數

使用類別名稱存取 static成員變數 在這個範例中, Test 類別的建構方法只會設定成員變數 x 的值, 而第 17行則在未產生任何物件之前即存取 static 成員變數 y, 並設妥其值, 所以之後的建構方法雖未設定 y 的值, 但由執行結果可看到各物件的 y 值都是 100 。

9-3-3 stat ic 初始區塊 由於 static 成員變數的共享特性, 通常不會在建構方法中設定其初值, 因此 static 成員變數要不就是在宣告的同時直接設定初值, 要不就是另外單獨設定。為了避免寫程式時忘了設定 static 成員變數, Java 還提供了 static 初始區塊 (Static Initializer), 可以確保在產生物件之前, 設定 static 成員。其用法就是在類別的定義中, 加入一個以 static 保留字為首的大括號區塊, 並在此區塊中加入初始化 static 成員變數的敘述。例如:

stat ic 初始區塊

stat ic 初始區塊

stat ic 初始區塊

stat ic 初始區塊 在類別中的 static 區塊, 會在程式使用到該類別之前執行。以上列程式為例, 第 5〜 7 行就是 static 初始區塊, 它會在程式中第一次使用到 Test 類別 (即第 23 行) 之前執行, 因此第 23 行顯示的結果就是執行過 static 初始區塊後的成員 y 的值100。

stat ic 初始區塊 由於 static 成員變數是由同一類別的所有物件所共享, 且不需先產生物件, 即可以透過類別名稱存取, 因此又稱為類別變數 (Class Variable);相對的, 非 static 的成員變數因為是每個物件各自擁有一份, 需建立物件後才能使用, 因此稱為實體變數 (Instance Variable)。

9 - 3 -4 static 方法 stat ic 除了可以使用在成員變數上以外, 也可以應用在方法上。一個標示有 static 存取控制的方法除了可以透過所屬類別的物件呼叫以外, 也和 static成員變數一樣, 可以在沒有產生任何物件的情況下透過類別名稱呼叫。例如:

static 方法

static 方法 由於 static 成員變數及方法不需產生物件即可使用的特性, 因此可以用來提供一組相關聯的工具方法或是常數值, 像是 Java 類別庫中的 Math 類別,就提供許多與數學相關的 static 運算方法 (像是算次方、取亂數、三角函數等等) 以及常數值 (例如圓周率)。

static 方法 請特別注意, 在 static 區塊或方法之中, 不能用到任何非 static 的成員變數以及方法, 也不能使用 this 保留字。這其實很容易理解, 因為非 static 的成員變數和方法是跟隨物件而生, 而 static 區塊或是方法可以在沒有產生任何物件之前使用, 此時因為沒有產生物件, 自然就不會配置有非 static 的成員變數, 而 this 也沒有物件可指。

解開main() 方法之謎 從這裡的解說就可以將 main() 方法的神秘面紗揭開了, 原來 main() 方法只是一個 public static 的方法, 而且不會傳回任何值。

9 - 3 - 5 final 存取控制 static 成員變數常常用來提供給同一類別的所有物件一份一致的資料, 供所有的物件共用。為達成此目的, 還需要有一種方法, 可以在設定好 static 成員變數的初值後就不能更改, 否則各別物件都隨意去更動 static 成員變數的值, 就不是一份一致的資料了。 為達到此目的, 可以搭配第 3 章介紹過的 final 字符, 限制特定的 static 成員變數在設定過初值之後就不允許更動, 例如:

final 存取控制

final 存取控制 第 2 行中宣告了 x 是 final, 所以第 9 行的設定動作在編譯時就會被視為錯誤。 要特別注意的是, 一旦宣告了成員變數為 final 後, 如果是 static 成員變數, 那麼就必須要在宣告同時或是在 static 初始區塊中設定初值;如果是非 static 成員變數, 就必須在宣告同時或是在建構方法中設定初值, 否則都會被視為是錯誤。

9 - 4 綜合演練 9 - 4 - 1 提供輔助工具的類別 9 - 4 - 2 善用多重定義

9 - 4 - 1 提供輔助工具的類別 在 Java 中, 通常會使用 static 方法提供輔助工具給其他類別使用。舉例來說, 我們可以提供一個負責找出最大值與最小值的類別, 以方便所有需要在陣列中尋找極值的場合。

提供輔助工具的類別

提供輔助工具的類別

提供輔助工具的類別 在這個程式中, Utility 類別的功能就是提供了 2 個方法, 個別可以在陣列中找出最小及最大值。只要遇到需要找尋極值的時機, 都可以直接使用這 2個方法, 完全不需要產生物件。這其實也是 Java 提供許多公用程式的作法, 我們會在第 17 章介紹 Java 標準類別庫。

9 - 4 - 2 善用多重定義 在定義類別時, 建議您為類別提供一個最富彈性的建構方法, 可以完全設定各個成員變數的值, 然後連同不需參數的建構方法在內, 都呼叫此建構方法來進行設定工作。舉例來說, 如果要撰寫一個代表矩形的類別, 就可以這樣做:

善用多重定義

善用多重定義

善用多重定義

善用多重定義 第 14 ~ 17 行就是最富彈性的建構方法, 其他的建構方法都呼叫它來完成建構的動作。透過這樣的設計方式, 在新增建構方法時, 就可以很方便的完成工作, 而不需要自行處理個別成員變數的設定動作。