目录
流的概念
输入输出流
在 Java 中所有数据都是使用流读写的。流是一组有序的数据序列,将数据从一个地方带到另一个地方。根据数据流向的不同,可以分为输入(Input)流和输出(Output)流两种。
在学习输入和输出流之前,我们要明白为什么应用程序需要输入和输出流。例如,我们平时用的 Office 软件,对于 Word、Excel 和 PPT 文件,我们需要打开文件并读取这些文本,和编辑输入一些文本,这都需要利用输入和输出的功能。在现实生活中,输入和输出的例子比比皆是。
Java 程序通过流来完成输入/输出,所有的输入/输出以流的形式处理。因此要了解 I/O 系统,首先要理解输入/输出流的概念。
输入就是将数据从各种输入设备(包括文件、键盘等)中读取到内存中,输出则正好相反,是将数据写入到各种输出设备(比如文件、显示器、磁盘等)。例如键盘就是一个标准的输入设备,而显示器就是一个标准的输出设备,但是文件既可以作为输入设备,又可以作为输出设备。
数据流是 Java 进行 I/O 操作的对象,它按照不同的标准可以分为不同的类别。 1、按照流的方向主要分为输入流和输出流两大类。 2、数据流按照数据单位的不同分为字节流和字符流。 3、按照功能可以划分为节点流和处理流。
系统流
每个 Java 程序运行时都带有一个系统流,系统流对应的类为 java.lang.System。Sytem 类封装了 Java 程序运行时的 3 个系统流,分别通过 in、out 和 err 变量来引用。这 3 个系统流如下所示: System.in:标准输入流,默认设备是键盘。 System.out:标准输出流,默认设备是控制台。 System.err:标准错误流,默认设备是控制台。
字符编码
Java 中常见编码说明如下: ISO8859-1:属于单字节编码,最多只能表示 0~255 的字符范围。 GBK/GB2312:中文的国标编码,用来表示汉字,属于双字节编码。GBK 可以表示简体中文和繁体中文,而 GB2312 只能表示 简体中文。GBK 兼容 GB2312。 Unicode:是一种编码规范,是为解决全球字符通用编码而设计的。UTF-8 和 UTF-16 是这种规范的一种实现,此编码不兼容 I SO8859-1 编码。Java 内部采用此编码。 UTF:UTF 编码兼容了 ISO8859-1 编码,同时也可以用来表示所有的语言字符,不过 UTF 编码是不定长编码,每一个字符的长 度为 1~6 个字节不等。一般在中文网页中使用此编码,可以节省空间。
在程序中如果处理不好字符编码,就有可能出现乱码问题。例如现在本机的默认编码是 GBK,但在程序中使用了 ISO8859-1 编码,则就会出现字符的乱码问题。就像两个人交谈,一个人说中文,另外一个人说英语,语言不同就无法沟通。为了避免产生乱码,程序编码应与本地的默认编码保持一致。
现在操作系统的默认编码是 GBK。
File类
在 Java 中,File 类是 java.io 包中唯一代表磁盘文件本身的对象,也就是说,如果希望在程序中操作文件和目录,则都可以通过 File 类来完成。File 类定义了一些方法来操作文件,如新建、删除、重命名文件和目录等。
File 类不能访问文件内容本身,如果需要访问文件内容本身,则需要使用输入/输出流。
File 类提供了如下三种形式构造方法。 File(String path):如果 path 是实际存在的路径,则该 File 对象表示的是目录;如果 path 是文件名,则该 File 对象表示的是文 件。 File(String path, String name):path 是路径名,name 是文件名。 File(File dir, String name):dir 是路径对象,name 是文件名。
注意:可以看到 File 类的常量定义的命名规则不符合标准命名规则,常量名没有全部大写,这是因为 Java 的发展经过了一段相当长的时间,而命名规范也是逐步形成的,File 类出现较早,所以当时并没有对命名规范有严格的要求,这些都属于 Java 的历史遗留问题。
import java.io.File;
public class Demo01 {
public static void main(String[] args) {
/**
* File类常用方法
*/
File f1 = new File("C:/WorkAndStudy/java/demo01.txt");
System.out.println(f1);
// 测试应用程序是否能从指定的文件中进行读取
System.out.println(f1.canRead());
// 测试应用程序是否能写当前文件
System.out.println(f1.canWrite());
// 删除当前对象指定的文件
// boolean delete = f1.delete();
// System.out.println("删除文件: " + delete);
// 测试当前 File 是否存在
System.out.println(f1.exists());
// 返回由该对象表示的文件的绝对路径名
System.out.println(f1.getAbsolutePath());
// 返回表示当前对象的文件名或路径名(如果是路径,则返回最后一级子路径名)
System.out.println(f1.getName());
// 返回当前 File 对象所对应目录(最后一级子目录)的父目录名
System.out.println(f1.getParent());
// 判断当前文件对象路径是否为绝对路径
System.out.println(f1.isAbsolute());
// 测试当前 File 对象表示的文件是否为一个路径
System.out.println(f1.isDirectory());
// 判断当前file对象是否为文件
System.out.println(f1.isFile());
// 返回当前 File 对象表示的文件最后修改的时间
System.out.println(f1.lastModified());
// 返回当前 File 对象表示的文件长度
System.out.println(f1.length());
}
获取文件属性
假设有一个文件位于 C:\windows\notepad.exe
。编写 Java 程序获取并显示该文件的长度、是否可写、最后修改日期以及文件路径等属性信息。实现代码如下:
public class Test02 {
public static void main(String[] args) {
String path = "C:/windows/"; // 指定文件所在的目录
File f = new File(path, "notepad.exe"); // 建立File变量,并设定由f变量引用
System.out.println("C:\\windows\\notepad.exe文件信息如下:");
System.out.println("============================================");
System.out.println("文件长度:" + f.length() + "字节");
System.out.println("文件或者目录:" + (f.isFile() ? "是文件" : "不是文件"));
System.out.println("文件或者目录:" + (f.isDirectory() ? "是目录" : "不是目录"));
System.out.println("是否可读:" + (f.canRead() ? "可读取" : "不可读取"));
System.out.println("是否可写:" + (f.canWrite() ? "可写入" : "不可写入"));
System.out.println("是否隐藏:" + (f.isHidden() ? "是隐藏文件" : "不是隐藏文件"));
System.out.println("最后修改日期:" + new Date(f.lastModified()));
System.out.println("文件名称:" + f.getName());
System.out.println("文件路径:" + f.getPath());
System.out.println("绝对路径:" + f.getAbsolutePath());
}
创建和删除文件
File 类不仅可以获取已知文件的属性信息,还可以在指定路径创建文件,以及删除一个文件。创建文件需要调用 createNewFile() 方法,删除文件需要调用 delete() 方法。无论是创建还是删除文件通常都先调用 exists() 方法判断文件是否存在。
创建文件
import java.io.File;
import java.util.Arrays;
public class Demo04 {
public static void main(String[] args) {
/**
* 删除非空目录a
*/
String patten = "D:/LianXi/java";
// 创建非空目录a的File对象
File f = new File(patten);
// 获取当前对象下所有内容
String[] result = f.list();
System.out.println(Arrays.toString(result));
// 获取当前对像下的所有file对象
File[] files = f.listFiles();
for (File file : files) {
System.out.println(file);
}
// 判断是否为文件或者目录
}
删除文件
import java.io.File;
public class Demo05 {
public static void main(String[] args) {
/**
* 使用递归删除C:\WorkAndStudy\java
* 目录
*/
// 创建file对象
File f = new File("C:\\WorkAndStudy\\java");
// 调用递归删除方法
del(f);
}
/**
* 删除文件或者目录
* @param file 文件或者目录对象
*/
public static void del(File file){
// 判断file对象是否为目录
if (file.isDirectory()) {
// 是目录,获取当前对象下的所有file对象
File[] files = file.listFiles();
for (File f : files) {
// 调用del函数执行删除
del(f);
}
}
// 非目录可直接删除
file.delete();
System.out.println("删除成功");
}
创建和删除目录
File 类除了对文件的创建和删除外,还可以创建和删除目录。创建目录需要调用 mkdir() 方法,删除目录需要调用 delete() 方法。无论是创建还是删除目录都可以调用 exists() 方法判断目录是否存在。
import java.io.File;
import java.util.Arrays;
public class Demo04 {
public static void main(String[] args) {
/**
* 删除非空目录a
*/
String patten = "C:/WorkAndStudy/java/a";
// 创建非空目录a的File对象
File f = new File(patten);
// 获取当前对象下所有内容
String[] result = f.list();
System.out.println(Arrays.toString(result));
// 获取当前对像下的所有file对象
File[] files = f.listFiles();
for (File file : files) {
System.out.println(file);
}
// 判断是否为文件或者目录
}
RandomAccessFile类
所谓动态读取是指从文件的任意位置开始访问文件,而不是必须从文件开始位置读取到文件末尾。动态读取需要用到 Java 中的 RandomAccessFile 类。
RandomAccessFile 是 Java 输入/输出流体系中功能最丰富的文件内容访问类,它提供了众多的方法来访问文件内容,它既可以读取文件内容,也可以向文件输出数据。由于 RandomAccessFile 可以从任意位置访问文件,所以在只需要访问文件部分内容的情况下,使用 RandonAccessFile 类是一个很好的选择。
RandomAccessFile 对象包含了一个记录指针,用以标识当前读写处的位置,当程序新创建一个 RandomAccessFile 对象时,该对象的文件记录指针位于文件头(也就是 0 处),当读/写了 n 个字节后,文件记录指针将会向后移动 n 个字节。除此之外,RandonAccessFile 可以自由移动该记录指针,既可以向前移动,也可以向后移动。
注意:如果使用 rw 方式声明 RandomAccessFile 对象时,要写入的文件不存在,系统将自动进行创建。
写入文件,代码如下:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
public class RandomAccessFileDemo01 {
public static void main(String[] args) throws Exception {
/**
* RandomAccessFile 是 Java 输入/输出流体系中功能最丰富的文件内容访问类,
* 它提供了众多的方法来访问文件内容,它既可以读取文件内容,也可以向文件输出数据。
* 由于 RandomAccessFile 可以从任意位置访问文件,所以在只需要访问文件部分内容的情况下,
* 使用 RandomAccessFile 类是一个很好的选择
*
* RandomAccessFile类提供了两个构造方法:
* RandomAccessFile(File file, String mode)
* 其中file表示文件对象,mode表示文档的属性(常用属性有r:只读模式,w:只写模式,
* rw:可读可写模式)
*
* RandomAccessFile(String path, String mode)
* 其中path表示文件所在路径,mode表示文档的属性(常用属性有r:只读模式,w:只写模式,
* rw:可读可写模式)
* 注意:当指定路径下文件不存在,则会创建改文件对象
*/
// 任务:将数据写入dir/a.txt文件当中
// 创建file对象
File f = new File("dir/a.txt");
// 创建随机文件对象
RandomAccessFile raf = new RandomAccessFile(f, "rw");
// 将hello java写入文件当中
// String content = "Hello Java";
String content = "今天安顺天气真好";
byte[] bytes = content.getBytes("UTF-8");
/**
* 注意:
* 每次写入内容时都会清空原本的内容
*/
raf.write(bytes);
raf.write(1);
System.out.println("写入完毕");
// 关闭文件
raf.close();
}
读取文件,代码如下:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
public class RandomAccessFileDemo02 {
public static void main(String[] args) throws Exception {
/**
* 读取文件
*/
// 创建file对象
File f = new File("dir/a.txt");
// 创建随机文件对象
RandomAccessFile raf = new RandomAccessFile(f, "r");
// 读取文件
// int read = raf.read();// 每次读取一个字节
// String s = raf.readLine();// 读取一行数据
// 获取文件内容长度
long length = raf.length();
byte[] data = new byte[(int) length];
// 读取数据
raf.read(data);
// 将二进制数据转为中文
String result = new String(data, "Utf-8");
System.out.println(result);
// 关闭文件流
raf.close();
}
根据上面所学的制作一个简易的记事本程序
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.util.Scanner;
public class RAFDemo03 {
public static void main(String[] args) throws Exception {
/**
* 任务:
* 制作一个简易的记事本程序
* 要求:
* 用户输入记事本名字打开记事本,如果不存在则创建,当用户输入Q或者q的时候
* 保存并且退出记事本
*/
// 创建扫描器对象
Scanner scan = new Scanner(System.in);
System.out.println("欢迎来到记事本,请输入要打开的记事本名字,如果不存在则会创建:");
String name = scan.nextLine();
// 创建随机读取文件对象
RandomAccessFile raf = new RandomAccessFile("dir/"+name+".txt", "rw");
String cont = "";
while (true) {
System.out.println("请输入内容:");
cont = scan.nextLine();
// 判断用户是否退出
if ("Q".equals(cont) || "q".equals(cont)) {
// 用户退出
break;
}
// 写入数据
raf.write(cont.getBytes("utf-8"));
}
// 关闭文件
raf.close();
}
文件指针
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
public class Demo04 {
public static void main(String[] args) throws Exception {
/**
* 文件指针
* 当读取到文件末尾的时候返回-1
*/
// 打开文件
RandomAccessFile raf = new RandomAccessFile("dir/a.txt", "rw");
// 获取当前文件指针的位置
System.out.println(raf.getFilePointer());
// 写入数据
raf.write("今".getBytes("utf-8"));
// 获取当前文件指针的位置
System.out.println(raf.getFilePointer());
// 写入数据
raf.write("天".getBytes("utf-8"));
// 获取当前文件指针的位置
System.out.println(raf.getFilePointer());
// 写入数据
raf.write("心".getBytes("utf-8"));
// 获取当前文件指针的位置
System.out.println(raf.getFilePointer());
// 写入数据
raf.write("情".getBytes("utf-8"));
// 获取当前文件指针的位置
System.out.println(raf.getFilePointer());
System.out.println("************************");
// 移动指针位置
raf.seek(3);
// 获取当前文件指针的位置
System.out.println(raf.getFilePointer());
}
文件复制
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
public class Demo05 {
public static void main(String[] args) throws Exception {
/**
* 文件复制
* 要求:将dir目录下的patten.txt文件复制到patten_back.txt文件中
*/
// 创建读文件对象
RandomAccessFile raf1 = new RandomAccessFile("dir/pattern.txt", "r");
// 创建写文件对象
RandomAccessFile raf2 = new RandomAccessFile("dir/pattern_back.txt", "rw");
int len = -1;
long start = System.currentTimeMillis();
while ((len = raf1.read()) != -1) {
// 写入文件
raf2.write(len);
}
long end = System.currentTimeMillis();
System.out.println("复制文件完毕,耗时:"+(end - start)+"毫秒");
// 关闭文件
raf1.close();
raf2.close();
}
字节流的使用
InputStream 是 Java 所有字节输入流类的父类,OutputStream 是 Java 所有字节输出流类的父类,它们都是一个抽象类,因此继承它们的子类要重新定义父类中的抽象方法。
字节输入流
InputStream 类及其子类的对象表示字节输入流,InputStream 类的常用子类如下。
ByteArrayInputStream 类:将字节数组转换为字节输入流,从中读取字节。
FileInputStream 类:从文件中读取数据。
PipedInputStream 类:连接到一个 PipedOutputStream(管道输出流)。
SequenceInputStream 类:将多个字节输入流串联成一个字节输入流。
ObjectInputStream 类:将对象反序列化。
使用 InputStream 类的方法可以从流中读取一个或一批字节。下表列出了 InputStream 类的常用方法。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/**
* FileInputStream: 文件字节输入流
* FileInputStream从文件系统中的文件获取输入字节。 什么文件可用取决于主机环境。
* FileInputStream用于读取诸如图像数据的原始字节流。 要阅读字符流,请考虑使用FileReader 。
*
* 构造方法
* FileInputStream(File file)
* 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
* FileInputStream(FileDescriptor fdObj)
* 通过使用文件描述符 fdObj创建 FileInputStream ,该文件描述符表示与文件系统中的实际文件的现有连接。
* FileInputStream(String name)
* 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
*/
public class FileInputStreamDemo01 {
public static void main(String[] args) throws Exception {
// 创建fileInputStream对象
FileInputStream fis = new FileInputStream("dir/a.txt");
// 常用方法
for (int i = 0; i < 10; i++) {
int read = fis.read();
System.out.println(fis.read(new byte[1024]));
System.out.println(read);
// 将输入流的指针返回到设置标记的起始处
// fis.reset();
}
// 关闭文件
fis.close();
}
}
字节输出流
OutputStream 类及其子类的对象表示一个字节输出流。OutputStream 类的常用子类如下。
ByteArrayOutputStream 类:向内存缓冲区的字节数组中写数据。
FileOutputStream 类:向文件中写数据。
PipedOutputStream 类:连接到一个 PipedlntputStream(管道输入流)。
ObjectOutputStream 类:将对象序列化。
利用 OutputStream 类的方法可以从流中写入一个或一批字节。下表列出了 OutputStream 类的常用方法。
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/**
* 文件字节输出流 FileOutputStream
* 文件输出流是用于将数据写入File或FileDescriptor的输出流。
* 文件是否可用或可能被创建取决于底层平台。 特别是某些平台允许一次只能打开一个文件来写入一个
* FileOutputStream (或其他文件写入对象)。 在这种情况下,如果所涉及的文件已经打开,
* 则此类中的构造函数将失败。
*
* FileOutputStream用于写入诸如图像数据的原始字节流。 要编写字符流,请考虑使用FileWriter 。
*
* 构造函数:
* FileOutputStream(File file)
* 创建文件输出流以写入由指定的 File对象表示的文件。
* FileOutputStream(FileDescriptor fdObj)
* 创建文件输出流以写入指定的文件描述符,表示与文件系统中实际文件的现有连接。
* FileOutputStream(File file, boolean append)
* 创建文件输出流以写入由指定的 File对象表示的文件。
* 参数
* file - 要打开的文件进行写入。
* append - 如果是 true ,则字节将写入文件的末尾而不是开头
* FileOutputStream(String name)
* 创建文件输出流以指定的名称写入文件。
* FileOutputStream(String name, boolean append)
* 创建文件输出流以指定的名称写入文件。
* 参数
* file - 要打开的文件进行写入。
* append - 如果是 true ,则字节将写入文件的末尾而不是开头
*/
public class FileOutputStreamDemo02 {
public static void main(String[] args) throws Exception {
// 创建字节输出流对象
FileOutputStream fos = new FileOutputStream("dir/fos.txt");
// 写入数据
String str = "慕然抬首已是秋,方觉岁月太匆匆。";
fos.write(str.getBytes("utf-8"));
// 关闭文件
fos.close();
}
}
字节数组输入流
ByteArrayInputStream 类可以从内存的字节数组中读取数据,该类有如下两种构造方法重载形式。
1、ByteArrayInputStream(byte[] buf):创建一个字节数组输入流,字节数组类型的数据源由参数 buf 指定。
2、ByteArrayInputStream(byte[] buf,int offse,int length):创建一个字节数组输入流,其中,参数 buf 指定字节数组类型的数据源,offset 指定在数组中开始读取数据的起始下标位置,length 指定读取的元素个数。
字节数组输出流
ByteArrayOutputStream 类可以向内存的字节数组中写入数据,该类的构造方法有如下两种重载形式。
1、ByteArrayOutputStream():创建一个字节数组输出流,输出流缓冲区的初始容量大小为 32 字节。
2、ByteArrayOutputStream(int size):创建一个字节数组输出流,输出流缓冲区的初始容量大小由参数 size 指定。
ByteArrayOutputStream 类中除了有前面介绍的字节输出流中的常用方法以外,还有如下两个方法。
1、intsize():返回缓冲区中的当前字节数。
2、byte[] toByteArray():以字节数组的形式返回输出流中的当前内容。
文件输入流
FileInputStream 是 Java 流中比较常用的一种,它表示从文件系统的某个文件中获取输入字节。通过使用 FileInputStream 可以访问文件中的一个字节、一批字节或整个文件。
在创建 FileInputStream 类的对象时,如果找不到指定的文件将拋出 FileNotFoundException 异常,该异常必须捕获或声明拋出。
FileInputStream 常用的构造方法主要有如下两种重载形式。
FileInputStream(File file):通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。
FileInputStream(String name):通过打开一个到实际文件的链接来创建一个 FileInputStream,该文件通过文件系统中的路 径名 name 指定。
注意:FileInputStream 类重写了父类 InputStream 中的 read() 方法、skip() 方法、available() 方法和 close() 方法,不支持 mark() 方法和 reset() 方法。
文件输出流
FileOutputStream 类继承自 OutputStream 类,重写和实现了父类中的所有方法。
FileOutputStream 类的对象表示一个文件字节输出流,可以向流中写入一个字节或一批字节。在创建 FileOutputStream 类的对象时,如果指定的文件不存在,则创建一个新文件;如果文件已存在,则清除原文件的内容重新写入。
FileOutputStream 类的构造方法主要有如下 4 种重载形式。
FileOutputStream(File file):创建一个文件输出流,参数 file 指定目标文件。
FileOutputStream(File file,boolean append):创建一个文件输出流,参数 file 指定目标文件,append 指定是否将数据添加到目标文件的内容末尾,如果为 true,则在末尾添加;如果为 false,则覆盖原有内容;其默认值为 false。
FileOutputStream(String name):创建一个文件输出流,参数 name 指定目标文件的文件路径信息。
FileOutputStream(String name,boolean append):创建一个文件输出流,参数 name 和 append 的含义同上。
注意:使用构造方法 FileOutputStream(String name,boolean append) 创建一个文件输出流对象,它将数据附加在现有文件的末尾。该字符串 name 指明了原文件,如果只是为了附加数据而不是重写任何已有的数据,布尔类型参数 append 的值应为 true。
对文件输出流有如下四点说明:
1、在 FileOutputStream 类的构造方法中指定目标文件时,目标文件可以不存在。
2、目标文件的名称可以是任意的,例如 D:\abc、D:\abc.de 和 D:\abc.de.fg 等都可以,可以使用记事本等工具打开并浏览这些文件中的内容。
3、目标文件所在目录必须存在,否则会拋出 java.io.FileNotFoundException 异常。
4、目标文件的名称不能是已存在的目录。例如 D 盘下已存在 Java 文件夹,那么就不能使用 Java 作为文件名,即不能使用 D:\Java,否则抛出 java.io.FileNotFoundException 异常。
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/**
* 文件字节输出流 FileOutputStream
* 文件输出流是用于将数据写入File或FileDescriptor的输出流。
* 文件是否可用或可能被创建取决于底层平台。 特别是某些平台允许一次只能打开一个文件来写入一个
* FileOutputStream (或其他文件写入对象)。 在这种情况下,如果所涉及的文件已经打开,
* 则此类中的构造函数将失败。
*
* FileOutputStream用于写入诸如图像数据的原始字节流。 要编写字符流,请考虑使用FileWriter 。
*
* 构造函数:
* FileOutputStream(File file)
* 创建文件输出流以写入由指定的 File对象表示的文件。
* FileOutputStream(FileDescriptor fdObj)
* 创建文件输出流以写入指定的文件描述符,表示与文件系统中实际文件的现有连接。
* FileOutputStream(File file, boolean append)
* 创建文件输出流以写入由指定的 File对象表示的文件。
* 参数
* file - 要打开的文件进行写入。
* append - 如果是 true ,则字节将写入文件的末尾而不是开头
* FileOutputStream(String name)
* 创建文件输出流以指定的名称写入文件。
* FileOutputStream(String name, boolean append)
* 创建文件输出流以指定的名称写入文件。
* 参数
* file - 要打开的文件进行写入。
* append - 如果是 true ,则字节将写入文件的末尾而不是开头
*/
public class FileOutputStreamDemo03 {
public static void main(String[] args) throws Exception {
// 创建字节输出流对象
FileOutputStream fos = new FileOutputStream("dir/fos.txt", true);
// 写入数据
String str = "晓看天色,暮看云....";
fos.write(str.getBytes("utf-8"));
// 关闭文件
fos.close();
}
}
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.util.Date;
/**
* 使用文件字节流对象实现复制功能
*/
public class CopyDemo04 {
public static void main(String[] args) throws Exception {
// 创建读取文件字节流对象
FileInputStream fis = new FileInputStream("dir/a.txt");
// 创建输出文件字节流对象
FileOutputStream fos = new FileOutputStream("dir/a_back.dat");
// 定义每次读取的大小
byte[] data = new byte[1024 * 8];
int len = -1;
// 循环读取和写入文件
while ((len = fis.read(data)) != -1) {
// 写入数据
fos.write(data, 0, len);
}
// 关闭流
fis.close();
fos.close();
}
}
字符流的使用
尽管 Java 中字节流的功能十分强大,几乎可以直接或间接地处理任何类型的输入/输出操作,但利用它却不能直接操作 16 位的 Unicode 字符。这就要用到字符流。
字符输入流
Reader 类是所有字符流输入类的父类,该类定义了许多方法,这些方法对所有子类都是有效的。
Reader 类的常用子类如下。
CharArrayReader 类:将字符数组转换为字符输入流,从中读取字符。
StringReader 类:将字符串转换为字符输入流,从中读取字符。
BufferedReader 类:为其他字符输入流提供读缓冲区。
PipedReader 类:连接到一个 PipedWriter。
InputStreamReader 类:将字节输入流转换为字符输入流,可以指定字符编码。
与 InputStream 类相同,在 Reader 类中也包含 close()、mark()、skip() 和 reset() 等方法,这些方法可以参考 InputStream 类的方法。下面主要介绍 Reader 类中的 read() 方法。
import javax.imageio.stream.FileImageInputStream;
import java.io.*;
/**
* BufferedReader 类主要用于辅助其他字符输入流,它带有缓冲区,可以先将一批数据读到内存缓冲区。
* 接下来的读操作就可以直接从缓冲区中获取数据,而不需要每次都从数据源读取数据并进行字符编码转换
* ,这样就可以提高数据的读取效率。
*/
public class BufferedReaderDemo01 {
public static void main(String[] args) throws Exception {
// 创建字节流对象
FileInputStream fis = new FileInputStream("dir/osw.txt");
// 创建字符流对象
InputStreamReader isr = new InputStreamReader(fis, "utf-8");
// 创建缓冲流对象
BufferedReader br = new BufferedReader(isr);
// 读
String s = br.readLine();
System.out.println(s);
String s1 = br.readLine();
System.out.println(s1);
// 读取到文件末尾结果为null
String s2 = br.readLine();
System.out.println(s2);
// 关闭资源
fis.close();
isr.close();
br.close();
}
}
字符输出流
与 Reader 类相反,Writer 类是所有字符输出流的父类,该类中有许多方法,这些方法对继承该类的所有子类都是有效的。
Writer 类的常用子类如下。
CharArrayWriter 类:向内存缓冲区的字符数组写数据。
StringWriter 类:向内存缓冲区的字符串(StringBuffer)写数据。
BufferedWriter 类:为其他字符输出流提供写缓冲区。
PipedWriter 类:连接到一个 PipedReader。
OutputStreamReader 类:将字节输出流转换为字符输出流,可以指定字符编码。
与 OutputStream 类相同,Writer 类也包含 close()、flush() 等方法,这些方法可以参考 OutputStream 类的方法。下面主要介绍 Writer 类中的 write() 方法和 append() 方法,如下表所示。
import java.io.*;
/**
* BufferedWriter 类主要用于辅助其他字符输出流,它同样带有缓冲区,可以先将一批数据写入缓冲区,
* 当缓冲区满了以后,再将缓冲区的数据一次性写到字符输出流,其目的是为了提高数据的写效率。
*/
public class BufferedWriterDemo02 {
public static void main(String[] args) throws Exception {
// 创建字节输出流对象
FileOutputStream fos = new FileOutputStream("dir/bw.txt");
// 创建字符输出流对象
OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8");
// 创建输出缓冲流对象
BufferedWriter bw = new BufferedWriter(osw);
// 写
bw.write("BufferedWriter 类主要用于辅助其他字符输出流,它同样带有缓冲区,可以先将一批数据写入缓冲区,");
// 写入一行分隔符
bw.newLine();
bw.write("当缓冲区满了以后,再将缓冲区的数据一次性写到字符输出流,其目的是为了提高数据的写效率。");
// 关闭资源
// fos.close();
// osw.close();
bw.close();
}
}
字符文件输入流
为了读取方便,Java 提供了用来读取字符文件的便捷类——FileReader。该类的构造方法有如下两种重载形式。
1、FileReader(File file):在给定要读取数据的文件的情况下创建一个新的 FileReader 对象。其中,file 表示要从中读取数据的文件。
2、FileReader(String fileName):在给定从中读取数据的文件名的情况下创建一个新 FileReader 对象。其中,fileName 表示要从中读取数据的文件的名称,表示的是一个文件的完整路径。
在用该类的构造方法创建 FileReader 读取对象时,默认的字符编码及字节缓冲区大小都是由系统设定的。要自己指定这些值,可以在 FilelnputStream 上构造一个 InputStreamReader。
注意:在创建 FileReader 对象时可能会引发一个 FileNotFoundException 异常,因此需要使用 try catch 语句捕获该异常。
字符流和字节流的操作步骤相同,都是首先创建输入流或输出流对象,即建立连接管道,建立完成后进行读或写操作,最后关闭输入/输出流通道。
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
/**
* 字符流(高级流)\转换流
* 作用:专用于与文本的读写
*
* 对应的构造方法:
* OutputStreamWriter(OutputStream out) 该构造方法会使用相同默认的字符编码
* OutputStreamWriter(OutputStream out, String charsetName) 该构造方法可以指定字符集
*
* OutputStreamWriter是从字符流到字节流的桥梁:使用指定的charset将写入的字符编码为字节。
* 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
* 每次调用write()方法都会使编码转换器在给定的字符上被调用。 所得到的字节在写入底层输出流之前累
* 积在缓冲区中。 请注意,传递给write()方法的字符不会缓冲。
*为了最大的效率,请考虑在BufferedWriter中包装一个OutputStreamWriter,以避免频繁的转换器调用。
*
*/
public class OutputStreamWriterDemo01 {
public static void main(String[] args) throws Exception {
// 创建字节输出流对象
FileOutputStream os = new FileOutputStream("dir/osw.txt");
// 字符输出缓冲流
OutputStreamWriter osw = new OutputStreamWriter(os, "utf-8");
//
osw.append("\t");
// 写出字符串
String str = "OutputStreamWriter是从字符流到字节流的桥梁";
osw.write(str);
osw.append("\n");
// 写出字符串
String str2 = "OutputStreamWriter是从字符流到字节流的桥梁";
osw.write(str2);
// 刷新流
osw.flush();
// 关闭资源
os.close();
osw.close();
}
}
字符文件输出流
Java 提供了写入字符文件的便捷类——FileWriter,该类的构造方法有如下 4 种重载形式。
1、FileWriter(File file):在指定 File 对象的情况下构造一个 FileWriter 对象。其中,file 表示要写入数据的 File 对象。
2、FileWriter(File file,boolean append):在指定 File 对象的情况下构造一个 FileWriter 对象,如果 append 的值为 true,则将字节写入文件末尾,而不是写入文件开始处。
3、FileWriter(String fileName):在指定文件名的情况下构造一个 FileWriter 对象。其中,fileName 表示要写入字符的文件名,表示的是完整路径。
4、FileWriter(String fileName,boolean append):在指定文件名以及要写入文件的位置的情况下构造 FileWriter 对象。其中,append 是一个 boolean 值,如果为 true,则将数据写入文件末尾,而不是文件开始处。
在创建 FileWriter 对象时,默认字符编码和默认字节缓冲区大小都是由系统设定的。要自己指定这些值,可以在 FileOutputStream 上构造一个 OutputStreamWriter 对象。
FileWriter 类的创建不依赖于文件存在与否,如果关联文件不存在,则会自动生成一个新的文件。在创建文件之前,FileWriter 将在创建对象时打开它作为输出。如果试图打开一个只读文件,将引发一个 IOException 异常。
注意:在创建 FileWriter 对象时可能会引发 IOException 或 SecurityException 异常,因此需要使用 try catch 语句捕获该异常。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStreamReader;
/**
* 高级流(字符流)\转换流
* InputStreamReader是从字节流到字符流的桥梁:它读取字节,并使用指定的charset将其解码为字符。
* 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集。
* 每个调用InputStreamReader的read()方法之一可能会导致从底层字节输入流读取一个或多个字节。
* 为了使字节有效地转换为字符,可以从底层流读取比满足当前读取操作所需的更多字节。
*
* 构造方法 Constructor 描述
* InputStreamReader(InputStream in)
* 创建一个使用默认字符集的InputStreamReader。
* InputStreamReader(InputStream in, String charsetName)
* 创建一个使用命名字符集的InputStreamReader。
* InputStreamReader(InputStream in, Charset cs)
* 创建一个使用给定字符集的InputStreamReader。
* InputStreamReader(InputStream in, CharsetDecoder dec)
* 创建一个使用给定字符集解码器的InputStreamReader。
*
* 为了最大的效率,请考虑在BufferedReader中包装一个InputStreamReader。
*/
public class InputStreamReaderDemo02 {
public static void main(String[] args) throws Exception {
// 字节流对象
FileInputStream fis = new FileInputStream("dir/osw.txt");
// 创建字符输入流对象
InputStreamReader isr = new InputStreamReader(fis, "utf-8");
// 读取字符
int read = isr.read(new char[10]);
System.out.println(read);
// 关闭 资源
fis.close();
isr.close();
}
}
字符缓冲区输入流
BufferedReader 类主要用于辅助其他字符输入流,它带有缓冲区,可以先将一批数据读到内存缓冲区。接下来的读操作就可以直接从缓冲区中获取数据,而不需要每次都从数据源读取数据并进行字符编码转换,这样就可以提高数据的读取效率。
BufferedReader 类的构造方法有如下两种重载形式。
1、BufferedReader(Reader in):创建一个 BufferedReader 来修饰参数 in 指定的字符输入流。
2、BufferedReader(Reader in,int size):创建一个 BufferedReader 来修饰参数 in 指定的字符输入流,参数 size 则用于指定缓冲区的大小,单位为字符。
除了可以为字符输入流提供缓冲区以外,BufferedReader 还提供了 readLine()
方法,该方法返回包含该行内容的字符串,但该字符串中不包含任何终止符,如果已到达流末尾,则返回 null。readLine() 方法表示每次读取一行文本内容,当遇到换行(\n)、回车(\r)或回车后直接跟着换行标记符即可认为某行已终止。
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/**
* 通过字节缓冲流的方式将给定的内容写入到文件当中去
*
* public class BufferedOutputStream extends FilterOutputStream
* 该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,
* 而不必为写入的每个字节导致底层系统的调用。
*
* 构造方法摘要
* 构造方法 Constructor 描述
* BufferedOutputStream(OutputStream out)
* 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
* BufferedOutputStream(OutputStream out, int size)
* 创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。
*/
public class BufferedOutputStreamDemo01 {
public static void main(String[] args) throws Exception {
// 创建流对象
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("dir/bos.txt"));
// 创建需要写入的字符串
String str = "该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节";
// 写入数据
bos.write(str.getBytes("utf-8"));
// 关闭流
bos.close();
}
}
字符缓冲区输出流
BufferedWriter 类主要用于辅助其他字符输出流,它同样带有缓冲区,可以先将一批数据写入缓冲区,当缓冲区满了以后,再将缓冲区的数据一次性写到字符输出流,其目的是为了提高数据的写效率。
BufferedWriter 类的构造方法有如下两种重载形式。
1、BufferedWriter(Writer out):创建一个 BufferedWriter 来修饰参数 out 指定的字符输出流。
2、BufferedWriter(Writer out,int size):创建一个 BufferedWriter 来修饰参数 out 指定的字符输出流,参数 size 则用于指定缓冲区的大小,单位为字符。
该类除了可以给字符输出流提供缓冲区之外,还提供了一个新的方法 newLine(),该方法用于写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义,并且不一定是单个新行(\n)符。
提示:BufferedWriter 类的使用与 FileWriter 类相同。
转换流
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
/**
* public class BufferedInputStream extends FilterInputStreamA
*
* BufferedInputStream为另一个输入流添加了功能,即缓冲输入并支持mark和reset方法的功能。
* 创建BufferedInputStream将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区
* 将根据需要从所包含的输入流中重新填充,一次很多字节。 mark操作会记住输入流中的一个点,并且
* reset操作会导致从最近的mark操作读取的所有字节在从包含的输入流中取出新字节之前重新读取。
*
* 构造方法 Constructor 描述
* BufferedInputStream(InputStream in)
* 创建一个 BufferedInputStream并保存其参数,输入流 in供以后使用。
* BufferedInputStream(InputStream in, int size)
* 创建具有指定缓冲区大小的 BufferedInputStream ,并保存其参数,输入流 in供以后使用。
*/
public class BufferedInputStreamDemo02 {
public static void main(String[] args) throws Exception {
// 创建字节输入流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("dir/bos.txt"));
// 创建读缓冲区大小
byte[] data = new byte[1024 * 8];
// 将读取数据转出字符串输出
bis.read(data);
String result = new String(data, "utf-8");
System.out.println(result);
// 关闭流
bis.close();
}
}
正常情况下,字节流可以对所有的数据进行操作,但是有些时候在处理一些文本时我们要用到字符流,比如,查看文本的中文时就是需要采用字符流更为方便。所以 Java IO 流中提供了两种用于将字节流转换为字符流的转换流。
InputStreamReader 用于将字节输入流转换为字符输入流,其中 OutputStreamWriter 用于将字节输出流转换为字符输出流。使用转换流可以在一定程度上避免乱码,还可以指定输入输出所使用的字符集。
由于 BufferedReader 具有一个 readLine() 方法,可以非常方便地进行一次读入一行内容,所以经常把读入文本内容地输入流包装成 BufferedReader,用来方便地读取输入流的文本内容。
import java.io.FileNotFoundException;
import java.io.PrintWriter;
/**
* java.io.PrintWriter
* 具有自动行刷新的缓冲字符流,开发中比较常用的字符高级流
* 可以按行进行写出字符串
* @author ASUS
*
*/
public class PrintWriterDemo01 {
public static void main(String[] args) throws Exception {
/**
* PrintWriter提供了专门针对文件的构造方法
* PrintWriter(String path)
* PrintWriter(File file)
*/
// 创建流对象
PrintWriter pw = new PrintWriter("dir/pw.txt");
// 写入数据
pw.println("这个Java高级流");
pw.println("PrintWriter提供了专门针对文件的构造方法");
// 关闭流
pw.close();
}
}
import java.io.*;
public class PrintWriterDemo02 {
public static void main(String[] args) {
PrintWriter pw = null;
try {
// 创建字节流
FileOutputStream fos = new FileOutputStream("dir/pw1.txt");
// 创建转换流,将字节流转为为字符流
OutputStreamWriter osw = new OutputStreamWriter(fos, "utf-8");
// 创建缓冲流
BufferedWriter bw = new BufferedWriter(osw);
// 创建printWriter高级流
pw = new PrintWriter(bw);
// 写入数据
pw.println("俄罗斯套娃");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 关闭流
if (pw != null) {
pw.close();
}
}
}
}