J2SE基礎(chǔ)
1. 九種基本數(shù)據(jù)類型的大小暗甥,以及他們的封裝類
注:事實上應(yīng)該是八種基本數(shù)據(jù)類型框往,String類并不屬于基本類型。
String a = "hello world"知允; | String b = new String ("hello world"); |
---|---|
先創(chuàng)建一個字符串對象“hello world”撒蟀,而這個字符串實際上是放在字符串緩沖區(qū)(敲黑板??)中,然后把a指向這個對象 | 創(chuàng)建兩個對象一個是“hello world”這個放在字符串緩沖區(qū)中的温鸽,另一個是new String()這個對象保屯,新對象中保存的是“hello world”對象罷了,這個對象是放在堆內(nèi)存(敲黑板??)中嗤朴,而b指向這個new String ()對象 |
字符串緩沖區(qū)中對相同的字符串只會存一次配椭。
假如我們同時寫了String a ="hello world",String b = new String("hello world")雹姊,那么字符串緩沖區(qū)實際只有一個hello world字符串股缸。
基本數(shù)據(jù)類型 | 大小 | 封裝類 |
---|---|---|
byte | 1字節(jié) | Byte |
short | 2字節(jié) | Short |
int | 4字節(jié) | Integer |
long | 8字節(jié) | Long |
float | 4字節(jié) | Float |
double | 8字節(jié) | Double |
char | 2字節(jié) | Character(不推薦使用) |
boolean | 1字節(jié)或者4字節(jié) | Boolean |
往ArrayList,HashMap中放東西時吱雏,像int敦姻,double這種內(nèi)建類型是放不進去的瘾境,因為容器都是裝object的
《Java虛擬機規(guī)范》一書中的描述:“雖然定義了boolean這種數(shù)據(jù)類型,但是只對它提供了非常有限的支持镰惦。在Java虛擬機中沒有任何供boolean值專用的字節(jié)碼指令迷守,Java語言表達式所操作的boolean值,在編譯之后都使用Java虛擬機中的int數(shù)據(jù)類型來代替旺入,而boolean數(shù)組將會被編碼成Java虛擬機的byte數(shù)組兑凿,每個元素boolean元素占8位”。這樣我們可以得出boolean類型占了單獨使用是4個字節(jié),在數(shù)組中又是1個字節(jié)。
PS:使用int的原因是库物,對于當(dāng)下32位的處理器(CPU)來說,一次處理數(shù)據(jù)是32位(這里不是指的是32/64位系統(tǒng)圣絮,而是指CPU硬件層面)更高效。
char是unsigned(無符號的)雕旨,byte/short/int/long/float/double是signed(有符號的)扮匠,boolean應(yīng)該是unsigned(個人的猜想,并未驗證過)
PS:因為char要用Unicode(敲黑板??)表示凡涩,必須16位棒搜,所以無符號,是實在擠不出來位了突照,要不然Java設(shè)計者肯定也設(shè)計成有符號的帮非,因為他覺得unsigned大部分人并不能真正搞懂??(不是針對你氧吐,是說在座的都是...)
//驗證char有幾位
public class Test {
public static void main(String[] args) {
System.out.println(java.nio.charset.Charset.defaultCharset());
String str = "中";
char x = '中';
//也可以getBytes('utf-8')讹蘑,我的系統(tǒng)默認是utf-8
byte[] bytesStr = str.getBytes();
byte[] bytesChar = charToByte(x);
System.out.println("bytesStr 大小:" + bytesStr.length);
byteResult(bytesStr);
//其實這個輸出是沒意義的筑舅,只是純粹的格式整齊而已
System.out.println("bytesChar 大凶俊:" + bytesChar.length);
byteResult(bytesChar);
}
private static byte[] charToByte(char c) {
/**
* 定義成3個字節(jié),只是為了更好的驗證翠拣,b[0]都是0版仔,并未使用
* 當(dāng)然英文字母的情況下b[1]都是0,但這情況下實際是的確使用了的
*/
byte[] b = new byte[3];
b[0] = (byte) ((c & 0xFF0000) >> 16);
b[1] = (byte) ((c & 0xFF00) >> 8);
b[2] = (byte) (c & 0xFF);
return b;
}
private static void byteResult(byte[] bs) {
for (byte b : bs) {
for (int i = 7; i >= 0; i--) {
System.out.print(b >> i & 1);
}
System.out.println();
}
}
}
結(jié)果如下:
UTF-8
bytesStr 大形竽埂:3
11100100
10111000
10101101
bytesChar 大新浮:3
00000000
01001110
00101101
為什么String和char按位輸出的結(jié)果不一樣呢?
因為char輸出的是Unicode格式谜慌,而String在本機的編譯器上輸出的是UTF-8格式
美國的ASCII全部編碼范圍是0-127(PS:編號從0開始的32種狀態(tài)分別規(guī)定了特殊的用途)
??
西歐國家規(guī)定從128 到255這一頁的字符集被稱擴展字符集
??
中國的GB2312(對ASCII 的中文擴展)規(guī)定:一個小于127的字符的意義與原來相同然想,但兩個大于127的字符連在一起時,就表示一個漢字欣范,前面的一個字節(jié)(高字節(jié))從0xA1用到0xF7变泄,后面一個字節(jié)(低字節(jié))從0xA1到0xFE令哟,這樣我們就可以組合出大約7000多個簡體漢字了(還包括了數(shù)學(xué)符號、羅馬希臘的字母妨蛹、日文的假名們)
PS:GB2312下一個字節(jié)大于0xA1屏富,就是漢字的內(nèi)碼的一部分(因為兩個字節(jié)才組成一個漢字)
??
但是中國的漢字太多了
0xA1十進制是161,那還有128->160的空間可以使用
還是不夠用
所以又規(guī)定只要第一個字節(jié)是大于127就固定表示這是一個漢字的開始蛙卤,不管后面跟的是不是擴展字
這就是GBK標(biāo)準(zhǔn)狠半,包括了GB2312 的所有內(nèi)容,同時又增加了近20000個新的漢字(包括繁體字)和符號
后來少數(shù)民族也要用電腦了颤难,又加了幾千個新的少數(shù)民族的字
GBK擴成了GB18030
??
計算機的巴比倫塔命題
ISO廢了所有的地區(qū)性編碼方案典予,重新搞一個包括了地球上所有文化、所有字母和符號的編碼乐严!
他們打算叫它”Universal Multiple-Octet Coded Character Set”瘤袖,簡稱UCS, 俗稱Unicode。
Unicode開始制訂時昂验,計算機的存儲器容量極大地發(fā)展了捂敌,空間再也不成為問題了,于是ISO就直接規(guī)定必須用兩個字節(jié)既琴。
這種大氣的方案在保存英文文本時會多浪費一倍的空間(其高8位永遠是0)
??
直到互聯(lián)網(wǎng)的出現(xiàn)占婉,為解決Unicode如何在網(wǎng)絡(luò)上傳輸?shù)膯栴},于是面向傳輸?shù)谋姸郩TF(UCS Transfer Format)標(biāo)準(zhǔn)出現(xiàn)了甫恩,顧名思義逆济,UTF-8就是每次8個位傳輸數(shù)據(jù),而UTF-16就是每次16個位
UTF-8最大的一個特點磺箕,就是它是一種變長的編碼方式奖慌。它可以使用1~4個字節(jié)表示一個符號,根據(jù)不同的符號而變化字節(jié)長度
當(dāng)字符在ASCII碼的范圍時松靡,就用一個字節(jié)表示简僧,保留了ASCII字符一個字節(jié)的編碼做為它的一部分,注意的是Unicode一個中文字符占2個字節(jié)雕欺,而UTF-8一個中文字符占3個字節(jié)
Unicode符號范圍(十六進制) | UTF-8編碼方式(二進制) |
---|---|
0000 0000-0000 007F | 0xxxxxxx |
0000 0080-0000 07FF | 110xxxxx 10xxxxxx |
0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx |
0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx |
其中byte和short應(yīng)用于底層文件處理或者需要占據(jù)存儲空間量的大數(shù)組(如:byte[])岛马,舉個??,??
對象的序列化就是將你程序中實例化的某個類的對象屠列,比如啦逆,你自定一個類MyClass,或者任何一個類的對象笛洛,將它轉(zhuǎn)換成字節(jié)數(shù)組夏志,也就是說可以放到一個byte數(shù)組中,這時候撞蜂,你既然已經(jīng)把一個對象放到了byte數(shù)組中盲镶,那么你當(dāng)然就可以隨便處置了它了侥袜,用得最多的就是把他發(fā)送到網(wǎng)絡(luò)上遠程的計算機上了
EJB概念
運行在一個獨立的服務(wù)器上,并封裝了業(yè)務(wù)邏輯的組件就是EJB(Enterprise JavaBean)
客戶端是通過網(wǎng)絡(luò)對EJB 對象進行調(diào)用的溉贿。在Java中枫吧,能夠?qū)崿F(xiàn)遠程對象調(diào)用的技術(shù)是RMI,而EJB 技術(shù)基礎(chǔ)正是RMI
2. Switch能否用string做參數(shù)
Java 7之后可以
在jdk1.7之前宇色,switch只能支持byte九杂、short、char宣蠕、int這幾個基本數(shù)據(jù)類型和其對應(yīng)的封裝類型例隆。switch后面的括號里面只能放int類型的值(敲黑板??),但由于byte抢蚀,short镀层,char類型,它們會自動轉(zhuǎn)換為int類型(精精度小的向大的轉(zhuǎn)化)皿曲,所以它們也支持唱逢。
注意,對于精度比int大的類型屋休,比如long坞古、float,doulble劫樟,不會自動轉(zhuǎn)換為int痪枫,如果想使用,就必須強轉(zhuǎn)為int叠艳,如(int)float;
為什么jdk1.7后又可以用string類型作為switch參數(shù)呢奶陈?
其實,jdk1.7并沒有新的指令來處理switch string虑绵,而是通過調(diào)用switch中string.hashCode()尿瞭,將string轉(zhuǎn)換為int從而進行判斷。
3. equals與==的區(qū)別
- 基本數(shù)據(jù)類型byte,short,char,int,long,float,double,boolean
他們之間的比較翅睛,應(yīng)用==,比較的是他們的值黑竞。- 復(fù)合數(shù)據(jù)類型(類)
當(dāng)他們用==進行比較的時候捕发,比較的是他們在內(nèi)存中的存放地址,所以除非是同一個new出來的對象很魂,他們的比較后的結(jié)果為true扎酷,否則比較后結(jié)果為false。
- Java中默認的equals方法實現(xiàn)如下:
public boolean equals(Object obj) {
return (this == obj);
}
- 而String類則覆寫了這個方法,直觀的講就是比較字符是不是都相同
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = count;
if (n == anotherString.count) {
char v1[] = value;
char v2[] = anotherString.value;
int i = offset;
int j = anotherString.offset;
while (n-- != 0) {
if (v1[i++] != v2[j++])
return false;
}
return true;
}
}
return false;
}
4. Object有哪些公用方法
- clone方法
實現(xiàn)對象的淺拷貝(敲黑板??)遏匆,只有實現(xiàn)了Cloneable接口才可以調(diào)用該方法法挨,否則拋出CloneNotSupportedException異常谁榜。
在java語言中,有幾種方式可以創(chuàng)建對象呢(敲黑板??)
使用new操作符創(chuàng)建一個對象 | 使用clone方法復(fù)制一個對象 |
---|---|
看new操作符后面的類型知道分配多大的內(nèi)存空間 | 分配和源對象的相同大小的內(nèi)存空間 |
再調(diào)用構(gòu)造函數(shù)凡纳,填充對象的各個域窃植,這一步叫做對象的初始化 | 然后再使用原對象中對應(yīng)的各個域,填充新對象的域 |
構(gòu)造方法返回后荐糜,一個對象創(chuàng)建完畢巷怜,可以引用(地址)發(fā)布到外部 | clone方法返回,一個新的相同的對象被創(chuàng)建暴氏,同樣可以把這個新對象的引用發(fā)布到外部 |
復(fù)制引用 or 復(fù)制對象
//復(fù)制引用
public static void main(String[] args) {
Person p = new Person(23, "zhang");
Person p1 = p;
System.out.println(p);
System.out.println(p1);
}
public static class Person {
private int age ;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
}
輸出結(jié)果:
java_study.CloneTest$Person@60e53b93
java_study.CloneTest$Person@60e53b93
//復(fù)制對象
public static void main(String[] args) {
Person p = new Person(23, "zhang");
Person p1 = null;
try {
p1 = (Person) p.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
System.out.println(p);
System.out.println(p1);
}
public static class Person implements Cloneable {
private int age ;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public String getName() {
return name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
輸出結(jié)果:
java_study.CloneTest$Person@60e53b93
java_study.CloneTest$Person@5e2de80c
由于age是基本數(shù)據(jù)類型延塑, 那么對它的拷貝沒有什么疑議,直接將一個4字節(jié)的整數(shù)值拷貝過來就行答渔。但是name是String類型的关带, 它只是一個引用, 指向一個真正的String對象沼撕,那么對它的拷貝有兩種方式
//驗證原生clone是淺拷貝還是深拷貝
String result = p.getName() == p1.getName() ? "clone是淺拷貝的" : "clone是深拷貝的";
System.out.println(result);
輸出結(jié)果:
clone是淺拷貝的
//覆蓋Object中的clone方法豫缨, 實現(xiàn)深拷貝
public static void main(String[] args) throws CloneNotSupportedException {
Body body = new Body(new Head());
Body body1 = (Body) body.clone();
System.out.println("body == body1 : " + (body == body1));
System.out.println("body.head == body1.head : " + (body.head == body1.head));
System.out.println("body.head.face == body1.head.face : " + (body.head.face == body1.head.face));
}
static class Body implements Cloneable {
public Head head;
public Body() {
}
public Body(Head head) {
this.head = head;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Body newBody = (Body) super.clone();
newBody.head = (Head) head.clone();
return newBody;
}
}
static class Head implements Cloneable {
public Face face;
public Head() {
}
public Head(Face face) {
this.face = face;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
static class Face {
}
輸出結(jié)果:
body == body1 : false
body.head == body1.head : false
body.head.face == body1.head.face : true
如果想要深拷貝一個對象, 這個對象必須要實現(xiàn)Cloneable接口端朵,實現(xiàn)clone方法好芭,并且在clone方法內(nèi)部,把該對象引用的其他對象也要clone一份 冲呢, 這就要求這個被引用的對象必須也要實現(xiàn)Cloneable接口并且實現(xiàn)clone方法舍败。
(PS:clone的前提是對象new過,
例如Body body = new Body(new Head());
后來改造Face時忘記把new Head()里加上new Face()敬拓,一直報空指針??)
- getClass方法
final方法邻薯,獲得運行時對象類型。
public static void main(String[] args) {
A a = new A();
B b = new B();
A ab = new B();
System.out.println(a.getClass() + " " + A.class);
System.out.println(b.getClass() + " " + B.class);
System.out.println(ab.getClass());
ab = a;
System.out.println(ab.getClass());
}
static class A {
}
static class B extends A {
}
輸出結(jié)果:
class A class A
class B class B
class B
class A
- toString方法
該方法用得比較多乘凸,一般子類都有覆蓋厕诡。
打印對象時默認調(diào)用toString方法
public static void main(String[] args) {
Person p = new Person(23, "zhang");
System.out.println(p);
}
public static class Person {
private int age ;
private String name;
public Person(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return name;
}
}
輸出結(jié)果:
zhang
- finalize方法
垃圾回收器要回收對象的時候,首先要調(diào)用這個類的finalize方法营勤,一般的純Java編寫的Class不需要重新覆蓋這個方法灵嫌。
- equals方法
該方法是非常重要的一個方法。一般equals和==是不一樣的葛作,但是在Object中兩者是一樣的寿羞。子類一般都要重寫這個方法。
- hashCode方法
該方法用于哈希查找赂蠢,可以減少在查找中使用equals的次數(shù)绪穆,重寫了equals方法一般都要重寫hashCode方法。
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 boolean equals(Object object) {
if (object == null || !(object instanceof HashTest))
return false;
HashTest other = (HashTest) object;
if (object == this || other.getI() == this.getI())
return true;
return false;
}
public static void main(String[] args) {
HashTest a = new HashTest();
HashTest b = new HashTest();
a.setI(1);
b.setI(1);
Set<HashTest> set = new HashSet<>();
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
true
[java_study.HashTest@1]
Set的集合里不允許對象有重復(fù)的值,List允許有重復(fù)玖院,它對集合中的對象進行索引菠红,Queue的工作原理是FCFS算法(First Come, First Serve)
如果不重寫hashCode(),在HashSet中添加兩個equals的對象(hashCode有可能會相同难菌,所以最后還是要用equals()比較)试溯,會將兩個對象都加入進去,這樣HashSet就失去了他本身的意義了扔傅。
- wait方法
wait方法就是使當(dāng)前線程等待該對象的鎖耍共,當(dāng)前線程必須是該對象的擁有者,也就是具有該對象的鎖猎塞。wait()方法一直等待试读,直到獲得鎖或者被中斷。wait(long timeout)設(shè)定一個超時間隔荠耽,如果在規(guī)定時間內(nèi)沒有獲得鎖就返回钩骇。
調(diào)用該方法后當(dāng)前線程進入睡眠狀態(tài),直到以下事件發(fā)生铝量。
(1)其他線程調(diào)用了該對象的notify方法倘屹。
(2)其他線程調(diào)用了該對象的notifyAll方法。
(3)其他線程調(diào)用了interrupt中斷該線程慢叨。
(4)時間間隔到了纽匙。
此時該線程就可以被調(diào)度了,如果是被中斷的話就拋出一個InterruptedException異常拍谐。- notify方法
該方法喚醒在該對象上等待的某個線程烛缔。- notifyAll方法
該方法喚醒在該對象上等待的所有線程。
5. Java的四種引用轩拨,強弱軟虛践瓷,用到的場景
(1) 強引用(StrongReference)
??強引用是我們在編程過程中使用的最簡單的引用,如代碼String s=”abc”中變量s就是字符串對象”abc”的一個強引用亡蓉。任何被強引用指向的對象都不能被垃圾回收器回收晕翠,這些對象都是在程序中需要的。
(2) 軟引用(SoftReference)
??軟引用是用來描述一些有用但并不是必需的對象砍濒。對于軟引用關(guān)聯(lián)著的對象淋肾,只有在內(nèi)存不足的時候JVM才會回收該對象。因此梯影,這一點可以很好地用來解決OOM的問題巫员,并且這個特性很適合用來實現(xiàn)緩存:比如網(wǎng)頁緩存、圖片緩存等甲棍。
??軟引用可以和一個引用隊列(ReferenceQueue)聯(lián)合使用,如果軟引用所引用的對象被JVM回收,這個軟引用就會被加入到與之關(guān)聯(lián)的引用隊列中感猛。
(3) 弱引用(WeakReference)
??弱引用也是用來描述非必需對象的七扰,當(dāng)JVM進行垃圾回收時,無論內(nèi)存是否充足陪白,都會回收被弱引用關(guān)聯(lián)的對象颈走。
(4) 虛引用(PhantomReference)
??虛引用和前面的軟引用、弱引用不同咱士,它并不影響對象的生命周期立由。如果一個對象與虛引用關(guān)聯(lián),則跟沒有引用與之關(guān)聯(lián)一樣序厉,在任何時候都可能被垃圾回收器回收锐膜。
??要注意的是,虛引用必須和引用隊列關(guān)聯(lián)使用弛房,當(dāng)垃圾回收器準(zhǔn)備回收一個對象時道盏,如果發(fā)現(xiàn)它還有虛引用,就會把這個虛引用加入到與之關(guān)聯(lián)的引用隊列中文捶。程序可以通過判斷引用隊列中是否已經(jīng)加入了虛引用荷逞,來了解被引用的對象是否將要被垃圾回收。如果程序發(fā)現(xiàn)某個虛引用已經(jīng)被加入到引用隊列粹排,那么就可以在所引用的對象的內(nèi)存被回收之前采取必要的行動种远。
public class ReferenceTest {
public static void main(String[] args) {
// 軟引用,沒有隊列,可以有
String hello = new String("hello");//強引用
SoftReference<String> sr = new SoftReference<>(hello);
hello = null;
System.out.println(sr.get());
System.out.println();
// 弱引用,沒有隊列,可以有
String hello1 = new String("hello1");
WeakReference<String> sr1 = new WeakReference<>(hello1);
hello1 = null;
System.out.println(sr1.get());
System.gc();
System.out.println(sr1.get());
System.out.println();
// 虛引用,有隊列
ReferenceQueue<String> queue = new ReferenceQueue<>();
String hello2 = new String("hello2");
PhantomReference<String> pr = new PhantomReference<>(hello2, queue);
hello2 = null;
System.out.println(pr.get());
System.out.println();
}
}
輸出結(jié)果:
hello
hello1
null
null
6. Hashcode的作用
見4.Object有哪些公用方法
7. ArrayList、LinkedList顽耳、Vector的區(qū)別
ArrayList | LinkedList | Vector |
---|---|---|
一個可改變大小的數(shù)組 | 一個雙鏈表 | ArrayList類似坠敷,但屬于強同步類 |
大小將會動態(tài)地增長.內(nèi)部的元素可以直接通過get與set方法進行訪問 | 在添加和刪除元素時具有比ArrayList更好的性能 | 程序本身是線程安全的(thread-safe,沒有在多個線程之間共享同一個集合/對象),那么使用ArrayList是更好的選擇 |
每次對size增長50% | 還實現(xiàn)了 Queue 接口 | 每次請求其大小的雙倍空間 |
8. String、StringBuffer與StringBuilder的區(qū)別
String | StringBuffer | StringBuilder |
---|---|---|
不可變類斧抱,任何對String的改變都會生成新的對象 | 可變類常拓,支持并發(fā)操作,適合多線程中使用 | 可變類辉浦,不支持并發(fā)操作弄抬,線程不安全的,單線程中的性能比StringBuffer高 |
經(jīng)常改變內(nèi)容的字符串最好不要用String宪郊,因為每次生成對象都會對系統(tǒng)性能產(chǎn)生影響掂恕,而如果是使用 StringBuffer 類則結(jié)果就不一樣了,每次結(jié)果都會對 StringBuffer 對象本身進行操作弛槐。而在某些特別情況下懊亡,String 效率是遠要比 StringBuffer 快的:
String S1 = "This is only a " + "simple " + "test";
StringBuffer Sb = new StringBuilder( "This is only a ").append( "simple ").append( "test");
你會很驚訝的發(fā)現(xiàn),生成 String S1 對象的速度簡直太快了乎串,而這個時候 StringBuffer 居然速度上根本一點都不占優(yōu)勢店枣。其實這是 JVM 的一個把戲,在 JVM 眼里,這個
String S1 = "This is only a " + "simple " + "test";其實就是:
String S1 = "This is only a simple test"; 所以當(dāng)然不需要太多的時間了鸯两。但大家這里要注意的是闷旧,如果你的字符串是來自另外的 String對象的話,速度就沒那么快了钧唐,譬如:
String S2 = "This is only a ";
String S3 = "simple " ;
String S4 = "test";
String S1 = S2 +S3 + S4;
這時候 JVM 會規(guī)規(guī)矩矩的按照原來的方式去做
9. Map忙灼、Set、List钝侠、Queue该园、Stack的特點與用法
圖其實畫的不準(zhǔn)確了,LinkedHashSet應(yīng)該是繼承自HashSet
LinkedList也只是實現(xiàn)了Queue帅韧,并不是繼承哦??
Collection-------Iterable是該的父接口里初,iterator()方法用于遍歷操作
├ List-----------List可以通過下標(biāo) (1,2..) 來取得值,值可以重復(fù)
│├ LinkedList---線程不安全的弱匪,雙向鏈表實現(xiàn)
│├ ArrayList----線程不安全的青瀑,數(shù)組實現(xiàn)
│└ Vector-------線程安全的,數(shù)組實現(xiàn)
│ └ Stack----一個后進先出的棧萧诫,push斥难、pop、peak帘饶、empty哑诊、search
├ Set-----------Set只能通過Iterable(迭代器)來取值,并且值不能重復(fù)
│├ HashSet-----使用hash來存儲元素及刻,因此具有良好的存取和查找性能
││ └ LinkedHashSet--使用鏈表維護元素的次序镀裤,遍歷時按添加順序來訪問
│└ TreeSet------是SortedSet接口的實現(xiàn)類,可以確保元素處于排序狀態(tài)
└ Queue--------Queue保持一個隊列(先進先出)的順序缴饭,offer暑劝、poll、peek
└ PriorityQueue--按照隊列元素的大小進行重新排序
Map------不關(guān)心元素添加的順序颗搂,采用了hash担猛,因此查找元素比ArrayList快
├ Hashtable---------線程安全的,key和value不能為null
├ HashMap----------線程不安全的丢氢,key和value可以為null
│├ LinkedHashMap--雙向鏈表來維護key-value對的次序傅联,與插入順序一致
│└ WeakHashMap
├ TreeMap---紅黑樹數(shù)據(jù)結(jié)構(gòu),每個key-value對即作為紅黑樹的一個節(jié)點
└ IdentifyHashMap--當(dāng)且僅當(dāng)key1 == key2時疚察,才認為兩個key相等
10. HashMap和HashTable的區(qū)別
主要區(qū)別在多個線程訪問Hashtable時蒸走,不需要自己為它的方法實現(xiàn)同步,
而HashMap 就必須為之提供外同步(Collections.synchronizedMap)貌嫡。
11. HashMap和ConcurrentHashMap的區(qū)別比驻,HashMap的底層源碼
Hashmap本質(zhì)是數(shù)組加鏈表该溯。根據(jù)key取得hash值,然后計算出數(shù)組下標(biāo)嫁艇,如果多個key對應(yīng)到同一個下標(biāo)朗伶,就用鏈表串起來弦撩,新插入的在前面步咪。
ConcurrentHashMap:在hashMap的基礎(chǔ)上,ConcurrentHashMap將數(shù)據(jù)分為多個segment益楼,默認16個(concurrency level)猾漫,然后每次操作對一個segment加鎖,避免多線程鎖的幾率感凤,提高并發(fā)效率悯周。
后面的太多了??提取不了了http://blog.csdn.net/stephenxe/article/details/52386786
12. TreeMap、HashMap陪竿、LindedHashMap的區(qū)別
見9. Map禽翼、Set、List族跛、Queue闰挡、Stack的特點與用法
13. Collection包結(jié)構(gòu),與Collections的區(qū)別
Collections是一個包裝類(java.util.Collections)礁哄,此類不能實例化长酗,就像一個工具類,用于對集合中元素進行排序桐绒、搜索以及線程安全等各種操作
Collection接口的意義是為各種具體的集合提供了最大化的統(tǒng)一操作方式
14. try catch finally夺脾,try里有return,finally還執(zhí)行么
public static void main(String[] args) {
fun();
}
static void fun() {
try {
System.out.println("代碼段");
return;
} catch (Exception e) {
}finally {
System.out.println("finally執(zhí)行");
}
}
輸出結(jié)果:
代碼段
finally執(zhí)行
(1) 在try中沒有異常的情況下try茉继、catch咧叭、finally的執(zhí)行順序 try — finally
(2) 如果try中有異常,執(zhí)行順序是try — catch — finally
(3) 如果try中沒有異常并且try中有return這時候正常執(zhí)行順序是try —- finally — return
(4) 如果try中有異常并且try中有return這時候正常執(zhí)行順序是try—-catch—finally— return
(5) 總之 finally 永遠執(zhí)行烁竭!
15. Excption與Error包結(jié)構(gòu)菲茬。OOM你遇到過哪些情況,SOF你遇到過哪些情況
(1) Exception與Error都繼承了Throwable
(2) Error類體系描述了Java運行系統(tǒng)中的內(nèi)部錯誤颖变,一般都是由JVM拋出生均,一般我們都不關(guān)注
(3) Exception類體系,如RuntimeException和IOException等繼承與它腥刹,一般都是由于程序本身的因數(shù)或是外部環(huán)境因數(shù)造成马胧,這是我們需要關(guān)注盡量解決的異常
JVM中常見的OutOfMemory和StackOverFlow產(chǎn)生的機理
首先必須了解JVM運行時數(shù)據(jù)區(qū)域
方法區(qū)
用于存儲已被JVM加載的類信息,常量衔峰,靜態(tài)變量佩脊,即時編譯器編譯后的代碼蛙粘,線程共享。
運行時常量池
方法區(qū)一部分威彰。存放編譯期生成的各種字面量和符號引用出牧。
虛擬機棧
內(nèi)部創(chuàng)建棧幀,來存放局部變量表歇盼,操作數(shù)棧舔痕,動態(tài)鏈接,方法出口等豹缀,線程私有伯复。
本地方法棧(HotSpot不區(qū)分虛擬機棧和本地方法棧)
類似虛擬機棧,但是只為Native方法服務(wù)邢笙。
堆
存放實例對象和數(shù)組啸如,線程共享。
程序計數(shù)器
存放當(dāng)前線程執(zhí)行的字節(jié)碼的行號氮惯。
//堆OutOfMemory
public class HeapOOM {
static class OOMObject {
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<OOMObject>();
while (true) {
list.add(new OOMObject());
}
}
}
//棧OutOfMemory
public class JavaVMStackOOM {
private void dontStop() {
while (true) {
}
}
public void stackLeakByThread() {
while (true) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
dontStop();
}
});
thread.start();
}
}
public static void main(String[] args) {
JavaVMStackOOM oom = new JavaVMStackOOM();
oom.stackLeakByThread();
}
}
//StackOverFlow
public class JavaVMStackSOF {
private int stackLength = 1;
public void stackLeak() {
stackLength++;
stackLeak();
}
public static void main(String[] args) throws Throwable {
JavaVMStackSOF oom = new JavaVMStackSOF();
try {
oom.stackLeak();
} catch(Throwable e) {
System.out.println("stack length:" + oom.stackLength);
e.printStackTrace();
}
}
}
16. Java面向?qū)ο蟮娜齻€特征與含義
封裝:將可以描述這一類事物的屬性和行為歸納到一個類中叮雳,以方便使用,提高了代碼的利用和效率妇汗,降低了代碼的重復(fù)帘不。
繼承:封裝的屬性和行為沒有包含到具體某一事物,這時我們就需要繼承與封裝類添加這一具體事物所獨有的特征铛纬。
多態(tài):多態(tài)是以封裝和繼承為基礎(chǔ)的厌均,多態(tài)是站在一個抽象的層面去實施一個統(tǒng)一的行為,具體到個體時這個統(tǒng)一的行為會施行自己的特征行為告唆。
17. Override和Overload的含義去區(qū)別
Override(重寫棺弊、覆蓋)
方法名、參數(shù)擒悬、返回值相同
存在于子父類之間
定義成final不能被覆寫
子類方法不能縮小父類的訪問權(quán)限
子類不能拋出比父類更多的異常
Overload(重載模她,過載)
參數(shù)類型、個數(shù)懂牧、順序至少一個不同
返回值不同是不行的
存在于子類侈净、父類、同類
區(qū)別
Override是子類與父類之間的多態(tài)表現(xiàn)僧凤,Overload是一個類中的多態(tài)
18. Interface與abstract類的區(qū)別
Interface | abstract |
---|---|
所有方法都是公開畜侦、抽象方法,所有屬性都是公開、靜態(tài)、常量 | 如果一個類如果要添加抽象方法就必須將類設(shè)置為abstract |
類只能是實現(xiàn)接口莹汤,并且可以實現(xiàn)多個接口您朽,類必須實現(xiàn)接口的方法,否則為抽象類 | abstract類必須被繼承使用缩膝,不能生成對象(所以Final和abstract永遠不會同時出現(xiàn)) |
接口可不寫public雇盖,但子類中實現(xiàn)接口的過程中public不可省 | abstract的方法不可以private修飾 |
接口和接口之間可以允許多繼承 | 其子類必須覆蓋父類的抽象方法 |
接口是實現(xiàn)了不同層次蝗罗、不同體系對象的共同屬性 | abstract和static不能在方法中放在一起(測了下類是可以放在一起的) |
19. Static class 與not static class的區(qū)別
static class | not static class |
---|---|
可以脫離外部類被創(chuàng)建 | 必須先new外部類义图,再new內(nèi)部類 |
只能訪問外部類的靜態(tài)成員 | 可以訪問外部類的數(shù)據(jù)和方法减俏,因為他就在外部類里面 |
20. java多態(tài)的實現(xiàn)原理
靠的是父類或接口定義的引用變量可以指向子類或具體實現(xiàn)類的實例對象,而程序調(diào)用的方法在運行期才動態(tài)綁定(敲黑板??)碱工,就是引用變量所指向的具體實例對象的方法娃承,也就是內(nèi)存里正在運行的那個對象的方法,而不是引用變量的類型中定義的方法
21. 實現(xiàn)多線程的兩種方法:Thread與Runable
public class ThreadRunableTest {
public static void main(String[] args) {
MyThreadRunnable mr = new MyThreadRunnable();
new Thread(mr).start();
new Thread(mr).start();
new MyThread().start();
new MyThread().start();
}
static class MyThreadRunnable implements Runnable {
@Override
public void run() {
System.out.println("hello");
}
}
static class MyThread extends Thread {
@Override
public void run() {
System.out.println("world");
}
}
}
輸出結(jié)果:
hello
hello
world
world
22. 線程同步的方法:sychronized痛垛、lock草慧、reentrantLock等
如果你向一個變量寫值,而這個變量接下來可能會被另一個線程所讀取匙头,或者你從一個變量讀值,而它的值可能是前面由另一個線程寫入的仔雷,此時你就必須使用同步
synchronized會在進入同步塊的前后分別形成monitorenter和monitorexit字節(jié)碼指令.在執(zhí)行monitorenter指令時會嘗試獲取對象的鎖,如果此沒對象沒有被鎖,或者此對象已經(jīng)被當(dāng)前線程鎖住,那么鎖的計數(shù)器加一,每當(dāng)monitorexit被鎖的對象的計數(shù)器減一.直到為0就釋放該對象的鎖.由此synchronized是可重入的,不會出現(xiàn)自己把自己鎖死.
synchronized關(guān)鍵字可以作為函數(shù)的修飾符蹂析,也可作為函數(shù)內(nèi)的語句,也就是平時說的同步方法和同步語句塊碟婆。如果再細的分類电抚,synchronized可作用于instance變量、object reference(對象引用)竖共、static函數(shù)和class literals(類名稱字面常量)身上蝙叛。
在進一步闡述之前,我們需要明確幾點:
A.無論synchronized關(guān)鍵字加在方法上還是對象上公给,它取得的鎖都是對象(敲黑板??)借帘,而不是把一段代碼或函數(shù)當(dāng)作鎖――而且同步方法很可能還會被其他線程的對象訪問。
B.每個對象只有一個鎖(lock)與之相關(guān)聯(lián)淌铐。
C.實現(xiàn)同步是要很大的系統(tǒng)開銷作為代價的肺然,甚至可能造成死鎖,所以盡量避免無謂的同步控制腿准。
一际起、當(dāng)兩個并發(fā)線程訪問同一個對象object中的這個synchronized(this)同步代碼塊時,一個時間內(nèi)只能有一個線程得到執(zhí)行吐葱。另一個線程必須等待當(dāng)前線程執(zhí)行完這個代碼塊以后才能執(zhí)行該代碼塊街望。
public class Thread1 implements Runnable {
public void run() {
synchronized(this) {
for (int i = 0; i < 5; i++) {
System.out.println(Thread.currentThread().getName() + " synchronized loop " + i);
}
}
}
public static void main(String[] args) {
Thread1 t1 = new Thread1();
Thread ta = new Thread(t1, "A");
Thread tb = new Thread(t1, "B");
ta.start();
tb.start();
}
}
輸出結(jié)果:
A synchronized loop 0
A synchronized loop 1
A synchronized loop 2
A synchronized loop 3
A synchronized loop 4
B synchronized loop 0
B synchronized loop 1
B synchronized loop 2
B synchronized loop 3
B synchronized loop 4
二、然而弟跑,當(dāng)一個線程訪問object的一個synchronized(this)同步代碼塊時灾前,另一個線程仍然可以訪問該object中的非synchronized(this)同步代碼塊。
public class Thread2 {
public void m4t1() {
synchronized (this) {
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void m4t2() {
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
final Thread2 myt2 = new Thread2();
Thread t1 = new Thread(new Runnable() {
public void run() {
myt2.m4t1();
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
public void run() {
myt2.m4t2();
}
}, "t2");
t1.start();
t2.start();
}
}
輸出結(jié)果:
t2 : 4
t1 : 4
t1 : 3
t2 : 3
t1 : 2
t2 : 2
t1 : 1
t2 : 1
t1 : 0
t2 : 0
三窖认、尤其關(guān)鍵的是豫柬,當(dāng)一個線程訪問object的一個synchronized(this)同步代碼塊時告希,其他線程對object中所有其它synchronized(this)同步代碼塊的訪問將被阻塞。
//修改Thread2.m4t2()方法:
public void m4t2() {
synchronized (this) {
int i = 5;
while (i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
輸出結(jié)果:
t1 : 4
t1 : 3
t1 : 2
t1 : 1
t1 : 0
t2 : 4
t2 : 3
t2 : 2
t2 : 1
t2 : 0
四烧给、第三個例子同樣適用其它同步代碼塊燕偶。也就是說,當(dāng)一個線程訪問object的一個synchronized(this)同步代碼塊時础嫡,它就獲得了這個object的對象鎖指么。結(jié)果,其它線程對該object對象所有同步代碼部分的訪問都被暫時阻塞榴鼎。
//修改Thread2.m4t2()方法如下:
public synchronized void m4t2() {
int i = 5;
while( i-- > 0) {
System.out.println(Thread.currentThread().getName() + " : " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
輸出結(jié)果:
t1 : 4
t1 : 3
t1 : 2
t1 : 1
t1 : 0
t2 : 4
t2 : 3
t2 : 2
t2 : 1
t2 : 0
具體見http://www.cnblogs.com/GnagWang/archive/2011/02/27/1966606.html??
Lock是一個接口伯诬,reentrantLock是Lock接口的一個實現(xiàn)類
Lock lock = new ReentrantLock();
public void fun() {
lock.lock();//得到鎖
try {
/ /同步代碼段
} finally{
lock.unlock();//釋放鎖
}
}
具體見http://blog.csdn.net/kai_wei_zhang/article/details/8196130
在并發(fā)量比較小的情況下,使用synchronized是個不錯的選擇巫财,但是在并發(fā)量比較高的情況下盗似,其性能下降很嚴重,此時ReentrantLock是個不錯的方案平项。
23. 鎖的等級:方法鎖赫舒、對象鎖、類鎖
對象鎖是用來控制實例方法之間的同步闽瓢,類鎖是用來控制靜態(tài)方法(或靜態(tài)變量互斥體)之間的同步
對于類鎖接癌,則會把整個類鎖住,也就說只能有一個對象擁有當(dāng)前類的鎖扣讼。當(dāng)一個對象擁有了類鎖之后缺猛,另外一個對象還想競爭鎖的話則會被阻塞。兩個對象A椭符,B荔燎,如果A正在訪問一個被類鎖修飾的方法function,那么B則不能訪問艰山。因為類鎖只能在同一時刻被一個對象擁有湖雹。相對于對象鎖,則是不同曙搬。還是A摔吏,B兩個對象,如果A正在訪問對象鎖修飾的function纵装,那么這個時候B也可以同時訪問征讲。
對于對象鎖,當(dāng)一個對象擁有鎖之后橡娄,訪問一個加了對象鎖的方法诗箍,而該方法中又調(diào)用了該類中其他加了對象鎖的方法,那么這個時候是不會阻塞住的挽唉。這是java通過可重入鎖機制(敲黑板??)實現(xiàn)的滤祖】昀牵可重入鎖指的是當(dāng)一個對象擁有對象鎖之后,可以重復(fù)獲取該鎖匠童。因為synchronized塊是可重入的埂材,所以當(dāng)你訪問一個對象鎖的方法的時候,在該方法中繼續(xù)訪問其他對象鎖方法是不會被阻塞的汤求。
// 對象鎖:形式1(方法鎖)
public synchronized void Method1() {
System.out.println(“我是對象鎖也是方法鎖”);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 對象鎖:形式2(代碼塊形式)
public void Method2() {
synchronized (this) {
System.out.println("我是對象鎖");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
// 類鎖:形式1
public static synchronized void Method1() {
System.out.println("我是類鎖一號");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 類鎖:形式2
public void Method2() {
synchronized (Test.class) {
System.out.println("我是類鎖二號");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
24. 寫出生產(chǎn)者消費者模式
public class ProduceConsumerTest {
public static void main(String[] args) {
MyService myService = new MyService();
ProduceThread[] pt = new ProduceThread[2];
ConsumeThread[] ct = new ConsumeThread[2];
for (int i = 0; i < 2; i++) {
pt[i] = new ProduceThread(myService);
pt[i].setName(String.valueOf(i + 1));
ct[i] = new ConsumeThread(myService);
ct[i].setName(String.valueOf(i + 1));
pt[i].start();
ct[i].start();
}
}
static class MyService {
ArrayList<Integer> list = new ArrayList<>(); //用list存放生產(chǎn)之后的數(shù)據(jù)俏险,最大容量為1
synchronized void produce() {
try {
while (!list.isEmpty()) { //只有l(wèi)ist為空時才會去進行生產(chǎn)操作
System.out.println("生產(chǎn)者" + Thread.currentThread().getName() + " waiting");
this.wait();
}
int value = 9999;
list.add(value);
System.out.println("生產(chǎn)者" + Thread.currentThread().getName() + " Runnable");
this.notifyAll(); //然后去喚醒因object調(diào)用wait方法處于阻塞狀態(tài)的線程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized void consumer() {
try {
while (list.isEmpty()) { //只有l(wèi)ist不為空時才會去進行消費操作
System.out.println("消費者" + Thread.currentThread().getName() + " waiting");
this.wait();
}
list.clear();
System.out.println("消費者" + Thread.currentThread().getName() + " Runnable");
this.notifyAll(); //然后去喚醒因object調(diào)用wait方法處于阻塞狀態(tài)的線程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
static class ProduceThread extends Thread {
private MyService p;
ProduceThread(MyService p) {
this.p = p;
}
@Override
public void run() {
while (true) {
p.produce();
}
}
}
static class ConsumeThread extends Thread {
private MyService c;
ConsumeThread(MyService c) {
this.c = c;
}
@Override
public void run() {
while (true) {
c.consumer();
}
}
}
}
輸出結(jié)果:
生產(chǎn)者1 Runnable
生產(chǎn)者1 waiting
消費者2 Runnable
消費者2 waiting
生產(chǎn)者2 Runnable
生產(chǎn)者2 waiting
消費者1 Runnable
消費者1 waiting
...
static class MyService {
private ReentrantLock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private boolean hasValue = false;
void produce() {
lock.lock();
try {
while (hasValue == true) {
System.out.println("生產(chǎn)者" + Thread.currentThread().getName() + " waiting");
condition.await();
}
hasValue = true;
System.out.println("生產(chǎn)者" + Thread.currentThread().getName() + " Runnable");
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
void consumer() {
lock.lock();
try {
while (hasValue == false) {
System.out.println("消費者" + Thread.currentThread().getName() + " waiting");
condition.await();
}
hasValue = false;
System.out.println("消費者" + Thread.currentThread().getName() + " Runnable");
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
輸出結(jié)果:
生產(chǎn)者1 Runnable
生產(chǎn)者1 waiting
消費者1 Runnable
消費者1 waiting
生產(chǎn)者2 Runnable
生產(chǎn)者2 waiting
消費者2 Runnable
消費者2 waiting
25. ThreadLocal的設(shè)計理念與作用
他并不是一個Thread,而是thread local variable(線程局部變量)
雖然所有的線程都能訪問到這個ThreadLocal實例扬绪,但是每個線程卻只能訪問到自己通過調(diào)用ThreadLocal的set()方法設(shè)置的值竖独。即使是兩個不同的線程在同一個ThreadLocal對象上設(shè)置了不同的值,他們?nèi)匀粺o法訪問到對方的值挤牛。
public class ThreadLocalTest {
public static void main(String[] args) {
MyThreadRunnable mr = new MyThreadRunnable();
Thread thread1 = new Thread(mr);
Thread thread2 = new Thread(mr);
thread1.start();
thread2.start();
}
static class MyThreadRunnable implements Runnable {
private ThreadLocal threadLocal = new ThreadLocal();
@Override
public void run() {
threadLocal.set(new Random().nextInt(30));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(threadLocal.get());
}
}
}
26. ThreadPool用法與優(yōu)勢
線程池可以應(yīng)對突然大爆發(fā)量的訪問莹痢,通過有限個固定線程為大量的操作服務(wù),減少創(chuàng)建和銷毀線程所需的時間赊颠。
public class DifferentKindsThreadPool {
public static void main(String[] args) {
//displayScheduledThreadPool();
//displaySingleThreadPool();
//displayCachedThreadPool();
displayThreadPool();
}
/**
* 創(chuàng)建一個定時任務(wù)的線程池
*/
public static void displayScheduledThreadPool() {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);
//它可以向固定線程池一樣執(zhí)行任務(wù)
distributeTaskForThreadPool(scheduledThreadPool);
//這是它的特殊之處格二,可以定時任務(wù)
scheduledThreadPool.schedule(
new Runnable() {
@Override
public void run() {
System.out.println("開始執(zhí)行任務(wù)1");
}
},
5,
TimeUnit.SECONDS);
//每隔2秒再次重新執(zhí)行任務(wù)
scheduledThreadPool.scheduleAtFixedRate(
new Runnable() {
@Override
public void run() {
System.out.println("開始執(zhí)行任務(wù)2");
}
},
5,
2,
TimeUnit.SECONDS);
}
/**
* 創(chuàng)建只有一個線程的線程池,如果線程終止竣蹦,
* 他將會創(chuàng)建一個新的線程加入到池子中,這
* 個線程池會保證池子中始終有一個線程
*/
public static void displaySingleThreadPool() {
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
distributeTaskForThreadPool(singleThreadPool);
}
/**
* 創(chuàng)建一個可根據(jù)需要創(chuàng)建線程的線程池沧奴,但是
* 當(dāng)先前創(chuàng)建的線程可得到時就會重用先前的線
* 程痘括,如果不存在可得到的線程,一個新的線程
* 將被創(chuàng)建并被加入到池子中滔吠。60秒沒有被用到
* 的線程將被終止并從緩存中移除
*/
public static void displayCachedThreadPool() {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
distributeTaskForThreadPool(cachedThreadPool);
}
/**
* 創(chuàng)建一個帶有固定線程的線程池
*/
public static void displayThreadPool() {
// 創(chuàng)建一個帶有4個固定線程的線程池
ExecutorService threadPool = Executors.newFixedThreadPool(4);
distributeTaskForThreadPool(threadPool);
}
/**
* 為線程池分配8個任務(wù)纲菌,使其驅(qū)動
*/
public static void distributeTaskForThreadPool(ExecutorService threadPool) {
// 讓線程池驅(qū)動8個任務(wù)
for (int i = 1; i <= 8; i++) {
// 由于內(nèi)部類里面不能放一個非final的變量,所以我把i的值賦予task
final int task = i;
threadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println("我是" + Thread.currentThread().getName()
+ "疮绷," + "拿到了第" + task + "個任務(wù)翰舌,我開始執(zhí)行了");
}
});
}
}
}
public class BankTest {
public static void main(String[] args) {
BankCount bankCount = new BankCount();
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.execute(new Runnable() { //存錢線程
@Override
public void run() {
int i = 5;
while (i-- > 0) {
bankCount.addMoney(200);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Future future = executor.submit(new Runnable() { //取錢線程
@Override
public void run() {
int i = 5;
while (i-- > 0) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
bankCount.getMoney(200);
}
}
});
try {
Object res = future.get();
System.out.println(res);
} catch (Exception e) {
e.printStackTrace();
} finally {
executor.shutdown(); // 關(guān)閉線程池
}
}
static class BankCount {
public synchronized void addMoney(int money) {
System.out.println(Thread.currentThread().getName() + ">存入:" + money);
}
public synchronized void getMoney(int money) {
System.out.println(Thread.currentThread().getName() + ">取錢:" + money);
}
}
}
27. Concurrent包里的其他東西:ArrayBlockingQueue、CountDownLatch等等
...
28. wait()和sleep()的區(qū)別
(1) 這兩個方法來自不同的類分別是Thread和Object
(2) 最主要是sleep方法沒有釋放鎖(OS認為該線程正在工作冬骚,不會讓出系統(tǒng)資源)椅贱,而wait方法釋放了鎖,使得其他線程可以使用同步控制塊或者方法
(3) wait只冻、notify和notifyAll只能在同步控制方法或者同步控制塊里面使用庇麦,而sleep可以在任何地方使用
4,sleep必須捕獲異常,而wait喜德、notify和notifyAll不需要捕獲異常
29. foreach與正常for循環(huán)效率對比
使用foreach的對象必須實現(xiàn)Iterator接口山橄,對Iterator進行了更多操作,效率相比for更低舍悯。但是也有例外:
public class ForAndForeachTest {
public static void main(String[] args) {
//實例化arrayList
List<Integer> arrayList = new ArrayList<Integer>();
//實例化linkList
List<Integer> linkList = new LinkedList<Integer>();
//插入10萬條數(shù)據(jù)
for (int i = 0; i < 100000; i++) {
arrayList.add(i);
linkList.add(i);
}
int array = 0;
//用for循環(huán)arrayList
long arrayForStartTime = System.currentTimeMillis();
for (int i = 0; i < arrayList.size(); i++) {
array = arrayList.get(i);
}
long arrayForEndTime = System.currentTimeMillis();
System.out.println("用for循環(huán)arrayList 10萬次花費時間:" + (arrayForEndTime - arrayForStartTime) + "毫秒");
//用foreach循環(huán)arrayList
long arrayForeachStartTime = System.currentTimeMillis();
for (Integer in : arrayList) {
array = in;
}
long arrayForeachEndTime = System.currentTimeMillis();
System.out.println("用foreach循環(huán)arrayList 10萬次花費時間:" + (arrayForeachEndTime - arrayForeachStartTime) + "毫秒");
//用for循環(huán)linkList
long linkForStartTime = System.currentTimeMillis();
int link = 0;
for (int i = 0; i < linkList.size(); i++) {
link = linkList.get(i);
}
long linkForEndTime = System.currentTimeMillis();
System.out.println("用for循環(huán)linkList 10萬次花費時間:" + (linkForEndTime - linkForStartTime) + "毫秒");
//用froeach循環(huán)linkList
long linkForeachStartTime = System.currentTimeMillis();
for (Integer in : linkList) {
link = in;
}
long linkForeachEndTime = System.currentTimeMillis();
System.out.println("用foreach循環(huán)linkList 10萬次花費時間:" + (linkForeachEndTime - linkForeachStartTime) + "毫秒");
}
}
輸出測試:
用for循環(huán)arrayList 10萬次花費時間:3毫秒
用foreach循環(huán)arrayList 10萬次花費時間:3毫秒
用for循環(huán)linkList 10萬次花費時間:7037毫秒
用foreach循環(huán)linkList 10萬次花費時間:3毫秒
30. Java IO與NIO
IO | NIO |
---|---|
面向流 | 面向緩沖 |
阻塞IO | 非阻塞IO |
無 | 選擇器 |
具體見http://ifeve.com/java-nio-vs-io/
31. 反射的作用與原理
在程序運行期間獲得類里面的信息
反射的常用方法:
(1) forName(String className)
返回與帶有給定字符串名的類或接口相關(guān)聯(lián)的 Class 對象
(2) forName(String name, boolean initialize, ClassLoader loader)
使用給定的類加載器航棱,返回與帶有給定字符串名的類或接口相關(guān)聯(lián)的 Class 對象
(3) getAnnotation(Class<A> annotationClass)
如果存在該元素的指定類型的注釋睡雇,則返回這些注釋,否則返回 null
(4) getAnnotations()
返回此元素上存在的所有注釋
(5) getConstructor(Class<?>... parameterTypes)
返回一個 Constructor 對象饮醇,它反映此 Class 對象所表示的類的指定公共構(gòu)造方法
(6) getDeclaredField(String name)
返回一個 Field 對象它抱,該對象反映此 Class 對象所表示的類或接口的指定已聲明字段
(7) getDeclaredMethod(String name, Class<?>... parameterTypes)
返回一個 Method 對象,該對象反映此 Class 對象所表示的類或接口的指定已聲明方法
public class ImitateTest {
public static void main(String[] args) {
//1. 首先我們創(chuàng)建一個bean,模擬從前端獲取的數(shù)據(jù)
ImitateTest t = new ImitateTest();
Bean bean = t.getBean();
//2.生成我們需要的SQL 并設(shè)值
t.save(bean);
}
private Bean getBean() {
//模擬用反射實現(xiàn)
Bean bean = null;
try {
Class c = Class.forName("java_study.Bean");
bean = (Bean) c.newInstance();
// 私有字段無法訪問驳阎,我們通過方法賦值
Method m1 = c.getDeclaredMethod("setId", Integer.class);
Method m2 = c.getDeclaredMethod("setName", String.class);
Method m3 = c.getDeclaredMethod("setPassword", String.class);
m1.invoke(bean, 1);
m2.invoke(bean, "admin");
m3.invoke(bean, "123456");
} catch (Exception e) {
e.printStackTrace();
}
return bean;
}
//假設(shè)我們的表 就是 BEAN
private void save(Bean bean) {
Field[] fields = Bean.class.getDeclaredFields();
StringBuffer sb = new StringBuffer("INSERT INTO BEAN VALUES");
sb.append(getInsertStr(fields.length));
//這里我們可以看到SQL 已經(jīng)生成
System.out.println(sb);
//這里就是我們的JDBC 根據(jù)字段名字賦值 的操作了抗愁。
//當(dāng)然hibernate 寫得肯定會復(fù)雜很多,但是基本原理不變
}
private String getInsertStr(int fields) {
StringBuffer sb = new StringBuffer("(");
for (int i = 0; i < fields; i++) {
sb.append("?,");
}
sb.delete(sb.length() - 1, sb.length());
sb.append(")");
return sb.toString();
}
}
32. 泛型常用特點呵晚,List<String>能否轉(zhuǎn)為List<Object>
一個方法如果接收List<Object>作為形式參數(shù)蜘腌,那么如果嘗試將一個List<String>的對象作為實際參數(shù)傳進去,卻發(fā)現(xiàn)無法通過編譯饵隙。雖然從直覺上來說撮珠,Object是String的父類,這種類型轉(zhuǎn)換應(yīng)該是合理的金矛。但是實際上這會產(chǎn)生隱含的類型轉(zhuǎn)換問題芯急,因此編譯器直接就禁止這樣的行為。
Java中的泛型基本上都是在編譯器這個層次來實現(xiàn)的驶俊。在生成的Java字節(jié)代碼中是不包含泛型中的類型信息的娶耍。使用泛型的時候加上的類型參數(shù),會被編譯器在編譯的時候去掉饼酿。這個過程就稱為類型擦除(敲黑板??)榕酒。如在代碼中定義的List<Object>和List<String>等類型,在編譯之后都會變成List故俐。JVM看到的只是List想鹰,而由泛型附加的類型信息對JVM來說是不可見的。Java編譯器會在編譯時盡可能的發(fā)現(xiàn)可能出錯的地方药版,但是仍然無法避免在運行時刻出現(xiàn)類型轉(zhuǎn)換異常的情況辑舷。
//所聲明的類型參數(shù)在Java類中可以像一般的類型一樣作為方法的參數(shù)和返回值,或是作為域和局部變量的類型槽片。
//但是由于類型擦除機制何缓,類型參數(shù)并不能用來創(chuàng)建對象或是作為靜態(tài)變量的類型。
class ClassTest<X, Y , Z extends Number> {
private X x;
private static Y y; //編譯錯誤筐乳,不能用在靜態(tài)變量中
public X getFirst() {
//正確用法
return x;
}
public void wrong() {
Z z = new Z(); //編譯錯誤歌殃,不能創(chuàng)建對象
}
}
33. 解析XML的幾種方式的原理與特點:DOM、SAX蝙云、PULL
SAX解析器的優(yōu)點是解析速度快氓皱,占用內(nèi)存少。
DOM在內(nèi)存中以樹形結(jié)構(gòu)存放,因此檢索和更新效率會更高波材。但是對于特別大的文檔股淡,解析和加載整個文檔將會很耗資源。
PULL解析器的運行方式和SAX類似廷区,都是基于事件的模式唯灵。PULL解析器小巧輕便,解析速度快隙轻,簡單易用埠帕。
具體見http://blog.csdn.net/cangchen/article/details/44034799
34. Java與C++對比
具體見http://www.cnblogs.com/sunyoung/p/5975995.html
35. Java1.7與1.8新特性
具體見http://blog.csdn.net/ludx212/article/details/17281729
36. 設(shè)計模式:單例、工廠玖绿、適配器敛瓷、責(zé)任鏈、觀察者等等
37. JNI的使用
具體見http://blog.csdn.net/jiangwei0910410003/article/details/17465085