前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >阿里面试官分享:7个Java面试题常见的坑

阿里面试官分享:7个Java面试题常见的坑

作者头像
千羽
发布2024-04-17 15:32:46
830
发布2024-04-17 15:32:46
举报
文章被收录于专栏:程序员千羽程序员千羽

转义字符

代码语言:javascript
复制
public static void main(String[] args) {
    System.out.println("a\u0022.length() +\u0022b".length());
    System.out.println("a".length() +"b".length());
}

在 Java 中,\u0022 是双引号字符 " 的 Unicode 转义序列。所以 "a\u0022.length()" 实际上是 "a".length() 的字符串形式。而 \u0022b 也是同样的情况,它是 "b" 的字符串形式。因此,整个表达式 "a\u0022.length() +\u0022b" 会被当做字符串处理。

让我们来分析代码:

代码语言:javascript
复制
javaCopy codepublic static void main(String[] args) {
    System.out.println("a\u0022.length() +\u0022b".length()); // 第一个打印语句
    System.out.println("a".length() +"b".length()); // 第二个打印语句
}

在第一个打印语句中,我们有 "a\u0022.length() +\u0022b" 这个字符串。它包含了两个部分:"a\u0022.length()""\u0022b"。第一个部分代表了字符串 "a" 的长度,而第二个部分代表了字符串 "b"。所以,第一个打印语句输出的是 1 + 1 = 2

在第二个打印语句中,我们有 "a".length()"b".length(),它们分别代表了字符串 "a""b" 的长度。所以,第二个打印语句输出的是 1 + 1 = 2

因此,这段代码的输出是:

代码语言:javascript
复制
2
2

打印输出类名

问题:打印出类名

代码语言:javascript
复制
public static void main(String[] args) {
    System.out.println(
            MyClass.class.getName().
                    replaceAll("\\.","/") + ".class");
}
  1. MyClass.class.getName(): 这段代码获取了 MyClass 类的全限定名,即包括包名在内的类名。例如,如果 MyClass 类的包名为 com.example,则这个表达式的结果是 "com.example.MyClass"
  2. replaceAll("\\.", "/"): 这是一个字符串替换操作,它使用正则表达式将全限定类名中的.替换为/。在 Java 的正则表达式中,. 表示任意字符,因此需要使用 \\. 来表示真正的.。所以,这个操作将类名中的.替换为/,例如,将 "com.example.MyClass" 替换为 "com/example/MyClass"
  3. + ".class": 最后,将结果字符串末尾添加上 ".class",这是 Java 中表示类文件的后缀。所以,最终得到的字符串就是类文件的路径,例如,"com/example/MyClass.class"

因此,这段代码的作用是打印出指定类的类文件路径。


如果想要打印类名,只需调用 getSimpleName() 方法。这个方法会返回类的简单名称,即不包含包名的类名。

代码语言:javascript
复制
public static void main(String[] args) {
    System.out.println(MyClass.class.getSimpleName());
}

随机数的问题

代码语言:javascript
复制
private static Random rnd = new Random();

public static void main(String[] args) {
    StringBuffer word = null;
    switch (rnd.nextInt(3)) {
        case 1:
            word = new StringBuffer("P");
            break;
        case 2:
            word = new StringBuffer("G");
            break;
        default:
            word = new StringBuffer("M");
    }
    word.append('a');
    word.append('i');
    word.append('n');
    System.out.println(word);
}
  1. private static Random rnd = new Random();: 这行代码创建了一个静态的 Random 对象,用于生成随机数。
  2. StringBuffer word = null;: 这里声明了一个 StringBuffer 对象 word,并将其初始化为 null
  3. switch (rnd.nextInt(3)) { ... }: 这是一个 switch 语句,根据生成的随机数在 0、1、2 之间选择不同的分支。
  4. rnd.nextInt(3): 这个表达式会生成一个 0、1 或 2 的随机数。
  5. switch 语句中,根据随机数的值,分别为 word 赋值为不同的字符串。
  6. word.append('a');, word.append('i');, word.append('n');: 不论 word 最初被赋值为何种字符串,这三行代码都会在其后追加字符 'a', 'i', 'n'
  7. System.out.println(word);: 最后,将 word 打印到控制台上。

由于 rnd.nextInt(3) 生成的随机数范围是 0 到 2,所以有 1/3 的几率 word 被赋值为 "P",有 1/3 的几率赋值为 "G",有 1/3 的几率赋值为 "M"。接下来,无论 word 的初始值是什么,都会追加字符 'a', 'i', 'n'

因此,最终的输出取决于随机数的值和 word 最初的赋值,可能是 "Pain", "Gain", 或 "Main",每个的几率都是 1/3。

增量操作

代码语言:javascript
复制
public static void main(String[] args) {
    int j = 0;
    for (int i = 0; i < 100; i++) {
        j = j++;
    }
    System.out.println(j);
}

在这个循环中,你尝试对变量 j 进行自增操作。然而,这里存在一个陷阱。在 Java 中,j++ 操作会先返回 j 的值,然后再对 j 自增。但是,由于你又将 j 的值赋给了 j,这可能会导致一个意外的结果。

具体来说,在 j = j++ 这行代码中,j++ 会返回 j 当前的值,然后 j 的值会增加 1。但是,由于赋值操作是在 j++ 的返回值之后进行的,所以赋给 j 的值实际上是 j 原来的值,而不是自增后的值。这意味着 j 的值没有发生变化,即 j++ 的效果被忽略了。

因此,循环中的 j 没有被增加,它仍然保持初始值 0。所以,无论循环执行多少次,j 都会保持为 0。因此,打印的结果将是 0

正确修改

要正确地对变量 j 进行自增操作,你可以使用 j++ 或者 ++j,而不需要将结果再次赋给 j。下面是修改后的代码:

代码语言:javascript
复制
public static void main(String[] args) {
    int j = 0;
    for (int i = 0; i < 100; i++) {
        j++;
    }
    System.out.println(j);
}

在这个修改后的代码中,我们直接使用 j++ 来对 j 进行自增操作,而不再将结果赋给 j。这样就能正确地对 j 进行自增操作了。这段代码的输出将会是 100,因为循环执行了 100 次,每次都将 j 增加了 1。

整数边界的问题

代码语言:javascript
复制
public static final int END = Integer.MAX_VALUE;
public static final int START = END - 100;

public static void main(String[] args) {
    int count = 0;
    for (int i = START; i <= END; i++)
        count++;
    System.out.println(count);
}

在这段代码中,你尝试对从 STARTEND 范围内的整数进行计数。但是,由于 STARTEND 都是 int 类型的常量,而且 ENDInteger.MAX_VALUE,这会导致 STARTEND 之间的循环会发生整数溢出,导致无限循环。因为 STARTInteger.MAX_VALUE - 100

所以 START 的值已经接近 Integer.MAX_VALUE,再加上循环中的每一次增加操作都会使 i 的值逼近 Integer.MAX_VALUE,所以在循环条件 i <= END 中,i 的值永远不会大于 END,导致循环无法结束。

解决这个问题的方法是避免整数溢出。你可以将 STARTEND 的类型改为 long,这样就可以容纳更大的值。以下是修改后的代码:

代码语言:javascript
复制
public static final int END = Integer.MAX_VALUE;
public static final int START = END - 100;

public static void main(String[] args) {
    long count = 0; // 将 count 的类型改为 long
    for (long i = START; i <= END; i++) // 将 i 的类型改为 long
        count++;
    System.out.println(count);
}

这样就能避免整数溢出,并且正确地计算循环中的整数数量了。

计数器的问题

代码语言:javascript
复制
public static void main(String[] args) {
int minutes = 0;
    for (int ms = 0; ms < 60*60*1000; ms++)
    if (ms % 60*1000 == 0)
        minutes++;
    System.out.println(minutes);
}

在这段代码中,你尝试计算从 0 毫秒到 1 小时之间的分钟数。但是,循环条件和计算条件存在问题,导致结果不正确。

让我们来分析一下:

代码语言:javascript
复制
int minutes = 0;
for (int ms = 0; ms < 60*60*1000; ms++) { // 循环条件有问题
    if (ms % 60*1000 == 0) { // 计算条件有问题
        minutes++;
    }
}
System.out.println(minutes);

问题出在循环条件和计算条件上:

  1. 循环条件应该是 ms < 60*1000*60,表示从 0 毫秒到 1 小时之间的所有毫秒数。原始的循环条件 ms < 60*60*1000 只考虑了 1 小时内的毫秒数,没有包括从 0 开始的毫秒数。
  2. 计算条件应该是 ms % (60*1000) == 0,表示当毫秒数是分钟的整数倍时,增加分钟数。原始的计算条件 ms % 60*1000 == 0 实际上是 ms % 60000 == 0,它将毫秒数先乘以 60,然后再取模,这并不是你想要的结果。

修改后的代码应该如下所示:

代码语言:javascript
复制
public static void main(String[] args) {
    int minutes = 0;
    for (int ms = 0; ms < 60*1000*60; ms++) {
        if (ms % (60*1000) == 0) {
            minutes++;
        }
    }
    System.out.println(minutes);
}

这样就能正确地计算从 0 毫秒到 1 小时之间的分钟数了。

优柔寡断的返回值

代码语言:javascript
复制
public static void main(String[] args) {
    System.out.println(decision());
}

public static boolean decision() {
    try {
        return true;
    } finally {
        return false;
    }
}

在这段代码中,decision() 方法中的 finally 块中的 return false; 语句将会覆盖 try 块中的 return true; 语句。这是因为 finally 块中的代码总是会在 try 块中的代码执行完毕后执行,无论 try 块中是否有异常抛出。

所以,无论 try 块中的代码返回什么值,最终 decision() 方法都会返回 false

因此,这段代码的输出是 false

你好,再见

代码语言:javascript
复制
public static void main(String[] args) {
    try {
        System.out.println("Hello world");
        System.exit(0);
    } finally {
        System.out.println("Goodbye world");
    }
}

在这段代码中,无论是否在try块中调用了System.exit(0)来终止Java虚拟机,finally块中的代码都会被执行。finally块中的代码在以下情况下才不会被执行:

  1. 当Java虚拟机在执行try块时遇到了System.exit(int status),导致Java虚拟机立即终止。在这种情况下,finally块中的代码将不会被执行。
  2. 当Java虚拟机遇到了严重的错误,例如虚拟机崩溃或者OutOfMemoryError,导致Java虚拟机无法正常继续执行。在这种情况下,finally块中的代码也不会被执行。

但是,在这段代码中,System.exit(0)会使得Java虚拟机立即终止,因此finally块中的代码不会被执行。所以,虽然System.out.println("Goodbye world")语句在finally块中,但是它不会被执行。因此,这段代码的输出只有一行:

代码语言:javascript
复制
Hello world
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2024-04-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 千羽的编程时光 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 打印输出类名
  • 随机数的问题
  • 增量操作
  • 整数边界的问题
  • 计数器的问题
  • 优柔寡断的返回值
  • 你好,再见
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档