4.1 面向對象基本概念
4.1.1 對象
對象:相關數據和方法的集合
4.1.2 封裝
封裝的含義是把類設計成一個黑箱,使用者只能看見類中定義的公共方法,而看不到方法實現的細節(jié),也不能直接對類的數據進行操作,迫使用戶通過接口去訪問數據。
封裝是一種信息隱蔽技術,封裝的定義為:
?。?)一個清楚的邊界;所有對象的內部軟件的范圍被限定在這個邊界內;
?。?)一個接口:這個接口描述該對象和其他對象之間的相互作用;
?。?)受保護的內部實現:這個實現給出了由軟件對象提供的功能的細節(jié),實現細節(jié)不能在定義這個對象的類的外面訪問。
4.1.3 消息
對象的行為由方法來實現,消息傳遞是對象之間進行交互的主要方式。
構成消息的3個要素是:接收消息的對象、接收消息后進行處理的方法和方法所需要的參數。
4.1.4 類
類是一種復雜的數據類型,是將不同類型的數據和這些數據相關的運算(即方法)封裝在一起的集合體。它是對所要處理的問題的抽象描述。
4.1.5 繼承
一個類的上一層稱為父類,而下一層稱為子類,一個類可以繼承其父類的變量和方法,且這種繼承具有傳遞性。
4.1.6 接口
接口可以看成是為兩個不相關的提供交流途徑的工具,是一個包含方法定義和常量值的集合。
Java不支持多繼承,子類只能有一個父類,有時需要使用其他類中的方法,但又無法直接繼承,這時可以使用接口技術。
接口不需要建立繼承關系,就可以使兩個不相關的類進行交互。
體現面向對象思想的程序:見P60
4.2 Java的類與對象
Java程序的所有數據類型都是用類來實現的,Java語言是建立在類這個邏輯結構之上的,所以Java是一種完全面對象的程序設計語言。類是Java的核心,Java程序都由類組成,一個程序至少包含一個類,也可以包含多個類。對象是類的實例,Java程序中可以使用標識符表示對象,并通過對象引用類中的變量和方法。
Java程序員的任務就是設計出類和對象來解決實際問題。創(chuàng)建類時既可以從父類繼承,也可以自行定義。
4.2.1 類的創(chuàng)建
格式: [修飾符] <class> <類名> [extends 父類] [implements接口]
{ 類體(成員變量和成員方法) }
修飾符:指明類在使用時所受到的限制,包括類訪問權限[public]和其他特性[abstract],[final]
例:public class C1 { …… }
public class C2 extends Applet implements ActionListener
{……}
1.class <類名>
告訴編譯器這是一個類
2.Public(公共的)
在沒有任何修飾符的默認情況下,類只能被同一個源程序文件或同一個包中的其他類使用,加上public后,該類可以被任何包中的類使用,稱為公共類。
注意:在同一個源程序文件中不能出現兩個及以上的public類,否則編譯器會告訴你將第二個public類放在另一個文件中。
3.abstract(抽象的)
abstract說明的類稱為抽象類,不能用它實例化一個對象,它只能被繼承,abstract方法是不含代碼的方法,需要以后的子類中重載實現,abstract類的子類必須實例化(實現)abstract方法,或將自己也聲明為abstract。這對于定義概念是有用的。
4.final(最終的)
final說明的類稱為最終類,最終類不能被繼承
Java中的String和Array類就是一個final類
注意:final和abstract不能同時修飾一個類(出錯),這樣的類沒有意義
無修飾符是默認方式,即不使用上述三種修飾符。因此它們的優(yōu)勢和限制都沒有作用,無修飾符的類可以被其他類訪問和繼承,但只有在相同程序包中的那些對象才可能使用這樣的類。
5.extends(繼承)父類名
extends告訴編譯器創(chuàng)建的類是從父類繼承下來的子類,父類必須是Java系統類或已經定義的類。
從父類繼承,可以提高代碼重用性,不必從頭開始設計程序。
6.implements(實現)接口名
implements告訴編譯器類實現的接口,接口必須有定義,一般為系統類。
接口是消息傳遞的通道,通過接口,消息才能傳遞到處理方法中進行處理。implements說明你的類可以實現的一個或多個接口,如果有多個接口,要用逗號分隔。
例:import java.awt.*;
import java.applet.*;
public class ch3 extends Applet{
public void paint(Graphics g) {
g.drawString("Hello Java!I love you^_^",25,25); } }
<html>
<head>
<title>Hello Java Applet測試</title>
</head>
<APPLET code=ch3.class width=200 height=40>
抱歉,你的瀏覽器不支持JAVA APPLET┅
</APPLET>
</html>
4.2.2 對象的創(chuàng)建
類的對象的模板,Java運行應該是用類創(chuàng)建實例化對象。
一旦任務完成,對象就會被垃圾收集器收回,完成它從創(chuàng)建、使用到清除。
見P66 例4.3
1.創(chuàng)建對象與構造方法
創(chuàng)建對象格式: 類名 對象名=new 類名([參數]) ;
說明:(1)運算符new為對象分配內存空間;
?。?)生成對象的最后一步是執(zhí)行構造方法;
(3)創(chuàng)建對象相當于定義一個變量,即分兩步進行。
創(chuàng)建對象分成兩步: 類名 對象名 ;
對象名=new 類名([參數]) ;
2.對象初始化的說明
?。?)系統如何對變量初始化
當用new創(chuàng)建了一個對象時,系統會為對象中的變量進行初始化。即不但為變量分配相應的存儲單元,還設置相應初值。系統為byte , short , int , long類型設置初值為0;float類型變量初值為0.0f;double類型變量初值為0.0;char字符型變量為’u\0000’;boolean邏輯變量初值為false;引用類型初值勤為null。
?。?)構造方法的作用與構成
new操作符為對象分配內存后將調用類的構造方法確定對象的初始狀態(tài),初始化所有變量。
構造方法功能:創(chuàng)建對象時,用給定的值,將對象初始化
構造方法特點:
(1)構造方法名與類名相同,且不指定類型說明;
?。?)可以重載,即可以定義多個參數個數不同的函數,系統可以根據參數的不同,自動調用正確的構造方法;
(3)程序中不能直接調用構造函數,在創(chuàng)建對象時系統自動;
?。?)可以不設計構造方法,若在初始化時還要執(zhí)行一些其他命令,就必須設計構造方法,因為Java規(guī)定命令語句不能出現在類體中,只能放在方法中。
重載:參數不同可以是數量不同,類型不同,或兩者都不同,但重載方法必須有相同的方法名和相同的返回類型。
例:class ABC{
public ABC( ) {……} // 無參數構造方法
public ABC(int a, int b ) {……} // 帶兩個參數構造方法
public ABC(String a ) {……} // 帶一個個參數構造方法
public int ABC(int a) {……} // 錯,構造方法不能有類型返回值
public void ABC( ) {……} // 錯,構造方法不能有類型返回值
}
例:public class SC{
public static void main(String args[]){
sc1 a=new sc1( ) ;
sc1 b=new sc1("練習") ;
System.out.println("程序結束!");} }
class sc1{
public sc1( ) {System.out.print("開始");}
public sc1(String z) {System.out.println(z);} }
運行結果:開始練習
程序結束
3.對象的使用
格式:<對象名>.<變量名>
<對象名>.<方法名([參數])>
例:SC a =new SC( ) //SC是已定義的類,a則是新建的SC對象
a.bian=23 // 將對象a的變量bian賦值23
a.Fan( ) // 調用對象a的方法Fan
例:class parents{
private String name[]=new String[5];
parents(String s[])
{for(int i=0;i<s.length;i++)
name[i]=s[i]; }
public void showname()
{for(int i=0;i<s.length;i++)
System.out.print(name[i]+” ”);
}}
class SC
{ public static void main(String args[])
{ String name[]={"Zhang","Wang","Li"};
parents p=parents(name);
p.showname()
}}
結果: 輸出 Zhang Wang Li
例:public class NewClass{
public static void main(String args[]){
G k=new G();
k.setK(8);
int y=k.getK();
System.out.println(“y=”+y); }
}}
class G{
private int k;
public void setK(int x){
k=x; }
public int getK(){ return k;}
}}
運行結果: y=8
4.清除對象
Java引入了新的內存管理機制,由Java虛擬機擔當垃圾收集器的工作。你可以任意創(chuàng)建對象,而不用擔心如何清除它們,垃圾收集器會自動清除它們。
如果要明確地清除一個對象,可以自行清除它。只需把一個空值賦給這個對象引用即可。如
SC a =new SC( )
……
a=null // 將對象a從內存中清除
5.析構方法(finalize)(補充)
析構方法(finalize)與構造方法相對應,當對象已經無用,需要清除時,編譯器將自動調用對象的finalize方法。析構方法一般是做一些清除工作,如關閉打開的文件等,但不能執(zhí)行用戶輸入操作或與其他對象進行交互。
如 class ABC{
……
protected|public void finalize( )
{ // do some cleanup code } }
4.3 成員變量與封裝
成員變量描述了類和對象的狀態(tài),有時也稱為屬性、數據或域
4.3.1 成員變量的聲明
成員變量的聲明必須放在類體中,通常是在成員方法之前。在方法中聲明的變量不是成員變量,而是方法的局部變量,二者是有區(qū)別的。
例:見P71
4.3.2 成員變量的修飾
聲明變量格式:[public][private][protected][package] //訪問控制修飾符
[static][final][transient][volatile]<數據類型><成員變量名稱>
1.訪問控制權限
表: 修飾符的作用范圍
修飾符
類
子類
包
所有類和包
Public
√
√
√
√
Private
√
Protected
√
√
√
package(默認)
√
√
?。?)public(公共)變量
由public修飾的變量稱為公共變量,可被任何包中的任何類訪問
?。?)private(私有)變量
由private修飾的變量稱為私有變量,只能被聲明它的類所使用
?。?)protected(受保護)變量
由protected修飾的變量稱為受保護變量,可被聲明它的類和派生的子類以及同一個包中的類訪問
?。?)package(包)變量
由package修飾的變量稱為包變量,沒有修飾符時,默認變量即是包變量,可被聲明它的類和同一個包中的其他類(包括派生子類)訪問
2.static(靜態(tài))變量
由static修飾的變量稱為靜態(tài)變量,靜態(tài)變量是類固有的,可以直接引用(方法:類名.靜態(tài)變量名),其他成員變量僅僅被聲明,只有等到生成實例對象后才存在,才可以被引用,靜態(tài)變量也稱為類變量,非靜態(tài)變量稱為實例變量。相應地靜態(tài)方法稱為類方法,非靜態(tài)方法稱為實例方法。
靜態(tài)變量可被此類創(chuàng)建的所有對象共享。
例:class Car {
String 車型;
static int 價格;
public Car(String 車型, int 價格)
{ this.車型=車型;
this.價格=價格; }
public void 介紹(String s)
{System.out.println(s+"\t"+車型+"\t"+"價格 "+價格);} }
public class SC{
public static void main(String args[]){
Car 奔馳=new Car("越野車",400000) ;
奔馳.介紹("奔馳");
Car 紅旗=new Car("轎車",200000) ;
紅旗.介紹("紅旗");
奔馳.介紹("奔馳"); }}
運行結果:奔馳 越野車 價格 400000
紅旗 轎車 價格 200000
奔馳 越野車 價格 200000
3.final(最終)變量
一旦成員變量被聲明為final,在程序運行中將不能被改變,即是一個常量。
如: final int I=5 ;
final double PI=3.1415926 ;
4.transient(過渡)變量(不做要求)
Java目前對transient修飾符沒有明確說明,它一般用在對象序列化(object serialization)上,說明成員變量不許被序列化。
5.volatile(易失)變量(不做要求)
volatile聲明的成員變量為易失變量,用來防止編譯器對該成員進行某種優(yōu)化。這是Java語言的高級特性,僅被少數程序員使用。
4.4 成員方法
對象的行為由類的方法實現,實際上,方法就是完成某種功能的程序塊。從功能上講,方法和函數十分類似。
4.4.1 成員方法的設計
例:見P77
4.4.2 成員方法的聲明與修飾
格式:[public][private][protected][package][static][final][abstract][native]
[synchronized] 返回值類型 方法名(參數表)[throws 異常類型]
說明:[public][private][protected][package][static] [final]與成員變量功能相同,都是定義方法訪問權限。
1.final(最終)方法
方法被聲明為最終方法后,將不能被子類覆蓋,即最終方法,能被子類繼承和使用但不能在子類中修改或重新定義它。這種修飾可以保護一些重要的方法不被修改。
在OOP中,子類可以把父類的方法重新定義,使之具有新功能但又和父類的方法同名、同參數、同返回值,這種情況稱為方法覆蓋(override)。
2.abstract(抽象)方法
抽象方法即無方法體的方法,抽象方法不能出現在非抽象類中。
為什么要使用抽象類和抽象方法呢?一個抽象類可以定義一個統一的編程接口,使其子類表現出共同的狀態(tài)和行為,但各自細節(jié)是不同的。子類共有的行為由抽象類中的抽象方法來約束,而子類行為的具體細節(jié)則通過抽象方法的覆蓋來實現。這種機制可增加編程的靈活性,也是OOP繼承樹的衍生基礎。
3.native(本地)方法(不做要求)
用其他語言編寫的方法在Java中稱為本地方法。
SDK提供了Java本地接口JNI(Java Native Interface),使得Java虛擬機能運行嵌入在Java程序中的其他語言,這些語言包括C/C++ ,FORTRAN ,匯編語言等。
4.synchronized(同步)方法
同步方法用于多線程編程。多線程在運行時,要能會同時存取一個數據。為避免數據的不一致性,應將方法聲明為同步方法,對數據進行加鎖。
5.throws(異常)類型
程序在運行時可能發(fā)生異?,F象。每一個異常對象都對應著一個異常類,如果暫時不希望方法處理某種異常,可將其拋出,使程序得以繼續(xù)運行。
如:protected void SC( ) throws IOException ; //輸入輸出異常拋出
6.返回值類型
Java要求一個方法必須聲明它的返回值類型,如果方法沒有返回值就用關鍵字void作為返回值類型。否則應使用基本數據類型或對象類型說明返回值類型。
如: public void SC( ) ; // 沒有返回值
int getx( ) ; // 返回值是int類型
private String abc( ) ; // 返回值是String類型
public static double du( ) ; // 返回值是double類型
protected Object oj( ) ; // 返回值是Object類型
7.方法名
方法名要以是任何有效的Java標識符,方法名可以和成員變量同名,也可以和成員方法同名。同一個類中的方法同名現象在OOP中稱為方法重載(overload)。
如: void SC( ) {……}
void SC(int a ) {……}
void SC(int a, int b) {……}
void SC(double a, int b) {……}
8.參數表
方法的調用者正是通過參數表將外部消息傳遞給方法的。在參數表中要聲明參數的類型,并用逗號分隔多個參數。
4.4.3 方法體
方法體包含在一對大括號中,即使方法體中沒有語句,一對大括號也是必不可少的。
4.4.4 消息傳遞
一個對象和外部交換信息主要靠方法的參數來傳遞,如果允許的話,外部對象也可以直接存取一個對象的成員變量。
在Java中調用方法時,如果傳遞的參數是基本數據類型,在方法中將不能改變參數的值,你只能使用它們。如果傳遞的是對象引用,你也不能在方法中修改這個引用,但可以調用對象的方法以及修改允許存取的成員變量。所以想改變參數的值,可采用傳遞對象的方法,間接修改參數的值。
例1:class SC{
public static void main(String[] args){
int m=10, n=20 ;
Power p=new Power( ) ;
p.doPower(m , n);
System.out.println("x="+m+",y="+n);
System.out.println("x="+p.x+",y="+p.y) ; } }
class Power{
int x , y ;
void doPower(int a , int b)
{ x=a*a ;
y=b*b ; } }
運行結果:x=10,y=20
x=100,y=400
例2:class SC{
float ptValue ; //成員變量
public static void main(String[] args){
int val=11 ; // 局部變量
SC pt=new SC( ) ;
System.out.println("val1="+val) ;
pt.changeInt(val) ;
System.out.println("val2="+val) ;
pt.ptValue=101f ; //float類型,加f
System.out.println("valValue1="+pt.ptValue) ;
pt.ChangeObj(pt) ;
System.out.println("valValue2="+pt.ptValue) ; }
public void changeInt(int value) //參數是簡單類型
{value=55 ; }
public void ChangeObj(SC ref)//參數是對象引用類型
{ref.ptValue=99f ; }
}
運行結果:val1=11
val2=11
valValue1=101.0
valValue2=99.0
作業(yè):P87-16 class Rectangle {
double width,height;
double zc(double b1,double b2)
{ return 2*(b1+b2); }
double mj(double b1,double b2)
{ return b1*b2; }}
public class SC{
public static void main(String args[]){
Rectangle r=new Rectangle();
r.width=10 ; r.height=20;
System.out.println("周長="+r.zc(r.width,r.height)) ;
System.out.println("面積="+r.mj(r.width,r.height)) ; }}
作業(yè):P87-17 public class SC{
public static void main(String args[]) {
int m[]={1,2,3,4,5};
Array A=new Array(m);
A.sum( );
System.out.println(A.getsum()); }}
class Array{
private int n[];
int sum=0;
Array(int i[]) { n=i; }
void sum( ){
for (int x=0;x<n.length;x++)
sum=sum+n[x] ; }
int getsum( ){
return sum; } }