初識java語言(四)- 類和物件

文章目錄
- 初識java語言(四)- 類和物件
- 一、類與物件的概念
- 1、類
- 2、物件
- 3、面向程序與面向物件的區別
- 二、類的成員
- 1、欄位
- 2、方法
- 3、static關鍵字
- 三、自定義類
- 1、從構造方法開始
- 2、用var宣告區域變數
- 3、顯式引數與隱式引數
- 4、認識參考
往期文章:
前言- IDEA如何配置?讓你敲代碼更輕松!
初識Java語言(一)- 基本資料型別及運算子
初識Java語言(二)- 方法以及遞回
初識Java語言(三)- 陣列
一、類與物件的概念
1、類
類(class)是構造物件的模板或藍圖,
我們可以將類(class)想象成一個模具,將物件想象成一個由模具形成的物品,如下:
![]()
-
封裝(encapsulation,有時稱為資料隱藏)是處理物件的一個重要概念,從形式上來看,封裝就是將資料和行為(方法)組合在一個包中,并對物件的使用者隱藏了具體的實作方式,實作封裝的關鍵在于,絕對不能讓類中的方法直接訪問其他類的實體欄位(成員變數),
-
通過擴展一個類來建立另外的一個類的程序稱為繼承,有關繼承的細節問題,我們后面再將,記住 : 在java中,所有的類源自一個“超類”,也就是Object,
2、物件
面向物件的程式是由物件組成的,每個物件包含對用戶公開的特定功能部分和隱藏的實作部分,對于一些規模較小的問題,將其分解為程序的開發方式比較理想(面向程序),面向物件更加適合解決規模比較大的問題,
要想使用面向物件程式設計,一定要清楚物件的三個特性:
- 物件的行為(behavior)----- 可以對物件完成哪些操作,或者是應用哪些方法?
- 物件的狀態(state)----- 當呼叫這些方法時,物件會如何回應?
- 物件的標識(identity)----- 如何區分具有相同 行為與狀態的不同物件?
3、面向程序與面向物件的區別
面向程序:
- 系統以程序方法為中心來組織
- 程序間相互發送資料
- 程序的執行動作與資料被明顯的分離
- 關注的焦點在于資料結構、演算法和執行步驟
- 程序通常難以被復用,
面向物件:
- 系統以物件為中心來組織
- 物件相互間發送訊息
- 相關的屬性和行為被統一到物件上
- 關注的焦點是物件及物件的職責
- 使得系統構建更容易,易維護,易擴展,易復用
- 解決問題的方式更接近人的
舉個例子: 假設你現在需要洗一堆衣服
? 面向程序: 拿盆 -> 加水 -> 放洗衣粉 -> 手洗 -> 再加水 -> 再洗一遍 -> …… -> 晾曬 -> 完成
? 面向物件: 只需要三個物件: 人 、 衣服 、 洗衣機; 我們將衣服放入洗衣機,放入洗衣服,啟動洗衣機即可,在這三個物件里分別寫一些方 法: 比如洗衣機 :自動加水,換水等等,

二、類的成員
1、欄位
物件中的資料稱為實體欄位(成員變數), 作為一個類的實體,特定物件都有一組特定的實體欄位值,這些值的集合就是這個物件當前狀態,無論何時,呼叫物件上的方法(函式),都有可能改變它的狀態,
//單鏈表結點
class Node {
public int val; //欄位或者 成員變數
public Node next;
}
有時候,我們需要設定成員變數的初始值,也可以這樣完成操作,(顯式設定初始值)
class Node {
public int val = 10;
public Node next = null;
}
2、方法
就是我上一篇文章講過的方法,是一樣的,用于描述物件的行為,Java中方法的概念以及遞回的討論
class Node {
public int val; //欄位
public Node next;
//方法
public void show() {
System.out.println(this.val);
}
}
public class Main {
public static void main(String[] args) {
Node node = new Node(10);
node.show(); //呼叫類中的方法
}
}
3、static關鍵字
- 修飾屬性
- 修飾方法
- 修飾代碼塊
- 修飾類(在后面的文章會具體講解)
修飾屬性: Java靜態屬性和類相關,和具體的實體無關,換句話說,同一個類的不同實體共用同一個靜態屬性,
class Test {
public int val;
public static int count;
}
public class Main {
public static void main(String[] args) {
Test demo1 = new Test();
demo1.count = 10; //為count進行初始化
System.out.println("呼叫demo1時 " + demo1.count);
demo1.count++;
System.out.println("============");
Test demo2 = new Test();
System.out.println("呼叫demo2時 " + demo2.count);
}
}
運行結果:
是不是感到很疑惑? 我們來畫一畫這個記憶體圖,就能理解其中的奧妙了!!!

上訴代碼記憶體圖:

由圖可知,我們雖然創建了兩個實體化物件(demo1和demo2),但是因為類中的實體欄位(count)被static修飾,所以它是在方法區開辟記憶體空間,且接下來由這個類所實體化的所有物件里面的count,都是指向方法區count所開辟的一塊空間,(說白了,就是count的空間被所有物件所共享,一個物件將count改了,其他物件輸出的count也是被改了的)
修飾方法: 如果在任何方法上應用static關鍵字,則這個方法稱為靜態方法,
- 靜態方法屬于類,而不屬于物件,
- 可以直接呼叫靜態方法,而無需創建類的實體,
- 靜態方法可以直接訪問靜態資料成員,并可以修改靜態資料成員的值,
class TestDemo {
public int val;
public static int count;
public static void change() {
//val = 100; //error,靜態方法不能訪問非靜態成員,原因看上面的三點
count = 100;
}
}
public class Main {
public static void main(String[] args) {
TestDemo.change;
System.out.println(TestDemo.count);
}
}
因為change方法是靜態的,所以不需要進行實體化,就可以直接通過類名進行呼叫,
注意事項1: 靜態方法和實體無關,而是和類相關,因此這導致了兩個情況:
- 靜態方法不能直接使用非靜態資料成員或呼叫非靜態方法,(因為非靜態資料成員和方法都是和實體相關的),
this和super兩個關鍵字不能在靜態背景關系中使用,(this是當前實體的參考,super是當前實體父類實體的參考,也是和當前實體相關的)- static不允許修飾區域變數,區域變數不是類中的變數,而是方法中的,
注意事項2:
- 實際上一個方法具體要不要帶static關鍵字,都是需要看情況的,
- main方法是被static修飾的!!!
修飾代碼塊:
java中初始化實體欄位的方法有三種:
- 在構造方法中設定值
- 在宣告中賦值
- 用初始化塊設定值
四種代碼塊:
1. 本地代碼塊(定義在方法里面的)
2. 實體代碼塊(構造代碼塊)
3. 靜態代碼塊
4. 同步代碼塊
在一個類的宣告中,可以包含任意多個代碼塊,只要構造這個類的物件,這些代碼塊就會執行,也就是是初始化塊里的資料,是為了構造物件時,做一些準備作業,例如:

初始化塊,放在前面或者后面,都沒關系,因為實體化時,首先會將這些初始化塊,從上至下的執行一次,然后才是執行構造方法的的陳述句,
如果類的靜態欄位需要很復雜的初始化代碼,那么就可以使用靜態的初始化塊,將代碼放在一個塊中,并標記關鍵字static,


看圖,上面的代碼,先是執行的靜態代碼塊->實體代碼塊->構造方法 ,且靜態代碼塊只會在第一次使用這個類的時候初始化這一次,看下圖:

代碼塊與實體欄位初始化的順序:

總結: 在類第一次加載的時候,將會進行靜態欄位的初始化,與實體欄位一樣,除非將靜態欄位顯式地設定成其他值,否則默認的初始值是0、false或者null,所有的靜態欄位初始化方法以及靜態初始化塊都將依照類宣告中出現的順序執行,
三、自定義類
1、從構造方法開始

在上(類的成員)文中,我們提到了類中實體欄位的初始化,說到了顯式初始化欄位值,除了這樣初始化,Java還提供了一個叫構造方法的功能,專門用于實體化物件時用,上圖所示,就是一些構造方法,
書寫格式: public 類名() {}
在構造ListNode類的物件時,會呼叫構造方法,從而將實體欄位初始化為所希望的初始狀態,下面三種初始化都可以:
ListNode node1 = new ListNode(); //無參構造
ListNode node2 = new ListNode(20); //一個引數的構造
ListNode node3 = new ListNode(20, null); //兩個引數的構造
構造方法與其他方法有一個重要的不同之處,構造方法總是結合new運算子來呼叫,不能對已經存在的物件呼叫構造方法來達到重新設定實體欄位的目的,
構造方法需要記住這幾個點:
- 構造方法與類同名
- 每個類都有一個以上的構造方法,(沒寫構造方法的,編譯器會默認自帶一個無參構造方法)
- 構造方法可以有0個、1個或多個引數
- 構造方法沒有回傳值
- 構造方法總是伴隨著new運算子一起呼叫
注:
我們上面說過,如果自己不寫構造方法,編譯器會默認自帶一個無參構造方法,但是:
如果我們自己寫了一個有參構造方法,沒寫無參構造方法, 此時如果我們實體化物件,呼叫無參構造方法,就會報錯,因為編譯器默認帶無參構造方法,是因為類中沒寫任何的構造方法,才會自帶無參構造,
要記住所有的Java物件都是在堆中構造的,
2、用var宣告區域變數
在Java10中,如果可以從變數的初始值推匯出它們的型別,那么就可以用var關鍵字宣告區域變數,而無需指定型別,例如,可以不用這樣宣告:
ListNode node = new ListNode();
只需寫一下代碼即可:
var node = new ListNode();
這樣就可以避免重復寫型別名ListNode,
從現在開始,倘若無須了解任何Java API就能從等號右邊明顯看出型別,在這種情況下,我們都將使用var表示法,不過,我們不會對數值型別使用var,如int、long、double等,使你不用當心0、0L、0.0之間的區別,
注意var關鍵字只能用于方法中的區域變數,引數和欄位的型別必須明確宣告,
3、顯式引數與隱式引數
我們先看一段代碼:

reDouble方法,有兩個引數,第一個引數salary稱為隱式引數,是出現在方法名前的TestDemo型別的物件,第二個引數是位于方法名后面括號中的數值,這是一個顯式引數,
可以看到,顯式引數顯式地列在方法宣告中,例如 int num, 隱式引數的宣告沒有出現在方法中,
在沒一個方法中,關鍵字this指示隱式引數,this代表 指向當前物件的參考,
注釋: 在C++中,通常在類的外面定義方法:
void TestDemo : : reDouble(int num) {} //C++
如果在類的內部定義方法,這個方法將自動成為行內方法,
class TestDemo
{
? int reDouble(int num){} //inline in C ++
}
但是在java中,所有的方法都必須在類的內部定義,但并不表示它們是行內方法,是否將某個方法設定為行內方法是java虛擬機的任務,編譯器及時監視那些簡短、經常呼叫而且沒有被覆寫的方法呼叫,并進行優化,
4、認識引用
-
參考一定只能在堆疊上嗎?
答:不一定,比如創建一個陣列,陣列的每個元素都是參考資料型別,此時陣列本身是在堆上開辟的空間,這些參考就存盤在堆上,
-
參考能指向參考嗎?
答:不能,參考只能指向物件,
-
一個參考能指向多個物件嗎?
答: 不可以,比如一個Person型別的參考變數 per ,再多次new新物件,且每次將回傳的地址賦值給per時,最終per還是只能指向最后一次賦值的物件的地址,
-
一個參考賦值null代表什么,
答:代表當前參考不指向任何物件,也不指向任何一塊記憶體空間,與C語言不一樣,
本期更新就到此結束啦,朋友們下期見!!!

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/292333.html
標籤:java
上一篇:JAVA并發基石——CAS

