4.5 异常(Exception)
什么是异常
尽管人人希望自己身体健康,处理的事情都能顺利进行,但在实际生活中总会遇到各种状况,比如感冒发烧,工作时电脑蓝屏、死机等。同样,在程序运行的过程中,也会发生各种非正常状况,比如程序运行时磁盘空间不足、网络连接中断、被装载的类不存在等。针对这种情况,在Java语言中,引入了异常,以异常类的形式对这些非正常情况进行封装,通过异常处理机制对程序运行时发生的各种问题进行处理。
接下来就对这两个直接子类进行详细的讲解。
·Error类称为错误类,它表示Java运行时产生的系统内部错误或资源耗尽的错误,是比较严重的,仅靠修改程序本身是不能恢复执行的。举一个生活中的例子,在盖楼的过程中因偷工减料,导致大楼坍塌,这就相当于一个Error。使用java命令去运行一个不存在的类就会出现Error错误。
Exception类称为异常类,它表示程序本身可以处理的错误,在开发Java程序中进行的异常处理,都是针对Exception类及其子类。在Exception类的众多子类中有一个特殊RuntimeException类,该类及其子类用于表示运行时异常,除了此类,Exception类下所有其他的子类都用于表示编译时异常。
try…catch和finally
>Java中提供了一种对异常进行处理的方式--异常捕获。异常捕获通常使用 try….catch语句。
其中在try代码块中编写可能发生异常的Java语句,catch代码块中编写针对异常
进行处理的代码。当try代码块中的程序发生了异常,系统会将这个异常的信息封装成一个异常对象,并将这个对象传递给catch代码块。catch代码块需要一个参数指明它所能够接收的异常类型,这个参数的类型必须是Exception类或其子类。
throws关键字
>在上一小节学习的文件4-35Example22.java中,由于调用的是自己写的divide()方法,因此很清楚该方法可能会发生异常。试想一下,如果去调用一个别人写的方法时,是否能知道别人写的方法是否会有异常呢?这是很难做出判断的。针对这种情况,Java中允许在方法的后面使用throws关键字对外声明该方法有可能发生的异常,这样调用者在调用方法时,就明确地知道该方法有异常,并且必须在程序中对异常进行处理,否则编译无法通过。
>throws关键字声明抛出异常的语法格式如下:
修饰符 返回值类型方法名([参数1,参数2...1)throws iceptionTypel[,ExceptionType2……1{*
>从上述语法格式中可以看出,throws关键字需要写在方法声明的后面,throws
后面需要声明方法中发生异常的类型,通常将这种做法称为方法声明抛出一个
运行时异常和编译时异常
在实际开发中,经常会在程序编译时产生一些异常,而这些异常必须要进行处理,这种异常被称为编译时异常,也称为checked异常。另外还有一种异常是在程序运行时产生的,这种异常即使不编写异常处理代码,依然可以通过编译因此被称为运行时异常,也称unchecked异常。接下来就分别对这两种异常进行详细的讲解。
1.编译时异常
>在Java中,Exception类中除了RuntimeException类及其子类都是编译时异常。编译时异常的特点是Java编译器会对其进行检查,如果出现异常就必须对异常进行处理,否则程序无法通过编译。处理编译时期的异常有两种方式:
使用try…catch语句对异常进行捕获
使用throws关键字声明抛出异常,调用者对其处理。
2.行时异常
RuntimeException类及其子类都是运行时异常。运行时异常的特点是Java编译器不会对其进行检查,也就是说,当程序中出现这类异常时,即使没有使用 trv..catch语句捕获或使用throws关键字声明抛出,程序也能编译通过。运行时异常一般是由程序中的逻辑错误引起的,在程序运行时无法恢复。比如通过数组的角标访问数组的元素时,如果超过了数组的最大角标,就会发生运行时异常,代码如下所示:
int[] arr=new int[5];
System.out.println(arr[6]);
>上面代码中,由于数组arr的length为5,最大角标应为4,当使用arr[6]访问数组中的元素就会发生数组角标越界的异常。
自定义异常
>JDK中定义了大量的异常类,虽然这些异常类可以描述编程时出现的大部分异常情况,但是在程序开发中有时可能需要描述程序中特有的异常情况,例如文件4-38中在divide()方法中不允许被除数为负数。为了解决这个问题,在Java中允许用户自定义异常,但自定义的异常类必须继承自Exception或其子类。
在实际开发中,如果没有特殊的要求,自定义的异常类只需继承Exception类,在构造方法中使用super()语句调用Exception的构造方法即可。
既然自定义了异常,那么该如何使用呢?这时就需要用到throw关键字,throw关键字用于在方法中声明抛出异常的实例对象,其语法格式如下:
throw Exception异常对象
>在Java中,针对类、成员方法和属性提供了四种访问级别,分别是private、
default、protected和public。接下来通过一个图将这四种控制级别由小到大依次列出,如下图所示。
>上图中展示了Java中的四种访问控制级别,具体介绍如下:
·private(类访问级别):如果类的成员被private访问控制符来修饰,则这个成员只能被该类的其他成员访问,其他类无法直接访问。类的良好封装就是通过private关键字来实现的。
·default(包访问级别):如果一个类或者类的成员不使用任何访问控制符修饰,则称它为默认访问控制级别,这个类或者类的成员只能被本包中的其他类访问。
protected(子类访问级别):如果一个类的成员被protected访问控制符修饰,那么这个成员既能被同一包下的其他类访问,也能被不同包下该类的子类访问。
public(公共访问级别):这是一个最宽松的访问控制级别,如果一个类或者类的成
员被public访问控制符修饰,那么这个类或者类的成员能被所有的类访问,不管访问类与被访问类是否在同一个包中。
>接下来通过一个表将这四种访问级别更加直观的表示出来,如下表所示。
5.1 String类和StringBuffer类
>在应用程序中经常会用到字符串,所谓字符串就是指一连串的字符,它是由许多单个字符连接而成的,如多个英文字母所组成的一个英文单词。字符串中可以包含任意字符,这些字符必须包含在一对双引号“”之内,例如“abc”I。在Java中定义了String和StringBuffer两个类来封装字符串,并提供了一系列操作字符串的方法,它们都位于java.lang包中,因此不需要导包就可以直接使用。接下来将针对String类和 StringBuffer类进行详细讲解。
String类的初始化
>在操作String类之前,首先需要对String类进行初始化,在Java中可以通过以下两种方式对String类进行初始化,具体如下:
1、使用字符串常量直接初始化一个String对象,具体代码如下:
String str1 = "abc";
2、使用String的构造方法初始化字符串对象,String类的构造方法如下表所示。
String类的常见操作
>String类在实际开发中的应用非常广泛,因此灵活地使用String类是非常重要的,接下来介绍String类常用的一些方法
StringBuffer类
>由于字符串是常量,因此一旦创建,其内容和长度是不可改变的。如果需要对一个字符串进行修改,则只能创建新的字符串。为了便于对字符串进行修改,在JDK中提供了一个StringBuffer类(也称字符串缓冲区)StringBuffer类和 String类最大的区别在于它的内容和长度都是可以改变的。StringBuffer类似一个字符容器,当在其中添加或删除字符时,并不会产生新的StringBuffer对象。
>针对添加和删除字符的操作,StringBuffer类提供了一系列的方法,具体如下表
System类
>System类对读者来说并不陌生,因为在之前所学知识中,需要打印结果时,使用的都是“Systemoutprintln();”语句,这句代码中就使用了System类。System类定义了一些与系统相关的属性和方法,它所提供的属性和方法都是静态的,因此,想要引用这些属性和方法,直接使用System类调用即可。System类的吊
用方法如下表所示。
3. arraycopy(Object src,int srcPos,Object dest,int destPos,int length)
>arraycopy()方法用于将一个数组中的元素快速拷贝到另一个数组。其中的参数具体作用如下:
·src:表示源数组。
·dest:表示目标数组。
·srcPos:表示源数组中拷贝元素的起始位置。
·destPos:表示拷贝到目标数组的起始位置。
·length;表示拷贝元素的个数。
Runtime类
>Runtime类用于表示虚拟机运行时的状态,它用于封装JVM虚拟机进程。每次使
用iava命令启动虚拟机都对应一个Runtime实例,并且只有一个实例,因此该类采用单例模式进行设计,对象不可以直接实例化。
Math类
>Math类是数学操作类,提供了一系列用于数学运算的静态方法,包括
求绝对值、三角函数等。Math类中有两个静态常量PI和E,分别代表数学常量π和e。
>由于Math类比较简单,因此初学者可以通过查看API文档来学习Math类的具体用法。
Random类
>在JDK的javautil包中有一个Random类,它可以在指定的取值范围内随机产生数
字。在Random类中提供了两个构造方法,具体如下表所示。
>表中列举了Random类的两个构造方法,其中第一个构造方法是无参的,通过
它创建的Random实例对象每次使用的种子是随机的,因此每个对象所产生的随机数不同。如果希望创建的多个Random实例对象产生相同序列的随机数,
math类和random类
>相对于Math的random()方法而言,Random类提供了更多的方法来生成各种伪
随机数,不仅可以生成整数类型的随机数,还可以生成浮点类型的随机数,表中列举了Random类中的常用方法。
>表中,列出了Random类常用的方法,其中,Random类的nextDouble()方法返回的是0.0和1.0之间double类型的值,nextFloat()方法返回的是0.0和1.0之间float类型的值,nextlnt(intn)返回的是0(包括)和指定值n(不包括)之间的值。
>在Java中,很多类的方法都需要接收引用类型的对象,此时就无法将一个基本数据类型的值传入。为了解决这样的问题,JDK中提供了一系列的包装类,通过这些包装类可以将基本数据类型的值包装为引用数据类型的对象。在Java中,每种基本类型都有对应的包装类,具体如下表。
5.4包装类
>表中,列举了8种基本数据类型及其对应的包装类。其中,除了Integer和Character类,其它包装类的名称和基本数据类型的名称一致,只是类名的第一个字母需要大写。
>包装类和基本数据类型在进行转换时,引入了装箱和拆箱的概念,其中装箱是指将基本数据类型的值转为引用数据类型,反之,拆箱是指将引用数据类型的对象转为基本数据类型。
>Integer类除了具有Object类的所有方法外,还有一些特有的方法,如下表所示。
>列举了Integer的常用方法,其中的intValue()方法可以将Integer类型的值转为int类型,这个方法可以用来进行拆箱操作。
5.5 JDK新特性--switch语句支持字符串类型