zoukankan      html  css  js  c++  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);
    }

    结论

      不知道这个面试题的正确答案是什么,这里提供了两种方法解决。很可惜反射没有搞定这个题目,如果哪位同学有更好的方法请留言。

  • 相关阅读:
    深圳移动 神州行(大众卡/轻松卡/幸福卡)套餐资费(含香港日套餐)信息及使用方法
    PHP设置时区,记录日志文件的方法
    微信公众平台消息接口使用指南
    C#日期时间格式化
    使用CMD实现批量重命名[转]
    Python高效编程技巧
    实用WordPress后台MySQL操作命令
    ubuntu-wine
    Javascript 笔记与总结(2-8)对象2
    Swift5.3 语言指南(十) 枚举
  • 原文地址:https://www.cnblogs.com/kingszelda/p/7142690.html
Copyright © 2011-2022 走看看