Java面試題精選

1.Spring,SpringMVC,SpringBoot,SpringCloud有什么區(qū)別和聯(lián)系?

  • Spring是核心,java開發(fā)框架,提供了基礎(chǔ)功能
  • SpringMVC是基于Spring的一個MVC框架,web框架
  • Spring Boot 是為簡化Spring配置的快速開發(fā)整合包
  • Spring Cloud是構(gòu)建在Spring Boot之上的服務治理框架

2.你能說說Spring框架中Bean的生命周期嗎?

  • 實例化Bean
  • 將Bean納入Spring容器管理
  • 一系列接口實現(xiàn)判斷
  • 后置處理器階段
  • 銷毀階段

3.如何決定使用 HashMap 還是 TreeMap?

TreeMap繼承自SortedMap接口

TreeMap<K,V>的Key值是要求實現(xiàn) java.lang.Comparable,所以迭代的時候TreeMap默認是按照Key值升序排序的;TreeMap的實現(xiàn)是基于紅黑樹結(jié)構(gòu)。適用于按自然順序或自定義順序遍歷鍵(key)。

HashMap繼承AbstractMap抽象類

HashMap<K,V>的Key值實現(xiàn)散列 hashCode(),分布是散列的、均勻的,不支持排序;數(shù)據(jù)結(jié)構(gòu)主要是桶(數(shù)組),鏈表或紅黑樹。適用于在Map中插入、刪除和定位元素。

4.分庫分表之后,id 主鍵如何處理?

分庫分表就倆原因,要不就是單庫并發(fā)太高,要不就是單庫數(shù)據(jù)量太大

  • 基于數(shù)據(jù)庫的實現(xiàn)方案
    • 數(shù)據(jù)庫自增 id(并發(fā)不高)
    • 設(shè)置數(shù)據(jù)庫 sequence 或者表自增字段步長(服務節(jié)點固定,步長也固定,不利于擴展)
  • UUID(如果你是要隨機生成個什么文件名、編號之類的,你可以用 UUID,但是作為主鍵是不能用 UUID 的)
  • 獲取系統(tǒng)當前時間(將當前時間跟很多其他的業(yè)務字段拼接起來,作為一個 id)
  • snowflake 算法
0 | 0001100 10100010 10111110 10001001 01011100 00 | 10001 | 1 1001 | 0000 00000000
// 不用的1個bit    41bit時間戳    5bit機房id,5bit機器id  12bit序列號
public class IdWorker {

    private long workerId;
    private long datacenterId;
    private long sequence;

    public IdWorker(long workerId, long datacenterId, long sequence) {
        // sanity check for workerId
        // 這兒不就檢查了一下,要求就是你傳遞進來的機房id和機器id不能超過32,不能小于0
        if (workerId > maxWorkerId || workerId < 0) {
            throw new IllegalArgumentException(
                    String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
        }
        if (datacenterId > maxDatacenterId || datacenterId < 0) {
            throw new IllegalArgumentException(
                    String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
        }
        System.out.printf(
                "worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d",
                timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId);

        this.workerId = workerId;
        this.datacenterId = datacenterId;
        this.sequence = sequence;
    }

    private long twepoch = 1288834974657L;

    private long workerIdBits = 5L;
    private long datacenterIdBits = 5L;

    // 這個是二進制運算,就是 5 bit最多只能有31個數(shù)字,也就是說機器id最多只能是32以內(nèi)
    private long maxWorkerId = -1L ^ (-1L << workerIdBits);

    // 這個是一個意思,就是 5 bit最多只能有31個數(shù)字,機房id最多只能是32以內(nèi)
    private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
    private long sequenceBits = 12L;

    private long workerIdShift = sequenceBits;
    private long datacenterIdShift = sequenceBits + workerIdBits;
    private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
    private long sequenceMask = -1L ^ (-1L << sequenceBits);

    private long lastTimestamp = -1L;

    public long getWorkerId() {
        return workerId;
    }

    public long getDatacenterId() {
        return datacenterId;
    }

    public long getTimestamp() {
        return System.currentTimeMillis();
    }

    public synchronized long nextId() {
        // 這兒就是獲取當前時間戳,單位是毫秒
        long timestamp = timeGen();

        if (timestamp < lastTimestamp) {
            System.err.printf("clock is moving backwards.  Rejecting requests until %d.", lastTimestamp);
            throw new RuntimeException(String.format(
                    "Clock moved backwards.  Refusing to generate id for %d milliseconds", lastTimestamp - timestamp));
        }

        if (lastTimestamp == timestamp) {
            // 這個意思是說一個毫秒內(nèi)最多只能有4096個數(shù)字
            // 無論你傳遞多少進來,這個位運算保證始終就是在4096這個范圍內(nèi),避免你自己傳遞個sequence超過了4096這個范圍
            sequence = (sequence + 1) & sequenceMask;
            if (sequence == 0) {
                timestamp = tilNextMillis(lastTimestamp);
            }
        } else {
            sequence = 0;
        }

        // 這兒記錄一下最近一次生成id的時間戳,單位是毫秒
        lastTimestamp = timestamp;

        // 這兒就是將時間戳左移,放到 41 bit那兒;
        // 將機房 id左移放到 5 bit那兒;
        // 將機器id左移放到5 bit那兒;將序號放最后12 bit;
        // 最后拼接起來成一個 64 bit的二進制數(shù)字,轉(zhuǎn)換成 10 進制就是個 long 型
        return ((timestamp - twepoch) << timestampLeftShift) | (datacenterId << datacenterIdShift)
                | (workerId << workerIdShift) | sequence;
    }

    private long tilNextMillis(long lastTimestamp) {
        long timestamp = timeGen();
        while (timestamp <= lastTimestamp) {
            timestamp = timeGen();
        }
        return timestamp;
    }

    private long timeGen() {
        return System.currentTimeMillis();
    }


    public static void main(String[] args) {
        IdWorker worker = new IdWorker(1, 1, 1);
        for (int i = 0; i < 30; i++) {
            System.out.println(worker.nextId());
        }
    }

}

5.消息隊列中,如何保證消息的順序性?

RabbitMQ:拆分多個 queue,每個 queue 一個 consumer,就是多一些 queue 而已,確實是麻煩點;或者就一個 queue 但是對應一個 consumer,然后這個 consumer 內(nèi)部用內(nèi)存隊列做排隊,然后分發(fā)給底層不同的 worker 來處理

Kafka:
1)一個 topic,一個 partition,一個 consumer,內(nèi)部單線程消費,單線程吞吐量太低,一般不會用這個
2)寫 N 個內(nèi)存 queue,具有相同 key 的數(shù)據(jù)都到同一個內(nèi)存 queue;然后對于 N 個線程,每個線程分別消費一個內(nèi)存 queue 即可,這樣就能保證順序性

6.單例模式有幾種寫法?

適用場景

  • 需要頻繁實例化然后銷毀的對象
  • 創(chuàng)建對象耗時過多或耗資源過多,但又經(jīng)常用到的對象
  • 有狀態(tài)的工具類對象
  • 頻繁訪問數(shù)據(jù)庫或文件的對象

懶漢模式

1.普通懶漢

// 優(yōu)點:啟動速度快,懶加載,節(jié)約資源
// 缺點:寫起來麻煩,線程不安全(不適用于多線程)
public class Singleton {
  private static Singleton singleton = null;
  private Singleton() {
  }
  public static Singleton getInstance() {
    if (singleton == null) {
      singleton = new Singleton();
    }
    return singleton;
  }
}

2.普通懶漢synchronized

// 優(yōu)點:寫起來簡單,且絕對線程安全
// 缺點:并發(fā)性能極差,事實上完全退化到了串行
public class Singleton {
  private static Singleton singleton = null;
  private Singleton() {}
  public synchronized static Singleton getInstance() {
    if (singleton == null) {
      singleton = new Singleton();
    }
    return singleton;
  }
}

3.雙檢鎖1.0

// 似乎已經(jīng)達到了理想的效果:懶加載+線程安全。
// 但是DCL仍然是線程不安全的,由于指令重排序,你可能會得到“半個對象”,即”部分初始化“問題
public class Singleton {
  private static Singleton singleton = null;

  public int f1 = 1;   // 觸發(fā)部分初始化問題
  public int f2 = 2;
  private Singleton() {}
  public static Singleton getInstance() {
    // may get half object
    if (singleton == null) {
      synchronized (Singleton.class) {
        if (singleton == null) {
          singleton = new Singleton();
        }
      }
    }
    return singleton;
  }
}

4.雙檢鎖2.0

// 適用于性能敏感的場景。但后面我們將了解到,就算是線程安全的,還有一些辦法能破壞單例
public class Singleton {
  private static volatile Singleton singleton = null;

  public int f1 = 1;   // 觸發(fā)部分初始化問題
  public int f2 = 2;
  private Singleton() {}
  public static Singleton getInstance() {
    if (singleton == null) {
      synchronized (Singleton.class) {
        // must be a complete instance
        if (singleton == null) {
          singleton = new Singleton();
        }
      }
    }
    return singleton;
  }
}

餓漢模式

// 優(yōu)點:天生的線程安全(得益于類加載機制)
// 缺點:有可能造成資源浪費
public class Singleton {
  private static final Singleton singleton = new Singleton();
  private Singleton() {}
  public static Singleton getInstance() {
    return singleton;
  }
}

靜態(tài)內(nèi)部類(餓漢的變種)

public class Singleton {
  private Singleton() {}
  public static Singleton getInstance() {
    return SingletonHolder.singleton;
  }
  private static class SingletonHolder {
    private static final Singleton singleton = new Singleton();
    private SingletonHolder() {}
 }
}

枚舉模式

基礎(chǔ)枚舉

// ThreadSafe
public enum Singleton {
  SINGLETON;
}
上述模式的總結(jié)

7.Redis中是如何實現(xiàn)分布式鎖的?

分布式鎖常見的實現(xiàn)方式

  • 數(shù)據(jù)庫樂觀鎖
  • 基于ZooKeeper的分布式鎖
  • 基于Redis的分布式鎖

Redis實現(xiàn)分布式鎖的條件:
互斥性:在任意時刻,只有一個客戶端能持有鎖
不能死鎖:客戶端在持有鎖的期間崩潰而沒有主動解鎖,也能保證后續(xù)其他客戶端能加鎖
容錯性:只要大部分的Redis節(jié)點正常運行,客戶端就可以加鎖和解鎖

Redis實現(xiàn)代碼

// 獲取鎖(unique_value可以是UUID等)
SET key unique_value NX PX  30000

// 釋放鎖(lua腳本中,一定要比較value,防止誤解鎖)
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

redlock算法

Redisson實現(xiàn)(redlock算法)

Redisson是一個在Redis的基礎(chǔ)上實現(xiàn)的Java駐內(nèi)存數(shù)據(jù)網(wǎng)格(In-Memory Data Grid)。它不僅提供了一系列的分布式的Java常用對象,還實現(xiàn)了可重入鎖(Reentrant Lock)、公平鎖(Fair Lock)、聯(lián)鎖(MultiLock)、 紅鎖(RedLock)、 讀寫鎖(ReadWriteLock)等,還提供了許多分布式服務。

Redisson 分布式重入鎖用法

Redisson 支持單點模式、主從模式、哨兵模式、集群模式,這里以單點模式為例:

// 1.構(gòu)造redisson實現(xiàn)分布式鎖必要的Config
Config config = new Config();
config.useSingleServer().setAddress("redis://127.0.0.1:5379").setPassword("123456").setDatabase(0);
// 2.構(gòu)造RedissonClient
RedissonClient redissonClient = Redisson.create(config);
// 3.獲取鎖對象實例(無法保證是按線程的順序獲取到)
RLock rLock = redissonClient.getLock(lockKey);
try {
    /**
     * 4.嘗試獲取鎖
     * waitTimeout 嘗試獲取鎖的最大等待時間,超過這個值,則認為獲取鎖失敗
     * leaseTime   鎖的持有時間,超過這個時間鎖會自動失效(值應設(shè)置為大于業(yè)務處理的時間,確保在鎖有效期內(nèi)業(yè)務能處理完)
     */
    boolean res = rLock.tryLock((long)waitTimeout, (long)leaseTime, TimeUnit.SECONDS);
    if (res) {
        //成功獲得鎖,在這里處理業(yè)務
    }
} catch (Exception e) {
    throw new RuntimeException("aquire lock fail");
}finally{
    //無論如何, 最后都要解鎖
    rLock.unlock();
}

RedissonLock是可重入的,并且考慮了失敗重試,可以設(shè)置鎖的最大等待時間, 在實現(xiàn)上也做了一些優(yōu)化,減少了無效的鎖申請,提升了資源的利用率。
需要特別注意的是,RedissonLock 同樣沒有解決 節(jié)點掛掉的時候,存在丟失鎖的風險的問題。而現(xiàn)實情況是有一些場景無法容忍的,所以 Redisson 提供了實現(xiàn)了redlock算法的 RedissonRedLock,RedissonRedLock 真正解決了單點失敗的問題,代價是需要額外的為 RedissonRedLock 搭建Redis環(huán)境。
所以,如果業(yè)務場景可以容忍這種小概率的錯誤,則推薦使用 RedissonLock, 如果無法容忍,則推薦使用 RedissonRedLock。

8.說說Object類下面有幾種方法呢?

1.Object()
2.registerNatives()
3.clone():clone()函數(shù)的用途是用來另存一個當前存在的對象
4.getClass():該方法返回的是此Object對象的類對象/運行時類對象Class
5.equals()
6.hashCode():該方法用來返回其所在對象的物理地址(哈希碼值)
7.toString()
線程通信
8.wait()
9.wait(long timeout)
10.wait(long timeout, int nanos)
11.notify() 12. notifyAll()
13.finalize():垃圾回收相關(guān)

9.說說hashCode() 和 equals() 之間的關(guān)系?

強制規(guī)則

1.不會創(chuàng)建類對應的散列表:我們不會在HashSet, Hashtable, HashMap等等這些本質(zhì)是散列表的數(shù)據(jù)結(jié)構(gòu)中,用到該類。例如,不會創(chuàng)建該類的HashSet集合

在這種情況下,該類的“hashCode() 和 equals() ”沒有半毛錢關(guān)系的!

2.會創(chuàng)建類對應的散列表:我們會在HashSet, Hashtable, HashMap等等這些本質(zhì)是散列表的數(shù)據(jù)結(jié)構(gòu)中,用到該類。例如,會創(chuàng)建該類的HashSet集合

在這種情況下,該類的“hashCode() 和 equals() ”是有關(guān)系的:
1)如果兩個對象相等,那么它們的hashCode()值一定相同。這里的相等是指,通過equals()比較兩個對象時返回true。
2)如果兩個對象hashCode()相等,它們并不一定相等

10.Redis 面試常見問答

  • 緩存雪崩(緩存同時大量失效)

解決方案:
對緩存做高可用,防止緩存宕機
斷路器限流

  • 緩存穿透
    緩存穿透
  • 緩存并發(fā)競爭(多個客戶端寫一個 key,如果順序錯了,數(shù)據(jù)就不對了)

解決方案:使用分布式鎖

  • 緩存和數(shù)據(jù)庫雙寫不一致

1.先更新數(shù)據(jù)庫,再更新緩存(并發(fā)寫的時候會有問題)
2.先刪緩存,再更新數(shù)據(jù)庫
3.先更新數(shù)據(jù)庫,再刪除緩存

11.分布式系統(tǒng)接口,如何避免表單的重復提交?

冪等性

  • 效果:系統(tǒng)對某接口的多次請求,都應該返回同樣的結(jié)果?。ňW(wǎng)絡訪問失敗的場景除外)
  • 目的:避免因為各種原因,重復請求導致的業(yè)務重復處理

冪等性的實現(xiàn)方式(針對新增、修改)

實現(xiàn)方法:客戶端做某一請求的時候帶上識別參數(shù)標識AddId,服務端對此標識進行識別,重復請求則重復返回第一次的結(jié)果即可;
這個參數(shù)標識什么時候更新呢?只有在保存成功并且清空表單之后,才變更這個參數(shù)標識,從而實現(xiàn)新數(shù)據(jù)的表單提交。

12.說說項目中單點登錄的實現(xiàn)原理?

  • 共享Session(應用體系簡單,子系統(tǒng)很少的情況)
  • 基于OpenId的單點登錄(用于C/S與B/S相結(jié)合的系統(tǒng))
    基于OpenId的單點登錄
  • 基于Cookie的OpenId存儲方案
    基于Cookie的OpenId存儲方案
  • B/S多域名環(huán)境下的單點登錄處理
    多域名環(huán)境下的單點登錄

13.說說 Redis 的過期策略

Redis采用的是 定期刪除 + 懶惰刪除策略

定期刪除策略
Redis 會將每個設(shè)置了過期時間的 key 放入到一個獨立的字典中,默認每 100ms 進行一次過期掃描:
1.隨機抽取 20 個 key
2.刪除這 20 個key中過期的key
3.如果過期的 key 比例超過 1/4,就重復步驟 1,繼續(xù)刪除

懶惰刪除策略
Redis 為什么要懶惰刪除(lazy free——redis4.0):當要刪除的對象太大會造成單線程卡頓。
unlink 指令:它能對刪除操作進行懶處理,丟給后臺線程來異步回收內(nèi)存
flushdb/flushall async:后臺慢慢清空數(shù)據(jù)庫

內(nèi)存淘汰機制(配置 maxmemory-policy):

  • noeviction:當內(nèi)存超出 maxmemory,寫入請求會報錯,但是刪除和讀請求可以繼續(xù)
  • allkeys-lru:當內(nèi)存超出 maxmemory,在所有的 key 中,移除最少使用的key。只把 Redis 既當緩存是使用這種策略。(推薦)
  • allkeys-random:當內(nèi)存超出 maxmemory,在所有的 key 中,隨機移除某個 key
  • volatile-lru:當內(nèi)存超出 maxmemory,在設(shè)置了過期時間 key 的字典中,移除最少使用的 key。把 Redis 既當緩存,又做持久化的時候使用這種策略
  • volatile-random:當內(nèi)存超出 maxmemory,在設(shè)置了過期時間 key 的字典中,隨機移除某個key
  • volatile-ttl:當內(nèi)存超出 maxmemory,在設(shè)置了過期時間 key 的字典中,優(yōu)先移除 ttl 小的

LRU 算法(Least Recently Used)最近最久未被使用:淘汰最長時間沒有被使用的
LFU算法(Least Frequently Used)最不經(jīng)常使用:淘汰一段時間內(nèi),使用次數(shù)最少的頁面

14.進程與線程的區(qū)別

進程與線程的區(qū)別

15.常見的多線程面試題

創(chuàng)建線程有幾種不同的方式:

  • 繼承Thread類
  • 實現(xiàn)Runnable接口
  • 應用程序可以使用Executor框架來創(chuàng)建線程池

線程的狀態(tài):

新建
就緒(可運行)
運行
阻塞:等待阻塞、同步阻塞、其他阻塞
死亡

同步方法與同步代碼塊的區(qū)別:

  • 同步方法默認使用this或者當前類的class對象作為鎖
  • 同步代碼塊可以選擇以什么來加鎖,比同步方法更細顆粒度,可以選擇只同步會發(fā)生問題的部分而不是整個方法

多線程產(chǎn)生死鎖的四個必要條件:

  • 互斥條件:一個資源每次只能被一個進程使用。
  • 保持和請求條件:一個進程因請求資源而阻塞時,對已獲得資源保持不放。
  • 不可剝奪性:進程已獲得資源,在未使用完成前,不能被剝奪。
  • 循環(huán)等待條件(閉環(huán)):若干進程之間形成一種頭尾相接的循環(huán)等待資源關(guān)系。

16.HashMap詳述

// 線程不安全,允許null鍵和null值,其余 HashMap 與 Hashtable 大致相同
Map map = Collections.synchronizedMap(new HashMap());

HashMap的底層主要是基于 數(shù)組和鏈表 來實現(xiàn)的

HashMap底層結(jié)構(gòu)

HashMap其實就是一個Entry數(shù)組,Entry對象中包含了鍵和值,其中next也是一個Entry對象,它就是用來處理hash沖突的,形成一個鏈表

HashMap的性能參數(shù)
1.HashMap():構(gòu)建一個初始容量為 16,負載因子為 0.75 的 HashMap。
2.HashMap(int initialCapacity):構(gòu)建一個初始容量為 initialCapacity,負載因子為 0.75 的 HashMap。
3.HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的負載因子創(chuàng)建一個 HashMap。

17.ArrayList與LinkedList

ArrayList的底層是基于 數(shù)組,LinkedList的底層是基于 雙向鏈表

容量參數(shù)是ArrayList和Vector等基于數(shù)組的List的特有性能參數(shù)

總結(jié)
1.對ArrayList和LinkedList而言,在列表末尾增加一個元素所花的開銷都是固定的。對ArrayList而言,主要是在內(nèi)部數(shù)組中增加一項,指向所添加的元素,偶爾可能會導致對數(shù)組重新進行分配;而對LinkedList而言,這個開銷是統(tǒng)一的,分配一個內(nèi)部Entry對象
2.在ArrayList的中間插入或刪除一個元素意味著這個列表中剩余的元素都會被移動;而在LinkedList的中間插入或刪除一個元素的開銷是固定的
3.LinkedList不支持高效的隨機元素訪問
4.ArrayList的空間浪費主要體現(xiàn)在在list列表的結(jié)尾預留一定的容量空間,而LinkedList的空間花費則體現(xiàn)在它的每一個元素都需要消耗相當?shù)目臻g

18.Java序列化與反序列化

  • Java序列化:將Java對象轉(zhuǎn)換為字節(jié)序列的過程;核心作用是對象狀態(tài)的保存與重建
  • Java反序列化:將字節(jié)序列恢復為Java對象的過程;根據(jù)字節(jié)流中所保存的對象狀態(tài)及描述信息,通過反序列化重建對象

為什么需要序列化與反序列化?

  • 對象序列化可以實現(xiàn)分布式對象
  • java對象序列化不僅保留一個對象的數(shù)據(jù),而且遞歸保存對象引用的每個對象的數(shù)據(jù)
  • 序列化可以將內(nèi)存中的類寫入文件或數(shù)據(jù)庫中
  • 對象、文件、數(shù)據(jù),有許多不同的格式,很難統(tǒng)一傳輸和保存

19.Java線程(JVM線程)與操作系統(tǒng)線程

Java 線程狀態(tài)的改變通常只與自身顯式引入的機制有關(guān)
操作系統(tǒng)的線程狀態(tài)是圍繞著 cpu 這一核心去述說的


二者區(qū)別

20.HashMap不安全的體現(xiàn)

1.在jdk1.7中,在多線程環(huán)境下,擴容時會造成環(huán)形鏈或數(shù)據(jù)丟失(采用的是頭插法
2.在jdk1.8中,在多線程環(huán)境下,會發(fā)生數(shù)據(jù)覆蓋的情況(采用的是尾插法
1.7中采用數(shù)組+鏈表,1.8采用的是數(shù)組+鏈表/紅黑樹(在1.7中鏈表長度超過一定長度后就改成紅黑樹存儲)
1.7擴容時需要重新計算哈希值和索引位置,1.8并不重新計算哈希值,巧妙地采用和擴容后容量進行&操作來計算新的索引位置

21.消息隊列介紹

1.消息隊列特性

  • 業(yè)務無關(guān)
  • FIFO
  • 容災
  • 性能

2.消息隊列優(yōu)點

  • 提高系統(tǒng)響應速度
  • 提高系統(tǒng)穩(wěn)定性
  • 異步
  • 解耦
  • 削峰

3.分布式產(chǎn)生的問題

  • 并發(fā)問題
  • 容錯
  • 可橫向擴展
  • 簡單的、統(tǒng)一的操作機制

4.常見消息隊列對比

22.Linux環(huán)境下的Network IO

IO時涉及兩個系統(tǒng)對象:調(diào)用Io的進程(線程)、系統(tǒng)內(nèi)核。
涉及兩個階段:數(shù)據(jù)準備階段、將數(shù)據(jù)從內(nèi)核拷貝到進程中

1.blocking IO —— 兩個階段都阻塞
2.nonblocking IO —— 2階段阻塞
3.IO multiplexing —— 兩個階段都阻塞
4.signal driven IO
5.asynchronous IO

A,B,C,D四個人釣魚
A用的是最老式的魚竿,所以呢,得一直守著,等到魚上鉤了再拉桿
B的魚竿有個功能,能夠顯示是否有魚上鉤,所以呢,B就和旁邊的MM聊天,隔會再看看有沒有魚上鉤,有的話就迅速拉桿
C用的魚竿和B差不多,但他想了一個好辦法,就是同時放好幾根魚竿,然后守在旁邊,一旦有顯示說魚上鉤了,它就將對應的魚竿拉起來
D是個有錢人,干脆雇了一個人幫他釣魚,一旦那個人把魚釣上來了,就給D發(fā)個短信

23.MySQL

MySQL查詢字段區(qū)不區(qū)分大小寫?—— 不區(qū)分

如何區(qū)分?
方案一:MySQL默認的字符檢索策略:utf8_general_ci,不區(qū)分大小寫;utf8_bin,區(qū)分大小寫。

-- 創(chuàng)建表
CREATE TABLE testt(
id INT PRIMARY KEY,
name VARCHAR(32) NOT NULL
) ENGINE = INNODB COLLATE =utf8_bin;

-- 修改表結(jié)構(gòu)的Collation屬性
ALTER TABLE TABLENAME MODIFY COLUMN COLUMNNAME VARCHAR(50) BINARY CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL;

方案二:直接修改sql語句,在要查詢的字段前面加上binary關(guān)鍵字

-- 在每一個條件前加上binary關(guān)鍵字
select * from user where binary username = 'admin' and binary password = 'admin';

-- 將參數(shù)以binary('')包圍
select * from user where username like binary('admin') and password like binary('admin');

MySQL innodb有多少種日志:

  • 錯誤日志
  • 查詢?nèi)罩?/li>
  • 慢查詢?nèi)罩?/li>
  • 二進制日志:記錄對數(shù)據(jù)庫執(zhí)行更改的所有操作
  • 中繼日志:中繼日志也是二進制日志,用來給slave 庫恢復
  • 事務日志

MySQL innodb的事務與日志的實現(xiàn)方式

事務日志是通過redo和innodb的存儲引擎日志緩沖(Innodb log buffer)來實現(xiàn)的
1.開啟事務時,會記錄該事務的lsn(log sequence number)號
2.事務執(zhí)行時,會往InnoDB存儲引擎的日志的日志緩存里面插入事務日志
3.事務提交時,必須將存儲引擎的日志緩沖寫入磁盤

MySQL binlog的幾種日志錄入格式以及區(qū)別

1.Statement:每一條會修改數(shù)據(jù)的sql都會記錄在binlog中
2.Row:不記錄sql語句上下文相關(guān)信息,僅保存哪條記錄被修改
3.Mixedlevel: 以上兩種level的混合使用

24.JVM

判斷對象是否存活

  • 引用計數(shù)法

給對象中添加一個引用計數(shù)器,每當有一個地方引用它時,計數(shù)器加1;當引用失效時,計數(shù)器值減1;任何時刻計數(shù)器為0的對象就是不能再被引用的

  • 可達性分析法

通過一系列的稱為“GC Roots”的對象作為起始點,從這些節(jié)點開始向下搜索,搜索所走過的路徑稱為引用鏈(Reference Chain),當一個對象到GC Root沒有任何引用鏈相連時,則證明此對象是不可用的

25.Dubbo

1.Dubbo是什么?

Dubbo簡單來說,就是個服務框架,遠程服務調(diào)用的分布式框架。
其核心包含:

1.遠程通訊:提供對多種基于長連接的NIO框架抽象封裝,包括多種線程模型,序列化,以及“請求-響應”模式的信息交換方式。
2.集群容錯:提供基于接口方法的透明遠程過程調(diào)用,包括多協(xié)議支持,以及軟負載均衡,失敗容錯,地址路由,動態(tài)配置等集群支持。
3.自動發(fā)現(xiàn):基于注冊中心目錄服務,使服務消費方能動態(tài)的查找服務提供方,使地址透明,使服務提供方可以平滑增加或減少機器。

2.Dubbo能做什么?

  • 透明化的遠程方法調(diào)用
  • 負載均衡及容錯機制
  • 服務自動注冊與發(fā)現(xiàn)

3.Dubbo內(nèi)置了如下容器:Spring Container、Jetty Container、Log4j Container

4.Dubbo的核心配置

核心配置

5.Dubbo有哪幾種集群容錯方案?

集群容錯方案

6.Dubbo有哪幾種負載均衡策略?

負載均衡策略

7.Dubbo默認采用的是 Netty 通信框架

8.Dubbo VS SpringCloud

沒有好壞,只有合不合適。
SpringCloud優(yōu)勢

約定優(yōu)于配置
開箱即用、快速啟動
適用于各種環(huán)境
輕量級的組件
組件支持豐富,功能齊全

二者比較

  • 傳輸協(xié)議

dubbo由于是二進制的傳輸,占用帶寬會更少
springCloud是http協(xié)議傳輸,帶寬會比較多,同時使用http協(xié)議一般會使用JSON報文,消耗會更大

  • 開發(fā)難度

dubbo的開發(fā)難度較大,原因是dubbo的jar包依賴問題很多大型工程無法解決

  • 接口協(xié)議

springcloud的接口協(xié)議約定比較自由且松散,需要有強有力的行政措施來限制接口無序升級

  • 注冊中心

dubbo的注冊中心可以選擇zk,redis等多種,springcloud的注冊中心只能用eureka或者自研

26.ZooKeeper

27.Java集合框架

1.HashMap和HashTable的區(qū)別?

  • HashMap線程不安全,HashTable線程安全
  • HashMap 允許 null key 和 null value,而 HashTable 不允許

2.ArrayList 和 LinkedList 的區(qū)別是什么?

1.ArrayList是實現(xiàn)了基于動態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu),LinkedList基于鏈表的數(shù)據(jù)結(jié)構(gòu)
2.對于隨機訪問get和set,ArrayList覺得優(yōu)于LinkedList,因為LinkedList要移動指針
3.對于新增和刪除操作add和remove,LinedList比較占優(yōu)勢,因為ArrayList要移動數(shù)據(jù)。

3.ArrayList 和 Vector 的區(qū)別是什么?

  • ArrayList線程不安全,Vector線程安全
  • Vector默認增長為原來兩倍,而ArrayList的增長策略在文檔中沒有明確規(guī)定(從源代碼看到的是增長為原來的1.5倍)。ArrayList與Vector都可以設(shè)置初始的空間大小,Vector還可以設(shè)置增長的空間大小,而ArrayList沒有提供設(shè)置增長空間的方法

4.Array 和 ArrayList 有何區(qū)別?

  • 支持的對象類型
  • 數(shù)組長度可變性

5.HashSet的實現(xiàn)原理

HashSet的底層是基于HashMap(的Key)

28.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容