程序員の面試題
程序員の面試題
金三銀四日,將現如今會碰到的面試題 整理記錄。
中間件
- tomcat
- Weblogic
- JBOSS
- Coldfusion
- Websphere
- GlassFish
SQL效率優化
1.建立索引
索引建立時應有以下事項:
- 應盡量避免在
WHERE子句中對字段進行NULL值判斷; - 應盡量避免在
WHERE子句中使用!=或<>操作符; - 應盡量避免在
WHERE子句中使用OR來連接條件; IN和NOT IN。<>、NOT IN、NOT EXIST;非常重要!LIKE中%在左邊;- 避免在
WHERE條件中,在索引列上進行計算或使用函數,因為這將導致索引不被使用。
2.索引條件
- 頻繁作為查詢條件的字段應該創建索引;
- 查詢中與其他表有關聯的字段,例如外鍵關系;
- 頻繁更新的字段不適合創建索引,因為每次更新不單單是更新記錄,還會更新索引,保存索引文件;
WHERE條件里用不到的字段,不創建索引;- 高并發的情況下一般選擇復合索引;
- 查詢中排序的字段創建索引將大大提高排序的速度(索引就是排序加快速查找);
- 查詢中統計或者分組的字段;
- 表記錄太少的情況,不需要創建索引;
- 經常增刪改的表不要建立索引;
3.SQL 注意事項
- 任何地方都不要進行全表檢索,使用
SELECT * FROM Table; - 盡量避免大事務操作,提高系統并發能力
- 盡量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。
JAVA日常使用
1.鎖表
: SELECT * FROM products WHERE id='3' FOR UPDATE;
鎖是什么?
鎖有兩種鎖,即悲觀鎖和樂觀鎖;
悲觀鎖:對于同一個數據的并發操作,悲觀鎖認為自己在使用數據的時候一定有別的線程來修改數據,因此在獲取數據的時候會先加鎖,確保數據不會被別的線程修改。
樂觀鎖:而樂觀鎖認為自己在使用數據時不會有別的線程修改數據,所以不會添加鎖,只是在更新數據的時候去判斷之前有沒有別的線程更新了這個數據。如果這個數據沒有被更新,當前線程將自己修改的數據成功寫入。如果數據已經被其他線程更新,則根據不同的實現方式執行不同的操作(例如報錯或者自動重試)。
在
Java中,synchronized關鍵字和Lock的實現類都是悲觀鎖。
Lock和synchronize 兩者區別:
- 首先
synchronized是java內置關鍵字,在jvm層面,Lock是個java類; synchronized無法判斷是否獲取鎖的狀態,Lock可以判斷是否獲取到鎖;synchronized會自動釋放鎖(a 線程執行完同步代碼會釋放鎖 ;b 線程執行過程中發生異常會釋放鎖),Lock需在finally中手工釋放鎖(unlock()方法釋放鎖),否則容易造成線程死鎖;- 用synchronized關鍵字的兩個線程1和線程2,如果當前線程1獲得鎖,線程2線程等待。如果線程1阻塞,線程2則會一直等待下去,而
Lock鎖就不一定會等待下去,如果嘗試獲取不到鎖,線程可以不用一直等待就結束了; synchronized的鎖可重入、不可中斷、非公平,而Lock鎖可重入、可判斷、可公平(兩者皆可)Lock鎖適合大量同步的代碼的同步問題,synchronized鎖適合代碼少量的同步問題。
2.線程
線程是進程的子集,一個進程可以擁有很多線程,每個線程并行執行不同的任務。不同的進程使用不同的內存空間,所有的線程共同享用相同的內存空間。別把線程和棧內存搞混,每個線程都擁有獨立的棧內存來存儲本地數據。
在 Java 中實現多線程有兩種手段,一種是繼承 Thread 類 ,另一種就是實現 Runnable 接口
覆寫run()方法,作為線程 的操作主體 ,如果一個類繼承 Thread類,則不適合于多個線程共享資源,而實現了 Runnable 接口,就可以方便的實現資源的共享。
一個多線程的程序如果是通過 Runnable 接口實現的,則意味著類中的屬性被多個線程共享,那么這樣就會造成一種問題,如果這多個線程要操作同一個資源時就有可能出現資源同步問題。
為什么要使用線程?
- 耗時的操作使用線程,提高應用程序的相應速度;
- 并行操作時使用線程,如C/S架構的服務器并發線程響應的請求;
- 多CPU系統中,使用線程提高CPU利用率;
- 改善程序結構,一個既長又復雜的進程可以考慮分為多個線程,成為幾個獨立或者半獨立的運行部分,這樣的程序會利于理解和修改。
// synchronized 代碼使用方法
synchronized(同步對象){
需要同步的代碼
}
3.Java 特性
封裝(Encapsulation) :
封裝:是指隱藏對象的屬性和實現細節,僅對外提供公共訪問方式。
優點:
- 將變化隔離。
- 便于使用。
- 提高重用性。
- 提高安全性。
private關鍵字:
是構造方法,無返回值,可以有參數,與類同名,jvm加載類時自動調用(變量,代碼塊,函數)
String類型:
String的值是不可變的,這就導致每次對String的操作都會生成新的String對象,不僅效率低下,而且浪費大量優先的內存空間 StringBuffer是可變類,和線程安全的字符串操作類,任何對它指向的字符串的操作都不會產生新的對象。每個StringBuffer對象都有一定的緩沖區容量,當字符串大小沒有超過容量時,不會分配新的容量,當字符串大小超過容量時,會自動增加容量。
不可變類: 只是它的實例不能被修改的類
| 可變類 | 不可變類 |
|---|---|
| 速度更快 | 速度較慢 |
| 線程不安全 | 線程安全 |
| 單線程操作字符串 | 多線程操作字符串 |
HashTable
是較為遠古的使用Hash算法的容器結構了,現在基本已被淘汰,單線程轉為使用HashMap,多線程使用ConcurrentHashMap。
異常和錯誤
-
異常和錯誤同屬于一個類:
Throwable;
異常和錯誤的區別是:異常能被程序本身可以處理,錯誤是無法處理。 -
處理異常的三個部件:
try,catch,finally -
輸入輸出異常:
IOException, -
數組越界異常:
ArrayIndexOutOfBoundsException, -
空指針異常:
NullPointerException;
繼承(inheritance):
繼承是面向對象最顯著的一個特性。 繼承是從已有的類中派生出新的類, 新的類能吸收已有類的數據屬性和行為,并能擴展新的能力。
extends。重寫父類方法
super關鍵字是一個特殊的變量, 它提供了對父類的方法。 可以用super主動調用父類的構造方法、 訪問父類中的成員。
多態(polymorphism)
在面向對象語言中, 多態性是指一個方法可以有多種實現版本,即“一種定義, 多種實現”。 利用多態可以設計和實現可擴展的系統, 只要新類也在繼承層次中。 新的類對程序的通用部分只需進行很少的修改, 或不做修改。 類的多態性表現為方法的多態性,方法的多態性主要有方法的重載和方法的覆蓋。
重載:
方法重載(overload):指在同一個類中的多個方法可以同名但參數列表必須不同。 重載表現為同一個類中方法的多態性。
方法重寫(override):指子類中定義了父類中同名的方法。 重寫表現為父子與子類之間方法的多態性。
jdk 1.8特性
default關鍵字
在java里面,我們通常都是認為接口里面是只能有抽象方法,不能有任何方法的實現的,那么在jdk1.8里面打破了這個規定,引入了新的關鍵字default,通過使用default修飾方法,可以讓我們在接口里面定義具體的方法實現,
Lambda 表達式
Lambda表達式是jdk1.8里面的一個重要的更新,這意味著java也開始承認了函數式編程,并且嘗試引入其中。
首先,什么是函數式編程,引用廖雪峰先生的教程里面的解釋就是說:函數式編程就是一種抽象程度很高的編程范式,純粹的函數式編程語言編寫的函數沒有變量,因此,任意一個函數,只要輸入是確定的,輸出就是確定的,這種純函數我們稱之為沒有副作用。而允許使用變量的程序設計語言,由于函數內部的變量狀態不確定,同樣的輸入,可能得到不同的輸出,因此,這種函數是有副作用的。函數式編程的一個特點就是,允許把函數本身作為參數傳入另一個函數,還允許返回一個函數!
簡單的來說就是,函數也是一等公民了,在java里面一等公民有變量,對象,那么函數式編程語言里面函數也可以跟變量,對象一樣使用了,也就是說函數既可以作為參數,也可以作為返回值了,
Date Api更新
Date Api更新
SpringMVC
SpringMVC核心處理流程:

- 用戶發送請求至前端控制器
DispatcherServlet DispatcherServlet收到請求調用處理器映射器HandlerMapping。- 處理器映射器根據請求url找到具體的處理器,生成處理器執行鏈
HandlerExecutionChain(包括處理器對象和處理器攔截器)一并返回給DispatcherServlet。 DispatcherServlet根據處理器Handler獲取處理器適配器HandlerAdapter執行HandlerAdapter處理一系列的操作,如:參數封裝,數據格式轉換,數據驗證等操作- 執行處理器
Handler(Controller,也叫頁面控制器)。 Handler執行完成返回ModelAndViewHandlerAdapter將Handler執行結果ModelAndView返回到DispatcherServletDispatcherServlet將ModelAndView傳給ViewReslover視圖解析器ViewReslover解析后返回具體ViewDispatcherServlet對View進行渲染視圖(即將模型數據model填充至視圖中)。DispatcherServlet響應用戶。IoC和AOP
Spring核心容器的主要組件是Bean工廠(BeanFactory),Bean工廠使用控制反轉(IoC)模式來降低程序代碼之間的耦合度,并提供了面向切面編程(AOP)的實現。
簡單來說,Spring是一個輕量級的控制反轉(IoC)和面向切面編程(AOP)的容器框架。
IoC 和 AOP
在具體介紹IoC和AOP之前,我們先簡要說明下Spring常用注解
@Controller:用于標注控制器層組件@Service:用于標注業務層組件@Component: 用于標注這是一個受 Spring 管理的組件,組件引用名稱是類名,第一個字母小寫。可以使用@Component(“beanID”)指定組件的名稱@Repository:用于標注數據訪問組件,即DAO組件@Bean:方法級別的注解,主要用在@Configuration和@Component注解的類里,@Bean注解的方法會產生一個Bean對象,該對象由Spring管理并放到IoC容器中。引用名稱是方法名,也可以用@Bean(name = "beanID")指定組件名@Scope("prototype"):將組件的范圍設置為原型的(即多例)。保證每一個請求有一個單獨的action來處理,避免action的線程問題。
由于Spring默認是單例的,只會創建一個action對象,每次訪問都是同一個對象,容易產生并發問題,數據不安全。@Autowired:默認按類型進行自動裝配。在容器查找匹配的Bean,當有且僅有一個匹配的Bean時,Spring將其注入@Autowired標注的變量中。@Resource:默認按名稱進行自動裝配,當找不到與名稱匹配的Bean時會按類型裝配。
面向切面編程(AOP)就是縱向的編程:
比如業務A和業務B現在需要一個相同的操作,傳統方法我們可能需要在A、B中都加入相關操作代碼,而應用AOP就可以只寫一遍代碼,A、B共用這段代碼。并且,當A、B需要增加新的操作時,可以在不改動原代碼的情況下,靈活添加新的業務邏輯實現。
設計模式MVC ??
第二種:工廠方法(Factory Method)
設計一個工廠的接口,你想要什么東西,就寫個類繼承于這個工廠,去調用工廠。抽象就是將更多的產品放到接口中。
第三種:單例模式(Singleton)
確保某一個類只有一個實例,并且提供一個全局訪問點,通過get來調用。
第四種:適配器(Adapter)
原本由于接口不兼容而不能一起工作的那些類可以在一起工作(繼承和實現)。
linux常用命令
關機
| 命令 | 注釋 |
|---|---|
| shutdown -h now | 立刻關機 |
| shutdown -h 5 | 5分鐘后關機 |
| poweroff | 立刻關機 |
| shutdown -r now | 立刻重啟 |
| shutdown -r 5 | 5分鐘后重啟 |
| reboot | 立刻重啟 |
目錄切換 cd
| 命令 | 注釋 |
|---|---|
| cd / | 切換到根目錄 |
| cd /usr | 切換到根目錄下的usr目錄 |
| cd …/ | 切換到上一級目錄 或者 cd … |
| cd ~ | 切換到home目錄 |
| cd - | 切換到上次訪問的目錄 |
目錄查看 ls [-al]
| 命令 | 注釋 |
|---|---|
| ls | 查看當前目錄下的所有目錄和文件 |
| ls -a | 查看當前目錄下的所有目錄和文件(包括隱藏的文件) |
| ls -l 或 ll | 列表查看當前目錄下的所有目錄和文件(列表查看,顯示更多信息) |
| ls /dir | 查看指定目錄下的所有目錄和文件 如:ls /usr |
目錄操作【增,刪,改,查】
創建目錄【增】 mkdir
| 命令 | 注釋 |
|---|---|
| rm 文件 | 刪除當前目錄下的文件 |
| rm -f 文件 | 刪除當前目錄的的文件(不詢問) |
刪除目錄或文件【刪】rm
| 命令 | 注釋 |
|---|---|
| rm 文件 | 刪除當前目錄下的文件 |
| rm -f 文件 | 刪除當前目錄的的文件(不詢問) |
| 刪除目錄: | |
| rm -r aaa | 遞歸刪除當前目錄下的aaa目錄 |
| rm -rf aaa | 遞歸刪除當前目錄下的aaa目錄(不詢問) |
| 全部刪除: | |
| rm -rf * | 將當前目錄下的所有目錄和文件全部刪除 |
| rm -rf /* | 【自殺命令!慎用!慎用!慎用!】將根目錄下的所有文件全部刪除 |
注意:rm不僅可以刪除目錄,也可以刪除其他文件或壓縮包,為了方便大家的記憶,無論刪除任何目錄或文件,都直接使用 rm -rf 目錄/文件/壓縮包
目錄修改【改】mv 和 cp
一、重命名目錄
命令:mv 當前目錄 新目錄
例如:mv aaa bbb 將目錄aaa改為bbb
注意:mv的語法不僅可以對目錄進行重命名而且也可以對各種文件,壓縮包等進行 重命名的操作
二、剪切目錄
命令:mv 目錄名稱 目錄的新位置
示例:將/usr/tmp目錄下的aaa目錄剪切到 /usr目錄下面 mv /usr/tmp/aaa /usr
注意:mv語法不僅可以對目錄進行剪切操作,對文件和壓縮包等都可執行剪切操作
三、拷貝目錄
命令:cp -r 目錄名稱 目錄拷貝的目標位置 -r代表遞歸
示例:將/usr/tmp目錄下的aaa目錄復制到 /usr目錄下面 cp /usr/tmp/aaa /usr
注意:cp命令不僅可以拷貝目錄還可以拷貝文件,壓縮包等,拷貝文件和壓縮包時不 用寫-r遞歸
搜索目錄【查】find
命令:find 目錄 參數 文件名稱
示例:find /usr/tmp -name ‘a*’ 查找/usr/tmp目錄下的所有以a開頭的目錄或文件
文件操作命令
文件操作【增,刪,改,查】
新建文件【增】touch
命令:touch 文件名
示例:在當前目錄創建一個名為aa.txt的文件 touch aa.txt
刪除文件 【刪】 rm
命令:rm -rf 文件名
修改文件【改】 vi或vim
【vi編輯器的3種模式】
基本上vi可以分為三種狀態,分別是命令模式(command mode)、插入模式(Insert mode)和底行模式(last line mode),各模式的功能區分如下:
-
命令行模式(command mode)
控制屏幕光標的移動,字符、字或行的刪除,查找,移動復制某區段及進入Insert mode下,或者到 last line mode。
命令行模式下的常用命令:
【1】控制光標移動:↑,↓,j
【2】刪除當前行:dd
【3】查找:/字符
【4】進入編輯模式:ioa
【5】進入底行模式:: -
編輯模式(Insert mode)
只有在Insert mode下,才可以做文字輸入,按「ESC」鍵可回到命令行模式。
編輯模式下常用命令:
【1】ESC 退出編輯模式到命令行模式; -
底行模式(last line mode)
將文件保存或退出vi,也可以設置編輯環境,如尋找字符串、列出行號……等。
底行模式下常用命令:
【1】退出編輯::q
【2】強制退出::q!
【3】保存并退出::wq
打開文件
命令:vi 文件名
示例:打開當前目錄下的aa.txt文件 vi aa.txt 或者 vim aa.txt
注意:使用vi編輯器打開文件后,并不能編輯,因為此時處于命令模式,點擊鍵盤i/a/o進入編輯模式。
編輯文件
使用vi編輯器打開文件后點擊按鍵:i,a或者o即可進入編輯模式。
i:在光標所在字符前開始插入
a:在光標所在字符后開始插入
o:在光標所在行的下面另起一新行插入
保存或者取消編輯
保存文件:
第一步:ESC 進入命令行模式
第二步:: 進入底行模式
第三步:wq 保存并退出編輯
取消編輯:
第一步:ESC進入命令行模式
第二步::進入底行模式
第三步:q!撤銷本次修改并退出編輯
ORACLE分頁
- 無
ORDER BY排序的寫法。(效率最高)
(經過測試,此方法成本最低,只嵌套一層,速度最快!即使查詢的數據量再大,也幾乎不受影響,速度依然!)
SELECT *
FROM (SELECT ROWNUM AS rowno, t.* FROM emp t
WHERE hire_date BETWEEN TO_DATE ('20060501', 'yyyymmdd')
AND TO_DATE ('20060731', 'yyyymmdd')
AND ROWNUM <= 20
) table_alias
WHERE table_alias.rowno >= 10;
- 有ORDER BY排序的寫法。(效率較高)
(經過測試,此方法隨著查詢范圍的擴大,速度也會越來越慢哦!)
SELECT *
FROM (SELECT tt.*, ROWNUM AS rowno
FROM ( SELECT t.*
FROM emp t
WHERE hire_date BETWEEN TO_DATE ('20060501', 'yyyymmdd')
AND TO_DATE ('20060731', 'yyyymmdd')
ORDER BY create_time DESC, emp_no) tt
WHERE ROWNUM <= 20) table_alias
WHERE table_alias.rowno >= 10;
集合類的用法
list
| 方法 | 描述 |
|---|---|
| add() | 將元素插入到指定位置的 arraylist 中 |
| addAll() | 添加集合中的所有元素到 arraylist 中 |
| clear() | 刪除 arraylist 中的所有元素 |
| clone() | 復制一份 arraylist |
| contains() | 判斷元素是否在 arraylist |
| get() | 通過索引值獲取 arraylist 中的元素 |
| indexOf() | 返回 arraylist 中元素的索引值 |
| removeAll() | 刪除存在于指定集合中的 arraylist 里的所有元素 |
| remove() | 刪除 arraylist 里的單個元素 |
| size() | 返回 arraylist 里元素數量 |
| isEmpty() | 判斷 arraylist 是否為空 |
| subList() | 截取部分 arraylist 的元素 |
| set() | 替換 arraylist 中指定索引的元素 |
| sort() | 對 arraylist 元素進行排序 |
| toArray() | 將 arraylist 轉換為數組 |
| toString() | 將 arraylist 轉換為字符串 |
| ensureCapacity() | 設置指定容量大小的 arraylist |
| lastIndexOf() | 返回指定元素在 arraylist 中最后一次出現的位置 |
| retainAll() | 保留 arraylist 中在指定集合中也存在的那些元素 |
| containsAll() | 查看 arraylist 是否包含指定集合中的所有元素 |
| trimToSize() | 將 arraylist 中的容量調整為數組中的元素個數 |
| removeRange() | 刪除 arraylist 中指定索引之間存在的元素 |
| replaceAll() | 將給定的操作內容替換掉數組中每一個元素 |
| removeIf() | 刪除所有滿足特定條件的 arraylist 元素 |
| forEach() | 遍歷 arraylist 中每一個元素并執行特定操作 |
map
| 方法 | 描述 |
|---|---|
| clear() | 刪除 hashMap 中的所有鍵/值對 |
| clone() | 復制一份 hashMap |
| isEmpty() | 判斷 hashMap 是否為空 |
| size() | 計算 hashMap 中鍵/值對的數量 |
| put() | 將鍵/值對添加到 hashMap 中 |
| putAll() | 將所有鍵/值對添加到 hashMap 中 |
| putIfAbsent() | 如果 hashMap 中不存在指定的鍵,則將指定的鍵/值對插入到 hashMap 中。 |
| remove() | 刪除 hashMap 中指定鍵 key 的映射關系 |
| containsKey() | 檢查 hashMap 中是否存在指定的 key 對應的映射關系。 |
| containsValue() | 檢查 hashMap 中是否存在指定的 value 對應的映射關系。 |
| replace() | 替換 hashMap 中是指定的 key 對應的 value。 |
| replaceAll() | 將 hashMap 中的所有映射關系替換成給定的函數所執行的結果。 |
| get() | 獲取指定 key 對應對 value |
| getOrDefault() | 獲取指定 key 對應對 value,如果找不到 key ,則返回設置的默認值 |
| forEach() | 對 hashMap 中的每個映射執行指定的操作。 |
| entrySet() | 返回 hashMap 中所有映射項的集合集合視圖。 |
| keySet() | 返回 hashMap 中所有 key 組成的集合視圖。 |
| values() | 返回 hashMap 中存在的所有 value 值。 |
| merge() | 添加鍵值對到 hashMap 中 |
| compute() | 對 hashMap 中指定 key 的值進行重新計算 |
| computeIfAbsent() | 對 hashMap 中指定 key 的值進行重新計算,如果不存在這個 key,則添加到 hasMap 中 |
| computeIfPresent() | 對 hashMap 中指定 key 的值進行重新計算,前提是該 key 存在于 hashMap 中。 |
迭代 HashMap
可以使用 for-each 來迭代 HashMap 中的元素。
如果你只想獲取 key,可以使用 keySet() 方法,然后可以通過 get(key) 獲取對應的 value,如果你只想獲取 value,可以使用 values() 方法。
實例:
// 引入 HashMap 類
import java.util.HashMap;
public class RunoobTest {
public static void main(String[] args) {
// 創建 HashMap 對象 Sites
HashMap<Integer, String> Sites = new HashMap<Integer, String>();
// 添加鍵值對
Sites.put(1, "Google");
Sites.put(2, "Runoob");
Sites.put(3, "Taobao");
Sites.put(4, "Zhihu");
// 輸出 key 和 value
for (Integer i : Sites.keySet()) {
System.out.println("key: " + i + " value: " + Sites.get(i));
}
// 返回所有 value 值
for(String value: Sites.values()) {
// 輸出每一個value
System.out.print(value + ", ");
}
}
}
hashmap 不安全
jdk1.7死循環,數據丟失:jdk1.8數據覆蓋
SQL鏈接
內連接:
select * from book as a inner join stu as b on a.sutid = b.stuid
左連接:
select * from book as a left join stu as b on a.sutid = b.stuid
Mybatis中用
#{},和${}傳參的區別:
$符是直接拼成sql的 ,#符則會以字符串的形式 與sql進行拼接。
/*字符串反轉*/
public static String reverse1(String str) {
return new StringBuffer(str).reverse().toString();
}
public static String reverse3(String str) {
char[] arr = str.toCharArray();//String 轉換成char數組
String reverse = "";
for (int i = arr.length - 1; i >= 0; i--) {
reverse += arr[i];
}
return reverse;
}
java 事務
為什么要事務?
事務是為解決數據安全操作提出的,事務控制實際上就是控制數據的安全訪問。
事務的4個特性(ACID):
- 原子性(atomicity):事務是數據庫的邏輯工作單位,而且是必須是原子工作單位,對于其數據修改,要么全部執行,要么全部不執行。
- 一致性(consistency):事務在完成時,必須是所有的數據都保持一致狀態。在相關數據庫中,所有規則都必須應用于事務的修改,以保持所有數據的完整性。(實例:轉賬,兩個賬戶余額相加,值不變。)
- 隔離性(isolation):一個事務的執行不能被其他事務所影響。
- 持久性(durability):一個事務一旦提交,事物的操作便永久性的保存在DB中。即便是在數據庫系統遇到故障的情況下也不會丟失提交事務的操作。
參考鏈接:https://blog.csdn.net/qq_37751790/article/details/118439711
var code = “f6ba92c5-fa0e-4c99-9137-025a31c25e27”

浙公網安備 33010602011771號