Ⅰ Java面試中都會遇到哪些問題
JAVA面試精選題
各位準程序猿們,能不能找到一份好工作,就看你知不知道這,66道最基本的JAVA面試題,請耐心看完,說不定這10分鍾,會影響你一輩子
面試題NO.01-NO.05
問題:如果main方法被聲明為private會怎樣?
答案:能正常編譯,但運行的時候會提示」main方法不是public的」。
問題:Java里的傳引用和傳值的區別是什麼?
答案:傳引用是指傳遞的是地址而不是值本身,傳值則是傳遞值的一份拷貝。
問題:如果要重寫一個對象的equals方法,還要考慮什麼?
答案:hashCode。
問題:Java的」一次編寫,處處運行」是如何實現的?
答案:Java程序會被編譯成位元組碼組成的class文件,這些位元組碼可以運行在任何平台,因此Java是平台獨立的。
問題:說明一下public static void main(String args[])這段聲明裡每個關鍵字的作用
答案:public: main方法是Java程序運行時調用的第一個方法,因此它必須對Java環境可見。所以可見性設置為pulic.
static: Java平台調用這個方法時不會創建這個類的一個實例,因此這個方法必須聲明為static。
void: main方法沒有返回值。
String是命令行傳進參數的類型,args是指命令行傳進的字元串數組。
面試題NO.06-NO.10
問題:==與equals的區別
答案:==比較兩個對象在內存里是不是同一個對象,就是說在內存里的存儲位置一致。兩個String對象存儲的值是一樣的,但有可能在內存里存儲在不同的地方 .
==比較的是引用而equals方法比較的是內容。public boolean equals(Object obj) 這個方法是由Object對象提供的,可以由子類進行重寫。默認的實現只有當對象和自身進行比較時才會返回true,這個時候和==是等價的。String, BitSet, Date, 和File都對equals方法進行了重寫,對兩個String對象 而言,值相等意味著它們包含同樣的字元序列。對於基本類型的包裝類來說,值相等意味著對應的基本類型的值一樣。
問題:如果去掉了main方法的static修飾符會怎樣?
答案:程序能正常編譯。運行時會拋NoSuchMethodError異常。
問題:為什麼oracle type4驅動被稱作瘦驅動?
答案:oracle提供了一個type 4 JDBC驅動,被稱為瘦驅動。這個驅動包含了一個oracle自己完全用Java實現的一個TCP/IP的Net8的實現,因此它是平台獨立的,可以在運行時由瀏覽器下載,不依賴任何客戶端 的oracle實現。客戶端連接字元串用的是TCP/IP的地址埠,而不是資料庫名的tnsname。
問題:介紹一下finalize方法
答案: final: 常量聲明。 finally: 處理異常。 finalize: 幫助進行垃圾回收。
介面里聲明的變數默認是final的。final類無法繼承,也就是沒有子類。這么做是出於基礎類型的安全考慮,比如String和Integer。這樣也使得編譯器進行一些優化,更容易保證線程的安全性。final方法無法重寫。final變數的值不能改變。finalize()方法在一個對象被銷毀和回收前會被調用。finally,通常用於異常處理,不管有沒有異常被拋出都會執行到。比如,關閉連接通常放到finally塊中完成。
問題:什麼是Java API?
答案:Java API是大量軟體組件的集合,它們提供了大量有用的功能,比如GUI組件。
面試題NO.11-NO.15
問題:GregorianCalendar類是什麼東西?
答案:GregorianCalendar提供了西方傳統日歷的支持。
問題:ResourceBundle類是什麼?
答案:ResourceBundle用來存儲指定語言環境的資源,應用程序可以根據運行時的語言環境來載入這些資源,從而提供不同語言的展示。
問題:為什麼Java里沒有全局變數?
答案:全局變數是全局可見的,Java不支持全局可見的變數,因為:全局變數破壞了引用透明性原則。全局變數導致了命名空間的沖突。
問題:如何將String類型轉化成Number類型?
答案:Integer類的valueOf方法可以將String轉成Number。下面是代碼示例:
問題:SimpleTimeZone類是什麼?
答案:SimpleTimeZone提供公歷日期支持。
面試題NO.16-NO.20
問題:while循環和do循環有什麼不同?
答案:while結構在循環的開始判斷下一個迭代是否應該繼續。do/while結構在循環的結尾來判斷是否將繼續下一輪迭代。do結構至少會執行一次循環體。
問題:Locale類是什麼?
答案:Locale類用來根據語言環境來動態調整程序的輸出。
問題:面向對象編程的原則是什麼?
答案:主要有三點,多態,繼承和封裝。
問題:介紹下繼承的原則
答案:繼承使得一個對象可以獲取另一個對象的屬性。使用繼承可以讓已經測試完備的功能得以復用,並且可以一次修改,所有繼承的地方都同時生效。
問題:什麼是隱式的類型轉化?
答案:隱式的類型轉化就是簡單的一個類型賦值給另一個類型,沒有顯式的告訴編譯器發生了轉化。並不是所有的類型都支持隱式的類型轉化。
代碼示例:
面試題NO.21-NO.25
問題:sizeof是Java的關鍵字嗎?
答案:不是。
問題:native方法是什麼?
答案:native方法是非Java代碼實現的方法。
問題:在System.out.println()裡面,System, out, println分別是什麼?
答案:System是系統提供的預定義的final類,out是一個PrintStream對象,println是out對象裡面一個重載的方法。
問題:封裝,繼承和多態是什麼?
答案:簡單來說,多態是指一個名字多種實現。多態使得一個實體通過一個通用的方式來實現不同的操作。具體的操作是由實際的實現來決定的。
多態在Java里有三種表現方式:方法重載通過繼承實現方法重寫通過Java介面進行方法重寫。
問題:顯式的類型轉化是什麼?
答案:顯式的類型轉化是明確告訴了編譯器來進行對象的轉化。
代碼示例:
面試題NO.26-NO.30
問題:什麼是Java虛擬機?
答案:Java虛擬機是能移植到不同硬體平台上的軟體系統。
問題:類型向下轉換是什麼?
答案:向下轉換是指由一個通用類型轉換成一個具體的類型,在繼承結構上向下進行。
問題:Java的訪問修飾符是什麼?
答案:訪問許可權修飾符是表明類成員的訪問許可權類型的關鍵字。使用這些關鍵字來限定程序的方法或者變數的訪問許可權。它們包含:
public: 所有類都可以訪問 protected: 同一個包內以及所有子類都可以訪問 private: 只有歸屬的類才能訪問默認: 歸屬類及相同包下的子類可以訪問。
問題:所有類的父類是什麼?
答案:Object.
問題:Java的基本類型有哪些?
答案:byte,char, short, int, long, float, double, boolean。
面試題NO.31-NO.40
問題:靜態類型有什麼特點?
答案:靜態變數是和類綁定到一起的,而不是類的實例對象。每一個實例對象都共享同樣一份靜態變數。也就是說,一個類的靜態變數只有一份,不管它有多少個對象。類變數或者說靜態變數是通過static這個關鍵字來聲明的。類變數通常被用作常量。靜態變數通常通過類名字來進行訪問。當程序運行的時候這個變數就會創建直到程序結束後才會被銷毀。類變數的作用域和實例變數是一樣的。它的初始值和成員變數也是一樣的,當變數沒被初始化的時候根據它的數據類型,會有一個默認值。類似的,靜態方法是屬於類的方法,而不是類對象,它的調用並不作用於類對象,也不需要創建任何的類實例。靜態方法本身就是final的,因為重寫只會發生在類實例上,靜態方法是和類綁定在一起的,不是對象。父類的靜態方法會被子類的靜態方法屏蔽,只要原來方法沒有聲明為final。非靜態方法不能重寫靜態方法,也就是說,你不能在子類中把一個靜態方法改成實例方法。
非靜態變數在每一個對象實例上都有單獨的一份值。
問題:&操作符和&&操作符有什麼區別?
答案:當一個&表達式在求值的時候,兩個操作數都會被求值,&&更像是一個操作符的快捷方式。當一個&&表達式求值的時候,先計算第一個操作數,如果它返回true才會計算第二個操作數。如果第一個操作數取值為fale,第二個操作數就不會被求值。
問題:Java是如何處理整型的溢出和下溢的?
答案:Java根據類型的大小,將計算結果中的對應低階位元組存儲到對應的值裡面。
問題:public static void寫成static public void會怎樣?
答案:程序正常編譯及運行。
問題,聲明變數和定義變數有什麼不同?
答案:聲明變數我們只提供變數的類型和名字,並沒有進行初始化。定義包括聲明和初始化兩個階段String s;只是變數聲明,String s = new String(「bob」); 或者String s = 「bob」;是變數定義。
面試題NO.41-NO.45
問題:Java支持哪種參數傳遞類型?
答案:Java參數都是進行傳值。對於對象而言,傳遞的值是對象的引用,也就是說原始引用和參數引用的那個拷貝,都是指向同一個對象。
問題:對象封裝的原則是什麼?
答案:封裝是將數據及操作數據的代碼綁定到一個獨立的單元。這樣保障了數據的安全,防止外部代碼的錯誤使用。對象允許程序和數據進行封裝,以減少潛在的干涉。對封裝的另一個理解是作為數據及代碼的保護層,防止保護層外代碼的隨意訪問。
問題:你怎麼理解變數?
答案:變數是一塊命名的內存區域,以便程序進行訪問。變數用來存儲數據,隨著程序的執行,存儲的數據也可能跟著改變。
問題:數值提升是什麼?
答案:數值提升是指數據從一個較小的數據類型轉換成為一個更大的數據類型,以便進行整型或者浮點型運算。在數值提升的過程中,byte,char,short值會被轉化成int類型。需要的時候int類型也可能被提升成long。long和float則有可能會被轉換成double類型。
問題:Java的類型轉化是什麼?
答案:從一個數據類型轉換成另一個數據類型叫做類型轉換。Java有兩種類型轉換的方式,一個是顯式的類型轉換,一個是隱式的。
面試題NO.46-NO.50
問題:main方法的參數裡面,字元串數組的第一個參數是什麼?
答案:數組是空的,沒有任何元素。不像C或者C++,第一個元素默認是程序名。如果命令行沒有提供任何參數的話,main方法中的String數組為空,但不是null。
問題:怎麼判斷數組是null還是為空?
答案:輸出array.length的值,如果是0,說明數組為空。如果是null的話,會拋出空指針異常。
問題:程序中可以允許多個類同時擁有都有main方法嗎?
答案:可以。當程序運行的時候,我們會指定運行的類名。JVM只會在你指定的類中查找main方法。因此多個類擁有main方法並不存在命名沖突的問題。
問題:靜態變數在什麼時候載入?編譯期還是運行期?靜態代碼塊載入的時機呢?
答案:當類載入器將類載入到JVM中的時候就會創建靜態變數,這跟對象是否創建無關。靜態變數載入的時候就會分配內存空間。靜態代碼塊的代碼只會在類第一次初始化的時候執行一次。一個類可以有多個靜態代碼塊,它並不是類的成員,也沒有返回值,並且不能直接調用。靜態代碼塊不能包含this或者super,它們通常被用初始化靜態變數。
問題:一個類能擁有多個main方法嗎?
答案:可以,但只能有一個main方法擁有以下簽名:
否則程序將無法通過編譯。編譯器會警告你main方法已經存在。
面試題NO.51-NO.60
問題:簡單的介紹下JVM是如何工作的?
答案:JVM是一台抽象的計算機,就像真實的計算機那樣,它們會先將.java文件編譯成.class文件(.class文件就是位元組碼文件),然後用它的解釋器來載入位元組碼。
問題:如果原地交換兩個變數的值?
答案:先把兩個值相加賦值給第一個變數,然後用得到的結果減去第二個變數,賦值給第二個變數。再用第一個變數減去第二個變數,同時賦值給第一個變數。代碼如下:
使用異或操作也可以交換。第一個方法還可能會引起溢出。異或的方法如下: int a=5,b=10;a=a+b; b=a-b; a=a-b;
問題:什麼是數據的封裝?
答案:數據封裝的一種方式是在類中創建set和get方法來訪問對象的數據變數。一般來說變數是private的,而get和set方法是public的。封裝還可以用來在存儲數據時進行數據驗證,或者對數據進行計算,或者用作自省(比如在struts中使用javabean)。把數據和功能封裝到一個獨立的結構中稱為數據封裝。封裝其實就是把數據和關聯的操作方法封裝到一個獨立的單元中,這樣使用關聯的這些方法才能對數據進行訪問操作。封裝提供的是數據安全性,它其實就是一種隱藏數據的方式。
問題:什麼是反射API?它是如何實現的?
答案:反射是指在運行時能查看一個類的狀態及特徵,並能進行動態管理的功能。這些功能是通過一些內建類的反射API提供的,比如Class,Method,Field, Constructors等。使用的例子:使用Java反射API的getName方法可以獲取到類名。
問題:JVM自身會維護緩存嗎,是不是在堆中進行對象分配,操作系統的堆還是JVM自己管理的堆?為什麼?
答案:是的,JVM自身會管理緩存,它在堆中創建對象,然後在棧中引用這些對象。
面試題NO.61-NO.66
問題:虛擬內存是什麼?
答案:虛擬內存又叫延伸內存,實際上並不存在真實的物理內存。
問題:方法可以同時即是static又是synchronized的嗎?
答案:可以。如果這樣做的話,JVM會獲取和這個對象關聯的java.lang.Class實例上的鎖。這樣做等於:
問題:String和StringTokenizer的區別是什麼?
答案:StringTokenizer是一個用來分割字元串的工具類。
問題:transient變數有什麼特點?
答案:transient變數不會進行序列化。例如一個實現Serializable介面的類在序列化到ObjectStream的時候,transient類型的變數不會被寫入流中,同時,反序列化回來的時候,對應變數的值為null。
問題:哪些容器使用Border布局作為它們的默認布局?
答案:Window, Frame, Dialog。
問題:怎麼理解什麼是同步?
答案:同步用來控制共享資源在多個線程間的訪問,以保證同一時間內只有一個線程能訪問到這個資源。在非同步保護的多線程程序裡面,一個線程正在修改一個共享變數的時候,可能有另一個線程也在使用或者更新它的值。同步避免了臟數據的產生。
以上回答轉載自求職類公眾號圈裡求職,裡面還有更多求職面試經,程序猿也要學起來~
Ⅱ #java高級工程師、項目經理#java高級開發,面試都問什麼
虛擬機
,
YGC和FGC
多線程並發處理
並發包裡面的一些類怎麼實現或者原理,各種框架,集合的內部原理,需要看源碼了,數據結構
資料庫事物,鎖,
存儲引擎
,sql執行原理
設計方面,緩存穿透
血崩
了怎麼處理,介面
冪等
,服務降級
熔斷
處理
恢復之類的
先問基礎後來經驗
Ⅲ 面試官要求我研究一個Java緩存框架,哪個比較好
緩存框架有ehcache、redis、memcached
現在大公司用的比較多的是memcached和redis,這也是分布式系統開發中常用的緩存中間件
個人比較推薦用redis,因為redis對於可支持的數據類型比memcached要多,而且redis是一個可持久化的緩存框架,在使用的時候還可以嘗試搭建redis集群環境。
如果你對redis深入研究,相信你的面試官會很樂意要你的。
Ⅳ java面試中redis,mongodb類的,會問哪些問題,怎麼回答
1、可能會問nosql和關系型資料庫的區別:
優點:
1)成本:nosql資料庫簡單易部署,基本都是開源軟體,不需要像使用Oracle那樣花費大量成本購買使用,相比關系型資料庫價格便宜
2)查詢速度:nosql資料庫將數據存儲於緩存之中,關系型資料庫將數據存儲在硬碟中,自然查詢速度遠不及nosql資料庫
3)存儲數據的格式:nosql的存儲格式是key,value形式、文檔形式、圖片形式等等,所以可以存儲基礎類型以及對象或者是集合等各種格式,而資料庫則只支持基礎類型
4)擴展性:關系型資料庫有類似join這樣的多表查詢機制的限制導致擴展很艱難
缺點:
1)維護的工具和資料有限,因為nosql是屬於新的技術,不能和關系型資料庫10幾年的技術同日而語。
2)不提供對sql的支持,如果不支持sql這樣的工業標准,將產生一定用戶的學習和使用成本
3)不提供關系型資料庫對事物的處理
2、介紹下redis和mongodb:
自行google。
3、應用場景:
redis:
a.主要是做熱點數據緩存。
b.數據過期處理。
c.消息隊列等功能。
d.計數,例如投票等。
mongodb:
mongodb的主要目標是在鍵/值存儲方式(提供了高性能和高度伸縮性)以及傳統的RDBMS系統(豐富的功能)架起一座橋梁,集兩者的優勢於一身。mongo適用於以下場景:
a.網站數據:mongo非常適合實時的插入,更新與查詢,並具備網站實時數據存儲所需的復制及高度伸縮性。
b.緩存:由於性能很高,mongo也適合作為信息基礎設施的緩存層。在系統重啟之後,由mongo搭建的持久化緩存可以避免下層的數據源過載。
c.大尺寸、低價值的數據:使用傳統的關系資料庫存儲一些數據時可能會比較貴,在此之前,很多程序員往往會選擇傳統的文件進行存儲。
d.高伸縮性的場景:mongo非常適合由數十或者數百台伺服器組成的資料庫。
e.用於對象及JSON數據的存儲:mongo的BSON數據格式非常適合文檔格式化的存儲及查詢。
4、支持的數據類型:
內容比較多,自行將網上的信息整理一下。
Ⅳ java面試問到java緩存有多少種實現方式
我知道的有兩種
緩存數據放內存里,key-value鍵值對,存取都快。redis、ehcache
放資料庫,讓資料庫的定時策略自動刪除。mongodb
還有些是直接存文件的.OSCache(不太熟悉)
Ⅵ 「干貨」redis面試題
Redis 的全稱是:Remote Dictionary.Server,本質上是一個 Key-Value 類型的內存資料庫,很像
memcached,整個資料庫統統載入在內存當中進行操作,定期通過非同步操作把資料庫數據 flush 到硬碟
上進行保存。
因為是純內存操作,Redis 的性能非常出色,每秒可以處理超過 10 萬次讀寫操作,是已知性能最快的
Key-Value DB。
Redis 的出色之處不僅僅是性能,Redis 最大的魅力是支持保存多種數據結構,此外單個 value 的最大限
制是 1GB,不像 memcached 只能保存 1MB 的數據,因此 Redis 可以用來實現很多有用的功能。
比方說用他的 List 來做 FIFO 雙向鏈表,實現一個輕量級的高性 能消息隊列服務,用他的 Set 可以做高
性能的 tag 系統等等。
另外 Redis 也可以對存入的 Key-Value 設置 expire 時間,因此也可以被當作一 個功能加強版的
memcached 來用。 Redis 的主要缺點是資料庫容量受到物理內存的限制,不能用作海量數據的高性能
讀寫,因此 Redis 適合的場景主要局限在較小數據量的高性能操作和運算上。
1.memcached 所有的值均是簡單的字元串,redis 作為其替代者,支持更為豐富的數據類型
2.redis 的速度比 memcached 快很多 redis 的速度比 memcached 快很多
3.redis 可以持久化其數據 redis 可以持久化其數據
String、List、Set、Sorted Set、hashes
內存。
1.noeviction:返回錯誤當內存限制達到,並且客戶端嘗試執行會讓更多內存被使用的命令。
2.allkeys-lru: 嘗試回收最少使用的鍵(LRU),使得新添加的數據有空間存放。
3.volatile-lru: 嘗試回收最少使用的鍵(LRU),但僅限於在過期集合的鍵,使得新添加的數據有空間存
放。
4.allkeys-random: 回收隨機的鍵使得新添加的數據有空間存放。
5.volatile-random: 回收隨機的鍵使得新添加的數據有空間存放,但僅限於在過期集合的鍵。
6.volatile-ttl: 回收在過期集合的鍵,並且優先回收存活時間(TTL)較短的鍵,使得新添加的數據有空間
存放。
因為目前 Linux 版本已經相當穩定,而且用戶量很大,無需開發 windows 版本,反而會帶來兼容性等問
題。
512M
Redis 為了達到最快的讀寫速度將數據都讀到內存中,並通過非同步的方式將數據寫入磁碟。
所以 redis 具有快速和數據持久化的特徵,如果不將數據放在內存中,磁碟 I/O 速度為嚴重影響 redis 的
性能。
在內存越來越便宜的今天,redis 將會越來越受歡迎, 如果設置了最大使用的內存,則數據已有記錄數達
到內存限值後不能繼續插入新值。
1.codis 2.目前用的最多的集群方案,基本和 twemproxy 一致的效果,但它支持在節點數量改變情況下,舊節點
數據可恢復到新 hash 節點。
redis cluster3.0 自帶的集群,特點在於他的分布式演算法不是一致性 hash,而是 hash 槽的概念,以及自
身支持節點設置從節點。具體看官方文檔介紹。
3.在業務代碼層實現,起幾個毫無關聯的 redis 實例,在代碼層,對 key 進行 hash 計算,然後去對應的
redis 實例操作數據。這種方式對 hash 層代碼要求比較高,考慮部分包括,節點失效後的替代演算法方
案,數據震盪後的自動腳本恢復,實例的監控,等等。
有 A,B,C 三個節點的集群,在沒有復制模型的情況下,如果節點 B 失敗了,那麼整個集群就會以為缺少
5501-11000 這個范圍的槽而不可用。
redis 內存數據集大小上升到一定大小的時候,就會施行數據淘汰策略。
(1)會話緩存(Session Cache)
最常用的一種使用 Redis 的情景是會話緩存(sessioncache),用 Redis 緩存會話比其他存儲(如
Memcached)的優勢在於:Redis 提供持久化。當維護一個不是嚴格要求一致性的緩存時,如果用戶的
購物車信息全部丟失,大部分人都會不高興的,現在,他們還會這樣嗎?
幸運的是,隨著 Redis 這些年的改進,很容易找到怎麼恰當的使用 Redis 來緩存會話的文檔。甚至廣為
人知的商業平台 Magento 也提供 Redis 的插件。
(2)全頁緩存(FPC)
除基本的會話 token 之外,Redis 還提供很簡便的 FPC 平台。回到一致性問題,即使重啟了 Redis 實
例,因為有磁碟的持久化,用戶也不會看到頁面載入速度的下降,這是一個極大改進,類似 PHP 本地
FPC。
再次以 Magento 為例,Magento 提供一個插件來使用 Redis 作為全頁緩存後端。
此外,對 WordPress 的用戶來說,Pantheon 有一個非常好的插件 wp-redis,這個插件能幫助你以最快
速度載入你曾瀏覽過的頁面。
(3)隊列
Reids 在內存存儲引擎領域的一大優點是提供 list 和 set 操作,這使得 Redis 能作為一個很好的消息隊列
平台來使用。Redis 作為隊列使用的操作,就類似於本地程序語言(如 Python)對 list 的 push/pop
操作。
如果你快速的在 Google 中搜索「Redis queues」,你馬上就能找到大量的開源項目,這些項目的目的
就是利用 Redis 創建非常好的後端工具,以滿足各種隊列需求。例如,Celery 有一個後台就是使用
Redis 作為 broker,你可以從這里去查看。
(4)排行榜/計數器 Redis 在內存中對數字進行遞增或遞減的操作實現的非常好。集合(Set)和有序集合(SortedSet)也使
得我們在執行這些操作的時候變的非常簡單,Redis 只是正好提供了這兩種數據結構。
所以,我們要從排序集合中獲取到排名最靠前的 10 個用戶–我們稱之為「user_scores」,我們只需要像
下面一樣執行即可:
當然,這是假定你是根據你用戶的分數做遞增的排序。如果你想返回用戶及用戶的分數,你需要這樣執
行:
ZRANGE user_scores 0 10 WITHSCORES
Agora Games 就是一個很好的例子,用 Ruby 實現的,它的排行榜就是使用 Redis 來存儲數據的,你可
以在這里看到。
立聊天系統!
Redisson、Jedis、lettuce 等等,官方推薦使用 Redisson。
Redisson 是一個高級的分布式協調 Redis 客服端,能幫助用戶在分布式環境中輕松實現一些 Java 的對
象 (Bloom filter, BitSet, Set, SetMultimap, ScoredSortedSet, SortedSet, Map, ConcurrentMap,
List, ListMultimap, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock,
ReadWriteLock, AtomicLong, CountDownLatch, Publish / Subscribe, HyperLogLog)。
Jedis 是 Redis 的 Java 實現的客戶端,其 API 提供了比較全面的 Redis 命令的支持;
Redisson 實現了分布式和可擴展的 Java 數據結構,和 Jedis 相比,功能較為簡單,不支持字元串操作,
Redis 集群沒有使用一致性 hash,而是引入了哈希槽的概念,Redis 集群有 16384 個哈希槽,每個 key 通
過 CRC16 校驗後對 16384 取模來決定放置哪個槽,集群的每個節點負責一部分 hash 槽。
為了使在部分節點失敗或者大部分節點無法通信的情況下集群仍然可用,所以集群使用了主從復制模型,
每個節點都會有 N-1 個復製品.
Redis 並不能保證數據的強一致性,這意味這在實際中集群在特定的條件下可能會丟失寫操作。
非同步復制
16384 個
Redis 集群目前無法做資料庫選擇,默認在 0 資料庫。
一次請求/響應伺服器能實現處理新的請求即使舊的請求還未被響應,這樣就可以將多個命令發送到服務
器,而不用等待回復,最後在一個步驟中讀取該答復。
這就是管道(pipelining),是一種幾十年來廣泛使用的技術。例如許多 POP3 協議已經實現支持這個功
能,大大加快了從伺服器下載新郵件的過程。
事務是一個單獨的隔離操作:事務中的所有命令都會序列化、按順序地執行,事務在執行的過程中,不會被其他客戶端發送來的命令請求所打斷。
事務是一個原子操作:事務中的命令要麼全部被執行,要麼全部都不執行。
MULTI、EXEC、DISCARD、WATCH
EXPIRE 和 PERSIST 命令
盡可能使用散列表(hashes),散列表(是說散列表裡面存儲的數少)使用的內存非常小,所以你應該盡可能的將你的數據模型抽象到一個散列表裡面。
比如你的 web 系統中有一個用戶對象,不要為這個用戶的名稱,姓氏,郵箱,密碼設置單獨的 key,而是應該把這個用戶的所有信息存儲到一張散列表裡面。
一個客戶端運行了新的命令,添加了新的數據。Redi 檢查內存使用情況,如果大於 maxmemory 的限制, 則根據設定好的策略進行回收。一個新的命令被執行,等等。
所以我們不斷地穿越內存限制的邊界,通過不斷達到邊界然後不斷地回收回到邊界以下。
如果一個命令的結果導致大量內存被使用(例如很大的集合的交集保存到一個新的鍵),不用多久內存限制就會被這個內存使用量超越。
咱們來看上面那張圖,現在某個客戶端要加鎖。如果該客戶端面對的是一個 redis cluster 集 群,他首先會根據 hash 節點選擇一台機器。這里注意,僅僅只是選擇一台機器!這點很關 鍵!緊接著,就會發送一段 lua 腳本到 redis 上,那段 lua 腳本如下所示:
為啥要用 lua 腳本呢?因為一大坨復雜的業務邏輯,可以通過封裝在 lua 腳本中發送給 redis, 保證這段復雜業務邏輯執行的原子性。
那麼,這段 lua 腳本是什麼意思呢?這里 KEYS[1]代表的是你加鎖的那個 key,比如說:RLoc
k lock = redisson.getLock("myLock");這里你自己設置了加鎖的那個鎖 key 就是「myLock」。
ARGV[1]代表的就是鎖 key 的默認生存時間,默認 30 秒。ARGV[2]代表的是加鎖的客戶端的 I D,類似於下面這樣:8743c9c0-0795-4907-87fd-6c719a6b4586:1
給大家解釋一下,第一段 if 判斷語句,就是用「exists myLock」命令判斷一下,如果你要加鎖 的那個鎖 key 不存在的話,你就進行加鎖。如何加鎖呢?很簡單,用下面的命令:hset myLoc k 8743c9c0-0795-4907-87fd-6c719a6b4586:1 1,通過這個命令設置一個 hash 數據結構,這行 命令執行後,會出現一個類似下面的數據結構:
上述就代表「8743c9c0-0795-4907-87fd-6c719a6b4586:1」這個客戶端對「myLock」這個鎖 key 完 成了加鎖。接著會執行「pexpire myLock 30000」命令,設置 myLock 這個鎖 key 的生存時間 是 30 秒。好了,到此為止,ok,加鎖完成了。
那麼在這個時候,如果客戶端 2 來嘗試加鎖,執行了同樣的一段 lua 腳本,會咋樣呢?很簡 單,第一個 if 判斷會執行「exists myLock」,發現 myLock 這個鎖 key 已經存在了。接著第二 個 if 判斷,判斷一下,myLock 鎖 key 的 hash 數據結構中,是否包含客戶端 2 的 ID,但是明 顯不是的,因為那裡包含的是客戶端 1 的 ID。
所以,客戶端 2 會獲取到 pttl myLock 返回的一個數字,這個數字代表了 myLock 這個鎖 key 的剩餘生存時間。比如還剩 15000 毫秒的生存時間。此時客戶端 2 會進入一個 while 循環,不 停的嘗試加鎖。
客戶端 1 加鎖的鎖 key 默認生存時間才 30 秒,如果超過了 30 秒,客戶端 1 還想一直持有這把 鎖,怎麼辦呢?
簡單!只要客戶端 1 一旦加鎖成功,就會啟動一個 watch dog 看門狗,他是一個後台線程,會 每隔 10 秒檢查一下,如果客戶端 1 還持有鎖 key,那麼就會不斷的延長鎖 key 的生存時間。
31.可重入加鎖機制
那如果客戶端 1 都已經持有了這把鎖了,結果可重入的加鎖會怎麼樣呢?比如下面這種代碼:
這時我們來分析一下上面那段 lua 腳本。第一個 if 判斷肯定不成立,「exists myLock」會顯示鎖 key 已經存在了。第二個 if 判斷會成立,因為 myLock 的 hash 數據結構中包含的那個 ID,就 是客戶端 1 的那個 ID,也就是「8743c9c0-0795-4907-87fd-6c719a6b4586:1」 此時就會執行可重入加鎖的邏輯,他會用:
incrby myLock 8743c9c0-0795-4907-87fd-6c71a6b4586:1 1 ,通過這個命令,對客戶端 1 的加鎖次數,累加 1。此時 myLock 數據結構變為下面這樣:
大家看到了吧,那個 myLock 的 hash 數據結構中的那個客戶端 ID,就對應著加鎖的次數
如果執行 lock.unlock(),就可以釋放分布式鎖,此時的業務邏輯也是非常簡單的。其實說白 了,就是每次都對 myLock 數據結構中的那個加鎖次數減 1。如果發現加鎖次數是 0 了,說明 這個客戶端已經不再持有鎖了,此時就會用:「del myLock」命令,從 redis 里刪除這個 key。 然後呢,另外的客戶端 2 就可以嘗試完成加鎖了。這就是所謂的分布式鎖的開源 Redisson 框 架的實現機制。
一般我們在生產系統中,可以用 Redisson 框架提供的這個類庫來基於 redis 進行分布式鎖的加 鎖與釋放鎖。
其實上面那種方案最大的問題,就是如果你對某個 redis master 實例,寫入了 myLock 這種鎖 key 的 value,此時會非同步復制給對應的 master slave 實例。但是這個過程中一旦發生 redis m aster 宕機,主備切換,redis slave 變為了 redis master。
接著就會導致,客戶端 2 來嘗試加鎖的時候,在新的 redis master 上完成了加鎖,而客戶端 1 也以為自己成功加了鎖。此時就會導致多個客戶端對一個分布式鎖完成了加鎖。這時系統在業 務語義上一定會出現問題,導致各種臟數據的產生。
所以這個就是 redis cluster,或者是 redis master-slave 架構的主從非同步復制導致的 redis 分布 式鎖的最大缺陷:在 redis master 實例宕機的時候,可能導致多個客戶端同時完成加鎖。
先拿 setnx 來爭搶鎖,搶到之後,再用 expire 給鎖加一個過期時間防止鎖忘記了釋放。
如果在 setnx 之後執行 expire 之前進程意外 crash 或者要重啟維護了,那會怎麼樣?
set 指令有非常復雜的參數,這個應該是可以同時把 setnx 和 expire 合成一條指令來用的!
緩存穿透
一般的緩存系統,都是按照 key 去緩存查詢,如果不存在對應的 value,就應該去後端系統查找(比如DB)。一些惡意的請求會故意查詢不存在的 key,請求量很大,就會對後端系統造成很大的壓力。這就叫做緩存穿透。
如何避免?
1:對查詢結果為空的情況也進行緩存,緩存時間設置短一點,或者該 key 對應的數據 insert 了之後清理緩存。
2:對一定不存在的 key 進行過濾。可以把所有的可能存在的 key 放到一個大的 Bitmap 中,查詢時通過該 bitmap 過濾。
緩存雪崩
當緩存伺服器重啟或者大量緩存集中在某一個時間段失效,這樣在失效的時候,會給後端系統帶來很大壓力。導致系統崩潰。
如何避免?
1:在緩存失效後,通過加鎖或者隊列來控制讀資料庫寫緩存的線程數量。比如對某個 key 只允許一個線程查詢數據和寫緩存,其他線程等待。
2:做二級緩存,A1 為原始緩存,A2 為拷貝緩存,A1 失效時,可以訪問 A2,A1 緩存失效時間設置為短期,A2 設置為長期
3:不同的 key,設置不同的過期時間,讓緩存失效的時間點盡量均勻
Ⅶ java 面試題 請教
如果光問執行效率的話可以這樣回答:
語句在被DB的編譯器編譯後的執行代碼被緩存下來,那麼下次調用時只要是相同的預編譯語句就不需要編譯,只要將參數直接傳入編譯過的語句執行代碼中(相當於一個涵數)就會得到執行.
這並不是說只有一個Connection中多次執行的預編譯語句被緩存,而是對於整個DB中,只要預編譯的語句語法和緩存中匹配.那麼在任何時候就可以不需要再次編譯而可以直接執行.而statement的語句中,即使是相同一操作,而由於每次操作的數據不同所以使整個語句相匹配的機會極小,幾乎不太可能匹配.
其實PreparedStatement還有好處就是增加了代碼的可讀性和維護性,以及安全性。
Ⅷ java面試問道:從技術方面描述下你做的項目,都用了什麼技術,遇到了什麼問題,用什麼方法解決的!
此項目是採用B/S結構,採用struts,hibernate,spring等技術實現.
然後說下項目的總體結構(概括)
在具體說下自己所做的模塊...面試官看重的是你的口述能力和技術方面(自己本身的)
新手一般在使用SSH時,對配置文件不是很熟悉,可以說這方面問題,就說框架是自己動手搭建...
在動手做項目時,很容易出現JS錯誤..JS因為沒有明確錯誤提示..所以特別難找..我相信你應該也遇見了(除非界面你沒動過)
其次在做項目時,JAVA自帶內的運行效率都很高...你可以說下自己最開始的時候使用的是循環..等,效率不高...
後來為了提高效率採用了什麼方法.
將字元串一個個取出,例如:
String str="123456789";
for()...{
str.substring(?.?);//循環拿
?
......只為說明,大概列出
String str="123456789";
char[] ch=str.toChararray(); //一個個拿,放入數組,效率是上列方法的N倍
------------------------
這個只是給你的意見...你在項目中使用的還是靠你自己來說.這里可以讓考官知道你很喜歡比較學習,善於動腦思考....
---------------
寫完後才想到非常重要的一點....
推薦一本書給你:<<java程序員上班那點事>>
Ⅸ java程序員面試時被問到:如何在j2ee項目中處理高並發量訪問 該怎麼回答 請仔細看題干再回答
一般需從三點入手。
一、程序本身支持高並發。
簡單來說就是要優化我們的代碼。
1、避免使用錯誤的方式,盡量不用instanceof做條件判斷,不要將數組聲明為:public static final 。
2、使用java中效率高的類,比如盡量使用HashMap 和ArrayList ,除非必要,否則不推薦使用HashTable和Vector ,後者由於使用同步機制,而導致了性能的開銷。
3、盡量指定類的final修飾符 帶有final修飾符的類是不可派生的。在Java核心API中,有許多應用final的例子,例如java.lang.String。為String類指定final防止了人們覆蓋length()方法。另外,如果指定一個類為final,則該類所有的方法都是final。Java編譯器會尋找機會內聯(inline)所有的final方法(這和具體的編譯器實現有關)。此舉能夠使性能平均提高50% 。
4、盡量重用對象,避免頻繁的使用new對象。對於整個應用只需要存在一個實例的類,我們可以使用單例模式。對於工具類可以使用靜態方法的方式訪問。
用new關鍵詞創建類的實例時,構造函數鏈中的所有構造函數都會被自動調用。但如果一個對象實現了Cloneable介面,我們可以調用它的clone()方法。clone()方法不會調用任何類構造函數。
在使用設計模式(Design Pattern)的場合,如果用Factory模式創建對象,則改用clone()方法創建新的對象實
例非常簡單。例如,
Java代碼收藏代碼
下面是Factory模式的一個典型實現:
(){
returnnewCredit();
}
改進後的代碼使用clone()方法,如下所示:
privatestaticCreditBaseCredit=newCredit();
(){
return(Credit)BaseCredit.clone();
}
上面的思路對於數組處理同樣很有用。
5、特別是String 對象的使用中,出現字元串連接情況時應用StringBuffer 代替。由於系統不僅要花時間生成對象,以後可能還需花時間對這些對象進行垃圾回收和處理。因此,生成過多的對象將會給程序的性能帶來很大的影響。
6、StringBuffer 的使用:StringBuffer表示了可變的、可寫的字元串。
它有三個構造方法 :
StringBuffer();//默認分配16個字元的空間
StringBuffer(intsize);//分配size個字元的空間
StringBuffer(Stringstr);//分配16個字元+str.length()個字元空間
你可以通過StringBuffer的構造函數來設定它的初始化容量,這樣可以明顯地提升性能。這里提到的構造函數是StringBuffer(int length),length參數表示當前的StringBuffer能保持的字元數量。你也可以使用ensureCapacity(int minimumcapacity)方法在StringBuffer對象創建之後設置它的容量。首先我們看看StringBuffer的預設行為,然 後再找出一條更好的提升性能的途徑。
StringBuffer在內部維護一個字元數組,當你使用預設的構造函數來創建StringBuffer對象的時候,因為沒有設置初始化字元長度,StringBuffer的容量被初始化為16個字元,也就是說預設容量就是16個字元。當StringBuffer達到最大容量 的時候,它會將自身容量增加到當前的2倍再加2,也就是(2*舊值+2)。如果你使用預設值,初始化之後接著往裡面追 加字元,在你追加到第16個字元的時候它會將容量增加到34(2*16+2),當追加到34個字元的時候就會將容量增加到 70(2*34+2)。無論何事只要StringBuffer到達它的最大容量它就不得不創建一個新的字元數組然後重新將舊字元和 新字元都拷貝一遍――這也太昂貴了點。所以總是給StringBuffer設置一個合理的初始化容量值是錯不了的,這樣會帶來 立竿見影的性能增益。
StringBuffer初始化過程的調整的作用由此可見一斑。所以,使用一個合適的容量值來初始化StringBuffer永遠都是一個最佳的建議。
7、盡量使用局部變數,調用方法時傳遞的參數以及在調用中創建的臨時變數都保存在棧(Stack)中,速度較快。其他變數,如靜態變數、實例變數等,都在堆(Heap)中創建,速度較慢。另外,依賴於具體的編譯器/JVM,局部變數還可能得到進一步優化。請參見《盡可能使用堆棧變數》。
8、不要重復初始化變數 默認情況下,調用類的構造函數時, Java會把變數初始化成確定的值:所有的對象被設置成null,整數變數(byte、short、int、long)設置成0,float和double變數設置成0.0,邏輯值設置成false。當一個類從另一個類派生時,這一點尤其應該注意,因為用new關鍵詞創建一個對象時,構造函數鏈中的所有構造函數都會被自動調用。
9、在JAVA + ORACLE 的應用系統開發中,java中內嵌的SQL語句盡量使用大寫的形式,以減輕ORACLE解析器的解析負擔。
10、Java 編程過程中,進行資料庫連接、I/O流操作時務必小心,在使用完畢後,即使關閉以釋放資源。因為對這些大對象的操作會造成系統大的開銷,稍有不慎,會導致嚴重的後果。
11、由於JVM的有其自身的GC機制,不需要程序開發者的過多考慮,從一定程度上減輕了開發者負擔,但同時也遺漏了隱患,過分的創建對象會消耗系統的大量內存,嚴重時會導致內存泄露,因此,保證過期對象的及時回收具有重要意義。JVM回收垃圾的條件是:對象不在被引用;然而,JVM的GC並非十分的機智,即使對象滿足了垃圾回收的條件也不一定會被立即回收。所以,建議我們在對象使用完畢,應手動置成null。
12、在使用同步機制時,應盡量使用方法同步代替代碼塊同步。
13、盡量減少對變數的重復計算
例如:
for(inti=0;i<list.size;i++){
…
}
應替換為:
for(inti=0,intlen=list.size();i<len;i++){
…
}
14、盡量採用lazy loading 的策略,即在需要的時候才開始創建。
例如:
Stringstr=「aaa」;
if(i==1){
list.add(str);
}
應替換為:
if(i==1){
Stringstr=「aaa」;
list.add(str);
}
15、慎用異常
異常對性能不利。拋出異常首先要創建一個新的對象。Throwable介面的構造函數調用名為fillInStackTrace()的本地(Native)方法,fillInStackTrace()方法檢查堆棧,收集調用跟蹤信息。只要有異常被拋出,VM就必須調整調用堆棧,因為在處理過程中創建了一個新的對象。 異常只能用於錯誤處理,不應該用來控製程序流程。
16、不要在循環中使用try...catch,應把其放置在最外層。
17、合理的使用Java類 java.util.Vector。
簡單地說,一個Vector就是一個java.lang.Object實例的數組。Vector與數組相似,它的元素可以通過整數形式的索引訪問。但是,Vector類型的對象在創建之後,對象的大小能夠根據元素的增加或者刪除而擴展、縮小。請考慮下面這個向Vector加入元素的例子:
Java代碼收藏代碼
Objectobj=newObject();
Vectorv=newVector(100000);
for(intI=0;
I<100000;I++){v.add(0,obj);}
Ⅹ 今天遇到一公司的超難的java面試題,面掛了,求幫忙!
交流一下,有些我也不會。^_^
1、數據沒有排序的嗎?排序了,可以用類似二分法搞。
3、只需用ThreadLocal包裝一下這個bean的日期屬性即可,簡單。
5、有介面直接用spring HttpInvoke遠程調用即可。
6、最簡單的辦法就是使用Terracotta伺服器集群tomcat,ecache、quartz、session一站式解決。不那樣的,緩存的話,可用memcached。tomcat中的session是復制式,直接在tomcat配置文件就可以吧。如果要實現自己手動更新感覺可以考慮用jms手動傳遞數據同步。
8、處理效率高了不少,並發效率好、數據更安全。
9、簽名的演算法是公開的,但是演算法相關的密鑰是私有的。