当前位置: 首页 > 科技观察

看完这篇Exception和Error,和面试官扯皮就没问题了

时间:2023-03-21 01:26:23 科技观察

看完这个ExceptionandError,和面试官争论是没有问题的,仅仅依靠对Java基本概念的理解就能发现问题。但是,并不是所有的问题都能在编译时发现。有些NullPointerException和ClassNotFoundException在编译时找不到。这些异常就是RuntimeException运行时异常,这些异常往往是在运行时发现的。我们在写Java程序的时候,经常会出现两种问题,一种是java.lang.Exception,一种是java.lang.Error,这两种都是用来表示异常情况发生的。下面是对这两个概念的理解。理解异常异常位于java.lang包下。它是继承自Throwable类的顶级接口。Exception类及其子类是Throwable的组成条件,是程序出现的合理情况。在了解Exception之前,有必要先了解一下Throwable是什么。什么是ThrowableThrowable类是Java语言中所有错误和异常的父类。只能抛出继承自Throwable或其子类的类,还有一种方式是Java中带有@throw注解的类也可以抛出。在Java规范中,非检查异常和检查异常的定义如下:“非检查异常类是运行时异常类和错误类”。Checked异常类是除unchecked异常类之外的所有异常类。即检查异常类是Throwable及其除RuntimeException及其子类和Error及其子类之外的所有子类。也就是说,除了RuntimeException及其子类,error及其子类之外,其他的异常都是checkedException。那么,根据这个逻辑关系,我们就可以对Throwable及其子类进行分类分析。可以看到,Throwable处于异常和错误的最顶层。我们查看Throwable类,发现它有很多方法和属性。我们只讨论其中一些更常用的。//返回抛出异常的详细信息publicstringgetMessage();publicstringgetLocalizedMessage();//发生异常时返回简要描述publicpublicStringtoString();//将异常信息打印到标准输出流publicvoidprintStackTrace();publicvoidprintStackTrace(PrintStreams);publicvoidprintStackTrace(PrintWriters)//记录当前状态栈帧publicsynchronizedThrowablefillInStackTrace();另外,由于Throwable的父类也是Object,所以常用的方法有继承其父类的getClass()和getName()方法。CommonException让我们回到Exception的讨论。现在你知道Exception的父类是Throwable,Exception有两种异常,一种是RuntimeException;另一个是CheckedException,两者都应该被捕获。下面列出了Java中一些常见的异常及其分类。这位面试官可能还会要求您列举一些常见的例外情况并对其进行分类。RuntimeException序号异常名称异常描述序号异常名称异常描述1ArrayIndexOutOfBoundsException数组越界异常2NullPointerException空指针异常3IllegalArgumentException非法参数异常4NegativeArraySizeException数组长度为负数异常5IllegalStateException非法状态异常6ClassCastException类型转换异常UncheckedException异常名称说明1NoSuchFieldException表示没有指定名称的类抛出的异常2NoSuchMethodException表示没有指定方法的类抛出的异常3IllegalAccessException不允许访问类的异常4ClassNotFoundException类没有找到与Exception相关的Java关键字,那么Java中如何处理这些异常呢?Java中有几个关键字:throws、throw、try、finally和catch。让我们分别讨论它们。Throws和throw在Java中,异常是一个可以被程序员或应用程序抛出的对象。您必须使用throws和throw语句来定义抛出异常。throw和throw通常成对出现。例如方法体中使用staticvoidcacheException()throwsException{thrownewException();}throw语句,表示抛出异常,由方法体中的语句处理。throws语句用在方法声明之后,表示异常被抛出并由方法的调用者处理。throws主要是声明这个方法会抛出这个类型的异常,让它的调用者知道要捕获这个异常。throw是专门抛出异常的action,所以抛出的是异常实例。try、finally、catch这三个关键字主要有以下组合:try...catch、try...finally、try...catch...finally。try...catch表示捕获某段代码可能抛出的异常,如下staticvoidcacheException()throwsException{try{System.out.println("1");}catch(Exceptione){e.printStackTrace();}}try...finally表示不管一段代码的执行状态如何,finally中的代码都会走staticvoidcacheException()throwsException{for(inti=0;i<5;i++){System.out.println("enter:i="+i);try{System.out.println("execute:i="+i);continue;}finally{System.out.println("leave:i="+i);}}}try...catch...finally也是一样,意思是捕获到异常后,接着执行finally中的代码逻辑。JDK1.7使用try...with...resources优雅关闭资源Java类库中有很多资源需要通过close方法关闭。如InputStream、OutputStream、数据库连接对象Connection、MyBatis中的SqlSession会话等。作为开发者,经常会忽略资源的关闭方法,从而导致内存泄漏。根据经验,try-finally语句是确保即使出现异常或返回也会关闭资源的最佳方式。try-catch-finally一般是这样使用的仍然很整洁,但是当我们添加第二个需要关闭的资源时,就像这样newbyte[100];intn;while((n=is.read())>=0){os.write(buf,n,0);}}finally{os.close();}}finally{is.close();}}感觉这个方法变得臃肿了。而且这种写法存在很多问题。即使try-finally可以正确关闭资源,也无法阻止异常的抛出,因为try和finally块中都可能出现异常。比如你在读取的时候硬盘坏了,这时候就不能读取文件和关闭资源,这时候会抛出两个异常。但在这种情况下,第二个异常会擦除第一个异常。在异常栈中找不到第一条异常记录。我应该怎么办?捕获异常是这样的吗?staticvoidtryThrowException(Stringpath)throwsException{BufferedReaderbr=newBufferedReader(newFileReader(path));try{Strings=br.readLine();System.out.println("s="+s);}catch(Exceptione){e.printStackTrace();}finally{try{br.close();}catch(Exceptione){e.printStackTrace();}finally{br.close();}}}可以解决抛异常的问题,但是各种try-cath-finally的嵌套会让代码非常臃肿。当Java7引入try-with-resources语句时,所有这些问题都得到了解决。要使用try-with-resources语句,首先要实现AutoCloseable接口,它包含一个returnclose方法。现在Java类库和第三方类库中的很多类和接口都实现或扩展了AutoCloseable接口。如果您编写一个表示必须关闭的资源的类,则此类应实现AutoCloseable接口。Java引入了try-with-resources语句,将try-catch-finally简化为try-catch,其实是一种语法糖,在编译时会转化为try-catch-finally语句。下面是第一个使用try-with-resources的例子/***使用try-with-resources重写例子一*@parampath*@return*@throwsIOException*/staticStringfirstLineOfFileAutoClose(Stringpath)throwsIOException{try(BufferedReaderbr=newBufferedReader(newFileReader(path))){returnbr.readLine();}}使用try-with-resources重写程序的第二个例子dst)){byte[]buf=newbyte[1000];intn;while((n=in.read(buf))>=0){os.write(buf,0,n);}}}使用try-with-resources不仅让代码更容易理解,也更容易诊断。以firstLineOfFileAutoClose方法为例,如果调用readLine()和close()方法都抛出异常,则后一个异常将被抑制,保留第一个异常。(在公众号回复效率高可获取EffectiveJava第三版中文pdf)异常处理原则我们在日常的异常处理代码中应该遵循三个原则:不要捕获Exception之类的异常,而应该捕获特定的异常之类的异常,比如InterruptedException,方便排错,也可以减少别人接管你的代码时骂你的次数。不要生吞异常。这是在异常处理中要特别注意的。如果我们不抛出异常,或者不把异常输出到Logger日志中,后面的程序可能会以不可控的方式结束。不要在函数式编程中使用checkedException。什么是ErrorError是程序无法处理的错误,表明在运行应用程序时出现了严重的问题。大多数错误与代码编写者执行的操作无关,而是表示代码运行时JVM(Java虚拟机)出现问题。这些错误是无法检查的,因为它们超出了应用程序的控制和处理能力,并且大多数是程序运行时不允许出现的情况。引入Java内存模型JDK1.7。它包括两部分,由所有线程共享的数据区和线程隔离的数据区组成。在上面的Java内存模型中,只有程序计数器是不会出现OutOfMemoryError的区域。程序计数器控制计算机指令的分支和循环。、跳转、异常处理和线程恢复,程序计数器是每个线程私有的。》什么是threadprivate:表示各个线程互不影响,独立存储。如果应用程序执行的是Java方法,那么这个计数器记录的是虚拟机字节码指令的地址;如果是Native方法执行的,并且计数器值为空(Undefined)。除了程序计数器,其他区域:方法区(MethodArea)、虚拟机栈(VMStack)、本地方法栈(NativeMethodStack)和堆(Heap)都是所有可能发生OutOfMemoryError的区域虚拟机堆栈:如果线程请求的堆栈深度大于虚拟机堆栈允许的深度,就会出现StackOverflowError异常;如果虚拟机动态扩展无法申请到足够的内存,则会出现会出现OutOfMemoryError本地方法栈和虚拟机栈是一样的堆:Java堆可以是物理上不连续逻辑上连续的,就像我们的磁盘空间一样,如果没有内存在堆中完成实例分配,堆无法扩展时,会抛出OutOfMemoryError。方法区:当方法区不能满足内存分配要求时,会抛出OutOfMemoryError异常。一道经典的面试题一道很经典的面试题,NoClassDefFoundError和ClassNotFoundException有什么区别?在类加载过程中,当JVM或ClassLoader找不到对应的类时,可能会导致这两种异常/错误,由于不同的ClassLoader会从不同的地方加载类,有时这种错误是由于CLASSPATH类路径错误导致的,有时这种错误是由于缺少某个库的jar包引起的。NoClassDefFoundError表示这个类在编译时存在,但在运行时找不到,有时静态初始化块也会导致NoClassDefFoundError错误。》ClassLoader是类路径加载器,Java中有三种类路径加载器,一种是虚拟机自带的ClassLoader,分为三种启动类加载器(Bootstrap),分别负责加载$JAVAHOME/jre/lib/rt.jar扩展类加载器(Extension),负责加载$JAVAHOME/jre/lib/ext/*.jar应用类加载器(AppClassLoader),加载当前应用的类路径下的所有类其次是用户自定义类加载器Java.lang.ClassLoader的子类,用户可以自定义加载类的方式。另一方面,ClassNotFoundException与编译时无关。当你尝试使用反射加载类时运行时,会出现ClassNotFoundException简而言之,ClassNotFoundException和NoClassDefFoundError都是由于CLASSPATH中缺少类引起的,通常是因为缺少JAR文件,但是如果JVM认为应用找不到类在运行时对应引用,会抛出NoClassDefFoundError错误;当你调用了Class.forName()等代码所示的加载类但没有找到对应的类时,会抛出java.lang.ClassNotFoundException。NoClassDefFoundError是JVM引起的错误,不检查不检查。因此,不会使用try-catch或finally语句块;另外,ClassNotFoundException是checkedexception,所以需要用try-catch语句块或者try-finally语句块包围起来,否则会导致编译错误。调用Class.forName()、ClassLoader.findClass()、ClassLoader.loadClass()等方法可能引发java.lang.ClassNotFoundException,如图,NoClassDefFoundError是链接错误,发生在链接阶段,当parsingreferencecannotfound相应的类会被触发;ClassNotFoundException是运行时发生的异常。