Java面试常见问题及答案

本文深入探讨了Java编程中的关键概念,包括switch语句支持的类型、自动装箱、finalize方法的作用、==与equals的区别、hashCode()的重要性、String对象的创建、StringBuffer与StringBuilder的差异、异常处理机制、Object的wait方法原理、锁的实现以及JVM垃圾回收。此外,还涵盖了函数式接口、Lambda表达式、Stream API和集合类的关系等实用话题,帮助Java开发者准备面试和提升技能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

java switch语句支持的类型

Incompatible types. Found: ‘long’, required: ‘char, byte, short, int, Character, Byte, Short, Integer, String, or an enum’
这是switch接收long 参数的编译报错,从提示可以看出,switch只支持“char, byte, short, int, Character, Byte, Short, Integer, String, or an enum

java 自动装箱经典题

  • case1
		Integer i1 = 128;
        Integer i2 = 128;
        System.out.println("i1==i2: "+(i1==i2));
        System.out.println("i1 == 128: " + (i1==128));

输出结果

i1==i2: false
i1 == 128: true
  • case 2
		Integer i1 = 100;
        Integer i2 = 100;
        System.out.println("i1==i2: "+(i1==i2));
        System.out.println("i1 = 100: " + (i1==100));

输出结果:

i1==i2: true
i1 = 100: true

分析:
默认情况下,-128–127 的常量对象在自动装箱时会被复用;
所以case2 时,i1和i2指向同一个常量对象。
如果是new的对象,则不会复用。

finalize是干嘛的?调用了就能表示对象被回收了吗?

finalize 是在 java.lang.Object 里定义的方法,也就是说每一个对象都有这么个方法,这个方法在 gc 启动,表示该对象可能被回收。
一个对象的 finalize 方法只会被调用一次,finalize 被调用不一定会立即回收该对象,所以有可能调用 finalize 后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会再次调用 finalize 了,进而产生问题,因此不推荐使用 finalize 方法。

==和 equals 的区别

本质上,== 是运算符,而equals是方法,二者根本没有可比性;
不过从逻辑上,二者又相似之处; == 比较的是两个对象地址是否相等,也就是是否是同一个对象;equals默认实现和== 一致,但是如果子类override了此方法,则多数情况下就是指内容是否相等。

hashCode()的默认值是什么?为什么重写equals也必须重写hashCode

对象的内存地址 通过hash 函数转化后得到的一个整数。
逻辑上来说,相等的对象,hashCode一定相等;如果重写了equals,两个不同的对象内容相等,我们认为相等,equals返回true,但原始的hashCode却是不等的,逻辑错误。

String str1 = new String(“abc”)和 String str2 = “abc” 和 区别?

  • 相同点:均会在常量池中查找是否有"abc"这个字符串,如果有,则都直接使用;如果没有则创建新的。
  • 不同点:
    String str2 = “abc” 在常量池无"abc"时创建对象,并将引用赋值给str2;
    new String(“abc”) 会额外堆中创建一个新的对象"abc".

StringBuffer 和StringBuilder 的区别

  • 相同点:
    都是用来创建字符串,不产生中间对象;String 是不可变的字符串。
    直接使用字符串拼接 +, 编译后会变为StringBuilder 来append。
  • 不同点:
    StringBuffer:效率低、线程安全; A thread-safe, mutable sequence of characters
    StringBuilder:效率高、线程不安全; 如果单线程情况,推荐使用这个而不是StringBuffer,因为其效率较高。

java 的异常体系

image

整体来说,Java的异常基类为Throwable,总的分为两类: Error和Exception。
Exception 是程序逻辑错误引起的异常,JVM自己抛的;
而Error是JVM内部的错误,是进程的异常,java程序没法处理。

java try catch 能够捕获异步跨线程的异常吗?

举个例子

点击查看代码
public static void test() {
        try {
            Logger.i("run outside: " + Thread.currentThread().getId());
            new Thread(()->{
                Logger.i("run in child: " + Thread.currentThread().getId());
                throw new RuntimeException("this is from child");
            }).start();
        } catch (Exception e){
            Logger.i("run in catch: " + Thread.currentThread().getId());
            e.printStackTrace();
        }
    }
直接抛异常了,说明兜不住。事实上,try 只能对同一个线程有用。
 run in child: 16
Exception in thread "Thread-0" java.lang.RuntimeException: this is from child
	at com.company.exception.TryCatchAsyncThreadExp.lambda$test$0(TryCatchAsyncThreadExp.java:13)
	at java.base/java.lang.Thread.run(Thread.java:831)

finally 经典题目

 public static int test1() {
        try {
            return 2;
        } finally {
            return 3;
        }
    }
// test1 输出 3
//------------------------------
private static int innerTryFinally() {
        try {
            return 1;
        } catch (Exception e) {
            return 2;
        } finally {
            System.out.print("3");
        }
    }
// innerTryFinally 先输出3,再返回1
//-----------------------------------------------------
public static int test2() {
        int i = 0;
        try {
            i = 2;
            return i;
        } finally {
            i = 3;
        }
    }
// test2 返回2, 可能有人会问了,finally不是会把i改掉吗?
//但是,再返回之前,返回值是i的一个副本,所以还是 返回2

finally 会在return之前执行。

Object的wait方法底层原理

wait

object.wait 需要先拿到该对象的锁(Monitor对象的所有权),也即在synchronized 块中。
wait在JVM的实现中,会找到对象对应的ObjectMonitor对象,然后调用其ObjectMonitor::wait方法,
ObjectMonitor::wait -> addWaiter() 会将线程包装为ObjectWaiter对象,添加到ObjectMonitor的等待队列中_waitSet, 然后调用ObjectMonitor::exit释放锁,接着 thread_ParkEvent->park 也就是实现了当前线程的wait。

Monitor有两个队列:
*_WaitSet ** :处于wait状态的线程,会被加入到wait set;
_EntryList:处于等待锁block状态的线程,会被加入到entry set;

notify、notifyAll

当有其他线程调用notify或者notifyAll时,
notify会选择该monitor对象的waitSet中的某个线程放到等待锁的双向链表EntryList中;
notifyAll会将该monitor对象的waitSet中的所有线程放到等待锁的双向链表EntryList中;;
notify并不会释放锁,也不会唤醒线程;真正唤醒线程是在释放锁时,ObjectMonitor::exit方法中
大概为这样:

// 释放锁
OrderAccess::release_store_ptr (&_owner, NULL) ;
OrderAccess::fence() ;
// 唤醒线程
Trigger->unpark() ;

notify 并不会释放锁,真正释放锁是在monitor exit时
package com.company.thread;

public class WaitAndNotify {
   
    private static Object lock = new Object();
    public static void test() {
   
        new Thread(new Runnable() {
   
            @Override
            public void run() {
   
                System.out.println(" Thread1 enter.. going to get lock");
                synchronized (lock) {
   
                    System.out.println(" Thread1 enter.. get lock");
                    try {
   
                        System.out.println(" Thread1 .. before wait");
                        lock.wai
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值