Java基礎隨記-不定時更新
一.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);
}
}
}
此方法可反轉陣列的值.