java知識點總結

天下間,豈有長生不滅者

對於很多初學者或則剛畢業的學生來說,面試找到一份好的工作是相當必要的,其實很多大的互聯網公司或則優秀的傳統企業都是比較看重基礎的,所以,在此,對面試經常被問到的基礎知識點做一個總結。

大綱:

 

  • java入門
  • 集合
  • JVM 內存管理
  • Java 8 知識點
  • 網絡協議相關
  • 數據庫相關
  • MVC 框架相關
  • git常規操作
  • memcache redis緩存
  • 分佈式數據集

 

 

1.java入門

a.面向對象的特徵:封裝,繼承,抽象,多態

b.覆蓋和重載的區別:這個主要是從使用注意點和重載條件兩方面進行一個分析

注意點:子類拋出的異常不能多於父類,訪問權限不能比父類小,比如說父類的訪問權限是平protected那麼子類只可能是public或則default,如果父類是private那麼子類就不能重載這個方法了

重載條件:參數個數不同,參數類型不同,參數順序不同    注意了,返回值的類型並不能構成重載,原因是java中調用方法不需要強制賦值,也就沒法區分所以不能構成重載。

c.java繼承的初始化順序

  • 按定義時的順序初始化基類的static成員
  • 按定義時的順序初始化擴展類的static成員
  • (以上兩條是對第一次定義擴展類對象時運行)
  • 按定義時順序執行基類的指定初始化
  • 執行基類的構造函數
  • 按定義時的順序執行擴展類的指定初始化
  • 執行擴展類的構造函數

 

d.抽象類和接口的區別

 

  • 抽象類中可以沒有抽象方法;接口中的方法必須是抽象方法;
  • 抽象類中可以有普通的成員變量;接口中的變量必須是 static final 類型的,必須被初始化 , 接口中只有常量,沒有變量;
  • 抽象類只能單繼承,接口可以繼承多個父接口;
  • 繼承一個接口必須要實現這個接口的所有方法;而抽象類不需要;
  • Java8 中接口中會有 default 方法,即方法可以被實現;

 

e.java和c++的區別

 

共同點:都支持繼承封裝和多態,都是面向對象編程的語言
不同點:Java 不提供指針來直接訪問內存,程序更加安全;Java 的類是單繼承的,C++ 支持多重繼承; Java 通過一個類實現多個接口來實現 C++ 中的多重繼承; Java 中類不可以多繼承,但是!!!接口可以多繼承; Java 有自動內存管理機制,不需要程序員手動釋放無用內存

 

 

f.list集合如何去重

利用set的特徵
Set set=new HashSet();
List<Long> newLibraryIds=new ArrayList<Long>();
for(Long libraryId:libraryIds){
    if(set.add(libraryId)){
        newLibraryIds.add(libraryId);
    }
}

g.java中的值傳遞和引用傳遞的區別

值傳遞是指對象被值傳遞,意味着傳遞了對象的一個副本,即使副本被改變,也不會影響源對象。引用傳遞是指對象被引用傳遞,意味着傳遞的並不是實際的對象,而是對象的引用。因此,外部對引用對象的改變會反映到所有的對象上。

h.jdk,jre,jvm簡單介紹下

 

JDK 是 java 開發工具包,是 java 開發環境的核心組件,並提供編譯、調試和運行一個 java 程序所需要的所有工具,可執行文件和二進制文件,是一個平臺特定的軟件。
JRE 是 java 運行時環境,是 JVM 的實施實現,提供了運行 java 程序的平臺。JRE 包含了 JVM,但是不包含 java 編譯器 / 調試器之類的開發工具。
JVM 是 java 虛擬機,當我們運行一個程序時,JVM 負責將字節碼轉換爲特定機器代碼,JVM 提供了內存管理 / 垃圾回收和安全機制等。
這種獨立於硬件和操作系統,正是 java 程序可以一次編寫多處執行的原因。


區別:
JDK 用於開發,JRE 用於運行 java 程序;
JDK 和 JRE 中都包含 JVM;
JVM 是 java 編程語言的核心並且具有平臺獨立性。

i.java繼承的初始化順序

按定義時的順序初始化基類的static成員
按定義時的順序初始化擴展類的static成員
以上兩條是對第一次定義擴展類對象時運行)
按定義時順序執行基類的指定初始化
執行基類的構造函數
按定義時的順序執行擴展類的指定初始化
行擴展類的構造函數

 

2.java集合

 

a.Hashtable和HashMap的區別

  1. HashMap 沒有考慮同步,是線程不安全的;Hashtable 使用了 synchronized 關鍵字,是線程安全的;

  2. 前者允許 null 作爲 Key;後者不允許 null 作爲 Key。

  3. hashmap詳解

 

 

HashMap的概述:
在java編程語言中,最基本的結構就是兩種,一個是數組,另外一個是模擬指針(引用),所有的數據結構都可以用這兩個基本結構來構造的,
HashMap也不例外。HashMap實際上是一個“鏈表散列”的數據結構,即數組和鏈表的結合體。這樣的結構結合了鏈表在增刪方面的高效和數組在
尋址上的優勢
HashMap的結構:
哈希表是由數組+鏈表組成的,數組的默認長度爲16(可以自動變長。在構造HashMap的時候也可以指定一個長度),數組裏每個元素存儲的是
一個鏈表的頭結點。而組成鏈表的結點其實就是hashmap內部定義的一個類:Entity。Entity包含三個元素:key,value和指向下一個Entity

 

的next

HashMap的存取:
這些元素是按照什麼樣的規則存儲到數組中呢。一般情況是通過hash(key)%(len-1)獲得,也就是元素的key的哈希值對數組長度取模得到。
比如上述哈希表中,12%16=12,28%16=12,108%16=12,140%16=12。所以12、28、108以及140都存儲在數組下標爲12的位置。

HashMap的存儲--put:
int hash = key.hashCode(); // 這個hashCode方法這裏不詳述,只要理解每個key的hash是一個固定的int值
int index = hash %( Entry[].length-1);
table[index] = value;//假定存儲鏈表頭結點的數組名爲table
用table[index]表示通過hash值計算出來的、元素需要存儲在數組中的位置。先判斷該位置上有沒有存有Entity,沒有的話就創建一個Entity<k,v>對象,在該位置上插入,插入結束;如果有的話,通過鏈表的遍歷方式去逐個遍歷,通過equals方法將key和已有的key進行比較,看看有沒有已經存在的key,有的話用新的value替換老的value;如果沒有,則在table[index]插入該Entity,把原來在table[index]位置上的Entity賦值給新的 Entity的next,也即,新的Entity插入(put)的位置永遠是在鏈表的最前面,這樣插入結束。 打個比方, 第一個鍵值對A進來,通過計算其key的hash得到的index=0,記做:table[0] = A。一會後又進來一個鍵值對B,通過計算其index也等於0,現在怎麼辦?HashMap會這樣做:B.next = A,table[0] = B,如果又進來C,index也等於0,那麼C.next = B,table[0] = C;這樣我們發現index=0的地方其實存取了A,B,C三個鍵值對,他們通過next這個屬性鏈接在一起。
注:null key總是存放在Entry[]數組的第一個元素。
擴展:
按照散列函數的定義,如果兩個對象相同,即obj1.equals(obj2)=true,則它們的hashCode必須相同,但如果兩個對象不同,則它們的hashCode不一定不同。如果兩個不同對象的hashcode相同,就稱爲衝突(解決hash衝突一般用鏈地址法)。衝突會導致操作哈希表的時間開銷增大,所以儘量定義好的hashCode()方法,能加快哈希表的操作。覆蓋了equals方法之後一定要覆蓋hashCode方法,原因很簡單,比如,String a = new String(“abc”);String b = new String(“abc”);如果不覆蓋hashCode的話,那麼a和b的hashCode就會不同,把這兩個類當做key存到HashMap中的話就 會出現問題,就會和key的唯一性相矛盾。

HashMap的讀取--get:
先定位到數組元素,再遍歷該元素處的鏈表
複製代碼
public V get(Object key) {
        if (key == null)
            return getForNullKey();
        int hash = hash(key.hashCode());
        //先定位到數組元素,再遍歷該元素處的鏈表
        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;//顯然,在尋找目標元素的時候,除了對比通過key計算出來的hash值,還會用雙等或equals方法對key本身來進行比較,兩者都爲true時纔會返回這個元素
        }
        return null;
}

 

b.ConcurrentHashMap 和 Hashtable 的區別

 

ConcurrentHashMap 結合了 HashMap 和 HashTable 二者的優勢。HashMap 沒有考慮同步,hashtable 考慮了同步的問題。但是 hashtable 在每次同步執行時都要鎖住整個結構。 ConcurrentHashMap 鎖的方式是稍微細粒度的。 ConcurrentHashMap 將 hash 表分爲 16 個桶(默認值),諸如 get,put,remove 等常用操作只鎖當前需要用到的桶。


ConcurrentHashMap 實現原理:
該類包含兩個靜態內部類 HashEntry 和 Segment;前者用來封裝映射表的鍵值對,後者用來充當鎖的角色;
Segment 是一種可重入的鎖 ReentrantLock,每個 Segment 守護一個 HashEntry 數組裏得元素,當對 HashEntry 數組的數據進行修改時,必須首先獲得對應的 Segment 鎖。

 

c.List、Set 和 Map 的初始容量和加載因子

1. List
  ArrayList 的初始容量是 10;加載因子爲 0.5; 擴容增量:原容量的 0.5 倍 +1;一次擴容後長度爲 16。
  Vector 初始容量爲 10,加載因子是 1。擴容增量:原容量的 1 倍,如 Vector 的容量爲 10,一次擴容後是容量爲 20。
2. Set
HashSet,初始容量爲 16,加載因子爲 0.75; 擴容增量:原容量的 1 倍; 如 HashSet 的容量爲 16,一次擴容後容量爲 32
3. Map
HashMap,初始容量 16,加載因子爲 0.75; 擴容增量:原容量的 1 倍; 如 HashMap 的容量爲 16,一次擴容後容量爲 32

 

d.java.util.ArrayList.trimToSize() 

具有去除空項壓縮 體積的作用,方法修整此ArrayList實例的是列表的當前大小的容量。應用程序可以使用此操作,以儘量減少一個ArrayList實例的存儲。

3.jvm內存管理

a.多線程和單線程的區別和聯繫

1.在單核 CPU 中,將 CPU 分爲很小的時間片,在每一時刻只能有一個線程在執行,是一種微觀上輪流佔用 CPU 的機制

2.多線程會存在線程上下文切換,會導致程序執行速度變慢,即採用一個擁有兩個線程的進程執行所需要的時間比一個線程的進程執行兩次所需要的時間要多一些 也就是說採用多線程並不會提高程序的執行速度,但是對於用戶來說,縮短了程序響應的時間

b.線程和進程的區別

1.進程是一個 “執行中的程序”,是系統進行資源分配和調度的一個獨立單位;
2.線程是進程的一個實體,一個進程中擁有多個線程,線程之間共享地址空間和其它資源(所以通信和同步等操作線程比進程更加容易);
3.線程上下文的切換比進程上下文切換要快很多。
(1)進程切換時,涉及到當前進程的 CPU 環境的保存和新被調度運行進程的 CPU 環境的設置。
(2)線程切換僅需要保存和設置少量的寄存器內容,不涉及存儲管理方面的操作。

c.多線程產生死鎖的四個必要條件

 

1.互斥條件:一個資源每次只能被一個線程使用;
2.請求與保持條件:一個線程因請求資源而阻塞時,對已獲得的資源保持不放;
3.不剝奪條件:進程已經獲得的資源,在未使用完之前,不能強行剝奪;
4.循環等待條件:若干線程之間形成一種頭尾相接的循環等待資源關係。

 

d.如何避免死鎖

 獲得鎖的順序是一定的,比如只有獲得A鎖的線程纔有資格獲取B鎖,按順序獲取鎖就可以避免死鎖

e.Java 集合的快速失敗機制 “fail-fast”

它是 java 集合的一種錯誤檢測機制,當多個線程對集合進行結構上的改變的操作時,有可能會產生 fail-fast 機制。
例如 :假設存在兩個線程(線程 1、線程 2),線程 1 通過 Iterator 在遍歷集合 A 中的元素,在某個時候線程 2 修改了集合 A 的結構(是結構上面的修改,而不是簡單的修改集合元素的內容),那麼這個時候程序就會拋出 ConcurrentModificationException 異常,從而產生 fail-fast 機制。
原因: 迭代器在遍歷時直接訪問集合中的內容,並且在遍歷過程中使用一個 modCount 變量。集合在被遍歷期間如果內容發生變化,就會改變 modCount 的值。
每當迭代器使用 hashNext()/next() 遍歷下一個元素之前,都會檢測 modCount 變量是否爲 expectedmodCount 值,是的話就返回遍歷;否則拋出異常,終止遍歷。
解決辦法:
1.在遍歷過程中,所有涉及到改變 modCount 值得地方全部加上 synchronized;
2.使用 CopyOnWriteArrayList 來替換 ArrayList。

f.如何指定多個線程的執行順序?

設定一個 orderNum,每個線程執行結束之後,更新 orderNum,指明下一個要執行的線程。並且喚醒所有的等待線程。
在每一個線程的開始,要 while 判斷 orderNum 是否等於自己的要求值!!不是,則 wait,是則執行本線程。

g.jvm運行過程

加載    將class文件字節碼內容加載到內存中,並將這些靜態數據轉換成方法區中的運行時數據結構,在堆中生成一個代表這個類的java.lang.Class對象,作爲方法區類數據的訪問入口,這個過程需要類加載器參與。
鏈接    將java類的二進制代碼合併到JVM的運行狀態之中的過程
  驗證:確保加載的類信息符合JVM規範,沒有安全方面的問題
  準備:正式爲類變量(static變量)分配內存並設置類變量初始值的階段,這些內存都將在方法去中進行分配
  解析:虛擬機常量池的符號引用替換爲字節引用過程 
初始化  初始化階段是執行類構造器<clinit>()方法的過程。類構造器<clinit>()方法是由編譯器自動收藏類中的所有類變量的賦值動作和靜態語句塊(static塊)中的語句合併產生
當初始化一個類的時候,如果發現其父類還沒有進行過初始化,則需要先觸發其父類的初始化
虛擬機會保證一個類的<clinit>()方法在多線程環境中被正確加鎖和同步
當範圍一個Java類的靜態域時,只有真正聲名這個域的類纔會被初始化
使用
卸載

 

h.ThreadLocal(線程局部變量)關鍵字:

答:當使用 ThreadLocal 維護變量時,其爲每個使用該變量的線程提供獨立的變量副本,所以每一個線程都可以獨立的改變自己的副本,而不會影響其他線程對應的副本。
ThreadLocal 內部實現機制:
每個線程內部都會維護一個類似 HashMap 的對象,稱爲 ThreadLocalMap,裏邊會包含若干了 Entry(K-V 鍵值對),相應的線程被稱爲這些 Entry 的屬主線程;
Entry 的 Key 是一個 ThreadLocal 實例,Value 是一個線程特有對象。Entry 的作用即是:爲其屬主線程建立起一個 ThreadLocal 實例與一個線程特有對象之間的對應關係;
Entry 對 Key 的引用是弱引用;Entry 對 Value 的引用是強引用。

 

i.垃圾回收算法有哪些?

引用計數 :原理是此對象有一個引用,即增加一個計數,刪除一個引用則減少一個計數。垃圾回收時,只用收集計數爲 0 的對象。此算法最致命的是無法處理循環引用的問題;
標記-清除 :此算法執行分兩階段。第一階段從引用根節點開始標記所有被引用的對象,第二階段遍歷整個堆,把未標記的對象清除;
此算法需要暫停整個應用,同時,會產生內存碎片;
複製算法 :此算法把內存空間劃爲兩個相等的區域,每次只使用其中一個區域。垃圾回收時,遍歷當前使用區域,把正在使用中的對象複製到另外一個區域中;
此算法每次只處理正在使用中的對象,因此複製成本比較小,同時複製過去以後還能進行相應的內存整理,不會出現 “碎片” 問題。當然,此算法的缺點也是很明顯的,就是需要兩倍內存空間;
標記-整理 :此算法結合了 “標記-清除” 和 “複製” 兩個算法的優點。也是分兩階段,第一階段從根節點開始標記所有被引用對象,第二階段遍歷整個堆,把清除未標記對象並且把存活對象 “壓縮” 到堆的其中一塊,按順序排放。
此算法避免了 “標記-清除” 的碎片問題,同時也避免了 “複製” 算法的空間問題。

j.啓動一個線程是用run()還是start()? 爲什麼?(一些初始化的操作)

啓動線程肯定要用start()方法。當用start()開始一個線程後,線程就進入就緒狀態,使線程所代表的虛擬處理機處於可運行狀態,這意味着它可以由JVM調度並執行。這並不意味着線程就會立即運行。當cpu分配給它時間時,纔開始執行run()方法(如果有的話)。start()是方法,它調用run()方法.而run()方法是你必須重寫的. run()方法中包含的是線程的主體。如果直接執行run方法,那就不是執行一個線程了,而只是一個普通的方法

j.sleep,wait,notify

sleep是Thread類的靜態方法,誰調用誰去睡覺。sleep是佔用cpu去睡覺,而wait是放棄cpu去睡覺, sleep沒有釋放鎖,而wait釋放了鎖,sleep不會讓出cpu資源,wait是進入線程池等待,一般wait是不會使用時間參數,他必須等待別人notify他纔會進入就緒隊中。而sleep只要時間到了,就會自動進入就緒隊列。如果等不及了,只能通過interrupt來強項打斷。

wait,notify以及notifyall只能在同步控制方法或者同步控制塊中使用,而sleep可是在任何地方使用。

sleep必須捕獲異常,而其他3個則不需要。

在JAVA中的Object類型中,都是帶有一個內存鎖的,在有線程獲取該內存鎖後,其它線程無法訪問該內存,從而實現JAVA中簡單的同步、互斥操作。

 

k.i++線程安全嗎

i++和++i都是i=i+1的意思,但是過程有些許區別:

i++:先賦值再自加。(例如:i=1;a=1+i++;結果爲a=1+1=2,語句執行完後i再進行自加爲2)

++i:先自加再賦值。(例如:i=1;a=1+++i;結果爲a=1+(1+1)=3,i先自加爲2再進行運算)

但是在單獨使用時沒有區別:如for(int i=0;i<10;i++){ }和for(int i=0;i<10;++i) { }沒有區別。

i++和++i的線程安全分爲兩種情況:

1、如果i是局部變量(在方法裏定義的),那麼是線程安全的。因爲局部變量是線程私有的,別的線程訪問不到,其實也可以說沒有線程安不安全之說,因爲別的線程對他造不成影響。

2、如果i是全局變量(類的成員變量),那麼是線程不安全的。因爲如果是全局變量的話,同一進程中的不同線程都有可能訪問到。

l.java的四種引用

 

強引用:

只要引用存在,垃圾回收器永遠不會回收
Object obj = new Object();
//可直接通過obj取得對應的對象 如obj.equels(new Object());
而這樣 obj對象對後面new Object的一個強引用,只有當obj這個引用被釋放之後,對象纔會被釋放掉,這也是我們經常所用到的編碼形式。

軟引用:

非必須引用,內存溢出之前進行回收,可以通過以下代碼實現
Object obj = new Object();
SoftReference<Object> sf = new SoftReference<Object>(obj);
obj = null;
sf.get();//有時候會返回null
這時候sf是對obj的一個軟引用,通過sf.get()方法可以取到這個對象,當然,當這個對象被標記爲需要回收的對象時,則返回null;
軟引用主要用戶實現類似緩存的功能,在內存足夠的情況下直接通過軟引用取值,無需從繁忙的真實來源查詢數據,提升速度;當內存不足時,自動刪除這部分緩存數據,從真正的來源查詢這些數據。

 

弱引用:

第二次垃圾回收時回收,可以通過如下代碼實現
Object obj = new Object();
WeakReference<Object> wf = new WeakReference<Object>(obj);
obj = null;
wf.get();//有時候會返回null
wf.isEnQueued();//返回是否被垃圾回收器標記爲即將回收的垃圾
弱引用是在第二次垃圾回收時回收,短時間內通過弱引用取對應的數據,可以取到,當執行過第二次垃圾回收時,將返回null。
弱引用主要用於監控對象是否已經被垃圾回收器標記爲即將回收的垃圾,可以通過弱引用的isEnQueued方法返回對象是否被垃圾回收器標記。

 

虛引用:

垃圾回收時回收,無法通過引用取到對象值,可以通過如下代碼實現
Object obj = new Object();
PhantomReference<Object> pf = new PhantomReference<Object>(obj);
obj=null;
pf.get();//永遠返回null
pf.isEnQueued();//返回是否從內存中已經刪除
虛引用是每次垃圾回收的時候都會被回收,通過虛引用的get方法永遠獲取到的數據爲null,因此也被成爲幽靈引用。
虛引用主要用於檢測對象是否已經從內存中刪除。

 

 

4.網絡協議相關

 

a.tcp ip協議三次握手描述和四次握手斷開連接

首先Client端發送連接請求報文,Server段接受連接後回覆ACK報文,併爲這次連接分配資源。Client端接收到ACK報文後也向Server段發生ACK報文,並分配資源,這樣TCP連接就建立了。

b.爲什麼連接的時候是三次握手,關閉的時候卻是四次握手?
答:因爲當Server端收到Client端的SYN連接請求報文後,可以直接發送SYN+ACK報文。其中ACK報文是用來應答的,SYN報文是用來同步的。但是關閉連接時,當Server端收到FIN報文時,很可能並不會立即關閉SOCKET,所以只能先回復一個ACK報文,告訴Client端,"你發的FIN報文我收到了"。只有等到我Server端所有的報文都發送完了,我才能發送FIN報文,因此不能一起發送。故需要四步握手。

 

 

6.springmvc web相關

a.ajax狀態值和狀態碼

狀態值

 0 - (未初始化)還沒有調用send()方法
 1 - (載入)已調用send()方法,正在發送請求
 2 - (載入完成)send()方法執行完成,已經接收到全部響應內容
 3 - (交互)正在解析響應內容
 4 - (完成)響應內容解析完成,可以在客戶端調用了

狀態碼(此處標明常出現的狀態碼)

200——交易成功
400——錯誤請求,如語法錯誤
401——請求授權失敗
403——請求不允許
404——沒有發現文件、查詢或URl
500——服務器產生內部錯誤
502——服務器暫時不可用,有時是爲了防止發生系統過載

 

 

7.數據庫相關

a.使用索引的注意事項

1.索引不會包含有NULL的列
只要列中包含有NULL值,都將不會被包含在索引中,複合索引中只要有一列含有NULL值,那麼這一列對於此符合索引就是無效的。
2.使用短索引
對串列進行索引,如果可以就應該指定一個前綴長度。例如,如果有一個char(255)的列,如果在前10個或20個字符內,多數值是唯一的,那麼就不要對整個列進行索引。短索引不僅可以提高查詢速度而且可以節省磁盤空間和I/O操作。
3.索引列排序
mysql查詢只使用一個索引,因此如果where子句中已經使用了索引的話,那麼order by中的列是不會使用索引的。因此數據庫默認排序可以符合要求的情況下不要使用排序操作,儘量不要包含多個列的排序,如果需要最好給這些列建複合索引。
4.like語句操作
一般情況下不鼓勵使用like操作,如果非使用不可,注意正確的使用方式。like ‘%aaa%’不會使用索引,而like ‘aaa%’可以使用索引。
5.不要在列上進行運算
6.不使用負向查詢NOT IN 、<>、!=操作,但<,<=,=,>,>=,BETWEEN,IN是可以用到索引的
7.索引要建立在經常進行select操作的字段上。
這是因爲,如果這些列很少用到,那麼有無索引並不能明顯改變查詢速度。相反,由於增加了索引,反而降低了系統的維護速度和增大了空間需求。
8.索引要建立在值比較唯一的字段上。
9.對於那些定義爲text、image和bit數據類型的列不應該增加索引。因爲這些列的數據量要麼相當大,要麼取值很少。
10.在where和join中出現的列需要建立索引。
11.where的查詢條件裏有不等號(where column != …),mysql將無法使用索引。
12.如果where字句的查詢條件裏使用了函數(如:where DAY(column)=…),mysql將無法使用索引。
在join操作中(需要從多個數據表提取數據時),mysql只有在主鍵和外鍵的數據類型相同時才能使用索引,否則及時建立了索引也不會使用。

b.事務的四大特性

原子性(Atomicity):事務作爲一個整體被執行 ,要麼全部執行,要麼全部不執行;
一致性(Consistency):保證數據庫狀態從一個一致狀態轉變爲另一個一致狀態;
隔離性(Isolation):多個事務併發執行時,一個事務的執行不應影響其他事務的執行;
持久性(Durability):一個事務一旦提交,對數據庫的修改應該永久保存。

c.事務的隔離級別

 

讀未提交,顧名思義,就是一個事務可以讀取另一個未提交事務的數據。

讀提交,顧名思義,就是一個事務要等另一個事務提交後才能讀取數據。(程序員拿着信用卡去享受生活(卡里當然是只有3.6萬),當他埋單時(程序員事務開啓),收費系統事先檢測到他的卡里有3.6萬,就在這個時候!!程序員的妻子要把錢全部轉出充當家用,並提交。當收費系統準備扣款時,再檢測卡里的金額,發現已經沒錢了(第二次檢測金額當然要等待妻子轉出金額事務提交完)。程序員就會很鬱悶,明明卡里是有錢的)

重複讀,就是在開始讀取數據(事務開啓)時,不再允許修改操作(重複讀可以解決不可重複讀問題。寫到這裏,應該明白的一點就是,不可重複讀對應的是修改,即UPDATE操作。但是可能還會有幻讀問題。因爲幻讀問題對應的是插入INSERT操作,而不是UPDATE操作)

Serializable 是最高的事務隔離級別,在該級別下,事務串行化順序執行,可以避免髒讀、不可重複讀與幻讀。但是這種事務隔離級別效率低下,比較耗數據庫性能,一般不使用。

 

d.數據庫索引的底層實現

底層數據結構是 B+ 樹

使用 B+ 樹的原因:查找速度快、效率高,在查找的過程中,每次都能拋棄掉一部分節點,減少遍歷個數

B+樹的數據結構

 

e.mysql爲什麼儘量不做連表查詢

1,mysql連表查詢是比較慢的,相比先查出一個表的記錄,然後再查另外一個表。

2,單表查詢的數據方便做緩存

3,連表查詢是會鎖多表,單表查詢只鎖單表。

 4,如果以後需要分庫分表,連表的sql語句就需要改了

 

f.對比MySQL,你究竟在什麼時候更需要MongoDB

 

你期望一個更高的寫負載

默認情況下,對比事務安全,MongoDB更關注高的插入速度。如果你需要加載大量低價值的業務數據,那麼MongoDB將很適合你的用例。但是必須避免在要求高事務安全的情景下使用MongoDB,比如一個1000萬美元的交易。

不可靠環境保證高可用性

設置副本集(主-從服務器設置)不僅方便而且很快,此外,使用MongoDB還可以快速、安全及自動化的實現節點(或數據中心)故障轉移。

未來會有一個很大的規模

數據庫擴展是非常有挑戰性的,當單表格大小達到5-10GB時,MySQL表格性能會毫無疑問的降低。如果你需要分片並且分割你的數據庫,MongoDB因爲支持自動分片將很容易實現這一點。

使用基於位置的數據查詢

MongoDB支持二維空間索引,因此可以快速及精確的從指定位置獲取數據。

非結構化數據的爆發增長

給RDBMS增加列在有些情況下可能鎖定整個數據庫,或者增加負載從而導致性能下降,這個問題通常發生在表格大於1GB(更是下文提到BillRun系統中的痛點——單表格動輒幾GB)的情況下。鑑於MongoDB的弱數據結構模式,添加1個新字段不會對舊錶格有任何影響,整個過程會非常快速;因此,在應用程序發生改變時,你不需要專門的1個DBA去修改數據庫模式。

缺少專業的數據庫管理員

如果你沒有專業的DBA,同時你也不需要結構化你的數據及做join查詢,MongoDB將會是你的首選。MongoDB非常適合類的持久化,類可以被序列化成JSON並儲存在MongoDB。需要注意的是,如果期望獲得一個更大的規模,你必須要了解一些最佳實踐來避免走入誤區。

g.表分區有以下優點:

改善查詢性能:對分區對象的查詢可以僅搜索自己關心的分區,提高檢索速度。
增強可用性:如果表的某個分區出現故障,表在其他分區的數據仍然可用;
維護方便:如果表的某個分區出現故障,需要修復數據,只修復該分區即可;
均衡I/O:可以把不同的分區映射到磁盤以平衡I/O,改善整個系統性能。
缺點:
分區表相關:已經存在的表沒有方法可以直接轉化爲分區表。不過 Oracle 提供了在線重定義表的功能。

 

8.mvc相關

 

a.JDK 動態代理實現和 cglib 實現
選擇:
如果目標對象實現了接口,默認情況下會採用 JDK 的動態代理實現 AOP,也可以強制使用 cglib 實現 AOP;
如果目標對象沒有實現接口,必須採用 cglib 庫,Spring 會自動在 JDK 動態代理和 cglib 之間轉換

JDK 動態代理,只能對實現了接口的類生成代理,而不是針對類,該目標類型實現的接口都將被代理。原理是通過在運行期間創建一個接口的實現類來完成對目標對象的代理。
spring aop
在是spring上下文中定義類BeanNameAutoProxyCreator的bean
在bean中注入interceptor攔截器的類以及要被代理的目標對象
在攔截器中實現MethodInterceptor接口並實現invoke的方法
在invoke方法中通過invocation.getArguments()獲取到被代理的目標對象並通過這個對象去調用目標對象的方法

jdk動態代理

定義一個實現接口 InvocationHandler 的類;
通過構造函數,注入被代理類;
實現 invoke( Object proxy, Method method, Object[] args)方法;
在主函數中獲得被代理類的類加載器;
使用 Proxy.newProxyInstance( ) 產生一個代理對象;
通過代理對象調用各種方法

package jiankunking;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
 * 調用處理器實現類
 * 每次生成動態代理類對象時都需要指定一個實現了該接口的調用處理器對象
 */
public class InvocationHandlerImpl implements InvocationHandler
{
    /**
     * 這個就是我們要代理的真實對象
     */
    private Object subject;
    /**
     * 構造方法,給我們要代理的真實對象賦初值
     *
     * @param subject
     */
    public InvocationHandlerImpl(Object subject)
    {
        this.subject = subject;
    }
    /**
     * 該方法負責集中處理動態代理類上的所有方法調用。
     * 調用處理器根據這三個參數進行預處理或分派到委託類實例上反射執行
     *
     * @param proxy  代理類實例
     * @param method 被調用的方法對象
     * @param args   調用參數
     * @return
     * @throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
    {
        //在代理真實對象前我們可以添加一些自己的操作
        System.out.println("在調用之前,我要乾點啥呢?");
        System.out.println("Method:" + method);
        //當代理對象調用真實對象的方法時,其會自動的跳轉到代理對象關聯的handler對象的invoke方法來進行調用
        Object returnValue = method.invoke(subject, args);
        //在代理真實對象後我們也可以添加一些自己的操作
        System.out.println("在調用之後,我要乾點啥呢?");
        return returnValue;
    }

}



package jiankunking;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
/**
 * 動態代理演示
 */
public class DynamicProxyDemonstration
{
    public static void main(String[] args)
    {
        //代理的真實對象
        Subject realSubject = new RealSubject();
        /**
         * InvocationHandlerImpl 實現了 InvocationHandler 接口,並能實現方法調用從代理類到委託類的分派轉發
         * 其內部通常包含指向委託類實例的引用,用於真正執行分派轉發過來的方法調用.
         * 即:要代理哪個真實對象,就將該對象傳進去,最後是通過該真實對象來調用其方法
         */
        InvocationHandler handler = new InvocationHandlerImpl(realSubject);
        ClassLoader loader = realSubject.getClass().getClassLoader();
        Class[] interfaces = realSubject.getClass().getInterfaces();
        /**
         * 該方法用於爲指定類裝載器、一組接口及調用處理器生成動態代理類實例
         */
        Subject subject = (Subject) Proxy.newProxyInstance(loader, interfaces, handler);
        System.out.println("動態代理對象的類型:"+subject.getClass().getName());
        String hello = subject.SayHello("jiankunking");
        System.out.println(hello);
    }

}

經常用到的是如下的方式,在代理類中完成調用,達到切面編程注入參數的效果

@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
    try {
        //  獲取攔截的方法的參數列表
        Object[] args = invocation.getArguments();
        if (args == null || args.length == 0) {
            return null;
        }


        for (Object arg : args) {
            if (!(arg instanceof 基礎接口)) {
                continue;
            }
            ((基礎接口) arg).setUserId('1234');
        }

    } catch (Exception e) {

    }

    return invocation.proceed();
}

 

b.Mybatis 知識點
關於 MyBatis 主要考察佔位符#和 $ 的區別,區別如下:

符號將傳入的數據都當做一個字符串,會對自動傳入的數據加一個雙引號;
$ 符號將傳入的數據直接顯示生成 SQL 中;
符號存在預編譯的過程,,對問號賦值,防止 SQL 注入;
$ 符號是直譯的方式,一般用在 order by ${列名}語句中;

 

 

能用#號就不要用 $ 符號。

 

c.單例

在controller中springmvc框架默認是單例的,當多個線程調用它的時候,它裏面的instance變量不是線程安全的,會發生竄數據的情況,因此我們在使用controller的時候
應避免在controller中定義實例變量
有以下幾種解決方案:
a.在controller中使用ThreadLocal變量

b.在spring配置文件controller中聲明scope="prototype",每次都創建新的controller

 

d.單點登錄

首先,你要寫一個單點登錄(sso)和一個登陸系統,將那些需要進行單點登陸的系統都歸納到sso的白名單裏,然後你在任意一個登陸系統登錄之後,請求道sso去,生成信任,然後這個信任和客戶端請求的系統(白名單的系統)返回給登陸的客戶端,並且sso存儲這個信任,客戶端收到返回的白名單系統請求(這個請求必須是sso給出,不能由客戶端直接可以請求),重定向請求到實際需要請求的地址,該系統驗證拿着客戶端的信任到sso中驗證,驗證ok說明在sso中進行過登陸,做放行,數據的獲取等等,實現單點登錄

 

e.springmvc執行步驟

DispatcherServlet在web.xml中的部署描述,從而攔截請求到Spring Web MVC
HandlerMapping的配置,從而將請求映射到處理器
HandlerAdapter的配置,從而支持多種類型的處理器
ViewResolver的配置,從而將邏輯視圖名解析爲具體視圖技術
處理器(頁面控制器)的配置,從而進行功能處理

 

 

 

9.git

版本回退
git reset --hard 139dcfaa558e3276b30b6b2e5cbbb9c00bbdca96 
git push -f -u origin master 
統計代碼git提交的行數

$ git log --author="$(git config --get user.name)" --pretty=tformat: --numstat | gawk '{ add += $1 ; subs += $2 ; loc += $1 - $2 } END { printf "added lines: %s removed lines : %s total lines: %s\n",add,subs,loc }' -

 

rebase和merge的區別
rebase也可以分支往主幹,主幹往分支,這只是方向的問題,真正的區別是歷史版本的不一致,merge是產生一個新的節點,將兩個分支的合併,rebase是丟棄分支的歷史版本提交併臨時保存爲補丁然後依次提交到要合併到的分支,效果是不會有合併的痕跡
所以一般主幹往分支合用rebase避免保留痕跡,這屬於是默認行爲了
分支往主幹合是有人爲干預,所以要用merge,要保留記錄

git和svn的區別
Git是分佈式的,SVN不是(自己寫的代碼放在自己電腦上,一段時間後再提交、合併,也可以不用聯網在本地提交)
GIT把內容按元數據方式存儲,而SVN是按文件
GIT沒有一個全局的版本號,而SVN有
GIT的內容完整性要優於SVN(GIT的內容存儲使用的是SHA-1哈希算法。這能確保代碼內容的完整性,確保在遇到磁盤故障和網絡問題時降低對版本庫的破壞)

 

 

10.memcached和redis

 

redis和memecache的不同在於:
1、存儲方式:
memecache 把數據全部存在內存之中,斷電後會掛掉,數據不能超過內存大小
redis有部份存在硬盤上,這樣能保證數據的持久性。
2、數據支持類型:
redis在數據支持上要比memecache多的多。
3、使用底層模型不同:
新版本的redis直接自己構建了VM 機制 ,因爲一般的系統調用系統函數的話,會浪費一定的時間去移動和請求。
4、運行環境不同:
Redis目前官方只支持Linux 上去行,從而省去了對於其它系統的支持,這樣的話可以更好的把精力用於本系統 環境上的優化,雖然後來微軟有一個小組爲其寫了補丁。但是沒有放到主幹上

1、Redis和Memcache都是將數據存放在內存中,都是內存數據庫。不過memcache還可用於緩存其他東西,例如圖片、視頻等等。
2、Redis不僅僅支持簡單的k/v類型的數據,同時還提供list,set,hash等數據結構的存儲。
3、虛擬內存--Redis當物理內存用完時,可以將一些很久沒用到的value 交換到磁盤
4、過期策略--memcache在set時就指定,例如set key1 0 0 8,即永不過期。Redis可以通過例如expire 設定,例如expire name 10
5、分佈式--設定memcache集羣,利用magent做一主多從;redis可以做一主多從。都可以一主一從
6、存儲數據安全--memcache掛掉後,數據沒了;redis可以定期保存到磁盤(持久化)
7、災難恢復--memcache掛掉後,數據不可恢復; redis數據丟失後可以通過aof恢復
8、Redis支持數據的備份,即master-slave模式的數據備份。

總結:
Redis使用最佳方式是全部數據in-memory。
Redis更多場景是作爲Memcached的替代者來使用。
當需要除key/value之外的更多數據類型支持時,使用Redis更合適。
當存儲的數據不能被剔除時,使用Redis更合適。

 

11.分佈式數據集

 

a.數據存儲中間介:Map/Reduce 和RDD

對於RDD:

·        它是不變的數據結構存儲

·        它是支持跨集羣的分佈式數據結構

·        可以根據數據記錄的key對結構進行分區

·        提供了粗粒度的操作,且這些操作都支持分區

·        它將數據存儲在內存中,從而提供了低延遲性

5個特點

RDD是Spark提供的核心抽象,全稱爲ResillientDistributed Dataset,即彈性分佈式數據集。

RDD在抽象上來說是一種元素集合,包含了數據。它是被分區的,分爲多個分區,每個分區分佈在集羣中的不同節點上,從而讓RDD中的數據可以被並行操作。(分佈式數據集)

RDD通常通過Hadoop上的文件,即HDFS文件或者Hive表,來進行創建;有時也可以通過應用程序中的集合來創建。

RDD最重要的特性就是,提供了容錯性,可以自動從節點失敗中恢復過來。即如果某個節點上的RDD partition,因爲節點故障,導致數據丟了,那麼RDD會自動通過自己的數據來源重新計算該partition。這一切對使用者是透明的。

RDD的數據默認情況下存放在內存中的,但是在內存資源不足時,Spark會自動將RDD數據寫入磁盤。(彈性)

 

12.分佈式鎖

 

分佈式鎖常用方式詳細介紹:https://www.cnblogs.com/austinspark-jessylu/p/8043726.html
分佈式鎖主要用來確保在分佈式服務中數據的一致性和準確性
主要通過四種 方式:1.數據庫對某方法名稱存入,有的話表示已經鎖住了沒有表示可以獲得鎖2.數據庫行級鎖select ...for update
3.memcache,redis,tair緩存 4.zookeper

 

 

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