一.hashMap與hashTable與ConcurrentHashMap: 1.HashMap是繼承自AbstractMap類,而HashTable是繼承自Dictionary類。不過它們都同時實現了map、Cloneable(可複製)、Serializable(可序列化)這三個介面。<Dicti ...
一.hashMap與hashTable與ConcurrentHashMap:
1.HashMap是繼承自AbstractMap類,而HashTable是繼承自Dictionary類。不過它們都同時實現了map、Cloneable(可複製)、Serializable(可序列化)這三個介面。<Dictionary類是一個已經被廢棄的類>
2.Hashtable既不支持Null key也不支持Null value。HashMap中,null可以作為鍵,這樣的鍵只有一個,可以有一個或多個鍵所對應的值為null。
3.Hashtable是線程安全的,它的每個方法中都加入了Synchronize方法。在多線程併發的環境下,可以直接使用Hashtable,不需要自己為它的方法實現 同步,HashMap不是線程安全的,在多線程併發的環境下,可能會產生死鎖等問題。如果想要線程安全的 HashMap,可以通過Collections類的靜態方法synchronize dMap獲得線程安全的HashMap。 <Map map = Collections.synchronizedMap(new HashMap())>;
4.hashMap的數據結構:HashMap的底層主要是基於數組和鏈表來實現的,它之所以有相當快的查詢速度主要是因為它是通過計算散列碼來決定存儲的位置。
5.ConcurrentHashMap:底層採用分段的數組+鏈表實現,線程安全ConcurrentHashMap允許多個修改操作併發進行,其關鍵在於使用了鎖分離技術。它使用了多個鎖來控制對hash表的不同部分進行的修改。ConcurrentHashMap內部使用段(Segment)來表示這些不同的部分,每個段其實就是一個小的Hashtable,它們有自己的鎖。只要多個修改操作發生在不同的段上,它們就可以併發進行。
JDK1.8的實現已經摒棄了Segment的概念,而是直接用Node數組+鏈表+紅黑樹的數據結構來實現,此時鎖加在key上,併發控制使用Synchronized和CAS來操作,整個看起來就像是優化過且線程安全的HashMap,雖然在JDK1.8中還能看到Segment的數據結構,但是已經簡化了屬性,只是為了相容舊版本。
二.(String)、toString、String.valueOf的區別
1.(String):使用這種方法時,需要註意的是類型必須能轉成String類型。因此最好用instanceof做個類型檢查,以判斷是否可以轉換。instanceof 運算符是用來在運行時指出對象是否是特定類的一個實例,
父類parent,子類son, 此時 flag= son instanceof parent,flag=true。
2.toString:String s = Object.toString(),在使用時要註意,必須保證object不是null值,否則將拋出NullPointerException異常。
3.String.valueOf:
內部實現:
public static String valueOf(Object obj){
return (obj==null) ? "null" : obj.toString()
};
當Object不為空時,調用toString()方法,當Object為null時,則返回一個字元串"null"!!!!
三.位元組流與字元流的區別
位元組流:InputStream,OutputStream,程式→文件
字元流:BufferedRead,BufferedWrite,程式→緩存區→文件
字元流需要關閉字元流,緩存區的數據才會被寫入文件,否則會一直堆在緩存區。或者可以用flush()方法,將數據強行寫入文件。
利用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 {//靜態緩存類
static final int low = -128;
static final int high;
static final Integer cache[];
static { //靜態代碼塊
// 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() {}
}
這個類就是在Integer類裝入記憶體中時,會執行其內部類中靜態代碼塊進行其初始化工作,做的主要工作就是把一位元組的整型數據(-128,127)裝包成Integer類並把其對應的引用存入cache數組中,這樣在方法區中開闢空間存放這些靜態Integer變數,同時靜態cache數組也存放在這裡,供線程享用,這也稱靜態緩存。
所以當用Integer聲明初始化變數時,會先判斷所賦值的大小是否在-128到127之間,若在,則利用靜態緩存中的空間並且返回對應cache數組中對應引用,存放到運行棧中,而不再重新開闢記憶體。若不在則new 一個新的對象放入堆中。
五.類的初始化過程
/*父類*/
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
解釋:在類中變數初始化時,順序為 static→變數→構造方法。
六.值傳遞,引用傳遞
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類型是不會被修改的,在編譯時,方法棧里有world,如果是輸入賦值給String應該會變,char數組傳遞的是數組的引用,Character傳遞的是值
傳值不會修改原來的,傳引用會修改原來的。
七.i++與++i
public static void main(String[] args) throws Exception {
int a=1;
int b=a++; //先執行b=a,再執行a++
System.out.println(b++); //先執行print(b),再執行b++
}
輸出:1
八.==與equals的區別
==:
1.在==中,如果比較的是int,long,short這種基本數據類型,那麼==比較的是它們的值
2.若比較的引用數據類型,如類,String,那麼比較的是它們的記憶體地址,除非是同一個new一個出來的對象,此時地址相同,返回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.所有類都繼承自Object,若不重寫equals()方法,那麼調用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()是會先用==方法,然後比較兩個String的值是否相等。
九.final,static關鍵字
final:
當用final修飾一個類時,表明這個類不能被繼承,比如出於安全的考慮,可修飾為final。
如果只有在想明確禁止該方法在子類中被覆蓋的情況下才將方法設置為final的。
對於一個final變數,如果是基本數據類型的變數,則其數值一旦在初始化之後便不能更改;如果是引用類型的變數,則在對其初始化之後便不能再讓其指向另一個對象。
static:
修飾類的成員變數時,每個類只有一個這個成員,而不是每個類的實例化對象都有一個這個變數。
用static修飾後,類名.方法名,類名.屬性名,可直接調用,不用實例化對象,避免了先要new出對象的繁瑣和資源消耗。
在創建對象時,static修飾的成員會首先被初始化。
十.sleep() 和 wait() 有什麼區別?
sleep就是正在執行的線程主動讓出cpu,cpu去執行其他線程,在sleep指定的時間過後,cpu才會回到這個線程上繼續往下執行,如果當前線程進入了同步鎖,sleep方法並不會釋放鎖,即使當前線程使用sleep方法讓出了cpu,但其他被同步鎖擋住了的線程也無法得到執行。wait是指在一個已經進入了同步鎖的線程內,讓自己暫時讓出同步鎖,以便其他正在等待此鎖的線程可以得到同步鎖並運行,只有其他線程調用了notify方法(notify並不釋放鎖,只是告訴調用過wait方法的線程可以去參與獲得鎖的競爭了,但不是馬上得到鎖,因為鎖還在別人手裡,別人還沒釋放。如果notify方法後面的代碼還有很多,需要這些代碼執行完後才會釋放鎖,可以在notfiy方法後增加一個等待和一些代碼,看看效果),調用wait方法的線程就會解除wait狀態和程式可以再次得到鎖後繼續向下運行。
十一.得到文件下的文件
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());
}
}
十二.實現多線程的三種方法
1.繼承thread類創建線程
public class MyThread extends Thread {
public void run() {
System.out.println("MyThread.run()");
}
}
MyThread myThread1 = new MyThread();
myThread1.start();
2.實現runnable介面創建線程
public class MyThread extends OtherClass implements Runnable {
public void run() {
System.out.println("MyThread.run()");
}
}
3.實現Callable介面通過FutureTask包裝器來創建Thread線程
PS:別說四種,第四種無法理解
十三.抽象類與介面的區別
主要是:單繼承(抽象類),多實現(介面)。
抽象類不能直接實例化對象,需要繼承抽象類才能實例化其子類。
從使用上來看,一個類可以實現多個介面,但是不能繼承多個抽象類。
介面的欄位只能是 static 和 final 類型的,而抽象類的欄位沒有這種限制。
介面的成員只能是 public 的,而抽象類的成員可以有多種訪問許可權。
十四.String Pool
String pool 指字元串常量池,保存著所有在編譯時就已經確定的String變數。調用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的區別
十五.ArrayList和Vector的區別
這兩個類都實現了List介面(List介面繼承了Collection介面),他們都是有序集合,即存儲在這兩個集合中的元素的位置都是有順序的,相當於一種動態的數組,我們以後可以按位置索引號取出某個元素,並且其中的數據是允許重覆的。
(1)同步性:
Vector是線程安全的,也就是說是它的方法之間是線程同步的,而ArrayList是線程式不安全的,它的方法之間是線程不同步的。如果只有一個線程會訪問到集合,那最好是使用ArrayList,因為它不考慮線程安全,效率會高些;如果有多個線程會訪問到集合,那最 好是使用Vector,因為不需要我們自己再去考慮和編寫線程安全的代碼。
備註:對於Vector&ArrayList、Hashtable&HashMap,要記住線程安全的問題,記住Vector與Hashtable是舊的,是java一誕生就提供了的,它們是線程安全的,ArrayList與HashMap是java2時才提供的,它們是線程不安全的。所以,我們講課時先講老的。
(2)數據增長:
ArrayList與Vector都有一個初始的容量大小,當存儲進它們裡面的元素的個數超過了容量時,就需要增加ArrayList與Vector的存儲空間,每次要增加存儲空間時,不是只增加一個存儲單元,而是增加多個存儲單元,每次增加的存儲單元的個數在記憶體空間利用與程式效率之間要取得一定的平衡。Vector預設增長為原來兩倍,而ArrayList的增長策略在文檔中沒有明確規定(從源代碼看到的是增長為原來的1.5倍)。ArrayList與Vector都可以設置初始的空間大小,Vector還可以設置增長的空間大小,而ArrayList沒有提供設置增長空間的方法。
總結:即Vector增長原來的一倍,ArrayList增加原來的0.5倍。
十六.Java 中Collections類里的reverse (反轉方法)
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);
}
}
}
此方法可反轉數組的值.