Java 對象和類
類
類(class)是構造對象的模板或藍圖。
對象
對象表示一個可以明確標識的實體。每個對象都有自己獨特的標識、狀態和行為。
對象的狀態(特征或屬性,即實例變量),由該對象的數據域來表示。
對象的行為(對象執行的動作,即功能),由方法來定義。
構造方法
每個類都有構造方法。如果沒有顯式地為類定義構造方法,Java編譯器將會為該類提供一個默認構造方法。
在創建一個對象的時候,至少要調用一個構造方法。構造方法的名稱必須與類同名,一個類可以有多個構造方法。
下面是一個構造方法示例:
public class Puppy{
public Puppy(){ }
public Puppy(String name){ // 這個構造器僅有一個參數:name
}
}
創建對象
對象是根據類創建的。在Java中,使用關鍵字new來創建一個新的對象。創建對象需要以下三步:
- 聲明:聲明一個對象,包括對象名稱和對象類型。
- 實例化:使用關鍵字new來創建一個對象。
- 初始化:使用new創建對象時,會調用構造方法初始化對象。
下面是一個創建對象的例子:
public class Puppy{
public Puppy(String name){ //這個構造器僅有一個參name
System.out.println("小狗的名字是 : " + name );
}
public static void main(String[] args){ // 下面的語句將創建一個Puppy對象
Puppy myPuppy = new Puppy( "tommy" );
}
}
訪問實例變量和方法
通過已創建的對象來訪問成員變量和成員方法,如下所示:
/* 實例化對象 */
Object referenceVariable = new Constructor();
/* 訪問類中的變量 */
referenceVariable.variableName;
/* 訪問類中的方法 */
referenceVariable.methodName();
Java 修飾符
Java語言提供了很多修飾符,主要分為以下兩類:
- 訪問修飾符
- 非訪問修飾符
修飾符用來定義類、方法或者變量,通常放在語句的最前端。
訪問控制修飾符
Java中,可以使用訪問控制符來保護對類、變量、方法和構造方法的訪問。Java 支持 4 種不同的訪問權限。
- default (即缺省,什么也不寫): 在同一包內可見,不使用任何修飾符。使用對象:類、接口、變量、方法。
- private : 在同一類內可見。使用對象:變量、方法。 注意:不能修飾類(外部類)
- public : 對所有類可見。使用對象:類、接口、變量、方法
- protected : 對同一包內的類和所有子類可見。使用對象:變量、方法。注意:不能修飾類(外部類)。
我們可以通過以下表來說明訪問權限:
修飾符 | 當前類 | 同一包內 | 子孫類(同一包) | 子孫類(不同包) | 其他包 |
---|---|---|---|---|---|
public | Y | Y | Y | Y | Y |
protected | Y | Y | Y | Y/N | N |
default | Y | Y | Y | N | N |
private | Y | N | N | N | N |
非訪問修飾符
為了實現一些其他的功能,Java 也提供了許多非訪問修飾符。
- static 修飾符,用來修飾類方法和類變量。
- final 修飾符,用來修飾類、方法和變量,final 修飾的類不能夠被繼承,修飾的方法不能被繼承類重新定義,修飾的變量為常量,是不可修改的。
- abstract 修飾符,用來創建抽象類和抽象方法。
- synchronized 和 volatile 修飾符,主要用於線程的編程。
static 修飾符
靜態變量:
static 關鍵字用來聲明獨立於對象的靜態變量,無論一個類實例化多少對象,它的靜態變量只有一份拷貝。 靜態變量也被稱為類變量。局部變量不能被聲明為 static 變量。
靜態方法:
static 關鍵字用來聲明獨立於對象的靜態方法。靜態方法不能使用類的非靜態變量。靜態方法從參數列表得到數據,然后計算這些數據。
對類變量和方法的訪問可以直接使用 classname.variablename 和 classname.methodname 的方式訪問。
final 修飾符
final 變量:
final 表示"最后的、最終的"含義,變量一旦賦值后,不能被重新賦值。被 final 修飾的實例變量必須顯式指定初始值。
final 修飾符通常和 static 修飾符一起使用來創建類常量。
final 方法
類中的 final 方法可以被子類繼承,但是不能被子類修改。
聲明 final 方法的主要目的是防止該方法的內容被修改。
final 類
final 類不能被繼承,沒有類能夠繼承 final 類的任何特性。
Java 面向對象編程三大特性: 封裝 繼承 多態
Java 封裝
在面向對象程式設計方法中,封裝(英語:Encapsulation)是指一種將抽象性函式接口的實現細節部份包裝、隱藏起來的方法。
封裝可以被認為是一個保護屏障,防止該類的代碼和數據被外部類定義的代碼隨機訪問。
要訪問該類的代碼和數據,必須通過嚴格的接口控制。
封裝最主要的功能在於我們能修改自己的實現代碼,而不用修改那些調用我們代碼的程序片段。
適當的封裝可以讓程式碼更容易理解與維護,也加強了程式碼的安全性。
封裝的優點
- 良好的封裝能夠減少耦合。
- 類內部的結構可以自由修改。
- 可以對成員變量進行更精確的控制。
- 隱藏信息,實現細節。
Java 繼承
繼承的概念
繼承是java面向對象編程技術的一塊基石,因為它允許創建分等級層次的類。
繼承就是子類繼承父類的特征和行為,使得子類對象(實例)具有父類的實例域和方法,或子類從父類繼承方法,使得子類具有父類相同的行為。
Java 多態
多態是同一個行為具有多個不同表現形式或形態的能力。
多態就是同一個接口,使用不同的實例而執行不同操作
多態性是對象多種表現形式的體現。
多態存在的三個必要條件
- 繼承
- 重寫
- 父類引用指向子類對象
多態的實現方式
方式一:重寫:Java 重寫(Override)與重載(Overload)。
方式二:接口
方式三:抽象類和抽象方法
-
重載: 發生在同一個類中,方法名必須相同,參數類型不同、個數不同、順序不同,方法返回值和訪問修飾符可以不同,發生在編譯時。
-
重寫: 發生在父子類中,方法名、參數列表必須相同,返回值范圍小於等於父類,拋出的異常范圍小於等於父類,訪問修飾符范圍大於等於父類;如果父類方法訪問修飾符為 private 則子類就不能重寫該方法。
重寫與重載之間的區別
區別點 | 重載方法 | 重寫方法 |
---|---|---|
參數列表 | 必須修改 | 一定不能修改 |
返回類型 | 可以修改 | 一定不能修改 |
異常 | 可以修改 | 可以減少或刪除,一定不能拋出新的或者更廣的異常 |
訪問 | 可以修改 | 一定不能做更嚴格的限制(可以降低限制) |
接口和抽象類的區別
- 接口的方法默認是 public,所有方法在接口中不能有實現(Java 8 開始接口方法可以有默認實現),而抽象類可以有非抽象的方法。
- 接口中除了static、final變量,不能有其他變量,而抽象類中則不一定。
- 一個類可以實現多個接口,但只能實現一個抽象類。接口自己本身可以通過extends關鍵字擴展多個接口。
- 接口方法默認修飾符是public,抽象方法可以有public、protected和default這些修飾符(抽象方法就是為了被重寫所以不能使用private關鍵字修飾!)。
- 從設計層面來說,抽象是對類的抽象,是一種模板設計,而接口是對行為的抽象,是一種行為的規范。
== 與 equals(重要)
== : 它的作用是判斷兩個對象的地址是不是相等。即,判斷兩個對象是不是同一個對象(基本數據類型比較的是值,引用數據類型比較的是內存地址)。
equals() : 它的作用也是判斷兩個對象是否相等。但它一般有兩種使用情況:
- 情況1:類沒有覆蓋 equals() 方法。則通過 equals() 比較該類的兩個對象時,等價於通過“==”比較這兩個對象。
- 情況2:類覆蓋了 equals() 方法。一般,我們都覆蓋 equals() 方法來比較兩個對象的內容是否相等;若它們的內容相等,則返回 true (即,認為這兩個對象相等)。
舉個例子:
public class test1 {
public static void main(String[] args) {
String a = new String("ab"); // a 為一個引用
String b = new String("ab"); // b為另一個引用,對象的內容一樣
String aa = "ab"; // 放在常量池中
String bb = "ab"; // 從常量池中查找
if (aa == bb) // true
System.out.println("aa==bb");
if (a == b) // false,非同一對象
System.out.println("a==b");
if (a.equals(b)) // true
System.out.println("aEQb");
if (42 == 42.0) { // true
System.out.println("true");
}
}
}
說明:
- String 中的 equals 方法是被重寫過的,因為 object 的 equals 方法是比較的對象的內存地址,而 String 的 equals 方法比較的是對象的值。
- 當創建 String 類型的對象時,虛擬機會在常量池中查找有沒有已經存在的值和要創建的值相同的對象,如果有就把它賦給當前引用。如果沒有就在常量池中重新創建一個 String 對象。
hashCode 與 equals (重要)
hashCode()介紹
hashCode() 的作用是獲取哈希碼,也稱為散列碼;它實際上是返回一個int整數。這個哈希碼的作用是確定該對象在哈希表中的索引位置。
hashCode() 定義在JDK的Object.java中,這就意味着Java中的任何類都包含有hashCode() 函數。
散列表存儲的是鍵值對(key-value),它的特點是:能根據“鍵”快速的檢索出對應的“值”。這其中就利用到了散列碼!(可以快速找到所需要的對象)
為什么要有 hashCode
我們先以“HashSet 如何檢查重復”為例子來說明為什么要有 hashCode: 當你把對象加入 HashSet 時,HashSet 會先計算對象的 hashcode 值來判斷對象加入的位置,同時也會與其他已經加入的對象的 hashcode 值作比較,如果沒有相符的hashcode,HashSet會假設對象沒有重復出現。但是如果發現有相同 hashcode 值的對象,這時會調用 equals()方法來檢查 hashcode 相等的對象是否真的相同。如果兩者相同,HashSet 就不會讓其加入操作成功。如果不同的話,就會重新散列到其他位置。(摘自《Head first java》第二版)。這樣我們就大大減少了 equals 的次數,相應就大大提高了執行速度。
通過我們可以看出:hashCode() 的作用就是獲取哈希碼,也稱為散列碼;它實際上是返回一個int整數。這個哈希碼的作用是確定該對象在哈希表中的索引位置。hashCode() 在散列表中才有用,在其它情況下沒用。在散列表中hashCode() 的作用是獲取對象的散列碼,進而確定該對象在散列表中的位置。
hashCode()與equals()的相關規定
- 如果兩個對象相等,則hashcode一定也是相同的
- 兩個對象相等,對兩個對象分別調用equals方法都返回true
- 兩個對象有相同的hashcode值,它們也不一定是相等的
- 因此,equals 方法被覆蓋過,則 hashCode 方法也必須被覆蓋
- hashCode() 的默認行為是對堆上的對象產生獨特值。如果沒有重寫 hashCode(),則該 class 的兩個對象無論如何都不會相等(即使這兩個對象指向相同的數據)
Java 接口
接口(英文:Interface),在JAVA編程語言中是一個抽象類型,是抽象方法的集合,接口通常以interface來聲明。一個類通過繼承接口的方式,從而來繼承接口的抽象方法。
接口並不是類,編寫接口的方式和類很相似,但是它們屬於不同的概念。類描述對象的屬性和方法。接口則包含類要實現的方法。
除非實現接口的類是抽象類,否則該類要定義接口中的所有方法。
接口無法被實例化,但是可以被實現。一個實現接口的類,必須實現接口內所描述的所有方法,否則就必須聲明為抽象類。另外,在 Java 中,接口類型可用來聲明一個變量,他們可以成為一個空指針,或是被綁定在一個以此接口實現的對象。
接口特性
- 接口中每一個方法也是隱式抽象的,接口中的方法會被隱式的指定為 public abstract(只能是 public abstract,其他修飾符都會報錯)。
- 接口中可以含有變量,但是接口中的變量會被隱式的指定為 public static final 變量(並且只能是 public,用 private 修飾會報編譯錯誤)。
- 接口中的方法是不能在接口中實現的,只能由實現接口的類來實現接口中的方法。
抽象類和接口的區別
- 抽象類中的方法可以有方法體,就是能實現方法的具體功能,但是接口中的方法不行。
- 抽象類中的成員變量可以是各種類型的,而接口中的成員變量只能是 public static final 類型的。
- 接口中不能含有靜態代碼塊以及靜態方法(用 static 修飾的方法),而抽象類是可以有靜態代碼塊和靜態方法。
- 一個類只能繼承一個抽象類,而一個類卻可以實現多個接口。
反射定義
JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱為java語言的反射機制。
用途:
在日常的第三方應用開發過程中,經常會遇到某個類的某個成員變量、方法或是屬性是私有的或是只對系統應用開放,這時候就可以利用Java的反射機制通過反射來獲取所需的私有成員或是方法。當然,也不是所有的都適合反射,之前就遇到一個案例,通過反射得到的結果與預期不符。閱讀源碼發現,經過層層調用后在最終返回結果的地方對應用的權限進行了校驗,對於沒有權限的應用返回值是沒有意義的缺省值,否則返回實際值起到保護用戶的隱私目的。
反射機制的相關類
與Java反射相關的類如下:
類名 | 用途 |
---|---|
Class類 | 代表類的實體,在運行的Java應用程序中表示類和接口 |
Field類 | 代表類的成員變量(成員變量也稱為類的屬性) |
Method類 | 代表類的方法 |
Constructor類 | 代表類的構造方法 |