1. 程式人生 > >如何修改int的列印內容——史上最難的JAVA面試題

如何修改int的列印內容——史上最難的JAVA面試題

今天看到了一個比較特別的面試題,考察的是如何改變int的System.out.print的結果。題目如下:
這裡寫圖片描述
下面的一句話“這是初級java實習生面試題”非常挑釁的激起了大家做題的慾望。

解題思路

解題的思路無外乎是3種方法:
1.作弊大法,因為一些面試題總是會出其不意,面對扯淡的問題就要用扯淡的方法。
2.反射,感覺反射是一種比較“高階”用法,因為很多中介軟體與框架都是通過反射實現的。
3.資料流。反射的思路是修改對應記憶體上的內容,當無法修改響應記憶體的時候就要找其他思路了。這裡對應的問題就是,System.out.println是如何列印的。

解題方案

作弊大法

  • System.exit
    這種方法通過退出jvm執行緒來終止後面的程式執行,看完之後讓人比較崩潰,因為業務中遇到 System.exit的場景比較少。
    private static void method(int a, int b) {
        System.out.println("a=100");
        System.out.println("b=200");
        System.exit(0);
    }
  • 異常
    上面的方法是執行緒優雅的自己退出JVM,通過異常就是強制使JVM中介當前執行緒了。
    private static
void method(int a, int b) { System.out.println("a=100"); System.out.println("b=200"); throw new RuntimeException(); }

反射

反射的方法我一直沒有成功,這裡提供幾個思路

  • 修改快取池
    因為Integer內部維護了一個IntegerCache內部類,其內部維護了一個Integer[]陣列來維護從-128到127的Integer物件。
private static void method(int a, int b) throws
Exception { Class<?> clzz = Integer.class.getDeclaredClasses()[0]; Field field = clzz.getDeclaredField("cache"); field.setAccessible(true); Integer[] cache = (Integer[]) field.get(clzz); cache[138]=100; cache[148]=200; }

這樣就修改了快取區的內容,但是依然沒有完成題目的要求,列印內容沒有改變,因為這樣改變了所有的Integer快取區物件,但是當前列印的是int。這種情況下,其他的場景倒是可以改變了。

Integer c =10;
System.out.println(c);
System.out.println(Integer.valueOf(10));
  • 修改物件本身
private static void method(int a, int b) throws Exception {
        Field value = Integer.class.getDeclaredField("value");
        value.setAccessible(true);
        value.set(10,100);
}

效果等於同修改快取池的方法

修改輸出流

瞭解一點System.out的同學應該很清楚這是通過PringStream流實現的。當然我不是很瞭解,我是通過反編譯看位元組碼發現的。
測試程式碼:

public class A {
    public static void main(String[] args) {
        int a = 9;
        System.out.println(a);
    }
}

檢視位元組碼:
這裡寫圖片描述
發現實際在輸出螢幕的時候呼叫的是PrintStream物件的println方法實現的,我們可以通過重寫這個方法列印任何內容。

  • 直接替換法
    private static void method(int a, int b) throws Exception {
        PrintStream printStream = new PrintStream(System.out) {
            @Override
            public void print(String s) {
                if (s.equals("a=10")) {
                    super.print("a=100");
                } else if (s.equals("b=20")) {
                    super.print("b=200");
                } else
                    super.print(s);
            }
        };
        System.setOut(printStream);
}

這種方法比較low,屬於手動替換的。下面的方法性質相同,不過看上去高階一點。

  • 間接替換法
    private static void method(int a, int b) throws Exception {
        PrintStream printStream = new PrintStream(System.out) {
            @Override
            public void print(String s) {
                s = s.replace(a + "", a * 10 + "").replace(b + "", b * 10 + "");
                super.print(s);
            }
        };
        System.setOut(printStream);
}

結論

不知道這個面試題的正確答案是什麼,這裡提供了兩種方法解決。很可惜反射沒有搞定這個題目,如果哪位同學有更好的方法請留言。