2019-12-18

八種基本數(shù)據(jù)類型的大小,以及他們的封裝類

八種基本數(shù)據(jù)類型:int、short、float、double、long、boolean、byte、char。

封裝類分別是:Integer、Short、Float、Double、Long、Boolean、Byte、Character。

引用數(shù)據(jù)類型

引用數(shù)據(jù)類型是由類的編輯器定義的,他們是用于訪問對象的。這些變量被定義為不可更改的特定類型。

例如:Employee, Puppy 等等

類對象和數(shù)組變量就是這種引用數(shù)據(jù)類型。

任何引用數(shù)據(jù)類型的默認值都為空。

一個引用數(shù)據(jù)類型可以被用于任何聲明類型和兼容類型的對象。

Switch能否用string做參數(shù)

jdk7之前 switch 只能支持 byte、short、char、int 這幾個基本數(shù)據(jù)類型和其對應的封裝類型。

switch后面的括號里面只能放int類型的值,但由于byte,short,char類型,它們會?自動?轉(zhuǎn)換為int類型(精精度小的向大的轉(zhuǎn)化),所以它們也支持。

jdk1.7后 整形,枚舉類型,字符串都可以。

原理

switch (expression)??// 括號里是一個表達式,結(jié)果是個整數(shù){

??case constant1:???// case 后面的標號,也是個整數(shù)

?????group of statements 1;

?????break;

??case constant2:

?????group of statements 2;

?????break;

??...

??default:

?????default group of statements

}

jdk1.7后,整形,枚舉類,字符串都可以。

public class TestString {

????static String string = "123";

????public static void main(String[] args) {

????????switch (string) {

????????case "123":

????????????System.out.println("123");

????????????break;

????????case "abc":

????????????System.out.println("abc");

????????????break;

????????default:

????????????System.out.println("defauls");

????????????break;

????????}

????}

}

為什么jdk1.7后又可以用string類型作為switch參數(shù)呢?

其實,jdk1.7并沒有新的指令來處理switch string,而是通過調(diào)用switch中string.hashCode,將string轉(zhuǎn)換為int從而進行判斷。

equals與==的區(qū)別

使用==比較原生類型如:boolean、int、char等等,使用equals()比較對象。

1、==是判斷兩個變量或?qū)嵗遣皇侵赶蛲粋€內(nèi)存空間。 equals是判斷兩個變量或?qū)嵗赶虻膬?nèi)存空間的值是不是相同。

2、==是指對內(nèi)存地址進行比較。 equals()是對字符串的內(nèi)容進行比較。

3、==指引用是否相同。 equals()指的是值是否相同。

public static void main(String[] args) {

?????????String a = new String("ab"); // a 為一個引用

????????String b = new String("ab"); // b為另一個引用,對象的內(nèi)容一樣

????????String aa = "ab"; // 放在常量池中

????????String bb = "ab"; // 從常量池中查找

????????System.out.println(aa == bb); // true

????????System.out.println(a == b); // false,非同一對象

????????System.out.println(a.equals(b)); // true

????????System.out.println(42 == 42.0);??// true

????}

public static void main(String[] args) {

????Object obj1 = new Object();

????Object obj2 = new Object();

????System.out.println(obj1.equals(obj2));//false

????System.out.println(obj1==obj2);//false

????obj1=obj2;

????System.out.println(obj1==obj2);//true

????System.out.println(obj2==obj1);//true

}

自動裝箱,常量池

自動裝箱 在jdk?1.5之前,如果你想要定義一個value為100的Integer對象,則需要如下定義:

Integer i = new Integer(100);

int intNum1 = 100; //普通變量

Integer intNum2 = intNum1; //自動裝箱

int intNum3 = intNum2; //自動拆箱

Integer intNum4 = 100; //自動裝箱

上面的代碼中,intNum2為一個Integer類型的實例,intNum1為Java中的基礎數(shù)據(jù)類型,將intNum1賦值給intNum2便是自動裝箱;而將intNum2賦值給intNum3則是自動拆箱。

八種基本數(shù)據(jù)類型: boolean byte char shrot int long float double ,所生成的變量相當于常量。

基本類型包裝類:Boolean Byte Character Short Integer Long Float Double。

自動拆箱和自動裝箱定義:

自動裝箱是將一個java定義的基本數(shù)據(jù)類型賦值給相應封裝類的變量。 拆箱與裝箱是相反的操作,自動拆箱則是將一個封裝類的變量賦值給相應基本數(shù)據(jù)類型的變量。

Object有哪些公用方法

Object是所有類的父類,任何類都默認繼承Object

clone保護方法,實現(xiàn)對象的淺復制,只有實現(xiàn)了Cloneable接口才可以調(diào)用該方法,否則拋出CloneNotSupportedException異常。

equals在Object中與==是一樣的,子類一般需要重寫該方法。

hashCode該方法用于哈希查找,重寫了equals方法一般都要重寫hashCode方法。這個方法在一些具有哈希功能的Collection中用到。

getClassfinal方法,獲得運行時類型

wait使當前線程等待該對象的鎖,當前線程必須是該對象的擁有者,也就是具有該對象的鎖。 wait() 方法一直等待,直到獲得鎖或者被中斷。 wait(long timeout) 設定一個超時間隔,如果在規(guī)定時間內(nèi)沒有獲得鎖就返回。

調(diào)用該方法后當前線程進入睡眠狀態(tài),直到以下事件發(fā)生

1、其他線程調(diào)用了該對象的notify方法。 2、其他線程調(diào)用了該對象的notifyAll方法。 3、其他線程調(diào)用了interrupt中斷該線程。 4、時間間隔到了。 5、此時該線程就可以被調(diào)度了,如果是被中斷的話就拋出一個InterruptedException異常。

notify 喚醒在該對象上等待的某個線程。

notifyAll 喚醒在該對象上等待的所有線程。

toString 轉(zhuǎn)換成字符串,一般子類都有重寫,否則打印句柄。

Java的四種引用,強弱軟虛,用到的場景

從JDK1.2版本開始,把對象的引用分為四種級別,從而使程序能更加靈活的控制對象的生命周期。這四種級別由高到低依次為:強引用、軟引用、弱引用和虛引用。

1、強引用

最普遍的一種引用方式,如String s = "abc",變量s就是字符串“abc”的強引用,只要強引用存在,則垃圾回收器就不會回收這個對象。

2、軟引用(SoftReference)

用于描述還有用但非必須的對象,如果內(nèi)存足夠,不回收,如果內(nèi)存不足,則回收。一般用于實現(xiàn)內(nèi)存敏感的高速緩存,軟引用可以和引用隊列ReferenceQueue聯(lián)合使用,如果軟引用的對象被垃圾回收,JVM就會把這個軟引用加入到與之關聯(lián)的引用隊列中。

3、弱引用(WeakReference)

弱引用和軟引用大致相同,弱引用與軟引用的區(qū)別在于:只具有弱引用的對象擁有更短暫的生命周期。在垃圾回收器線程掃描它所管轄的內(nèi)存區(qū)域的過程中,一旦發(fā)現(xiàn)了只具有弱引用的對象,不管當前內(nèi)存空間足夠與否,都會回收它的內(nèi)存。

4、虛引用(PhantomReference)

就是形同虛設,與其他幾種引用都不同,虛引用并不會決定對象的生命周期。如果一個對象僅持有虛引用,那么它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。 虛引用主要用來跟蹤對象被垃圾回收器回收的活動。

虛引用與軟引用和弱引用的一個區(qū)別在于:

虛引用必須和引用隊列 (ReferenceQueue)聯(lián)合使用。當垃圾回收器準備回收一個對象時,如果發(fā)現(xiàn)它還有虛引,就會在回收對象的內(nèi)存之前,把這個虛引用加入到與之關聯(lián)的引用隊列中。

Hashcode的作用

1、HashCode的特性

(1)HashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,HashCode經(jīng)常用于確定對象的存儲地址。

(2)如果兩個對象相同,?equals方法一定返回true,并且這兩個對象的HashCode一定相同。

(3)兩個對象的HashCode相同,并不一定表示兩個對象就相同,即equals()不一定為true,只能夠說明這兩個對象在一個散列存儲結(jié)構(gòu)中。

(4)如果對象的equals方法被重寫,那么對象的HashCode也盡量重寫。

2、HashCode作用

Java中的集合有兩類,一類是List,再有一類是Set。前者集合內(nèi)的元素是有序的,元素可以重復;后者元素無序,但元素不可重復。

equals方法可用于保證元素不重復,但如果每增加一個元素就檢查一次,若集合中現(xiàn)在已經(jīng)有1000個元素,那么第1001個元素加入集合時,就要調(diào)用1000次equals方法。這顯然會大大降低效率。?于是,Java采用了哈希表的原理。

哈希算法也稱為散列算法,是將數(shù)據(jù)依特定算法直接指定到一個地址上。

這樣一來,當集合要添加新的元素時,先調(diào)用這個元素的HashCode方法,就一下子能定位到它應該放置的物理位置上。

(1)如果這個位置上沒有元素,它就可以直接存儲在這個位置上,不用再進行任何比較了。

(2)如果這個位置上已經(jīng)有元素了,就調(diào)用它的equals方法與新元素進行比較,相同的話就不存了。

(3)不相同的話,也就是發(fā)生了Hash key相同導致沖突的情況,那么就在這個Hash key的地方產(chǎn)生一個鏈表,將所有產(chǎn)生相同HashCode的對象放到這個單鏈表上去,串在一起(很少出現(xiàn))。

這樣一來實際調(diào)用equals方法的次數(shù)就大大降低了,幾乎只需要一兩次。

如何理解HashCode的作用:

從Object角度看,JVM每new一個Object,它都會將這個Object丟到一個Hash表中去,這樣的話,下次做Object的比較或者取這個對象的時候(讀取過程),它會根據(jù)對象的HashCode再從Hash表中取這個對象。這樣做的目的是提高取對象的效率。若HashCode相同再去調(diào)用equal。

3、HashCode實踐(如何用來查找)

HashCode是用于查找使用的,而equals是用于比較兩個對象是否相等的。

(1)例如內(nèi)存中有這樣的位置

而我有個類,這個類有個字段叫ID,我要把這個類存放在以上8個位置之一,如果不用HashCode而任意存放,那么當查找時就需要到這八個位置里挨個去找,或者用二分法一類的算法。

但以上問題如果用HashCode就會使效率提高很多 定義我們的HashCode為ID%8,比如我們的ID為9,9除8的余數(shù)為1,那么我們就把該類存在1這個位置,如果ID是13,求得的余數(shù)是5,那么我們就把該類放在5這個位置。依此類推。

(2)但是如果兩個類有相同的HashCode,例如9除以8和17除以8的余數(shù)都是1,也就是說,我們先通過?HashCode來判斷兩個類是否存放某個桶里,但這個桶里可能有很多類,那么我們就需要再通過equals在這個桶里找到我們要的類。

請看下面這個例子

public class HashTest {

????private int i;

????public int getI() {

????????return i;

????}

????public void setI(int i) {

????????this.i = i;

????}

????public int hashCode() {

????????return i % 10;

????}

????public final static void main(String[] args) {

????????HashTest a = new HashTest();

????????HashTest b = new HashTest();

????????a.setI(1);

????????b.setI(1);

????????Set<HashTest> set = new HashSet<HashTest>();

????????set.add(a);

????????set.add(b);

????????System.out.println(a.hashCode() == b.hashCode());

????????System.out.println(a.equals(b));

????????System.out.println(set);

????}

}

輸出結(jié)果為:

true

False

[HashTest@1, HashTest@1]

以上這個示例,我們只是重寫了HashCode方法,從上面的結(jié)果可以看出,雖然兩個對象的HashCode相等,但是實際上兩個對象并不是相等,因為我們沒有重寫equals方法,那么就會調(diào)用Object默認的equals方法,顯示這是兩個不同的對象。

這里我們將生成的對象放到了HashSet中,而HashSet中只能夠存放唯一的對象,也就是相同的(適用于equals方法)的對象只會存放一個,但是這里實際上是兩個對象ab都被放到了HashSet中,這樣HashSet就失去了他本身的意義了。

下面我們繼續(xù)重寫equals方法:

public class HashTest {

????private int i;

????public int getI() {

????????return i;

????}

????public void setI(int i) {

????????this.i = i;

????}

????public boolean equals(Object object) {

????????if (object == null) {

????????????return false;

????????}

????????if (object == this) {

????????????return true;

????????}

????????if (!(object instanceof HashTest)) {

????????????return false;

????????}

????????HashTest other = (HashTest) object;

????????if (other.getI() == this.getI()) {

????????????return true;

????????}

????????return false;

????}

????public int hashCode() {

????????return i % 10;

????}

????public final static void main(String[] args) {

????????HashTest a = new HashTest();

????????HashTest b = new HashTest();

????????a.setI(1);

????????b.setI(1);

????????Set<HashTest> set = new HashSet<HashTest>();

????????set.add(a);

????????set.add(b);

????????System.out.println(a.hashCode() == b.hashCode());

????????System.out.println(a.equals(b));

????????System.out.println(set);

????}

}

輸出結(jié)果如下所示。

從結(jié)果我們可以看出,現(xiàn)在兩個對象就完全相等了,HashSet中也只存放了一份對象。

注意:

hashCode()只是簡單示例寫的,真正的生產(chǎn)換將不是這樣的

true

true

[HashTest@1]

HashMap的hashcode的作用

hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用來在散列存儲結(jié)構(gòu)中確定對象的存儲地址的。

如果兩個對象相同,就是適用于equals(java.lang.Object) 方法,那么這兩個對象的hashCode一定要相同。

如果對象的equals方法被重寫,那么對象的hashCode也盡量重寫,并且產(chǎn)生hashCode使用的對象,一定要和equals方法中使用的一致,否則就會違反上面提到的第2點。

兩個對象的hashCode相同,并不一定表示兩個對象就相同,也就是不一定適用于equals(java.lang.Object) 方法,只能夠說明這兩個對象在散列存儲結(jié)構(gòu)中,如Hashtable,他們“存放在同一個籃子里”。

什么時候需要重寫?

一般的地方不需要重載hashCode,只有當類需要放在HashTable、HashMap、HashSet等等hash結(jié)構(gòu)的集合時才會重載hashCode,那么為什么要重載hashCode呢?

要比較兩個類的內(nèi)容屬性值,是否相同時候,根據(jù)hashCode 重寫規(guī)則,重寫類的 指定字段的hashCode(),equals()方法。

例如

public class EmpWorkCondition{

?????/**

?????* 員工ID

?????*/

????private Integer empId;

????/**

?????* 員工服務總單數(shù)

?????*/

????private Integer orderSum;

????@Override

????public boolean equals(Object o) {

????????if (this == o) {

????????????return true;

????????}

????????if (o == null || getClass() != o.getClass()) {

????????????return false;

????????}

????????EmpWorkCondition that = (EmpWorkCondition) o;

????????return Objects.equals(empId, that.empId);

????}

????@Override

????public int hashCode() {

????????return Objects.hash(empId);

????}

????// 省略 getter setter

}

public static void main(String[] args) {

????List<EmpWorkCondition> list1 = new ArrayList<EmpWorkCondition>();

????EmpWorkCondition emp1 = new EmpWorkCondition();

????emp1.setEmpId(100);

????emp1.setOrderSum(90000);

????list1.add(emp1);

????List<EmpWorkCondition> list2 = new ArrayList<EmpWorkCondition>();

????EmpWorkCondition emp2 = new EmpWorkCondition();

????emp2.setEmpId(100);

????list2.add(emp2);

????System.out.println(list1.contains(emp2));

}

輸出結(jié)果:true

上面的方法,做的事情就是,比較兩個集合中的,實體類對象屬性值,是否一致

OrderSum 不在比較范圍內(nèi),因為沒有重寫它的,equals()和hashCode()方法

為什么要重載equal方法?

因為Object的equal方法默認是兩個對象的引用的比較,意思就是指向同一內(nèi)存,地址則相等,否則不相等;如果你現(xiàn)在需要利用對象里面的值來判斷是否相等,則重載equal方法。

為什么重載hashCode方法?

一般的地方不需要重載hashCode,只有當類需要放在HashTable、HashMap、HashSet等等hash結(jié)構(gòu)的集合時才會重載hashCode,那么為什么要重載hashCode呢?

如果你重寫了equals,比如說是基于對象的內(nèi)容實現(xiàn)的,而保留hashCode的實現(xiàn)不變,那么很可能某兩個對象明明是“相等”,而hashCode卻不一樣。

這樣,當你用其中的一個作為鍵保存到hashMap、hasoTable或hashSet中,再以“相等的”找另一個作為鍵值去查找他們的時候,則根本找不到。

為什么equals()相等,hashCode就一定要相等,而hashCode相等,卻不要求equals相等?

1、因為是按照hashCode來訪問小內(nèi)存塊,所以hashCode必須相等。 2、HashMap獲取一個對象是比較key的hashCode相等和equal為true。

之所以hashCode相等,卻可以equal不等,就比如ObjectA和ObjectB他們都有屬性name,那么hashCode都以name計算,所以hashCode一樣,但是兩個對象屬于不同類型,所以equal為false。

為什么需要hashCode?

1、通過hashCode可以很快的查到小內(nèi)存塊。 2、通過hashCode比較比equal方法快,當get時先比較hashCode,如果hashCode不同,直接返回false。

ArrayList、LinkedList、Vector的區(qū)別

List的三個子類的特點

ArrayList:

底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組,查詢快,增刪慢。

線程不安全,效率高。

Vector:

底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組,查詢快,增刪慢。

線程安全,效率低。

Vector相對ArrayList查詢慢(線程安全的)。

Vector相對LinkedList增刪慢(數(shù)組結(jié)構(gòu))。

LinkedList

底層數(shù)據(jù)結(jié)構(gòu)是鏈表,查詢慢,增刪快。

線程不安全,效率高。

Vector和ArrayList的區(qū)別

Vector是線程安全的,效率低。

ArrayList是線程不安全的,效率高。

共同點:底層數(shù)據(jù)結(jié)構(gòu)都是數(shù)組實現(xiàn)的,查詢快,增刪慢。

ArrayList和LinkedList的區(qū)別

ArrayList底層是數(shù)組結(jié)果,查詢和修改快。

LinkedList底層是鏈表結(jié)構(gòu)的,增和刪比較快,查詢和修改比較慢。

共同點:都是線程不安全的

List有三個子類使用

查詢多用ArrayList。

增刪多用LinkedList。

如果都多ArrayList。

String、StringBuffer與StringBuilder的區(qū)別

String:適用于少量的字符串操作的情況。

StringBuilder:適用于單線程下在字符緩沖區(qū)進行大量操作的情況。

StringBuffer:適用多線程下在字符緩沖區(qū)進行大量操作的情況。

StringBuilder:是線程不安全的,而StringBuffer是線程安全的。

這三個類之間的區(qū)別主要是在兩個方面,即運行速度和線程安全這兩方面。 首先說運行速度,或者說是執(zhí)行速度,在這方面運行速度快慢為:StringBuilder > StringBuffer > String。

String最慢的原因

String為字符串常量,而StringBuilder和StringBuffer均為字符串變量,即String對象一旦創(chuàng)建之后該對象是不可更改的,但后兩者的對象是變量,是可以更改的。

再來說線程安全

在線程安全上,StringBuilder是線程不安全的,而StringBuffer是線程安全的。

如果一個StringBuffer對象在字符串緩沖區(qū)被多個線程使用時,StringBuffer中很多方法可以帶有synchronized關鍵字,所以可以保證線程是安全的,但StringBuilder的方法則沒有該關鍵字,所以不能保證線程安全,有可能會出現(xiàn)一些錯誤的操作。所以如果要進行的操作是多線程的,那么就要使用StringBuffer,但是在單線程的情況下,還是建議使用速度比較快的StringBuilder。

Map、Set、List、Queue、Stack的特點與用法

Map

Map是鍵值對,鍵Key是唯一不能重復的,一個鍵對應一個值,值可以重復。

TreeMap可以保證順序。

HashMap不保證順序,即為無序的。

Map中可以將Key和Value單獨抽取出來,其中KeySet()方法可以將所有的keys抽取正一個Set。而Values()方法可以將map中所有的values抽取成一個集合。

Set

不包含重復元素的集合,set中最多包含一個null元素。

只能用Lterator實現(xiàn)單項遍歷,Set中沒有同步方法。

List

有序的可重復集合。

可以在任意位置增加刪除元素。

用Iterator實現(xiàn)單向遍歷,也可用ListIterator實現(xiàn)雙向遍歷。

Queue

Queue遵從先進先出原則。

使用時盡量避免add()和remove()方法,而是使用offer()來添加元素,使用poll()來移除元素,它的優(yōu)點是可以通過返回值來判斷是否成功。

LinkedList實現(xiàn)了Queue接口。

Queue通常不允許插入null元素。

Stack

Stack遵從后進先出原則。

Stack繼承自Vector。

它通過五個操作對類Vector進行擴展,允許將向量視為堆棧,它提供了通常的push和pop操作,以及取堆棧頂點的peek()方法、測試堆棧是否為空的empty方法等。

用法

如果涉及堆棧,隊列等操作,建議使用List。

對于快速插入和刪除元素的,建議使用LinkedList。

如果需要快速隨機訪問元素的,建議使用ArrayList。

更為精煉的總結(jié)

Collection 是對象集合, Collection 有兩個子接口 List 和 Set

List 可以通過下標 (1,2..) 來取得值,值可以重復。 Set 只能通過游標來取值,并且值是不能重復的。

ArrayList , Vector , LinkedList 是 List 的實現(xiàn)類

ArrayList 是線程不安全的, Vector 是線程安全的,這兩個類底層都是由數(shù)組實現(xiàn)的。

LinkedList 是線程不安全的,底層是由鏈表實現(xiàn)的。

Map 是鍵值對集合

HashTable 和 HashMap 是 Map 的實現(xiàn)類。

HashTable 是線程安全的,不能存儲 null 值。

HashMap 不是線程安全的,可以存儲 null 值。

Stack類:繼承自Vector,實現(xiàn)一個后進先出的棧。提供了幾個基本方法,push、pop、peak、empty、search等。

Queue接口:提供了幾個基本方法,offer、poll、peek等。已知實現(xiàn)類有LinkedList、PriorityQueue等。

HashMap和HashTable的區(qū)別

Hashtable是基于陳舊的Dictionary類的,HashMap是Java 1.2引進的Map接口的一個實現(xiàn),它們都是集合中將數(shù)據(jù)無序存放的。

1、hashMap去掉了HashTable的contains方法,但是加上了containsValue()和containsKey()方法

HashTable Synchronize同步的,線程安全,HashMap不允許空鍵值為空?,效率低。 HashMap 非Synchronize線程同步的,線程不安全,HashMap允許空鍵值為空?,效率高。 Hashtable是基于陳舊的Dictionary類的,HashMap是Java 1.2引進的Map接口的一個實現(xiàn),它們都是集合中將數(shù)據(jù)無序存放的。

Hashtable的方法是同步的,HashMap未經(jīng)同步,所以在多線程場合要手動同步HashMap這個區(qū)別就像Vector和ArrayList一樣。

查看Hashtable的源代碼就可以發(fā)現(xiàn),除構(gòu)造函數(shù)外,Hashtable的所有 public 方法聲明中都有 synchronized 關鍵字,而HashMap的源代碼中則連 synchronized 的影子都沒有,當然,注釋除外。

2、Hashtable不允許 null 值(key 和 value 都不可以),HashMap允許 null 值(key和value都可以)。

3、兩者的遍歷方式大同小異,Hashtable僅僅比HashMap多一個elements方法。

Hashtable table = new Hashtable();

table.put("key", "value");

Enumeration em = table.elements();

while (em.hasMoreElements()) {

???String obj = (String) em.nextElement();

???System.out.println(obj);

}

4、HashTable使用Enumeration,HashMap使用Iterator

從內(nèi)部機制實現(xiàn)上的區(qū)別如下:

哈希值的使用不同,Hashtable直接使用對象的hashCode

int hash = key.hashCode();

int index = (hash & 0x7FFFFFFF) % tab.length;

而HashMap重新計算hash值,而且用與代替求模:

int hash = hash(k);

int i = indexFor(hash, table.length);

static int hash(Object x) {

??int h = x.hashCode();

??h += ~(h << 9);

??h ^= (h >>> 14);

??h += (h << 4);

??h ^= (h >>> 10);

??return h;

}

static int indexFor(int h, int length) {

??return h & (length-1);

Hashtable中hash數(shù)組默認大小是11,增加的方式是 old*2+1。HashMap中hash數(shù)組的默認大小是16,而且一定是2的指數(shù)。

JDK7與JDK8中HashMap的實現(xiàn)

JDK7中的HashMapHashMap底層維護一個數(shù)組,數(shù)組中的每一項都是一個Entry。

transient Entry<K,V>[] table;

我們向 HashMap 中所放置的對象實際上是存儲在該數(shù)組當中。 而Map中的key,value則以Entry的形式存放在數(shù)組中。

static class Entry<K,V> implements Map.Entry<K,V> {

????????final K key;

????????V value;

????????Entry<K,V> next;

????????int hash;

總結(jié)一下map.put后的過程:

當向 HashMap 中 put 一對鍵值時,它會根據(jù) key的 hashCode 值計算出一個位置, 該位置就是此對象準備往數(shù)組中存放的位置。

如果該位置沒有對象存在,就將此對象直接放進數(shù)組當中;如果該位置已經(jīng)有對象存在了,則順著此存在的對象的鏈開始尋找(為了判斷是否是否值相同,map不允許<key,value>鍵值對重復), 如果此鏈上有對象的話,再去使用 equals方法進行比較,如果對此鏈上的每個對象的 equals 方法比較都為 false,則將該對象放到數(shù)組當中,然后將數(shù)組中該位置以前存在的那個對象鏈接到此對象的后面。

JDK8中的HashMap

JDK8中采用的是位桶+鏈表/紅黑樹(有關紅黑樹請查看紅黑樹)的方式,也是非線程安全的。當某個位桶的鏈表的長度達到某個閥值的時候,這個鏈表就將轉(zhuǎn)換成紅黑樹。

JDK8中,當同一個hash值的節(jié)點數(shù)不小于8時,將不再以單鏈表的形式存儲了,會被調(diào)整成一顆紅黑樹(上圖中null節(jié)點沒畫)。這就是JDK7與JDK8中HashMap實現(xiàn)的最大區(qū)別。

接下來,我們來看下JDK8中HashMap的源碼實現(xiàn)。

JDK中Entry的名字變成了Node,原因是和紅黑樹的實現(xiàn)TreeNode相關聯(lián)。

transient Node<K,V>[] table;

當沖突節(jié)點數(shù)不小于8-1時,轉(zhuǎn)換成紅黑樹。

static final int TREEIFY_THRESHOLD = 8;

HashMap和ConcurrentHashMap的區(qū)別,HashMap的底層源碼

為了線程安全從ConcurrentHashMap代碼中可以看出,它引入了一個“分段鎖”的概念,具體可以理解為把一個大的Map拆分成N個小的HashTable,根據(jù)key.hashCode()來決定把key放到哪個HashTable中。

Hashmap本質(zhì)是數(shù)組加鏈表。根據(jù)key取得hash值,然后計算出數(shù)組下標,如果多個key對應到同一個下標,就用鏈表串起來,新插入的在前面。

ConcurrentHashMap:在hashMap的基礎上,ConcurrentHashMap將數(shù)據(jù)分為多個segment,默認16個(concurrency level),然后每次操作對一個segment加鎖,避免多線程鎖的幾率,提高并發(fā)效率。

總結(jié)

JDK6,7中的ConcurrentHashmap主要使用Segment來實現(xiàn)減小鎖粒度,把HashMap分割成若干個Segment,在put的時候需要鎖住Segment,get時候不加鎖,使用volatile來保證可見性,當要統(tǒng)計全局時(比如size),首先會嘗試多次計算modcount來確定,這幾次嘗試中,是否有其他線程進行了修改操作,如果沒有,則直接返回size。如果有,則需要依次鎖住所有的Segment來計算。

jdk7中ConcurrentHashmap中,當長度過長碰撞會很頻繁,鏈表的增改刪查操作都會消耗很長的時間,影響性能。

jdk8 中完全重寫了concurrentHashmap,代碼量從原來的1000多行變成了 6000多 行,實現(xiàn)上也和原來的分段式存儲有很大的區(qū)別。

JDK8中采用的是位桶+鏈表/紅黑樹(有關紅黑樹請查看紅黑樹)的方式,也是非線程安全的。當某個位桶的鏈表的長度達到某個閥值的時候,這個鏈表就將轉(zhuǎn)換成紅黑樹。

JDK8中,當同一個hash值的節(jié)點數(shù)不小于8時,將不再以單鏈表的形式存儲了,會被調(diào)整成一顆紅黑樹(上圖中null節(jié)點沒畫)。這就是JDK7與JDK8中HashMap實現(xiàn)的最大區(qū)別。

主要設計上的變化有以下幾點

1.jdk8不采用segment而采用node,鎖住node來實現(xiàn)減小鎖粒度。 2.設計了MOVED狀態(tài) 當resize的中過程中 線程2還在put數(shù)據(jù),線程2會幫助resize。 3.使用3個CAS操作來確保node的一些操作的原子性,這種方式代替了鎖。 4.sizeCtl的不同值來代表不同含義,起到了控制的作用。

至于為什么JDK8中使用synchronized而不是ReentrantLock,我猜是因為JDK8中對synchronized有了足夠的優(yōu)化吧。

ConcurrentHashMap能完全替代HashTable嗎

hashTable雖然性能上不如ConcurrentHashMap,但并不能完全被取代,兩者的迭代器的一致性不同的,hash table的迭代器是強一致性的,而concurrenthashmap是弱一致的。

ConcurrentHashMap的get,clear,iterator 都是弱一致性的。 Doug Lea 也將這個判斷留給用戶自己決定是否使用ConcurrentHashMap。

ConcurrentHashMap與HashTable都可以用于多線程的環(huán)境,但是當Hashtable的大小增加到一定的時候,性能會急劇下降,因為迭代時需要被鎖定很長的時間。因為ConcurrentHashMap引入了分割(segmentation),不論它變得多么大,僅僅需要鎖定map的某個部分,而其它的線程不需要等到迭代完成才能訪問map。簡而言之,在迭代的過程中,ConcurrentHashMap僅僅鎖定map的某個部分,而Hashtable則會鎖定整個map。

那么既然ConcurrentHashMap那么優(yōu)秀,為什么還要有Hashtable的存在呢?ConcurrentHashMap能完全替代HashTable嗎?

HashTable雖然性能上不如ConcurrentHashMap,但并不能完全被取代,兩者的迭代器的一致性不同的,HashTable的迭代器是強一致性的,而ConcurrentHashMap是弱一致的。 ConcurrentHashMap的get,clear,iterator 都是弱一致性的。 Doug Lea 也將這個判斷留給用戶自己決定是否使用ConcurrentHashMap。

那么什么是強一致性和弱一致性呢?

get方法是弱一致的,是什么含義?可能你期望往ConcurrentHashMap底層數(shù)據(jù)結(jié)構(gòu)中加入一個元素后,立馬能對get可見,但ConcurrentHashMap并不能如你所愿。換句話說,put操作將一個元素加入到底層數(shù)據(jù)結(jié)構(gòu)后,get可能在某段時間內(nèi)還看不到這個元素,若不考慮內(nèi)存模型,單從代碼邏輯上來看,卻是應該可以看得到的。

下面將結(jié)合代碼和java內(nèi)存模型相關內(nèi)容來分析下put/get方法。put方法我們只需關注Segment#put,get方法只需關注Segment#get,在繼續(xù)之前,先要說明一下Segment里有兩個volatile變量:count和table;HashEntry里有一個volatile變量:value。

總結(jié)

ConcurrentHashMap的弱一致性主要是為了提升效率,是一致性與效率之間的一種權衡。要成為強一致性,就得到處使用鎖,甚至是全局鎖,這就與Hashtable和同步的HashMap一樣了。

為什么HashMap是線程不安全的

HashMap 在并發(fā)執(zhí)行 put 操作時會引起死循環(huán),導致 CPU 利用率接近100%。因為多線程會導致 HashMap 的 Node 鏈表形成環(huán)形數(shù)據(jù)結(jié)構(gòu),一旦形成環(huán)形數(shù)據(jù)結(jié)構(gòu),Node 的 next 節(jié)點永遠不為空,就會在獲取 Node 時產(chǎn)生死循環(huán)。

如何線程安全的使用HashMap

了解了 HashMap 為什么線程不安全,那現(xiàn)在看看如何線程安全的使用 HashMap。這個無非就是以下三種方式:

Hashtable ConcurrentHashMap Synchronized Map

Hashtable

例子

//Hashtable

Map<String, String> hashtable = new Hashtable<>();

//synchronizedMap

Map<String, String> synchronizedHashMap = Collections.synchronizedMap(new HashMap<String, String>());

//ConcurrentHashMap

Map<String, String> concurrentHashMap = new ConcurrentHashMap<>();

Hashtable

先稍微吐槽一下,為啥命名不是 HashTable 啊,看著好難受不管了就裝作它叫HashTable 吧。這貨已經(jīng)不常用了,就簡單說說吧。HashTable 源碼中是使用?synchronized?來保證線程安全的,比如下面的 get 方法和 put 方法:

public synchronized V get(Object key) {

???// 省略實現(xiàn)

}

public synchronized V put(K key, V value) {

// 省略實現(xiàn)

}

所以當一個線程訪問 HashTable 的同步方法時,其他線程如果也要訪問同步方法,會被阻塞住。舉個例子,當一個線程使用 put 方法時,另一個線程不但不可以使用 put 方法,連 get 方法都不可以,好霸道?。。?!so~~,效率很低,現(xiàn)在基本不會選擇它了。

ConcurrentHashMap

ConcurrentHashMap 于 Java 7 的,和8有區(qū)別,在8中 CHM 摒棄了 Segment(鎖段)的概念,而是啟用了一種全新的方式實現(xiàn),利用 CAS 算法,有時間會重新總結(jié)一下。

SynchronizedMap

synchronizedMap() 方法后會返回一個 SynchronizedMap 類的對象,而在 SynchronizedMap 類中使用了 synchronized 同步關鍵字來保證對 Map 的操作是線程安全的。

性能對比

這是要靠數(shù)據(jù)說話的時代,所以不能只靠嘴說 CHM 快,它就快了。寫個測試用例,實際的比較一下這三種方式的效率(源碼來源),下面的代碼分別通過三種方式創(chuàng)建 Map 對象,使用 ExecutorService 來并發(fā)運行5個線程,每個線程添加/獲取500K個元素。

Test started for: class java.util.Hashtable

2500K entried added/retrieved in 2018 ms

2500K entried added/retrieved in 1746 ms

2500K entried added/retrieved in 1806 ms

2500K entried added/retrieved in 1801 ms

2500K entried added/retrieved in 1804 ms

For class java.util.Hashtable the average time is 1835 ms

Test started for: class java.util.Collections$SynchronizedMap

2500K entried added/retrieved in 3041 ms

2500K entried added/retrieved in 1690 ms

2500K entried added/retrieved in 1740 ms

2500K entried added/retrieved in 1649 ms

2500K entried added/retrieved in 1696 ms

For class java.util.Collections$SynchronizedMap the average time is 1963 ms

Test started for: class java.util.concurrent.ConcurrentHashMap

2500K entried added/retrieved in 738 ms

2500K entried added/retrieved in 696 ms

2500K entried added/retrieved in 548 ms

2500K entried added/retrieved in 1447 ms

2500K entried added/retrieved in 531 ms

For class java.util.concurrent.ConcurrentHashMap the average time is 792 ms

ConcurrentHashMap 性能是明顯優(yōu)于 Hashtable 和 SynchronizedMap 的,CHM 花費的時間比前兩個的一半還少。

多并發(fā)情況下HashMap是否還會產(chǎn)生死循環(huán)

今天本來想看下了ConcurrentHashMap的源碼,ConcurrentHashMap是Java 5中支持高并發(fā)、高吞吐量的線程安全HashMap實現(xiàn)。

在看很多博客在介紹ConcurrentHashMap之前,都說HashMap適用于單線程訪問,這是因為HashMap的所有方法都沒有進行鎖同步,因此是線程不安全的,不僅如此,當多線程訪問的時候還容易產(chǎn)生死循環(huán)。

雖然自己在前幾天的時候看過HashMap的源碼,感覺思路啥啥的都還清楚,對于多線程訪問只知道HashMap是線程不安全的,但是不知道HashMap在多線程并發(fā)的情況下會產(chǎn)生死循環(huán)呢,為什么會產(chǎn)生,何種情況下才會產(chǎn)生死循環(huán)呢???

《Java困惑》:多并發(fā)情況下HashMap是否還會產(chǎn)生死循環(huán)。

既然會產(chǎn)生死循環(huán),為什么并發(fā)情況下,還是用ConcurrentHashMap。 jdk 好像有,但是Jdk8 已經(jīng)修復了這個問題。

TreeMap、HashMap、LindedHashMap的區(qū)別

LinkedHashMap可以保證HashMap集合有序,存入的順序和取出的順序一致。

TreeMap實現(xiàn)SortMap接口,能夠把它保存的記錄根據(jù)鍵排序,默認是按鍵值的升序排序,也可以指定排序的比較器,當用Iterator遍歷TreeMap時,得到的記錄是排過序的。

HashMap不保證順序,即為無序的,具有很快的訪問速度。 HashMap最多只允許一條記錄的鍵為Null;允許多條記錄的值為 Null。 HashMap不支持線程的同步。

我們在開發(fā)的過程中使用HashMap比較多,在Map中在Map 中插入、刪除和定位元素,HashMap 是最好的選擇。

但如果您要按自然順序或自定義順序遍歷鍵,那么TreeMap會更好。

如果需要輸出的順序和輸入的相同,那么用LinkedHashMap 可以實現(xiàn),它還可以按讀取順序來排列。

Collection包結(jié)構(gòu),與Collections的區(qū)別

Collection 是集合類的上級接口,子接口主要有Set、List 、Map。

Collecions 是針對集合類的一個幫助類, 提供了操作集合的工具方法,一系列靜態(tài)方法實現(xiàn)對各種集合的搜索、排序線性、線程安全化等操作。

例如

Map<String, Object> map4 = Collections.synchronizedMap(new HashMap<String, Object>()); 線程安全 的HashMap

Collections.sort(List<T> list, Comparator<? super T> c); 排序 List

Collection

Collection 是單列集合

List

元素是有序的、可重復。 有序的 collection,可以對列表中每個元素的插入位置進行精確地控制。 可以根據(jù)元素的整數(shù)索引(在列表中的位置)訪問元素,并搜索列表中的元素。 可存放重復元素,元素存取是有序的。

List接口中常用類

Vector:線程安全,但速度慢,已被ArrayList替代。底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組結(jié)構(gòu)。 ArrayList:線程不安全,查詢速度快。底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組結(jié)構(gòu)。 LinkedList:線程不安全。增刪速度快。底層數(shù)據(jù)結(jié)構(gòu)是列表結(jié)構(gòu)。

Set

Set接口中常用的類

Set(集) 元素無序的、不可重復。 取出元素的方法只有迭代器。不可以存放重復元素,元素存取是無序的。

HashSet:線程不安全,存取速度快。它是如何保證元素唯一性的呢?依賴的是元素的hashCode方法和euqals方法。 TreeSet:線程不安全,可以對Set集合中的元素進行排序。它的排序是如何進行的呢?通過compareTo或者compare方法中的來保證元素的唯一性。元素是以二叉樹的形式存放的。

Map

map是一個雙列集合

Hashtable:線程安全,速度快。底層是哈希表數(shù)據(jù)結(jié)構(gòu)。是同步的。不允許null作為鍵,null作為值。

Properties:用于配置文件的定義和操作,使用頻率非常高,同時鍵和值都是字符串。是集合中可以和IO技術相結(jié)合的對象。

HashMap:線程不安全,速度慢。底層也是哈希表數(shù)據(jù)結(jié)構(gòu)。是不同步的。允許null作為鍵,null作為值,替代了Hashtable。

LinkedHashMap: 可以保證HashMap集合有序。存入的順序和取出的順序一致。

TreeMap:可以用來對Map集合中的鍵進行排序

try?catch?finally,try里有return,finally還執(zhí)行么

肯定會執(zhí)行。finally{}塊的代碼。 只有在try{}塊中包含遇到System.exit(0)。 之類的導致Java虛擬機直接退出的語句才會不執(zhí)行。

當程序執(zhí)行try{}遇到return時,程序會先執(zhí)行return語句,但并不會立即返回——也就是把return語句要做的一切事情都準備好,也就是在將要返回、但并未返回的時候,程序把執(zhí)行流程轉(zhuǎn)去執(zhí)行finally塊,當finally塊執(zhí)行完成后就直接返回剛才return語句已經(jīng)準備好的結(jié)果。

Excption與Error包結(jié)構(gòu)。OOM你遇到過哪些情況,SO F你遇到過哪些情況

Throwable是 Java 語言中所有錯誤或異常的超類。 Throwable包含兩個子類: Error 和 Exception 。它們通常用于指示發(fā)生了異常情況。 Throwable包含了其線程創(chuàng)建時線程執(zhí)行堆棧的快照,它提供了printStackTrace()等接口用于獲取堆棧跟蹤數(shù)據(jù)等信息。

Java將可拋出(Throwable)的結(jié)構(gòu)分為三種類型:

被檢查的異常(Checked Exception)。 運行時異常(RuntimeException)。 錯誤(Error)。

運行時異常RuntimeException

定義 : RuntimeException及其子類都被稱為運行時異常。 特點 : Java編譯器不會檢查它 也就是說,當程序中可能出現(xiàn)這類異常時,倘若既"沒有通過throws聲明拋出它",也"沒有用try-catch語句捕獲它",還是會編譯通過。

例如,除數(shù)為零時產(chǎn)生的ArithmeticException異常,數(shù)組越界時產(chǎn)生的IndexOutOfBoundsException異常,fail-fail機制產(chǎn)生的ConcurrentModificationException異常等,都屬于運行時異常。

堆內(nèi)存溢出 OutOfMemoryError(OOM)

除了程序計數(shù)器外,虛擬機內(nèi)存的其他幾個運行時區(qū)域都有發(fā)生OutOfMemoryError(OOM)異常的可能。

Java Heap 溢出。 一般的異常信息:java.lang.OutOfMemoryError:Java heap spacess。 java堆用于存儲對象實例,我們只要不斷的創(chuàng)建對象,并且保證GC Roots到對象之間有可達路徑來避免垃圾回收機制清除這些對象,就會在對象數(shù)量達到最大堆容量限制后產(chǎn)生內(nèi)存溢出異常。

堆棧溢出 StackOverflow (SOF)

StackOverflowError 的定義: 當應用程序遞歸太深而發(fā)生堆棧溢出時,拋出該錯誤。 因為棧一般默認為1-2m,一旦出現(xiàn)死循環(huán)或者是大量的遞歸調(diào)用,在不斷的壓棧過程中,造成棧容量超過1m而導致溢出。

棧溢出的原因:

遞歸調(diào)用。 大量循環(huán)或死循環(huán)。 全局變量是否過多。 數(shù)組、List、map數(shù)據(jù)過大。

Java(OOP)面向?qū)ο蟮娜齻€特征與含義

封裝(高內(nèi)聚低耦合 -->解耦)

封裝是指將某事物的屬性和行為包裝到對象中,這個對象只對外公布需要公開的屬性和行為,而這個公布也是可以有選擇性的公布給其它對象。在java中能使用private、protected、public三種修飾符或不用(即默認defalut)對外部對象訪問該對象的屬性和行為進行限制。

java的繼承(重用父類的代碼)

繼承是子對象可以繼承父對象的屬性和行為,亦即父對象擁有的屬性和行為,其子對象也就擁有了這些屬性和行為。

java中的多態(tài)(父類引用指向子類對象)

多態(tài)是指父對象中的同一個行為能在其多個子對象中有不同的表現(xiàn)。

有兩種多態(tài)的機制:編譯時多態(tài)、運行時多態(tài)。

1、方法的重載:重載是指同一類中有多個同名的方法,但這些方法有著不同的參數(shù)。,因此在編譯時就可以確定到底調(diào)用哪個方法,它是一種編譯時多態(tài)。 2、方法的重寫:子類可以覆蓋父類的方法,因此同樣的方法會在父類中與子類中有著不同的表現(xiàn)形式。

Override和Overload的含義去區(qū)別

重載 Overload方法名相同,參數(shù)列表不同(個數(shù)、順序、類型不同)與返回類型無關。 重寫 Override 覆蓋。 將父類的方法覆蓋。 重寫方法重寫:方法名相同,訪問修飾符只能大于被重寫的方法訪問修飾符,方法簽名個數(shù),順序個數(shù)類型相同。

Override(重寫)

方法名、參數(shù)、返回值相同。

子類方法不能縮小父類方法的訪問權限。

子類方法不能拋出比父類方法更多的異常(但子類方法可以不拋出異常)。

存在于父類和子類之間。

方法被定義為final不能被重寫。

Overload(重載)

參數(shù)類型、個數(shù)、順序至少有一個不相同。

不能重載只有返回值不同的方法名。

存在于父類和子類、同類中。

而重載的規(guī)則

1、必須具有不同的參數(shù)列表。 2、可以有不同的返回類型,只要參數(shù)列表不同就可以了。 3、可以有不同的訪問修飾符。 4、可以拋出不同的異常。

重寫方法的規(guī)則

1、參數(shù)列表必須完全與被重寫的方法相同,否則不能稱其為重寫而是重載。 2、返回的類型必須一直與被重寫的方法的返回類型相同,否則不能稱其為重寫而是重載。 3、訪問修飾符的限制一定要大于被重寫方法的訪問修飾符(public>protected>default>private)。 4、重寫方法一定不能拋出新的檢查異?;蛘弑缺恢貙懛椒ㄉ昝鞲訉挿旱臋z查型異常。

例如: 父類的一個方法申明了一個檢查異常IOException,在重寫這個方法是就不能拋出Exception,只能拋出IOException的子類異常,可以拋出非檢查異常。

Interface與abstract類的區(qū)別

Interface 只能有成員常量,只能是方法的聲明。 Abstract class可以有成員變量,可以聲明普通方法和抽象方法。

interface是接口,所有的方法都是抽象方法,成員變量是默認的public static final 類型。接口不能實例化自己。

abstract class是抽象類,至少包含一個抽象方法的累叫抽象類,抽象類不能被自身實例化,并用abstract關鍵字來修飾。

Static?class?與non?static?class的區(qū)別

static class(內(nèi)部靜態(tài)類)

1、用static修飾的是內(nèi)部類,此時這個內(nèi)部類變?yōu)殪o態(tài)內(nèi)部類;對測試有用。 2、內(nèi)部靜態(tài)類不需要有指向外部類的引用。 3、靜態(tài)類只能訪問外部類的靜態(tài)成員,不能訪問外部類的非靜態(tài)成員。

non static class(非靜態(tài)內(nèi)部類)

1、非靜態(tài)內(nèi)部類需要持有對外部類的引用。 2、非靜態(tài)內(nèi)部類能夠訪問外部類的靜態(tài)和非靜態(tài)成員。 3、一個非靜態(tài)內(nèi)部類不能脫離外部類實體被創(chuàng)建。 4、一個非靜態(tài)內(nèi)部類可以訪問外部類的數(shù)據(jù)和方法。

java多態(tài)的實現(xiàn)原理

foreach與正常for循環(huán)效率對比

用for循環(huán)arrayList 10萬次花費時間:5毫秒。 用foreach循環(huán)arrayList 10萬次花費時間:7毫秒。 用for循環(huán)linkList 10萬次花費時間:4481毫秒。 用foreach循環(huán)linkList 10萬次花費時間:5毫秒。

循環(huán)ArrayList時,普通for循環(huán)比foreach循環(huán)花費的時間要少一點。 循環(huán)LinkList時,普通for循環(huán)比foreach循環(huán)花費的時間要多很多。

當我將循環(huán)次數(shù)提升到一百萬次的時候,循環(huán)ArrayList,普通for循環(huán)還是比foreach要快一點;但是普通for循環(huán)在循環(huán)LinkList時,程序直接卡死。

ArrayList:ArrayList是采用數(shù)組的形式保存對象的,這種方式將對象放在連續(xù)的內(nèi)存塊中,所以插入和刪除時比較麻煩,查詢比較方便。

LinkList:LinkList是將對象放在獨立的空間中,而且每個空間中還保存下一個空間的索引,也就是數(shù)據(jù)結(jié)構(gòu)中的鏈表結(jié)構(gòu),插入和刪除比較方便,但是查找很麻煩,要從第一個開始遍歷。

結(jié)論:

需要循環(huán)數(shù)組結(jié)構(gòu)的數(shù)據(jù)時,建議使用普通for循環(huán),因為for循環(huán)采用下標訪問,對于數(shù)組結(jié)構(gòu)的數(shù)據(jù)來說,采用下標訪問比較好。

需要循環(huán)鏈表結(jié)構(gòu)的數(shù)據(jù)時,一定不要使用普通for循環(huán),這種做法很糟糕,數(shù)據(jù)量大的時候有可能會導致系統(tǒng)崩潰。

Java?IO與NIO

NIO是為了彌補IO操作的不足而誕生的,NIO的一些新特性有:非阻塞I/O,選擇器,緩沖以及管道。管道(Channel),緩沖(Buffer) ,選擇器( Selector)是其主要特征。

概念解釋

Channel——管道實際上就像傳統(tǒng)IO中的流,到任何目的地(或來自任何地方)的所有數(shù)據(jù)都必須通過一個 Channel 對象。一個 Buffer 實質(zhì)上是一個容器對象。

每一種基本 Java 類型都有一種緩沖區(qū)類型:

ByteBuffer——byte

CharBuffer——char

ShortBuffer——short

IntBuffer——int

LongBuffer——long

FloatBuffer——float

DoubleBuffer——double

Selector——選擇器用于監(jiān)聽多個管道的事件,使用傳統(tǒng)的阻塞IO時我們可以方便的知道什么時候可以進行讀寫,而使用非阻塞通道,我們需要一些方法來知道什么時候通道準備好了,選擇器正是為這個需要而誕生的。

NIO和傳統(tǒng)的IO有什么區(qū)別呢?

IO是面向流的,NIO是面向塊(緩沖區(qū))的。

IO面向流的操作一次一個字節(jié)地處理數(shù)據(jù)。一個輸入流產(chǎn)生一個字節(jié)的數(shù)據(jù),一個輸出流消費一個字節(jié)的數(shù)據(jù)。,導致了數(shù)據(jù)的讀取和寫入效率不佳。

NIO面向塊的操作在一步中產(chǎn)生或者消費一個數(shù)據(jù)塊。按塊處理數(shù)據(jù)比按(流式的)字節(jié)處理數(shù)據(jù)要快得多,同時數(shù)據(jù)讀取到一個它稍后處理的緩沖區(qū),需要時可在緩沖區(qū)中前后移動。這就增加了處理過程中的靈活性。通俗來說,NIO采取了“預讀”的方式,當你讀取某一部分數(shù)據(jù)時,他就會猜測你下一步可能會讀取的數(shù)據(jù)而預先緩沖下來。

IO是阻塞的,NIO是非阻塞的

對于傳統(tǒng)的IO,當一個線程調(diào)用read() 或 write()時,該線程被阻塞,直到有一些數(shù)據(jù)被讀取,或數(shù)據(jù)完全寫入。該線程在此期間不能再干任何事情了。

而對于NIO,使用一個線程發(fā)送讀取數(shù)據(jù)請求,沒有得到響應之前,線程是空閑的,此時線程可以去執(zhí)行別的任務,而不是像IO中那樣只能等待響應完成。

NIO和IO適用場景

NIO是為彌補傳統(tǒng)IO的不足而誕生的,但是尺有所短寸有所長,NIO也有缺點,因為NIO是面向緩沖區(qū)的操作,每一次的數(shù)據(jù)處理都是對緩沖區(qū)進行的,那么就會有一個問題,在數(shù)據(jù)處理之前必須要判斷緩沖區(qū)的數(shù)據(jù)是否完整或者已經(jīng)讀取完畢,如果沒有,假設數(shù)據(jù)只讀取了一部分,那么對不完整的數(shù)據(jù)處理沒有任何意義。所以每次數(shù)據(jù)處理之前都要檢測緩沖區(qū)數(shù)據(jù)。

那么NIO和IO各適用的場景是什么呢?

如果需要管理同時打開的成千上萬個連接,這些連接每次只是發(fā)送少量的數(shù)據(jù),例如聊天服務器,這時候用NIO處理數(shù)據(jù)可能是個很好的選擇。

而如果只有少量的連接,而這些連接每次要發(fā)送大量的數(shù)據(jù),這時候傳統(tǒng)的IO更合適。使用哪種處理數(shù)據(jù),需要在數(shù)據(jù)的響應等待時間和檢查緩沖區(qū)數(shù)據(jù)的時間上作比較來權衡選擇。

通俗解釋,最后,對于NIO和傳統(tǒng)IO

有一個網(wǎng)友講的生動的例子:

以前的流總是堵塞的,一個線程只要對它進行操作,其它操作就會被堵塞,也就相當于水管沒有閥門,你伸手接水的時候,不管水到了沒有,你就都只能耗在接水(流)上。

nio的Channel的加入,相當于增加了水龍頭(有閥門),雖然一個時刻也只能接一個水管的水,但依賴輪換策略,在水量不大的時候,各個水管里流出來的水,都可以得到妥

善接納,這個關鍵之處就是增加了一個接水工,也就是Selector,他負責協(xié)調(diào),也就是看哪根水管有水了的話,在當前水管的水接到一定程度的時候,就切換一下:臨時關上當

前水龍頭,試著打開另一個水龍頭(看看有沒有水)。

當其他人需要用水的時候,不是直接去接水,而是事前提了一個水桶給接水工,這個水桶就是Buffer。也就是,其他人雖然也可能要等,但不會在現(xiàn)場等,而是回家等,可以做

其它事去,水接滿了,接水工會通知他們。

這其實也是非常接近當前社會分工細化的現(xiàn)實,也是統(tǒng)分利用現(xiàn)有資源達到并發(fā)效果的一種很經(jīng)濟的手段,而不是動不動就來個并行處理,雖然那樣是最簡單的,但也是最浪費資源的方式。

java反射的作用于原理

什么是Java的反射呢?

Java 反射是可以讓我們在運行時,通過一個類的Class對象來獲取它獲取類的方法、屬性、父類、接口等類的內(nèi)部信息的機制。

這種動態(tài)獲取信息以及動態(tài)調(diào)用對象的方法的功能稱為JAVA的反射。

反射的作用?

反射就是:在任意一個方法里:

1.如果我知道一個類的名稱/或者它的一個實例對象, 我就能把這個類的所有方法和變量的信息找出來(方法名,變量名,方法,修飾符,類型,方法參數(shù)等等所有信息)

2.如果我還明確知道這個類里某個變量的名稱,我還能得到這個變量當前的值。

3.當然,如果我明確知道這個類里的某個方法名+參數(shù)個數(shù)類型,我還能通過傳遞參數(shù)來運行那個類里的那個方法。

反射機制主要提供了以下功能:

在運行時判斷任意一個對象所屬的類。

在運行時構(gòu)造任意一個類的對象。

在運行時判斷任意一個類所具有的成員變量和方法。

在運行時調(diào)用任意一個對象的方法。

生成動態(tài)代理。

反射的原理?

JAVA語言編譯之后會生成一個.class文件,反射就是通過字節(jié)碼文件找到某一個類、類中的方法以及屬性等。

反射的實現(xiàn)API有哪些?

反射的實現(xiàn)主要借助以下四個類:

Class:類的對象

Constructor:類的構(gòu)造方法

Field:類中的屬性對象

Method:類中的方法對象

反射的實例

泛型常用特點

List<String>能否轉(zhuǎn)為List<Object>

不可以強轉(zhuǎn)類型的

這個問題涉及到了,范型向上轉(zhuǎn)型 和 范型向下轉(zhuǎn)型問題。 List向上轉(zhuǎn)換至List(等價于List)會丟失String類的身份(String類型的特有接口)。 當需要由List向下轉(zhuǎn)型時,你的程序必須明確的知道將對象轉(zhuǎn)換成何種具體類型,不然這將是不安全的操作。

如果要強轉(zhuǎn)類型,Json 序列化轉(zhuǎn)型

List<String> str = new ArrayList<String>();

List<Object> obj= JSONObject.parseArray(JSONObject.toJSONString(str));

或者遍歷,或者克隆,但是取出來就是(Object)了,需要強轉(zhuǎn),String 因為類型丟了。

解析XML的幾種方式的原理與特點:DOM、SAX

Android中三種常用解析XML的方式(DOM、SAX、PULL)簡介及區(qū)別。

xml解析的兩種基本方式:DOM和SAX的區(qū)別是?

DOM: document object model。

SAX: simple api for xml 。

dom一次性把xml文件全部加載到內(nèi)存中簡歷一個結(jié)構(gòu)一摸一樣的樹, 效率低。 SAX解析器的優(yōu)點是解析速度快,占用內(nèi)存少,效率高。

DOM在內(nèi)存中以樹形結(jié)構(gòu)存放,因此檢索和更新效率會更高。但是對于特別大的文檔,解析和加載整個文檔將會很耗資源。

DOM,它是生成一個樹,有了樹以后你搜索、查找都可以做。 SAX,它是基于流的,就是解析器從頭到尾解析一遍xml文件,解析完了以后你不過想再查找重新解析。 sax解析器核心是事件處理機制。例如解析器發(fā)現(xiàn)一個標記的開始標記時,將所發(fā)現(xiàn)的數(shù)據(jù)會封裝為一個標記開始事件,并把這個報告給事件處理器。

平時工作中,xml解析你是使用什么?

JDOM

DOM4J

Java1.7與1.8,1.9,10 新特性

1.5

自動裝箱與拆箱

枚舉(常用來設計單例模式)

靜態(tài)導入

可變參數(shù)

內(nèi)省

1.6

Web服務元數(shù)據(jù)

腳本語言支持

JTable的排序和過濾

更簡單,更強大的JAX-WS

輕量級Http Server

嵌入式數(shù)據(jù)庫 Derby

1.7

switch中可以使用字串了

運用List tempList = new ArrayList<>(); 即泛型實例化類型自動推斷

語法上支持集合,而不一定是數(shù)組

新增一些取環(huán)境信息的工具方法

Boolean類型反轉(zhuǎn),空指針安全,參與位運算

兩個char間的equals

安全的加減乘除

map集合支持并發(fā)請求,且可以寫成 Map map = {name:"xxx",age:18};

1.8

允許在接口中有默認方法實現(xiàn)

Lambda表達式

函數(shù)式接口

方法和構(gòu)造函數(shù)引用

Lambda的范圍

內(nèi)置函數(shù)式接口

Streams

Parallel Streams

Map

時間日期API

Annotations

1.9

Jigsaw 項目;模塊化源碼

簡化進程API

輕量級 JSON API

錢和貨幣的API

改善鎖爭用機制

代碼分段緩存

智能Java編譯, 第二階段

HTTP 2.0客戶端

Kulla計劃: Java的REPL實現(xiàn)

10

本地變量類型推斷

統(tǒng)一JDK倉庫

垃圾回收器接口

G1的并行Full GC

應用程序類數(shù)據(jù)共享

ThreadLocal握手機制

設計模式:單例、工廠、適配器、責任鏈、觀察者等等

【示例】設計模式——單例模式、工廠模式、代理模式、觀察者模式、裝飾器模式

什么是設計模式

設計模式是一種解決方案,用于解決在軟件設計中普遍存在的問題,是前輩們對之前軟件設計中反復出現(xiàn)的問題的一個總結(jié)。

我們學設計模式,是為了學習如何合理的組織我們的代碼,如何解耦,如何真正的達到對修改封閉對擴展開放的效果,而不是去背誦那些類的繼承模式,然后自己記不住,回過頭來就罵設計模式把你的代碼搞復雜了,要反設計模式。

設計模式的六大原則

開閉原則:實現(xiàn)熱插拔,提高擴展性。

里氏代換原則:實現(xiàn)抽象的規(guī)范,實現(xiàn)子父類互相替換;

依賴倒轉(zhuǎn)原則:針對接口編程,實現(xiàn)開閉原則的基礎;

接口隔離原則:降低耦合度,接口單獨設計,互相隔離;

迪米特法則,又稱不知道原則:功能模塊盡量獨立;

合成復用原則:盡量使用聚合,組合,而不是繼承;

1、開閉原則(Open Close Principle)

開閉原則的意思是:對擴展開放,對修改關閉。在程序需要進行拓展的時候,不能去修改原有的代碼,實現(xiàn)一個熱插拔的效果。簡言之,是為了使程序的擴展性好,易于維護和升級。想要達到這樣的效果,我們需要使用接口和抽象類,后面的具體設計中我們會提到這點。

2、里氏代換原則(Liskov Substitution Principle)

里氏代換原則是面向?qū)ο笤O計的基本原則之一。 里氏代換原則中說,任何基類可以出現(xiàn)的地方,子類一定可以出現(xiàn)。LSP 是繼承復用的基石,只有當派生類可以替換掉基類,且軟件單位的功能不受到影響時,基類才能真正被復用,而派生類也能夠在基類的基礎上增加新的行為。里氏代換原則是對開閉原則的補充。實現(xiàn)開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關系就是抽象化的具體實現(xiàn),所以里氏代換原則是對實現(xiàn)抽象化的具體步驟的規(guī)范。

3、依賴倒轉(zhuǎn)原則(Dependence Inversion Principle)

這個原則是開閉原則的基礎,具體內(nèi)容:針對接口編程,依賴于抽象而不依賴于具體。

4、接口隔離原則(Interface Segregation Principle)

這個原則的意思是:使用多個隔離的接口,比使用單個接口要好。它還有另外一個意思是:降低類之間的耦合度。由此可見,其實設計模式就是從大型軟件架構(gòu)出發(fā)、便于升級和維護的軟件設計思想,它強調(diào)降低依賴,降低耦合。

5、迪米特法則,又稱最少知道原則(Demeter Principle)

最少知道原則是指:一個實體應當盡量少地與其他實體之間發(fā)生相互作用,使得系統(tǒng)功能模塊相對獨立。

6、合成復用原則(Composite Reuse Principle)

合成復用原則是指:盡量使用合成/聚合的方式,而不是使用繼承。

JNI的使用

JNI是 Java Native Interface 的縮寫,它提供了若干的API實現(xiàn)了Java和其他語言的通信(主要是C&C++)。從Java1.1開始,JNI標準成為java平臺的一部分,它允許Java代碼和其他語言寫的代碼進行交互。JNI一開始是為了本地已編譯語言,尤其是C和C++而設計的,但是它并不妨礙你使用其他編程語言,只要調(diào)用約定受支持就可以了。使用java與本地已編譯的代碼交互,通常會喪失平臺可移植性。

JNI步驟

java類中編寫帶有native 聲明的方法。

使用 javac 命令編譯所編寫的java類。

使用 javah 命令生成頭文件。

使用C/C++實現(xiàn)本地方法。

生成動態(tài)連接庫。

執(zhí)行(java)。

JNI實例

public class HelloWorld {

????public native void displayHelloWorld();//所有native關鍵詞修飾的都是對本地的聲明

????static {

????????System.loadLibrary("hello");//載入本地庫

????}

????public static void main(String[] args) {

????????new HelloWorld().displayHelloWorld();

????}

}

AOP是什么

AOP(Aspect Oriented Programming) 面向切面編程,是目前軟件開發(fā)中的一個熱點,是Spring框架內(nèi)容,利用AOP可以對業(yè)務邏輯的各個部分隔離,從而使的業(yè)務邏輯各部分的耦合性降低,提高程序的可重用性,踢開開發(fā)效率,主要功能:日志記錄,性能統(tǒng)計,安全控制,事務處理,異常處理等。

AOP實現(xiàn)原理是java動態(tài)代理,但是jdk的動態(tài)代理必須實現(xiàn)接口,所以spring的aop是用cglib這個庫實現(xiàn)的,cglis使用里asm這個直接操縱字節(jié)碼的框架,所以可以做到不使用接口的情況下實現(xiàn)動態(tài)代理。

OOP是什么

OOP面向?qū)ο缶幊?,針對業(yè)務處理過程的實體及其屬性和行為進行抽象封裝,以獲得更加清晰高效的邏輯單元劃分。

AOP與OOP的區(qū)別

OOP面向?qū)ο缶幊?,針對業(yè)務處理過程的實體及其屬性和行為進行抽象封裝,以獲得更加清晰高效的邏輯單元劃分。而AOP則是針對業(yè)務處理過程中的切面進行提取,它所面對的是處理過程的某個步驟或階段,以獲得邏輯過程的中各部分之間低耦合的隔離效果。這兩種設計思想在目標上有著本質(zhì)的差異。

1、HashMap的源碼,實現(xiàn)原理,JDK8中對HashMap做了怎樣的優(yōu)化。

HashMap的源碼,實現(xiàn)原理:

HashMap的鏈表元素對應的是一個靜態(tài)內(nèi)部類Entry,Entry主要包含key,value,next三個元素

主要有put和get方法,put的原理是,通過hash%Entry.length計算index,此時記作Entry[index]=該元素。如果index相同

就是新入的元素放置到Entry[index],原先的元素記作Entry[index].next

get就比較簡單了,先遍歷數(shù)組,再遍歷鏈表元素。

null key總是放在Entry數(shù)組的第一個元素

解決hash沖突的方法:鏈地址法

再散列rehash的過程:確定容量超過目前哈希表的容量,重新調(diào)整table 的容量大小,當超過容量的最大值時,取

Integer.Maxvalue

數(shù)組和鏈表的區(qū)別:

1 鏈表在內(nèi)存是是不連續(xù)的,數(shù)組是連續(xù)的

2 數(shù)組內(nèi)存分配是一次性的全部分配,鏈表不是

3 數(shù)組是同級別的,鏈表是一個連接一個,查找效率鏈表不如數(shù)組

4 鏈表的增刪比數(shù)組方便

5 鏈表的擴展?jié)摿Υ笥跀?shù)組?


JDK8中對HashMap做了怎樣的優(yōu)化:

1.生成一個entry初始容量16的數(shù)組+鏈表結(jié)構(gòu),使用容量大于0.75f時,自動擴容2^n

2.當鏈表長度大于8時,轉(zhuǎn)化為紅黑樹結(jié)構(gòu).

2、HaspMap擴容是怎樣擴容的,為什么都是2的N次冪的大小。

首先檢查大小,看是否需要擴容(默認元素超過最大值的0.75時擴容),如果需要擴容就進行擴容

然后計算出key的hashcode,根據(jù)hashcode定位數(shù)值所在的bucketIndex

如果該位置上沒有元素,就直接插入,結(jié)束

如果該位置上有元素就使用equal比較是否相同

如果key相同就把新的value替換舊的value,結(jié)束

如果key不同,就繼續(xù)遍歷,找到根節(jié)點,如果沒找到key的話,就構(gòu)造一個新的節(jié)點,然后把節(jié)點插入到鏈表尾部,表示put成功(jdk 1.8 之后鏈表長度超過閾值就會轉(zhuǎn)化為紅黑樹)

3、HashMap,HashTable,ConcurrentHashMap的區(qū)別。

都是鍵值對存儲

hashmap線程不安全的,多線程環(huán)境下put會出現(xiàn)死循環(huán),因為node會形成環(huán)形結(jié)構(gòu)出現(xiàn)循環(huán)

hashtable?線程安全的,多線程下鎖競爭會出現(xiàn)阻塞

concurrenthashmap?線程安全,采用分段鎖技術,數(shù)據(jù)增加或者刪除的時候只鎖住部分數(shù)據(jù)

4、極高并發(fā)下HashTable和ConcurrentHashMap哪個性能更好,為什么,如何實現(xiàn)的。

HashTable使用一把鎖處理并發(fā)問題,當有多個線程訪問時,需要多個線程競爭一把鎖,導致阻塞

ConcurrentHashMap則使用分段,相當于把一個HashMap分成多個,然后每個部分分配一把鎖,這樣就可以支持多線程訪問

5、HashMap在高并發(fā)下如果沒有處理線程安全會有怎樣的安全隱患,具體表現(xiàn)是什么。

HashMap 在并發(fā)執(zhí)行 put 操作時會引起死循環(huán),導致 CPU 利用率接近100%。因為多線程會導致 HashMap 的 Node 鏈表形成環(huán)形數(shù)據(jù)結(jié)構(gòu),一旦形成環(huán)形數(shù)據(jù)結(jié)構(gòu),Node 的 next 節(jié)點永遠不為空,就會在獲取 Node 時產(chǎn)生死循環(huán)

6、java中四種修飾符的限制范圍。

private ->default-> protected ->public

7、Object類中的方法。

equals();

hashCode();

toString();

getClass()

8、接口和抽象類的區(qū)別,注意JDK8的接口可以有實現(xiàn)。

接口,關鍵字interface,只有方法沒有具體的實現(xiàn),默認都是抽象方法,不能被實例化,強調(diào)特定功能的實現(xiàn)

抽象類,關鍵字abstract,可以有方法可以有實現(xiàn),不能被實例化,抽象類強調(diào)所屬關系

9、動態(tài)代理的兩種方式,以及區(qū)別。

jdk動態(tài)代理?實現(xiàn)implementsInvocationHandler

cglib動態(tài)代理?實現(xiàn)implementsMethodInterceptor

10、Java序列化的方式。

實現(xiàn)Serializable,使對象能以流的形式在網(wǎng)絡中傳輸和保存在內(nèi)存中

11、傳值和傳引用的區(qū)別,Java是怎么樣的,有沒有傳值引用。

傳值:傳遞的是值的副本。方法中對副本的修改,不會影響到調(diào)用方。

傳引用:傳遞的是引用的副本,共用一個內(nèi)存,會影響到調(diào)用方。此時,形參和實參指向同一個內(nèi)存地址。對引用副本本身(對象地址)的修改,如設置為null,重新指向其他對象,不會影響到調(diào)用方

12、一個ArrayList在循環(huán)過程中刪除,會不會出問題,為什么

發(fā)生并發(fā)修改異常

2JVM

1、JVM的內(nèi)存結(jié)構(gòu)。

程序計數(shù)器,存放線程運行指令位置,線程私有

虛擬機堆,存放對象,

虛擬機棧,為虛擬機運行Java服務,存放程序運行棧幀。線程私有

本地方法棧,執(zhí)行本地native方法服務,線程私有

方法區(qū),存放類的信息,靜態(tài)方法,局部變量和常量,垃圾回收針對就是堆內(nèi)存和方法區(qū),不需要考慮程序計數(shù)器和虛擬機棧的垃圾回收,這些地方在類的屬性確定的時候就已經(jīng)創(chuàng)建好,隨著方法的生成而生成,隨著方法的結(jié)束而結(jié)束,

2、JVM方法棧的工作過程,方法棧和本地方法棧有什么區(qū)別。

方法棧存放的是程序運行的棧幀線程私有

本地方法服務的是本地native方法

3、JVM的棧中引用如何和堆中的對象產(chǎn)生關聯(lián)。

堆內(nèi)存復制存放對象,棧內(nèi)存獲取堆內(nèi)存的對象地址值得引用

4、可以了解一下逃逸分析技術。

5、GC的常見算法,CMS以及G1的垃圾回收過程,CMS的各個階段哪兩個是Stop the world的,CMS會不會產(chǎn)生碎片,G1的優(yōu)勢。

引用計數(shù)

6、標記清除和標記整理算法的理解以及優(yōu)缺點。

7、eden survivor區(qū)的比例,為什么是這個比例,eden survivor的工作過程。

幸存區(qū)和新生代,對象的引用沒有被立即回收掉的對象會進入到新生代中,新生代中的對象過段時間沒有被垃圾回收掉的會進入到老生代內(nèi)存區(qū)域

8、JVM如何判斷一個對象是否該被GC,可以視為root的都有哪幾種類型。

9、強軟弱虛引用的區(qū)別以及GC對他們執(zhí)行怎樣的操作。

10、Java是否可以GC直接內(nèi)存。

11、Java類加載的過程。

class文件通過類加載器進入到虛擬機內(nèi)部,

編譯加載連接運行

12、雙親委派模型的過程以及優(yōu)勢。

13、常用的JVM調(diào)優(yōu)參數(shù)。

14、dump文件的分析。

15、Java有沒有主動觸發(fā)GC的方式(沒有)。

3數(shù)據(jù)結(jié)構(gòu)與算法

1、B+樹

2、快速排序,堆排序,插入排序(八大排序算法)

3、一致性Hash算法,一致性Hash算法的應用

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

  • 本文出自 Eddy Wiki ,轉(zhuǎn)載請注明出處:http://eddy.wiki/interview-java.h...
    eddy_wiki閱讀 1,223評論 0 16
  • 九種基本數(shù)據(jù)類型的大小,以及他們的封裝類。(1)九種基本數(shù)據(jù)類型和封裝類 (2)自動裝箱和自動拆箱 什么是自動裝箱...
    關瑋琳linSir閱讀 2,049評論 0 47
  • Java SE 基礎: 封裝、繼承、多態(tài) 封裝: 概念:就是把對象的屬性和操作(或服務)結(jié)合為一個獨立的整體,并盡...
    Jayden_Cao閱讀 2,234評論 0 8
  • 相關概念 面向?qū)ο蟮娜齻€特征 封裝,繼承,多態(tài).這個應該是人人皆知.有時候也會加上抽象. 多態(tài)的好處 允許不同類對...
    東經(jīng)315度閱讀 2,191評論 0 8
  • 周一以為小兒子的發(fā)燒好了,沒成想周一晚上兒子開始發(fā)燒,半夜就聽到他哭,一摸燒到滾燙,一晚上就照顧他,都沒有合眼,這...
    Grace202004閱讀 119評論 0 1

友情鏈接更多精彩內(nèi)容