第6章 Java输入输出流与文件操作
1.理解流的概念; 3.掌握文件输入输出流的使用方法; 4、掌握对象的序列化方法。 本章学习要点 2.掌握Java中输入输出流的分类;
目录 CONTENTS 6.1 6.3 6.4 6.2 6.5 输入输出流概述 输入流 输出流 对象的序列化 Java的文件操作
课 前 回 顾 与 思 考 输入输出流 +文件 程序中数据的存储方法: 单个、临时的:变量 多个、同类型、临时的(退出即消失):数组 多个、不同类型、临时的:向量 输入输出流 +文件 若Java应用程序中需要较多或长久保留的数据, 可以用什么方式进行存储和调用更有效呢?
6.1 输入输出流概述
水流:江、河、沟、渠、雨水、自来水、生活污水… 什 么 是 “流”? 看,那些在大千世界中我们见过的“流”…… 水流:江、河、沟、渠、雨水、自来水、生活污水… 汽/油/气流:液化气、空调冷气、煤气、汽油、压缩空气… 车流:高铁、普铁、地铁、高速公路、市区马路、乡村小路… 信号流: 电视线、电话线、光纤、网线、电线、…
什 么 是“流”? 看,大千世界中是如何 “流”起来的…… 特征归纳: 1)流动方向:起点 终点, 包括流入、流出 2)流动介质:液体、气体、固体(人、物)、电子、数字信号 3)流道材质:钢、铁、铜、复合塑料、水泥、沥青…… 4)流道形状:管、轨、线、大路、凹地……
计算机世界的“流”? 看,Java如果处理“流” …… 特征类比: 1)流动方向:数据源 数据宿, 包括 输入流、输出流 2)流的源/目的地: 文件,字节数组,StringBuffer,其它线程,已经被序列化的对象 3)流动介质 :文件流、数组流、管道流、对象流;缓冲流(提高速度)、数据流(将byte转成基本数据类型)、随机流(双向流动)等 4)流道形状:字节流(一次传输1个字节)、字符流(一次传输2个字节)
6.1 输入输出流概述 输入/输出处理是程序设计中非常重要的一部分,比如从键盘读取数据、从文件中读取数据或向文件中写数据等。 6.1 输入输出流概述 输入/输出处理是程序设计中非常重要的一部分,比如从键盘读取数据、从文件中读取数据或向文件中写数据等。 Java把一组有序的数据序列称为流(Stream)。流是Java语言中,用来处理输入/输出(I/O)的方式。根据流的方向,可以把流分为输入流和输出流。 如:执行的程序通常会输出各种信息到显示器,使用户可以随时了解程序的状态信息,而这些信息的通道就是一个数据流,其中的数据就是要显示的信息,数据的起点(源)就是执行的程序,而数据的终点就是显示器。
6.1 输入输出流概述 所谓的“数据流(stream)”指的是所有数据通信通道中数据的起点和终点。 输入流 输出流 数据宿 数据源 6.1 输入输出流概述 所谓的“数据流(stream)”指的是所有数据通信通道中数据的起点和终点。 文件 网络 键盘 JAVA程序 文件 网络 显示器 输入流 输出流 数据宿 数据源 以程序为参照,若程序是数据流动的起点,即数据的提供者,这个数据流就是一个“输出数据流”;若程序是数据流动的终点,这个数据流就是一个“输入数据流”。
6.1 输入输出流概述 输入流: 为了从信息源获取信息,程序打开一个输入流,程序可从输入流读取信息 输出流: 6.1 输入输出流概述 输入流: 为了从信息源获取信息,程序打开一个输入流,程序可从输入流读取信息 输出流: 当程序需要向目标位置写信息时,便需要打开一个输出流,程序通过输出流向这个目标位置写信息 11
6.1 输入输出流概述 字节流 : 抽象父类是输入流 InputStream 和 输出流 OutputStream 6.1 输入输出流概述 Java I/O系统负责处理程序的输入和输出,I/O类库位于java.io包中,它对各种输入流和输出流进行了抽象。 I/O类库两个对称性:输入-输出对称,字节流和字符流对称。 按照最小的数据单元,可以把流分为: 字节流 : 抽象父类是输入流 InputStream 和 输出流 OutputStream 用于一般目的,以字节(8位)为单位传输;数据源或目标中含有非字符 数据,必须用字节流来输入/输出。 字符流: 抽象父类是输入流 Reader 和 输出流 Writer 专门用于字符数据, 以字符(16位)为单位传输,效率比字节流高。
6.1 输入输出流概述
6.1 输入输出流概述 采用数据流方式的优点: 统一的处理方式 简化程序的编写 6.1 输入输出流概述 采用数据流方式的优点: 统一的处理方式 通过流的方式允许Java 程序使用相同的方式来访问不同的输入/输出源,使得程序员在处理不同的数据或数据存储时,更加方便、鲜明和统一。 简化程序的编写 对于输入数据流(程序是数据流的终点),一旦数据流建立完成后,程序可以不必关心数据流的起点是什么,只要读取自己需要的数据即可;对于输出数据流(程序是数据流的起点),一旦建立起数据流后,程序只负责提供数据,而不必理会数据流的目的地具体是什么(可能是显示器、打印机、文件、网络中的远端客户等)。
6.1 输入输出流概述——基本流 Java有3个内置的标准流对象: 1)标准输入流System.in: 用来读取用户从键盘的输入 6.1 输入输出流概述——基本流 Java有3个内置的标准流对象: 1)标准输入流System.in: 用来读取用户从键盘的输入 2)标准输出流System.out: 用来在屏幕上显示信息 3)标准输出流System.err: 用来显示出错信息 (1)键盘输入 使用System.in对象的read()方法 例: char c=(char)System.in.read( ); 使用InputStreamReader 、BufferReader流类 例: InputStreamReader isr=new InputStreamReader(System.in); BufferedReader br=new BufferedReader(isr);
6.1 输入输出流概述——基本流 (2)格式化输出 用Java的标准输出System.out,可以输出不同类型的对象: 6.1 输入输出流概述——基本流 (2)格式化输出 用Java的标准输出System.out,可以输出不同类型的对象: 例:Int a=20; System.out .print(a) System.out .println(“大家好”) //加换行 用java.text包中的NumberFormat类可以控制显示格式,有三种方法: NumberFormat.getNumberInstance( ) //附加逗号 NumberFormat.getCurrencyInstance( ) //货币符号 NumberFormat.getPercentInstance( ) //百分号
6.2 输入流
6.2.1 字节输入流 已经过时不用, 替代类是StringReader
6.2.1 字节输入流——InputStream类中定义的方法 int read(): 从输入流中读取一个字节,把它转换为0-255之间的整数,并返回这一整数,如果遇到输入流的结尾,则返回-1。 int read(byte[] b): 从输入流中读取若干个字节,把它们保存到参数b指定的字节数组中,返回的整数表示读取的字节数,如果遇到输入流的结尾,返回-1。 int read(byte[] b, int off, int len) :从输入流中读取若干个字节,把它们保存到参数b指定的字节数组中。 off指定在字节数组中开始保存数据的起始下标, len指定读取的字节数目。返回的整数表示实际读取的字节数。如遇到输入流结尾,返回-1。 void close() : 关闭输入流 int available() : 返回可以从输入流中读取的字节数目 long skip(long n): 从输入流中跳过参数n指定数目的字节
6.2.1 字节输入流——字节数组输入流使用示例
6.2.2 字符输入流 1、Reader:字符输入流,抽象类。字符类输入流都是抽象类Reader的子类。 6.2.2 字符输入流 1、Reader:字符输入流,抽象类。字符类输入流都是抽象类Reader的子类。 2、在Java中,字符是以两个字节的Unicode码的形式表示的。
6.2.2 字符输入流——Reader类中定义的方法 int read(): 从输入流中读取一个字符,返回一个整数值,如输入流结束,返回-1 int read(char[] b): 从输入流中读取最多b.length个字符,存入b,返回实际读入的字符数。 int read(char[] b, int off, int len) :从输入流中读取最多len个字符,保存到b的off起始位置,返回实际读入字符数。 void close() : 关闭输入流 long skip(long n): 从输入流中跳过参数n指定数目的字符。
6.2.2 字符输入流——BufferedReader使用示例
6.3 输出流
6.3.1 字节输出流 1、OutputStream:字节输出流的顶级父类,抽象类; 2、
6.3.1 字节输出流——OutputStream类中定义的方法 void write(int b): 向输出流写出一个字节 void write(byte[] b): 把参数b指定的字节数组中的所有字节写到输出流 void write(byte[] b,int off, int len): 把参数b指定的字节数组中的若干字节写到输出流,参数off为起始下标,参数len为长度 void close(): 关闭输出流 void flush(): OutputStream类本身的flush方法不执行任何操作,它的一些带缓冲区的子类覆盖了flush方法,该方法强制把缓冲区内的数据写到输出流中
6.3.1 字节输出流——FileOutputStream使用示例
6.3.2 字符输出流 Writer:字符输入流,抽象类
6.3.2 字符输出流——Writer类中定义的方法 void write(int b): 把整形值b的二进制16位写入输出流中 void write(char[] b): 把数组b[]指定的字符写到输出流 void write(char[] b,int off, int len): 把数组b[]指定的字符写到输出流,参数off为起始下标,参数len为长度 void close(): 关闭输出流 void flush(): 清空输出流,该方法强制把缓冲区内的字符全部写到输出流中。
6.3.2 字符输出流——FileWriter 使用示例
6.4 对象的序列化
6.4 对象的序列化 序列化&反序列化: 对象的序列化通常在下面两种情况下使用: 对象的序列化: 是指把对象写到输出流中 对象的序列化: 是指把对象写到输出流中 对象的反序列化: 是指从输入流中读取对象 对象的序列化通常在下面两种情况下使用: 远程方法调用(RMI)——在远程方法调用时,需要在客户机与服务器之间传递各种对象。 对象持久性保存——允许对象在创建它们的程序的生命周期结束后仍然存在保存,供以后的程序调用。 所谓对象串行化是指读写对象的过程,其关键之处在于以串行的流的形式来表示结构化的对象。
6.4 对象的序列化 对象序列化涉及的类和接口: 对象序列化与对象克隆: java语言要求只有实现了java.io.Serializable接口的类的对象才能被序列化和反序列化 实现序列化用ObjectOutputStream类 反序列化用ObjectInputStream类 对象序列化与对象克隆: 使用对象流很容易获取一个序列化对象的克隆(复制品): 只需将该对象写入对象输出流指向的目的地,再将该目的地作为一个对象输入流的源,则读回来的就是该对象的克隆。
Serializable接口的两点理解: 6.4 对象的序列化 Serializable接口的两点理解: 类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化;可序列化类的所有子类本身都是可序列化的。 Serializable是空接口,没有方法或字段;仅用于标识可序列化的语义。
6.4 对象的序列化——示例 如果某些属性不想被序列化,则 这些属性使用transient修饰符 定义类实现Serializable接口 class Employee implements Serializable{} 使用ObjectOutputStream实现“对象的序列化” ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream("d:/emp.obj")); Employee emp = new Employee("aa", 23); oout.writeObject(emp); oout.close(); 使用ObjectInputStream实现“反序列化” ObjectInputStream oin = new ObjectInputStream(new FileInputStream("d:/emp.obj")); Employee e = (Employee) oin.readObject(); System.out.println(e.getName()); System.out.println(e.getAge()); oin.close();
6.5 Java的文件操作
6.5 Java的文件操作——File类 File类是Java中专门用来管理磁盘文件和目录的类。 继承关系:
6.5 Java的文件操作——File类 File类的构造方法: File(String path) File(String path, String name) File(File dir, String name) 参数path:指明磁盘文件或目录名及其路径;目录分隔符用System.dirSep表示。如: File f1=new File(“c:”+ System.dirSep+”sj”) 参数name:文件或目录名 参数dir:已经存在的磁盘目录 1、第一个参数表示要操作的文件。可以使用字符串类型的文件名也可以使用一个文件对象;
File类的成员方法(获取文件或目录属性) 6.5 Java的文件操作——File类 File类的成员方法(获取文件或目录属性) boolean exists( ) 若文件或目录存在则返回true boolean isFile( ) 若对象代表有效文件则返回true boolean isDirectory( ) 若对象代表有效目录则返回true String getName( ) 返回文件名或目录名 String getPath( ) 返回文件或目录的路径 long length( ) 返回文件的字节数 boolean canRead( ) 若文件为可读文件则返回true boolean canWrite( ) 若文件为可写文件则返回true boolean equals(File f ) 若两个文件对象相同则返回true String [] list( ) 将目录中所有文件名存在数组中返回
File类的成员方法(文件或目录的操作) 6.5 Java的文件操作——File类 File类的成员方法(文件或目录的操作) boolean renameTo(File newFile) 将文件重命名 void delete( ) 将当前文件删除 boolean mkdir( ) 创建当前目录的子目录 注意: 由于Java Applet程序是从网络上下载到本地机器运行的,不可知也不可控,所以Java的安全机制禁止Java Applet程序访问和存取本地文件,如果试图在Java Applet程序中使用文件操作,则将引发Java的安全性异常。
6.5 Java的文件操作——访问文件示例0 示例实现的功能: (1)列出指定文件夹下的所有文件 (2)创建一个子文件夹,再次列出所有文件,验证是否创建成功 (3)显示用户指定的现有文件的属性 访问文件示例 FileOperation.java
6.5 Java的文件操作——FileInputStream流 继承关系 读取字节时:与BufferedInputStream、 DataInputStream一起使用; 读取字符时:与InputStreamReader、BufferedReader一起使用。 要读取字符流,建议使用 FileReader类。
6.5 Java的文件操作——读文件示例1 读文件方式1:用文件字节输入流FileInputStream打开文件,以文件字节输入流对象为参数创建一个字符输入流InputStreamReader, 再以字符输入流为参数创建一个缓冲输入流BufferedReader对象,使用该对象的readLine()方法以一行为单位读出文件中的所有字符串。 读文件示例1 FileInput.java
边 学 边 练(1) 代码片段填空: FileInputStream fs=new FileInputStream(“File1.txt”); //文件含汉字 BufferedReader in=new _________________________________________________; String s=null; while ( (s = in.readLine()) != null ) System.out.println(s); //输出文件内容 _____________________; // 关闭流
6.5 Java的文件操作——FileReader流 继承关系 读取字符时:与BufferedReader一起使用。 要读取字符流,建议使用 FileReader类。
6.5 Java的文件操作——读文件示例2 读文件方式2:用文件输入流FileReader打开文件,创建缓冲输入流BufferedReader类的对象,再用该对象 的readline()方法读取文件中的字符串。 读文件示例2 FileRead.java
边 学 边 练(2) 代码片段填空: __________________________________=new FileReader (“File2.txt”); BufferedReader br = new BufferedReader(fr); String record = new String( ); While (__________________________________) //判断文件是否到结尾 { System.out.println( record); } br.close(); //关闭缓存
6.5 Java的文件操作——FileOutputStream流 继承关系 写字节时,与BufferedOutputStream、 DataOutputStream一起使用; 写字符时,与OutputStreamWriter、BufferedWriter一起使用。 要读取字符流,建议使用 FileReader类。
6.5 Java的文件操作 ——写文件示例1 写文件方式1: 用文件输出流FileOutputStream打开文件,创建数据字节输出流DataOutputStream,再用该对象的各种writeXXX()方法把各类型数据到写入文件,注意 writeChars()方法写汉字会出现乱码。或者创建打印流PrintStream的对象,再用该对象的println()方法把字符串写入文件中。 写文件示例1 FileOutput.java
边 学 边 练(3) 代码片段填空: FileOutputStream fout=new FileOutputStream(“File3.txt”); DataOutputStream dos=new DataOutputStream( ); // dos. ; //写入整数 1000 dos. ; //写入字符串“Hi, 亲学生们!” dos.writeChars(“To study hard! "); ; //关闭流
6.5 Java的文件操作——写文件示例2 写文件方式2:用文件字符输出流FileWriter 打开文件,创建打印输出流PrintWriter 的对象,再用该对象 的write( )、print( )或者println( )方法把字符串写入文件中。或者创建缓冲输出流BufferedWriter的对象,再用该对象的write( )方法把字符串写入文件中。 写文件示例2 FileWrite.java
边 学 边 练(4) 代码片段填空: File f=_____________ (“d:\\myFile2.txt”); FileWriter fw= new FileWriter( f ); PrintWriter out=new PrintWriter( fw ); out. write(65); //写入文件中的内容是_____________? out. //写入字符串”学以致用” //关于打印流 //关闭文件流 java中io各种流的关闭顺序 一般情况下是:先打开的后关闭,后打开的先关闭 另一种情况:看依赖关系,如果流a依赖流b,应该先关闭流a,再关闭流b 例如处理流a依赖节点流b,应该先关闭处理流a,再关闭节点流b 当然完全可以只关闭处理流,不用关闭节点流。处理流关闭的时候,会调用其处理的节点流的关闭方法 如果将节点流关闭以后再关闭处理流,会抛出IO异常
思 考: 可否建立一个流,对一个文件既可以读也可以写呢? 6.5 Java的文件操作 思 考: 可否建立一个流,对一个文件既可以读也可以写呢? RandomAccessFile类 随机访问文件流 在这里,“随机”是指所存取的数据不需要与以前存取过的历史数据有任何的关系。使用随即文件存取方式可以在打开一个文件后同时进行读写操作,
6.5 Java的文件操作—— RandomAccessFile类 继承关系: 实现的接口: DataInput接口:从二进制流中读取字节,并根据所有 Java 基本类型数据进行重构。 DataOutput接口:将数据从任意 Java 基本类型转换为字节,并将这些字节写入二进制流。
6.5 Java的文件操作—— RandomAccessFile类 可以实现对文件的随机读写操作;而且,在读/写时具备强大的含类型转换的输入/输出功能; RandomAccessFile类的使用 该类的所有方法都有可能抛出IOException异常。 RandomAccessFile流指向文件时,不刷新文件 1、因为实现了DataInput和 DataOutput接口,既可以读文件、又可以写文件;而且,在读/写时具备强大的含类型转换的输入/输出功能; 2、3、该类的所有方法都有可能抛出IOException异常,在利用它实现文件操作时需要将相关语句放在try块中,并配上catch块来处理异常。
6.5 Java的文件操作—— RandomAccessFile类 RandomAccessFile(File file, String mode) RandomAccessFile(String name, String mode) 第1个参数表示要操作的文件; 第2个字符串类型的参数mode表示了对文件的操作方式: mode为 ”r” 时表示可以从文件读取; mode 为 ”rw” 时表示既可以从文件读取也可以向文件写入 1、第一个参数表示要操作的文件。可以使用字符串类型的文件名也可以使用一个文件对象; 2、该类的所有方法都有可能抛出IOException异常,在利用它实现文件操作时需要将相关语句放在try块中,并配上catch块来处理异常。
6.5 Java的文件操作—— RandomAccessFile类 RandomAccessFile类的常用方法:【教材P: 293-294 表10.1】 Public long getFilePointer( ) 获得当前文件位置指针从文件头算起的绝对位置。 public void seek( long pos ): 将指针移动到pos指定的文件位置 public void readLine(): 读取一个文本行 public void write( byte b[]): 写b.length个字节到文件 public void writeBytes (String s): 向文件写入一个字符串
6.6 6.5 Java的文件操作—— RandomAccessFile类 示例 作为数据源的文件 访问模式:rw: 可读、可写 r: 只读
本 章 编 程 挑 战 题目:已知一份成绩表结构如下, 请编程求每位学生的平均分,按得分从低到高排序,并将排序结果写入该成绩表。
本 章 知 识 回 顾 1. 什么是Java的输入输出流? Java把一组有序的数据序列称为流(Stream) 流是Java语言中用来处理输入/输出(I/O)的方式 以Java程序为数据流动起点的流是输出流 以Java程序为数据流动终点的流是输入流 文件 网络 键盘 JAVA程序 文件 网络 显示器 输入流 输出流 数据宿 数据源
本 章 知 识 回 顾 2. Java输入输出流分类(4个顶层抽象类) 输入流 输出流 字节流 InputStream OutputStream 字符流 Reader Writer 字节流 :用于一般目的,以字节为单位传输; 字符流: 专门用于字符数据, 以字符为单位传输,效率比字节流高; 数据源或目标中含有非字符数据,必须用字节流来输入/输出。
本 章 知 识 回 顾
本 章 知 识 回 顾 3. 输入流与输出流的常用方法 字节输入流InputStream类的常用方法: int read() int read(byte[] b) int read(byte[] b, int off, int len) void close() int available() long skip(long n) 本 章 知 识 回 顾 字符输入流Reader类的常用方法: int read() int read(char[] b) int read(char[] b, int off, int len) void close() long skip(long n) 3. 输入流与输出流的常用方法 字节输出流OutputStream类的常用方法: void write(int b) void write(byte[] b) void write(byte[] b,int off, int len) void close() void flush() 字符输出流Writer类的常用方法: void write(int b) void write(char[] b) void write(char[] b,int off, int len) void close() void flush()
本 章 知 识 回 顾 4. 对象的序列化: 一个类如果实现了 java.io.Serializable 接口,这个类所创建的对象就是序列化的对象。 对象的序列化 是指用ObjectOutputStream类 把对象写到输出流中。 对象的反序列化 是指用ObjectInputStream类从输入流中读取对象。 使用对象流很容易获取一个序列化对象的克隆。
本 章 知 识 回 顾 4. Java的文件操作: 用File类管理文件和目录、查看文件属性 用文件字节输入流FileInputStream、文件字符输入流FileReader读文本文件 用文件字节输出流FileOutputStream、文件字符输出流FileWriter写文本文件 用随机访问文件流RandomAccessFile读、写文本文件
读文本文件 方法一:用FileInputStream FileInputStream fs=new FileInputStream(“my.txt”) BufferedReader br=new BufferedReader(new InputStreamReader(fs)); String record=null; while ((record =br.readLine())!=null) System.out.println(record); br.close(); //关闭输入流 读文本文件 方法二:用FileReader FileReader fr= new FileReader (“myf.txt”); BufferedReader br=new BufferedReader(fr); String record=null; while ((record =br.readLine())!=null) System.out.println(record); br.close(); //关于缓存
写文本文件 方法一:用FileOutputStream FileOutputStream fout=new FileOutputStream(“my.txt”) DataOutputStream dos=new DataOutputStream(fout); dos. write((“要写入文本文件中的内容”).getBytes()); dos.close(); 写文本文件 方法二:用FileWriter FileWriter fw= new FileWriter(“myf.txt”); PrintWriter out=new PrintWriter(fw); out.println(“要写入文本文件中的内容”); out.close(); //关于输出 fw.close(); //关闭文件
读写文本文件 方法:用随机流 RandomAccessFile RandomAccessFile raf = new RandomAccessFile("随机.txt", "rw"); long length=raf.length(); //获取文件长度 long position=0; raf.seek(position); //将文件指针移到起始位置 while(position<length) { String str=raf.readLine(); byte b[]=str.getBytes(“iso-8859-1”); //避免中文乱码 System.out.println(new String(b); //输出文件内容 position=raf.getFilePointer(); //获取文件当前指针 } raf.write(("大家好,Hello World!!").getBytes()); raf.close(); 读写文本文件