Java面試

自己經(jīng)驗有限,篇幅也有限笋婿,這里只是記錄一些比較容易混淆或有難度和一些易忘的技術(shù)知識點,里面有一些也是面試阿里經(jīng)常會被問到的問題顿颅,但是不保證答案全部正確缸濒,有錯誤的地方望大家指正

JVM相關(guān)
  1. JVM內(nèi)存是如何分配的?
    堆:占用內(nèi)存最大的區(qū)塊粱腻,主要存放new出來的對象庇配,線程共享,主要設(shè)置大小參數(shù)名是-Xms-Xmx
    棧(以前概括都叫棧绍些,具體說其實是非堆內(nèi)存):一般是線程私有
    • 寄存器:即是程序計數(shù)器讨永,存放當(dāng)前正要執(zhí)行的下一條指令地址
    • 本地方法棧(不同jvm的實現(xiàn)可能不同,比如平常所用的sun的實現(xiàn)中方法棧和虛擬機棧是一個):線程私有遇革,存儲比如ObjecthashCode();虛擬機棧中用于存儲局部變量表揭糕、動態(tài)鏈接萝快、操作數(shù)、方法出口等信息
    • 方法區(qū):所有線程共享著角,用于存放加載類信息揪漩,比如常量、靜態(tài)常量吏口,需要注意的是1.8以后將靜態(tài)常量放在了堆里
  2. GC
    垃圾回收的算法基礎(chǔ)是標(biāo)記和復(fù)制算法奄容,標(biāo)記一般是樹形結(jié)構(gòu),采用根搜索算法产徊,標(biāo)記可以回收的對象昂勒,一般是對象搜索不到根節(jié)點即可以回收,有2次機會舟铜,在第一次被標(biāo)記回收后可以重新被掛靠根節(jié)點(也即是被重新引用戈盈,涉及的的方法是finalize()),如果沒有下一次判定對象死亡;基礎(chǔ)的復(fù)制算法我舉個例子說明塘娶,將一塊內(nèi)存分成2份归斤,運行時只使用其中一塊,GC時將活的對象復(fù)制到另一塊內(nèi)存刁岸,然后清除前一塊所有內(nèi)存空間脏里,類似于給U盤格式化,這樣比一個一個釋放內(nèi)存要快得多虹曙,相信大家做格式化的時候體會過迫横,現(xiàn)在jvm gc使用的的復(fù)制算法是結(jié)果改良的,不是平均的分成2份根吁,默認(rèn)比例好像是1/8员淫,即平常見到的新生代、老年代击敌、持久代等介返。具體的算法大家看資料文檔吧,這種東西不是說說就能清楚的沃斤。
  3. 內(nèi)存泄漏和內(nèi)存溢出
    內(nèi)存泄漏圣蝎,當(dāng)一個對象不會被使用但占著內(nèi)存即會導(dǎo)致內(nèi)存泄漏,比如
Object o1 = new Object();
Object o2 = new Object();
o1 = o2; // 這時2個對象的引用地址是一樣的衡瓶,但是o1申請的內(nèi)存就沒有被使用

內(nèi)存泄漏積累多了徘公,內(nèi)存不斷被無用對象占用,新的對象申請不到足夠的空間就會產(chǎn)生內(nèi)存溢出哮针。

架構(gòu)相關(guān)
  1. springMVC的處理流程
    • 一個http請求
    • 經(jīng)過一些過濾器或攔截器到達(dá)DispatcherServlet將請求轉(zhuǎn)發(fā)給對應(yīng)的@Controller@RequestMapping
    • 參數(shù)封裝关面,請求頭判定等等
    • 調(diào)用業(yè)務(wù)方法獲得Model
    • 返回ModelAndView查找ViewResolver返回對應(yīng)的View,可能是需要渲染的jsp十厢,可能是json等太,可能是文件流等等。
  2. 說說redis里的bitmap
    bitmap一般用于計數(shù)或top計算蛮放,比如統(tǒng)計網(wǎng)站當(dāng)前在線人數(shù)缩抡,假設(shè)用戶id是遞增的整數(shù),當(dāng)用戶上線時將用戶id存進(jìn)bitmap包颁,比如id是4瞻想,則bitmap就是00001000,id為8的用戶上線娩嚼,bitmap的值變成10001000蘑险,對bitmap做count計算得出是2,而1KB=1024B=8196b且位運算是計算機最快的待锈,這樣做的好處是速度快漠其,還能知道是誰上下線的。同理如果要按月統(tǒng)計某個操作,只需要用每天做key值和屎,然后做并集得到新的bitset計數(shù)就可拴驮。
  3. Ioc和AOP分別使用了哪些設(shè)計模式?
    工廠模式和代理模式柴信,細(xì)一點還有單例套啤、模版、原型随常,這里說一下代理模式潜沦,常用的一般是動態(tài)代理模式,jdk中提供了InvocationHandler接口可以方便實現(xiàn)動態(tài)代理:
public interface IService {
    void service();
}
public class MyService implements IService {
    @Override
    public void service() {
        System.out.println("service...");
    }
}
public class MyProxy implements InvocationHandler {

    private IService service;

    public MyProxy(IService service) {
        this.service = service;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before service");
        Object invoke = method.invoke(service, args);
        System.out.println("after service ");
        return invoke;
    }
}
public class Main {

    public static void main(String[] args) {
        MyService myService = new MyService();
        MyProxy myProxy = new MyProxy(myService);

        // 動態(tài)生成代理對象
        IService instance = (IService) Proxy.newProxyInstance(MyService.class.getClassLoader(), MyService.class.getInterfaces(), myProxy);
        instance.service();// 利用代理對象調(diào)用service方法

    }
}
  1. MySQL數(shù)據(jù)庫的鎖
    MySQL常見的有2種引擎绪氛,MyISAM采用的是表級鎖唆鸡,給整表加鎖,速度快枣察,不會死鎖争占,但是但是鎖競爭激烈,效率低序目,lock table_name read or write臂痕;InnoDB采用的是行級鎖,只鎖行猿涨,效率高握童,但是會死鎖,另外MySQL的行級鎖采用鎖索引實現(xiàn)叛赚,所以只有通過索引檢索數(shù)據(jù)才能使用行級鎖澡绩,否則會使用表級鎖
  2. 冪等性
    冪等性是數(shù)學(xué)里的一個概念,我并不是很精通俺附,簡單來說就是N次變換和1次變換的結(jié)果應(yīng)該保持一致英古,計算機里他是一種Http協(xié)議中提到的性質(zhì),注意冪等性本身并不是協(xié)議昙读,沒有辦法通過規(guī)范一致化操作,多用于分布式系統(tǒng)膨桥,用于保證分布式系統(tǒng)中數(shù)據(jù)的一致性操作蛮浑,類似于分布式事務(wù),但是分布式事務(wù)中間件一般較重且效率有很大虧損只嚣,對于要求高性能的分布式場景中沮稚,冪等設(shè)計可能是唯一的選擇。實現(xiàn)場景簡單舉例來說册舞,假設(shè)微信支付后端是分布式的(肯定是的)蕴掏,我發(fā)起了一個支付,如果服務(wù)器端已經(jīng)處理完成但是我的手機沒網(wǎng)了,我會誤以為支付失敗盛杰,重新支付挽荡,冪等設(shè)計在此類場景中一般會這樣設(shè)計,在發(fā)起支付操作前會先向服務(wù)端申請一個ticket即供,這個ticket會關(guān)聯(lián)此次支付的操作定拟,這個ticket只能增長一次,這樣在我重新發(fā)起支付的時候逗嫡,服務(wù)器就可以正確返回支付成功且保證我只支付了一次青自。
java基礎(chǔ)
  1. ConcurrentHashMap
    ConcurrentHashMap是線程安全的集合類,功能類似于Hashtable驱证,但是Hashtable雖然也是線程安全的延窜,但是Hashtable只有一把同步鎖,并發(fā)性能不高抹锄,ConcurrentHashMap則是利用了鎖分段技術(shù)逆瑞,簡單來說就是,多個類似的HashTable祈远,單獨維護(hù)自己的鎖呆万,這樣多線程操作的時候減少了競爭鎖的等待,在多線程應(yīng)用里是最常用的線程安全集合類车份。查看源碼可以知道谋减,ConcurrentHashMap內(nèi)部主要成員是SegmentNodeSegment充當(dāng)鎖扫沼,繼承ReentrantLock出爹,Node相當(dāng)于一個Map.Entry,其他大部分成員變量都是volatile的缎除,因為happen before的存在严就,volatile字段的寫入操作先于讀操作,這也是用volatile替換鎖的經(jīng)典應(yīng)用場景器罐。
  2. ThreadLocal
    ThreadLocal梢为,利用線程局部變量來實現(xiàn)線程安全的方式,使用時需要小心應(yīng)對轰坊,因為線程局部變量一旦使用完沒有被釋放就會導(dǎo)致內(nèi)存泄漏铸董。
  3. 有沒有可能兩個不相等的對象有相同的 hashcode?
    hashcode并不是唯一的肴沫,只是重復(fù)概率非常小而已粟害,但是相等的對象hashcode一定是一樣的。
  4. 編寫多線程程序的時候你需要注意哪些颤芬?
    • 盡量使用volatile替換同步鎖
    • 給線程取個name
    • 使用并發(fā)集合而不是讓集合同步
    • 合理創(chuàng)建線程數(shù)悲幅,一般而言是CPU的核心數(shù)*2+1
    • 給需要同步的代碼同步套鹅,而不是圖簡單給整個方法或類加同步
  5. DateFormat的所有實現(xiàn)都不是線程安全的,如果一定要在多線程中使用可以利用ThreadLocal
  6. 對稱加密和非對稱加密
    對稱加密:需要同一把密鑰來解密汰具,速度快卓鹿,一般用于需要加密大量數(shù)據(jù)時使用,常見用于對稱加密的算法有DES郁副、3DES减牺、RC系、AES等存谎。
    非對稱加密:需要2把密鑰才能解密拔疚,分作公鑰和私鑰,如果用公開密鑰對數(shù)據(jù)進(jìn)行加密既荚,只有用對應(yīng)的私有密鑰才能解密稚失;如果用私有密鑰對數(shù)據(jù)進(jìn)行加密,那么只有用對應(yīng)的公開密鑰才能解密恰聘;常見的https協(xié)議里的證書機制就是采用的這種方式句各,常用于非對稱加密算法的有RSA、ECC晴叨、Elgamal凿宾。
  7. NIO和普通IO的區(qū)別?
    最主要的區(qū)別在于非阻塞與阻塞兼蕊,NIO是先寫入緩沖區(qū)在再讀出操作初厚,是非阻塞的,而普通IO操作主要是針對流的孙技,一個線程讀寫流時是不能做其他操作的产禾,就好比如下載文件有些軟件可以斷點續(xù)傳,有些不可以牵啦。
問題
  1. 統(tǒng)計log文件里所有出現(xiàn)的單詞以及出現(xiàn)的次數(shù)并且按照次數(shù)排序找出最頻繁的單詞亚情?
    步驟其實很簡單:
    • 讀取文件
    • 排序
      這里直接提供代碼,分別是jdk 1.7和jdk 1.8的2個版本
      1.7:
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;

public class Test {

    /**
     * 根據(jù)map的value進(jìn)行排序
     * @param map
     * @param <K>
     * @param <V>
     * @return
     */
    public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
        List<Map.Entry<K, V>> list = new LinkedList<Map.Entry<K, V>>(map.entrySet());
        // 先將map轉(zhuǎn)換成List便于使用sort排序
        Collections.sort(list, new Comparator<Map.Entry<K, V>>() {
            public int compare(Map.Entry<K, V> o1, Map.Entry<K, V> o2) {
                return (o2.getValue()).compareTo(o1.getValue());
            }
        });

        Map<K, V> result = new LinkedHashMap<K, V>();
        for (Map.Entry<K, V> entry : list) {
            result.put(entry.getKey(), entry.getValue());
        }
        return result;
    }

    public static void main(String[] args) {
        FileInputStream fileInputStream = null;
        String fileName = "ngen.log";
        TreeMap<String, Integer> map = new TreeMap<>();
//      SortedMap<String, Integer> map = new
        try {
            fileInputStream = new FileInputStream(fileName);
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
            String line = null;
            // 按行讀取文件分解單詞
            while ((line = bufferedReader.readLine()) != null) {
                String[] ss = line.split(" ");
                for (int i = 0; i < ss.length; i++) {
                    String s = ss[i];
                    if (s != null && s.matches("\\w+")) {
                        // 如果map中有此單詞就將次數(shù)+1
                        // 否則此單詞第一次出現(xiàn)
                        if (map.containsKey(s)) {
                            map.put(s, map.get(s) + 1);
                        } else {
                            map.put(s, 1);
                        }
                    }
                }
            }

            Map<String, Integer> sortedMap = sortByValue(map);
            for (Map.Entry<String, Integer> entry : sortedMap.entrySet()) {
                String key = entry.getKey();
                Integer value = entry.getValue();
                System.out.println(key + ":" + value);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

1.8:

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Main {

    /**
     * 根據(jù)map的value進(jìn)行排序
     * @param map
     * @param <K>
     * @param <V>
     * @return
     */
    public static <K, V extends Comparable<? super V>> Map<K, V> sortByValue(Map<K, V> map) {
        return map.entrySet()
                .stream()
                .sorted(Map.Entry.comparingByValue(Collections.reverseOrder())) // 逆序
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (e1, e2) -> e1,
                        LinkedHashMap::new
                ));
    }

    public static void main(String[] args) {
        FileInputStream fileInputStream = null;
        String fileName = "D:/app.log";
        TreeMap<String, Integer> map = new TreeMap<>();
        try {
            fileInputStream = new FileInputStream(fileName);
            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
            bufferedReader.lines()
                    .flatMap(line -> Stream.of(line.split(" ")))
                    .filter(word -> word.matches("\\w+"))
                    .forEach(s -> { // Stream語法不太熟悉哈雏,不知道有木有更方便的方法楞件?
                        if (map.containsKey(s)) {
                            map.put(s, map.get(s) + 1);
                        } else {
                            map.put(s, 1);
                        }
                    })
            ;

            Map<String, Integer> sortedMap = sortByValue(map);
            sortedMap.forEach((k, v) -> System.out.println(k + "," + v));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  1. 從日志文件中讀取的是字節(jié)還是字符?
    乍一看這問題不要太簡單裳瘪,但是我挺佩服問這問題的面試官的履因,這問題向后衍生無論是廣度還是深度都無可挑剔。如何回答這問題要從兩方面出發(fā)盹愚,首先一點,所有操作系統(tǒng)存放在磁盤的任何數(shù)據(jù)肯定都是字節(jié)的站故,那么讀出來肯定也是字節(jié)的皆怕;第二點毅舆,通常日志文件操作寫代碼的時候讀出來的肯定是字符,不然你如何操作呢愈腾?只是java提供的方便的I/O操作方法而已憋活,其實里面是將字節(jié)轉(zhuǎn)成了字符而已。向后延伸就會問比如java的IO操作注意事項虱黄、編碼等等問題悦即,還有操作系統(tǒng)底層如何處理等等的問題,這道題很簡單橱乱,但是切記不要盲目作答辜梳。

  2. 秒殺系統(tǒng)設(shè)計
    自己并沒有實際秒殺系統(tǒng)設(shè)計經(jīng)驗,這里從朋友以及網(wǎng)絡(luò)總結(jié)幾點:

    • 高并發(fā)泳叠,總的來說肯定是Nginx做負(fù)載均衡作瞄,后臺做服務(wù)集群
    • 秒殺計時前的靜態(tài)頁面使用cdn
    • 秒殺計時不需要做高并發(fā)處理,因為new一個Date返回給前臺危纫,任何語言支持個幾億并發(fā)都是沒問題的
    • 先緩存再查庫宗挥,保證低延遲
    • 秒殺系統(tǒng)單獨設(shè)計,不要與已有業(yè)務(wù)混淆种蝶,不然一旦阻塞會全盤崩潰
    • 庫存要保持事務(wù)唯一契耿,數(shù)據(jù)庫最好另建表,不要與日常業(yè)務(wù)沖突
    • 預(yù)估請求處理最大量螃征,當(dāng)請求過多時攔截并直接返回等待
    • 預(yù)防惡意刷單搪桂,比如同一個IP只能有一個請求
    • 動態(tài)加載js來激活秒殺按鈕,避免秒殺沒有開始時被惡意操作
    • 并發(fā)請求隊列

本文已在版權(quán)印備案会傲,如需轉(zhuǎn)載請訪問版權(quán)印锅棕。40142943

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市淌山,隨后出現(xiàn)的幾起案子裸燎,更是在濱河造成了極大的恐慌,老刑警劉巖泼疑,帶你破解...
    沈念sama閱讀 206,602評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件德绿,死亡現(xiàn)場離奇詭異,居然都是意外死亡退渗,警方通過查閱死者的電腦和手機移稳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,442評論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來会油,“玉大人个粱,你說我怎么就攤上這事》妫” “怎么了都许?”我有些...
    開封第一講書人閱讀 152,878評論 0 344
  • 文/不壞的土叔 我叫張陵稻薇,是天一觀的道長。 經(jīng)常有香客問我胶征,道長塞椎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,306評論 1 279
  • 正文 為了忘掉前任睛低,我火速辦了婚禮案狠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钱雷。我一直安慰自己骂铁,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,330評論 5 373
  • 文/花漫 我一把揭開白布急波。 她就那樣靜靜地躺著从铲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪澄暮。 梳的紋絲不亂的頭發(fā)上名段,一...
    開封第一講書人閱讀 49,071評論 1 285
  • 那天,我揣著相機與錄音泣懊,去河邊找鬼伸辟。 笑死,一個胖子當(dāng)著我的面吹牛馍刮,可吹牛的內(nèi)容都是我干的信夫。 我是一名探鬼主播,決...
    沈念sama閱讀 38,382評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼卡啰,長吁一口氣:“原來是場噩夢啊……” “哼静稻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起匈辱,我...
    開封第一講書人閱讀 37,006評論 0 259
  • 序言:老撾萬榮一對情侶失蹤振湾,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后亡脸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體押搪,經(jīng)...
    沈念sama閱讀 43,512評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,965評論 2 325
  • 正文 我和宋清朗相戀三年浅碾,在試婚紗的時候發(fā)現(xiàn)自己被綠了大州。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,094評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡垂谢,死狀恐怖厦画,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情滥朱,我是刑警寧澤根暑,帶...
    沈念sama閱讀 33,732評論 4 323
  • 正文 年R本政府宣布娃豹,位于F島的核電站,受9級特大地震影響购裙,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜鹃栽,卻給世界環(huán)境...
    茶點故事閱讀 39,283評論 3 307
  • 文/蒙蒙 一躏率、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧民鼓,春花似錦薇芝、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,286評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至饮亏,卻和暖如春耍贾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背路幸。 一陣腳步聲響...
    開封第一講書人閱讀 31,512評論 1 262
  • 我被黑心中介騙來泰國打工荐开, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人简肴。 一個月前我還...
    沈念sama閱讀 45,536評論 2 354
  • 正文 我出身青樓晃听,卻偏偏與公主長得像,于是被迫代替她去往敵國和親砰识。 傳聞我的和親對象是個殘疾皇子能扒,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,828評論 2 345

推薦閱讀更多精彩內(nèi)容

  • 小編費力收集:給你想要的面試集合 1.C++或Java中的異常處理機制的簡單原理和應(yīng)用。 當(dāng)JAVA程序違反了JA...
    八爺君閱讀 4,573評論 1 114
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 3,790評論 0 11
  • (一)Java部分 1、列舉出JAVA中6個比較常用的包【天威誠信面試題】 【參考答案】 java.lang;ja...
    獨云閱讀 7,071評論 0 62
  • 轉(zhuǎn)自:http://blog.csdn.net/jackfrued/article/details/4492194...
    王帥199207閱讀 8,500評論 3 93
  • 相關(guān)概念 面向?qū)ο蟮娜齻€特征 封裝,繼承,多態(tài).這個應(yīng)該是人人皆知.有時候也會加上抽象. 多態(tài)的好處 允許不同類對...
    東經(jīng)315度閱讀 1,925評論 0 8