Java面试题大全(Java基础十三)

来源:互联网 发布:麦卡锡主义 知乎 编辑:程序博客网 时间:2024/06/05 04:21

71、heap和stack有什么区别。

stack内存指的是程序进入一个方法时,系统会专门为这个方法分配一块内存空间,这块内存空间也被称为该方法栈区,该方法的栈区专门用于存储该方法中定义的局部变量,包括基本类型的变量和引用变量。当这个方法结束时,该方法栈区将会自动被销毁,栈区中的所有局部变量都会随之销毁。
heap内存是Java虚拟机拥有的内存区,所有Java对象都将被放在heap内存内,位于heap内存中的Java对象由系统的垃圾回收器负责跟踪管理——也就是进行垃圾回收,当堆内存中的Java对象没有引用变量引用它时,这个Java对象就变成了垃圾,垃圾回收期就会在合适的时候回收它。

72、try{}里有一个return语句,那么紧跟在这个try后的finally{}里的code会不会被执行,什么时候被执行,在return前还是后?

肯定会执行。finally{}块的代码只有在try{}块中包含遇到System.exit(0);之类的导致Java虚拟机直接退出的语句才会不执行。
当程序执行try{}遇到return时,程序会先执行return语句,但并不会立即返回——也就是把return语句要做的一切事情都准备好,也就是在将要返回、但并未返回的时候,程序把执行流程转去执行finally块,当finally块执行完成后就直接返回刚才return语句已经准备好的结果。
例如我们有如下程序:

public class Test{    public static void main(String[] args)    {        System.out.println(new Test().test());;    }    static int test()    {        int x = 1;        try        {            return x;        }        finally        {            System.out.println("finally块执行:" + ++x);        }    }}

此时的输出结果为:
finally块执行:2
1
看到上面程序中finally块已经执行了,而且程序执行finally块时已经把x变量增加到2了。但test()方法返回的依然是1,这就是由return语句执行流程决定的,Java会把return语句先执行完、把所有需要处理的东西都先处理完成,需要返回的值也都准备好之后,但是还未返回之前,程序流程会转去执行finally块,但此时finally块中的对x变量的修改已经不会影响return要返回的值了。
但如果finally块里也包含return语句,那就另当别论了, 因为finally块里的return语句也会导致方法返回,例如把程序该为如下形式:

public class Test{    public static void main(String[] args)    {        System.out.println(new Test().test());;    }    static int test()    {        int x = 1;        try        {            return x++;        }        finally        {            System.out.println("finally块执行:" + ++x);            return x;        }    }}

此时的输出结果为:
finally块执行:3
3
正如介绍的,程序在执行return x++;时,程序会把return语句执行完成,只是等待返回,此时x的值已经是2了,但程序此处准备的返回值依然是1。接下来程序流程转去执行finally块,此时程序会再次对x自加,于是x变成了3,而且由于finally块中也有return x;语句,因此程序将会直接由这条语句返回了,因此上面test()方法将会返回3。

73、下面的程序代码输出的结果是多少?

public class smallT{    public static void main(String args[])    {        smallT t = new smallT();        int b = t.get();        System.out.println(b);    }    public int get()    {        try        {            return 1 ;        }        finally        {            return 2 ;        }    }}

输出结果是:2。
这个程序还是刚才介绍的return语句和finally块的顺序问题。
Java会把return语句先执行完、把所有需要处理的东西都先处理完成,需要返回的值也都准备好之后,但是还未返回之前,程序流程会转去执行finally块。但如果在执行finally块时遇到了return语句,程序将会直接使用finally块中的return语句来返回——因此上面程序将会输出2。

74、final, finally, finalize的区别。

final是一个修饰符,它可以修改类、方法、变量。
final修饰类时表明这个类不可以被继承。
final修饰方法时表明这个方法不可以被其子类重写。
final修饰变量时可分为局部变量、实例变量和静态变量,当final修饰局部变量时,该局部变量可以被一次赋值,以后该变量的值不能发生该变量;当final修饰实例变量时,实例变量必须由程序在构造器、初始化块、定义时这3个位置的其中之一指定初始值;当final修饰静态变量时,静态变量必须由程序在静态初始化块、定义时这2个位置的其中之一指定初始值。
finally是异常处理语句结构的一部分,表示总会执行的代码块。
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收。但实际上重写该方法进行资源回收并不安全,因为JVM并不保证该方法总被调用。

75、运行时异常与一般异常有何异同?

Checked异常体现了Java的设计哲学:没有完善错误处理的代码根本就不会被执行!
对于Checked异常的处理方式有两种:
A.当前方法明确知道如何处理该异常,程序应该使用try…catch块来捕获该异常,然后在对应的catch块中修补该异常。
B.当前方法不知道如何处理这种异常,应该在定义该方法时声明抛出该异常。
但Runtime异常则更加灵活,Runtime异常无须显式声明抛出,如果程序需要捕捉Runtime异常,也可以使用try…catch块来捕捉Runtime异常。
76、error和exception有什么区别?
Error错误,一般是指虚拟机相关的问题,如系统崩溃、虚拟机出错误、动态链接失败等,这种错误无法恢复或不可能捕获,将导致应用程序中断。通常应用程序无法处理这些错误,因此应用程序不应该试图使用catch块来捕获Error对象。
Exception表示一种设计或实现问题。也就是说,程序员应该对这些情况进行考虑、并提供相应的处理。

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

程序运行过程中可能出现各种“非预期”情况,这些非预期情况可能导致程序非正常结束。
为了提高程序的健壮性,Java提供了异常处理机制:

try{    s1...    s2...    s3...}catch(Exception ex){    //对异常情况的修复处理}

对于上面处理流程,当程序执行try块里的s1、s2、s3遇到异常时,Java虚拟机将会把这个异常情况封装成异常对象,这个异常对象可以被后面对应的catch块捕捉到,这样保证这些异常会得到合适的处理。
Java对异常进行了分类,不同类型的异常分别用不同的Java类表示,所有异常的根类为java.lang.Throwable,Throwable下面又派生了两个子类:Error和Exception,Error错误,一般是指虚拟机相关的问题,如系统崩溃、虚拟机出错误、动态链接失败等,这种错误无法恢复或不可能捕获,将导致应用程序中断。通常应用程序无法处理这些错误,因此应用程序不应该试图使用catch块来捕获Error对象。
Exception表示一种设计或实现问题。也就是说,程序员应该对这些情况进行考虑、并提供相应的处理。
异常有可分为Runtime异常和Checked异常,Checked异常体现了Java的设计哲学:没有完善错误处理的代码根本就不会被执行!对于Checked异常的处理方式有两种:
A.当前方法明确知道如何处理该异常,程序应该使用try…catch块来捕获该异常,然后在对应的catch块中修补该异常。
B.当前方法不知道如何处理这种异常,应该在定义该方法时声明抛出该异常。
实际上Java的Checked异常后来“争议不断”,因为Checked异常要求程序员要么显式声明抛出,要么进行捕捉,不能对Checked异常不闻不问,这样就给编程带来了一定的复杂度,比如Spring、Hibernate框架的一大特点就是把Checked异常包装成了Runtime异常。
Runtime异常则比较灵活,开发者既可以选择捕获Runtime异常,也可以不捕获。

78、请写出你最常见到的5个runtime exception。

对于一个有1~2年左右编程经验的人来说,总会经常遇到一些常见的异常,其中有些就是Runtime Exception。比如:
NullPointerException - 当调用一个未初始化的引用变量(实际值为null)的实例Field、实例方法时都会引发该异常。
ArithmeticException - 算术异常。比如5/0将引发该异常。
ArrayIndexOutOfBoundsException:数组索引越界异常。
ClassCastException:类型转换异常。
IllegalArgumentException:参数非法的异常。

79、JAVA语言如何进行异常处理,关键字:throws、throw、try、catch、finally分别代表什么意义?在try块中可以抛出异常吗?

try块表示程序正常的业务执行代码。如果程序在执行try块的代码时出现了“非预期”情况,JVM将会生成一个异常对象,这个异常对象将会被后面相应的catch块捕获。
catch块表示一个异常捕获块。当程序执行try块引发异常时,这个异常对象将会被后面相应的catch块捕获。
throw用于手动地抛出异常对象。throw后面需要一个异常对象。
throws用于在方法签名中声明抛出一个或多个异常类,throws关键字后可以紧跟一个或多个异常类。
finally块代表异常处理流程中总会执行的代码块。
对于一个完整的异常处理流程而言,try块是必须的,try块后可以紧跟一个或多个catch块,最后还可以带一个finally块。
try块中可以抛出异常。

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

在Java5以前,有如下两种:
第一种:继承Thread类,重写它的run()方法。
代码如下:

new Thread(){    public void run()    {        //线程执行体    }}.start();

第二种:实现Runnable接口,并重写它的run()方法。
代码如下:

new Thread(new Runnable(){    public void run()    {        //线程执行体    }   }).start();

从上面代码不难看出,线程的执行体是一个run()方法,然后程序通过start()方法启动一条线程。
从Java 5开始,Java提供了第三种方式来创建多线程:实现Callable接口,并实现call()方法。Callable接口相当于Runnable接口的增强版,因为Callable接口中定义的call()方法既拥有返回值,也可以声明抛出异常。
代码如下:

new Thread(new FutureTask<Object >(new Callable<Object>(){    public Object call() throws Exception    {        //线程执行体    }})).start();

不仅如此,Java 5还提供了线程支持,ExecutorService对象就代表了线程池,如果开发者利用ExecutorService来启动线程,ExecutorService底层会负责管理线程池。此时,开发者只要把Runnable对象传给ExecutorService即可。如下代码:

ExecutorService pool = Executors.newFixedThreadPool(3)pool.execute(new Runnable(){    public void run()    {        //线程执行体    }   });

如果执行通过Callable方式实现的线程,则可按如下代码:

ExecutorService pool = Executors.newFixedThreadPool(3)pool.execute(new FutureTask<Object >(new Callable<Object>(){    public Object call() throws Exception    {        //线程执行体    }}));

用synchronized关键字修饰同步方法。需要指出的是,非静态的同步方法的同步监视器是this,也就是调用该方法对象,而静态的同步方法的同步监视器则是该类本身。因此使用synchronized修饰的静态方法、非静态方法的同步监视器并不相同,只有基于同一个同步监视器的同步方法、同步代码块才能实现同步。
反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被”挂起”的线程恢复运行。对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。

0 0
原创粉丝点击