1. 程式人生 > >【第11天】Java的單例模式、介面以及Object類常用的方法

【第11天】Java的單例模式、介面以及Object類常用的方法

1 單例模式

  • 什麼是設計模式?
    一套被反覆使用、多數人知曉的、經過分類的、程式碼設計經驗的總結。為了程式碼可重用性、讓程式碼更容易被他人理解、保證程式碼可靠性。世上本沒有設計模式,用的人多了,也便有了設計模式。

  • 什麼是單例模式?
    用來控制一個類有且只有一個物件的設計模式。

1.1 醉漢式

  • 實現的注意事項:
    • 私有化構造方法,以防止類體之外別人隨意建立物件,通過new Moon獲取到。
    • 建立一個私有的、靜態的、屬於本類型別的物件。
    • 提供一個公共的、靜態的、返回本類型別物件的方法。
public class Test{
	public static void main(String[] args){
		
		//驗證:
		Moon x = Moon.getMm();
		Moon y = Moon.getMm(
); Moon z = Moon.getMm(); System.out.println(x == y); System.out.println(y == z); } } //只有一個月亮 class Moon{ //私有化構造方法 //private:私有化構造方法,防止類體之外別人隨意new Moon物件獲取到 private Moon(){} //private:防止類體之外別人隨意的對靜態屬性賦值,若設定為“Moon.mm = null;”就尷尬了 //static:防止死迴圈* Moon-> mm -> mm -> mm...... private
static Moon mm = new Moon(); //getter //public:封裝 //static: 防止方法依賴於物件,只有類才可以定義, public static Moon getMm(){//月亮.getMm(); return mm; } }
  • 為何要“static Moon mm = new Moon();”靜態化mm物件?

    • 類似
    class Student{
    	int age = 20;
    }
    

    在這裡插入圖片描述

  • 這樣會造成如圖的死迴圈,所以一定要使用static讓每次呼叫都共享一個成員moon。

    class Moon{
    	Moon moon = new Moon();
    }
    

在這裡插入圖片描述

1.2 懶漢式

       因為還沒有學習到執行緒控制,所以這裡的懶漢式是不完善的,學習執行緒後再補全這裡的內容。

class Moon{

	private Moon(){}

	private static Moon only;

	public static Moon getOnly(){
		//當被呼叫時物件是否存在,如果不存在,new一個返回,如果存在則直接返回
		//如果這樣寫,呼叫時可能會出現執行緒併發問題,需要繼續完善
		if(only == null){
			only = new Moon();
			return only;
		}else{
			return only;
		}
	}
}

1.3 兩者之間的區別

醉漢式不管用不用,總是先例項化,可能會浪費記憶體,但是隻要呼叫就很快返回這個例項。效率較高,但記憶體空間佔用較大。
懶漢式注重記憶體管理,節省記憶體空間,如果不呼叫就不建立。效率較低,但記憶體空間佔用較小。

2 介面(interface)

       相當於工業生產中的規範。它是Java四大類*中的第二大型別,與abstract抽象類一樣,它不能建立物件

  • Java四大類(是編譯之後生成對應的.class檔案的那種,注意與資料型別作區分)

    • 類(class)
    • 介面(interface)
    • 列舉(enum)
    • 註解(@interface)
  • interface如何定義?

interface XXX{
	//屬性:
	//接口裡面的屬性預設加上三個修飾符:
	//public static final
	int x = 45;
	String y = "etoak";
	
	//方法:返回型別 方法簽名();
	//接口裡面的方法預設加上兩個修飾符:
	//public abstract
	void test();
	int show();
}

類實現介面之後,值不需要修改了,方法需要覆蓋(重寫)後再呼叫執行。

  • 例:
public class TestInterface{
	public static void main(String[] args){

		//一個USB裝置接入電腦
		USBKeyboard df = new USBKeyboard();
		Computer dell = new Computer();

		dell.open(df);
	}
}

/**
	USB裝置和電腦之間的關係:

	先制定所有USB裝置都需要尊重的規範/條約 -> 介面 interface

	找到一個型別的開發滿足這樣的規範 -> USB滑鼠  USB鍵盤
	找到一個型別的開發使用這樣的規範 -> 電腦

*/
//指定所有USB裝置的規範/條約
interface USB{

	//接口裡面定義屬性和方法

	//標定電壓
	//接口裡面的屬性預設加上三個修飾符:
	//public static final
	int v = 5;

	//連線電腦的功能
	//接口裡面的方法預設加上兩個修飾符:
	//public abstract
	void connect();
}
class USBKeyboard implements USB{

	@Override
	public void connect(){
		System.out.println("USB鍵盤連線電腦的方法");
	}
}

//類 implements 實現/遵循 介面規範
class USBMouse implements USB{

	//當我們拿著一個類去實現一個介面的時候 需要給出介面
	//裡面所有抽象方法的具體實現

	@Override
	public void connect(){
		System.out.println("USB滑鼠連線電腦的方法");
	}
}

class Computer{
	public void open(USB x){
		x.connect();
		System.out.println("電腦開啟連線");
	}
}
  • 面試題

1 類與介面兩兩之間的關係:
類與類之間:繼承關係(extends)
介面與介面:繼承關係(extends)
類與介面:實現關係(implements)
另外

Java中的類只允許單根繼承
Java中的介面允許多重實現
也就是說Java中的類在繼承一個類的同時可以實現多個介面

2 從哪個版本開始,方法覆蓋的時候允許加@Override註解?

類和類之間的方法覆蓋:JDK5.0及以上
類和介面之間的方法覆蓋:JDK6.0及以上

4:介面和抽象類之間的區別?★

名稱 兩者之間的關係 單根繼承/多重實現 型別 對屬性的定義 對方法的定義
介面 類implements(實現)介面 多重實現(一個類實現多個介面) interface 靜態的最終變數(public static final) 抽象方法(public abstract)
抽象類 類extends抽象類 單根繼承(只允許單繼承) class 普通屬性 抽象方法 + 普通方法

介面是對事物動作的抽象,而抽象類是對事物根源的抽象。 動作的抽象可以被不同事物的抽象共享,而事物的抽象屬性只能對一類事物使用,比如飛機和鳥都會飛,飛這個動作就可以抽象為一個介面的方法供飛機和鳥實現,只是實現的內容不同。但是飛機和鳥自身特有的屬性要定義在抽象類中。

關於介面和抽象類的區別理解,我找到了一篇能很全面並形象描述的文章,出自[email protected] 大神。

3 Object類常用的方法

       Object(物件)類,是所有引用資料型別的直接父類或者間接父類(父類的父類)。每個類直接或者間接繼承了Object類,也就是說每個類建立之後預設都有自己的如下幾個方法。定義返回型別為Object的方法,如果使用其他引用型別接收,需要強轉。

  • 存在一個許可權較小但功能較好的方法,如何將其許可權改大:
public class Test{
	public static void main(String[] args){

	}
}

class A{
	protected void test(){
		System.out.println("優秀的方法實現");
	}
}

class B extends A{

	//重寫將其訪問許可權改大
	@Override
	public void test(){
		//直接使用super呼叫父類的test()
		super.test();
	}
}

3.1 clone()

       使用Object類中clone()克隆出來的陣列、物件雖然內容與之前相同,但地址發生改變。

public class TestClone{//extends Object
	public static void main(String[] args){

		Sheep s1 = new Sheep("克隆羊母體");
		Sheep s2 = s1.clone();
		System.out.println(s1 == s2);
	}
}
class Sheep{//extends Object

	String name;
	public Sheep(String name){
		this.name = name;
	}
}

上面的程式碼報“錯誤: clone()可以在Object中訪問protected/不相容的型別”,是因為clone()沒有顯式重寫前,預設執行的是Object類中的clone()方法。

檢視Object類原始碼可得“protected native Object clone() throws CloneNotSupportedException”,protected意為可以在包(目錄)內或者包外的具有繼承關係的子類中使用,然而這裡是在TestClone類中訪問了Sheep類中的clone()方法,報錯。如果是在Sheep類呼叫Sheep類的clone()則是可以的。

結合前敘中改大許可權的辦法,我們可以在Sheep類中將clone()方法重寫,使其許可權增大,這樣就可以在TestClone類中訪問了。

改後:

public class TestClone{//extends Object
	public static void main(String[] args) throws Exception{

		Sheep s1 = new Sheep("克隆羊母體");
		//呼叫的.clone()方法需要丟擲異常,在方法上丟擲
		Sheep s2 = s1.clone();
		System.out.println(s1 == s2);//--->false
	}
}
//如果類能夠克隆,它需要滿足某個條約(實現Cloneable介面)
class Sheep implements Cloneable{//extends Object

	String name;
	public Sheep(String name){
		this.name = name;
	}
	
	//這裡的返回型別使用了協變返回型別:任何Object類的子類都所以可以作為返回型別
	//重寫Object類中的clone()方法,可以改大訪問許可權,在TestClone類中訪問到
	//因為是super.直接呼叫了父類的clone(),所以異常丟擲需與父類相同或更小
	@Override
	public Sheep clone() throws CloneNotSupportedException{

		//在子類呼叫父類的clone()幫助我們去克隆一個物件,與父類相同
		Object obj = super.clone();
		//返回子類Sheep型別的obj
		return (Sheep)obj;
	}
}

所以要使得類Sheep可以在TestClone類中呼叫其自身clone()方法,需要:

  • 重寫父類Object的clone()
  • 在Sheep類實現Cloneable介面
  • 在TestClone這個需要訪問clone()方法的類丟擲一個異常

3.2 finalize()

       物件的“遺言”方法,當一個物件要被gc執行緒回收時,會主動呼叫這個物件。

public class TestFinalize{
	public static void main(String[] arfgs){

		while(true){
			Teacher tea = new Teacher();
			//主動召喚gc執行緒進來回收
			//System.gc();
		}
	}
}

class Teacher{//extends Object

	public Teacher(){
		System.out.println("建立Teacher的物件");
	}

	//記憶體快要滿了時,GC出來工作回收這個物件之前,執行這個方法
	@Override
	public void finalize(){
		System.out.println("快要被GC回收了=======================");
	}
}
  • 如何主動召喚gc執行緒進來回收? 使用“System.gc();”

3.3 toString() (需重寫)

       指定一個物件列印顯示的內容,當要列印一個引用型別的物件時,底層都會使用這個物件呼叫toString()。

  • 在toString()沒有覆蓋(直接呼叫由Object類繼承來的方法)時列印:型別@XXX。如下例
public class TestToString1{
	public static void main(String[] args){

		String str = new String("張三");
		System.out.println(str);//--->張三
		System.out.println(str.toString());//--->張三
	
		Student stu = new Student("張三");
		System.out.println(stu);//--->[email protected]
		System.out.println(stu.toString());//--->[email protected]
	}
}

class Student{

	String name;

	public Student(String name){
		this.name = name;
	}
}

為何String的toString()、直接輸出可以獲得“張三”,而自定義的引用資料型別輸出的是地址?
       因為String類重寫了父類Object類中的toString()方法,這樣在輸出其物件引用時,直接輸出其toString()方法返回的內容。Student類中未重寫該方法。

       如果我們想要得到一條有用的資訊時,需要在我們定義的Object子類中重寫toString()。

public class TestToString2{
	public static void main(String[] args){

		String str = new String("張三");
		System.out.println(str);//--->張三
		System.out.println(str.toString());//--->張三
	
		Student stu = new Student("張三");
		System.out.println(stu);//--->張三
		System.out.println(stu.toString());//--->張三
	}
}

class Student{

	String name;

	public Student(String name){
		this.name = name;
	}

	//重寫了Object類中的toString()方法
	@Override
	public String toString(){
		return name;
	}
}

例題:

//使用toString()打印出有用的資訊
public class ExampleToString{
	public static void main(String[] args){

		Person x = new Person("張三",33,'男');
		System.out.println(x);

		Person y = new Person("李四",28,'女');
		System.out.println(y);
	}
}

class Person{
	String name;
	int age;
	char gender;

	public Person(String name,int age,char gender){
		this.name = name;
		this.age = age;
		this.gender = gender;
	}

	@Override
	public String toString(){
		return name + (gender == '男'? "先生":"女士" ) + "今年" + age + "歲";
	}
}

3.4 equals()(需重寫)

       制定一個型別的比較規則,當我們想要將記憶體裡面不同的兩個物件視為“邏輯”相等物件的時候,需要重寫equals方法,來定義我們自己的相等法則。
       在equals()沒有被覆蓋時,比較兩個物件地址。如下例

public class TestEquals1{
	public static void main(String[] args){

		String x = new String("OK");
		String y = new String("OK");

		System.out.println(x == y);//判斷地址是否相同--->false
		System.out.println(x.equals(y));//判斷內容是否相同--->true

		Student a = new Student("張三");
		Student b = new Student("張三");

		System.out.println(a == b);//判斷地址是否相同--->false
		System.out.println(a.equals(b));//判斷地址是否相同--->false
	}
}

class Student{//extends Object
	String name;
	public Student(String name){
		this.name = name;
	}
}

       Student類中重寫equals(),使Student的引用呼叫equals()時判斷名字是否相同。也可以傳入更多的值,判斷多個條件符合來返回equals()方法的結果(邏輯相等),此處只是舉一個例子。

public class TestEquals2{
	public static void main(String[] args){

		String x = new String("OK");
		String y = new String("OK");

		System.out.println(x == y);//判斷地址是否相同--->false
		System.out.println(x.equals(y));//判斷內容是否相同--->true

		Student a = new Student("張三");
		Student b = new Student("張三");

		System.out.println(a == b);//判斷地址是否相同--->false
		System.out.println(a.equals(b));//判斷內容是否相同--->true
	}
}

class Student{//extends Object
	String name;
	public Student(String name){
		this.name = name;
	}
	
	//重寫:只要兩個學生的姓名一樣就視為相等物件
	@Override
	public boolean equals(Object obj){//stu1.equals(stu2)

		//先找到要比較的兩個學生物件
		Student s1 = this;//當前呼叫該方法的物件
		Student s2 = (Student)obj;//要比較的第二個學生物件

		//分別通過物件得到他們的姓名
		String x = s1.name;
		String y = s2.name;
		
		//使用String的equals(),判斷學生的姓名內容是否相同
		return x.equals(y);
	}
}

但上面這樣重寫的方式並不是特別健壯和高效,需要注意新增一些條件判斷以備出現的不健壯情況,並提升效率。

public class TestEquals3{
	public static void main(String[] args){

		String x = new String("OK");
		String y = new String("OK");

		System.out.println(x == y);//判斷地址是否相同--->false
		System.out.println(x.equals(y));//判斷內容是否相同--->true

		Student a = new Student("張三");
		Student b = new Student("張三");

		System.out.println(a == b);//判斷地址是否相同--->false
		System.out.println(a.equals(b));//判斷內容是否相同--->true
	}
}

class Student{//extends Object
	String name;
	public Student(String name){
		this.name = name;
	}
	
	//重寫:只要兩個學生的姓名一樣就視為相等物件
	@Override
	public boolean equals(Object obj){

		//對引數obj排空判斷
		//引數是Object類,任何一個引用資料型別預設值都是null
		//為了保證程式碼健壯,此處不考慮呼叫物件的引用為空的情況,只考慮引數列表內的情況
		if(obj == null) return false;

		//由於引數是Object型別,導致所有的引用資料型別都可以傳進來 
		//但學生物件就應該和學生類的物件比較,instanceof的意思是判斷obj是否是Student類的物件
		//為了保證程式碼健壯
		if(!(obj instanceof Student)) return false;
		
		//為了保證程式更加高效
		if(this == obj) return true;
		
		//先找到要比較的兩個學生物件
		Student s1 = this;//當前呼叫該方法的物件
		Student s2 
            
           

相關推薦

11Java模式介面以及Object常用方法

1 單例模式 1.1 醉漢式 1.2 懶漢式 2 介面(interface) 3 Object類常用的方法 3.1 clone() 3.2 finalize()

22Java執行緒基礎併發集合

1 概述 2 生命週期(五狀態圖) 3 如何定義一個執行緒 4 控制執行緒執行的方法 1 概述        程式當中一條獨立的執行線索。Java中的主執行緒是一個J

17Java集合(四)---Sorted介面實現的TreeSet集合及值型別集合總結

1 TreeSet簡介 2 基本用法與特點 3 制定單值比較規則 3.1 自然排序(compareTo(Object obj)) 3.2 定製排序(定義比較器類) 3.2.1 普通類內定義

12Java集合(一)

1 什麼是集合?有哪些分類 1.1 JCF(Java Collections FrameWork) 2 ArrayList ★ 2.1 包裝類 2.2 基本用法與特點 2.3 刪除元素

10Java面向物件的高階特徵(修飾符的介紹)

1 訪問許可權 2 static 2.1 靜態成員 2.2 程式碼塊 2.3 載入順序 3 final 4 abstract 1 訪問許可權 修飾符:(√:可訪問

9Java中字串的處理

1 String類的初始化、與StringBuffer類和StringBuilder類三者的區別 1.1 String類的初始化兩種方式 1.2 String類、StringBuffer類和StringBuilder類三者的區別

8Java方法過載方法重寫(覆蓋)構造方法及引數傳值

1 方法過載(overload) 2 方法重寫(覆蓋)(override) 3 構造方法 4 引數傳值 1 方法過載(overload) 方法過載的作用? 同時滿足使用者的不同需求。 同一個方法,使用者可以傳入不同

18Java集合(五)---Map介面概述及Map介面實現的HashMapSortedMap介面實現的TreeMap

1 Map的通性 1.1 基本用法與特點 1.2 遍歷 2 HashMap集合的特性 3 TreeMap集合的特性 1 Map的通性     &nb

16Java集合(三)---Set介面實現的HashSet集合

1 HashSet簡介 2 基本用法與特點 3 HashSet的唯一性 4 增刪改時需要注意 1 HashSet簡介        作為Set介面的一個實現類,特

13Java集合(二)---手動實現ArrayList及其他List介面實現的集合

1 ArrayList(續) 1.1 擴容與縮容 1.2 手動實現ArrayList 2 Vector 3 LinkedList 4 Stack 1 ArrayList(續) 1.1

Java 模式Java 模式在多執行緒環境中可能存在的問題

在多執行緒環境下,使用延遲載入的方式實現單例模式,會出現錯誤。 例如,使用如下方式實現單例類: package study20170307; /** * Created by apple on 17/3/7. */ public class Sin

Velocity官方指南使用模式還是非模式

譯者:大胃  原文連結 從Velocity 1.2以後的版本,開發者對於Velocity引擎的使用有了兩種方式,單例模型(Singleton)以及多個獨立例項模型。Velocity的核心部分也採用了這兩種模型,目的是為了讓Velocity可以更容易與你的JAVA應用相整合。 單例模式(Sin

php利用模式設計資料庫連線Model

之前在《【php】利用php的建構函式與解構函式編寫Mysql資料庫查詢類》(點選開啟連結)寫過的Mysql資料庫查詢類還不夠完美,利用《【Java】單例模式》(點選開啟連結)介紹的思想可以將這個資料庫連結類搞成單例,不會因為多個使用者訪問網站就建立一個數據庫查詢例項,拖慢

2021Java的內部類異常處理機制

1 內部類 1.1 成員內部類 1.2 靜態內部類 1.3 區域性內部類 1.4 匿名內部類 2 異常處理機制 2.1 異常類的體系結構及各部分介紹 2.2

直播預告Java Spring Boot開發實戰系列課程11:訊息中介軟體 RabbitMQ 與api原始碼解析

內容概要:mq訊息中介軟體在高併發系統架構中扮演關鍵角色,阿里雙11高併發使用了mq技術。本次課程一起學習最新Java Spring Boot 2.0、RabbitMQ中介軟體的最新特性與實戰應用,同樣會分析核心api原始碼。主講人:徐雷(阿里雲棲特邀Java專家)直播時間:2019年1月8日 週二 今晚20

java設計模式初探0_模式

在java的幾十種設計模式中,可能單例模式算是最容易理解的吧!因為不論是目前的我自己,還是偶爾面試的別人,能稍微講清楚的,基本就是單例模式。 什麼叫單例模式?顧名思義,就是單一的例項,唯一的例項。也就是說對於某個java類來說,他的例項物件最多隻能建立一個。

Django 五篇ORM表增刪改查

contains 字典 exc 單表 pytho name屬性 作者 包括 刪除數據 一、添加表記錄 對於單表有兩種方式 # 添加數據的兩種方式 # 方式一:實例化對象就是一條表記錄 Frank_obj = models.Student(name ="海

C語言Windows程序開發—MessageBox函數介紹01

class ner windows.h can lpctstr 字符串 return napi ext (一)MessageBox函數的參數介紹: 1 int MessageBox ( 2 HWND hWnd, //彈出Messa

C語言Windows程序開發—TextOut函數介紹02

菜單 stock rec null 主函數 callback 介紹 關閉 windows.h (一)TextOut函數的參數介紹: 1 BOOL TextOut ( //如果函數調用成功,返回TRUE,否則,返回FALSE 2 H

Python設計模式02 模式

1. python實現經典的單例模式 python通過覆蓋__new__()方法來控制物件的建立。 if not hasattr(cls, ‘instance’):方法hasattr用於檢視物件cls是否具有屬性instance, 該屬性的作用是檢查該類是否已經生成了一個物件。