多态
重載與重寫
輸出是Object。
在這個例子中,重載的3個get方法,Object包含另外兩種參數類型。這種設計會帶來問題:String... 本可以是null,單String,String[];int數組也有可能在運作時當做Object類型。
接口與抽象類
接口定義了必須實作的規範,接口也可以保持對非可見類的引用。面向接口讓擴充變得很容易。
抽象類定義一些公有骨架,這些骨架應該自成内部邏輯。如果抽象類調用子類的實作,那麼很可能這個結果是不可控的。如果必須調用子類的實作,要麼子類的實作對父類的邏輯沒影響,要麼規定子類結果的規範。
接口标記和注解标記
java.io.Serializable與@resource
例子
分支與循環
do whlie whlile do
for三個條件 i++與++i
foreach數組或疊代器
異常分支
continue break
if bealpoint else
switch
享元模式
控制類執行個體的數量。
基本類型的設計模式,String的字元串常量池
靜态工廠方法
以Boolean為例,這三個靜态工廠方法都保證隻有兩個(有限個數)對象執行個體被使用。
有限個數不同意義對象使用的好處在于可以隻通過 == 位址比較對象意義是否相同,并且減少了對象的配置設定。 同樣的控制對象個數的如:單例和枚舉。在後面會提到。
合租
某較高價的電梯大廈合租4個月,500元/月(水電煤網均攤,網費30元/月),空調、衛生間、廚房齊全,室内均為IT行業人士,喜歡安靜。是以要求來租者最好為同行或者剛畢業的年輕人,愛幹淨、安靜。
有意者請電話聯系。
聯系人:趙先生
聯系方式:請閱讀代碼
建造者模式
試想這麼一個場景,使用者注冊。為了使用者良好的體驗,在注冊時應該避免過多的資訊錄入。除了使用者名、密碼等必要資訊外,不同的使用者可能有意願輸入的資訊并不一樣。當然可以采用不同的構造器重疊來解決這個問題,或者統一的構造器(必要參數),其它資訊使用Java Bean的set/get方法。 這裡User省略了set/get方法,但已經生成了,這裡沒有必要顯示。
import lombok.Data;
@Data
public class User {
protected String userName;
protected String password;
protected Boolean gender;
protected String email;
protected Long tel;
}
構造器重疊
靜态工廠方法
public final class Users {
private Users() {
}
public static User register(String userName, String password) {
User user = new User();
user.setUserName(userName);
user.setPassword(password);
return user;
}
public static User registerByMail(String userName, String password, String email) {
User user = new User();
user.setUserName(userName);
user.setPassword(password);
user.setEmail(email);
return user;
}
}
建造者
public class UserBuilder extends User {
public UserBuilder(Build build) {
this.userName = build.userName;
this.password = build.password;
this.gender = build.gender;
this.email = build.email;
this.tel = build.tel;
}
public static class Build {
private String userName;
private String password;
private Boolean gender;
private String email;
private Long tel;
public Build(String userName, String password) {
this.userName = userName;
this.password = password;
}
public Build gender(Boolean gender) {
this.gender = gender;
return this;
}
public Build email(String email) {
this.email = email;
return this;
}
public Build tel(Long tel) {
this.tel = tel;
return this;
}
public User build() {
return new UserBuilder(this);
}
}
public static void main(String[] args) {
User user = new Build("name","pwd").email("[email protected]").tel(18076767865L).build();
}
}
組合與繼承
繼承要慎用,其使用場合僅限于你确信使用該技術有效的情況。一個判斷方法是,問一問自己是否需要從新類向基類進行向上轉型。如果是必須的,則繼承是必要的。反之則應該好好考慮是否需要繼承。隻有當子類真正是超類的子類型時,才适合用繼承。換句話說,對于兩個類A和B,隻有當兩者之間确實存在 b is a 關系的時候,類B才應該繼承類A。
組合(has-a)關系可以顯式地獲得被包含類(繼承中稱為父類)的對象,而繼承(is-a)則是隐式地獲得父類的對象,被包含類和父類對應,而組合外部類和子類對應。如果你确定複用另外一個類的方法永遠不需要改變時,應該使用組合,因為組合隻是簡單地複用被包含類的接口,而繼承除了複用父類的接口外,它甚至還可以覆寫這些接口,修改父類接口的預設實作,這個特性是組合所不具有的。
從邏輯上看,組合最主要地展現的是一種整體和部分的思想,例如在電腦類是由記憶體類,CPU類,硬碟類等等組成的,而繼承則展現的是一種可以回溯的父子關系,子類也是父類的一個對象。
比如人的例子:不同穿着風格的人:運動服、正裝、休閑裝。這些裝飾隻是不同類型人的屬性,并不是人的子類。
矩陣轉置
問題描述
給出一個矩陣,求出矩陣的轉置,如:
給出左側矩陣,轉置為右側矩陣
、
将轉置前的a矩陣行列互換賦給轉置後的b矩陣。列印出矩陣
實作代碼
加載順序
在寫java代碼的時候,我們按照自然語言邏輯所寫的代碼就是java程式的執行順序。這讓我們不需要關注java是如何解析層級的加載順序。也可以忽略類屬子產品和對象子產品的差別。這是java設計的簡單之處。大巧若拙。
代碼塊的執行順序:
父類
靜态内部子類:(這裡隻是為了代碼友善)
執行Parent b = new Sub();
成員的加載順序
我們定義一個接口
ICallable.java
public interface ICallable {
String IBASE = "IBASE";
void call();
}
父類實作這個接口,子類重寫接口
public class Parent implements ICallable {
private String base = "base";
public Parent() {
call();
}
@Override
public void call() {
System.out.println(IBASE + "-" + base);
}
static class Sub extends Parent {
private String sub = "sub";
@Override
public void call() {
System.out.println(IBASE + "-" + sub);
}
}
public static void main(String[] args) {
Parent o = new Sub();
}
}
執行Parent b = new Sub();構造器this逃逸。結果是什麼?答案是:IBASE-null
代碼從右往左運作。調用子類構造方法,發現有繼承的父類。初始化父類:成員變量。調用父類構造方法。父類調用call方法。發現子類重寫。調用子類方法(這時子類還沒初始化)。子類初始化。
靜态與非靜态
接着上面的例子寫個測試:
通路權限運作(如:public)靜态内部類可以直接通路,這在某種場合下是需要的。
通路權限運作(如:public)内部類需要外部類的執行個體來調用。這是受限于static關鍵字。如果我們隻需要用内部類,不需要外部類的執行個體,用靜态内部類。一般非靜态内部類用法較多。
靜态方法(Static Method)與靜态成員變量一樣,屬于類本身,在類裝載的時候被裝載到記憶體(Memory),不自動進行銷毀,會一直存在于記憶體中,直到JVM關閉。
非靜态方法(Non-Static Method)又叫執行個體化方法,屬于執行個體對象,執行個體化後才會配置設定記憶體,必須通過類的執行個體來引用。不會常駐記憶體,當執行個體對象被JVM 回收之後,也跟着消失。
這個描述存在問題,後續修正?!靜态方法和靜态變量建立後始終使用同一塊記憶體,是連續的。非靜态方法會存在于記憶體的多個地方,是離散的。如果靜态方法在系統中定義太多,會占用大量的資源,最後造成記憶體溢出,是以靜态方法不能濫用。
異常
簡介
異常:是程式在運作時出現的不正常情況。異常的産生有三種情況:
- 主動抛出異常
- 同步異常,目前線程不正常執行
- 異步異常,虛拟機内部錯誤或異常線程導緻其它線程的異常
在 Java 中,所有的異常都有一個共同的祖先 Throwable(可抛出)。Throwable 指定代碼中可用異常傳播機制通過 Java 應用程式傳輸的任何問題的共性。
- Throwable: 有兩個重要的子類:Exception(異常)和 Error(錯誤),二者都是 Java 異常處理的重要子類,各自都包含大量子類。
- Error(錯誤):是程式無法處理的錯誤不需要捕獲,表示運作應用程式中較嚴重問題。大多數錯誤與代碼編寫者執行的操作無關,而表示代碼運作時 JVM(Java 虛拟機)出現的問題。例如,Java虛拟機運作錯誤(Virtual MachineError),當 JVM 不再有繼續執行操作所需的記憶體資源時,将出現 OutOfMemoryError。這些異常發生時,Java虛拟機(JVM)一般會選擇線程終止。
- Exception(異常):是程式本身可以處理的異常。
- Exception 類有一個重要的子類 RuntimeException。RuntimeException 類及其子類表示"JVM 常用操作"引發的錯誤。
注意:異常和錯誤的差別:異常能被程式本身可以處理,錯誤是無法處理。
通常,Java的異常(包括Exception和Error)分為可查的異常(checked exceptions)和不可查的異常(unchecked exceptions)。
- 可查異常(編譯器要求必須處置的異常):正确的程式在運作中,很容易出現的、情理可容的異常狀況。可查異常雖然是異常狀況,但在一定程度上它的發生是可以預計的,而且一旦發生這種異常狀況,就必須采取某種方式進行處理。 除了RuntimeException及其子類以外,其他的Exception類及其子類都屬于可查異常。這種異常的特點是Java編譯器會檢查它,也就是說,當程式中可能出現這類異常,要麼用try-catch語句捕獲它,要麼用throws子句聲明抛出它,否則編譯不會通過。
- 不可查異常(編譯器不要求強制處置的異常):包括運作時異常(RuntimeException與其子類)和錯誤(Error)。
Exception 這種異常分兩大類運作時異常和非運作時異常(編譯異常)。程式中應當盡可能去處理這些異常。
- 運作時異常:RuntimeException(運作時異常)是指程式員因設計或實作方式不當而導緻的問題 運作時異常的特點是Java編譯器不會檢查它,也就是說,當程式中可能出現這類異常,即使沒有用try-catch語句捕獲它,也沒有用throws子句聲明抛出它,也會編譯通過。
- 非運作時異常 (編譯異常):是RuntimeException以外的異常,類型上都屬于Exception類及其子類。從程式文法角度講是必須進行處理的異常,如果不處理,程式就不能編譯通過。如IOException、SQLException等以及使用者自定義的Exception異常,一般情況下不自定義檢查異常。
允許忽略不可查的RuntimeException和Error。
異常處理的5個關鍵字
- try
- 代碼塊,可能發生異常的邏輯代碼。必須與catch或finally一個或多個搭配使用
- catch
- 代碼塊,對應try代碼塊發生的異常處理,一個或者多個
- 異常捕獲規則先捕獲子類異常再捕獲父類異常,否則會被負載,如:聲明Exception,可以捕獲任何異常。為了更清楚處理異常,應該捕獲或者抛出具體的異常類型。
- finally
- 避免在finally塊中使用return,否則會報"finally block does not complete normally"提醒
- 代碼塊,發生或者不發生異常都會執行
- 代碼塊可以嵌套使用try、catch/finally
- throw
- 語句,對于處理不了的異常,抛出讓調用這處理
- throws
- 用于方法聲明中,表示抛出的一個或者多個異常
Throwable中的方法
- String getMessage()
- 擷取異常資訊,傳回字元串。
- String toString()
- 擷取異常類名和異常資訊,傳回字元串。
- void printStackTrace()
- 列印異常在堆棧中的跟蹤資訊;
- 擷取異常類名和異常資訊,以及異常出現在程式中的位置。
。
異常使用的建議
- 不要忽略異常。很多時候對于異常的處理采用妥協的方式,空的try-catch。對于當時不能處理的異常應該記錄異常資訊,保證能擷取異常發生時程式的運作狀态。
- 異常資訊保真。記錄的異常資訊應該是程式運作的堆棧,這樣能讓我們從技術角度來分析問題,而不是業務角度。比如:此異常方式是因為使用者輸入數字不合法,這種業務記錄資訊應該與異常資訊分别記錄,而不是替換。
- 不要避免抛出異常。抛出異常讓目前線程停止執行目前棧幀,并抛往上層的棧幀直至處理或者結束目前線程。往下的邏輯沒有必要執行。為了前端展示可能在底層要求不要抛出異常,但是這個異常應該有業務前端做處理,而不是不抛出異常。并且要詳細記錄,如非必要請勿将異常向上轉型。
- 記錄文檔。異常的出現由于輸入不合法,或者硬體執行異常。明确指明輸入參數的範圍,是否做了參數校驗。哪種情況抛出什麼樣的異常。
異常處理
在方法抛出異常之後,運作時系統将轉為尋找合适的異常處理器(exception handler)。異常處理器是目前方法注冊的異常處理集合,描述異常處理器的作用範圍、能處理的異常類型、處理異常代碼的所在位置。
沒有找到任何異常處理器,那麼恢複到該方法調用這的棧幀中重新抛出。在方法調用鍊裡重複進行前面的操作,直到方法調用鍊的頂端,還沒找到對應的異常處理器,執行線程退出。
異常程式執行是按照程式的執行分支執行。每種執行的情況是不同的分支。在分支進行中,finally代碼塊會跟在每種分支後面執行。也就是說,有多少種執行分支就會複制多少份finally代碼塊,保證立即執行finally代碼。各個執行分支是對立的,互不影響。