一、Java异常分类与处理机制
如上图所示,异常分类分为3大类。
①由系统内部错误和系统资源耗尽等引起的严重错误异常,如虚拟机内部错误,电源断电以及病毒破坏等原因引起的错误。这类异常的特点是一旦发生,一般很难对系统进行控制和恢复。
②编程错误引发的异常,如数组越界、空指针访问以及除0等编程错误而引发的异常,其特点是数量大,并且是由于程序的错误引发的,一般可以避免。
③运行环境引发的异常,如网络连接不通、文件找不到等。其特点是只要运行环境正常,程序是可以正常运行的。
项目实战(异常try处理):
编写程序以命令行形式输入两个整数,要求计算并输出两个整数相除的商和余数。程序中必须对无命令行参数或少命令行参数引起的ArrayIndexOutOfBoundsException异常。数据格式不对引起的NumberFormatException以及除数为0引起的ArithmeticException异常分别进行捕获。
public class ExceptionDemo {
public static void main(String[] args) {
String a="",b="";
int i = 0,j = 0;
int value = 0,remain = 0;
try {
a = args[0];
b = args[1];
i = Integer.parseInt(a);
j = Integer.parseInt(b);
value = i / j;
remain = i % j;
}catch(ArrayIndexOutOfBoundsException e) {
System.out.println("没有输入命令行参数");
System.out.println(e.getMessage());
System.exit(0);
}catch(NumberFormatException e1) {
System.out.println("输入参数的格式不对");
System.out.println(e1.getMessage());
return;
}catch(ArithmeticException e2) {
System.out.println("输入的被除数为0");
System.out.println(e2.getMessage());
return;
}finally {
System.out.println("");
}
System.out.println(i+"/"+j+"的商为:"+value);
System.out.println(i+"/"+j+"的余数为:"+remain);
}
}
项目实战(异常抛出处理)
编写程序,输入10-30的数字为合法字符,当输入小于10或大于30的数字的时候抛出异常信息。
public class ThrowsDemo {
static void call(int value) throws IllegalAccessException,InterruptedException{
if(value<10) throw new IllegalAccessException();
if(value>30) throw new InterruptedException();
}
public static void main(String[] args) {
int value = (int)(Math.random()*50);
try {
call(value);
}catch(IllegalAccessException e) {
System.out.println("IllegalAccessException is caught");
}catch(InterruptedException e) {
System.out.println("InterruptedException is caught");
}finally {
System.out.println("value="+value);
}
}
}
项目实战(自定义异常)
编程求解并输出从键盘输入的浮点数平方根,并设计自定义异常来处理输入数据小于0的异常情况,并在出现异常时输出提示信息“输入了负数”。
import java.util.Scanner;
public class SqrtException extends Exception{
public SqrtException(String str) {
super(str);
}
}
class UserSqrtExceptionDemo{
static void InputNum(String str) throws SqrtException{
double value = Double.parseDouble(str);
if(value<0)
throw new SqrtException("输入了负数");
else {
System.out.println(Math.sqrt(value));
}
}
public static void main(String args[] ) {
System.out.println("请输入一个大于浮点数:");
Scanner sc = new Scanner(System.in);
String str = sc.nextLine();
try {
InputNum(str);
}catch(SqrtException e) {
System.out.println(e.getMessage());
e.printStackTrace();
}
}
}
注意:Java异常处理的一般策略
(1)非检查型异常:非检查型异常包含Error类及其直接或间接子类与RuntimeException类及其直接子类或间接子类。
对于非检查型子类,在编译过程中编译器不对齐进行检查。对于Error类异常不进行编译期检查的原因在于,一旦发生这类异常,对异常的恢复是很难或根本不可能的,因此Java认为对这类错误的检测是不必要的;而对于RuntimeException异常,由于该类异常是某种编程错误引起的,这类错误在程序中大量存在,且是完全可以人为避免的,因此编译器也不对这类异常进行检查。
(2)检查型异常:检查型是指除RuntimeException及其所有子类以外的Exception的异常子类。对于检查型异常,编译器在编译期将对其进行检查,要求程序对这类异常进行捕获处理或将其从方法中抽出,否则无法通过编译。Java这样处理检查型异常的原因在于,该类异常是由于运行环境的异常引发的,作为一个健壮的程序,必须考虑所有这类异常的情况。
二、Java输入输出与文件处理
1、输入输出流基本概念:在实际的编程中,数据输出源和输出目的地是多种多样的。如文件、控制台、内存、另一个程序或网络等。为了解决数据源和数据输出目的地的多样性带来的输入/输出操作的复杂性,并实现以一种统一的方式进行数据的输入输出编程,Java语言使用“流”来进行数据的输入输出处理。“流”是指在数据源(数据的起点)和数据宿(数据的终点)之间所建立的按顺序进行读写的数据通道。一旦在程序和数据源/数据宿之间建立了“流”,所有的输入/输出操作都转换为对“流”操作,大大降低了输入/输出操作的复杂性。
2、Java API中的java.io包中提供了许多用于输入输出处理的数据流类,从功能上可以划分为输入流和输出流两大类。输入数据时使用输入流,输出数据使用输出流。从文件中读取数据时从文件到程序的数据通道称为“输入流”,从程序中写入数据到文件中的数据通道称为“输出流”。
3、Java API中,InputStream和OutputStream两个抽象类是所有字节流类的父类,而Reader和Writer抽象类是所有字符流类的父类。4个抽象类共同构成了Java输入输出流的基础。
4、根据流的建立方式和工作原理,“流”又分为节点流和过滤流。节点流是指程序与输入源或输出宿之间直接建立的“流”,而过滤流是指以某一个节点流作为“流”的来源,并对其进行一些加工处理的“流”。过滤流能够有效的改善输入/输出操作。
①字节节点流:
使用流进行输入输出操作的基本步骤为:
A:导入相应的流类,如import java.io.*
B:创建相应的流对象。
C:使用流对象进行数据的输入输出处理。
D:关闭流以释放资源。
字节节点输入流的方法:
int read() 从字符流中读入一个字节,若流结束,返回-1。
int read(byte b[]) 从字符流中读取字节到字节数组b,返回所读字节数,若流结束,返回-1。
int read(byte b[],int off,int len) 从字节流中读取字节到字节数组b,存放位置从b[off]处开始,返回所读字节数,若流结束,则返回-1。
long skip(long n) 从输入流中跳过n个字节。
void close() 关闭输入流,释放资源。
字节节点输出流的方法:
void write(int b) 将整型数b的低8位写入输入流。
void write(byte b[]) 将字节数组写入输入流。
void write(byte b[],int off,int len) 将字节数组b中从off处开始写len个字节到输出流中。
void flush() 强制将输出流保存在缓存区中的数据写入数据流终点。
void close() 先调用 flush 然后关闭输出流释放资源。
项目:
使用字节流实现文件的拷贝,要求一次读入10个字节。
import java.io.*;
public class FileCopy {
public static void main(String[] args) throws IOException {
if (args.length < 2) {
System.out.println("请输入源文件和目标文件");
}
int i;
FileInputStream fin = null;
FileOutputStream fout = null;
try {
fin = new FileInputStream(args[0]);
} catch (FileNotFoundException e) {
System.out.println("源文件没找到");
return;
}
try {
fout = new FileOutputStream(args[1]);
} catch (FileNotFoundException e) {
System.out.println("创建目标文件错误!");
return;
}
try {
byte[] j = new byte[10];
while((i=fin.read(j))!=-1)
fout.write(j);
}catch(IOException e) {
System.out.println("文件读取错误!");
}
finally {
fin.close();
fout.close();
}
System.out.println();
}
}
②字符节点流
字符节点输入流实现、继承了Reader抽象类的方法。主要方法有:
int read 从字符流中读入一个字符,若流结束,返回-1。
int read(char ch[]) 从字符流中读取字符数组ch,返回所读字符数,若流结束,返回-1。
int read(char ch[],int off,int len) 从字符流中读取字符到字符数组ch,存放位置从ch[off]处开始,返回所读字符数,若流结束,则返回-1。
long skip(long n) 从输入流中跳过n个字符。
void close() 关闭输入流,释放资源。
字符节点输出流实现、继承了Writer类的方法,主要方法有:
void write(int c) 将字符c写入输出流。
void write(char ch[]) 将字符数组写入输出流。
void write(byte b[],int off,int len) 将字节数组b中从off处开始写len个字节到输出流中。
void flush() 强制将输出流保存在缓存区中的数据写入数据流终点。
void close() 先调用 flush 然后关闭输出流释放资源。
项目:
使用字符流实现文本文件的拷贝,同时将文本文件内容显示在屏幕上,并统计文本文件的字符数,源文件与目标文件名均以命令行参数形式提供。
import java.io.*;
public class FileCopy1 {
public static void main(String[] args) throws IOException{
if(args.length<2){
System.out.println("请输入源文件 目标文件");
return;
}
int count=0;
int ch;
FileReader fin = null;
FileWriter fout = null;
try {
fin = new FileReader(args[0]);
fout = new FileWriter(args[1]);
while((ch=fin.read())!=-1) {
count++;
System.out.print((char)ch);
fout.write(ch);
}
}catch(FileNotFoundException e) {
System.out.println("找不到源文件");
}catch(IOException e) {
System.out.println("文件读取错误");
}finally {
fin.close();
fout.close();
}
System.out.println("\n"+"共有"+count+"个单词");
}
}