Java OOP 9 Java I/O

Java OOP 9 Java I/O

一、学习目标
  1. 会使用File类操作文件和目录
  2. 掌握IO流的基本概念和分类
  3. 掌握使用字节流读写文本文件
  4. 掌握使用字符流读写文本文件
  5. 掌握使用缓冲流读写文本文件
  6. 掌握使用数据操作流读写二进制文件
  7. 掌握使用对象流实现序列化和反序列化
二、文件

什么是文件?

  • 文件可认为是相关记录或放在一起的数据的集合

文件一般存储在哪里?

在这里插入图片描述

Java程序如何访问文件?

  • Java API :java.io.File
三、File类
  • File类是java.io包下代表操作与平台无关的文件和目录的类
  • 实现对文件或目录的新建、删除、重命名等操作

在这里插入图片描述

  • 一个File类的对象可以代表一个文件或目录
  • 可获取与文件相关的信息,如名称、修改日期、文件大小等

语法:

public File(String pathName);//pathName:文件路径
  • 绝对路径: 以根目录开头的完整路径

  • 相对路径: 相对于当前目录文件的路径

“.”表示当前目录

“…”表示上级目录

注意事项: Windows文件路径名中,分隔符可以使用正斜杠“/”,也可以使用反斜杠“\”。但必须写成“\\ ”,其中第一个“\”表示转义符

示例:

File file = new File("E:\\test.txt");//创建File类对象时,必须设置文件路径
  • File类的常用方法
方法名描述
boolean createNewFile()创建新文件
boolean delete()删除文件
boolean exists()判断文件是否存在
Boolean isFile()判断是否是文件
boolean isDirectory()判断是否是目录
long length()返回文件长度,单位为字节 若文件不存在,则返回0L
String getPath()返回此对象文件名所对应的路径
String getAbsolutePath()返回此对象表示的文件的绝对路径名

File类应用示例1

在程序当前目录下创建非空文件test.txt,使用File类方法对该文件进行基本判断和操作

package com.aiden.io.demo01;

import java.io.File;

public class FileTest {
 public static void main(String[] args) {
     File file = new File("E:\\BDQN\\BCSP\\JavaCode\\T147JavaOOP\\JavaOOP09\\src\\test.txt");
     if (file.exists()) {
         if (file.isFile()) {
             System.out.println("文件名:" + file.getName() + ",文件长度:" + file.length() + "字节。");
             System.out.println("文件路径是:" + file.getPath());
             System.out.println("文件绝对路径是:" + file.getAbsolutePath());
         }
         if (file.isDirectory()) {
             System.out.println("此文件是目录");
         }
     } else {
         System.out.println("此文件不存在!");
     }
 }
}

运行结果

文件名:test.txt,文件长度:5字节。
文件路径是:E:\BDQN\BCSP\JavaCode\T147JavaOOP\JavaOOP09\src\test.txt
文件绝对路径是:E:\BDQN\BCSP\JavaCode\T147JavaOOP\JavaOOP09\src\test.txt

File类应用示例2

package com.aiden.io.demo02;

import java.io.File;
import java.io.IOException;

public class FileUtil {
    //创建文件
    public void createFile(File file) {
        if (file.exists()) {
            file.delete();
        } else {
            try {
                file.createNewFile();   //创建文件
                System.out.println("文件创建成功!");
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }
    }

    //删除文件
    public void deleteFile(File file) {
        if (file.exists()) {
            file.delete();
            System.out.println("文件删除成功!");
        }
    }

    //输出文件信息
    public void print(File file) {
        if (file.exists()) {
            if (file.isFile()) {
                System.out.println("文件名:" + file.getName() + ",文件长度:" + file.length() + "字节。");
                System.out.println("文件路径是:" + file.getPath());
                System.out.println("文件绝对路径是:" + file.getAbsolutePath());
            }
            if (file.isDirectory()) {
                System.out.println("此文件是目录");
            }
        } else {
            System.out.println("此文件不存在!");
        }
    }
}

测试类

package com.aiden.io.demo02;

import java.io.File;

public class Test {
    public static void main(String[] args){
        FileUtil test = new FileUtil();
        File file = new File("D:\\doc\\test.txt");
        test.createFile(file);//创建文件
        test.print(file);//输出txt文件中的信息
        test.deleteFile(file);//删除
    }
}
四、IO流
  • File类能够实现对文件和目录的创建、删除等基础性操作,但是无法实现对文件的读写操作。如何读写文件?

  • IO流是指一连串流动的字符,以先进先出方式发送信息的通道

    • I:input,指读入操作
    • O:output,指写出操作
  • Java把所有流类型都封装到java.io包中,以实现输入/输出操作

    输入流

    在这里插入图片描述

    输出流

    在这里插入图片描述

五、Java流的分类
Java流的分类
  1. 按流向划分:输入流输出流
  2. 按处理单元划分:字节流字符流
  3. 按流的角色划分:节点流处理流

按流向划分:输入流和输出流

输入流

  • 只能从中读取数据,而不能写入数据的流,实现程序从数据源中读数据

在这里插入图片描述

输出流

  • 只能向其写入数据,而不能从中读数据的流,实现程序向目标数据源中写数据

在这里插入图片描述

输入流主要由InputStream和Reader作为基类

输出流则主要由OutputStream和Writer作为基类

都是抽象类,无法直接实例化对象

注意事项

​ 按照流的流向进行分类时,输入流完成数据读入操作,输出流完成数据写出操作,这里的“出”和“入”一定是从程序运行所在内存的角度来论述。


按处理单元划分:字节流和字符流

字节流

  • 以8位字节为操作数据单元的流,可操作二进制数据
  • 可细分为字节输入流、字节输出流

字符流

  • 以16位字符为操作数据单元的流,可操作文本数据
  • 可细分为字符输入流、字符输出流

在这里插入图片描述

区别

  • 操作的数据单元不同
  • 使用方法几乎相同

按流的角色划分:节点流和处理流

节点流

可以直接向一个特定的存储介质(如磁盘、文件)读写数据的流
使用节点流进行读写数据操作时,程序直接连接到数据源

在这里插入图片描述

处理流

对已存在的流进行连接和封装,通过封装后的流实现数据读写操作的流
使用处理流进行读写操作时,程序并不会直接连接到实际的数据源

在这里插入图片描述

使用处理流包装节点流,通过处理流执行输入和输出功能,让节点流与文件或磁盘等存储设置交互,可隐藏底层节点流的差异

六、字节流
1、什么是字节流?

具有输入和输出操作

主要操作byte类型数据

基类

  • 字节输出流类: OutputStream
  • 字节输入流类: InputStream

2、字节输出流的基类:OutputStream

是抽象类,必须使用该类的子类进行实例化对象

常用方法

方法描述
void close()关闭输出流
void flush()刷新缓冲区
void write(byte[] b)将每个byte数组写入数据流
void write(byte[] b,int off,int len)将每一个指定范围的byte数组写入数据流
void write(int b)将一个字节数据写入数据流

如果需要操作文件,则使用FileOutputStream实例化


3、字节输出流FileOutputStream类

使用OutputStream类的FileOutputStream子类向文本文件写入数据

常用构造方法

方法描述
FileOutputStream( File file)创建向指定File对象写数据的文件输出流 file:指定目标文件的对象
FileOutputStream( String name)创建向指定路径的文件写数据的文件输出流 name:指定目标文件的路径字符串
FileOutputStream( String name, boolean append)创建一个向指定路径的文件写入数据的文件输出流 name:指定目标文件的路径字符串 append:表示是否在文件末尾追加数据。如果为true,则表示可以在文件末尾追加数据

创建文件输出流对象的常用方式

使用FileInputStream类按多字节读取数据//方式一:使用File对象构造对象
File file = new File("D:\\mydoc\\test.txt");
OutputStream fos = new FileOutputStream(file);
//方式二:使用文件路径构造对象
OutputStream fos = new FileOutputStream("D:\\doc\\test.txt");
//方式三:使用文件路径构造对象,且可向文件末尾追加数据
OutputStream fos = new FileOutputStream("D:\\doc\\test.txt"true);

4、字节输出流FileOutputStream类应用示例

需求:

  • 使用字节输出流向D:/doc/test.txt文件中写入字符串“I love Java! ”

实现步骤:

  1. 导入相关的类
  2. 构造文件输出流FileOutputStream对象
  3. 把数据写入文本文件
  4. 关闭文件输出流对象

关键代码

//1、导入相关的类
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
//2、构造文件输出流
OutputStream fos = null;
fos = new FileOutputStream("D:\\doc\\test.txt", true);
//3、将数据写入文本文件中
//准备一个字符串
String str = "I love Java!";
//将字符串转换为byte数组
byte[] words = str.getBytes();   
fos.write(words, 0, words.length);
System.out.println("文件已写入成功!");
//4、关闭文件输出流对象
if(fos != null) {
	fos.close();
}

注意事项

  • 创建输出流对象、执行写操作、关闭输出流时都可能会有IOException异常发生,因此,除处理FileNotFoundException异常外,还需处理IOException异常。
5、字节输入流的基类:InputStream
  • 从文件中读数据,与OutputStream一样,也是抽象类

常用方法

方法描述
int read()读取一个字节数据
int read(byte[] b)将数据读取到字节数组中
int read(byte[] b,int off,int len)从输入流中读取最多len长度的字节,保存到字节数组中,保存的位置从off开始
void close()关闭输入流
int available()返回输入流读取的字节数
6、字节输入流FileInputStream类

使用InputStream类的FileInputStream子类实现文本文件内容的读取

常用构造方法

方法描述
FileInputStream(File file)用于创建从指定File对象读取数据的文件输入流 file:指定目标文件数据源对象
FileInputStream(String name)用于创建从指定路径的文件读取数据的文件输入流 name:指定目标文件数据源路径字符串

创建文件输入流对象的常用方式

//方式一:使用File对象构造对象
File file = new File("D:\\doc\\test.txt");
InputStream fis = new FileInputStream(file);
//方式二:使用文件路径构造对象
InputStream fis = new FileInputStream("D:\\doc\\test.txt");

从文件读取到计算机内存中的过程

在这里插入图片描述

read()方法

  1. 每次读取一个字节(0~255的整数)
  2. 每次读取多个字节

读取文件中全部数据

while( (data=fis.read()) != -1 ) {
   System.out.print((char)data);
}

注意事项

  1. 按字节读取并显示数据时需进行强制类型转换
  2. 使用read()读取文件中的数据时,当返回结果为-1时,即输入流已经读到末尾
  3. 在创建输入流对象、读取数据、关闭流时必须进行异常处理
7、使用FileInputStream类按单字节读取数据

从D:/doc/test.txt文件以字节为单元读取数据,输出到控制台

实现步骤:

  1. 导入相关类
  2. 构造文件输入流FilelnputStream对象
  3. 读取文本文件中的数据
  4. 关闭文件输入流对象
//1、导入相关类
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
//2、构造文件输入流
fis= new FileInputStream("D:\\doc\\test.txt");
System.out.println("可读取的字节数:"+ fis.available());
//3、读取文本文件中的数据
int data = 0;
System.out.println("文件的内容:");
while((data = fis.read()) != -1) {
 	System.out.print((char)data + " ");
}
//4、关闭文件输入流对象
if(fos != null) {
	fos.close();
}
8、使用FileInputStream类按多字节读取数据

需求:D:/doc/test.txt文件以多字节为单元读取数据,输出到控制台

分析:

  • 使用read()重载方法,实现每次读取多个字节,如:read(byte[] b)

关键代码

fis= new FileInputStream("D:\\doc\\test.txt");
//执行读操作
byte[] words = new byte[1024];
int len = 0;//实际读取长度
while( (len = fis.read(words)) > 0 ) {//读取数据,获取实际长度
	//按实际长度输出数据,避免出现多余的空白
    System.out.println(new String(words, 0, len ));
}

常见错误

假设 D:/doc/test.txt文件

l love Java!l love Java!
—种新技术—旦开始流行,你要么坐上压路机,要么成为铺路石。

说出下面代码的执行结果

fis= new FileInputStream("D:\\doc\\test.txt");
byte[] words = new byte[21];//长度是21字节数组,不能一次读取全部数据
int len = 0;
while( (len = fis.read(words)) > 0 ) {
    System.out.println(new String(words, 0, len ));
}

结论:

  1. 文本文件保存时,采用GBK编码方式,每个中文占2个字节
  2. read()方法一次读取1个字节(半个中文字符)
  3. 当数组长度不足时,可能导致中文乱码
七、字符流
1、问题:
  • 一个字符占用内存的两个字节
  • 如果用字节流处理文本文件,需要将字节转换成字符,会降低程序的执行效率,怎么办?
    • 当输入和输出是文本文件时,尽量使用字符流
    • 使用Reader类和Writer类操作字符
  • 基类
    • 字符输出流类:Writer
    • 字符输入流类:Reader
  • 使用字符流读写文本更合适

2、字符输出流的基类:Writer
  • 抽象类

  • 常用方法

    方法描述
    void write(String str)将str字符串中包含的字符输出到输出流中
    void write(String str, int off, int len)将字符串中从off位置开始,长度为len的字符输出到输出流中
    void close()关闭输出流
    void flush()刷新输出流

3、字符输出流FileWriter类

概念:

  • FileWriter类是Writer类的子类
  • 以字符为数据处理单元向文本文件中写数据

示例:

  • 将字符串写入D:/doc/mysaying.txt文件

实现步骤:

  1. 导入相关的类
  2. 创建FileWriter对象
  3. 使用write方法向文本文件写数据
  4. 清空流 关闭流
//1、导入相关的类
import java.io.Writer;
import java.io.FileWriter;
import java.io.IOException;
Writer fw = null;
try {
     //2.创建FileWriter对象
     fw = new FileWriter("D:\\doc\\mysaying.txt");
     //3.使用write方法向文本文件写数据
     fw.write("预测未来的最好方式就是创造未来!");
     fw.flush();//刷新缓冲区
     System.out.println("文件写入成功!");
} catch (IOException ex) {
    //....
} finally {
     //4.清空流 关闭流
     if(fw!=null) {
         try {
                 fw.close();
             } catch(IOException ex) {
                 ex.printStackTrace();
             }
     }
}

4、字符输入流基类:Reader
  • 是抽象类

  • 常用方法

    方法描述
    int read()从输入流中读取单个字符,返回所读取的字符数据
    int read(char[] c)从输入流中最多读取c.length个字符数据,并将其存储在字符数组c中,返回实际读取的字符数
    int read(char[] c, int off, int len)从输入流中最多读取len个字符的数据,并将其存储在字符数组c中 存入数组c中时,并不是从数组起点开始,而是从off位置开始,返回实际读取的字符数

5、字符输入流FileReader类
  • FileReader是Reader类的子类

需求:

  • 从“D:/doc/诗词.txt”中读取数据,显示到控制台

实现步骤:

  1. 导入相关的类
  2. 创建FileReader对象
  3. 循环使用read()方法读取文件中数据
  4. 关闭流
import java.io.FileReader;
import java.io.Reader;
import java.io.FileNotFoundException;
import java.io.IOException;
Reader fr = null;
try {
    //创建对象
	fr = new FileReader("D:\\doc\\诗词.txt");
    char[] ch = new char[1024]; 
    int len = 0;
    //读取数据
    while((len = fr.read(ch)) > 0) {
         System.out.println(new String(ch,0,len));
    }
}catch (FileNotFoundException ex) { 
    //省略异常处理代码
}finally {
    //关闭流
	fr.close();
}
八、缓冲流

1、问题

  • 如何提高读写文件数据的执行效率?

    • java.io包提供了缓冲流
  • Java缓冲流自身并不具有IO功能,只是在别的流上增加缓冲,以提高程序性能

  • 分类

    • 字节缓冲流
    • 字符缓冲流
      • BufferedWriter
      • BufferedReader

    在这里插入图片描述

  • 这里我们以字符缓冲流为例,介绍缓冲流的用法

  • BufferedReader类的缓冲区(它可以先把一批数据读到缓冲区,接下来的读操作都是从缓冲区内获取数据,避免每次都从数据源读取数据进行字符编码转换,从而提高读取操作的效率)

2、BufferedWriter类
  • 是Writer类的子类
  • 带有缓冲区
  • 默认情况下,只有缓冲区满的时候,才会把缓冲区的数据真正写到目的地,能减少物理写数据的次数,提高输入/输出操作的执行效率
  • 常用的构造方法
方法描述
BufferedWriter(Writer out)创建一个缓冲字符输出流
  • 使用FileWriter类与BufferedWriter类,可提高字符流写文本文件的效率
3、使用 BufferedWriter 写文件

示例:

  • 使用BufferedWriter类对象向文本文件D:/doc/buff.txt中写数据

实现步骤:

  1. 导入相关的类
  2. 构造FileWriter对象和BufferedWriter对象
  3. 调用write()方法写数据
  4. 清空、关闭流对象
//1.导入相关的类
import java.io.FileWriter ;
import java.io.BufferedWriter ;
import java.io.IOException;
Writer fw = null;
BufferedWriter bw = null;
try {
  //2.构造FileWriter对象和BufferedWriter对象
  fw = new FileWriter("D:\\doc\\buff.txt");
  bw = new BufferedWriter(fw); 
  //3.调用write()方法写数据
  bw.write("亲爱的小伙伴们:");
  bw.newLine();//插入一个换行符
  bw.write("让我们来使用缓冲流让程序加速吧!");
  bw.flush();//刷新缓冲区
  System.out.println("文件写入成功!")
}cath(Exception e){
    //...
}
//4.清空、关闭流对象
bw.close();
fw.close();

4、BufferedReader类
  • BufferedReader类是Reader类的子类

  • 带有缓冲区,提高文件读取的效率

  • 把一批数据读到缓冲区

  • 从缓冲区内获取数据

  • 常用构造方法

    方法描述
    BufferedReader(Reader in)创建一个缓冲字符输入流
  • readLine()方法

    • 按行读取文件中数据

示例:

  • 读取文本文件D:/doc/buff.txt中数据,并输出

实现步骤:

  1. 导入相关的类
  2. 构造FileReader对象和BufferedReader对象
  3. 调用readLine()方法读取数据
  4. 清空、关闭流对象

代码实现:

//1.导入相关的类
import java.io.FileReader;
import java.io. BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
//2.构造FileReader对象和BufferedReader对象
Reader fr = null;                            
BufferedReader br = null; 
//省略try块 
 fr = new FileReader("D:\\doc\\buff.txt");
 br = new BufferedReader(fr);
//3.调用readLine()方法读取数据 
String line = br.readLine();             
 while(line != null) {
     System.out.println(line);            
     line = br.readLine();
 }
//4.清空、关闭流对象
if(br != null)  br.close();                      
if(fr != null)  fr.close();
九、数据操作流
1、数据操作流程简介
  • 数据操作流主要实现对二进制文件读写操作
  • 分类
    • DataOutputStream类
      • OutputStream类的子类
      • 与FileOutputStream类结合使用写二进制文件
    • 使用DataOutputStream类写二进制文件的实现步骤与FileOutputStream类写文本文件相似
    • DataInputStream类
      • InputStream类的子类
      • 与FileInputStream类结合使用读取二进制文件
    • 使用DataInputStream类读取二进制文件的实现步骤与FileInputStream类读取文本文件相似

2、DataOutputStream类
  • 按照与平台无关的方式向流中写入基本数据类型的数据

    • 如int、float、long、double和boolean等
  • 使用writeUTF()方法能写入采用utf-8字符编码的字符串

    FileOutputStream out1 = new FileOutputStream("D:\\doc\\test.txt");		
    BufferedOutputStream out2 = new BufferedOutputStream(out1);
    DataOutputStream out = new DataOutputStream(out2);
    out.writeByte(1);
    out.writeLong(2);
    out.writeChar('c');
    out.writeUTF("hello");
    

3、DataInputStream类
  • 按照与平台无关的方式从流中读取基本数据类型的数据

    • 如int、float、long、double和boolean等
  • 使用readUTF( )方法能读取采用utf-8字符编码的字符串

    FileInputStream in1 = new  FileInputStream("D:\\doc\\test.txt");		
    BufferedInputStream in2 = new BufferedInputStream(in1);
    DataInputStream in = new DataInputStream(in2);
    System.out.println(in.readByte());
    System.out.println(in.readLong());
    System.out.println(in.readChar());
    System.out.println(in.readUTF());
    
4、使用数据操作流读写二进制文件

示例:

  • 读取二进制文件D:/doc/User.class数据,写入二进制文件D:/doc/newUser.class

核心代码:

DataOutputStream dos = null;
DataInputStream dis = null;
FileInputStream fis= null;
FileOutputStream fos=null;
try {
    //创建输入流文件
    fis = new FileInputStream("D:\\doc\\User.class");
    dis = new DataInputStream(fis);
    //创建输出流文件
    fos = new FileOutputStream("D:\\doc\\newUser.class");
    dos = new DataOutputStream(fos);
    int temp;
    while(((temp=dis.read()) != -1)) {//读取文件中二进制数据
        fos.write(temp);//向文件写二进制数据	
    }
} catch(Exception ex) {
    ex.printStackTrace();
} finally {
     //省略异常处理代码
     dis.close();
     fis.close();
     fos.close();
     dos.close();
}
十、序列化与反序列化

问题:

  • 为每个对象属性一一编写读写代码,过程很繁琐且非常容易出错,如何解决?

方案:

  • 使用序列化和反序列化,方便数据传输和存储

概念:

  • 序列化是将对象的状态存储到特定的存储介质中的过程,

  • 反序列化是将特定的存储介质中数据重新构建对象的过程

    在这里插入图片描述

    对象输出流( ObjectOutputStream ):实现序列化
    对象输入流( ObjectInputStream):实现反序列化


1、使用ObjectOutputStream类实现序列化
  • 序列化的对象所属类须为可序列化的类

  • 一个类实现java.io.Serializable接口,该类的对象是可序列化的

    //一个标识接口
    public interface Serializable{}
    
  • JDK1.8类库中,有些类实现了java.io.Serializable接口

    • 如:String类、包装类和日期时间类等
  • 创建一个Person类,并标记该类的对象是可序列化的

    import java.io.Serializable;
    public class Person implements Serializable {
        private String name;
        private int age;
        private String sex;
        //省略getter/setter
        public void print() {
            System.out.println("姓名:"+this.name+",年龄:"+this.age+",性别:"+this.sex+"。");
        }
    }
    
  • 常用方法

    方法描述类型
    ObjectOutputStream(OutputStream out)创建对象输出流对象构造方法
    final void writeObject(Object obj)将指定对象写入流实例方法

    如何解决序列化和反序列化的版本不一致问题?

  • 引入serialVersionUID常量

  • serialVersionUID常量为long类型

  • JVM在编译时自动生成serialVersionUID常量,也可显式定义

  • 在Person类中显式定义serialVersionUID常量

    import java.io.Serializable;
    public class Person implements Serializable {
        //serialVersionUID常量
        private static final long serialVersionUID=1L;
        
        private String name;
        private int age;
        private String sex;
        public void print() {
            System.out.println("姓名:"+this.name+",年龄:"+this.age+",性别:"+this.sex+"。");
        }
    }
    

序列化对象示例

实现步骤:

  1. 导入相关的类
  2. 创建可序列化的类,要求实现Serializable接口
  3. 创建对象输出流(ObjectOutputStream)
  4. 输出可序列化对象
  5. 关闭对象输出流
//1.导入相关的类
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
//2.创建可序列化的类,要求实现Serializable接口
public class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    //…
}
//3.创建对象输出流(ObjectOutputStream)
ObjectOutputStream oos = null;
//省略异常处理代码
oos = new ObjectOutputStream(
new FileOutputStream("D:\\doc\\person.bat"));
//4.输出可序列化对象
Person person = new Person("杰米", 25, "男");
oos.writeObject(person);//对象序列化,写入输出流
//5.关闭对象输出流
if(oos!=null) {
	//省略异常处理代码
	oos.close();
}

序列化集合示例

ObjectOutputStream oos = null;
try {
    oos = new ObjectOutputStream(new FileOutputStream("D:\\doc\\persons.bat"));
    Person person = new Person("杰米", 25, "男");
    Person person2 = new Person("Lisa", 30, "女");
    ArrayList<Person> list = new ArrayList<Person>();
    list.add(person);
    list.add(person2);
    oos.writeObject(list);//集合对象序列化
    System.out.println("序列化成功!");
}catch(IOException ex){
    ex.printStackTrace();
}finally {
    if(oos!=null){
         try{
             oos.close();
         }catch(IOException ex){
             ex.printStackTrace();
         }
    }
}

2、使用ObjectInputStream类实现反序列化
  • 使用对象输出流ObjectInputStream可以还原序列化的对象

  • 常用方法

    方法描述类型
    ObjectInputStream(InputStream in)创建对象输入流对象构造方法
    final Object readObject()从指定位置读取对象实例方法

    readObject() 返回一个Object类型的对象,如果确定该Object对象的真实类型,则可以将该对象强制转换成其真实类型

反序列化对象示例

需求

  • 读取D:/doc/person.bat文件中一个Person对象的数据

实现步骤

  1. 导入相关的类
  2. 创建一个对象输入流(ObjectInputStream)
  3. 输出反序列化后对象信息
  4. 关闭对象输入流
//1.导入相关的类
import java.io.ObjectInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
//2.创建一个对象输入流(ObjectInputStream)
ObjectInputStream ois = null;
//省略异常处理代码
ois = new ObjectInputStream(new FileInputStream("D:\\doc\\person.bat"));
//3.输出反序列化后对象信息
//反序列化时,readObject()方法返回的是Object对象需强制类型转换为Person对象
Person per = (Person) ois.readObject();
per.print();
//4.关闭对象输入流
if (ois != null)  ois.close();

反序列化集合示例

ObjectInputStream ois = null;
try {
      ois = new ObjectInputStream(new FileInputStream("D:\\doc\\persons.bat"));
      ArrayList<Person> list = (ArrayList<Person>)ois.readObject();
      for(Person person:list) {
           person.print();
      }
}catch(ClassNotFoundException ex) {
     ex.printStackTrace();
} catch(FileNotFoundException ex) {
     ex.printStackTrace();
} catch (IOException e) {
     e.printStackTrace();
} finally {
     try {
         if (ois != null) ois.close();
     }catch (IOException ex) {
          ex.printStackTrace();
     }
}

限制序列化

问题:

  • 出于安全考虑,对于一些比较敏感的信息(如用户密码),应限制被序列化,如何实现?

方案:

  • 使用transient关键字修改不需要序列化的对象属性

示例:

  • 希望Person类对象中的年龄信息不被序列化
import java.io.Serializable;
public class Person implements Serializable {
    private String name;
    private transient int age;//使用transient关键字修改不需要序列化的对象属性
    private String sex;
    public void print() {
        System.out.println("姓名:"+this.name+",年龄:"+this.age+",性别:"+this.sex+"。");
    }
}
十一、IO流小结

Java常用IO流有哪些?

分类字节输出流字节输入流字符输出流字符输入流
基类OutputStreamInputStreamWriterReader
文件流FileOutputSteamFileInputStreamFileWriterFileReader
缓冲流BufferedOutputStreamBufferedInputStreamBufferedWriterBufferedReader
对象流ObjectOutputStreamObjectInputStream--
数据操作流DataOutputStreamDataInputStream--
  • 所有的基类都是抽象类,无法直接创建实例,需要借助其实现类
  • 所有输出流实现写数据,所有输入流实现读数据
  • 输入和输出是相对程序而言
  • 所有的文件流直接与存储介质关联,需指定物理节点

经验:

  1. 在操作文本文件时,应使用字符流
  2. 字节流可以处理二进制数据,它的功能比字符流更强大
  3. 如果是二进制文本,应考虑使用字节流
十二、本章总结

在这里插入图片描述

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

众生云海,一念初见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值