Java面死题精选


最近在看一本 2006 年的 Java 书.

Java面死题精选
Java解惑

这书中文译名叫《Java 解惑》.不过我觉得, 中文版要不是翻译成解惑, 估计,他根本卖不出去.这玩意可能能解一点惑,但剩下满脑子都是:

  • 原来**的还能这样?
  • 我对 Java ,知之甚少
来都来了,别走了.这里是K字的研究,一起研究研究这本书.

1作者简介

书的作者有两位,分别是Joshua BlochNeal Gafter,可能很多人或多或少对这两个名字有印象. 

没的话也不着急, 我来加深点印象.

/**
Author: Doug Lea, Josh Bloch, Arthur van Hoff, Neal Gafter
**/
public class HashMap<K,V> extends AbstractMap<K,V>

HashMap 注释上面 4 位作者中的两位.

2涉及内容

其实书的副标题很直白

  1. Trap 圈套
  2. Pitfall 陷阱
  3. Corner case 极端情况

就是这些内容,非常黑暗 ,坑到人不要不要的.而且, 最关键是,10 多年了,96 道题里面, 还有很多没过时.要是真拿来当面试题的话,不少题能面死.

接下来简单分享几个我印象深刻的.主要是题目标题+我的笔记.要是想看题最好还是去看书.

3简单热身

先来几个简单的热热身

谜题 64: 绝对值 Math.abs

众所周知周知, 绝对值是数字到原点的距离. . 返回的是非负值. 然而由于数字的不对称性, 负数比正数多.Math.abs(Integer.MIN_VALUE) 是个负值.

然后作者批评了类库设计者, 如果这里在传入MIN_VALUE时候,直接丢异常可能更好,而不是顶着一个 abs 的名字, 做一个有坑的设计.

Java 开发者估计是接受了批评, 在 Java15 版本的 Math 上, 添加了一个absExact方法.这个方法就会在最小值抛出异常了.虽然迟到了十几年,坑过了很多人.

有篇文讲的特别好, 可以翻翻看. 

RocketMQ 的源码,如果你看过源码你会发现到处都有这样的判断。

想着已经取绝对值了,然后再取余,肯定是正数啊,这 if(pos<0) 不就是多余的判断吗?

是Yes呀,公众号:yes的练级攻略Math.abs 竟然返回了负数??


谜题 61: 日期和时间

这题其实是在批评 Java 的时间设计.

初代的时间设计是Date类, 然后这个类巨坑无比.为了解决这个问题,又出了个Calendar类. 但是Calendar类也是满满的坑. 比如这个代码:

Calendar cal = Calendar.getInstance();
cal.set(2021,11,7);
Date date = cal.getTime();
date.getDay();  //输出
date.getDate(); //输出

会惊异的的发现, 方法奇葩:

  • getTime 返回的是Date.
  • getDay 返回的是礼拜几.
  • getDate 返回的是当月的第几天.

而且还有一个坑点是, 程序员从 0 开始计数, 11 指的是 12 月.

不过记不住这个坑也没关系, 好在, 这个问题也已经被解决了. Java8 时代最重要的更新之一,就是java.time库.接口设计已经很像正常人类的表达, 终于, 可以优雅的写代码, 不被坑了.

LocalDate date = LocalDate.of(2021, 11, 7);

谜题 76: 乒乓

这个就不放题了, 简单说, 就是作者在批评Thread类的设计.

相信很多朋友都会有在创建new Thread()误调用了run()而不是start()的时候.

在使用工具时候, 一个人犯错,是用的人有问题.很多人犯错,其实那很可能是设计者也得多少有点问题.

Java面死题精选

Thread 类目前是实现了Runnable接口的,但是其实这里完全可以不用继承.用组合来实现的话,按照组合>继承的原则,如果这里 API 设计的好一点, 或许会更好. Thread has aRunnable 明显优于 Thread is a Runnable.

4较深的问题

除了前面那种盘点 Java 设计问题的小题目, 还有一些深一点的题目.

谜题 80 嵌套类

这个题目比较好玩. 我们都知道, java 写在一个类里面的类,叫嵌套类.

  • 静态的叫static nested class,静态嵌套类
  • 非静态的叫inner class    内部类

静态嵌套类在编译以后,其实跟普通类没太大差别, 就是写的时候组织方式有点不一样. 内部类,就有点意思了.

我们先写成这样

public class Outer{
  public class Inner{}
}

javap '.Outer$Inner.class' 可以发现

Compiled from "Outer.java"
public class Outer$Inner {
  final Outer this$0;
  public Outer$Inner(Outer);
}

虽然每个没有构造器的类都会生成一个无参构造器,但是对于内部类例外,他会生成一个以外部类为参数的构造器.这东西其实有点复杂. 很多 JSON 库对内部类支持的不够好,都跟这玩意有关. 静态的还可以,如果不是完全掌握和确信有必要使用的话, 不要用内部类.

谜题 46,48 构造器和方法的选择

这个倒是一个常见的面试题了.主要考查是在有多个构造器或者方法符合的情况下, Java 是如何选择具体使用哪个方法的.

public class C {
    public C(Object o) {}
    public C(Integer o) {}
    public C(int o) {}

    public static void main(String[] args) {
        new C(null);
    }
}

这里面大致的规则是, 获取所有带选择方法,谁接收的参数范围小,用谁.这里使用的是Integer版本.如果想使用Object版本, 需要写成new C((Object) null).

谜题 36,39 try finally

36 题倒是也常被当面试题.

public static  int foo(){
    try{return 1;}
    finally{return 2;}
}
public static  int bar(){
    try{return 1/0;}
    finally{return(Integer)(Object)"";}
}

前面 return 啥都没用.前面的异常,也会被后面的吃掉.

39 题则是提供了一个不执行 finally 的方法.虽然很少用.

try{
    System.out.println("Hello world");
    System.exit(0);
}finally{
     System.out.println("Goodbye world");
}

如果有需要在系统推出的时候做清理的话,不能用 finally,而是使用shutdownhook.

Runtime.getRuntime().addShutdownHook(
    new Thread(){
        public void run(){
            System.out.println("Goodbye world");
    }
});

dubbo 就使用DubboShutdownHook来做了这么个清理操作.

谜题 43 sneakyThrow

这题,其实很多lombok玩家应该都见过了,这是lombok的一个功能.

比如我们写这样一个方法,如果不 try 也不往外扔的话,肯定不能编译通过:

//@SneakyThrow
public void foo(){
    throw new IOException();
}

如果加上lombok的@SneakyThrow,那么就可以编译通过了. 他可以省掉try或者throw.这里用的就是这一题的技巧,如何在运行中扔出一个checkedException. @SneakyThrows (projectlombok.org)[1] . 巧妙利用泛型丢出来的.

谜题 45 递归 try finally

这题非常灵异, 估计不会有人考. 但是拿来检查递归是否掌握了,很有趣.

static void foo(){
    try{
        foo();
    }finally{
        foo();
    }
}

这个方法会爆栈.但是假如栈深度为 n 的话, 他会产生一个完全二叉的调用树.在 次调用后,产生 个异常.注意到棋盘放米的故事话, 这几乎等价于永远不会停了.

Java面死题精选
原书插图,n=3

6数字和字符相关

除了上面那些, 还有一些跟数字和循环有关的.

谜题 6 多次 cast

System.out.println((int)(char)(byte)-1);

有一批主要是混合类型和类型拓宽之类带来的谜题,就不摘抄太多了了.

可以猜猜看,上面那句的答案是多少.猜不对的话,千万不要混合byte,char,int 等等类型进行计算,惹不起躲得起.

谜题 28,29,30,32,34 循环

这里是几个概念.

  1. int 是有边界的, i+1 可能<i
  2. double 浮点数不是均匀的. 比较大的浮点数, 两个相邻数之间的间隔可能大于>1. 这时候 f+1 ==f. 用浮点数做循环很可能被坑死.
  3. NaN!=NaN 浮点数中有一个表示不是数的符号, 他跟自己不相等.
  4. +用作字符串拼接 作者认为 java 不支持运算符重载却支持+作为字符串加法是个败笔
  5. i <= j && j <= i && i != j 可能为真. 这个涉及到自动拆装箱.

谜题 14,15,16 转义字符

这题涉及到 Java 的编译逻辑,代码里写的uxxxx会先进行翻译, 即使是注释里写uxxxx如果不是合法 Unicode 也能引起编译问题. 甚至能把 helloworld 写成这样.

u0070u0075u0062u006cu0069u0063u0020u0020u0020u0020
u0063u006cu0061u0073u0073u0020u0055u0067u006cu0079
u007bu0070u0075u0062u006cu0069u0063u0020u0020u0020
u0020u0020u0020u0020u0073u0074u0061u0074u0069u0063
u0076u006fu0069u0064u0020u006du0061u0069u006eu0028
u0053u0074u0072u0069u006eu0067u005bu005du0020u0020
u0020u0020u0020u0020u0061u0072u0067u0073u0029u007b
u0053u0079u0073u0074u0065u006du002eu006fu0075u0074
u002eu0070u0072u0069u006eu0074u006cu006eu0028u0020
u0022u0048u0065u006cu006cu006fu0020u0077u0022u002b
u0022u006fu0072u006cu0064u0022u0029u003bu007du007d

7

今天是一篇打着面死题旗号的书评.

也就到此结束, 不能再摘了, 再摘整本书都摘上来了.面还有很多我抄书都不知道怎么抄出来的题,有大佬感兴趣,可以去拿出来玩玩儿.没事看看有利于身心健康.

参考资料

[1]

@SneakyThrows (projectlombok.org): https://projectlombok.org/features/SneakyThrows


原文始发于微信公众号(K字的研究):Java面死题精选

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/24822.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!