注:本章节用来复习异常、IO流、序列化相关知识。
一、异常
异常是用来封装错误信息的对象;它由异常的类型、提示信息、报错的行号提示三部分组成;
1.异常的体系架构
第一层:顶级父类Throwable
第二层:Error和Exception
Error:错误类,表示系统内部错误或者资源耗尽错误,紧靠修复程序本身是不能恢复 执行的。子类有IOError 、AWTError
Exception:异常类,表示程序本身出现的错误,编译错误(编译出错或者运行出错)。
2.异常的处理方式
当程序遇到异常,通常有两种处理方式:捕获异常或者向上抛出。
-
捕获异常
try{
需要捕获的代码
}catch(异常类型 异常名){
处理方案
}
-
抛出方案
对于不想现在处理或者处理不了的异常可以选择向上抛出-----在方法上设置了抛出管道---throws异常类型
eg:void method1() throws Exception1,Exception2{}
TIPS:方法上有默认的异常管道:RuntimeException
3.throws与throw的区别
throws:用在方法声明处,其后跟着的是异常的名字,表示此方法会抛出异常,需要有本方法的调用者来处理这些异常。
throw:用在方法内部,其后跟着的是异常对象的名字,表示在此处抛出异常,由方法体内的语句处理。(执行throw一定抛出了异常)
/**本类用于异常的入门案例*/
public class ExceptionDemo {
public static void main(String[] args) {
// method1();//调用用来暴露异常的方法
//method2();//调用异常解决方案1 -- 捕获处理 --自己解决
//method3();//调用异常解决方案2 -- 向上抛出 --别人解决
f();
//f2();
f3();
/**1.10/0
* --报错ArithmeticException
* 2.10/3.5
* --报错InputMismatchException--输入不匹配异常
* */
/**1.不要害怕BUG,真正的勇士敢于直面自己写的BUG;
* 2.学会看报错的信息提示,确定自己错误的方向;
* 3.学会看报错的行号提示,确定自己报错的位置,哪里不对点哪里
* 注意:源码不会报错,要看的是自己写的代码
* */
/**异常抛出的格式:在方法的小括号与大括号之间,写throws 异常类型
* 如果有多个异常,使用逗号分隔开
*
* 如果一个方法抛出了异常,那么谁来调用这个方法,谁就需要处理这个异常
* 这里处理也有两种方案:捕获解决 或者 继续向上抛出
*
* 注意:我们一般会在main()调用方法之前,将这个方法抛出的异常处理掉
* 而不是将问题抛给main(),因为调用main()的是JVM,而后面的没人解决了,该报错还是报错
* */
}
private static void f3() {
try{
f2();
}catch (Exception e){
System.out.println("解决方案~");
}
}
//继续向上抛出异常 ---别人解决
private static void f2()throws Exception {
System.out.print("请输入第一个要计算的整数:");
int a = new Scanner(System.in).nextInt();
System.out.print("请输入要计算的第二个整数:");
int b = new Scanner(System.in).nextInt();
System.out.println(a/b);
}
private static void f() {
try{
method3();
}catch (Exception e){
System.out.println("解决方案~");
}
}
private static void method3()throws Exception {
System.out.print("请输入第一个要计算的整数:");
int a = new Scanner(System.in).nextInt();
System.out.print("请输入要计算的第二个整数:");
int b = new Scanner(System.in).nextInt();
System.out.println(a/b);
}
/* try{
可能抛出的异常代码
}catch(异常的类型 异常的名字){
万一捕获到了预先设定的异常,进行处理的解决方案
}
**/
private static void method2() {
try{
System.out.print("请输入第一个要计算的整数:");
int a = new Scanner(System.in).nextInt();
System.out.print("请输入要计算的第二个整数:");
int b = new Scanner(System.in).nextInt();
System.out.println(a/b);
}catch (ArithmeticException e){
System.out.println("除数不能为0");
}catch (InputMismatchException e){
System.out.println("请输入规定的整数!");
/**使用多态的思想,不论是什么子异常,统一看作父类型Exception
* 做出更加通用的解决方案,甚至可以只写这一个,上面那2个不写了*/
}catch (Exception e){
System.out.println("输入的数据不对,请重新输入~~");
}
}
}
二、IO流
8.1流的分类:
根据流的传输处理的单位:字节流 字符流
根据流的方向:输入流 输出流
-
字节输入流InputStream---抽象父级,无法实例化
FileInputStream ---操作文件的字节输入流,构造要:File/路径
BufferdInputStream:高效字节输入流,构造方法要:InputStream
-
字节输出流OutputStream---抽象父级,无法实例化
FileOutputStream---操作文件的字节输出流,构造函数有(File)、(File, boolean append)、(String name)、(String name, boolean append)
注意:这四种都可可以覆盖输出;
BufferedOutputStream---高效字节输出流,构造要:OutputStream
-
字符输入流Reader---抽象父级,无法实例化
FileReader---操作文件的字符输入流,构造要:File/路径
BufferedReader---高效字符输入流,构造要:Reader
-
字符输出流Writer---抽象父级,无法实例化
FileWriter---操作文件的字符输出流,构造有:(File)、(File, boolean append)、(String name)、(String name, boolean append)
BufferedWriter---高效字符输出流,构造要:Writer
import java.io.*;
/**本类用于测试字节输入流*/
public class TestIn {
public static void main(String[] args) {
method();
method2();
}
private static void method2() {
FileInputStream in = null;
try {
in = new FileInputStream("D:\\ready\\1.txt");
int b;
while ((b = in.read()) != -1){
System.out.println(b);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void method() {
//InputStream in = new InputStream();--抽象父级不可以被实例化
FileInputStream in = null;
try {
in = new FileInputStream(new File("D:\\ready\\1.txt"));
// System.out.println(in.read());//97
// System.out.println(in.read());//98
// System.out.println(in.read());//99
// System.out.println(in.read());//-1
//需求:需要循环读取文件中的所有内容,直至读完
//定义变量,保存读取到的数据
int b;
while (( b = in.read()) != -1){//只要读到的数据不为-1,说明还有数据
System.out.println(b);//就打印当前循环读到的数据
}
//关流
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
/**finally{}代码块是try-catch结果中的第三个部分
* 这个部分,不论是否扑捉到了异常,最终都会执行
* 也就是说,这是一块一定会被执行的代码块,常用于关流操作*/
}finally {
//关流---流对象使用完必须释放!
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*********************************************************************/
import java.io.*;
/**本类用于测试字节输出流*/
public class TestOut {
public static void main(String[] args) {
// method1();//用于测试普通字节输出流
method2();//用于测试高效字节输出流
}
private static void method2() {
BufferedOutputStream out = null;
try {
// out = new BufferedOutputStream(new FileOutputStream("D:\\ready\\1.txt"));
out = new BufferedOutputStream(
new FileOutputStream(new File("D:\\ready\\1.txt"),true));
out.write(99);
out.write(99);
out.write(99);
}catch (Exception e){
e.printStackTrace();
}finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void method1() {
FileOutputStream out = null;
try {
// out = new FileOutputStream(new File("D:\\ready\\1.txt"));
out = new FileOutputStream("D:\\ready\\1.txt",true);
out.write(97);
out.write(98);
out.write(99);
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**本类用于测试字符输入流*/
public class TestIn2 {
public static void main(String[] args) {
method1();//普通输入流读取
method2();//高效输入流读取
}
private static void method2() {
BufferedReader in1 = null;
try {
in1 = new BufferedReader(
new FileReader("D:\\ready\\1.txt"));
int b;
while((b = in1.read())!= -1){
System.out.println(b);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
in1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void method1() {
FileReader in = null;
try {
//FileReader in2 = new FileReader(new File("D:\\ready\\1.txt"));
in = new FileReader("D:\\ready\\1.txt");
int b;
while((b = in.read())!= -1){
System.out.println(b);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*******************************************************************/
/**本类用于测试字符输出流*/
public class TestOut2 {
public static void main(String[] args) {
//method1();//用于测试普通字符输出流
method2();//用于测试高效字符输出流
}
private static void method2() {
BufferedWriter out2 = null;
try {
// out2 = new BufferedWriter(new FileWriter("D:\\ready\\1.txt"));
out2 = new BufferedWriter(new FileWriter(new File(
"D:\\ready\\1.txt"),true));
// out2 = new BufferedWriter(new FileWriter("D:\\ready\\1.txt",true));
out2.write(98);
out2.write(98);
out2.write(98);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
out2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void method1() {
FileWriter in = null;
try{
in = new FileWriter("D:\\ready\\1.txt");
in.write(97);
in.write(97);
in.write(97);
}catch(Exception e){
e.printStackTrace();
}finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
三、序列化与反序列化
1.概述
序列化:把对象的信息保存到文件中---ObjectOutputStream
反序列化:ObjectInputStream对以前使用的 ObjectOutputStream写入的基本书记和对象进行反序列化重构对象。
- 准备Student类,注意:需要实现可序列化的接口
- 创建序列化流对象;
- 创建被序列化输出的学生类对象;
- 用流对象
注意:自定义类需要重写toString()才能查看对象的属性与属性值,否则打印地址值。
反序列化如何成功?
核心:Student类中的UID,与反序列化流中的UID保持一致。
- 一次序列化对应一次序列化;【推荐】
- 一次序列化后不修改Student中的内容,然后反序列化;
- 将Student中的UID写成固定值;
注意:反序列化流持有UID与Student类中的UID不一致时,反序列化会失败---eg:使用自动生成的UID,先序列化,然后修改Student,再来反序列化,这样就会失败。
2序列化的应用场景
- 需要序列化的文件必须实现Serializable接口,用来启动序列化功能;
- 不需要序列化的数据可以修饰成static,原因:static资源属于类资源,不随着对象被序列化输出;
- 每一个被序列化的文件都有唯一的id,如果没有添加此id,编译器会自动根据类的定义信息计算产生一个;
- 在反序列化时,如果和序列化的版本号不一致,无法完成反序列化;
- 常用与服务器之间的数据传输,序列化成文件、反序列化读取数据;
- 常用使用套接字流在主机之间传递对象;
- 不需要序列化的数据可以被修饰成transient(临时),只在程序运行期间在内存中存在,不会被序列化持久保存。
import java.io.*;
/**本类用于测试序列化与反序列化
* 序列化:
* 是指把程序中的Java对象,永久保存在磁盘中,
* 相当于是写出的过程,方向是out,对应的流是:ObjectOutputStream
* 反序列化:
* 是指把已经序列化在文件中保存的数据,读取/恢复到Java程序中的过程
* 方向是in,对应流是:ObjectInputStream
*
* */
public class TestSerializeable {
public static void main(String[] args) {
method1();//完成序列化
method2();//完成反序列化
}
private static void method2() {
ObjectInputStream in = null;
try{
in = new ObjectInputStream(
new FileInputStream("D:\\ready\\1.txt"));
System.out.println(in.readObject());
System.out.println("反序列化成功~~~");
}catch(Exception e){
System.out.println("反序列化失败~~~");
e.printStackTrace();
}finally {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**序列化方法*/
private static void method1() {
//1.声明一个在本方法中生效的局部变量,局部变量不需要初始化
ObjectOutputStream out =null;
//2.由于IO操作可能会抛出异常,所以需要完成try-catch结构
try{
//创建流对象
out = new ObjectOutputStream(
new FileOutputStream("D:\\ready\\1.txt"));
//指定要序列化的对象
Student obj = new Student("海绵宝宝",3,"海里",'男');
out.writeObject(obj);
System.out.println("序列化成功~");
}catch (Exception e){
System.out.println("序列化失败~~");
e.printStackTrace();
}finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}