java面试总结之一
前言
從事java開發也有個三年多了,本著不本分的精神想出來蹦噠蹦噠,最近經歷了比較痛苦的一個月,面試了大大小小的十多家公司,有過欣喜,有過悲傷,有過期待,當然也有失望,個人覺得這是一個值得懷念的經歷,所以想把這些寫下來,也希望能幫助到一些和我一樣為夢想而努力的人。此篇博客主要講述一些面試遇到的技術問題,部分地方可能不夠詳盡,大家可以根據題意再深入學習之。
一、自我簡介
此處不是我們重點關注的地方,但是切記不要太過緊張。首先,說話的時候要自然得體,不要顯得沉悶,要給人一種親和、熱情、開朗的感覺,性格往往也是決定面試的關鍵因素之一;其次,要在自我簡介中突出自己的優勢,比如學歷、學校或者年齡等等,不要覺得簡歷上都有,面試官對基本信息都是一掃而過的,你說出來會讓他加深記憶;再次,要懂得適度謙虛,如我是XX大學的,我們學校是985院校,后面這句是裝X用的,會給你的面試打折扣。對于自己不是很了解的問題,可以虛心請教,也會給人一種好學的趕腳;最后,要懂得察言觀色,根據面試官的長相和言行舉止get到他的性格,投其所好,這個看個人能力了。
二、java筆試篇
? ? ? 很多公司還是有這個惡心的過程的(筆試題都是基礎題,可以自行百度),即使沒有筆試,面試官基本也會讓你寫兩個算法,通常來說都比較相似,總結如下:
1、冒泡排序
// 冒泡算法void sort(int[] array) {if (array == null || array.length == 0) {// 數據校驗return;}int tem;// 臨時變量for (int i = 0; i < array.length - 1; i++) {for (int j = i + 1; j < array.length; j++) {if (array[i] > array[j]) {// 交換位置tem = array[i];array[i] = array[j];array[j] = tem;}}}}冒泡算法是比較基礎的算法,更多的需要了解一下思路,這里不再多說。寫代碼時需要注意的幾點,思路、邊界值驗證、注釋、命名規則以及代碼格式,可以從側面提現一個人的編碼風格。
補充:上面的算法和網上的略有不同,此算法是將最小值放到數組的首位,網上給的算法是將最大值放到數組的末尾,其原理是一樣的,這里貼上最大值放到數據末尾的代碼
1 static void sort(int[] array) { 2 if (array == null || array.length == 0) { 3 return; 4 } 5 for (int i = 0; i < array.length - 1; i++) {// 控制循環次數 6 for (int j = 0; j < array.length - 1 - i; j++) {// 控制每次循環排序次數 7 if (array[j] > array[j + 1]) { 8 int temp = array[j]; 9 array[j] = array[j + 1]; 10 array[j + 1] = temp; 11 } 12 } 13 } 14 }2、快速排序
1 // 分割數組 2 static int partition(int[] array, int low, int high) { 3 int key = array[low]; 4 while (low < high) { 5 while (key <= array[high] && low < high) { 6 high--; 7 } 8 array[low] = array[high]; 9 while (key >= array[low] && low < high) { 10 low++; 11 } 12 array[high] = array[low]; 13 } 14 array[high] = key; 15 return high; 16 } 17 18 // 快速排序 19 static void quickSort(int[] array, int low, int high) { 20 // TODO 數據校驗 21 if (low >= high) { 22 return; 23 } 24 int index = partition(array, low, high); 25 quickSort(array, low, index - 1); 26 quickSort(array, index + 1, high); 27 }快速排序就是"分割-遞歸"的思想,只要理解思想之后,代碼很easy,數據校驗這塊包括null的判斷,面試的時候寫個標記就OK,讓面試官知道你有這方面的考慮。
3、二分查找法
1 /** 2 * 二分查找法 3 * @param array 4 * @param low 5 * @param high 6 * @param key 要查詢的值 7 * @return 8 */ 9 static int binarySearch(int[] array, int low, int high, int key) { 10 // TODO 數據校驗 11 if (array[low] > key || array[high] < key) { 12 return -1;// 未找到 13 } 14 int mid = (low + high) / 2;// 中位數 15 if (array[mid] < key) {// 在左側 16 return binarySearch(array, mid + 1, high, key); 17 } else if (array[mid] > key) {// 在右側 18 return binarySearch(array, low, mid - 1, key); 19 } else if (array[mid] == key) {// 找到值 20 return mid; 21 } 22 return -1; 23 }二分查找法又名折半查找法,思想相對來說還是比較好理解的。
4、其他算法
面試的時候還會問到一些其他的算法問題,沒有具體的標準答案,無非就是根據題目的思想完成代碼的編寫,如果實在寫不出來,可以用偽代碼或者文字來描述,切記不要說不會,如果真的一點思路沒有,可以讓面試官給點提示,就看個人的發揮水平了,這里簡單說幾個面試遇到的問題。
1)在一個int數組里,記錄著一個人參加社保的年度,中間可能出現斷檔的情況,求這個人連續參保的最大年限,數組形式如下:
1 int[] array = {1990, 1991, 1992, 1993, 1994, 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2005, 2006, 2007 ...};思考:觀察數組,理解題意,無非就是求最大連續的元素個數
解決方法:可以通過一個for循環,循環數組的每個元素,比較相鄰的兩個元素是否相差1,如果是則計數器+1,否則,比較當前計數器的值與最大值的小大,將二者較大的賦值給最大值,然后重置計數器,最終返回最大值就OK;
代碼如下:
1 static int getMaxLength(int[] array) { 2 // TODO 數據校驗,null和長度為1的校驗 3 int max = 0;// 最大值 4 int count = 1;// 計數器 5 for (int i = 0; i < array.length - 1; i++) {// 遍歷至lengh-1即可,否則會數組下標越界 6 if (isContinue(array[i], array[i + 1])) {// 連續,計數器+1 7 count++; 8 } else {// 不連續,將max和count較大的賦值給max,并重置計數器 9 max = max > count ? max : count; 10 count = 1; 11 } 12 } 13 return max; 14 } 15 16 private static boolean isContinue(int x, int y) { 17 return (x + 1) == y; 18 }2)在一臺內存只有1G的電腦磁盤中,有一個10G大的數據文件,里面存儲的都是整數,請找出這10G大小的整數中最小的前100個。
思考:內存有限,無法一次性將數據全部加載到內存進行排序,考慮文件分割的方式;一大份數據中最小的前100個,也就是從n小份數據各自最小的前100個集合中選取最小的前100個。
解決方法:將文件平均分成10份,每份大小為1G,依次在內存中進行堆排序(其他排序也OK,這里不是重點),選出最小的前100個數字,之后再從10份最小的前100個數字集合中選擇最小的前100個就是結果。
PS:也可以將數據存儲到數據庫之類的系統中進行排序,不過這應該屬于投機取巧的方式,不推薦首先使用,如果上述辦法能想出來再說這個。
3)其他問題
a)鏈表是否有環的問題,使用不同步長去遍歷鏈表,如果經過n次循環之后,不同步長處于同一元素節點,說明鏈表有環。
b)去除數組重復元素,放入set即可
c)12345-->一萬兩千三百四十五,每次對數字進行除10的操作,使用stringbuilder的insert方法來實現
上述問題可以自行百度搜一下,這里不再闡述。算法是面試中不可獲取的一部分,而且每個面試官的問題也有所不同,所以不要死記硬背,掌握基本的算法原理,根據面試官的需求去解決問題,腦洞大開吧!這里做個友情提示,不要只局限于代碼邏輯和java,可以適當的使用一些類或者其他工具的特性來實現,比如BlockingQueue,Atomic類,ThreadLocal,CountDownLatch,CycleBarrier,Semaphore等等,甚至可以使用redis等,諸如redis的list實現隊列或者棧的功能,進行數組的交集、并集、差集的運算。
三、java基礎篇
1、集合
java的集合類是開發中經常使用的,諸如ArrayList、LinkedList、HashSet、HashMap、TreeMap、LinkedHashMap等等,相信很多人都了解list、set、map的區別,這里也不做闡述。面試主要的關注點有以下幾方面:
1)數據結構
ArrayList-->基于數組實現的,可以自動擴容,每次擴容1.5倍,初始容量為10;查詢速度快,添加刪除速度慢(這里才是重點,為什么慢,因為要移動元素的位置)
LinkedList-->基于雙向鏈表實現的;查詢速度慢,添加刪除速度快(移動指針即可)
HashMap-->基于數組+單鏈表實現的,允許有一個空的key(空的key放在數組第一個位置,每次get/put方法都會優先考慮空的key)和多個空的value
TreeMap-->基于紅黑樹實現的,可以對數據進行排序;紅黑樹是啥?有興趣的可以自行研究學習,反正我看了倆點沒懂,一般的面試官也說不出個所以然來,只要知道是一個有顏色的平衡二叉樹即可,每次添加刪除元素都是基于二叉樹實現的,然后進行變形補色轉成紅黑樹。
HashSet-->和HashMap一致,沒什么好說的,只不過value是new Object()而已。
LinkedHashMap-->繼承HashMap,除了擁有父類的數據結構,內部還維護著一個雙向鏈表,用來存儲數據的順序,可以根據accessOrder(boolean值)屬性來設置根據插入順序排序還是根據訪問順序排序,默認是false,按照插入順序排序。新數據放在雙向鏈表的尾部,所以可以用來實現lru(最近最少使用)算法,第一個元素就是最近最少訪問的。
2)線程安全
線程安全的集合類,我們能接觸的只有hashtable,可以做適當的了解(內部都是synchronized方法),其他上述的集合類都是線程不安全的,在多線程情況下使用時需要注意,可以使用一些類帶代替,如ConcurrentHashMap。也可以使用Collections.synchronized系列的方法來實現對非線程安全的集合類轉成線程安全的。這里需要提出的是Collections的同步方法其實是生成一個靜態內部類,這個類的方法中都使用了同步代碼塊,所以具有線程安全的作用,可以簡單的看一下源碼,非常簡單,萬一面試官問你Collections類怎么實現線程安全的,你可以好說出個一二來,我就被問過,當然我肯定沒答上來,見笑!
3)源碼
源碼不要求把所有的集合類源碼都記住,但是HashMap一定要十分熟悉,包括get/put方法的流程,put時需要注意什么(作為key的對象要不可變的,可變的話hashcode就變了;hash碰撞,這也是為什么key的對象要重寫hashcode和equals方法;resieze,然后觸發rehash,會問你擴容的流程),再有就是加載因子這個參數的意義,閥值=容量*加載因子,當hashmap大小達到閥值時就會自動擴容,所以使用hashmap時,如果知道確切的大小,最好初始化的時候就指定大小,防止resize發生。另外一點作為了解的就是hashmap的容量永遠是2的n次方,你初始化時設置的容量大小會向上增加至最近的一個2的n次方,比如你設置容量大小為10,因為23=8,24=16,所以實際hashmap的容量是16(初始化時設置9,10,11,12,13,14,15,其結果都一樣),主要是為了進行hash運算的方便,當length為2的n次方時,hashcode%lengh == hashcode & (length - 1)。其他的如ArrayList和LinkedList源碼相對較簡單,HashSet和HashMap是一樣的,熟悉即可。
2、IO、線程和反射
1)IO
BIO和NIO的區別,有興趣的同學可以了解一下NIO,一般網絡開發的話都會有NIO的要求,不過我這塊比較渣,只知道selector,buffer,channel這些。
IO使用的設計模式,裝飾模式,在緩沖流中會用到,根據io流創建緩沖流,然后在創建之前和之前做了一些操作,有點像代理模式,這個地方可以了解一下
2)Thread
繼承Thread類或者實現Runnable接口,推薦實現接口(java單繼承的原因)。線程的五種狀態,新建、就緒、運行、阻塞和死亡。更多的會使用線程池,后面會講到。
3)反射
其實個人覺得反射這塊沒有什么好問的問題,但是如果不會反射的人不能說會java,在面試的時候遇到private之類的東西問你怎么訪問,果斷反射懟上去,絕對沒問題的。
3、異常
1)Error,嚴格來說,Error不屬于異常,Error和Exception屬于Throwable,常見的Error就是OutOfMemoryError和StackOverFlowError,前者是內存溢出,出現的原因就是內存不足,可能是內存碎片較多而無法申請足夠大的連續空間造成的,這個可能和GC算法有關,也可能是java堆過小等等;后者是棧溢出,方法中調用另一個方法的時候就是入棧,返回數據的時候就是出棧,如果調用的深度過高就會出現這種情況,常見出現這種情況的原因就是遞歸調用次數太多。這兩個Error要有所了解,包括是什么,為什么會出現。
2)運行時異常,常見的空指針,數組下標越界,格式轉換錯誤等等。通常都可以通過修改代碼來解決,不需要捕獲,繼承至RunTimeException。
3)非運行時異常,常見的FileNotFoundException、IOException等,這種異常在編譯階段就可以發現,可以通過try/catch來捕獲處理,或者throws到上一層進行集中處理。
4、類加載器
java的類加載器分為四種,BootstrapClassLoader(啟動類加載器,C語言編寫,所以java里為null),ExtentionClassLoader(擴展類加載器)、ApplicationClassLoader(應用類加載器)和用戶自定義的ClassLoader。不同的classloader加載同一個類時,返回的是不同的實例,因為命名空間不一樣。classloader采用雙親委派模式來加載類,即一個類加載器在加載類之前會先讓加載的請求讓上一級類加載器去執行,如果上一級類加載器加載不到再由自己去加載,這樣做的好處是安全性的考慮。
四、java高級
1、線程池
1)Executors類創建的四種線程池都是什么,有哪些特性?
a)newCachedThreadPool,創建一個阻塞隊列為SynchronousQueue的線程池,核心線程數為0,線程最大值為Integer的最大值,線程的失效時間為一分鐘,其阻塞隊列不存儲任務,也就是說,當新來一個任務時,如果沒有空閑線程,則創建一個新的線程去執行。
b)newFixedThreadPool,根據參數創建一個核心線程數和最大線程數一致的線程池,其阻塞隊列為LinkedBlockingQueue,大小為Integer的最大值,默認線程池中的線程均不會失效,當新來一個任務時,如果沒有空閑線程,則將任務放入阻塞隊列中。
c)newSingleThreadExecutor,創建一個單線程的線程池,能保證所有的任務按照順序執行,當這個唯一的線程消亡后,會產生的一個新的線程繼續執行任務。
d)newScheduledThreadPool,創建一個可以定時循環執行任務的線程池。
2)自定義ThreadPoolExecutor
在實際的開發中,上述的四種線程池可能無法滿足我們的需求,我們可以根據實際情況通過new?newScheduledThreadPool()去自定義線程池,也可以使用spring的org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor來創建線程池,其實spring的底層也是通過new?newScheduledThreadPool()來創建,不過spring的bean默認是單例的,使用起來更加方便。關于new?newScheduledThreadPool()的參數需要做一個了解,包括Reject策略。
3)線程池的流程
首先,判斷線程池中線程個數是否達到核心線程數,如果沒有達到,則創建一個核心線程去執行任務;如果已經達到核心線程數,則執行第二步;其次,判斷阻塞隊列是否已滿,如果阻塞隊列未滿,則將任務放入阻塞隊列,等待任務被執行;如果阻塞隊列已滿,則執行第三步;再次,判斷線程池中線程個數是否已經達到最大線程數,如果未達到,則創建一個緩存線程來執行任務;如果已經達到,則根據拒絕策略執行任務處理。
思想就是:核心線程優先創建,其次是向阻塞隊列中存放任務,再次是創建緩存線程(創建線程的代價比較高,所以能使用阻塞隊列盡量使用阻塞隊列,個人覺得設計者應該是這么考慮的),最后任務實在沒有地方能處理或存放,只能根據拒絕策略來處理了。
4)線程池如何維護線程,即緩存線程如何消亡,核心線程如何一直工作?在runWork方法中有一個while循環一直調用getTask方法從阻塞隊列中獲取任務,由于阻塞隊列的take()方法可以設置時間,當在規定時間內沒有獲取到任務,則返回false,線程跳出while循環,執行完畢即死亡,而核心線程會一直處于while循環中,會一直阻塞直到阻塞隊列有新的任務,從而不會死亡。
5)線程池如何保證同一個任務只被一個線程執行?ReentrantLock加鎖來實現
2、鎖
在java中鎖分為樂觀鎖和悲觀鎖,所謂的悲觀鎖就是真正意義上的加鎖,即同一時間內只有一個線程能獲取數據的訪問權限,其他線程需要阻塞等待;樂觀鎖只是概念上的鎖,實際中并沒有加鎖的操作,其思想就是樂觀的認為對象并沒有被其他線程所修改,根據自己的預期值和對象的實際值進行比較,如果結果一致則進行訪問處理,如果不一致,則在此過程中有其他線程對該對象進行了訪問修改,會重新循環獲取并操作,直到預期值和對象實際值一致為止。樂觀鎖是基于CAS(compare and swap)的思想,例如在java中的Atomic類的方法。CAS會帶來一個叫ABA的問題,即一個對象的值從A到B再到A的變化過程,對于另一個線程來說是感覺不到的,因為第二個線程兩次拿到的值均為A。通常我們在數據庫中添加一個冗余字段version記錄當前數據的版本,每次修改時,version做加1的操作。在多線程情況下,每次修改數據庫數據時,可以在where條件中添加一個version的判斷,如果version是最新的值,則允許修改,否則說明有其他用戶或其他線程對該數據進行過修改,拒絕當前修改操作。
synchronized和ReentrantLock,syschronized是jvm來實現同步的,ReentrantLock是通過代碼來控制的,需要加鎖和解鎖(finally里解鎖),內部有一個計數器,加鎖時計數器+1,解鎖時計算器-1,當計數器為0時才釋放鎖,可重入鎖,但加鎖幾次也要解鎖幾次。也可以鎖等候,即加鎖時可以設置等待時間。
3、volatile關鍵字
有些公司會問此關鍵字的含義,volatile和synchronized的區別等等。在多線程開發中,對于共享變量的修改,其實是在線程私有區對共享變量做了一個拷貝,每次的讀取和修改都是針對本地的拷貝變量,在線程退出之前,會將本地的拷貝寫回共享變量。volatile修飾的變量會避免這種方式,每次強制線程從共享區讀取數據,并將修改的數據馬上寫回。但volatile只支持可見性,并不支持原子性。所謂的可見性就是當一個線程對一個共享數據做修改時,另一個線程能馬上發現該值被修改;原子性是指,從獲取該對象的值到修改,最后寫回共享數據的過程是原子性的,也就是說volatile并不能保證線程安全。
volatile關鍵字的另一個作用是在字節碼中設置一個柵欄(字節碼中有一個lock字樣),我們都知道,在編譯階段會對代碼進行重排序,volatile能保證在它之前的代碼不會因為重排序而在它之后執行,在它之后的代碼也不會因為重排序在它之前執行。
4、并發包
并發包下最常見的類就是ConcurrentHashMap,熟悉ConcurrentHashMap、HashMap和HashTable的區別。HashMap是線程不安全的,HashTable通過同步方法對整個數組加鎖,而ConCurrentHashMap是兩者的折中,采用二次hash,降低了鎖的粒度。ConCurrentHashMap將內部的數據分成一個個的段(segment),每次只對某個段進行加鎖操作,比如線程t1訪問的數據在第一個段,則對第一個段加鎖,而線程t2訪問的數據在第二個段,則不會因為第一個段加鎖而阻塞等待。建議看一看源碼
再有就是CountDownLatch、CycleBarrier、Semaphore工具類的功能,前兩者的區別,CountDownLatch采用計數器減一的策略,阻塞的是主線程;CycleBarrier采用計數器加一的策略,阻塞的是同時運行的多個線程
5、jvm和gc
1)jmm(java內存模型)
包括線程私有區和線程共享區,線程私有區又分為線程私有棧(我通常說的棧,存放基本數據類型、對象引用等)、程序計數器(記錄下一行該執行程序的位置)、本地方法棧(執行本地方法時使用);線程共享區分為方法區(HotSpot又稱永久代,存放class信息等)和java堆(存放對象的共享區域)。
2)內存分代
按照分代算法的思想,將內存分為年輕代、年老代和永久代。其中年輕代包括一個Eden區和兩個Survivor區,大部分對象在Eden區中生成。當Eden區滿時,還存活的對象將被復制到Survivor區(兩個中的一個),當這個 Survivor區滿時,此區的存活對象將被復制到另外一個Survivor區,當這個Survivor去也滿了的時候,從第一個Survivor區復制過來的并且此時還存活的對象,將被復制年老代。需要注意,Survivor的兩個區是對稱的,沒先后關系,所以同一個區中可能同時存在從Eden復制過來對象,和從前一個Survivor復制過來的對象,而復制到年老代的只有從第一個Survivor區過來的對象。而且,Survivor區總有一個是空的。同時,根據程序需要,Survivor區是可以配置為多個的(多于兩個),這樣可以增加對象在年輕代中的存在時間,減少被放到年老代的可能。
3)gc算法
常用的算法有,標記-清除、標記-整理、復制算法,根據分代的思想,不同地方使用不同的算法。在年輕代,由于對象存活率較低,所以采用復制算法,可以整理內存空間,避免內存碎片;在年老代,基于垃圾收集器的不同采用不同的算法;垃圾收集器分為串行、并行和并發三種。典型的CMS(并發)和G1(并行),串行使用的較少,會嚴重阻塞用戶線程。
6、簡單的linux命令
對于linux命令不熟悉的同學需要花點時間去學習一下,只要掌握常用的就OK。一般面試問到的如下:
kill——殺進程
netstat——查看端口號,可根據參數執行類別(-a 所有,-e 連接,-l 監聽,-t tcp協議的,-u udp協議的)
free——查看內存情況
top——查看服務器CPU、內存等情況
df——查看磁盤情況
tail——查看日志
其他的適當也了解一些吧,畢竟服務器基本都是linux系統的,將來連切換目錄都不知道就尷尬了。
還有一些jvm自帶的命令也需要了解一些:
jps——可以看到當前運行了多少個jvm以及進程號
jmap——查看內存情況
jstat——gc次數
最常問的問題就是,系統執行的慢了,怎么排查,無非就是通過top命令查看cpu使用率高的進程號,然后通過ps -mp pid -o THREAD,tid定位到具體的線程,printf "%x\n" tid將tid轉成16進制的數,最后使用jstack pid|grep 16進制的tid -A 30打印出日志,其中30表示打印的行數。或者通過top命令查看cpu使用率高的進程號pid之后,使用top -H -p pid查詢這個進程下線程的情況,-H會顯示出線程號tid,最后使用jstack tid > 指定文件,將日志信息dump到指定文件。這里涉及到另外一個問題就是線程和進程的區別,可以自行查詢,這里不再累述。
7、設計模式
單例模式:spring bean默認是單例的,自己也要會寫單例模式,推薦枚舉或靜態內部類方式。
工廠模式:Executors就是簡單工廠模式,再有就是工廠方法模式,通過不同的實現類對同一個接口的不同實現方式,從而達到生產不同產品的目的,有點多態的意思。抽象工廠模式應用的不算太多,大概的意思就是一個接口里多個抽象方法,每個實現類根據各個抽象方法的產品進行組合,最后返回給用戶,比如造車的工廠可以生產白色的寶馬車、黑色的奔馳車,也可以生產黑色的寶馬車和白色的奔馳車,其中顏色和車就是一種組合。
代理模式:spring的AOP,mybatis的接口綁定都是使用的代理模式,常見的代理方式有兩種,jdk proxy和cglib,兩者的區別就是jdk proxy要求被代理類必須實現接口,cglib沒有這個約束,因為生成的代理類是被代理類的子類。
策略模式:通過不同的實現方式進而達到同一目的,比如我做圖片上傳的時候,你可以提供一個圖片的URL,也可以提供一個圖片的IO,最終的目的是一樣的,方式可以讓用戶來選擇,spring加載配置文件也是如此。
模板方法:定義了整個流程或者算法,具體的細節通過子類去逐步實現,典型的就是spring的dao模塊,里面的preparedstatement對象是由用戶來創建生成的,通常我們都是使用new?PreparedStatementCreator的方式創建一個匿名內部類。
觀察者模式:spring啟動的時候是通過ContextLoaderListener來實現的,再有就是active mq的消費者也可以通過Listener來消費消息。
生產者-消費者模式:active mq就是最經典的。
裝飾模式:前面提到的緩沖流
其實設計模式就是一個概念級別的東西,有時候你可能不知道卻無意間使用了,所以一般面試官不會在這個地方為難你,除非你連單例、工廠和代理都不知道!
至此,java相關的面試題大概這么多,里面可能有些地方描述的不夠準確或不夠詳細,希望大家及時指出更正,之后會更新其他相關技術的面試經驗,敬請期待!面試過程中切記不要被面試官牽著鼻子走,更要將被動轉為主動,不要人家問了你才說,人家不問你只字不提,記得把相關的知識點都說出來,這樣才能給自己加分!希望能幫助更多的人找到更理想的工作~如果覺得還可以或者對你有幫助,可以打賞呦,我會將所有的錢捐給騰訊公益!
?一分也是愛 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?一元見真情
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接:http://www.cnblogs.com/1ning/p/6692866.html
轉載于:https://www.cnblogs.com/1ning/p/6692866.html
總結
以上是生活随笔為你收集整理的java面试总结之一的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 行走即告别
- 下一篇: HDU 1043 Eight(八数码)