JAVA后端开发面经5

​面经来源于github上的

Java-Interview

在学习时,用自己的语言解释​

 41.error和exception有什么区别?

error 表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。

exception 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。

相同点:

  • Error类和Exception类都是继承Throwable类

不同点:

  • Error(错误)是系统中的错误,程序员是不能改变的和处理的,是在程序编译时出现的错误,只能通过修改程序才能修正。
  • Exception(异常)表示程序可以处理的异常,可以捕获且可能恢复。

42.Java中的异常处理机制的简单原理和应用。

原作者写的挺好,可以看。

异常是指java程序运行时(非编译)所发生的非正常情况或错误,与现实生活中的事件很相似,现实生活中的事件可以包含事件发生的时间、地点、人物、情节等信息,可以用一个对象来表示,Java使用面向对象的方式来处理异常,它把程序中发生的每个异常也都分别封装到一个对象来表示的,该对象中包含有异常的信息。

Java对异常进行了分类,不同类型的异常分别用不同的Java类表示,所有异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error和Exception,Error 表示应用程序本身无法克服和恢复的一种严重问题,程序只有死的份了。

例如,说内存溢出和线程死锁等系统问题。Exception表示程序还能够克服和恢复的问题,其中又分为系统异常和普通异常:(也就是说,系统异常是我的问题,普通异常是使用者的问题)

  • 系统异常是软件本身缺陷所导致的问题,也就是软件开发人员考虑不周所导致的问题,软件使用者无法克服和恢复这种问题,但在这种问题下还可以让软件系统继续运行或者让软件死掉,例如数组脚本越界(ArrayIndexOutOfBoundsException),空指针异常(NullPointerException)、类转换异常(ClassCastException);
  • 普通异常是运行环境的变化或异常所导致的问题,是用户能够克服的问题,例如,网络断线,硬盘空间不够,发生这样的异常后,程序不应该死掉。

java为系统异常和普通异常提供了不同的解决方案,编译器强制普通异常必须try..catch处理或用throws声明继续抛给上层调用方法处理,所以普通异常也称为checked异常,而系统异常可以处理也可以不处理.

所以,编译器不强制用try..catch处理或用throws声明,所以系统异常也称为unchecked异常。

提示答题者:就按照三个级别去思考:虚拟机必须宕机的错误,程序可以死掉也可以不死掉的错误,程序不应该死掉的错误;

总结一下:为啥会宕机,因为出现问题,这个问题有两种异常:系统异常和普通异常。

系统异常是我的问题,普通异常是使用者的问题。程序不死掉的问题,应该发生在用户身上,也就是普通异常;程序可以死掉也不可以死掉的问题,发生在程序员的身上(作为程序员开发的时候肯定要进行测试嘛)

43.请写出你最常见到的5个runtime exception。

这道题主要考你的代码量到底多大,如果你长期写代码的,应该经常都看到过一些系统方面的异常,你不一定真要回答出5个具体的系统异常,但你要能够说出什么是系统异常,以及几个系统异常就可以了,当然,这些异常完全用其英文名称来写是最好的,如果实在写不出,那就用中文吧,有总比没有强!

所谓系统异常,它们都是RuntimeException的子类,在jdk doc中查RuntimeException类,就可以看到其所有的子类列表,也就是看到了所有的系统异常。我比较有印象的系统异常有:NullPointerException、ArrayIndexOutOfBoundsException、ClassCastException

解释一下:也就是空指针异常,数组下标溢出,类型转换错误

我再补充两个我见到的:IllegalArgumentException,IllegalStateException

第一个就是传递的参数非法,第二个就是程序状态不正确时却让它执行(相当于想要删除文件,但是你的文件还在使用中)

44.java中有几种方法可以实现一个线程?用什么关键字修饰同步方法? stop()和suspend()方法为何不推荐使用?

java5以前,有如下两种:

第一种:new Thread(){}.start();这表示调用Thread子类对象的run方法,new Thread(){}表示一个Thread的匿名子类的实例对象,子类加上run方法后的代码如下:

new Thread(){
    public void run(){
    }
}.start();

第二种:

new Thread(new Runnable(){}).start();这表示调用Thread对象接受的Runnable对象的run方法,new Runnable(){}表示一个Runnable的匿名子类的实例对象,runnable的子类加上run方法后的代码如下:

new Thread(new Runnable(){
            public void run(){
            }   
        }
    ).start();

从java5开始,还有如下一些线程池创建多线程的方式:

ExecutorService pool = Executors.newFixedThreadPool(3)
for(int i=0;i<10;i++)
{
    pool.execute(new Runable(){public void run(){}});
}
Executors.newCachedThreadPool().execute(new Runable(){public void run(){}});
Executors.newSingleThreadExecutor().execute(new Runable(){public void run(){}});

有两种实现方法,分别使用new Thread()和new Thread(runnable)形式,第一种直接调用thread的run方法,所以,我们往往使用Thread子类,即new SubThread()。第二种调用runnable的run方法。

有两种实现方法,分别是继承Thread类与实现Runnable接口,用synchronized关键字修饰同步方法。

反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。

suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。

45.sleep() 和 wait() 有什么区别?

sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。

wait是Object类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。

  • 理解一下:其实就是你身上有一个锁子甲,穿着在跑步。sleep就是你虽然获得了跑步的权利(穿上锁子甲),但是sleep出来你就得停下。wait就是把你的锁子甲脱下来,给下一个人,自己去排队等下一次机会

sleep就是正在执行的线程主动让出cpu,cpu去执行其他线程,在sleep指定的时间过后,cpu才会回到这个线程上继续往下执行,如果当前线程进入了同步锁,sleep方法并不会释放锁,即使当前线程使用sleep方法让出了cpu,但其他被同步锁挡住了的线程也无法得到执行。

wait是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,只有其他线程调用了notify方法(notify并不释放锁,只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还在别人手里,别人还没释放。

可能有友友对同步锁的概念有些陌生,这里通俗解释一下。

  1. 同步锁和异步锁是两种不同的线程同步机制
  2. 同步锁是为了保证多个线程可以安全地共享资源而设计的。当一个线程需要访问共享资源时,它必须先获取同步锁,以确保在任意时刻只有一个线程可以访问该资源。如果其他线程也尝试获取同步锁,它们将被阻塞,直到拥有锁的线程释放该锁。同步锁可以防止数据竞争和不一致状态的出现,但也可能导致线程阻塞和性能问题。
  3. 相比之下,异步锁是一种更为灵活的线程同步机制。它允许线程在不互相等待的情况下独立运行,只有在需要访问共享资源时才会获取锁。如果其他线程也尝试获取异步锁,它们不会被阻塞,而是继续执行其他任务。异步锁可以提高线程的并发性和吞吐量,但也可能导致数据竞争和不一致状态的出现。
  4. 通俗地说,同步锁就像是一个排队系统,所有线程都必须按照顺序一个一个地获取锁和释放锁;而异步锁则像是一个分叉路口,线程可以在不同的道路上独立运行,只有在需要交集时才会获取锁。这两种机制各有优缺点,选择使用哪种机制取决于具体的场景和需求。

46.同步和异步有何异同,在什么情况下分别使用他们?举例说明。

如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。

当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。

47.多线程有几种实现方法?同步有几种实现方法?

多线程有两种实现方法,分别是继承Thread类与实现Runnable接口。

同步的实现方面有两种,分别是synchronized,wait与notify。

wait():使一个线程处于等待状态,并且释放所持有的对象的lock。(丢掉锁子甲)

sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。(跑步停下来)

notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。

Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

48.启动一个线程是用run()还是start()?

启动一个线程是调用start()方法,使线程就绪状态,以后可以被调度为运行状态,一个线程必须关联一些具体的执行代码,run()方法是该线程所关联的执行代码。

49.当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?

分几种情况:

1)其他方法前是否加了synchronized关键字(这玩意就是阻塞),如果没加,则能。

2)如果这个方法内部调用了wait(就是让你丢掉锁子甲,等别人),则可以进入其他synchronized方法。

3)如果其他个方法都加了synchronized关键字,并且内部没有调用wait,则不能。

4)如果其他方法是static,它用的同步锁是当前类的字节码,与非静态的方法不能同步,因为非静态的方法用的是this。

50.线程的基本概念、线程的基本状态以及状态之间的关系。 

一个程序中可以有多条执行线索同时执行,一个线程就是程序中的一条执行线索,每个线程上都关联有要执行的代码,即可以有多段程序代码同时运行,每个程序至少都有一个线程,即main方法

执行的那个线程。如果只是一个cpu,它怎么能够同时执行多段程序呢?这是从宏观上来看的,cpu一会执行a线索,一会执行b线索,切换时间很快,给人的感觉是a,b在同时执行,好比大家在同一个办公室上网,只有一条链接到外部网线,其实,这条网线一会为a传数据,一会为b传数据,由于切换时间很短暂,所以,大家感觉都在同时上网。

状态:就绪,运行,synchronize阻塞,wait和sleep挂起,结束。

wait必须在synchronized内部调用。

调用线程的start方法后线程进入就绪状态,线程调度系统将就绪状态的线程转为运行状态,遇到synchronized语句时,由运行状态转为阻塞,当synchronized获得锁后,由阻塞转为运行,在这种情况可以调用wait方法转为挂起状态,当线程关联的代码执行完后,线程变为结束状态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值