1 IO简介
1.1 IO
对于任何程序设计语言而言,输入输出(Input/Output)系统都是非常核心的功能。程序运行需要数据,数据的获取往往需要跟外部系统进行通信,外部系统可能是文件、数据库、其他程序、网络、I0 设备等等。外部系统比较复杂多变,那么我们有必要通过某种手段进行抽象、屏蔽外部的差异,从而实现更加便捷的编程。
输入(Input)指的是:可以让程序从外部系统获得数据(核心含义是“读”,读取外部数据)。
常见的应用:
- 读取硬盘 上的文件内容到程序。例如:播放器打开一个视频文件、 word打开一个doc文件。
- 读取网络 上某个位置内容到程序。例如:浏览器中输入网址后,打开该网址对应的页面内容;下载网络上某个网址的文件。
- 读取数据库系统的数据到程序。
- 读取某些硬件系统数据到程序。例如:车载电脑读取雷达扫描信息到程序;温控系统等。
输出(Output)指的是:程序输出数据给外部系统从而可以操作外部系统(核心含义是“写",将数据写出到外部系统) 。
常见的应用有:
- 将数据写到硬盘中。 例如:我们编辑完一个word文档后,将内容写到硬盘上进行保存。
- 将数据写到数据库系统中。例如:我们注册一个网站会员,实际就是后台程序向数据库中写入一条记录。
- 将数据写到某些硬件系统中。例如:导弹系统导航程序将新的路径输出到飞控子系统,飞控子系统根据数据修正飞行路径。
java.io 包为我们提供了相关的API,实现了对所有外部系统的输入输出操作。
1.2 数据源
数据源Data Source,提供数据的原始媒介。常见的数据源有:数据库、文件、其他程序、内存、网络连接、IO 设备。
数据源分为:源设备、目标设备。
- 源设备:为程序提供数据,一般对应输入流。
- 目标设备:程序数据的目的地,一般对应输出流。
1.3 流的概念
流是一个抽象、动态的概念,是一连串连续动态的数据集合。
对于输入流而言,数据源就像水箱,流(Stream)就像水管中流动着的水流,程序就是我
们最终的用户。我们通过流(A Stream)将数据源(Source) 中的数据(information) 输送到程序(Program) 中。
对于输出流而言,目标数据源就是目的地(dest) ,我们通过流(A Stream)将程序(Program)中的数据(information) 输送到目的数据源(dest) 中。
注意:输入/输出流的划分是相对于程序而言,并不是相对数据源
1.4 Java中的四大IO抽象类
InputStream/0utputStream 和Reader/Writer类是所有IO流类的抽象父类,我们有必要简单了解一下这个四个抽象类的作用。然后,通过它们具体的子类熟悉相关的用法。
InputStream
此抽象类是表示字节输入流的所有类的父类。InputSteam 是一个抽象类,它不可以实例化。数据的读取需要由它的子类来实现。根据节点的不同,它派生了不同的节点流子类.继承自InputSteam的流都是用于向程序中输入数据,且数据的单位为字节(8 bit)。
常用方法:
int read():读取一个字节的数据,并将字节的值作为int类型返回(0-255之间的一个值)。如果未读出字节则返回-1 (返回值为-1 表示读取结束)。
void close():关闭输入流对象,释放相关系统资源。
OutputStream
此抽象类是表示字节输出流的所有类的父类。输出流接收输出字节并将这些字节发送到
某个目的地。
常用方法:
void write(int n):向目的地中写入一个字节。
void closel)::关闭输出流对象,释放相关系统资源。
Reader
Reader用于读取的字符流抽象类,数据单位为字符。
常用方法:
int read():读取一个字符的数据, 并将字符的值作为int类型返回(0-65535之间的一个值,即Unicode值)。如果未读出字符则返回-1 (返回值为-1表示读取结束) 。
void close() :关闭流对象, 释放相关系统资源。
Writer
Writer 用于输出的字符流抽象类,数据单位为字符。
常用方法:
void write(int n):向输出流中写入一个字符。
void close() :关闭输出流对象, 释放相关系统资源。
1.5 Java中流的概念细分
按流的方向分类:
输入流:数据流从数据源到程序(以InputStream、 Reader 结尾的流)。
输出流:数据流从程序到目的地(以OutPutStream、Writer结尾的流)。
按处理的数据单元分类:
字节流:以字节为单位获取数据,命名上以Stream结尾的流一般是字节流,如FilelnputStream、FileOutputStream。如读取二进制文件,使用字节流
字符流:以字符为单位获取数据,命名上以Reader/Writer结尾的流一般是字符流,如FileReader、FileWriter。如读取文本类文件,使用字符流
按处理对象不同分类:
节点流:可以直接从数据源或目的地读写数据, 如FilelnputStream、FileReader.DatalnputStream等.
处理流:不直接连接到数据源或目的地,是"处理流的流”。通过对其他流的处理提高程序的性能,如BufferedInputStream、BufferedReader 等。处理流也叫包装流。
节点流处于IO操作的第一线,所有操作必须通过它们进行;处理流可以对节点流进行包装,提高性能或提高程序的灵活性。
1.6 Java中IO流类的体系
Java为我们提供了多种多样的IO流,我们可以根据不同的功能及性能要求挑选合适的IO流。
Java中IO流类的体系(这里只列出常用的类,详情可以参考JDK API文档)
从上图发现,很多流都是成对出现的,比如: FilelnputStream/FileOutputStream, 显然是对文件做输入和输出操作的。下面简单做个总结:
- InputStream/OutputStream 字节流的抽象类。
- Reader/Writer 字符流的抽象类。
- FilelnputStream/FileOutputStream
节点流:以字节为单位直接操作"文件”。 - ByteArraylnputStream/ByteArrayOutputStream
节点流:以字节为单位直接操作"字节数组对象"。 - ObjectinputStream/ObjectOutputStream
处理流:以字节为单位直接操作“对象”。 - DatalnputStream/DataOutputStream
处理流:以字节为单位直接操作“基本数据类型与字符串类型"。 - FileReader/FileWriter
节点流:以字符为单位直接操作"文本文件”(注意:只能读写文本文件)。 - BufferedReader/BufferedWriter
处理流:将Reader/Writer对象进行包装,增加缓存功能,提高读写效率。 - BufferedInputStream/BufferedOutputStream
处理流:将InputStream/OutputStream对象进行包装,增加缓存功能,提高读写效率。 - InputStreamReader/OutputStreamWriter
处理流:将字节流对象转化成字符流对象。 - PrintStream
处理流:将OutputStream进行包装,可以方便地输出字符,更加灵活。
2 IO流入门案例
第一个简单的IO流程序
当程序需要读取数据源的数据时,就会通过IO流对象开启一个通向数据源的流,通过这个IO流对象的相关方法可以顺序读取数据源中的数据。
【示例】一个简单的IO流程序
import java.io.FileInputStream;
import java.io.IOException;
public class FirstDemo {
public static void main(String[] args) {
//创建字节输入流对象
FileInputStream fis = null;
try {
fis = new FileInputStream("d:/a.txt");
int s1 = fis.read(); //打印输入字符a的ASCII码值
int s2 = fis.read(); //打印输入字符b的ASCII码值
int s3 = fis.read(); //打印输入字符c的ASCII码值
int s4 = fis.read(); //由于文件内容已读取完毕,则返回-1
System.out.println(s1+"\t"+s2+"\t"+s3+"\t"+s4); //97 98 99 -1
}catch (Exception e){
e.printStackTrace();
}finally {
if (fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
【示例】改进上个案例
import java.io.FileInputStream;
public class SecondDemo {
public static void main(String[] args) {
FileInputStream fis = null;
StringBuffer sb = new StringBuffer();
try {
//创建字节输入流对象
fis = new FileInputStream("d:/a.txt");
int temp = 0;
while ((temp = fis.read()) != -1){
System.out.println(temp);
sb.append((char)temp);
}
System.out.println(sb.toString());
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (fis != null){
fis.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
3 File类的使用
3.1 File 类简介
File类的作用
File类是Java提供的针对磁盘中的文件或目录转换对象的包装类。一个File 对象而可以代表-个文件或目录,File 对象可以实现获取文件和目录属性等功能,可以实现对文件和目录的创建,删除等功能。
File类操作目录与文件的常用方法
- 针对文件操作的方法:
createNewFile() //创建新文件。
delete() //直接从磁盘上删除
exists() //查询磁盘中的文件是否存在
getAbsolutePath() //获取绝对路径
getPath() //获取相对路径
getNamel() //获取文件名相当于调用了一个toString方法
isFile() //判断是否是文件
length() //查看文件中的字节数
isHidden() //测试文件是否被这个抽象路径名是一个隐藏文件。 - 针对目录操作的方法:
exists() //查询目录是否存在
isDirectory() //判断当前路径是否为目录
mkdir() //创建目录
getParentFile() //获取当前目录的父级目录
list() //返回一个字符串数组,包含目录中的文件和目录的路径名
listFiles() //返回一个File数组,表示用此抽象路径名表示的目录中的文件
3.2 File类的基本使用
【示例】操作文件
import java.io.File;
import java.io.IOException;
public class FileDemo {
public static void main(String[] args) throws IOException {
//创建File对象
File file = new File("d:/aa.txt");
System.out.println(file.createNewFile()); //true
//System.out.println(file.delete()); //true
System.out.println(file.exists()); //true
System.out.println(file.getName()); //aa.txt
System.out.println(file.isFile()); //true
System.out.println(file.isHidden()); //false
}
}
【示例】操作目录
import java.io.File;
public class DirectoryDemo {
public static void main(String[] args) {
//创建File对象
File file = new File("d:/a");
System.out.println(file.mkdir()); //true
System.out.println(file.exists()); //true
System.out.println(file.isDirectory()); //true
System.out.println(file.getParentFile()); //d:\
File file2 = new File("d:/");
String[] arr = file2.list();
for (String temp:arr) {
System.out.println(temp);
}
System.out.println("=================");
File[] arr2 = file2.listFiles();
for (File temp:arr2) {
System.out.println(temp);
}
}
}
4 常用流对象
4.1 文件字节流
FileInputStream通过字节的方式读取文件,适合读取所有类型的文件(图像、视频、文本文件等)。Java 也提供了FileReader 专门读取文本文件。
FileOutputStream通过字节的方式写数据到文件中,适合所有类型的文件。Java 也提供了FileWriter专门写入文本文件。
【示例】文件字节输入流
import java.io.FileInputStream;
public class FileInputStreamDemo {
public static void main(String[] args) {
FileInputStream fis = null;
try {
//创建文件字节输入流对象
fis = new FileInputStream("d:/Photo.jpg");
int temp = 0;
while ((temp = fis.read()) != -1){
System.out.println(temp);
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (fis != null){
fis.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
【示例】文件字节输出流
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class FileInputStreamDemo {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//创建文件字节输入流对象
fis = new FileInputStream("d:/Photo.jpg");
//创建文件字节输出流对象
fos = new FileOutputStream("d:/aa.jpg");
int temp = 0;
while ((temp = fis.read()) != -1){
fos.write(temp);
}
//将数据从内存中写入到磁盘中
fos.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (fis != null){
fis.close();
}
if(fos != null){
fos.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
通过缓冲区提高读写效率
方式一
通过创建一个指定长度的字节数组作为缓冲区,以此来提高IO流的读写效率。该方式适用于读取较大图片时的缓冲区定义。注意:缓冲区的长度一定是2的整数幂。一般情况下1024长度较为合适。
【示例】创建一个指定长度的字节数组作为缓冲区
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class FileStreamBufferedDemo {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//创建文件字节输入流对象
fis = new FileInputStream("d:/Photo.jpg");
//创建文件字节输出流对象
fos = new FileOutputStream("d:/aa.jpg");
//创建一个缓冲区提高读写效率
byte[] buff = new byte[1024];
int temp = 0;
while ((temp = fis.read(buff)) != -1){
fos.write(buff,0,temp);
}
//将数据从内存中写入到磁盘中
fos.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (fis != null){
fis.close();
}
if(fos != null){
fos.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
方式二
通过创建一个字节数组作为缓冲区,数组长度是通过输入流对象的available()返回当前文件的预估长度来定义的。在读写文件时,是在一次读写操作中完成文件读写操作的。
注意:如果文件过大,那么对内存的占用也是比较大的。所以大文件不建议使用该方法。
【示例】创建一个字节数组作为缓冲区
package com.testIO;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class FileStreamBuffered2Demo {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//创建文件字节输入流对象
fis = new FileInputStream("d:/Photo.jpg");
//创建文件字节输出流对象
fos = new FileOutputStream("d:/aa.jpg");
//创建一个缓冲区提高读写效率
byte[] buff = new byte[fis.available()];
fis.read(buff);
//将数据从内存中写入到磁盘中
fos.write(buff);
fos.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (fis != null){
fis.close();
}
if(fos != null){
fos.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
【示例】通过字节缓冲流提高读写效率
Java缓冲流本身并不具有IO流的读取与写入功能,只是在别的流(节点流或其他处理流)上加上缓冲功能提高效率,就像是把别的流包装起来一样,因此缓冲流是一种处理流(包装流)。
当对文件或者其他数据源进行频繁的读写操作时,效率比较低,这时如果使用缓冲流就能够更高效的读写信息。因为缓冲流是先将数据缓存起来,然后当缓存区存满后或者手动刷新时再一次性的读取到程序或写入目的地。
因此,缓冲流还是很重要的,我们在IO操作时记得加上缓冲流来提升性能。
BufferedInputStream和BufferedOutputStream这两个流是缓冲字节流,通过内部缓存数组来提高操作流的效率。
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class FileStreamBuffered3Demo {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
fis = new FileInputStream("d:/Photo.jpg");
bis = new BufferedInputStream(fis);
fos = new FileOutputStream("d:/ff.jpg");
bos = new BufferedOutputStream(fos);
//缓冲流中的byte数组的默认长度是8192 2的13次方
int temp = 0;
while ((temp = bis.read()) != -1){
bos.write(temp);
}
bos.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
//注意:关闭流顺序:后开的先关闭
if (bis != null){
bis.close();
}
if (fis != null){
fis.close();
}
if (bos != null){
bos.close();
}
if(fos != null){
fos.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
【示例】定义文件拷贝工具类
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class FileCopyTools {
public static void main(String[] args) {
copyFile("d:/Photo.jpg","d:/abc.jpg");
}
/**
* 文件拷贝方法
*/
public static void copyFile(String src,String des){
FileInputStream fis = null;
BufferedInputStream bis = null;
FileOutputStream fos = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(src));
bos = new BufferedOutputStream(new FileOutputStream(des));
int temp = 0;
while ((temp = bis.read()) != -1){
bos.write(temp);
}
bos.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
//注意:关闭流顺序:后开的先关闭
if (bis != null){
bis.close();
}
if (fis != null){
fis.close();
}
if (bos != null){
bos.close();
}
if(fos != null){
fos.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
4.2 文件字符流
如果处理的文件是文本文件,也可以使用文件字符流,它以字符为单位进行操作。
【示例】文件字符输入流
import java.io.FileReader;
public class FileReaderDemo {
public static void main(String[] args) {
FileReader frd = null;
try {
//创建文件字符输入流对象
frd = new FileReader("d:/a.txt");
int temp = 0;
while ((temp = frd.read()) != -1){
System.out.println((char) temp);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (frd != null){
frd.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
【示例】文件字符输出流
import java.io.FileWriter;
public class FileWriterDemo {
public static void main(String[] args) {
FileWriter fw = null;
FileWriter fw2 = null;
try {
//创建文件字符输出流对象
fw = new FileWriter("d:/baidu.txt");
fw.write("baidu.com\r\n"); //"\r\n代表回车换行"
fw.write("Hello\r\n");
fw.flush();
fw = new FileWriter("d:/baidu.txt",true); //append参数默认是false会直接覆盖
fw.write("zhang");
fw.flush();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (fw != null){
fw.close();
}
if (fw2 != null){
fw2.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
【示例】使用字符流实现文本文件的拷贝处理
import java.io.FileReader;
import java.io.FileWriter;
public class FileCopyTools2 {
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader("d:/2.txt");
fw = new FileWriter("d:/3.txt");
char[] buffer = new char[1024]; //字符流没有available()方法
int temp = 0;
while ((temp = fr.read(buffer)) != -1){
fw.write(buffer,0,temp);
}
fw.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (fr != null){
fr.close();
}
if (fw != null){
fw.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
4.3 字符缓冲流
BufferedReader/BufferedWriter 增加了缓存机制,大大提高了读写文本文件的效率。
【示例】字符输入缓冲流
BufferedReader 是针对字符输入流的缓冲流对象,提供了更方便的按行读取的方法:readLine();在使用字符流读取文本文件时,我们可以使用该方法以行为单位进行读取。
import java.io.BufferedReader;
import java.io.FileReader;
public class BufferedReaderDemo {
public static void main(String[] args) {
FileReader fr = null;
BufferedReader br = null;
try{
fr = new FileReader("d:/baidu.txt");
br = new BufferedReader(fr);
String temp = "";
while ((temp = br.readLine()) != null){
System.out.println(temp);
}
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (br != null){
br.close();
}
if (fr != null){
fr.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
【示例】字符输出缓冲流
BufferedWriter是针对字符输出流的缓冲流对象,在字符输出缓冲流中可以使用newline(); 方法实现换行处理。
package com.testIO;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
public class BufferedWriterDemo {
public static void main(String[] args) {
FileWriter fw = null;
BufferedWriter bw = null;
try{
fw = new FileWriter("d:/baidu2.txt");
bw= new BufferedWriter(fw);
bw.write("baidu.com");
bw.write("Hello");
bw.newLine();
bw.write("zhang");
bw.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (bw != null){
bw.close();
}
if (fw != null){
fw.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
【示例】通过字符缓冲流实现文本文件的拷贝
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
public class FileCopyTools3 {
public static void main(String[] args) {
copyFile("d:/2.txt","d:/22.txt");
}
/**
* 基于字符缓冲流实现文件拷贝
*/
public static void copyFile(String src,String des){
BufferedReader br = null;
BufferedWriter bw = null;
try{
br = new BufferedReader(new FileReader(src));
bw = new BufferedWriter(new FileWriter(des));
String temp = "";
while ((temp = br.readLine()) != null){
bw.write(temp);
bw.newLine();
}
bw.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (br != null){
br.close();
}
if (bw != null){
bw.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
【示例】通过字符缓冲流为文件中的内容添加行号
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
public class LineNumberDemo {
public static void main(String[] args) {
BufferedReader br = null;
BufferedWriter bw = null;
try{
br = new BufferedReader(new FileReader("d:/baidu.txt"));
bw = new BufferedWriter(new FileWriter("d:/baidu3.txt"));
String temp = "";
int i = 1;
while ((temp = br.readLine()) != null){
bw.write(i+","+temp);
bw.newLine();
i++;
}
bw.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (br != null){
br.close();
}
if (bw != null){
bw.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
4.4 转换流
InputStreamReader/OutputStreamWriter用来实现将字节流转化成字符流。
比如,如下场景:
System.in是字节流对象,代表键盘的输入,如果我们想按行接收用户的输入时,就必须用到缓冲字符流BufferedReader特有的方法readLine(), 但是经过观察会发现在创建BufferedReader的构造方法的参数必须是一个Reader对象,这时候我们的转换流InputStreamReader就派上用场了。
而System.out也是字节流对象,代表输出到显示器,按行读取用户的输入后,并且要将读取的一行字符串直接显示到控制台,就需要用到字符流的write(String str)方法,所以我们要使用OutputStreamWriter将字节流转化为字符流。
【示例】通过转换流实现键盘输入屏幕输出
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
public class ConvertStream {
public static void main(String[] args) {
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new InputStreamReader(System.in));
bw = new BufferedWriter(new OutputStreamWriter(System.out));
while (true){
bw.write("请输入:");
bw.flush();
String input = br.readLine();
if ("exit".equals(input)){
break;
}
bw.write("您输入的是:"+input);
bw.newLine();
bw.flush();
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (br != null){
br.close();
}
if(bw != null){
bw.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
【示例】通过字节流读取文本文件并添加行号
import java.io.*;
public class LineNumberDemo2 {
public static void main(String[] args) {
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new InputStreamReader(new FileInputStream("d:/baidu.txt")));
bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("d:/baidu4.txt")));
String temp = "";
int i = 1;
while ((temp = br.readLine()) != null){
bw.write(i+" "+temp);
bw.newLine();
i++;
}
bw.flush();
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (br != null){
br.close();
}
if (bw != null){
bw.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
4.5 字符输出流
在Java的IO流中专门提供了用于字符输出的流对象PrintWriter。该对象具有自动行刷新缓冲字符输出流(在做字符输出时就可以不用再使用flush()),特点是可以按行写出字符串,并且可通过println();方法实现自动换行。
【示例】字符输出流
import java.io.*;
public class LineNumberDemo3 {
public static void main(String[] args) {
BufferedReader br = null;
PrintWriter pw = null;
try {
br = new BufferedReader(new InputStreamReader(new FileInputStream("d:/baidu.txt")));
pw = new PrintWriter("d:/baidu5.txt");
String temp = "";
int i = 1;
while ((temp = br.readLine()) != null){
pw.println(i+" "+temp);
i++;
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (br != null){
br.close();
}
if (pw != null){
pw.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
4.6 字节数组流
ByteArrayInputStream和ByteArrayOutputStream 经常用在需要流和数组之间转化的情况!
字节数组输入流
FileInputStream 是把文件当做数据源。ByteArrayInputStream 则是把内存中的”字节数组对象”当做数据源。
【示例】字节数组输入流
import java.io.ByteArrayInputStream;
public class ByteArrayInputDemo {
public static void main(String[] args) {
byte[] arr = "abcdefg".getBytes();
ByteArrayInputStream bis = null;
StringBuilder sb = new StringBuilder();
try {
//该构造法的参数是一个字节数据,这个字节数组就是数据源
bis = new ByteArrayInputStream(arr);
int temp = 0;
while ((temp = bis.read()) != -1){
sb.append((char) temp);
}
System.out.println(sb.toString());
}finally {
try {
if (bis != null){
bis.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
【示例】字节数组输出流
ByteArrayOutputStream流对象则是把流中的数据写入到自己数组中。
import java.io.ByteArrayOutputStream;
public class ByteArrayOutputDemo {
public static void main(String[] args) {
ByteArrayOutputStream bos = null;
try{
StringBuilder sb = new StringBuilder();
bos =new ByteArrayOutputStream();
bos.write('a');
bos.write('b');
bos.write('c');
byte[] arr = bos.toByteArray();
for (byte temp:arr) {
sb.append((char) temp);
}
System.out.println(sb.toString());
}finally {
try {
if (bos != null){
bos.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
注意:没有文件的写入或者写出,就没有IOExpcetion,就不用catch,也不用flush缓存刷新
4.7 数据流
数据流将“基本数据类型与字符串类型”作为数据源,从而允许程序以与机器无关的方式
从底层输入输出流中操作Java基本数据类型与字符串类型。
DatalnputStream和DataOutputStream提供了可以存取与机器无关的所有Java基础类型数据(如: int、double、 String 等)的方法。
【示例】数据输出流
import java.io.BufferedOutputStream;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
public class DataOutputDemo {
public static void main(String[] args) {
DataOutputStream dos = null;
try{
dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream("d:/data.txt")));
dos.writeChar('a');
dos.writeInt(10);
dos.writeDouble(Math.random());
dos.writeBoolean(true);
dos.writeUTF("您好");
dos.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (dos != null){
dos.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
【示例】数据输入流
能读取基于数据输出流向文件中所写出的数据通过数据输入流按照相应的类型读取到文件中
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.sql.SQLOutput;
public class DataInputDemo {
public static void main(String[] args) {
DataInputStream dis = null;
try{
dis = new DataInputStream(new BufferedInputStream(new FileInputStream("d:/data.txt")));
//直接读取数据,注意:读取的顺序要与写入的顺序一致,否则不能正确读取数据
System.out.println("char: "+dis.readChar());
System.out.println("int: "+dis.readInt());
System.out.println("double: "+dis.readDouble());
System.out.println("boolean: "+dis.readBoolean());
System.out.println("String: "+dis.readUTF());
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (dis != null){
dis.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
4.8 对象流
对象的本质是用来组织和存储数据的,对象本身也是数据。那么,能不能将对象存储到硬盘上的文件中呢?能不能将对象通过网络传输到另一个电脑呢?我们可以通过序列化和反序列化来实现这些需求。
1. Java对象的序列化和反序列化
当两个进程远程通信时,彼此可以发送各种类型的数据。无论是何种类型的数据, 都会以二进制序列的形式在网络上传送。比如,我们可以通过http协议发送字符串信息,我们也可以在网络上直接发送Java对象。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象才能正常读取。
把Java对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为Java对象的过程称为对象的反序列化。
对象序列化的作用:
- 持久化:把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中。
- 网络通信:在网络上传送对象的字节序列。比如:服务器之间的数据通信、对象传递。
序列化涉及的类和接口
ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到-一个目标输出流中。
ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
只有实现了Serializable接口的类的对象才能被序列化。Serialiable 接口是一个空接口,只起到标记作用。
2. 操作基本数据类型
我们前边学到的数据流只能实现对基本数据类型和字符串类型的读写,并不能对Java对象进行读写操作(字符串除外) ,但是在对象流中除了能实现对基本数据类型进行读写操作以外,还可以对Java对象进行读写操作。
【示例】写出基本数据类型数据
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class ObjectOutputStreamBasicTypeDemo {
public static void main(String[] args) {
ObjectOutputStream oos = null;
try{
oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("d:/test.txt")));
oos.writeInt(10);
oos.writeDouble(Math.random());
oos.writeChar('a');
oos.writeBoolean(true);
oos.writeUTF("您好!");
oos.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
try{
if (oos != null){
oos.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
【示例】读取基本数据类型数据
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class ObjectInputStreamBasicTypeDemo {
public static void main(String[] args) {
ObjectInputStream ois = null;
try{
ois = new ObjectInputStream(new BufferedInputStream(new FileInputStream("d:/test.txt")));
//必须要按照写入的顺序读取数据
System.out.println("int: "+ois.readInt());
System.out.println("dounble: "+ois.readDouble());
System.out.println("char: "+ois.readChar());
System.out.println("boolean: "+ois.readBoolean());
System.out.println("String: "+ois.readUTF());
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (ois != null){
ois.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
3. 操作对象
将对象序列化到文件
ObjectOutputStream可以将一个内存中的 Java对象通过序列化的方式写入到磁盘的文件中。被序列化的对象必须要实现Serializable序列化接口,否则会抛出异常。
【示例】创建对象
import java.io.Serializable;
public class Users implements Serializable {
private int id;
private String name;
private String age;
public Users(int id, String name, String age) {
this.id = id;
this.name = name;
this.age = age;
}
public Users() {
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
}
【示例】序列化对象
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
public class ObjectOutputStreamObjectTypeDemo {
public static void main(String[] args) {
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream("d:/test1.txt")));
Users users = new Users(1,"zhang","18");
oos.writeObject(users);
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (oos != null){
oos.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
【示例】将对象反序列化到内存中
import java.io.FileInputStream;
import java.io.ObjectInputStream;
public class ObjectInputStreamObjectTypeDemo {
public static void main(String[] args) {
ObjectInputStream ois = null;
try{
ois = new ObjectInputStream(new FileInputStream("d:/test1.txt"));
Users users = (Users) ois.readObject();
System.out.println(users.getId()+"\t"+users.getName()+"\t"+users.getAge());
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (ois != null){
ois.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
4.9 随机访问流
RandomAccessFile 可以实现两个作用:
- 实现对一个文件做读和写的操作。
- 可以访问文件的任意位置。不像其他流只能按照先后顺序读取。
在开发某些客户端软件时,经常用到这个功能强大的可以”任意操作文件内容”的类。比如,软件的使用次数和使用日期,可以通过本类访问文件中保存次数和日期的地方进行比对和修改。Java 很少开发客户端软件,所以在Java开发中这个类用的相对较少。
学习这个流需掌握三个核心方法:
- RandomAccessile(String name, String mode)
name用来确定文件; mode 取r(读)或rw(可读写),通过mode可以确定流对文件的访问权限。 - seek(long a)
用来定位流对象读写文件的位置,a确定读写位置距离文件开头的字节个数。 - getFilePointer() 获得流的当前读写位置。
【示例】随机访问流
import java.io.RandomAccessFile;
public class RandomAccessFileDemo {
public static void main(String[] args) {
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile("d:/test2.txt","rw");
//将若干数据写入到文件中
int[] arr = new int[]{10,20,30,40,50,60,70,80,90,100};
for (int i = 0; i < arr.length; i++) {
raf.writeInt(arr[i]);
}
raf.seek(4); //默认0,移动到四个字节
System.out.println(raf.readInt());
//隔一个读一个数据
for (int i = 0; i < 10; i+=2) {
raf.seek(i*4);
System.out.print(raf.readInt()+"\t");
}
System.out.println();
//在第8个字节位置插入一个新的数据45,替换之前的数据30
raf.seek(8);
raf.writeInt(45);
for (int i = 0; i < 10; i+=2) {
raf.seek(i*4);
System.out.print(raf.readInt()+"\t");
}
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
if (raf != null){
raf.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
4.10 File类在IO中的作用
当以文件作为数据源或目标时,除了可以使用字符串作为文件以及位置的指定以外,也可以使用File 类指定。
【示例】File类在IO中的作用
import java.io.*;
public class FileInDemo {
public static void main(String[] args) {
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new FileReader(new File("d:/test.txt")));
bw = new BufferedWriter(new FileWriter(new File("d:/test3.txt")));
String temp = "";
int i = 1;
while ((temp = br.readLine()) != null){
bw.write(i+" "+temp);
bw.newLine();
i++;
}
bw.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
try {
if (br != null){
br.close();
}
if (bw != null){
bw.close();
}
}catch (Exception e){
e.printStackTrace();
}
}
}
}
5. Apache I0包.
JDK中提供的文件操作相关的类,但是功能都非常基础,进行复杂操作时需要做大量编
程工作。实际开发中,往往需要你自己动手编写相关的代码,尤其在遍历目录文件时,经常用到递归,非常繁琐。Apache- commons工具包中提供了lOUtils/FileUtils, 可以让我们非常方便的对文件和目录进行操作。本文就是让大家对loUtis/FileUtils类有一个全面的认识,便于大家以后开发与文件和目录相关的功能。
Apache loUtils和FileUtils类库为我们提供了更加简单、功能更加强大的文件操作和IO流操作功能。非常值得大家学习和使用。
5.1 Apache 基金会介绍
Apache软件基金会(也就是Apache Software Foundation,简称为ASF) ,是专门为支持开源软件项目而办的一个非盈利性组织。在它所支持的Apache项目与子项目中,所发行的
软件产品都遵循Apache许可证(Apache L icense)。官方网址为::www.apache.org 。
很多著名的Java开源项目都来源于这个组织。比如: commons.、kafka、 lucene.、 maven、shiro、 struts 等技术,以及大数据技术中的: hadoop (大数据第一技术) 、hbase、 spark、storm. mahout 等。
5.2 下载与添加commons-io包
下载地址: 下载commons-io包
添加jar包:
添加完成后:
5.3 FileUtils的使用
FileUtils类中常用方法:
打开FileUtils的api文档,一些工作中比较常用的方法如下:
cleanDirectory:清空目录,但不删除目录。
contentEquals:比较两个文件的内容是否相同。
copyDirectory:将一个目录内容拷贝到另一个目录。可以通过FileFilter过滤需要拷贝的文件。.
copyFile:将一个文件拷贝到一个新的地址。
copyFileToDirectory:将一个文件拷贝到某个目录下。
copyInputStreamToFile:将一个输入流中的内容拷贝到某个文件。deleteDirectory:删除目录。
deleteQuietly:删除文件。
listFiles:列出指定目录下的所有文件。
openInputSteam:打开指定文件的输入流。
readFileToString:将文件内容作为字符串返回。
readLines:将文件内容按行返回到-一个字符串数组中。size:返回文件或目录的大小。
write:将字符串内容直接写到文件中。
writeByteArrayToFile:将字节数组内容写到文件中。
writeLines:将容器中的元素的toString方法返回的内容依次写入文件中。
writeStringToFile:将字符串内容写到文件中。
【示例】FileUtils的使用一
import org.apache.commons.io.FileUtils;
import java.io.File;
public class FileUtilsDemo1 {
public static void main(String[] args) throws Exception{
String content = FileUtils.readFileToString(new File("d:/baidu.txt"),"utf-8");
System.out.println(content);
}
}
【示例】FileUtils的使用二
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.FileFilter;
public class FileUtilsDemo2 {
public static void main(String[] args) throws Exception{
FileUtils.copyDirectory(new File("d:/a"), new File("c:/a"), new FileFilter() {
//在文件拷贝时的过滤条件
@Override
public boolean accept(File pathname) {
if (pathname.isDirectory() || pathname.getName().endsWith("html")){
return true;
}
return false;
}
});
}
}
5.4 IOUtils的使用
打开lOUtils的api文档,它的方法大部分都是重载的。
对于方法的用法总结如下:
buffer方法:将传入的流进行包装,变成缓冲流。并可以通过参数指定缓冲大小。
closeQueitly方法:关闭流。
contentEquals方法:比较两个流中的内容是否一致。
copy方法:将输入流中的内容拷贝到输出流中,并可以指定字符编码。
copyLarge方法:将输入流中的内容拷贝到输出流中,适合大于2G内容的拷贝。
linelterator 方法:返回可以迭代每一行内容的迭代器。
read方法:将输入流中的部分内容读入到字节数组中。
readFully方法:将输入流中的所有内容读入到字节数组中。
readLine方法:读入输入流内容中的每一行。
toBufferedInputStream, toBufferedReader:将输入转为带缓存的输入流。
toByteArray, toCharArray:将输入流的内容转为字节数组、字符数组。
toString:将输入流或数组中的内容转化为字符串。
write方法:向流里面写入内容。
writeLine方法:向流里面写入一行内容。
【示例】 IOUtils的使用
import org.apache.commons.io.IOUtils;
import java.io.FileInputStream;
public class IOUtilsDemo {
public static void main(String[] args) throws Exception{
String content = IOUtils.toString(new FileInputStream("d:/baidu.txt"),"utf-8");
System.out.println(content);
}
}
6. 总结
按流的方向分类:
输入流:数据源到程序(InputStream、Reader 读进来)。
输出流:程序到目的地(OutputStream、Writer 写出去)。
按流的处理数据单元分类:
字节流: 按照字节读取数据(InputStream、OutputStream)。
字符流:按照字符读取数据(Reader、Writer)。
按流的功能分类:
节点流:可以直接从数据源或目的地读写数据。
处理流:不直接连接到数据源或目的地,是处理流的流。通过对其他流的处理提高程序的性能。
IO的四个基本抽象类: InputStream、 OutputStream、Reader、Writer
InputStream的实现类:
FileInputStream
ByteArraylnputStream
BufferedInputStream
DatalnputStream
ObjectlnputStream
OutputStream的实现类:. FileOutputStream
ByteArrayOutputStream
BufferedOutputStream
DataOutputStream
ObjectOutputStream
PrintStream
Reader的实现类:
FileReader
BufferedReader
InputStreamReader
Writer的实现类:
FileWriter
BufferedWriter
OutputStreamWriter
把Java对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为 Java对象的过程称为对象的反序列化。