一.hashMap與hashTable與ConcurrentHashMap: 1.HashMap是繼承自AbstractMap類,而HashTable是繼承自Dictionary類。不過它們都同時(shí)實(shí)現(xiàn)了map、Cloneable(可復(fù)制)、Serializable(可序列化)這三個(gè)接口。<Dictionary類是一個(gè)已經(jīng)被廢棄的類> 2.Hashtable既不支持Null key也不支持Null value。HashMap中,null可以作為鍵,這樣的鍵只有一個(gè),可以有一個(gè)或多個(gè)鍵所對應(yīng)的值為null。 3.Hashtable是線程安全的,它的每個(gè)方法中都加入了Synchronize方法。在多線程并發(fā)的環(huán)境下,可以直接使用Hashtable,不需要自己為它的方法實(shí)現(xiàn) 同步,HashMap不是線程安全的,在多線程并發(fā)的環(huán)境下,可能會(huì)產(chǎn)生死鎖等問題。如果想要線程安全的 HashMap,可以通過Collections類的靜態(tài)方法synchronize dMap獲得線程安全的HashMap。 <Map map = 4.hashMap的數(shù)據(jù)結(jié)構(gòu):HashMap的底層主要是基于數(shù)組和鏈表來實(shí)現(xiàn)的,它之所以有相當(dāng)快的查詢速度主要是因?yàn)樗峭ㄟ^計(jì)算散列碼來決定存儲(chǔ)的位置。 5.ConcurrentHashMap:底層采用分段的數(shù)組+鏈表實(shí)現(xiàn),線程安全ConcurrentHashMap允許多個(gè)修改操作并發(fā)進(jìn)行,其關(guān)鍵在于使用了鎖分離技術(shù)。它使用了多個(gè)鎖來控制對hash表的不同部分進(jìn)行的修改。ConcurrentHashMap內(nèi)部使用段(Segment)來表示這些不同的部分,每個(gè)段其實(shí)就是一個(gè)小的Hashtable,它們有自己的鎖。只要多個(gè)修改操作發(fā)生在不同的段上,它們就可以并發(fā)進(jìn)行。 JDK1.8的實(shí)現(xiàn)已經(jīng)摒棄了Segment的概念,而是直接用Node數(shù)組+鏈表+紅黑樹的數(shù)據(jù)結(jié)構(gòu)來實(shí)現(xiàn),此時(shí)鎖加在key上,并發(fā)控制使用Synchronized和CAS來操作,整個(gè)看起來就像是優(yōu)化過且線程安全的HashMap,雖然在JDK1.8中還能看到Segment的數(shù)據(jù)結(jié)構(gòu),但是已經(jīng)簡化了屬性,只是為了兼容舊版本。 二.(String)、toString、String.valueOf的區(qū)別 1.(String):使用這種方法時(shí),需要注意的是類型必須能轉(zhuǎn)成String類型。因此最好用instanceof做個(gè)類型檢查,以判斷是否可以轉(zhuǎn)換。instanceof 運(yùn)算符是用來在運(yùn)行時(shí)指出對象是否是特定類的一個(gè)實(shí)例, 父類parent,子類son, 此時(shí) flag= son instanceof parent,flag=true。 2.toString:String s = Object.toString(),在使用時(shí)要注意,必須保證object不是null值,否則將拋出NullPointerException異常。 3.String.valueOf: 內(nèi)部實(shí)現(xiàn): public static String valueOf(Object obj){ return (obj==null) ? 'null' : obj.toString() }; 當(dāng)Object不為空時(shí),調(diào)用toString()方法,當(dāng)Object為null時(shí),則返回一個(gè)字符串'null'?。。?! 三.字節(jié)流與字符流的區(qū)別 字節(jié)流:InputStream,OutputStream,程序→文件 字符流:BufferedRead,BufferedWrite,程序→緩存區(qū)→文件 字符流需要關(guān)閉字符流,緩存區(qū)的數(shù)據(jù)才會(huì)被寫入文件,否則會(huì)一直堆在緩存區(qū)?;蛘呖梢杂胒lush()方法,將數(shù)據(jù)強(qiáng)行寫入文件。 利用BufferedRead讀取文件: public static void main(String[] args) throws Exception { String s= 'F:/456.txt'; File f =new File(s); FileReader fr = new FileReader(f); BufferedReader br = new BufferedReader(fr); String temp=''; while((temp=br.readLine())!=null) { System.out.println(temp); } } 四.Integer 源代碼: private static class IntegerCache {//靜態(tài)緩存類 static final int low = -128; static final int high; static final Integer cache[]; static { //靜態(tài)代碼塊 // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty('java.lang.Integer.IntegerCache.high'); if (integerCacheHighPropValue != null) { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); } private IntegerCache() {} } 這個(gè)類就是在Integer類裝入內(nèi)存中時(shí),會(huì)執(zhí)行其內(nèi)部類中靜態(tài)代碼塊進(jìn)行其初始化工作,做的主要工作就是把一字節(jié)的整型數(shù)據(jù)(-128,127)裝包成Integer類并把其對應(yīng)的引用存入cache數(shù)組中,這樣在方法區(qū)中開辟空間存放這些靜態(tài)Integer變量,同時(shí)靜態(tài)cache數(shù)組也存放在這里,供線程享用,這也稱靜態(tài)緩存。 所以當(dāng)用Integer聲明初始化變量時(shí),會(huì)先判斷所賦值的大小是否在-128到127之間,若在,則利用靜態(tài)緩存中的空間并且返回對應(yīng)cache數(shù)組中對應(yīng)引用,存放到運(yùn)行棧中,而不再重新開辟內(nèi)存。若不在則new 一個(gè)新的對象放入堆中。 五.類的初始化過程 /*父類*/ public class Person { public Person() { System.out.println('im person_1'); } { System.out.println('im person_2'); } static { System.out.println('im person_3'); } } /*子類*/ public class test extends Person { public test() { System.out.println('im test_1'); } { System.out.println('im test_2'); } static { System.out.println('im test_3'); } public static void main(String[] args) throws Exception { new test(); } } 輸出: im person_3 im test_3 im person_2 im person_1 im test_2 im test_1 解釋:在類中變量初始化時(shí),順序?yàn)?static→變量→構(gòu)造方法。 六.值傳遞,引用傳遞 public class test { String s='hello'; char[] ch={'a','b','c'}; Character ck='k'; public static void main(String[] args) throws Exception { test tt = new test(); tt.change(tt.s,tt.ch,tt.ck); System.out.println('--------'); System.out.println('s+'+tt.s.hashCode()); System.out.println('ch+'+tt.ch.hashCode()); System.out.println('ck+'+tt.ck.hashCode()); System.out.println('--------'); System.out.println(tt.s); System.out.println(tt.ch); System.out.println(tt.ck); } public void change(String str,char[] ch,Character ck){ str='world'; ch[0]='d'; ck='c'; System.out.println('str+'+str.hashCode()); System.out.println('ch+'+ch.hashCode()); System.out.println('ckl+'+ck.hashCode()); } } 輸出: str+113318802 ch+1828682968 ckl+99 -------- s+99162322 ch+1828682968 ck+107 -------- hello dbc k 可見,String類型是不會(huì)被修改的,在編譯時(shí),方法棧里有world,如果是輸入賦值給String應(yīng)該會(huì)變,char數(shù)組傳遞的是數(shù)組的引用,Character傳遞的是值 傳值不會(huì)修改原來的,傳引用會(huì)修改原來的。 七.i++與++i public static void main(String[] args) throws Exception { int a=1; int b=a++; //先執(zhí)行b=a,再執(zhí)行a++ System.out.println(b++); //先執(zhí)行print(b),再執(zhí)行b++ } 輸出:1 八.==與equals的區(qū)別 ==: 1.在==中,如果比較的是int,long,short這種基本數(shù)據(jù)類型,那么==比較的是它們的值 2.若比較的引用數(shù)據(jù)類型,如類,String,那么比較的是它們的內(nèi)存地址,除非是同一個(gè)new一個(gè)出來的對象,此時(shí)地址相同,返回ture,否則返回false 如: String a= new String('abc'); String b=a; sout(a==b); //ture 若: String c= new String('c'); String c1= 'c'; sout(c==c1);//false equals: 1.所有類都繼承自O(shè)bject,若不重寫equals()方法,那么調(diào)用Object類中的equals()方法,源代碼: public boolean equals(Object obj) { return (this == obj); } 也就是仍然是比較其地址。 2.若重寫其方法: 在String中: 源代碼: public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String) anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; } 可以看出equals()是會(huì)先用==方法,然后比較兩個(gè)String的值是否相等。 九.final,static關(guān)鍵字 final: 當(dāng)用final修飾一個(gè)類時(shí),表明這個(gè)類不能被繼承,比如出于安全的考慮,可修飾為final。 如果只有在想明確禁止該方法在子類中被覆蓋的情況下才將方法設(shè)置為final的。 對于一個(gè)final變量,如果是基本數(shù)據(jù)類型的變量,則其數(shù)值一旦在初始化之后便不能更改;如果是引用類型的變量,則在對其初始化之后便不能再讓其指向另一個(gè)對象。 static: 修飾類的成員變量時(shí),每個(gè)類只有一個(gè)這個(gè)成員,而不是每個(gè)類的實(shí)例化對象都有一個(gè)這個(gè)變量。 用static修飾后,類名.方法名,類名.屬性名,可直接調(diào)用,不用實(shí)例化對象,避免了先要new出對象的繁瑣和資源消耗。 在創(chuàng)建對象時(shí),static修飾的成員會(huì)首先被初始化。 十.sleep() 和 wait() 有什么區(qū)別? sleep就是正在執(zhí)行的線程主動(dòng)讓出cpu,cpu去執(zhí)行其他線程,在sleep指定的時(shí)間過后,cpu才會(huì)回到這個(gè)線程上繼續(xù)往下執(zhí)行,如果當(dāng)前線程進(jìn)入了同步鎖,sleep方法并不會(huì)釋放鎖,即使當(dāng)前線程使用sleep方法讓出了cpu,但其他被同步鎖擋住了的線程也無法得到執(zhí)行。wait是指在一個(gè)已經(jīng)進(jìn)入了同步鎖的線程內(nèi),讓自己暫時(shí)讓出同步鎖,以便其他正在等待此鎖的線程可以得到同步鎖并運(yùn)行,只有其他線程調(diào)用了notify方法(notify并不釋放鎖,只是告訴調(diào)用過wait方法的線程可以去參與獲得鎖的競爭了,但不是馬上得到鎖,因?yàn)殒i還在別人手里,別人還沒釋放。如果notify方法后面的代碼還有很多,需要這些代碼執(zhí)行完后才會(huì)釋放鎖,可以在notfiy方法后增加一個(gè)等待和一些代碼,看看效果),調(diào)用wait方法的線程就會(huì)解除wait狀態(tài)和程序可以再次得到鎖后繼續(xù)向下運(yùn)行。 十一.得到文件下的文件 public static void FileRead(String path)throws Exception{ File f= new File(path); if(!f.exists()) System.out.println('file not exist'); if (f.isDirectory()) { File[] ss = f.listFiles(); for (File s : ss) { System.out.println(s.getPath()); // F:\txt\1.txt } }else{ System.out.println(f.getName()); } } 十二.實(shí)現(xiàn)多線程的三種方法 1.繼承thread類創(chuàng)建線程 public class MyThread extends Thread { public void run() { System.out.println('MyThread.run()'); } } MyThread myThread1 = new MyThread(); myThread1.start(); 2.實(shí)現(xiàn)runnable接口創(chuàng)建線程 public class MyThread extends OtherClass implements Runnable { public void run() { System.out.println('MyThread.run()'); } } 3.實(shí)現(xiàn)Callable接口通過FutureTask包裝器來創(chuàng)建Thread線程 PS:別說四種,第四種無法理解 十三.抽象類與接口的區(qū)別 主要是:單繼承(抽象類),多實(shí)現(xiàn)(接口)。 抽象類不能直接實(shí)例化對象,需要繼承抽象類才能實(shí)例化其子類。 從使用上來看,一個(gè)類可以實(shí)現(xiàn)多個(gè)接口,但是不能繼承多個(gè)抽象類。 接口的字段只能是 static 和 final 類型的,而抽象類的字段沒有這種限制。 接口的成員只能是 public 的,而抽象類的成員可以有多種訪問權(quán)限。 十四.String Pool String pool 指字符串常量池,保存著所有在編譯時(shí)就已經(jīng)確定的String變量。調(diào)用String.intern()方法,可以將此String變量加入常量池中。 String pool在堆中。 String a= new String('a'); String a1= new String('a'); sout(a==a1);//false String b='b'; String b1='b'; sout(b==b1);//true String c= new String('c'); String c1= 'c'; sout(c==c1);//false sout(c.equals(c1));//true 詳情見 八. ==與equals的區(qū)別 十五.ArrayList和Vector的區(qū)別 這兩個(gè)類都實(shí)現(xiàn)了List接口(List接口繼承了Collection接口),他們都是有序集合,即存儲(chǔ)在這兩個(gè)集合中的元素的位置都是有順序的,相當(dāng)于一種動(dòng)態(tài)的數(shù)組,我們以后可以按位置索引號取出某個(gè)元素,并且其中的數(shù)據(jù)是允許重復(fù)的。 (1)同步性: Vector是線程安全的,也就是說是它的方法之間是線程同步的,而ArrayList是線程序不安全的,它的方法之間是線程不同步的。如果只有一個(gè)線程會(huì)訪問到集合,那最好是使用ArrayList,因?yàn)樗豢紤]線程安全,效率會(huì)高些;如果有多個(gè)線程會(huì)訪問到集合,那最 好是使用Vector,因?yàn)椴恍枰覀冏约涸偃タ紤]和編寫線程安全的代碼。 備注:對于Vector&ArrayList、Hashtable&HashMap,要記住線程安全的問題,記住Vector與Hashtable是舊的,是java一誕生就提供了的,它們是線程安全的,ArrayList與HashMap是java2時(shí)才提供的,它們是線程不安全的。所以,我們講課時(shí)先講老的。 (2)數(shù)據(jù)增長: ArrayList與Vector都有一個(gè)初始的容量大小,當(dāng)存儲(chǔ)進(jìn)它們里面的元素的個(gè)數(shù)超過了容量時(shí),就需要增加ArrayList與Vector的存儲(chǔ)空間,每次要增加存儲(chǔ)空間時(shí),不是只增加一個(gè)存儲(chǔ)單元,而是增加多個(gè)存儲(chǔ)單元,每次增加的存儲(chǔ)單元的個(gè)數(shù)在內(nèi)存空間利用與程序效率之間要取得一定的平衡。Vector默認(rèn)增長為原來兩倍,而ArrayList的增長策略在文檔中沒有明確規(guī)定(從源代碼看到的是增長為原來的1.5倍)。ArrayList與Vector都可以設(shè)置初始的空間大小,Vector還可以設(shè)置增長的空間大小,而ArrayList沒有提供設(shè)置增長空間的方法。 總結(jié):即Vector增長原來的一倍,ArrayList增加原來的0.5倍。 十六.Java 中Collections類里的reverse (反轉(zhuǎn)方法) public static void reverse(List<?> list) { int size = list.size(); if (size < REVERSE_THRESHOLD || list instanceof RandomAccess) { for (int i=0, mid=size>>1, j=size-1; i<mid; i++, j--) swap(list, i, j); } else { ListIterator fwd = list.listIterator(); ListIterator rev = list.listIterator(size); for (int i=0, mid=list.size()>>1; i<mid; i++) { Object tmp = fwd.next(); fwd.set(rev.previous()); rev.set(tmp); } } } 此方法可反轉(zhuǎn)數(shù)組的值. |
|