6、類與物件
面向物件簡介
面向過程:指的是針對某一個問題單獨提出解決方案和程式碼開發。
面向物件:以元件化的形式進行程式碼設計,優點是程式碼可重用。
面嚮物件語言的特徵:
(1)封裝性
:內部操作對外部不可見,保護內部結構安全性。
(2)繼承性
:在已有的程式結構上擴充新的功能。
(3)多型性
:相同的操作或函式、過程可作用於多種型別的物件上並獲得不同的結果
面向物件開發步驟:OOA(面向物件分析),OOD(面向物件設計),OOP(面向物件程式設計)。
類與物件
1 類與物件是面向物件中最基礎的組成單元。類是共性的集合,物件是某一個性的產物。
2 類用於描述物件的結構, 例如每個人的名字、年齡等一系列特徵。類除了包含特徵(屬性)
行為(方法)
。根據這個類產生的物件都具有相同的行為。3 物件所擁有的行為由類決定,無法執行超出類定義範疇的操作。
4 綜上所述,類是物件的模版,但類無法直接使用,必須通過例項物件來使用。物件是由類產生的。
類與物件的定義及使用
1.定義類使用class class_name {}
語句。類的組成:
(1)field
(屬性,成員,變數):一堆變數的集合;
(2)method
(方法,行為):由物件呼叫。
範例:定義類
package com.java.entity;
public class Book { // 類名首字母大寫
// 定義屬性
public String title;
public double price;
// 定義方法
public void getInfo(){
System.out.println("書名:" + title + ",價格:" + price);
}
}
2.要使用類,必須要有物件,物件定義的語法有如下兩種:
(1)宣告並例項化物件:class_name object_name = new class_name()
;
(2)分步完成:
第一步-宣告物件:class_name object_name = null
;
第二步-例項化物件:object_name =new class_name()
引用資料型別與基本資料型別最大區別是需要記憶體的開闢及使用,所以關鍵字
new
的主要功能是開闢記憶體空間。3.當一個物件例項化後,利用如下方式操作類:
(1)
object_name.field
:操作類中的屬性;(2)
object_name.method()
:呼叫類中的方法。範例:使用類
public class Demo {
public static void main(String[] args) {
Book book = new Book(); // 宣告並例項化物件
book.title = "Java開發"; // 定義屬性
book.price = 66.6;
book.getInfo(); // 呼叫方法
}
}
4.堆記憶體和棧記憶體的概念:
堆記憶體 | 棧記憶體 |
---|---|
儲存物件的屬性內容,使用關鍵字new開闢 | 棧記憶體:儲存堆記憶體的地址。可以理解為棧記憶體儲存物件的名字 |
範例:分步使用例項化物件
public class Demo {
public static void main(String[] args) {
Book book = null; // 宣告物件
book = new Book(); // 例項化物件
book.title = "Java開發";
book.price = 66.6;
book.getInfo();
}
}
記憶體分析:使用關鍵字new開闢了新的的堆記憶體,堆記憶體中儲存類定義的屬性,此時所有的屬性值都為預設值。
使用未例項化的物件,程式執行時會出現NullPointerException(空指向異常)
物件引用分析
1.在引用分析中,每次使用new
關鍵字便會開闢新的堆記憶體。假設聲明瞭兩個物件,且都用new分別進行了例項化,那麼兩個物件佔據的是不同的堆記憶體,因此不會互相影響。
範例:宣告兩個物件
public class Demo {
public static void main(String[] args) {
Book bkA = new Book();
Book bkB = new Book();
bkA.title = "Java開發";
bkA.price = 66.6;
bkA.getInfo();
bkB.title = "C++開發";
bkB.price = 22.6;
bkB.getInfo();
}
}
2. 範例:物件引用傳遞
public class Demo {
public static void main(String[] args) {
Book bkA = new Book();
Book bkB = null;
bkA.title = "Java開發";
bkA.price = 66.6;
bkA.getInfo(); // 66.6
bkB = bkA; // 引用傳遞
bkB.price = 90.5;
bkA.getInfo(); // 90.5
bkB.getInfo(); // 90.5
}
}
由於兩個物件指向同一塊堆記憶體,所以任意一個物件修改了堆記憶體的屬性值後,都會影響到其他物件的值。在引用中,一塊堆記憶體可以被多個棧記憶體指向,但一個棧記憶體只能儲存一個堆記憶體的地址。
3. 範例:引用傳遞
public class Demo {
public static void main(String[] args) {
Book bkA = new Book();
Book bkB = new Book();
bkA.title = "Java開發";
bkA.price = 66.6;
bkB.title = "C++開發";
bkB.price = 90.5;
bkB = bkA; // 引用傳遞
bkB.price = 100;
bkA.getInfo(); // 100
bkB.getInfo(); // 100
}
}
通過記憶體分析可知,在引用時,一塊沒有棧記憶體指向的堆記憶體將成為垃圾,最後被垃圾收集器(GC)回收,回收後,釋放其所佔空間。
封裝性
public class Demo {
public static void main(String[] args) {
Book bkA = new Book();
bkA.title = "Java開發";
bkA.price = -66.6;
bkA.getInfo();
}
}
上述程式碼沒有語法錯誤,但存在業務邏輯錯誤,因為書的價格不能為負數。造成該問題是因為物件可以在類的外部直接訪問屬性。
1.因此應將Book類的屬性設為對外不可見(只允許本類訪問),可以使用private
關鍵字定義屬性。
public class Book {
private String title;
private double price;
public void getInfo(){
System.out.println("書名:" + title + ",價格:" + price);
}
}
public class Demo {
public static void main(String[] args) {
Book bkA = new Book();
bkA.title = "Java開發"; // 報錯,無法訪問私有屬性
}
}
此時外部物件無法直接呼叫類中的屬性,但程式要正常執行,必須讓物件可以操作類中的屬性。因此在開發中,對於屬性有該要求:類中的屬性要使用private宣告,如果屬性需被外部使用,按照要求定義對應的setter()/getter().
2. 以Book類中的title屬性為例,定義setter()/getter():
setter() | getter() | |
---|---|---|
作用 | 設定屬性值 | 取得屬性值 |
語法 | public void setTitle(String t) | public void getTitle() |
是否含參 | 有參 | 無參 |
package com.java.entity;
public class Book {
private String title;
private double price;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public void getInfo(){
System.out.println("書名:" + title + ",價格:" + price);
}
}
如果要有價格不能為負數的功能,可以在setter()中新增:
public void setPrice(double price) {
if (price > 0.0){
this.price = price;
}
}
實際開發中,對於資料的驗證,應由其他輔助程式碼完成,setter()只是簡單地設定資料,getter()只用於返回資料。
構造方法與匿名物件
定義物件的語法:類名稱 物件名稱 = new 類名稱();
①類名稱:定義物件的型別;
②物件名稱:識別符號,要使用物件,需要有一個物件名;
③new:用於開闢堆記憶體空間,例項化物件;
④類名稱():一個方法名和類名稱一樣的方法,這就是構造方法。
通過上述分析,我們發現了構造方法的存在,但我們並沒有定義構造方法。之所以能使用這個構造方法,是因為Java預設在沒有自定義構造方法的情況下,程式編譯時自動在類中新增一個無參無返回值的構造方法。
1.構造方法的定義原則:方法名稱與類名稱相同,沒有返回值,也不能使用void關鍵字
class Book {
public Book() { // 系統自動生成的構造方法
}
}
2.構造方法在物件使用new
例項化時呼叫。
範例:證明構造方法被呼叫
public class Book {
public Book() {
System.out.println("構造方法被呼叫");
}
}
public class Demo {
public static void main(String[] args) {
Book book = null ; // 宣告物件
book = new Book(); // 例項化物件時呼叫構造方法
//結果:構造方法被呼叫
}
}
構造方法與普通方法的最大區別:構造方法只在例項化物件時呼叫一次。普通方法在物件例項化後可以多次呼叫。
3.範例:自定義構造方法
class Book {
private String title;
private double price;
// 自定義構造方法後,系統不再自動生成無參無返回值的構造方法
public Book(String t, double p) {
title = t;
price = p;
}
public void getInfo() {
System.out.println("書名:" + title + ",價格:" + price);
}
}
public class Demo {
public static void main(String[] args) {
Book book = new Book("Java開發", 66.6);
book.getInfo();
}
}
由上述程式碼可知構造方法的作用:在類物件例項化時設定屬性的初始值,即構造方法用於屬性初始化
。
4.構造方法也屬於方法,因此可以進行過載。
範例:構造方法過載
class Book {
public Book() {
System.out.println("系統自動生成的構造方法");
}
// 進行方法過載的構造方法
public Book(String t, double p) {
System.out.println("方法過載後的構造方法");
}
}
public class Demo {
public static void main(String[] args) {
Book bookA = new Book(); // 系統自動生成的構造方法
Book bookB = new Book("Java開發", 66.6); // 方法過載後的構造方法
}
}
過載方法時要求:按照引數個數,對方法進行升序或者降序排列。
5.在定義類時可為屬性設定預設值。但該預設值只有在物件例項化後才進行設定。而物件例項化是物件構造的最後一步。物件例項化過程的步驟:類的載入,記憶體的分配,預設值的設定,構造方法
。
class Book {
private String title = "Java開發"; // 設定預設值
private double price;
public Book() {
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
if (price > 0.0){
this.price = price;
}
}
public void getInfo() {
System.out.println("書名:" + title + ",價格:" + price);
}
}
public class Demo {
public static void main(String[] args) {
Book bkA = new Book();
bkA.getInfo(); // 書名:Java開發,價格:0.0
}
}
本程式中,只有在物件例項化後,才會將Java開發
設定給title
屬性。在沒有例項化前,title的值仍為其資料型別的預設值,即null
。真實的物件資訊都儲存在堆記憶體中。
6.匿名物件:沒有棧記憶體指向的物件,即沒有識別符號的物件
。
public class Demo {
public static void main(String[] args) {
new Book("Java開發",6.6).getInfo();
}
}
由於匿名物件沒有識別符號,也沒有其他物件對其引用,因此只能使用一次,之後該物件空間變為垃圾,等待回收。
何時使用匿名物件:但一個物件僅需要被使用一次時就使用匿名物件。當一個物件要被反覆呼叫時,就使用非匿名物件。
簡單Java類實踐
題目要求:定義一個僱員類,類中有僱員編號、姓名、職位、基本工資、佣金等屬性。
提示:這種類被稱為簡單java類
,因為這種類不包含過於複雜的程式邏輯。
對於簡單Java類而言,它的要求如下:
·類名必須有意義;
·類中所有屬性必須private封裝,封裝後的屬性必須提供setter和getter方法;
·類中可以有多個構造方法,但必須保留無參構造方法;
·類中不允許出現輸出語句,資訊輸出必須交給呼叫處。
·類中需要提供有一個取得物件完整資訊的方法,暫定為getInfo(),返回值型別為String型。
第一步:定義類
public class Emp {
private int eId; // 編號
private String eName; // 姓名
private String job; // 職位
private double sal; // 工資
private double comm; // 佣金
// 定義構造方法
public Emp() {
}
public Emp(int eId, String eName, String job, double sal, double comm) {
this.eId = eId;
this.eName = eName;
this.job = job;
this.sal = sal;
this.comm = comm;
}
// 定義setter和getter方法
public int geteId() {
return eId;
}
public void seteId(int eId) {
this.eId = eId;
}
public String geteName() {
return eName;
}
public void seteName(String eName) {
this.eName = eName;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public double getSal() {
return sal;
}
public void setSal(double sal) {
this.sal = sal;
}
public double getComm() {
return comm;
}
public void setComm(double comm) {
this.comm = comm;
}
// 定義普通方法
public String getInfo() {
return "編 號" + this.eId + "\n" +
"姓 名" + this.eName + "\n" +
"職 位" + this.job + "\n" +
"工 資" + this.sal + "\n" +
"傭 金" + this.comm;
}
}
第二步:測試
public class TEmp {
public static void main(String[] args) {
Emp e = new Emp(1, "Tom", "保安", 800.0, 10.0);
System.out.println(e.getInfo()); // 獲取全部資訊
System.out.println(e.geteId()); // 通過getter()獲取單一資訊
}
}
類中的setter()/getter()必須寫。setter()除了有設定屬性內容功能外,還有修改屬性值的功能。