本周主题:IO数据流

目录

​本周主题:IO数据流​

​一、流的基础知识​

​(一)、流的概念​

​(二)、流的分类与特点​

​(三)、IO流的体系结构​

​1、IO流的总体分类​

​2、字节流和字符流体系​

​二、常用类的使用​

​(一)、File类​

​(二)、FileInputStream和FileOutputStream​

​(三)、FileReader和FileWriter​

​(四)、综合实例Demo​


一、流的基础知识

(一)、流的概念

大多数应用程序都需要实现与设备之间的数据传输,如键盘可以输入数据,显示器可以显示程序运行的结果等。任何一种编程语言都必须拥有输入/输出的处理方式,Java也不例外。在Java中将这种通过不同输入、输出设备(键盘、内在、显示器、网络等)之间的数据传输抽象表达为“流”,程序允许通过流的方式与输入/输出设备进行数据传输。Java提供了大量的类来对流进行操作,从而实现了输入/输出功能。

James Gosling所著《Java程序设计》中描述Java I/O流模式图如下。Program是中间环节,用于对Source进行处理,然后输出到Dest处。

Java第十四周作业_字节流

从Java 1.0开始,引入java.io包;到Java 1.4再扩展了java.nio包;再到java 1.7又添加了新的流类,使得Java的流机制变得十分强大。Java中的“流”都位于java.io/java.nio包中,称为IO(输入/输出)流,如下图所示。

Java第十四周作业_字节流_02

(二)、流的分类与特点

IO流有很多种,按照不同的分类标准可以分为不同的类别。

1、按数据传输方向的不同,数据流可分为输入流输出流

判断当前流是输入流还是输出流,依据的是二进制数据相对于计算机内存的位置,由于程序就是运行在内存中的,所以判断当前流是输入流还是输出流都是站在程序的立场来看待的,比如输入流是输入到计算机内存的二进制数据,输出流是从计算机内存中输出的二进制数据。比如键盘键入数据属于输入流,内存数据持久化存储到磁盘中则属于输出流。

Java第十四周作业_数据_03

2、按照操作数据的不同,可以分为字节流字符流

从物理层面来看,流中数据都是二进制比特流。而计算机中储存信息的基本单位是字节(Byte)。因此,可以认为计算机中信息传输在底层是靠字节流来实现的。字符流只是通过不同的字符编码方式,对字节流的封装,即字符流的实现还是得依靠字节流。

一般情况下,我们习惯使用字符流来处理文本类型的数据,如文本文件的读写、键盘输入的处理等,而使用字节流来处理二进制数据,如图像、音频、视频数据等。

  • a)字节流默认是不带缓冲区的,而字符流默认是带缓冲区的。
  • b)字节流是底层数据流,是数据有意义的最小单位。字符流是字节流的包装,底层实现是字节流。
  • c)基于b点,文本文件可以用字节流来实现,当然使用字符流速度会更快。

字节流用来处理没有进行加工的原始数据,字符流是经过了编码的符合某种格式规定的数据。程序从输入流中读取数据,向输出流中写入数据。

3、按照流的功能来分,流又可以分为:节点流(又称低级流)、过滤流(又称高级流、处理流、包装流)

节点流(Node Stream)是流管道两端直接连接data source和data destination上的,即为取放数据的真实载体,在流通道本身不对数据做任何加工,因而也被称为低级流。

Java第十四周作业_字节流_04

  过滤流(Filter Stream)是套在节点流或过滤上的,而且过滤流是不能够脱离节点流(低级流)存在的,因此称为高级流。过滤流的流管道本身封装方法,能够对低级流数据进行处理,因此也被称为处理流。究其本质,过滤流就是把不符合通过管道条件的数据过滤掉,而让满足条件的数据通过过滤流管道。

Java第十四周作业_java_05

(三)IO流的体系结构

1、IO流的总体分类

Java第十四周作业_数据_06

2、字节流和字符流体系

Java第十四周作业_java_07

二、常用IO类的使用

(一)、File类

在介绍其他IO流及其使用方法之前先介绍File类,因为大多数流是以文件为数据源和目的地的。Java提供的File类既可以表示文件,也可以表示目录,利用File类可以对文件或目录及其属性进行基本操作,创建和删除等,也可以获取与文件相关的信息,如名称、最后的修改日期、文件大小等。

File中的方法是对磁盘上的文件进行属性读写操作,但是无法读取文件的内容。 需要注意的是,创建一个文件对象和创建一个文件在Java中是两个不同的概念,前者是在JVM中创建了一个文件,但并没有将它真正地创建(写入)到OS的文件系统中(磁盘)。

举个File类使用测试的例子:

import java.io.File;

/**
* 文件名:MyFileTest.java
* 功能描述:File类的使用测试Demo
*/
public class MyFileTest {
public static void main(String[] args) {
//此处实例化了一个文件对象,并非是在磁盘上创建了一个文件,且此File对象是无法读取文件内容的
File file = new File("example.txt");
System.out.println("文件名称:"+file.getName());
System.out.println("文件路径:"+file.getPath());
System.out.println("文件绝对路径:"+file.getAbsolutePath());
System.out.println("文件父路径:"+file.getParent());
System.out.println(file.canRead()?"文件可读":"文件不可读");
System.out.println(file.canWrite()?"文件可写":"文件不可写");
System.out.println(file.isFile()?"是一个文件":"不是一个文件");
System.out.println(file.isDirectory()?"是一个目录":"不是一个目录");
System.out.println(file.isAbsolute()?"是一个绝对路径":"不是一个绝对路径");
System.out.println("文件最后修改时间:"+file.lastModified());
System.out.println("文件大小:"+file.length()+"bytes");
System.out.println("文件删除是否成功:"+file.delete());
}
}

运行结果:

 

Java第十四周作业_java_08

 

分析:

本例子中, E:\xxx\java_demo\week14\MyFileTest\ 目录下并不存在example.txt,因此file变量所获取的对象,既不是一个目录也不是一个文件,其父路径为null,且不可读、不可写,无文件大小,无最后修改日期、无法删除……。

(二)、FileInputStream和FileOutputStream

FileInputStream和FileOutputStream是专门用于读取/写入文件中的数据,它们操作文件的字节流的输入/输出。

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/**
* 文件名:ByteStreamTest.java
* 功能描述:字节流文件读写测试
*/
public class ByteStreamTest {
void readFile() throws IOException {
FileInputStream in = null;
try {
in = new FileInputStream("test.txt");
int b = 0;
while (true) {
b = in.read(); //一次读取一个字节
if (b == -1)
break;
System.out.printf("%d ", b);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null)
in.close();
}
}

void writeFile() throws IOException {
FileOutputStream out = null;
try {
out = new FileOutputStream("test.txt");
String str = "Java数据写入";
byte[] b = str.getBytes();
for (int i = 0; i < b.length; i++) {
out.write(b[i]);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
out.close();
}
}

public static void main(String[] args) {
ByteStreamTest test = new ByteStreamTest();
try {
test.writeFile();
test.readFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}

运行结果:

 

Java第十四周作业_数据_09

(三)、FileReader和FileWriter

 在程序开发中,经常需要对文本文件的内容进行读取/写入,如果想从文件中直接读取/写入字符,便可以使用字符输入流FileReader和输出流FileWriter。

import java.io.*;

/**
* 文件名:CharStreamTest.java
* 功能描述:字符流文件读写测试
*/
public class CharStreamTest {
void readFile() throws IOException {
FileReader in = null;
try {
in = new FileReader("test.txt");
int b = 0;
while (true) {
b = in.read(); //一次读取一个字符
if (b == -1)
break;
System.out.printf("%c", b);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (in != null)
in.close();
}
}

void writeFile() throws IOException {
FileWriter out = null;
try {
out = new FileWriter("test.txt");
String str = "Java数据写入";
out.write(str);
out.write("\r\n");
} catch (IOException e) {
e.printStackTrace();
} finally {
out.close();
}
}

public static void main(String[] args) {
CharStreamTest test = new CharStreamTest();
try {
test.writeFile();
test.readFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}

运行结果:

  

Java第十四周作业_数据_10

(四)、综合实例Demo

 分别使用字节流(FileInputStream、FileOutputStream)、字节流+缓存流(BufferedInputStream、BufferedOutputStream)、字符流(InputStreamReader、OutStreamReader)、字符流+缓存流(BufferedReader、BufferedWriter)进行文件复制的综合实例Demo。

1、文件操作类

import java.io.*;
/**
* 文件名:StreamOperator.java
* 功能描述:流操作类文件
*/
public class StreamOperator {
private final int BUF_SIZE = 10; //为演示效果,故意把缓存设置得很小,正常一般为1024的倍数

//-----------------字节流文件复制方法---------------//
void CopyFileByByteStream(String sFileName,String dFileName) throws IOException {
FileInputStream fis = null;
FileOutputStream fos = null;

long byteStart = System.currentTimeMillis();//获取系统当前时间戳
try {
fis = new FileInputStream(sFileName);
fos = new FileOutputStream(dFileName);

int len = 0;

byte[] byteArray = new byte[BUF_SIZE];

while ((len = fis.read(byteArray))>0) {
fos.write(byteArray,0,len);
}
fos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null)
fis.close();
if (fos != null)
fos.close();

long byteEnd = System.currentTimeMillis();
System.out.println("字节流复制文件完毕!共花费"+(byteEnd-byteStart)+"毫秒。");
}

}

//-----------------字节流+缓存流文件复制方法---------------//
void CopyFileByByteBufferedStream(String sFileName,String dFileName) throws IOException {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;

long startTime = System.currentTimeMillis();
try {
bis = new BufferedInputStream(new FileInputStream(sFileName));
bos = new BufferedOutputStream(new FileOutputStream(dFileName));

int len = 0;
byte[] bufArray = new byte[BUF_SIZE];
while ((len = bis.read(bufArray))>0) {
bos.write(bufArray,0,len);
}
bos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bis != null)
bis.close();
if (bos != null)
bos.close();

long endTime = System.currentTimeMillis();
System.out.println("字节流+缓存流复制文件完毕!共花费"+(endTime-startTime)+"毫秒。");
}
}

//-----------------字符流文件复制方法---------------//
void CopyFileByCharStream(String sFileName,String dFileName) throws IOException {
InputStreamReader reader = null;
OutputStreamWriter writer = null;

//FileReader fileReader = null;
//FileWriter fileWriter = null;

long startTime = System.currentTimeMillis();
try {
String charsetName = getFileCharsetName(sFileName);
reader = new InputStreamReader(new FileInputStream(sFileName),charsetName);
writer = new OutputStreamWriter(new FileOutputStream(dFileName),charsetName);

//fileReader = new FileReader(sFileName);
//fileWriter = new FileWriter(dFileName);

int len = 0;
char[] charArray = new char[BUF_SIZE];
while ((len = reader.read(charArray))>0) {
writer.write(charArray,0,len);
}
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null)
reader.close();
if (writer != null)
writer.close();
long endTime = System.currentTimeMillis();
System.out.println("字符流复制文件完毕!共花费"+(endTime-startTime)+"毫秒。");
}
}

//-----------------字符流+缓存流文件复制方法---------------//
void CopyFileByCharBufferedStream(String sFileName,String dFileName) throws IOException {
InputStreamReader reader = null;
OutputStreamWriter writer = null;

BufferedReader bufferedReader = null;
BufferedWriter bufferedWriter = null;

long startTime = System.currentTimeMillis();
try {
String charsetName = getFileCharsetName(sFileName);
reader = new InputStreamReader(new FileInputStream(sFileName),charsetName);
writer = new OutputStreamWriter(new FileOutputStream(dFileName),charsetName);

bufferedReader = new BufferedReader(reader);
bufferedWriter = new BufferedWriter(writer);

int len = 0;
char[] charArray = new char[BUF_SIZE];
while ((len = bufferedReader.read(charArray))>0) {
bufferedWriter.write(charArray,0,len);
}
bufferedWriter.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (bufferedReader != null)
bufferedReader.close();
if (bufferedWriter != null)
bufferedWriter.close();
if (reader != null)
reader.close();
if (writer != null)
writer.close();
long endTime = System.currentTimeMillis();
System.out.println("字符流+缓存流复制文件完毕!共花费"+(endTime-startTime)+"毫秒。");
}
}

/**
* 获取文本文件的编码格式
* @param fileName :file
* @return 文件编码格式
* @throws IOException
*/
public static String getFileCharsetName(String fileName) throws IOException {
InputStream inputStream = new FileInputStream(fileName);
byte[] head = new byte[3];
inputStream.read(head);

String charsetName = "GBK";//或GB2312,即ANSI
if (head[0] == -1 && head[1] == -2 ) //0xFFFE
charsetName = "UTF-16";
else if (head[0] == -2 && head[1] == -1 ) //0xFEFF
charsetName = "Unicode";//包含两种编码格式:UCS2-Big-Endian和UCS2-Little-Endian
else if(head[0]==-27 && head[1]==-101 && head[2] ==-98)
charsetName = "UTF-8"; //UTF-8(不含BOM)
else if(head[0]==-17 && head[1]==-69 && head[2] ==-65)
charsetName = "UTF-8"; //UTF-8-BOM

inputStream.close();

//System.out.println(charsetName);
return charsetName;
}
}

2、测试类文件: 

import java.io.FileInputStream;
import java.io.IOException;

/**
* 文件名:StreamOperatorTest.java
* 功能描述:测试文件
*/
public class StreamOperatorTest {
public static void main(String[] args) {
StreamOperator streamTest = new StreamOperator();

//获取*.class文件所在的目录
String path = StreamOperatorTest.class.getProtectionDomain().getCodeSource().getLocation().getPath();
//System.out.println(path);
//System.out.println(System.getProperty("user.dir"));

String sFileName = path+"回到明朝当王爷.txt";
String fileName = path+"*结果文件.txt";
String dFileName;

try {
long fileSize = new FileInputStream(sFileName).available();
System.out.printf("源文件大小为:%,d Byte!字符编码为:%s \n",fileSize,StreamOperator.getFileCharsetName(sFileName));
dFileName = fileName.replace("*","字节流复制");
streamTest.CopyFileByByteStream(sFileName,dFileName);

dFileName = fileName.replace("*","字节缓存流复制");
streamTest.CopyFileByByteBufferedStream(sFileName,dFileName);

dFileName = fileName.replace("*","字符流复制");
streamTest.CopyFileByCharStream(sFileName,dFileName);

dFileName = fileName.replace("*","字符缓存流复制");
streamTest.CopyFileByCharBufferedStream(sFileName,dFileName);
} catch (IOException e) {
e.printStackTrace();
}

}
}

3、运行结果:

 

Java第十四周作业_字节流_11