Java面试手册:核心基础-3

1.如何把一段逗号分割的字符串转换成一个数组?

  • 用正则表达式,代码大概为:String [] result = orgStr.split(“,”);
  • 用 StringTokenizer 代码为(高逼格): StringTokenizer tokener = StringTokenizer(orgStr,”,”); String [] result = new String[tokener .countTokens()]; int i=0; while(tokener.hasNext(){ result[i++]=toker.nextToken(); }

2.数组有没有length()这个方法? String有没有length()这个方法?

  • 数组没有length()这个方法,有length的属性。String有有length()这个方法。

3.下面这条语句一共创建了多少个对象:String s="a"+"b"+"c"+"d";

  • javac编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。 第一条语句打印的结果为false,第二条语句打印的结果为true String s1 = "a"; String s2 = s1 + "b"; String s3 = "a" + "b"; System.out.println(s2 == "ab"); System.out.println(s3 == "ab"); -------------------------------------------------------- 题目中的第一行代码被编译器在编译时优化后,相当于直接定义了一个”abcd”的字符串 所以,上面的代码应该只创建了一个String对象。写如下两行代码,最终打印的结果应该为true。 String s = "a" + "b" + "c" + "d"; System.out.println(s == "abcd");

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

  • 详细的说是在,return执行中,但是还未执行的这个过程执行finally代码块,如下代码:主函数调用子函数并得到结果的过程,好比主函数准备一个空罐子,当子函数要返回结果时,先把结果放在罐子里(函数栈里),然后再将程序逻辑返回到主函数。所谓返回,就是子函数说,我不运行了,你主函数继续运行吧,这没什么结果可言,结果是在说这话之前放进罐子里的。
  • 如下代码: public static void main(String[] args) { // TODO Auto-generated method stub System.out.println(new Test().test());; } static int test(){ int x = 1; try { return x; }finally {++x; } } } 运行结果是1.

5.下面的程序代码输出的结果是多少?

  • 返回的结果是2: 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 ; } } }
  • 通过下面一个例子程序来帮助我解释这个答案,从下面例子的运行结果中可以发现,try中的return语句调用的函数先于finally中调用的函数执行,也就是说return语句先执行,finally语句后执行,所以,返回的结果是2,return并不是让函数马上返回,而是return语句执行后,将把返回结果放置进函数栈中,此时函数并不是马上返回,它要执行finally句后才真正开始返回。 在讲解答案时可以用下面的程序来帮助分析: public class Test { public static void main(String[] args) { System.out.println(new Test().test());; } int test() { try{ return func1(); }finally { return func2(); } } int func1() { System.out.println("func1"); return 1; } int func2() { System.out.println("func2"); return 2; } } -----------执行结果----------------- func1 func2 2
  • 结论:finally中的代码比return 和break语句后执行,只要JVM中没有遇到system.exition的异常,是JVM直接退出,那么finally都会被执行。

6.final, finally, finalize的区别。

  • final:用于声明属性方法,分别表示属性不可变,方法不可覆盖,类不可继承,内部类要访问局部变量,局部变量必须定义成final类型。
  • finally:是异常处理语句结构的一部分,表示总是执行,除非是遇到重大错误error,才不会执行finally。
  • finalize:是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。JVM不保证此方法总被调用。

7.运行时异常与一般异常有何异同

  • 异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常(程序在虚拟机上运行时发生的非正常状态),是一种常见运行错误。
  • java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。

8.error和exception有什么区别?

  • error:表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。
  • exception: 表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。

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

  • 异常是指java程序运行时(非编译)所发生的非正常情况或错误,Java使用面向对象的方式来处理异常,它把程序中发生的每个异常也都分别封装到一个对象来表示的,该对象中包含有异常的信息。
  • 所有异常的根类为java.lang.Throwable:Throwable下面又派生了两个子类:Error和Exception。
    • Error 表示应用程序本身无法克服和恢复的一种严重问题,程序只有死的份了(内存溢出和线程死锁等系统问题)。
    • Exception表示程序还能够克服和恢复的问题:
    1. 其中又分为系统异常和普通异常,系统异常是软件本身缺陷所导致的问题,也就是软件开发人员考虑不周所导致的问题,软件使用者无法克服和恢复这种问题,但在这种问题下还可以让软件系统继续运行或者让软件死掉。
    2. 普通异常是运行环境的变化或异常所导致的问题,是用户能够克服的问题,例如,网络断线,硬盘空间不够,发生这样的异常后,程序不应该死掉。
    3. java为系统异常和普通异常提供了不同的解决方案,编译器强制普通异常必须try..catch处理或用throws声明继续抛给上层调用方法处理,所以普通异常也称为checked异常,而系统异常可以处理也可以不处理,所以,编译器不强制用try..catch处理或用throws声明,所以系统异常也称为unchecked异常。
    4. 提示答题者:就按照三个级别去思考:虚拟机必须宕机的错误,程序可以死掉也可以不死掉的错误,程序不应该死掉的错误。

10.Throwable:异常的父类。

  • error :jvm严重错误,JVM无法继续,因此这是不可捕捉无法用程序去恢复的错误。
  • exception: 可以捕获到,可以恢复。
    • cheched exception:IO/SQL异常,JVM要求我们对出现的异常进行catch。
    • runtime exception:运行时异常,我们可以不处理,将其抛出最后可以抛给JVM处理,多线程由thread.run()抛出,单线程由main()函数抛出。运行时异常也有一般异常的子类,可以被catch到,如果不对其处理,要么线程终止,要么主线程终止(异常的处理目标就是将异常程序恢复正常)。

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

  • 在jdk doc中查RuntimeException类,就可以看到其所有的子类列表,也就是看到了所有的系统异常。
  • 我比较有印象的异常是:
    • ArrayindexOfBoundsException:数组越界异常。
    • NullPointerException:空指针异常。
    • ClassCastException:类型转换异常。
    • ClassNotFoundException:指定类不存在。
    • ArithmeticException:数字运算异常。
    • ArrayStoreException:数组存储与声明类型不兼容。
    • numberFormatException:数字格式异常。

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

  • Java通过面向对象的方法进行异常处理,把各种不同的异常进行分类,并提供了良好的接口。
  • 在Java中,每个异常都是一个对象,它是Throwable类或其它子类的实例。
  • 当一个方法出现异常后便抛出一个异常对象,该对象中包含有异常信息,调用这个对象的方法可以捕获到这个异常并进行处理。
  • 一般情况下是用try来执行一段程序,如果出现异常,系统会抛出(throws)一个异常,这时候你可以通过它的类型来捕捉(catch)它,或最后(finally)由缺省处理器来处理。
  • 在一个成员函数调用的外面写一个try语句,在这个成员函数内部写另一个try语句保护其他代码。每当遇到一个try语句,"异常"的框架就放到堆栈上面,直到所有的try语句都完成。如果下一级的try语句没有对某种"异常"进行处理,堆栈就会展开,直到遇到有处理这种"异常"的try语句。
  • Java的异常处理是通过5个关键词来实现的:try、catch、throw、throws和finally。
    • try:指定一块预防所有"异常"的程序。
    • cache:紧跟在try程序后面,用来指定想要捕捉的"异常"的类型。
    • throw:不处理异常,直接明确地抛出一个"异常",给上一层处理。
    • finally:确保一段代码不管发生什么"异常"都被执行一段代码。

13.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(){ } });
  • 反对使用stop(),是因为它不安全。它会解除由线程获取的所有锁定,而且如果对象处于一种不连贯状态,那么其他线程能在那种状态下检查和修改它们。结果很难检查出真正的问题所在。
  • suspend()方法容易发生死锁。调用suspend()的时候,目标线程会停下来,但却仍然持有在这之前获得的锁定。此时,其他任何线程都不能访问锁定的资源,除非被"挂起"的线程恢复运行。
  • 对任何线程来说,如果它们想恢复目标线程,同时又试图使用任何一个锁定的资源,就会造成死锁。所以不应该使用suspend(),而应在自己的Thread类中置入一个标志,指出线程应该活动还是挂起。若标志指出线程应该挂起,便用wait()命其进入等待状态。若标志指出线程应当恢复,则用一个notify()重新启动线程。
  • 用synchronized关键字修饰同步方法。

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

  • sleep:
    • 就是正在执行的线程主动让出cpu,cpu去执行其他线程,在sleep指定的时间过后,cpu才会回到这个线程上继续往下执行。
    • 如果当前线程进入了同步锁,sleep方法并不会释放锁,即使当前线程使用sleep方法让出了cpu,但其他被同步锁挡住了的线程也无法得到执行。
  • wait: package com.huawei.interview; public class MultiThread { public static void main(String[] args) { new Thread(new Thread1()).start(); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(new Thread2()).start(); } private static class Thread1 implements Runnable { @Override public void run() { //由于这里的Thread1和下面的Thread2内部run方法要用同一对象作为监视器,我们这里不能用this,因为在Thread2里面的this和这个Thread1的this不是同一个对象。 我们用MultiThread.class这个字节码对象当前虚拟机里引用这个变量时,指向的都是同一个对象。 } } private static class Thread2 implements Runnable { @Override public void run() { synchronized (MultiThread.class) { System.out.println("enter thread2..."); System.out.println("thread2 notify other thread can release wait status.."); //由于notify方法并不释放锁, 即使thread2调用下面的sleep方法休息了10毫秒,但thread1仍然不会执行,因为thread2没有释放锁,所以Thread1无法得不到锁。 MultiThread.class.notify(); System.out.println("thread2 is sleeping ten millisecond..."); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("thread2 is going on..."); System.out.println("thread2 is being over!"); } } } }
    • 是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行。
    • 只有其他线程调用了notify方法(notify并不释放锁,只是告诉调用过wait方法的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还在别人手里,别人还没释放。
    • 如果notify方法后面的代码还有很多,需要这些代码执行完后才会释放锁,可以在notfiy方法后增加一个等待和一些代码,看看效果),调用wait方法的线程就会解除wait状态和程序可以再次得到锁后继续向下运行。

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

  • 如果数据将在线程间共享。例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就是共享数据,必须进行同步存取。
  • 当应用程序在对象上调用了一个需要花费很长时间来执行的方法,并且不希望让程序等待方法的返回时,就应该使用异步编程,在很多情况下采用异步途径往往更有效率。

16.同步有几种实现方法?

  • 同步的实现方面有两种,分别是synchronized,wait与notify。
  • wait():使一个线程处于等待状态,并且释放所持有的对象的lock。
  • sleep():使一个正在运行的线程处于睡眠状态,是一个静态方法,调用此方法要捕捉InterruptedException异常。
  • notify():唤醒一个处于等待状态的线程,注意的是在调用此方法的时候,并不能确切的唤醒某一个等待状态的线程,而是由JVM确定唤醒哪个线程,而且不是按优先级。
  • Allnotity():唤醒所有处入等待状态的线程,注意并不是给所有唤醒线程一个对象的锁,而是让它们竞争。

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

  • 分几种情况:
    • 其他方法前是否加了synchronized关键字,如果没加,则能。
    • 如果这个方法内部调用了wait,则可以进入其他synchronized方法。
    • 如果其他个方法都加了synchronized关键字,并且内部没有调用wait,则不能。
    • 如果其他方法是static,它用的同步锁是当前类的字节码,与非静态的方法不能同步,因为非静态的方法用的是this。

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

  • 通俗来说:一个程序中可以有多条执行线索同时执行,一个线程就是程序中的一条执行线索,每个线程上都关联有要执行的代码,即可以有多段程序代码同时运行,每个程序至少都有一个线程,即main方法执行的那个线程。如果只是一个cpu,它怎么能够同时执行多段程序呢?这是从宏观上来看的,cpu一会执行a线索,一会执行b线索,切换时间很快,给人的感觉是a,b在同时执行,好比大家在同一个办公室上网,只有一条链接到外部网线,其实,这条网线一会为a传数据,一会为b传数据,由于切换时间很短暂,所以,大家感觉都在同时上网。
  • 状态:就绪,运行,synchronize阻塞,wait和sleep挂起,结束。wait必须在synchronized内部调用。调用线程的start方法后线程进入就绪状态,线程调度系统将就绪状态的线程转为运行状态,遇到synchronized语句时,由运行状态转为阻塞,当synchronized获得锁后,由阻塞转为运行,在这种情况可以调用wait方法转为挂起状态,当线程关联的代码执行完后,线程变为结束状态。

19.简述synchronized和java.util.concurrent.locks.Lock的异同?

  • 主要相同点:Lock能完成synchronized所实现的所有功能。
  • 主要不同点:Lock有比synchronized更精确的线程语义和更好的性能。synchronized会自动释放锁,而Lock一定要求程序员手工释放,并且必须在finally从句中释放。Lock还有更强大的功能,例如,它的tryLock方法可以非阻塞方式去拿锁。
  • 举例说明(对下面的题用lock进行了改写): package com.huawei.interview; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class ThreadTest { private int j; private Lock lock = new ReentrantLock(); public static void main(String[] args) { // TODO Auto-generated method stub ThreadTest tt = new ThreadTest(); for(int i=0;i<2;i++) { new Thread(tt.new Adder()).start(); new Thread(tt.new Subtractor()).start(); } } private class Subtractor implements Runnable { @Override public void run() { // TODO Auto-generated method stub while(true) { lock.lock(); try { System.out.println("j--=" + j--); }finally { lock.unlock(); } } } } private class Adder implements Runnable { @Override public void run() { while(true) { /*synchronized (ThreadTest.this) { System.out.println("j++=" + j++); }*/ lock.lock(); try { System.out.println("j++=" + j++); }finally { lock.unlock(); } } } } }

原文发布于微信公众号 - Java大联盟(javaunion)

原文发表时间:2018-10-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java 源码分析

Java 虚拟机运行时数据区

运行时数据区: Java 虚拟机的运行时数据区按照大的可以分为线程独立使用的数据区,和所有线程共享的数据区。 一.线程独立使用数据区 1.程序计数器 程序计数器...

2675
来自专栏星回的实验室

Angularjs的回调

$q.reject() 方法是在你捕捉异常之后,又要把这个异常在回调链中传下去时使用:

702
来自专栏思考的代码世界

Python基础学习06天

1474
来自专栏编程心路

Java虚拟机内存管理(二)—堆的使用

Java 虚拟机作为运行 Java 程序抽象出来的计算机,具有内存管理的能力,像内存分配、垃圾回收等这些相关的内存管理问题,Java 虚拟机都会帮我们解决,所以...

1182
来自专栏电光石火

null或空值的判断处理

1,错误用法一: if (name == "") {      //do something } 2,错误用法二: if (name.equals(""))...

17610
来自专栏前端桃园

JavaScript核心概念之执行上下文和栈

现在想改变一下写作方式,以问答的形式来讲解这些枯燥无味的知识,尽量把每一个为什么都讲透,每个知识点都不迷惑。

691
来自专栏Java 源码分析

Java 虚拟机运行时数据区

运行时数据区: Java 虚拟机的运行时数据区按照大的可以分为线程独立使用的数据区,和所有线程共享的数据区。 一.线程独立使用数据区 1.程序计数器 程序计数器...

3364
来自专栏转载gongluck的CSDN博客

Lua学习笔记

--Lua笔记-- --0.Lua开篇-- --http://www.cnblogs.com/stephen-liu74/archive/2012/06/11/...

5256
来自专栏用户画像

浅析JAVA堆内存和栈内存的区别

堆内存:https://baike.baidu.com/item/%E5%A0%86%E5%86%85%E5%AD%98/7270805?fr=aladdin

1191
来自专栏个人分享

HotSpot 自动内存管理笔记与实战

1.对象的创建 虚拟机遇到一条new指令时,首先会去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、...

1044

扫码关注云+社区