對象與類
關於oop的概念,網上已有很多介紹。
對象的三個主要特性:
對象的行為(behavior)——可以對對象施加哪些操作,或可以對對象施加哪些方法。
對象的狀態(state)——當施加那些方法時,對象如何響應。
對象的標識(identity)——如何區分具有相同行為與狀態的不同對象。
類之間最常見的關係有依賴(“uses-a”)、彙總(“has-a”)、繼承(“is-a”)。依賴是一種最明顯、最常見的關係。
對象與物件變數,要想使用變數,就必須首先構造對象,並指定其初始狀態,然後對對象施加方法。使用構造器(constructor)構造新執行個體,這是一種特殊的方法,用來構造並初始化對象。構造器的名字應該與類名相同。例如,要想構造一個Date對象,需要在構造器前面加上new操作符,如new Date();這個運算式構造了一個新對象。這個對象初始化為當前日期和時間。如果需要的話,也可以將這個對象傳遞給一個方法:System.out.println(new Date());也可以將一個方法應用於剛剛構造的對象上。Date類中有一個toString方法。這個方法將返回日期的字串描述:String s = new Daate().toString();通常,希望構造的對象可以被多次使用,因此需要將對象放在一個變數中:Date birthday = new Date();
在對象與物件變數之間存在著一個重要的區別。例如:Date deadling;//這不是一個對象。定義了一個物件變數deadline,它可以引用Date類型的對象,但是它本身不是一個對象,也沒有引用對象,此時不能將任何Date方法應用於這個變數上。語句s = deadline.toString();將會編譯錯誤。必須首先初始設定變數deadline。當然可以用新構造的對象初始化這個變數:deadline = new Date();或者引用一個已存在的對象:deadline = birthday;這樣這兩個變數將引用同一個對象。一定要認識到,一個物件變數並沒有實際包含一個對象,而僅僅引用一個對象。在java中,任何物件變數的值都是對儲存在另外一處的一個對象的引用。new操作符的傳回值也是一個引用。
更改器方法和訪問器方法
通常的習慣是在訪問器方法名前加上首碼get,在更改器方法前面加上首碼set。
使用者自訂類
在java中,最簡單的類定義形式為:
Class ClassName
{
constructor1
constructor2
...
method1
method2
...
field1
field2
...
}
例子:
import java.util.*;
public class EmployeeTest
{
public static void main(String[] args)
{
//file the staff array with three Employee objects
Employee[] staff = new Employee[3];
staff[0] = new Employee("Carl Cracker",7500,1987,12,15);
staff[1] = new Employee("Harry Hacker",50000,1989,10,1);
staff[2] = new Employee("Tony Tester",40000,1990,3,15);
//raise everyone's salary by 5%
for(Employee e : staff)
e.raiseSalary(5);
//print out information about all Employee objects
for(Employee e : staff)
System.out.println("name=" + e.getName()
+ ",salary=" + e.getSalary()
+ ",hireDay=" + e.getHireDay());
}
}
class Employee
{
public Employee(String n,double s,int year,int month,int day)
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year,month - 1,day);
//GregorianCalendar uses 0 for January
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
private String name;
private double salary;
private Date hireDay;
}
在這個例子中,一個源檔案包含了兩個類。編譯的時候,可以使用萬用字元調用java編譯器:javac Employee*.java 或者 僅鍵入:javac EmployeeTest.java。編譯之後,將會建立兩個類檔案EmployeeTest.class和Employee.class。在一個源檔案中,只能有一個公有類,但是可以有任意數目個非公有類。
通過這個例子,我們發現,Employee類中有一個構造器和4個方法:
public Employee(String n,double s,int year,int month,int day);
public String getName();
public double getSalary();
public Date getHireDay();
public void raiseSalary(double byPercent);
三個執行個體域:
private String name;
private double salary;
private Date hireDay;
這個類的所有方法都標誌為public。意味著,任何類的任何方法都可以調用這個方法。域中採用關鍵字private確保只有Employee類自身的方法能夠訪問,其它類不能夠讀寫。
注意:
構造器與類同名;
每個類可以有一個以上的構造器;
構造器可以有0個、1個或者1個以上的參數;
構造器沒有傳回值;
構造器總是伴隨著new操作符一同使用。
隱式參數與顯式參數:在這個例子中的raiseSalary方法有兩個參數,第一個參數被稱為隱式參數,是出現在方法名前的Employee類型對象,第二個參數是方法名後面括弧中的數值,這是一個顯式參數。關鍵字this表示隱式參數。
封裝的優點:
有時候,需要擷取或設定執行個體域的值,應該提供三項內容:一個私人的資料域;一個公有的域訪問器方法;一個公有的域更改器方法。這個做雖複雜些,但是卻有明顯的好處:可以改變內部實現,除了該類的方法之外,不會影響其他代碼;更改器方法可以執行錯誤檢查,然而直接對域進行賦值將不會做這些處理。例如setSalary方法可能會檢查薪金是否小於0。
final修改符大都應用於基礎資料型別 (Elementary Data Type)域,或者不可變類型的域,如果類中的每個方法都不會改變其對象,這種類就是不可變的類。String類就是一個不可變的類。
靜態域與靜態方法:
如果將域定義為static,那麼每個類中只有一個這樣的域。
靜態方法是不能向對象實施操作的方法。例如,Math類的pow方法就是一個靜態方法。運算式Math.pow(x,a)計算冪xa。它在運算的時候,不使用任何Math對象。換句話說,沒有隱式的參數。可以認為靜態方法是沒有this參數的方法。因為靜態方法不能操作對象,所以不能在靜態方法中訪問執行個體域。但是,靜態方法可以訪問自身類中的靜態域。
在下面兩種情況下使用靜態方法:
當一個方法不需要訪問對象狀態,其所需要參數都是通過顯式參數提供的。
當一個方法只需要訪問類的靜態域。