天天看點

Java基礎+多線程+jvm

Java基礎

面向對象與面向過程的差別

⾯向過程 :⾯向過程性能⽐⾯向對象⾼。 因為類調⽤時需要執行個體化,開銷⽐᫾⼤,⽐如消耗資源,是以當性能是最重要的考量因素的時候,⽐如單⽚機、嵌⼊式開發、Linux/Unix 等⼀般采⽤⾯向過程開發。但是,⾯向過程沒有⾯向對象易維護、易複⽤、易擴充。

⾯向對象 :⾯向對象易維護、易複⽤、易擴充。 因為⾯向對象有封裝、繼承、多态性的特性,是以可以設計出低耦合的系統,使系統更加靈活、更加易于維護。但是,⾯向對象性能⽐⾯向過程低。

Java 語⾔有哪些特點?

  1. 簡單易學;
  2. ⾯向對象(封裝,繼承,多态);
  3. 平台⽆關性( Java 虛拟機實作平台⽆關性);
  4. 可靠性;
  5. 安全性;
  6. ⽀持多線程( C++ 語⾔沒有内置的多線程機制,是以必須調⽤作業系統的多線程功能來進⾏多線程程式設計,⽽ Java 語⾔卻提供了多線程⽀持);
  7. ⽀持⽹絡程式設計并且很⽅便( Java 語⾔誕⽣本身就是為簡化⽹絡程式設計設計的,是以 Java 語⾔不僅⽀持⽹絡程式設計⽽且很⽅便);
  8. 編譯與解釋并存;

關于 JVM JDK 和 JRE 最詳細通俗的解答

JVM

Java 虛拟機(JVM)是運⾏ Java 位元組碼的虛拟機。JVM 有針對不同系統的特定實作(Windows,Linux,macOS),⽬的是使⽤相同的位元組碼,它們都會給出相同的結果。

什麼是位元組碼?采⽤位元組碼的好處是什麼?

在 Java 中,JVM 可以了解的代碼就叫做 位元組碼 (即擴充名為 .class 的檔案),它不⾯向任何特定的處理器,隻⾯向虛拟機。Java 語⾔通過位元組碼的⽅式,在⼀定程度上解決了傳統解釋型語⾔執⾏效率低的問題,同時⼜保留了解釋型語⾔可移植的特點。是以 Java 程式運⾏時⽐較⾼效,⽽且,由于位元組碼并不針對⼀種特定的機器,是以,Java 程式⽆須重新編譯便可在多種不同作業系統的計算機上運⾏。

Java 程式從源代碼到運⾏⼀般有下⾯ 3 步:

Java基礎+多線程+jvm

總結:

Java 虛拟機(JVM)是運⾏ Java 位元組碼的虛拟機。JVM 有針對不同系統的特定實作(Windows,Linux,macOS),⽬的是使⽤相同的位元組碼,它們都會給出相同的結果。位元組碼和不同系統的 JVM 實作是 Java 語⾔“⼀次編譯,随處可以運⾏”的關鍵所在。

JDK 和 JRE

JDK 是 Java Development Kit,它是功能⻬全的 Java SDK。它擁有 JRE 所擁有的⼀切,還有編譯器(javac)和⼯具(如 javadoc 和 jdb)。它能夠建立和編譯程式。

JRE 是 Java 運⾏時環境。它是運⾏已編譯 Java 程式所需的所有内容的集合,包括 Java 虛拟機(JVM),Java 類庫,java 指令和其他的⼀些基礎構件。但是,它不能⽤于建立新程式。

如果你隻是為了運⾏⼀下 Java 程式的話,那麼你隻需要安裝 JRE 就可以了。如果你需要進⾏⼀些Java 程式設計⽅⾯的⼯作,那麼你就需要安裝 JDK 了。但是,這不是絕對的。有時,即使您不打算在計算機上進⾏任何 Java 開發,仍然需要安裝 JDK。例如,如果要使⽤ JSP 部署 Web 應⽤程式,那麼從技術上講,您隻是在應⽤程式伺服器中運⾏ Java 程式。那你為什麼需要 JDK 呢?因為應⽤程式伺服器會将 JSP 轉換為 Java servlet,并且需要使⽤ JDK 來編譯 servlet。

字元型常量和字元串常量的差別?

  1. 形式上: 字元常量是單引号引起的⼀個字元; 字元串常量是雙引号引起的若⼲個字元
  2. 含義上: 字元常量相當于⼀個整型值( ASCII 值),可以參加表達式運算; 字元串常量代表⼀個位址值(該字元串在記憶體中存放位置)
  3. 占記憶體大小,字元常量隻占 2 個位元組; 字元串常量占若⼲個位元組 (注意: char 在 Java 中占兩個位元組)
    Java基礎+多線程+jvm

構造器 Constructor 是否可被 override?

Constructor 不能被 override(重寫),但是可以 overload(重載),是以你可以看到⼀個類中有多個構造函數的情況。

重載和重寫的差別:

重載就是同樣的⼀個⽅法能夠根據輸⼊資料的不同,做出不同的處理,重載就是同⼀個類中多個同名⽅法根據不同的傳參來執⾏不同的邏輯處理。

重寫就是當⼦類繼承⾃⽗類的相同⽅法,輸⼊資料⼀樣,但要做出有别于父類的響應時,你就要覆寫⽗類⽅法;重寫發⽣在運⾏期,是子類對⽗類的允許通路的方法的實作過程進⾏重新編寫。

  1. 傳回值類型、⽅法名、參數清單必須相同,抛出的異常範圍⼩于等于⽗類,通路修飾符範圍⼤于等于⽗類。
  2. 如果⽗類⽅法通路修飾符為 private/final/static 則⼦類就不能重寫該⽅法,但是被 static修飾的⽅法能夠被再次聲明。
  3. 構造⽅法⽆法被重寫

    綜上:重寫就是⼦類對⽗類⽅法的重新改造,外部樣子不能改變,内部邏輯可以改變 。

Java ⾯向對象程式設計三⼤特性: 封裝 繼承 多态

封裝

封裝把⼀個對象的屬性私有化,同時提供⼀些可以被外界通路的屬性的⽅法,如果屬性不想被外界通路,我們⼤可不必提供⽅法給外界通路。但是如果⼀個類沒有提供給外界通路的⽅法,那麼這個類也沒有什麼意義了。

繼承

繼承是使⽤已存在的類的定義作為基礎建⽴新類的技術,新類的定義可以增加新的資料或新的功能,也可以⽤⽗類的功能,但不能選擇性地繼承⽗類。通過使用繼承我們能夠⾮常⽅便地複⽤以前的代碼。

關于繼承如下 3 點請記住:

  1. ⼦類擁有⽗類對象所有的屬性和⽅法(包括私有屬性和私有⽅法),但是⽗類中的私有屬性和⽅

    法⼦類是⽆法通路,隻是擁有。

  2. ⼦類可以擁有⾃⼰屬性和⽅法,即⼦類可以對⽗類進⾏擴充。
  3. ⼦類可以⽤⾃⼰的⽅式實作⽗類的⽅法。(以後介紹)。

多态

所謂多态就是指程式中定義的引⽤變量所指向的具體類型和通過該引⽤變量發出的⽅法調⽤在程式設計時并不确定,⽽是在程式運⾏期間才确定,即⼀個引⽤變量到底會指向哪個類的執行個體對象,該引⽤變量發出的⽅法調⽤到底是哪個類中實作的⽅法,必須在由程式運⾏期間才能決定。在 Java 中有兩種形式可以實作多态:繼承(多個⼦類對同⼀⽅法的重寫)和接⼝(實作接⼝并覆寫接⼝中同⼀⽅法)。

String StringBuffer 和 StringBuilder 的差別是什麼? String 為什麼是不可變的?

可變性

簡單的來說:String 類中使⽤ final 關鍵字修飾字元數組來儲存字元串, private final char value[] ,是以 String 對象是不可變的。

StringBuilder 與 StringBuffer 都繼承⾃ AbstractStringBuilder 類,在

AbstractStringBuilder 中也是使⽤字元數組儲存字元串 char[]value 但是沒有⽤ final 關鍵字修飾,是以這兩種對象都是可變的。

abstract class AbstractStringBuilder implements Appendable, CharSequence 
{
 /**
 * The value is used for character storage.
 */
 char[] value;
 /**
 * The count is the number of characters used.
 */
 int count;
 AbstractStringBuilder(int capacity) {
 value = new char[capacity];
 }
           

線程安全性

String 中的對象是不可變的,也就可以了解為常量,線程安全。AbstractStringBuilder 是StringBuilder 與 StringBuffer 的公共⽗類,定義了⼀些字元串的基本操作,如 expandCapacity、append、insert、indexOf 等公共⽅法。StringBuffer 對⽅法加了同步鎖或者對調⽤的⽅法加了同步鎖,是以是線程安全的。StringBuilder 并沒有對⽅法進⾏加同步鎖,是以是⾮線程安全的。

⾃動裝箱與拆箱

裝箱:将基本類型⽤它們對應的引⽤類型包裝起來;

拆箱:将包裝類型轉換為基本資料類型;

接口和抽象類的差別是什麼?

  1. 接口的⽅法預設是 public,所有⽅法在接⼝中不能有實作(Java 8 開始接⼝⽅法可以有預設實

    現),⽽抽象類可以有⾮抽象的⽅法。

  2. 接口中除了 static、final 變量,不能有其他變量,⽽抽象類中則不⼀定。
  3. ⼀個類可以實作多個接⼝,但隻能實作⼀個抽象類。接⼝⾃⼰本身可以通過 extends 關鍵字擴充多個接⼝。
  4. 接⼝⽅法預設修飾符是 public,抽象⽅法可以有 public、protected 和 default 這些修飾符(抽象⽅法就是為了被重寫是以不能使⽤ private 關鍵字修飾!)。
  5. 從設計層⾯來說,抽象是對類的抽象,是⼀種模闆設計,⽽接⼝是對⾏為的抽象,是⼀種⾏為的規範。

成員變量與局部變量的差別有哪些?

  1. 從文法形式上看:成員變量是屬于類的,⽽局部變量是在⽅法中定義的變量或是⽅法的參數;成員變量可以被 public,private,static 等修飾符所修飾,⽽局部變量不能被通路控制修飾符及static 所修飾;但是,成員變量和局部變量都能被 final 所修飾。
  2. 從變量在記憶體中的存儲⽅式來看:如果成員變量是使⽤ static 修飾的,那麼這個成員變量是屬于類的,如果沒有使⽤ static 修飾,這個成員變量是屬于執行個體的。對象存于堆記憶體,如果局部變量類型為基本資料類型,那麼存儲在棧記憶體,如果為引⽤資料類型,那存放的是指向堆記憶體對象的引⽤或者是指向常量池中的位址。
  3. 從變量在記憶體中的⽣存時間上看:成員變量是對象的⼀部分,它随着對象的建立⽽存在,⽽局部變量随着⽅法的調⽤⽽⾃動消失。
  4. 成員變量如果沒有被賦初值:則會⾃動以類型的預設值⽽指派(⼀種情況例外:被 final 修飾的成員變量也必須顯式地指派),⽽局部變量則不會⾃動指派。

構造⽅法有哪些特性?

  1. 名字與類名相同。
  2. 沒有傳回值,但不能⽤ void 聲明構造函數。
  3. ⽣成類的對象時⾃動執⾏,⽆需調⽤。

靜态⽅法和執行個體⽅法有何不同

  1. 在外部調⽤靜态⽅法時,可以使⽤"類名.⽅法名"的⽅式,也可以使⽤"對象名.⽅法名"的⽅式。⽽執行個體⽅法隻有後⾯這種⽅式。也就是說,調⽤靜态⽅法可以⽆需建立對象。
  2. 靜态⽅法在通路本類的成員時,隻允許通路靜态成員(即靜态成員變量和靜态⽅法),⽽不允許通路執行個體成員變量和執行個體⽅法;執行個體⽅法則⽆此限制。

== 與 equals(重要)

== : 它的作⽤是判斷兩個對象的位址是不是相等。即,判斷兩個對象是不是同⼀個對象(基本資料類型**==**⽐較的是值,引⽤資料類型⽐較的是記憶體位址)。

equals() : 它的作⽤也是判斷兩個對象是否相等。但它⼀般有兩種使⽤情況:

情況 1:類沒有覆寫 equals() ⽅法。則通過 equals() ⽐᫾該類的兩個對象時,等價于通過“wx”⽐᫾這兩個對象。

情況 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 WX bb) // true
 System.out.println("aaWXbb");
 if (a WX b) // false,⾮同⼀對象
 System.out.println("aWXb");
 if (a.equals(b)) // true
 System.out.println("aEQb");
 if (42 WX 42.0) { // true
 System.out.println("true");
 }
 }
}
           

說明:

String 中的 equals ⽅法是被重寫過的,因為 object 的 equals ⽅法是⽐᫾的對象的記憶體位址,⽽ String 的 equals ⽅法⽐᫾的是對象的值。

當建立 String 類型的對象時,虛拟機會在常量池中查找有沒有已經存在的值和要建立的值相同的對象,如果有就把它賦給目前引⽤。如果沒有就在常量池中重新建立⼀個 String 對象。

hashCode 與 equals (重要)

⾯試官可能會問你:“你重寫過 hashcode 和 equals 麼,為什麼重寫 equals 時必須重寫 hashCode⽅法?”

hashCode()介紹

hashCode() 的作⽤是擷取哈希碼,也稱為散列碼;它實際上是傳回⼀個 int 整數。這個哈希碼的作⽤是确定該對象在哈希表中的索引位置。hashCode() 定義在 JDK 的 Object.java 中,這就意味着 Java中的任何類都包含有 hashCode() 函數。散清單存儲的是鍵值對(key-value),它的特點是:能根據“鍵”快速的檢索出對應的“值”。這其中就利⽤到了散列碼!(可以快速找到所需要的對象)

為什麼要有 hashCode

我們先以“HashSet 如何檢查重複”為例⼦來說明為什麼要有 hashCode: 當你把對象加⼊ HashSet時,HashSet 會先計算對象的 hashcode 值來判斷對象加⼊的位置,同時也會與該位置其他已經加⼊的對象的 hashcode 值作⽐᫾,如果沒有相符的 hashcode,HashSet 會假設對象沒有重複出現。但是如果發現有相同 hashcode 值的對象,這時會調⽤ equals() ⽅法來檢查 hashcode 相等的對象是否真的相同。如果兩者相同,HashSet 就不會讓其加⼊操作成功。如果不同的話,就會重新散列到其他位置。(摘⾃我的 Java 啟蒙書《Head first java》第⼆版)。這樣我們就⼤⼤減少了 equals 的次數,相應就⼤⼤提⾼了執⾏速度。通過我們可以看出: hashCode() 的作⽤就是擷取哈希碼,也稱為散列碼;它實際上是傳回⼀個 int整數。這個哈希碼的作⽤是确定該對象在哈希表中的索引位置。 hashCode() 在散清單中才有⽤,在其它情況下沒⽤。在散清單中 hashCode() 的作⽤是擷取對象的散列碼,進⽽确定該對象在散清單中的位置。

hashCode()與 equals()的相關規定:

  1. 如果兩個對象相等,則 hashcode ⼀定也是相同的
  2. 兩個對象相等,對兩個對象分别調⽤ equals ⽅法都傳回 true
  3. 兩個對象有相同的 hashcode 值,它們也不⼀定是相等的
  4. 是以,equals ⽅法被覆寫過,則 hashCode ⽅法也必須被覆寫
  5. hashCode() 的預設⾏為是對堆上的對象産⽣獨特值。如果沒有重寫hashCode(),則該 class的兩個對象⽆論如何都不會相等(即使這兩個對象指向相同的資料)

簡述線程、程式、程序的基本概念。以及他們之間關系是什麼?

線程與程序相似,但線程是⼀個⽐程序更⼩的執⾏機關。⼀個程序在其執⾏的過程中可以産⽣多個線程。與程序不同的是同類的多個線程共享同⼀塊記憶體空間和⼀組系統資源,是以系統在産⽣⼀個線程,或是在各個線程之間作切換⼯作時,負擔要⽐程序⼩得多,也正因為如此,線程也被稱為輕量級程序。

程式是含有指令和資料的⽂件,被存儲在磁盤或其他的資料儲存設備中,也就是說程式是靜态的代碼。

程序是程式的⼀次執⾏過程,是系統運⾏程式的基本機關,是以程序是動态的。系統運⾏⼀個程式即是⼀個程序從建立,運⾏到消亡的過程。簡單來說,⼀個程序就是⼀個執⾏中的程式,它在計算機中⼀個指令接着⼀個指令地執⾏着,同時,每個程序還占有某些系統資源如 CPU 時間,記憶體空間,⽂件,輸⼊輸出裝置的使⽤權等等。換句話說,當程式在執⾏時,将會被作業系統載⼊記憶體中。 線程是程序劃分成的更⼩的運⾏機關。線程和程序最⼤的不同在于基本上各程序是獨⽴的,⽽各線程則不⼀定,因為同⼀程序中的線程極有可能會互相影響。從另⼀⻆度來說,程序屬于作業系統的範疇,主要是同⼀段時間内,可以同時執⾏⼀個以上的程式,⽽線程則是在同⼀程式内⼏乎同時執⾏⼀個以上的程式段。

線程有哪些基本狀态?

Java基礎+多線程+jvm

線程生命周期(狀态)

當線程被建立并啟動以後,它既不是一啟動就進入了執行狀态,也不是一直處于執行狀态。線上程的生命周期中,它要經過建立(New)、就緒(Runnable)、運作(Running)、阻塞(Blocked)和死亡(Dead)5 種狀态。尤其是當線程啟動以後,它不可能一直"霸占"着 CPU 獨運作,是以 CPU 需要在多條線程之間切換,于是線程狀态也會多次在運作、阻塞之間切換

建立狀态(NEW)

當程式使用 new 關鍵字建立了一個線程之後,該線程就處于建立狀态,此時僅由 JVM 為其配置設定記憶體,并初始化其成員變量的值

就緒狀态(RUNNABLE):

當線程對象調用了 start()方法之後,該線程處于就緒狀态。Java 虛拟機會為其建立方法調用棧和程式計數器,等待排程運作。

運作狀态(RUNNING):

如果處于就緒狀态的線程獲得了 CPU,開始執行 run()方法的線程執行體,則該線程處于運作狀态。

阻塞狀态(BLOCKED):

阻塞狀态是指線程因為某種原因放棄了 cpu 使用權,也即讓出了 cpu timeslice,暫時停止運作。直到線程進入可運作(runnable)狀态,才有機會再次獲得 cpu timeslice 轉到運作(running)狀态。阻塞的情況分三種:

等待阻塞(o.wait->等待對列):

運作(running)的線程執行 o.wait()方法,JVM 會把該線程放入等待隊列(waitting queue)中。

同步阻塞(lock->鎖池)

運作(running)的線程在擷取對象的同步鎖時,若該同步鎖被别的線程占用,則 JVM 會把該線程放入鎖池(lock pool)中。

其他阻塞(sleep/join)

運作(running)的線程執行 Thread.sleep(long ms)或 t.join()方法,或者發出了 I/O 請求時,JVM 會把該線程置為阻塞狀态。當 sleep()狀态逾時、join()等待線程終止或者逾時、或者 I/O處理完畢時,線程重新轉入可運作(runnable)狀态。

線程死亡(DEAD)

線程會以下面三種方式結束,結束後就是死亡狀态。

正常結束

  1. run()或 call()方法執行完成,線程正常結束。

    異常結束

  2. 線程抛出一個未捕獲的 Exception 或 Error。

    調用 stop

  3. 直接調用該線程的 stop()方法來結束該線程—該方法通常容易導緻死鎖,不推薦使用。

關于 final 關鍵字的⼀些總結

final 關鍵字主要⽤在三個地⽅:變量、⽅法、類。

  1. 對于⼀個 final 變量,如果是基本資料類型的變量,則其數值⼀旦在初始化之後便不能更改;如果是引⽤類型的變量,則在對其初始化之後便不能再讓其指向另⼀個對象。
  2. 當⽤ final 修飾⼀個類時,表明這個類不能被繼承。final 類中的所有成員⽅法都會被隐式地指定為 final ⽅法。
  3. 使⽤ final ⽅法的原因有兩個。第⼀個原因是把⽅法鎖定,以防任何繼承類修改它的含義;第⼆個原因是效率。在早期的 Java 實作版本中,會将 final ⽅法轉為内嵌調⽤。但是如果⽅法過于龐⼤,可能看不到内嵌調⽤帶來的任何性能提升(現在的 Java 版本已經不需要使⽤ final⽅法進⾏這些優化了)。類中所有的 private ⽅法都隐式地指定為 final。

Java 中的異常處理

Java 異常類層次結構圖:

Java基礎+多線程+jvm

Throwable 是 Java 語言中所有錯誤或異常的超類。下一層分為 Error 和 Exception

Error

  1. Error 類是指 java 運作時系統的内部錯誤和資源耗盡錯誤。應用程式不會抛出該類對象。如果出現了這樣的錯誤,除了告知使用者,剩下的就是盡力使程式安全的終止。

Exception(RuntimeException、CheckedException)

3. Exception 又有兩個分支,一個是運作時異常 RuntimeException ,一個是

CheckedException。 RuntimeException 如 : NullPointerException 、 ClassCastException ;一個是檢查異常CheckedException,如 I/O 錯誤導緻的 IOException、SQLException。 RuntimeException 是那些可能在 Java 虛拟機正常運作期間抛出的異常的超類。 如果出現 RuntimeException,那麼一定是程式員的錯誤.

檢查異常 CheckedException:一般是外部錯誤,這種異常都發生在編譯階段,Java 編譯器會強制程式去捕獲此類異常,即會出現要求你把這段可能出現異常的程式進行 try catch,該類異常一般包括幾個方面:

4. 試圖在檔案尾部讀取資料

5. 試圖打開一個錯誤格式的 URL

6. 試圖根據給定的字元串查找 class 對象,而這個字元串表示的類并不存在

異常的處理方式

1、遇到問題不進行具體處理,而是繼續抛給調用者 (throw,throws)

抛出異常有三種形式,一是 throw,一個 throws,還有一種系統自動抛異常。

2、try catch 捕獲異常針對性處理方式

Throw 和 throws 的差別:

位置不同

  1. throws 用在函數上,後面跟的是異常類,可以跟多個;而 throw 用在函數内,後面跟的是異常對象。

    功能不同:

  2. throws 用來聲明異常,讓調用者隻知道該功能可能出現的問題,可以給出預先的處理方式;throw 抛出具體的問題對象,執行到 throw,功能就已經結束了,跳轉到調用者,并将具體的問題對象抛給調用者。也就是說 throw 語句獨立存在時,下面不要定義其他語句,因為執行不到。
  3. throws 表示出現異常的一種可能性,并不一定會發生這些異常;throw 則是抛出了異常,執行 throw 則一定抛出了某種異常對象。
  4. 兩者都是消極處理異常的方式,隻是抛出或者可能抛出異常,但是不會由函數去處理異常,真正的處理異常由函數的上層調用處理。

    (更詳情解釋請看:https://blog.csdn.net/hjfcgt123/article/details/53349275)

JAVA 序列化(建立可複用的 Java 對象)

儲存(持久化)對象及其狀态到記憶體或者磁盤

Java 平台允許我們在記憶體中建立可複用的 Java 對象,但一般情況下,隻有當 JVM 處于運作時,這些對象才可能存在,即,這些對象的生命周期不會比 JVM 的生命周期更長。但在現實應用中,就可能要求在JVM停止運作之後能夠儲存(持久化)指定的對象,并在将來重新讀取被儲存的對象。Java 對象序列化就能夠幫助我們實作該功能。

序列化對象以位元組數組保持-靜态成員不儲存

使用 Java 對象序列化,在儲存對象時,會把其狀态儲存為一組位元組,在未來,再将這些位元組組裝成對象。必須注意地是,對象序列化儲存的是對象的”狀态”,即它的成員變量。由此可知,對象序列化不會關注類中的靜态變量。

序列化使用者遠端對象傳輸

除了在持久化對象時會用到對象序列化之外,當使用 RMI(遠端方法調用),或在網絡中傳遞對象時,都會用到對象序列化。Java序列化API為處理對象序列化提供了一個标準機制,該API簡單易用。

Serializable 實作序列化

在 Java 中,隻要一個類實作了 java.io.Serializable 接口,那麼它就可以被序列化。 ObjectOutputStream 和 ObjectInputStream 對對象進行序列化及反序列化

通過 ObjectOutputStream 和 ObjectInputStream 對對象進行序列化及反序列化。

writeObject 和 readObject 自定義序列化政策

在類中增加 writeObject 和 readObject 方法可以實作自定義序列化政策。

序列化 ID 虛拟機是否允許反序列化,不僅取決于類路徑和功能代碼是否一緻,一個非常重要的一點是兩個類的序列化 ID 是否一緻(就是 private static final long serialVersionUID)

序列化并不儲存靜态變量

序列化子父類說明

要想将父類對象也序列化,就需要讓父類也實作 Serializable 接口。

(更詳盡解釋請看:https://blog.csdn.net/qq_19782019/article/details/80422143)

Transient 關鍵字阻止該變量被序列化到檔案中

  1. 在變量聲明前加上 Transient 關鍵字,可以阻止該變量被序列化到檔案中,在被反序列化後,transient 變量的值被設為初始值,如 int 型的是 0,對象型的是 null。
  2. 伺服器端給用戶端發送序列化對象資料,對象中有一些資料是敏感的,比如密碼字元串等,希望對該密碼字段在序列化時,進行加密,而用戶端如果擁有解密的密鑰,隻有在用戶端進行反序列化時,才可以對密碼進行讀取,這樣可以一定程度保證序列化對象的資料安全。

Java中I/O流的結構及分類:

Java基礎+多線程+jvm

BIO,NIO,AIO 有什麼差別?

BIO (Blocking I/O): 同步阻塞 I/O 模式,資料的讀取寫⼊必須阻塞在⼀個線程内等待其完成。在活動連接配接數不是特别⾼(⼩于單機 1000)的情況下,這種模型是⽐較不錯的,可以讓每⼀個連接配接專注于⾃⼰的 I/O 并且程式設計模型簡單,也不⽤過多考慮系統的過載、限流等問題。線程池本身就是⼀個天然的漏⽃,可以緩沖⼀些系統處理不了的連接配接或請求。但是,當⾯對⼗萬甚⾄百萬級連接配接的時候,傳統的 BIO 模型是⽆能為⼒的。是以,我們需要⼀種更⾼效的 I/O 處理

模型來應對更⾼的并發量。

NIO (Non-blocking/New I/O): NIO 是⼀種同步⾮阻塞的 I/O 模型,在 Java 1.4 中引⼊了NIO 架構,對應 java.nio 包,提供了 Channel , Selector,Buffer 等抽象。NIO 中的 N 可以了解為 Non-blocking,不單純是 New。它⽀持⾯向緩沖的,基于通道的 I/O 操作⽅法。 NIO提供了與傳統 BIO 模型中的 Socket 和 ServerSocket 相對應的 SocketChannel 和ServerSocketChannel 兩種不同的套接字通道實作,兩種通道都⽀持阻塞和⾮阻塞兩種模式。阻塞模式使⽤就像傳統中的⽀持⼀樣,⽐較簡單,但是性能和可靠性都不好;⾮阻塞模式正

好與之相反。對于低負載、低并發的應⽤程式,可以使⽤同步阻塞 I/O 來提升開發速率和更好的維護性;對于⾼負載、⾼并發的(⽹絡)應⽤,應使⽤ NIO 的⾮阻塞模式來開發

AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引⼊了 NIO 的改進版 NIO 2,它是異步⾮阻塞的 IO 模型。異步 IO 是基于事件和回調機制實作的,也就是應⽤操作之後會直接傳回,不會堵塞在那⾥,當背景處理完成,作業系統會通知相應的線程進⾏後續的操作。AIO 是異步 IO 的縮寫,雖然 NIO 在⽹絡操作中,提供了⾮阻塞的⽅法,但是 NIO 的 IO ⾏為還是同步的。對于 NIO 來說,我們的業務線程是在 IO 操作準備好時,得到通知,接着就由這個線程⾃⾏進⾏ IO 操作,IO 操作本身是同步的。查閱⽹上相關資料,我發現就⽬前來說 AIO 的應⽤還不是很⼴泛,Netty 之前也嘗試使⽤過 AIO,不過⼜放棄了。

深拷⻉ vs 淺拷⻉

  1. 淺拷⻉:對基本資料類型進⾏值傳遞,對引⽤資料類型進⾏引⽤傳遞般的拷⻉,此為淺拷⻉。
  2. 深拷⻉:對基本資料類型進⾏值傳遞,對引⽤資料類型,建立⼀個新的對象,并複制其内容,此為深拷⻉。
    Java基礎+多線程+jvm