<< 乘 >>除
Collections.shuffle(array);
小寫轉大寫(相差32) c-'0'
基本類型:
32位 | 64位 | |
char | 1 | 1 |
char* | 4 | 8 |
short int | 2 | 2 |
unsigned int | 4 | 4 |
float | 4 | 4 |
double | 8 | 8 |
long | 4 | 8 |
long long | 8 | 8 |
unsigned long | 4 | 8 |
int Integer自動裝箱(-128-127):https://blog.csdn.net/to_myslef/article/details/79523055
計算機存儲單位換算:
16位機,32位機表示比特(bit)
- bit:計算機中表示數據的最小單位
- Byte:計算機處理數據的單位
1Byte = 8bit
1G=2 10MB = 2 10 10 KB = 2 10 10 10Byte = 8 * 2 10 10 10 bit
GB 與 Gb不同 ==> GByte Gbit
public protected default private範圍:
類內部 | 本包 | 子類 | 外部包 | |
public | √ | √ | √ | √ |
protected | √ | √ | √ | × |
default | √ | √ | × | × |
private | √ | × | × | × |
Collection:
List |
ArrayList、Vector、LinkedList |
Map |
HashMap、HashTable、LinkedHashMap、TreeMap、WeakHashMap
LinkedHashMap accessOrder爲 true: 訪問爲時間順序 / false:插入順序排序 |
Set |
HashSet、LinkedHashSet、TreeSet
是基於Map的:map(e,PERSENT); //PERSENT爲靜態Object對象 |
Stack |
基於Vector |
Queue |
ArrayBlockingQueue、LinkedBlockingQueue、PriorityBlockingQueue、DelayQueue、SynchronousQueue |
Deque |
基於Queue ArrayDeque(基於Vector)、LinkedList、LinkedBlockingDeque |
List補充:
- ArrayList 底層爲數組,擴容時擴1.5倍
擴容:底層Object數組默認長度爲10。擴容時新建一個數組的拷貝,修改原數組指向新數組
- Vector 底層爲數組,擴容時擴2倍
- LinkedList 非線程安全
LinkedHashMap有序:
LinkedHashMap繼承自HashMap,具有高效性,同時在HashMap的基礎上,又在內部增加了一個鏈表,用以存放元素的順序。LinkedHashMap內部多了一個雙向循環鏈表的維護,該鏈表是有序的,可以按元素插入順序或元素最近訪問順序(LRU)排列,簡單地說:
LinkedHashMap=散列表+循環雙向鏈表
TreeMap有序:
TreeMap 實現了NavigableMap接口,意味着它支持一系列的導航方法。比如返回有序的key集合
TreeMap提供了四個構造方法,實現了方法的重載。無參構造方法中比較器的值爲null,採用自然排序的方法,如果指定了比較器則稱之爲定製排序.
- 自然排序:TreeMap的所有key必須實現Comparable接口,所有的key都是同一個類的對象
- 定製排序:創建TreeMap對象傳入了一個Comparator對象,該對象負責對TreeMap中所有的key進行排序,採用定製排序不要求Map的key實現Comparable接口。
Map補充:
- HashMap hash數組默認16
- HashTable hash數組默認11
解決衝突的方法:
- 開方地址法
- 再hash法
- 鏈地址法
HashMap實現原理:
位桶 + 鏈表 + 紅黑樹(當閾值超過8時,轉換爲紅黑樹)
HashMap存儲結構:
transient Entry[] table:
/**實際存儲的key-value鍵值對的個數*/
transient int size;
/**閾值,當table == {}時,該值爲初始容量(初始容量默認爲16);當table被填充了,也就是爲table分配內存空間後,
threshold一般爲 capacity*loadFactory。HashMap在進行擴容時需要參考threshold,後面會詳細談到*/
int threshold;
/**負載因子,代表了table的填充度有多少,默認是0.75
加載因子存在的原因,還是因爲減緩哈希衝突,如果初始桶爲16,等到滿16個元素才擴容,某些桶裏可能就有不止一個元素了。
所以加載因子默認爲0.75,也就是說大小爲16的HashMap,到了第13個元素,就會擴容成32。
*/
final float loadFactor;
/**HashMap被改變的次數,由於HashMap非線程安全,在對HashMap進行迭代時,
如果期間其他線程的參與導致HashMap的結構發生變化了(比如put,remove等操作),
需要拋出異常ConcurrentModificationException*/
transient int modCount;
staic class Entry<K,V> implements Map.Entry<K,V>{
V values; //concurrent HashMap: volatile V values;
Entry<K,V> next; //concurrent HashMap:final HashEntry<K,V> next;
final int hash;
}
Hash原理:使用key、hashcode找到bucket位置,獲取存儲的Entry對象
- index 獲取: index = (n-1) $ hash
- hash方法: return (h=key.hashcode()^(h>>>16))
- hashcode(): return key==null?0:key.hashcode() ^ value.hashcode()
- get方法:
get(Object key){
for(Entry<k,v> e = table[indexFor(hash,table.length)];e!=null;e=e.next){
Object k;
if(e.hash == hash && ((k=e.key)==key)||key.equals(k)) return e.value;
return null;
} }
Hash攻擊:hashCode相同,將HashMap變成了SingleLinkedList(Java8使用TreeMap提高性能)
HashMap死循環:多個線程同時put,同時觸發rehash操作,會導致出現循環節點,從而在get的時候,產生死循環
什麼時候擴容:通過HashMap源碼可以看到是在put操作時,即向容器中添加元素時,判斷當前容器中元素的個數是否達到閾值(負載因子*容量>目前Map大小)的時候,就要自動擴容了。
擴容(resize):當表中75%被佔用時重新計算容量;而這個擴容是計算出所需容器的大小之後重新定義一個新的容器,將原來容器中的元素放入其中。
https://www.jianshu.com/p/ee0de4c99f87
https://blog.csdn.net/woshimaxiao1/article/details/83661464
BlockingQueue不熟悉的操作:
拋異常 | 返回特殊值 | 執行阻塞 | |
插入 | add | offer | put |
取出 | remove | poll | take |
檢驗 | element | peek |
併發數據結構
併發List |
Vector、Collections.synchronized(List list) CopyOnWriteArrayList: 當對象進行寫操作時,複製該對象;若進行讀操作,則直接返回結果 (add方法比Vecor弱) 高併發讀選CopyOnWriteArrayList,寫頻繁用Vector |
併發Set |
CopyOnWriteArraySet(基於CopyOnWriteArrayList) |
併發Map |
HashTable、synchronizedMap()、ConcurrentHashMap() |
併發Queue |
ConcurrentLinkedQueue、BlockingQueue |
併發Deque(雙端隊列) |
LinkedBlockingDeque |
- 淺拷貝:會複製這個對象,但原對象的“對象”是共享的,即原對象和拷貝對象共享
- 深拷貝:會複製這個對象 + 對象的對象
程序優化手段:
緩衝:控制流速
實例:BufferedWriter、BufferedOutputStream
優點:改善I/O性能,提高顯示效果
緩存:存結果
實例:
- HashMap ---> WeakHashMp
- EHCache ---> Hibernate
- OSCache ---> OpenSymphony
- JBossCache ---> JBoss
池:對象池化。線程池、數據庫連接池
Java:Jakarta Commons Pool組件
ThreadPool{
List<PThread> idleThreads;
public synchronized static ThreadPool getInstance(){...}
}
負載均衡:
Java Terracotta分佈式緩存框架
引用類型:
- 強引用:只要引用存在,永遠不會回收
- 軟引用:在系統內存緊張的情況下,軟引用會被回收(SoftReference)
- 弱引用:在下一次垃圾回收時回收(WeakRefence)
- 虛引用:主要用於跟蹤對象回收,不能單獨使用,與引用隊列聯合使用(RefenceQueue:如PhantomQueue 虛引用隊列)(PhantomRefence)
String類的hashcode方法:
hash = S[0]*pow(31,n-1)+S[1]*pow(31,n-2)+...+S[n-1];
紅黑樹:
- 根永遠是黑色
- 葉節點是空節點
- 紅色節點的兩個子節點是黑色的(黑色不一定)
- 每個路徑包含相同數量的黑色節點
- 節點要麼是紅色,要麼是黑色
衝突解決方法:1. 改變節點顏色 2. 執行旋轉操作
旋轉操作不會影響旋轉結點的父結點,父結點以上的結構還是保持不變的
左旋隻影響旋轉結點和其右子樹的結構,把右子樹的結點往左子樹挪了
右旋隻影響旋轉結點和其左子樹的結構,把左子樹的結點往右子樹挪了
ps:黑色節點要麼是根,要麼是兩個孩子,單個孩子不可能是黑
插入操作:一查找插入的位置;二插入後自平衡
插入時要解決:紅紅衝突
插入情景1:紅黑樹爲空樹:直接把插入結點作爲根結點就行,插入的節點的顏色轉換成黑色
插入情景2:插入節點已經存在:把I設爲當前結點的顏色,更新當前結點的值爲插入結點的值
插入情景3:插入節點的父節點爲黑節點:直接插入
插入情景4:插入節點的父節點爲紅節點:該父結點不可能爲根結點,所以插入結點總是存在祖父結點
插入情景4.1:叔叔結點存在並且爲紅結點:
- 將P和S設置爲黑色
- 將PP設置爲紅色
- 把PP設置爲當前插入結點
如果PP的父結點是黑色,那麼無需再做任何處理;但如果PP的父結點是紅色,根據性質4,此時紅黑樹已不平衡了,所以還需要把PP當作新的插入結點,繼續做插入操作自平衡處理,直到平衡爲止
試想下PP剛好爲根結點時,那麼根據性質2,我們必須把PP重新設爲黑色,那麼樹的紅黑結構變爲:黑黑紅。換句話說,從根結點到葉子結點的路徑中,黑色結點增加了。這也是唯一一種會增加紅黑樹黑色結點層數的插入情景
插入情景4.2:叔叔結點不存在或爲黑結點,並且插入結點的父親結點是祖父結點的左子結點
- 將P設爲黑色
- 將PP設爲紅色
- 對PP進行右旋
插入情景4.2.2:插入結點是其父結點的右子結點
這種情景顯然可以轉換爲情景4.2.1
- 對P進行左旋
- 把P設置爲插入結點,得到情景4.2.1
- 進行情景4.2.1的處理
插入情景4.3:叔叔結點不存在或爲黑結點,並且插入結點的父親結點是祖父結點的右子結點
插入情景4.3.1:插入結點是其父結點的右子結點
- 將P設爲黑色
- 將PP設爲紅色
- 對PP進行左旋
插入情景4.3.2:插入結點是其父結點的右子結點
- 對P進行右旋
- 把P設置爲插入結點,得到情景4.3.1
- 進行情景4.3.1的處理
https://www.jianshu.com/p/e136ec79235c
紅黑樹與跳錶:性能差別不大,更新數據時,跳錶更新部分少,且在併發下跳錶性能更好
B-:多路搜索樹(非二叉)
B+:B-樹的變體,多路搜索樹。只有到葉子節點才命中
B*:在非根非葉子節點再加入指針鏈表
AVL:平衡二叉查找樹
- 平衡因子:bf(x) = h(x-right) - h(x-left);
- 平衡因子 1、0、-1的節點都被認爲是平衡的
LL型、RR型:(1)旋轉(2)變換根節點
LR型、RL型:(1)轉化LL,RR (2)再進行LL,RR轉換
紅黑樹與AVL樹的差別:
AVL是嚴格的平衡樹,因此在增加或者刪除節點的時候,根據不同情況,旋轉的次數比紅黑樹要多;
紅黑是弱平衡的,用非嚴格的平衡來換取增刪節點時候旋轉次數的降低;
所以簡單說,搜索的次數遠遠大於插入和刪除,那麼選擇AVL樹,如果搜索,插入刪除次數幾乎差不多,應該選擇RB樹。
聚合 & 組合:
Family ---> Child
Person ---> Brain
Java內部類:靜態內部類、非靜態內部類、局部內部類、匿名內部類
爲什麼匿名內部類必須用final修飾?
內部類編譯後class文件與外部不同,僅僅保留了外部引用;外部參數傳入內部類時,內部類並不是直接調用,而是對參數備份,保持內部參數與外部參數一致,用final讓引用不改變,否則,內外不一致。保證了即使外部生命週期結束,內部依然可用
不可變類:
不可變對象一旦創建之後就不可更改,不可變類自身線程安全;任何修改都會創建一個新的對象
- 成員變量使用private
- 不提供setter方法
- getter方法中,不直接返回對象本身,而是克隆對象
String爲不可變類:public final class String extends Object
String 不可變原因:
- 字符串常量池需要
- hash唯一,可放入緩存
- 當參數時,保證使用安全
subString潛在內存泄漏:
內部調用了String(int offset, int count, char value[]){......},很長的字符串,截取一個短小的字符串,如果短小字符串沒有被回收,大的字符串也沒有被回收。
new(s.substring())
StringTokenizer的使用:
StringTokenizer st = new StringTokenizer(";");
st.hasMoreTokens();
st.nextToken();
String中isEmpty(),null以及""的區別:
- isEmpty():分配了內存空間,值爲空(值=空)
- null:未分配內存空間,值爲無(值不存在)
- "":分配了內存空間,值爲空字符串(值=空字符串)
Enumeration接口和Iterator接口的區別?
- Enumeration的速度是Iterator的兩倍,也使用更少的內存
- Iterator更加安全,允許從集合中移除元素(Iterator的remove方法)
Java反射機制底層:
- ClassforName
- 方法反射
- 方法調用
ClassforName:
- findLoadedClass(String)
- 父加載器loadClass方法
- findClass()
- ClassLoader.defineclass將字節數組轉化爲class實例
- Class.newInstance 創建
方法反射:
- getDeclaredMethod
- SearchMethods
- privateGetDeclaredMethods 找到class中聲明方法的列表
- getRefectionFactory().copyMehod返回
方法調用
- MethodAccessor通過RefactionFactory類中的newMethodAccessor創建一個MethodAccessor接口對象
- NativeMethodAccessorImpl,DelegatingMehodAccessorImpl 中的invoke方法
- MehodAccessorGenerator().generateMehod返回
有關序列化:
父類序列化時,子類自動實現序列化,無需顯示實現Serializable接口。對象引用到其他對象,跟着序列化
SerialVersionId:提供運行效率,序列化時會計算(hashCode值)
Java如何實現跨平臺的?Java虛擬機負責將.class字節碼文件翻譯成特定平臺下的機器碼
爲什麼Java有反射而C++沒有?
Java運行時擁有類的一切信息,而C++通過RTT運行時識別類型信息不完整
面向對象五大原則:
- 單一職責原則:僅有一個引起變化的原因
- 開放封閉原則:既開放,又封閉
- 里氏替換原則:子類可以替換父類
- 依賴倒置原則
- 接口隔離原則
異常 & 錯誤:
- 運行時異常:除數爲0,數組越界
- 被檢查異常:
- Error:一般與虛擬機有關(系統崩潰、JVM錯誤、內存空間不足、方法調用棧溢出等)
this逃逸:
對象還沒有構造成功,this引用被髮布出去,線程中看到該對象狀態是沒有初始化完的狀態。取得對象線程並不一定會等待對象完結後才使用
發生條件:1. 在構造函數中創建內部類 2. 在構造函數中把這個內部類發佈出去
避免:避免這兩個條件同時出現(private修飾、getInstance方法)
Timsort思想:(待確認???)
合併 + 插入排序
排序單位是一個個塊分區,優化了merge最壞情況O(n2),複雜度小於nlongn,最壞nlongn
- 分區:對嚴格反序做分區,並反轉。如果分區過小,用後面元素補足
- 合併:(優化合並排序)
- 小於某值,直接二分插入排序
- 二分插入合併
- 如果兩個run長度加起來比前一個長,則中間位置和較短的合併
- 若2個run長度加起來比前面短,合併
蓄水池問題:一共有N個數據,且N個數據未知。把前K個元素放在水庫中,對之後的第i個元素,以k/i的概率替換掉某一個元素。
原型模式:
- 簡化對象創建過程
- 性能優於new一個對象
- Class.forName可重複創建相似對象
MyObject object = (MyObject)Class.forName("...").newInstance()
Class.forName 與 ClassLoader.loadClass 的區別
- Class.forName將.class文件加載,執行static塊 ClassLoader.loadClass將.class文件加載,不執行static塊,只有newInstance才執行static塊
- Class.forName(xx.xx)等同於Class.forName(xx.xx,true,CALLClass.class.getClassLoader()) 可以控制是否初始化類
- 實際調用了ClassLoader.load(className,false),loadClass加載時是沒有初始化的
HashCode算法:
- Object:對象經過處理後的內存地址
- String:只要字符串內容相同,哈希碼相同
- Integer包裝類:返回對象中包含的整數值
- int,char基礎類,如需存儲將自動裝箱
OOM異常 OutOfMemoryError
除了程序計數器外,虛擬機內存的其他幾個運行時區域都有OOM異常的可能
- Java Heap溢出(不斷創建對象)-->檢查虛擬機的參數-Xmx
- 運行時常量池溢出:如果向常量池中添加內容,使用String.intern()這個native方法
- 方法區溢出
- 虛擬機棧和本地方法棧溢出:
- 線程請求的棧深度大於虛擬機最大深度 StackOverFlowError
- 在擴展棧時無法申請到足夠的內存空間 OutOfMemoryError
Java8特性:
- Lambda表達式
- 函數式接口
- 接口方法(父類方法優先於接口)
- Base64編碼引入標準包中
- 日期?
- 並行數組
- HashMap的變化
- Stream更強大?
- jjs接收一些JavaScript源碼參數
- 類依賴分析器jdeps
Java動態代理
- 通過實現InvocationHandler接口創建自己的調用處理器
- 通過爲Proxy類指定ClassLoader對象和一組interface創建動態代理類
- 通過反射機制獲得動態代理類的構造參數
- 通過構造函數創建動態代理類實例,構造時調用處理器對象作爲參數傳入
Interface proxy =
(Interface)Proxy.newProxyInstance(classloader,new Class[]{Interface.class},new Invocation HandlerImpl...);
- 動態代理主要使用了newProxyInstance
- ClassLoader loader:指定當前目標對象使用類加載器,寫法固定
- Class<?>[] interfaces:目標對象實現的接口的類型,寫法固定
- InvocationHandler:事件處理接口,需傳入一個實現類,一般直接使用匿名內部類
User proxy = (User) Proxy.newProxyInstance(
user.getClass().getClassLoader(), //classloader
user.getClass().getInterfaces(), //interfaces
new InvocationHandler() {//handler
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
return method.invoke(proxy, args);
}
});
CGLib代理:https://github.com/vicotorz/Vcode/blob/master/src/Proxy_CGLib/ProxyFactory.java
算法:
- 動態規劃:從開始推到終
- 貪心算法:從終推到開始
- 回溯法:深度優先(所有解)
- 分支限界法:廣度優先(一個解)
Bloom Filter:查找一個元素是不是在集合中
原理:用K個Hash函數計算數字的K個位數,在數組中相應位置置爲1
查詢過程中,檢查K個位中的值,若有位數不爲1,則一定不存在
- Java BIO : 同步並阻塞 適用於連接數目比較小且固定的架構
- Java NIO : 同步非阻塞 適用於連接數目多且連接比較短(輕操作)的架構,比如聊天服務器
- Java AIO(NIO.2) : 異步非阻塞 連接數目多且連接比較長(重操作)的架構
https://blog.csdn.net/baiye_xing/article/details/73135566
Java NIO: 相比面向流I/O NIO面向緩存
- 爲所有原始類提供緩存支持
- 使用Java.nio.charset.Charset作爲字符集編碼解決方案
- 增加通道Channel對象
- 支持鎖和內存映射文件的文件訪問接口
- 基於Selector異步網絡I/O
核心:Buffer、Channel、Selector
- Buffer:
類型:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer
MappredBuffer:Java大文件直接映射到內存,比較大可以分段映射(force()、load()、isloaded())
DirectBuffer:直接訪問系統內部類,直接分配在物理內存,不佔用堆空間。讀寫比Buffer快,但創建和銷燬慢
Buffer使用步驟:
- 分配空間
- 寫入到Buffer
- 調用flip方法
- 從Buffer讀取到數據
- 調用clear方法
- flip:讀寫轉換
- allocate:創建
- warp:從數組中創建
- rewind:將position置零,清除標誌位,爲Buffer提取有效數據
- clear:重寫Buffer準備
- compact,mark:標記
- duplicate:複製緩衝區,完全一樣的Buffer,但position和limit不同
- slice:創建子緩存,緩衝分區
2. Channel:
類型:FileChannel、DatagramChannel、SocketChannel、ServerSocketChannel
- SocketChannel.open();
- SocketChannel.write(buffer);
- ScoketChannel.close();
3. Selector:運行單線程處理多個Channel
Selector管理多個I/O
創建一個Selector實例,並將channel註冊監控的信道上,調用Selector方法,阻塞等待
channel.register(selector,就緒事件(Connect、Accept、Read、Write))
selector.open() 主要完成建立pipe,並把pipe的wakeupSourceFd放入pollArray中,註冊過程中把channel文件描述符放在pollArray中
select對應內核sys_select調用,有事件發生,select會將臨時結果寫入到用戶空間並返回(輪詢pollArray中FD)
Linux 中用epoll不斷輪詢,當事件發生,回調函數把發生事件存儲在就緒事件鏈表,寫到用戶空間
1. 使用 FileInputStream fin = new FileInputStream(new File(...));
2. Channel FileChannel fc = fin.getChannel();
3. 創建Buffer ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
4. channel(Buffer) fc.read(byteBuffer); fc.write(byteBuffer);
5. 關閉channel fc.close();
6. Buffer關閉 byteBuffer.flip();
NIO補充功能:
- Scatter / Gatter:分散多個buffer,多個buffer寫入一個Channel
- transferForm / transferTo:數據直接在內核空間移動
toChannel.transforForm(fromChannel,position,count);
fromChannel.transforTo...
3. Pipe:兩個線程之間的單向連接
Thread A ------> sink channel ------> source channel ------>Thread B
sinkChannel = Pipe.sink(); //寫入sink通道
sourceChannel = Pipe.source(); //從source通道讀
4. DatagramChannel:用於接收UDP數據包
- NIO:非阻塞IO Buffer / Channel / Selector
- NIO2:引入四個異步 Channel,異步阻塞IO (WatchService watch類)
selector 無阻塞io,阻塞喚醒通過:
channel有事件發生
selector.select(timeout)超時
selector.wakeup()主動喚醒
Wakeup() 用於喚醒阻塞在select方法上的線程
在開始建立的pipe的sink端寫入一個字節,source文件描述符處於就緒狀態
poll方法返回,select方法返回
NIO優化數據訪問的方式:
- FileChannel.transferForm / FileChannel.transferTo 數據直接在內核空間移動
- FileChannel.map 適合大文件只讀操作
將文件按照一定大小映射到內存區域,程序訪問內存區域將直接操作這個文件數據,省去了內核空間到用戶空間複製的過程的損耗
DSL語言:Html,shell,make,ant,maven,rpm,dpkg,awk,正則表達式,dc計算機語言
1.爲什麼等待和通知是在 Object 類而不是 Thread 中聲明的?
- wait和notify不僅僅是普通方法或同步工具,更重要的他們是java中兩個線程之間的通信機制
- 每個對象都可以上鎖
- 在java中爲了進入代碼臨界區,線程需要鎖定並等待鎖定
- java是基於Hoare的監視器的思想:在Java中,所有對象都有一個監視器。在 Java 中,所有在另一個線程的執行中侵入的操作都被棄用了(例如 stop 方法)
2.爲什麼Java中不支持多重繼承?
- 可能產生鑽石型繼承問題產生歧義
- 多重繼承使設計複雜化並在轉換,構造函數鏈接等過程中產生問題
3.爲什麼Java不支持運算符重載?
- 簡單和清晰
- 避免編譯錯誤
- JVM複雜性:複雜的 JVM 可能導致 JVM 更慢,併爲保證在 Java 中運算符行爲的確定性從而減少了優化代碼的機會
- 讓開發工具處理更容易
4.爲什麼 String 在 Java 中是不可變的?
- 字符串池中的字符改變會影響其他引用的對象
- 字符串已被廣泛引用到許多java類的參數
- 線程安全,避免了java中的同步問題
- 允許string緩存其哈希碼,不會在每次調用String的hashcode方法時重新計算,在HashMao中作爲鍵時非常快
- 類加載機制使用
5.爲什麼 char 數組比 Java 中的 String 更適合存儲密碼?
- String會存在字符串池中,會在內存中持續很長時間,構成安全威脅
- 存在日誌文件或打印純文本的風險
6.爲什麼 char 數組比 Java 中的 String 更適合存儲密碼?
- 如果Serializable包含一個不可序列化的成員,會發生什麼?:任何序列化嘗試都會因NotSerializableException失敗,可以通過trancient或static變量解決
- 什麼是序列化?:序列化是把對象改成可以存到磁盤或通過網絡發送到其他運行中的 Java 虛擬機的二進制格式的過程, 並可以通過反序列化恢復對象狀態
- 可序列化接口 & 可外部接口 :Externalizable 給我們提供 writeExternal() 和 readExternal() 方法,讓我們靈活控制Java序列化機制,不依賴Java默認序列化。正確實現Externalizable接口可以顯著提高程序性能
- serialVersionUID:SerialVerionUID 用於對象的版本控制,不指定 serialVersionUID的後果是,當你添加或修改類中的任何字段時, 則已序列化類將無法恢復, 因爲爲新類和舊序列化對象生成的 serialVersionUID 將有所不同。Java 序列化過程依賴於正確的序列化對象恢復狀態的, ,並在序列化對象序列版本不匹配的情況下引發 java.io.InvalidClassException 無效類異常
- 如果類中一個成員未實現序列化接口,會發生什麼?:拋出NotSerializableException
- 假設新類的超級類實現可序列化接口, 如何避免新類被序列化?新類實現writeObject()和readObject()方法,並從該方法引發NotSerializableException異常
7. 爲什麼Java中 wait 方法需要在 synchronized 的方法中調用?
- Java 會拋出 IllegalMonitorStateException,如果我們不調用來自同步上下文的wait(),notify()或者notifyAll()方法。
- Javac 中 wait 和 notify 方法之間的任何潛在競爭條件(由於競態條件,我們可能會丟失通知,如果我們使用緩衝區或只使用一個元素,生產線程將永遠等待,你的程序將掛起)
8. 能用Java覆蓋靜態方法嗎?如果我在子類中創建相同的方法是編譯時錯誤?
不,你不能在Java中覆蓋靜態方法,但在子類中聲明一個完全相同的方法不是編譯時錯誤,這稱爲隱藏在Java中的方法
一些設計思考:
秒殺系統:
- 前端:頁面靜態化、禁止重複提交、用戶限流、圖片服務器和應用服務器分離
- 後端:限制uuid,消息隊列緩存
搖一搖功能:用戶座標geohash傳入到redis,並設置過期時間
手機掃二維碼登錄功能:
- index.html ====== GetQrCodeServlet(生成uuid唯一標識,生成二維碼)
- index展示二維碼
- 手機掃碼後發送驗證+ uuid到Server
- index調用LongConnectionCheckServlet進行長時間輪詢,參數是uuid;拿到uuid後檢查loginUsermap是否爲空
設計開發連接池:
- 編寫class實現DataSource接口
- 在class的構造器一次性創建n個連接,保存在LinkedList中
- 實現getConnection,從LinkedList中返回一個連接
- 提供將連接放回連接池的方法
定時器設計:https://www.jianshu.com/p/5a973f3ac409
時間幀Tick:提高時間精度
Timer:定時器本身屬性(時間間隔,作用次數,觸發行爲)
TimerId:Timer的唯一標識
Timer屬主:每個定時器的歸屬對象
當屬主對象釋放時,其負責釋放所佔的Timer資源,以防止Timer泄露。另一方面Timer觸發時,如果找不到屬主,也會主動釋放自己
Timer:時間輪(循環遍歷隊列),多級時間輪,最小堆