若是Long的值在[-127,128]之間,用「==」判斷是否相等是沒問題的,若是不在這個區間,是不能用「==」的,緣由以下源碼解釋:java
public static Long valueOf(long l) { final int offset = 128; if (l >= -128 && l <= 127) { // will cache return LongCache.cache[(int)l + offset]; } return new Long(l); }
若是不在[-127,128]之間,則會new一個新對象,天然「==」兩個不一樣的對象,其結果必然是false了。web
解決辦法:
a. : 使用Long中的longValue()進行轉換算法
Long a = 128l; Long b = 128l; a.longValue() == b.longValue() //true
b. : Long中的equals()sql
public boolean equals(Object obj) { if (obj instanceof Long) { return value == ((Long)obj).longValue(); } return false; }
http://www.javashuo.com/article/p-yopmvqtc-he.html
進程是資源分配的最小單位,線程是CPU調度的最小單位數據庫
線程之間如何實現資源共享:數組
對於==:若是做用於基本數據類型的變量,則直接比較其存儲的 「值」是否相等;
若是做用於引用類型的變量,則比較的是所指向的對象的地址緩存
對於equals方法:注意:equals方法不能做用於基本數據類型的變量
若是沒有對equals方法進行重寫,則比較的是引用類型的變量所指向的對象的地址;
諸如String、Date等類對equals方法進行了重寫的話,比較的是所指向的對象的內容。安全
hashCode()方法:Object類中的本地方法,也用來判斷兩個對象是否相等。
若是沒有重寫hashCode()方法,該方法返回的是對象在內存中的地址轉換成的int值,所以,任何對象的hashCode()方法值是不相等的。網絡
equals()方法也是判斷兩個對象是否相等,可是與hashCode()方法有區別。通常來說,equals()方法是給用戶調用的,依據狀況能夠選擇重寫或者不重寫。對於hashCode()方法,用戶通常不會去調用,hashCode至關於根據必定的規則將與對象相關的信息映射爲一個數值,稱爲散列值。通常在在覆蓋equals()方法的同時也要覆蓋hashCode()方法,不然將會違反Object hashCode的通用約定,從而致使該類沒法與全部基於散列值的集合類結合在一塊兒正常工做。多線程
Object hashCode()方法的通用約定:
淺拷貝:使用一個已知實例對新建立實例的成員變量逐個賦值,這個方式被稱爲淺拷貝。
深拷貝:當一個類的拷貝構造方法,不只要複製對象的全部非引用成員變量值,還要爲引用類型的成員變量建立新的實例,而且初始化爲形式參數實例值。
也就是說淺拷貝只複製一個對象,傳遞引用,不能複製實例。而深拷貝對對象內部的引用均複製,它是建立一個新的實例,而且複製實例。對於淺拷貝當對象的成員變量是基本數據類型時,兩個對象的成員變量已有存儲空間,賦值運算傳遞值,因此淺拷貝可以複製實例。可是當對象的成員變量是引用數據類型時,就不能實現對象的複製了
String 構成:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence{ /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0 ...
源碼咱們能夠知道String底層是由char 數組構成的,咱們建立一個字符串對象的時候,實際上是將字符串保存在char數組中,由於數組是引用對象,爲了防止數組可變,jdk加了final修飾,加了final修飾的數組只是表明了引用不可變,不表明數組的內容不可變,所以jdk爲了真正防止不可變,又加了一個private修飾符。
說到String 不得不提字符串常量池,這個常量池主要存儲在方法區中,當一個字符串被建立的時候,首先會去常量池中查找,若是找到了就返回對改字符串的引用,若是沒找到就建立這個字符串並塞到常量池中。
下面代碼只會在:
String s1 ="abc"; String s2 ="abc";
試想一下若是String是可變的,當兩個引用指向指向同一個字符串時,對其中一個作修改就會影響另一個。
對於Java而言,除了基本類型(即int, long, double等),其他的都是對象。
對於何爲不可變對象,《java concurrency in practice》一書給出了一個粗略的定義:對象一旦建立後,其狀態不可修改,則該對象爲不可變對象。
通常一個對象知足如下三點,則能夠稱爲是不可變對象:
這裏重點說明一下第2點,一個對象其全部域都是final類型,該對象也多是可變對象。由於final關鍵字只是限制對象的域的引用不可變,但沒法限制經過該引用去修改其對應域的內部狀態。所以,嚴格意義上的不可變對象,其final關鍵字修飾的域應該也是不可變對象和primitive type值。
從技術上講,不可變對象內部域並不必定全都聲明爲final類型,String類型便是如此。在String對象的內部咱們能夠看到有一個名爲hash的域並非final類型,這是由於String類型惰性計算hashcode並存儲在hash域中(這是經過其餘final類型域來保證每次的hashcode計算結果一定是相同的)。除此以外,String對象的不可變是因爲對String類型的全部改變內部存儲結構的操做都會new出一個新的String對象。
一、多線程安全性由於String是不可變的,所以在多線程操做下,它是安全的,咱們看下以下代碼:
public String get(String str){ str +="aaa"; return str; }
試想一下若是String是可變的,那麼get方法內部改變了str的值,方法外部str也會隨之改變。
二、類加載中體現的安全性類加載器要用到字符串,不可變性提供了安全性,以便正確的類被加載。
譬如你想加載java.sql. Connection類,而這個值被改爲了hacked.Connection,那麼會對你的數據庫形成不可知的破壞。
只有當字符串是不可變的,字符串池纔有可能實現。字符串池的實現能夠在運行時節約不少heap空間,由於不一樣的字符串變量都指向池中的同一個字符串。但若是字符串是可變的,那麼String interning將不能實現(String interning是指對不一樣的字符串僅僅只保存一個,即不會保存多個相同的字符串),由於這樣的話,若是變量改變了它的值,那麼其它指向這個值的變量的值也會一塊兒改變
由於字符串是不可變的,因此在它建立的時候hashcode就被緩存了,不須要從新計算。這就使得字符串很適合做爲Map中的鍵,字符串的處理速度要快過其它的鍵對象。這就是HashMap中的鍵每每都使用字符串。咱們能夠看到String中有以下代碼:
private int hash;//this is used to cache hash code.
以上代碼中hash變量中就保存了一個String對象的hashcode,由於String類不可變,因此一旦對象被建立,該hash值也沒法改變。因此,每次想要使用該對象的hashcode的時候,直接返回便可。
不可變對象也有一個缺點就是會製造大量垃圾,因爲他們不能被重用並且對於它們的使用就是」用「而後」扔「,字符串就是一個典型的例子,它會創造不少的垃圾,給垃圾收集帶來很大的麻煩。固然這只是個極端的例子,合理的使用不可變對象會創造很大的價值。
因爲String在Java中是不可變的,若是你將密碼以明文的形式保存成字符串,那麼它將一直留在內存中,直到垃圾收集器把它清除。而因爲字符串被放在字符串緩衝池中以方便重複使用,因此它就可能在內存中被保留很長時間,而這將致使安全隱患,由於任何可以訪問內存(memorydump內存轉儲)的人都能清晰的看到文本中的密碼,這也是爲何你應該老是使用加密的形式而不是明文來保存密碼。因爲字符串是不可變的,因此沒有任何方式能夠修改字符串的值,由於每次修改都將產生新的字符串,然而若是你使用char[]來保存密碼,你仍然能夠將其中全部的元素都設置爲空或者零。因此將密碼保存到字符數組中很明顯的下降了密碼被竊取的風險。固然只使用字符數組也是不夠的,爲了更安全你須要將數組內容進行轉化。建議使用哈希的或者是加密過的密碼而不是明文,而後一旦完成驗證,就將它從內存中清除掉
一旦一個string對象在內存(堆)中被建立出來,他就沒法被修改。特別要注意的是,String類的全部方法都沒有改變字符串自己的值,都是返回了一個新的對象。
若是你須要一個可修改的字符串,應該使用StringBuffer或者StringBuilder。不然會有大量時間浪費在垃圾回收上,由於每次試圖修改都有新的string對象被建立出來。
查看API會發現,String、StringBuffer、StringBuilder都實現了 CharSequence接口,雖然它們都與字符串相關,可是其處理機制不一樣。
String:是不可改變的量,也就是建立後就不能在修改了。
StringBuffer:是一個可變字符串序列,它與String同樣,在內存中保存的都是一個有序的字符串序列(char類型的數組),不一樣點是StringBuffer對象的值是可變的。
StringBuilder:與StringBuffer類基本相同,都是可變字符串序列,不一樣點是StringBuffer是線程安全的,StringBuilder是線程不安全的。 在性能方面,因爲String類的操做是產生新的String對象,而StringBuilder和StringBuffer只是一個字符數組的擴容而已,因此String類的操做要遠慢於StringBuffer和StringBuilder。
使用String類的場景:在字符串不常常變化的場景中可使用String類,例如常量的聲明、少許的變量運算。共享的場合
使用StringBuffer類的場景:在頻繁進行字符串運算(如拼接、替換、刪除等),而且運行在多線程環境中,則能夠考慮使用StringBuffer,例如XML解析、HTTP參數解析和封裝。
使用StringBuilder類的場景:在頻繁進行字符串運算(如拼接、替換、和刪除等),而且運行在單線程的環境中,則能夠考慮使用StringBuilder,如SQL語句的拼裝、JSON封裝等。
簡要的說,String 類型和 StringBuffer 類型的主要性能區別其實在於 String 是不可變的對象, 所以在每次對 String 類型進行改變的時候其實都等同於生成了一個新的 String 對象,而後將指針指向新的 String 對象。因此常常改變內容的字符串最好不要用 String,由於每次生成對象都會對系統性能產生影響,特別當內存中無引用對象多了之後,JVM 的 GC 就會開始工做,那速度是必定會至關慢的。
而若是是使用StringBuffer類則結果就不同了,每次結果都會對 StringBuffer 對象自己進行操做,而不是生成新的對象,再改變對象引用。因此在通常狀況下咱們推薦使用 StringBuffer,特別是字符串對象常常改變的狀況下。
在某些特別狀況下, String 對象的字符串拼接實際上是被 JVM 解釋成了 StringBuffer 對象的拼接,因此這些時候 String 對象的速度並不會比 StringBuffer 對象慢,而特別是如下的字符串對象生成中, String 效率是遠要比 StringBuffer 快的:
String S1 = "This is only a" + "simple" + "test"; StringBuffer Sb =new StringBuilder(「This is only a").append("simple").append(" test");
你會很驚訝的發現,生成 String S1 對象的速度簡直太快了,而這個時候 StringBuffer 竟然速度上根本一點都不佔優點。其實這是 JVM 的一個把戲,在 JVM 眼裏,這個
String S1 = 「This is only a" + 「 simple" + 「test";
其實就是:String S1 = 「This is only a simple test";
因此固然不須要太多的時間了。但你們這裏要注意的是,若是你的字符串是來自另外的 String 對象的話,速度就沒那麼快了,譬如:
String S2 = "This is only a"; String S3 = "simple"; String S4 = "test"; String S1 = S2 +S3 + S4;
這時候 JVM 會規規矩矩的按照原來的方式去作。
在大部分狀況下 StringBuffer > String
Java.lang.StringBuffer是線程安全的可變字符序列。一個相似於 String 的字符串緩衝區,但不能修改。雖然在任意時間點上它都包含某種特定的字符序列,但經過某些方法調用能夠改變該序列的長度和內容。在程序中可將字符串緩衝區安全地用於多線程。並且在必要時能夠對這些方法進行同步,所以任意特定實例上的全部操做就好像是以串行順序發生的,該順序與所涉及的每一個線程進行的方法調用順序一致。
StringBuffer 上的主要操做是 append 和 insert 方法,可重載這些方法,以接受任意類型的數據。每一個方法都能有效地將給定的數據轉換成字符串,而後將該字符串的字符追加或插入到字符串緩衝區中。append 方法始終將這些字符添加到緩衝區的末端;而 insert 方法則在指定的點添加字符。
例如,若是 z 引用一個當前內容是「start」的字符串緩衝區對象,則此方法調用 z.append(「le」) 會使字符串緩衝區包含「startle」(累加);而 z.insert(4, 「le」) 將更改字符串緩衝區,使之包含「starlet」。
在大部分狀況下 StringBuilder > StringBuffer
java.lang.StringBuilder是一個可變的字符序列,是JAVA 5.0新增的。此類提供一個與 StringBuffer 兼容的 API,但不保證同步,因此使用場景是單線程。該類被設計用做 StringBuffer 的一個簡易替換,用在字符串緩衝區被單個線程使用的時候(這種狀況很廣泛)。若是可能,建議優先採用該類,由於在大多數實現中,它比 StringBuffer 要快。二者的使用方法基本相同。
Java序列化是指把Java對象保存爲二進制字節碼的過程,Java反序列化是指把二進制碼從新轉換成Java對象的過程。
1.java對象的生命週期要比java虛擬機短,實際應用中但願虛擬機中止運行以後可以持久化指定的對象,此時能夠將對象序列化保存;
2.java對象經過網絡傳輸的時候,由於數據只能以二進制的形式在網絡中進行傳輸,所以當對象經過網絡發送出去以前,須要先序列化爲二進制數據,在接收端收到二進制數據以後反序列化成二進制對象。
在序列化的時候使用默認的方式來進行序列化,這種序列化方式僅僅對對象的非transient的實例變量進行序列化,而不會序列化對象的transient的實例變量,也不會序列化靜態變量,因此咱們對不想持久化的變量能夠加上transient關鍵字。注意使用默認機制,在序列化對象時,不只會序列化當前對象自己,還會對該對象引用的其它對象也進行序列化,一樣地,這些其它對象引用的另外對象也將被序列化,以此類推。因此,若是一個對象包含的成員變量是容器類對象,而這些容器所含有的元素也是容器類對象,那麼這個序列化的過程就會較複雜,開銷也較大。若是須要實現自定義序列化和反序列化,那麼須要重寫writeObject()方法和readObject()方法。在序列化過程當中,若是被序列化的類中定義了writeObject 和 readObject 方法,將會使用反射的方式調用自定義的 writeObject 和 readObject 方法,進行用戶自定義的序列化和反序列化。
Serializable既能夠採用默認的序列化和反序列化方式,也可使用用戶自定義的序列化和反序列化的方式。
Externalizable序列化,雖然Externalizable接口繼承自Serializable接口,可是須要序列化類繼承此接口的話,Serializable全部序列化機制所有失效。Externalizable序列化的過程:使用Externalizable序列化時,在進行反序列化的時候,會從新實例化一個對象,而後再將被反序列化的對象的狀態所有複製到這個新的實例化對象當中去,所以必須有一個無參構造方法供其調用,而且權限是public。
Java序列化和反序列化本質上是將對象信息生成一串二進制字節碼和從二進制字節碼解析的過程。序列化算法:
1)當前類的描述
2)當前類屬性的描述
3)父類描述
4)父類屬性描述
5)父類屬性值描述
6)子類屬性值描述
類描述是從下到上,類屬性描述是從上到下。
面向對象的三個特徵:封裝、繼承、多態
封裝:封裝是將客觀事物抽象成類,每一個類都包含自身的數據以及操做,沒必要須要其餘的類來完成操做。類內部的實現能夠自由的修改;具備清晰的對外接口。良好的封裝可以減小耦合;
繼承:繼承是從已有的類中派生出新的類稱爲子類,子類繼承父類的屬性和行爲,並可以根據本身的需求擴展新的行爲,提供了代碼的複用性。
多態:多態容許不一樣類的對象對同一消息作出響應。提供繼承關係,子類重寫父類的方法;父類的引用執行子類的對象;在調用父類的方法是實際上表現的是子類的狀態。
從內存角度考慮:
局部變量在棧內存中存在,當for循環語句結束,那麼變量會及時被gc(垃圾回收器)及時的釋放掉,不浪費空間
從應用場景角度考慮:
若是一個需求明確循環的次數,那麼使用for循環(開發中使用for循環的概率大於while循環)