Java易忘點梳理

<< 乘  >>除

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

解決衝突的方法:

  1. 開方地址法
  2. 再hash法
  3. 鏈地址法

 

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反射機制底層:

  1. ClassforName
  2. 方法反射
  3. 方法調用

ClassforName:

  1. findLoadedClass(String)
  2. 父加載器loadClass方法
  3. findClass()
  4. ClassLoader.defineclass將字節數組轉化爲class實例
  5. Class.newInstance 創建

方法反射:

  1. getDeclaredMethod
  2. SearchMethods
  3. privateGetDeclaredMethods 找到class中聲明方法的列表
  4. getRefectionFactory().copyMehod返回

方法調用

  1. MethodAccessor通過RefactionFactory類中的newMethodAccessor創建一個MethodAccessor接口對象
  2. NativeMethodAccessorImpl,DelegatingMehodAccessorImpl 中的invoke方法
  3. MehodAccessorGenerator().generateMehod返回

 

有關序列化:

父類序列化時,子類自動實現序列化,無需顯示實現Serializable接口。對象引用到其他對象,跟着序列化

SerialVersionId:提供運行效率,序列化時會計算(hashCode值)

 

Java如何實現跨平臺的?Java虛擬機負責將.class字節碼文件翻譯成特定平臺下的機器碼

 

爲什麼Java有反射而C++沒有?

Java運行時擁有類的一切信息,而C++通過RTT運行時識別類型信息不完整

 

面向對象五大原則:

  1. 單一職責原則:僅有一個引起變化的原因
  2. 開放封閉原則:既開放,又封閉
  3. 里氏替換原則:子類可以替換父類
  4. 依賴倒置原則
  5. 接口隔離原則

 

異常 & 錯誤:

  • 運行時異常:除數爲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算法:

  1. Object:對象經過處理後的內存地址
  2. String:只要字符串內容相同,哈希碼相同
  3. Integer包裝類:返回對象中包含的整數值
  4. int,char基礎類,如需存儲將自動裝箱

 

OOM異常  OutOfMemoryError

除了程序計數器外,虛擬機內存的其他幾個運行時區域都有OOM異常的可能

  • Java Heap溢出(不斷創建對象)-->檢查虛擬機的參數-Xmx
  • 運行時常量池溢出:如果向常量池中添加內容,使用String.intern()這個native方法
  • 方法區溢出
  • 虛擬機棧和本地方法棧溢出:
    • 線程請求的棧深度大於虛擬機最大深度 StackOverFlowError
    • 在擴展棧時無法申請到足夠的內存空間  OutOfMemoryError

Java8特性:

  1. Lambda表達式
  2. 函數式接口
  3. 接口方法(父類方法優先於接口)
  4. Base64編碼引入標準包中
  5. 日期?
  6. 並行數組
  7. HashMap的變化
  8. Stream更強大?
  9. jjs接收一些JavaScript源碼參數
  10. 類依賴分析器jdeps

Java動態代理

  1. 通過實現InvocationHandler接口創建自己的調用處理器
  2. 通過爲Proxy類指定ClassLoader對象和一組interface創建動態代理類
  3. 通過反射機制獲得動態代理類的構造參數
  4. 通過構造函數創建動態代理類實例,構造時調用處理器對象作爲參數傳入
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面向緩存

  1. 爲所有原始類提供緩存支持
  2. 使用Java.nio.charset.Charset作爲字符集編碼解決方案
  3. 增加通道Channel對象
  4. 支持鎖和內存映射文件的文件訪問接口
  5. 基於Selector異步網絡I/O

核心:Buffer、Channel、Selector

  1. Buffer:

類型:ByteBuffer、CharBuffer、DoubleBuffer、FloatBuffer、IntBuffer、LongBuffer、ShortBuffer

 

MappredBuffer:Java大文件直接映射到內存,比較大可以分段映射(force()、load()、isloaded())

DirectBuffer:直接訪問系統內部類,直接分配在物理內存,不佔用堆空間。讀寫比Buffer快,但創建和銷燬慢

 

Buffer使用步驟:

  1. 分配空間
  2. 寫入到Buffer
  3. 調用flip方法
  4. 從Buffer讀取到數據
  5. 調用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補充功能:

  1. Scatter / Gatter:分散多個buffer,多個buffer寫入一個Channel
  2. 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 中聲明的?

  1. wait和notify不僅僅是普通方法或同步工具,更重要的他們是java中兩個線程之間的通信機制
  2. 每個對象都可以上鎖
  3. 在java中爲了進入代碼臨界區,線程需要鎖定並等待鎖定
  4. java是基於Hoare的監視器的思想:在Java中,所有對象都有一個監視器。在 Java 中,所有在另一個線程的執行中侵入的操作都被棄用了(例如 stop 方法)

2.爲什麼Java中不支持多重繼承?

  1. 可能產生鑽石型繼承問題產生歧義
  2. 多重繼承使設計複雜化並在轉換,構造函數鏈接等過程中產生問題

3.爲什麼Java不支持運算符重載?

  1. 簡單和清晰
  2. 避免編譯錯誤
  3. JVM複雜性:複雜的 JVM 可能導致 JVM 更慢,併爲保證在 Java 中運算符行爲的確定性從而減少了優化代碼的機會
  4. 讓開發工具處理更容易

4.爲什麼 String 在 Java 中是不可變的?

  1. 字符串池中的字符改變會影響其他引用的對象
  2. 字符串已被廣泛引用到許多java類的參數
  3. 線程安全,避免了java中的同步問題
  4. 允許string緩存其哈希碼,不會在每次調用String的hashcode方法時重新計算,在HashMao中作爲鍵時非常快
  5. 類加載機制使用

5.爲什麼 char 數組比 Java 中的 String 更適合存儲密碼?

  1. String會存在字符串池中,會在內存中持續很長時間,構成安全威脅
  2. 存在日誌文件或打印純文本的風險

6.爲什麼 char 數組比 Java 中的 String 更適合存儲密碼?

  1. 如果Serializable包含一個不可序列化的成員,會發生什麼?:任何序列化嘗試都會因NotSerializableException失敗,可以通過trancient或static變量解決
  2. 什麼是序列化?:序列化是把對象改成可以存到磁盤或通過網絡發送到其他運行中的 Java 虛擬機的二進制格式的過程, 並可以通過反序列化恢復對象狀態
  3. 可序列化接口 & 可外部接口 :Externalizable 給我們提供 writeExternal() 和 readExternal() 方法,讓我們靈活控制Java序列化機制,不依賴Java默認序列化。正確實現Externalizable接口可以顯著提高程序性能
  4. serialVersionUID:SerialVerionUID 用於對象的版本控制,不指定 serialVersionUID的後果是,當你添加或修改類中的任何字段時, 則已序列化類將無法恢復, 因爲爲新類和舊序列化對象生成的 serialVersionUID 將有所不同。Java 序列化過程依賴於正確的序列化對象恢復狀態的, ,並在序列化對象序列版本不匹配的情況下引發 java.io.InvalidClassException 無效類異常
  5. 如果類中一個成員未實現序列化接口,會發生什麼?:拋出NotSerializableException
  6. 假設新類的超級類實現可序列化接口, 如何避免新類被序列化?新類實現writeObject()和readObject()方法,並從該方法引發NotSerializableException異常

7. 爲什麼Java中 wait 方法需要在 synchronized 的方法中調用?

  1. Java 會拋出 IllegalMonitorStateException,如果我們不調用來自同步上下文的wait(),notify()或者notifyAll()方法。
  2. Javac 中 wait 和 notify 方法之間的任何潛在競爭條件(由於競態條件,我們可能會丟失通知,如果我們使用緩衝區或只使用一個元素,生產線程將永遠等待,你的程序將掛起)

8. 能用Java覆蓋靜態方法嗎?如果我在子類中創建相同的方法是編譯時錯誤?

不,你不能在Java中覆蓋靜態方法,但在子類中聲明一個完全相同的方法不是編譯時錯誤,這稱爲隱藏在Java中的方法


一些設計思考:

秒殺系統:

  • 前端:頁面靜態化、禁止重複提交、用戶限流、圖片服務器和應用服務器分離
  • 後端:限制uuid,消息隊列緩存

 

搖一搖功能:用戶座標geohash傳入到redis,並設置過期時間

 

手機掃二維碼登錄功能:

  • index.html ====== GetQrCodeServlet(生成uuid唯一標識,生成二維碼)
  • index展示二維碼
  • 手機掃碼後發送驗證+ uuid到Server
  • index調用LongConnectionCheckServlet進行長時間輪詢,參數是uuid;拿到uuid後檢查loginUsermap是否爲空

設計開發連接池:

  1. 編寫class實現DataSource接口
  2. 在class的構造器一次性創建n個連接,保存在LinkedList中
  3. 實現getConnection,從LinkedList中返回一個連接
  4. 提供將連接放回連接池的方法

定時器設計:https://www.jianshu.com/p/5a973f3ac409

時間幀Tick:提高時間精度

Timer:定時器本身屬性(時間間隔,作用次數,觸發行爲)

TimerId:Timer的唯一標識

Timer屬主:每個定時器的歸屬對象

當屬主對象釋放時,其負責釋放所佔的Timer資源,以防止Timer泄露。另一方面Timer觸發時,如果找不到屬主,也會主動釋放自己

Timer:時間輪(循環遍歷隊列),多級時間輪,最小堆

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章