1.Java關(guān)鍵字總結(jié)梳理
首先這里總結(jié)一下在編寫類時常常會碰到的一些關(guān)鍵字:
private,public瑰钮,protected,default
關(guān)鍵字同一個包中的其他類不同包中的其他類子類自身privateNoNoNoYesprotectedYesNoYesYespublicYesYesYesYes無修飾(default)YesNoNoYes
注意:以上幾個修飾詞是和包有關(guān)的
static
static關(guān)鍵字修飾內(nèi)容的幾個特點:
1. static修飾的變量和類文件一同被加載到內(nèi)存中
2. 被修飾的方法可以直接通過類名加點來引用微驶,也就是說static修飾部分的引用是不需要將對象實例化的浪谴。
有關(guān)static一些注意事項:
static方法只能訪問static變量
static方法中不能使用this,super這樣的關(guān)鍵字,因為static優(yōu)先于對象被加載到內(nèi)存之中因苹,static執(zhí)行時對象可能還未被實例化苟耻。
內(nèi)部類包含static修飾的屬性或方法時,內(nèi)部類必須也被static修飾容燕,其實理解起來也很簡答梁呈,應(yīng)為static會優(yōu)先被加載,如果內(nèi)部類不被static修飾蘸秘,那么內(nèi)部變量是不會被提前加載的官卡,這時static關(guān)鍵字修飾就不起作用了蝗茁。
final
final是一個修飾詞,可修飾類寻咒,變量哮翘,函數(shù)
final修飾的類不可被繼承
final修飾的函數(shù)無法被復(fù)寫
final修飾的變量只能賦值一次
abstract
abstract同樣是一個修飾詞,能夠修飾方法和類
abstract修飾的類無法被實例化毛秘,只能夠通過子類的繼承并實現(xiàn)內(nèi)部所有的抽象函數(shù)才能被實例化饭寺。
abstract修飾的函數(shù)只需要申明方法名,參數(shù)叫挟,不需要寫函數(shù)體艰匙。
抽象類中同樣可以定義非抽象的方法,同時抽象類也有構(gòu)造函數(shù)抹恳,這個構(gòu)造函數(shù)提供給子類實例化時使用员凝。
抽象類中也可以沒有抽象的方法。
abstract不可以和static奋献,private健霹,final公用,簡單理解一下瓶蚂,static修飾說明優(yōu)先加載糖埋,而abstract未被實現(xiàn),所以無法被優(yōu)先加載窃这。final修飾表名為最終狀態(tài)無法修改瞳别,而abstract修飾的需要子類去實現(xiàn),必須可以修改钦听。private表示私有化洒试,自由自身能夠訪問到倍奢,而abstract需要子類訪問并實現(xiàn)函數(shù)體朴上。
instanceof
用于判斷類是否實現(xiàn)了指定接口或?qū)崿F(xiàn)了指定的類,舉個簡單的例子:
public class Test {
public static void main(String[] args) {
NullPointerException e = new NullPointerException();
System.out.println(e instanceof Exception);
}
}
1
2
3
4
5
6
7
8
輸出結(jié)果為true
2.面向?qū)ο?/p>
面向?qū)ο笞顬橹饕膬蓚€內(nèi)容
過程卒煞,其實也就是函數(shù)
對象痪宰,則是對一些函數(shù)和屬性進行了封裝
所以,其實在面向?qū)ο蟮木幊踢^程中畔裕,編寫類衣撬,也就是完成對函數(shù)和成員變量的封裝(當(dāng)然,在編寫前需要對類進行設(shè)計)扮饶。
接口和接口的實現(xiàn)
接口有interface關(guān)鍵字定義具练,內(nèi)部函數(shù)默認(rèn)為抽象的,所以接口無法實例化.
一個類在實現(xiàn)了接口中所有的方法時才能被實例化甜无,否則這個類還是一個抽象類扛点,無法被實例化哥遮,實現(xiàn)某個接口使用關(guān)鍵字implements
一個類可以實現(xiàn)多個接口,而且接口之間也可以相互繼承陵究,并且接口可以多繼承眠饮,一個簡單的例子:
public class Test {
interface interface1 {
void function1();
}
interface interface2 {
void function2();
}
interface interface3 extends interface1, interface2 {
}
class MyClass implements interface3 {
@Override
public void function1() {
// TODO Auto-generated method stub
}
@Override
public void function2() {
// TODO Auto-generated method stub
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
可以看到,當(dāng)MyClass實現(xiàn)了interface3,并且interface3繼承了interface1和interface2時铜邮,MyClass需要實現(xiàn)interface1和interface2中的所有方法仪召。
繼承
繼承當(dāng)中子類與父類之間的一些關(guān)系:
成員變量:當(dāng)子類中出現(xiàn)與父類相同的成員變量時,在子類中調(diào)用優(yōu)先調(diào)用子類的該變量松蒜,如果想要調(diào)用父類中的該成員扔茅,則需要使用super關(guān)鍵字。
成員函數(shù):當(dāng)子類中出現(xiàn)與父類之中相同的方法時秸苗,子類的方法會將父類中的方法覆蓋咖摹。在外部調(diào)用時,會調(diào)用子類的方法难述。
構(gòu)造函數(shù):子類的構(gòu)造函數(shù)中會默認(rèn)調(diào)用super()意味著在構(gòu)造子類之前需要先對其父類進行構(gòu)造萤晴。
多態(tài)
舉一個簡單的多態(tài)例子:
父類定義
class Father
{
void sayHi()
{
System.out.println("father hello");
}
}
1
2
3
4
5
6
7
子類定義:
class Son extends Father
{
void sayHi()
{
System.out.println("son hello");
}
}
1
2
3
4
5
6
7
測試代碼:
public static void main(String[] args) {
Father f = new Son();
f.sayHi();
}
1
2
3
4
5
6
7
輸出結(jié)果為“son hello”
可以看出,可以通過父類訪問到子類的方法胁后,這樣為我們操作大量對象提供了方便店读,當(dāng)我們需要操作大量不同對象時,我們只需要通過訪問他們的父類來操作他們共性的一部分即可攀芯。
當(dāng)然以上只是簡單的多態(tài)的例子屯断,動態(tài)在Java中有很多體現(xiàn),接口的實現(xiàn)侣诺,繼承殖演,復(fù)寫都體現(xiàn)著多態(tài)。
3.那些我們常常遇到的類
Exception
異常是程序運行期間發(fā)生的不正常的行為年鸳,Java中將異常進行了封裝趴久,并能夠使用try catch語句對異常捕獲和處理。所有異常的父類都是Exception,這些異常類的共有特點就是能夠被拋出搔确。
這里對比一下throws和throw的區(qū)別:
throws拋出的是異常的類名彼棍,出現(xiàn)在方法頭部,throws只是存在拋出異常的可能并且自身無法處理膳算,需要交給調(diào)用者來處理座硕。
private void main() {
try {
function();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void function() throws IOException {
Socket s = new Socket("", 11);
}
1
2
3
4
5
6
7
8
9
10
11
12
可以看到,我們通過throws將異常拋出涕蜂,并且在function被其他函數(shù)調(diào)用時华匾,調(diào)用者任然需要對異常進行捕獲和處理,當(dāng)然調(diào)用者也可以繼續(xù)講異常拋出机隙,等待其他的調(diào)用者對異常進行處理蜘拉。
throw拋出的是異常的對象刊头,出現(xiàn)在函數(shù)體內(nèi)部,throw一定會拋出異常诸尽≡樱看例子
public static void main(String[] args) {
String real_pass_word = "123456768";
String input_pass_word = "123456";
try {
if (!real_pass_word.equals(input_pass_word)) {
throw new IOException();
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
可以看到需要對當(dāng)密碼不相等時,我們使用throw拋出了異常IOException,并且在外層通過try catch對異常進行了捕獲您机。
Thread
首選明確一下線程和進程的區(qū)別:
進程是程序在執(zhí)行當(dāng)中分配資源和管理資源的最小單位穿肄。進程之間通常不會共享資源,每個進程獨有系統(tǒng)分配的資源际看,進程中可以包含多個線程咸产。
線程是進程中的一個程序執(zhí)行控制單元,同時也是一條執(zhí)行路徑仲闽,屬于同一個進程的線程之間共享進程內(nèi)的數(shù)據(jù)脑溢,并且可以相互之間進行通訊。
創(chuàng)建一個新的線程有兩種辦法:
- 通過Thread或繼承至Thread的類創(chuàng)建線程對象
- 通過實現(xiàn)Runable接口赖欣,并將實現(xiàn)Runable接口的對象作為參數(shù)傳入Thread()構(gòu)造方法中(這里解釋一下Runable接口出現(xiàn)的原因屑彻,由于,應(yīng)為Java中類是單繼承的顶吮,所以當(dāng)一個類既想作為一個線程也有其他必須要繼承的類時社牲,就可以通過實現(xiàn)Runable接口來將需要作為線程執(zhí)行的部分放入run方法之中)
這里有一個常見的面試題
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("哈哈哈");
}
}) {
public void run() {
System.out.println("呵呵呵");
};
}.start();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
上面的代碼運行結(jié)果輸出的“呵呵呵”,簡單的解釋一下悴了,上述代碼首先創(chuàng)建了一個Thread對象搏恤,此時run中輸出“哈哈哈”,然后我們通過Thread實現(xiàn)了一個匿名的內(nèi)部類湃交,這個類的run方法輸出“呵呵呵”熟空,最后運行輸出的結(jié)果就是“呵呵呵”了。
多線程的好處當(dāng)然是能夠更好的利用cpu搞莺,但是多線程同樣也帶來了一些問題息罗,由于同一個進程內(nèi)的多個線程共享著進程內(nèi)的資源,當(dāng)多個線程同時運行時就容易出現(xiàn)共享資源的搶奪腮敌,一個常見的情況是多個線程在共同訪問一個數(shù)據(jù)時阱当,A線程正在操作數(shù)據(jù)俏扩,這個時候B線程進入并修改數(shù)據(jù)糜工,這樣會導(dǎo)致產(chǎn)生錯誤的結(jié)果,為了解決線程訪問共享資源的問題录淡,Java提供了關(guān)鍵字synchronized捌木,以下提供一個synchronized關(guān)鍵的使用例子:
public class Test {
public static void main(String[] args) {
Example example = new Example();
Thread t1 = new Thread1(example);
Thread t2 = new Thread1(example);
t1.start();
t2.start();
}
}
class Example {
public synchronized void execute() {
for (int i = 0; i < 10; ++i) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Hello: " + i);
}
}
}
class Thread1 extends Thread {
private Example example;
public Thread1(Example example) {
this.example = example;
}
@Override
public void run() {
example.execute();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
上面的代碼對于excute方法使用了synchronized修飾,執(zhí)行結(jié)果為先打印一次0-9再打印一次0-9嫉戚,而去除synchronized關(guān)鍵字后刨裆,會同步打印澈圈,輸出結(jié)果為001122…..99。證明synchronized保證了函數(shù)被調(diào)用后執(zhí)行完才能被其他調(diào)用者再次調(diào)用帆啃。
當(dāng)然瞬女,處理synchronized方法以外,我們同樣可以使用synchronized塊來對小部分的代碼進行同步努潘,再來一個例子:
public class Test {
private static Test instance;
private Test() {
}
public static Test getInstance() {
if (instance == null) {
//鎖是類的字節(jié)碼文件诽偷,由于是靜態(tài)方法
synchronized (Test.class) {
if (instance == null) {
instance = new Test();
}
}
}
return instance;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
上面的代碼是一個典型的使用延遲加載的單例模式類,這種時候當(dāng)多線程同事調(diào)用getInstance方法時疯坤,可能出現(xiàn)創(chuàng)建多個實體的情況报慕,這樣顯然不符合單例模式的思想,所以我們需要在創(chuàng)建實體時加上代碼同步压怠。為了避免由于判斷加鎖和解鎖的過程帶來的低效眠冈,可以再同步部分再次添加判斷,避免沒有必要的同步菌瘫。從Java 5 開始蜗顽,推出了新的同步解決辦法Lock。
這部分的內(nèi)容這里有一個不錯的博文雨让,推薦給大家诫舅,傳送門點這里。
String
String類宫患,作為Java中使用最為頻發(fā)的幾個類之一刊懈,有必要好好熟悉一下,String類其實很簡單娃闲,就是對字符串的封裝虚汛,以及提供了很多方法方便我們操作字符串。這里整理了一些很好用的皇帮,但是可能被大家忽略的方法:
方法作用charAt獲取指定位置的字符indexOf順序獲取字符或字符串的位置卷哩,沒有返回-1lastIndexOf倒序獲取字符或字符串的位置,沒有返回-1subString獲取指定位置的子串contain判斷字符串是否包含指定字符串startWith判斷字符串是否以指定字符串開頭endWith判斷字符串是否以指定字符串結(jié)尾equalsIgnoreCase判斷字符串是否相等属拾,忽略大小寫toLowerCase所有字母轉(zhuǎn)換為小寫toUpperCase所有字母轉(zhuǎn)換為大寫replace替換trim去除字符串首位空格
總結(jié)完String的一些方法将谊,這里再簡單的提一下StringBuilder和StringBuffer兩個類。String是賦值后無法修改的(我們可以看到所有的關(guān)于String的操作返回結(jié)果都是一個新的String對象而不是原對象)渐白,而StringBuilder和StringBuffer是可修改的尊浓,這兩個類作用類似都用于構(gòu)建字符串,不同在于:StringBuffer對于多線程是安全的纯衍,而StringBuilder對于多線程是不安全的栋齿。所以推薦的使用情況如下:
- 單線程操作時推薦使用StringBuilder,效率更高。
- 多線程時操作推薦使用StringBuffer瓦堵,更加安全基协。
集合
Java中提供了功能強大的集合類,這里我們對Java中的集合類進行整理:
Iterator (訪問集合的迭代器接口)
Collection(單列)
List(有序菇用,可存儲重復(fù)元素澜驮,元素有索引)
Set(無序,不可存儲重復(fù)元素惋鸥,元素都是唯一的)
Map(雙列)
Hashtable
TreeMap
List接口下我們經(jīng)常用到主要為以下幾個類:
ArrayList: 底層數(shù)組實現(xiàn)泉唁,查詢速度很快,線程不同步
LinkedList:底層鏈表實現(xiàn)揩慕,增刪熟讀很快亭畜,線程不同步
Vector:底層數(shù)組實現(xiàn),查詢增刪速度都很慢迎卤,線程同步
在通過Iterator對List接口對象進行迭代時拴鸵,如果想要對集合對象進行操作,會出現(xiàn)ConcurrentModificationException的異常蜗搔,錯誤原因是迭代過程中Iterator已經(jīng)在操作集合對象了劲藐,這時我們再去操作集合對象會導(dǎo)致訪問沖突。解決辦法是使用iterator的子接口ListIterator,這個接口提供了對List集合對象的操作樟凄。
注意:由于在List集合中聘芜,判斷元素是否存在或是刪除元素都是通過元素的equals方法,所以在日常的開發(fā)過程中缝龄,通常需要自己重寫集合元素對象的equals方法汰现,這樣能夠提高List集合的操作效率。
Set接口下我們經(jīng)常用到主要為以下幾個類:
- HashSet:底層數(shù)據(jù)結(jié)構(gòu)為Hash表叔壤,效率高瞎饲,線程不同步
- LinkedHashSet:HashSet的子類,有序
- TreeSet:底層數(shù)據(jù)結(jié)構(gòu)為二叉樹炼绘,可以對之中的數(shù)據(jù)進行指定順序的排序嗅战,線程不同步
簡單介紹一下Hash表是什么:
1.對元素中特有數(shù)據(jù)進行Hash算法,并得到元素的Hash值
2.Hash值就是這個元素在表中的位置
3.在存儲過程中俺亮,如果Hash值發(fā)生沖突驮捍,則需要進行沖突解決,最簡單的一個Hash沖突解決辦法為再次判斷元素是否相同脚曾,如果相同則不存儲东且,如果不同則存儲,在原元素的Hash值的基礎(chǔ)上加1斟珊。
(提供一個Hash沖突解決的博文鏈接)
4.存儲Hash值的結(jié)構(gòu)稱之為Hash表
5.為了提高效率苇倡,應(yīng)該盡量保證元素關(guān)鍵字的唯一性富纸,這樣能夠提高Hash表的效率囤踩。
TreeSet中的元素需要是可比較的旨椒,為了保證元素可比較,需要元素實現(xiàn)Comparable接口堵漱,TreeSet中為保證元素的唯一综慎,是通過Comparable接口的toCompare方法來實現(xiàn)的,當(dāng)toCompare方法返回0時勤庐,表示兩個元素相同示惊。
Map
Map接口與Collection有很大的區(qū)別,Map一次存儲一個鍵值對,并且需要保證Map中所有的健是唯一的愉镰。
迭代Map的方法:
通過Map.keySet()獲取健的Set,然后再遍歷時通過getKey方法迭代
通過Map.entrySet()方法獲取到鍵值對set米罚,直接遍歷。
這里總結(jié)一下集合的使用規(guī)律:
當(dāng)需要存儲的是單個數(shù)據(jù)時考慮使用Collection丈探,當(dāng)需要存儲的內(nèi)容是鍵值對形式的數(shù)據(jù)使用Map
需要保證內(nèi)部元素唯一用Set录择,不要需要使用List
看到Array說明底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組,證明查詢速度快
看到Linked說明底層數(shù)據(jù)結(jié)構(gòu)是鏈表碗降,增加刪除的速度開
看到Hash說明底層數(shù)據(jù)結(jié)構(gòu)是Hash表隘竭,需要盡量保證內(nèi)部元素的Hash值唯一,并且需要復(fù)寫元素的hasCode方法和equals方法讼渊。
看到Tree就說明底層的數(shù)據(jù)結(jié)構(gòu)是二叉樹动看,需要相當(dāng)排序,內(nèi)部元素需要Comparable
泛型
泛型只是針對編譯時期爪幻,在運行時期并不存在泛型的概念菱皆,泛型只是為了將一些類型強制轉(zhuǎn)換的異常轉(zhuǎn)化為編譯錯誤。使用泛型時必須保證等式兩邊聲明的泛型是一樣的挨稿。
泛型的上限與下限:
搔预?extends E 泛型指 接受E和E的所有子類
? super E 泛型指接受E和E的所有父類
IO Stream
首先明確一下字符流和字節(jié)流的區(qū)別(這個問題今天早上把我一個電話面阿里的同學(xué)給難住了,有必要好好記一下)
字節(jié)流:字節(jié)流可以處理幾乎計算機當(dāng)中的所有數(shù)據(jù)(凡是以InputStream和OutputStream結(jié)尾的都為字節(jié)流)
字符流:字符流的出現(xiàn)是應(yīng)為叶组,各個國家的語言不通拯田,字符也不通,所以當(dāng)將各總編碼表和流封裝在一起甩十,為了方便字符的操作船庇,所以設(shè)計到字符操作的時候優(yōu)先考慮字符流(凡是以Reader和Writer結(jié)尾的都是字符流)
開發(fā)的時候如何明確該使用什么樣的流呢:
如果需要讀入數(shù)據(jù)使用InputStream和Reader,寫入數(shù)據(jù)使用OutputStream和Writer
如果需要處理純文本對象 使用Reader和Writer侣监,否者使用InputStream和OutputStream
明確使用那個具體的流鸭轮,通過明確具體操作的數(shù)據(jù)設(shè)備:(磁盤)File,(內(nèi)存)CharArray。橄霉。窃爷。
是否需要利用緩存提高效率,如果需要可以使用Buffer對流進再一次的封裝。
Java部分到這里基本整理完畢了按厘,4天的時間医吊,也算是查漏補缺,感覺的確再看一遍書又有了一些提高逮京。這里整理的是我不太熟悉的一些知識點卿堂,并不一定全面