IO流
IO流引入
IO流之前我们的数据都是存在内存中的。内存中的数据程序结束就没有了。但是在实际开发中很多时候我们的数据是需要持久化保存的。持久化保存的数据需要保存在硬盘的文件上。那么如何才能将数据保存到文件上?如何才能如何文件上的数据呢?这就需要用到IO流了。
IO流
IO:Input/Output 输入输出
流(Stream):数据流。连续不断的数据。
操作系统本身就有IO功能。
但是今天我们学习的是使用java提供的IO流API操作文件数据。
JavaIO和系统IO的关系

IO流的分类
根据数据的流向分类
- 输入流:读数据
- 输出流:写数据
根据操作数据的不同分类
- 字节流:IO读写数据的单位是字节
- 字符流:IO读写数据的单位是字符
综合以上两种分类就得到了JavaIO中的四大基本抽象流:
JavaIO中的四大基本抽象流:
- 字节输入流(InputStream): 按照字节读数据
- 字节输出流 (OutputStream):按照字节写数据
- 字符输入流(Reader):按照字符读数据
- 字符输出流(Writer): 按照字符写数据
OutputStream-字节输出流
public abstract class OutputStream extends Object implements Closeable, Flushable
此抽象类是表示输出字节流的所有类的超类。
常用方法
| 方法摘要 | |
|---|---|
void |
close()关闭此输出流并释放与此流有关的所有系统资源。 |
void |
flush()刷新此输出流并强制写出所有缓冲的输出字节。 |
void |
write(byte[] b)将 b.length个字节从指定的 byte 数组写入此输出流。 |
void |
write(byte[] b, int off, int len)将指定 byte 数组中从偏移量 off开始的 len个字节写入此输出流。 |
abstract void |
write(int b)将指定的字节写入此输出流。 |
OutputStream是一个抽象类,所以我们需要使用其子类来完成数据的写操作。
FileOutputStream - 文件字节输出流 ⭐⭐⭐
文件输出流是用于将数据写入 File 的输出流。
单字节写数据
package com.powernode.p1;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;/*** FileOutputStream类:** 构造方法摘要* FileOutputStream(File file)* 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。* FileOutputStream(File file, boolean append)* 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。* FileOutputStream(String name)* 创建一个向具有指定名称的文件中写入数据的输出文件流。* FileOutputStream(String name, boolean append)* 创建一个向具有指定 name 的文件中写入数据的输出文件流*** 方法摘要* void close()* 关闭此文件输出流并释放与此流有关的所有系统资源。* void write(byte[] b)* 将 b.length 个字节从指定 byte 数组写入此文件输出流中。* void write(byte[] b, int off, int len)* 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。* void write(int b)* 将指定字节写入此文件输出流。***/public class Demo01 {public static void main(String[] args) {FileOutputStream fos = null;try {// 1. 创建对象// 创建文件流对象的时候,如果文件不存在会创建文件// 创建文件流对象的时候,如果文件存在,就要根据参数append来决定:// 如果append是true就在原有文件内容末尾追加;false就是覆盖原有文件// 如果文件路径中的文件夹不存在就会抛出FileNotFoundExceptionfos = new FileOutputStream(new File("D://a.txt"),true);// 2. 让对象干活 -- 调用对象的相关方法// 单字节写数据fos.write(97); // afos.write(57); // 9fos.write(55); // 7} catch (FileNotFoundException exception) {exception.printStackTrace();} catch (IOException exception) {exception.printStackTrace();}finally {if(fos != null){try {fos.close();} catch (IOException exception) {exception.printStackTrace();}// 以下的代码非必需,这样做的目的是让对象变成垃圾,方便垃圾回收fos = null;}}}}
按照字节数组写数据
package com.powernode.p1;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.util.Arrays;/*** 演示: 按照字节数组写数据** void write(byte[] b)* 将 b.length 个字节从指定 byte 数组写入此文件输出流中。* void write(byte[] b, int off, int len)* 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流。*/public class Demo03 {public static void main(String[] args) {FileOutputStream fos = null;try {// 1. 创建对象fos = new FileOutputStream("D://b.txt");// 2. 干活byte[] bs = "ai,ya高考,我好紧张".getBytes();System.out.println(Arrays.toString(bs));// 将字节数组的数据全部写入文件// fos.write(bs);// 将字节数组的一部分数据写入文件fos.write(bs,5,21); // 高考,我好紧张} catch (IOException exception) {exception.printStackTrace();}finally {if(fos != null){try {fos.close();} catch (IOException exception) {exception.printStackTrace();}fos = null;}}}}
InputStream-字节输入流
public abstract class InputStream extends Object implements Closeable
此抽象类是表示字节输入流的所有类的超类。
常用方法
abstract int |
read()从输入流中读取数据的下一个字节。 |
|---|---|
int |
read(byte[] b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b中。 |
int |
read(byte[] b, int off, int len)将输入流中最多 len个数据字节读入 byte 数组。返回读取到的字节总数的长度 |
| void | close()关闭此输入流并释放与该流关联的所有系统资源。 |
InputStream是一个抽象类,需要使用其子类完成数据的读取。
FileInputStream-文件字节输入流⭐⭐⭐
public class FileInputStream extends InputStream
FileInputStream 从文件系统中的某个文件中获得输入字节.
单字节读取数据
package com.powernode.p1;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;/*** FileInputStream类:** 构造方法摘要* FileInputStream(File file)* 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定。* FileInputStream(String name)* 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定。** 方法摘要* int available()* 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。* void close()* 关闭此文件输入流并释放与此流有关的所有系统资源。* int read()* 从此输入流中读取一个数据字节。* int read(byte[] b)* 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。* int read(byte[] b, int off, int len)* 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。* long skip(long n)* 从输入流中跳过并丢弃 n 个字节的数据。*/public class Demo04 {public static void main(String[] args) {FileInputStream fis = null;try {// 1. 创建对象fis = new FileInputStream(new File("D://a.txt"));// 2. 干活 -- 读取数据// read() 按照单字节读取数据;返回读取到的字节数据,如果读取到文件末尾返回-1int b;while((b = fis.read()) != -1){System.out.println(b);}} catch (FileNotFoundException exception) {exception.printStackTrace();} catch (IOException exception) {exception.printStackTrace();}finally {// 3. 释放资源if(fis != null){try {fis.close();} catch (IOException exception) {exception.printStackTrace();}fis = null;}}}}
按照字节数据读取数据
package com.powernode.p1;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.util.Arrays;/*** FileInputStream类:** int read(byte[] b)* 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。* int read(byte[] b, int off, int len)* 从此输入流中将最多 len 个字节的数据读入一个 byte 数组中。*/public class Demo05 {public static void main(String[] args) {FileInputStream fis = null;try {fis = new FileInputStream("D://b.txt");byte[] bs = new byte[1024];// 按照字节数组读取数据,将数据读取到字节数组(缓冲区)中// 返回读取到的字节总数,也就是读取到的字节长度;如果到了文件末尾返回-1int len;while((len = fis.read(bs)) != -1){System.out.print(new String(bs,0,len));}} catch (FileNotFoundException exception) {exception.printStackTrace();} catch (IOException exception) {exception.printStackTrace();}finally {if(fis != null){try {fis.close();} catch (IOException exception) {exception.printStackTrace();}fis = null;}}}}
其他方法
package com.powernode.p1;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;/*** FileInputStream类:** int available()* 可用的剩余字节数* long skip(long n)* 从输入流中跳过 n 个字节的数据。*/public class Demo06 {public static void main(String[] args) {FileInputStream fis = null;try {fis = new FileInputStream("D://b.txt");// 可用的剩余字节数int count = fis.available();System.out.println(count);// 表示跳过2个字节fis.skip(2);int b = fis.read();System.out.println(b);} catch (FileNotFoundException exception) {exception.printStackTrace();} catch (IOException exception) {exception.printStackTrace();}finally {if(fis != null){try {fis.close();} catch (IOException exception) {exception.printStackTrace();}fis = null;}}}}
拷贝文件
package com.powernode.p1;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;/*** 演示:使用字节流拷贝文件* 分析:* 1. 字节输入流读取源文件数据* 2. 字节输出流写数据到目标文件* 3. 边读边写* 4. 关闭资源** 字节流是万能流,什么类型的文件都可以读写(复制),因为任何数据在计算机中都是字节。**/public class CopyFileDemo {public static void main(String[] args) {String src = "D://video/228.泛型和集合_LinkedHashMap集合的概述.avi";String dest ="E://b.avi";// copyFileBySingleByte(src,dest); // 耗时:121738copyFileByByteArray(src,dest);// 耗时:162 105 77 45}/*** 使用字节数组的方式拷贝文件* @param srcPath 源文件路径* @param destPath 目标文件路径*/public static void copyFileByByteArray(String srcPath,String destPath){// 创建输入流和输出流对象FileInputStream fis = null;FileOutputStream fos = null;try {fis = new FileInputStream(srcPath);fos = new FileOutputStream(destPath);// 边读边写int len; // 存储读取到的字节总数byte[] bs = new byte[1024];long start = System.currentTimeMillis();while((len = fis.read(bs)) != -1){fos.write(bs,0,len);}long end = System.currentTimeMillis();System.out.println("耗时:" + (end - start));} catch (IOException exception) {exception.printStackTrace();}finally {// 释放资源if(fos != null){try {fos.close();} catch (IOException exception) {exception.printStackTrace();}fos = null;}if(fis != null){try {fis.close();} catch (IOException exception) {exception.printStackTrace();}fis = null;}}}/*** 使用单字节的方式拷贝文件* @param srcPath 源文件路径* @param destPath 目标文件路径*/public static void copyFileBySingleByte(String srcPath,String destPath){// 创建输入流和输出流对象FileInputStream fis = null;FileOutputStream fos = null;try {fis = new FileInputStream(srcPath);fos = new FileOutputStream(destPath);// 边读边写int b; // 存储读取到的字节数据long start = System.currentTimeMillis();while((b = fis.read()) != -1){fos.write(b);}long end = System.currentTimeMillis();System.out.println("耗时:" + (end - start));} catch (IOException exception) {exception.printStackTrace();}finally {// 释放资源if(fos != null){try {fos.close();} catch (IOException exception) {exception.printStackTrace();}fos = null;}if(fis != null){try {fis.close();} catch (IOException exception) {exception.printStackTrace();}fis = null;}}}}
通过以上文件拷贝的案例测试,我们发现使用数组拷贝的方式比单字节拷贝效率高很多。使用字节数组拷贝的时候,自己数组的大小不是越大越好,一般在1024-8192字节区间设置,效率逐渐提高。增加更大的字节数组大小反而造成效率下降了。
jdk中其实已经帮我们设计好了一个IO流,这个IO流就可以帮助我们提高读写数据的效率。这个IO流就是缓冲流。
字节缓冲流
BufferedInputStream-字节缓冲输入流⭐⭐⭐
public class BufferedInputStream extends FilterInputStream
BufferedInputStream为另一个输入流添加一些功能,即缓冲输入以及支持 mark 和 reset 方法的能力。
单字节读取数据
package com.powernode.p2;import java.io.BufferedInputStream;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;/*** BufferedInputStream 缓冲字节输入流** 构造方法摘要* BufferedInputStream(InputStream in)* 创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。* BufferedInputStream(InputStream in, int size)* 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。** 节点流(普通流):可以直接从数据源或目的地读写数据。* 处理流(包装流、包裹流):不直接连接到数据源或目的地,是“处理流的流”。通过对其它流进行封装,目的主要是简化操作和提高程序的性能。** 缓冲就是一种处理流(包装流、包裹流),在节点流的基础上添加了缓冲的功能。*/public class Demo01 {public static void main(String[] args) {BufferedInputStream bis = null;try {bis = new BufferedInputStream(new FileInputStream("D://a.txt"));int b;while((b = bis.read()) != -1){System.out.println(b);}} catch (IOException exception) {exception.printStackTrace();}finally {if(bis != null){try {bis.close();} catch (IOException exception) {exception.printStackTrace();}}}}}
缓冲流单字节读取数据的原理

按照字节数组读取数据
package com.powernode.p2;import java.io.BufferedInputStream;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;public class Demo02 {public static void main(String[] args) {BufferedInputStream bis = null;try {bis = new BufferedInputStream(new FileInputStream("D://a.txt"));// 按照字节数组读取int len;byte[] bs = new byte[1024];while((len = bis.read(bs))!= -1){System.out.println( new String(bs,0,len));}} catch (FileNotFoundException exception) {exception.printStackTrace();} catch (IOException exception) {exception.printStackTrace();}finally {// close}}}
缓冲流字节数组读取数据的原理

BufferedOutputStream-字节缓冲输出流⭐⭐⭐
public class BufferedOutputStream extends FilterOutputStream
该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
单字节写数据
package com.powernode.p1;import java.io.BufferedOutputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;/*** BufferedOutputStream-字节缓冲输出流* 该类实现缓冲的输出流。通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。** 构造方法摘要* BufferedOutputStream(OutputStream out)* 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。* BufferedOutputStream(OutputStream out, int size)* 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流** 方法摘要* void flush()* 刷新此缓冲的输出流。* void write(byte[] b, int off, int len)* 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此缓冲的输出流。* void write(int b)* 将指定的字节写入此缓冲的输出流。**/public class Demo01 {public static void main(String[] args) {BufferedOutputStream bos = null;try {// 创建对象bos = new BufferedOutputStream(new FileOutputStream("D://out.txt"));// 干活bos.write(97);// 手动将缓冲区中的数据刷写到文件bos.flush();} catch (IOException exception) {exception.printStackTrace();}finally {if(bos != null){try {bos.close(); // 缓冲流释放资源的时候会调用flush} catch (IOException exception) {exception.printStackTrace();}bos = null;}}}}

按照字节数组写数据
package com.powernode.p1;import java.io.BufferedOutputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;public class Demo02 {public static void main(String[] args) {BufferedOutputStream bos = null;try {bos = new BufferedOutputStream(new FileOutputStream("D://c.txt"),12);// 按照字节数组写数据byte[] bs = "好好学习".getBytes();bos.write(bs);bos.flush();} catch (IOException exception) {exception.printStackTrace();}finally {if(bos != null){try {bos.close();} catch (IOException exception) {exception.printStackTrace();}bos= null;}}}}

缓冲流的好处
- 减少了对磁盘读写的频率,提高了效率
- 站在代码设计的角度考虑,使用了装饰者设计模式,增强了流的功能。(简化代码)
- 支持了mark和reset方法
字符流
字符流 = 字节流 + 字符编码
因为计算机中的数据是字节,所以读写数据还是需要使用字节流。但是读取中文的时候,由于中文的编码格式不一样,所以字节转中文的时候就比较麻烦,jdk帮我们设计了字符流,我们只需要在字节流的基础上添加对应的字符编码就可以将字节转成中文。方便我们操作中文。
Writer
public abstract class Writer extends Object implements Appendable, Closeable, Flushable
写入字符流的抽象类。
OutputStreamWriter-转换流(字符输出流)⭐⭐⭐
OutputStreamWriter 是字符流通向字节流的桥梁。

单字符写数据
package com.powernode.p2;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStreamWriter;import java.util.Arrays;/*** OutputStreamWriter: 字符输出流* 构造方法摘要* OutputStreamWriter(OutputStream out)* 创建使用默认字符编码的 OutputStreamWriter。* OutputStreamWriter(OutputStream out, Charset cs)* 创建使用给定字符集的 OutputStreamWriter。* OutputStreamWriter(OutputStream out, CharsetEncoder enc)* 创建使用给定字符集编码器的 OutputStreamWriter。* OutputStreamWriter(OutputStream out, String charsetName)* 创建使用指定字符集的 OutputStreamWriter。** 常用方法:* void write(char[] cbuf)* 写入字符数组。* void write(char[] cbuf, int off, int len)* 写入字符数组的某一部分。* void write(int c)* 写入单个字符。* void write(String str)* 写入字符串。* void write(String str, int off, int len)* 写入字符串的某一部分。*/public class Demo01 {public static void main(String[] args) {OutputStreamWriter writer = null;try {// 创建使用默认字符编码的 OutputStreamWriter。writer = new OutputStreamWriter(new FileOutputStream("D://a.txt"));writer.write('我');// 注意:使用字符流需要flush()writer.flush();} catch (IOException exception) {exception.printStackTrace();}finally {if(writer != null){try {writer.close(); // close也会flush} catch (IOException exception) {exception.printStackTrace();}writer = null;}}}}
按照字符数组写数据
package com.powernode.p2;import java.io.*;public class Demo02 {public static void main(String[] args) {OutputStreamWriter writer = null;try {// 创建字符流对象,指定字符编码格式writer = new OutputStreamWriter(new FileOutputStream("D://b.txt",true), "GBK");// 按照字符数组写数据writer.write("好好学习".toCharArray());// 按照字符串写数据writer.write("天天向上");// 写入一个换行符,windows环境的换行符是 \r\nwriter.write("\r\n");writer.flush();} catch (UnsupportedEncodingException e) {e.printStackTrace();} catch (IOException exception) {exception.printStackTrace();}finally {// close}}}
Reader
public abstract class Reader extends Object implements Readable, Closeable
用于读取字符流的抽象类。
InputStreamReader-转换流(字符输入流)⭐⭐⭐
InputStreamReader 是字节流通向字符流的桥梁。
单字符读取数据
package com.powernode.p2;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStreamReader;/*** InputStreamReader:** 构造方法摘要:* InputStreamReader(InputStream in)* 创建一个使用默认字符集的 InputStreamReader。* InputStreamReader(InputStream in, Charset cs)* 创建使用给定字符集的 InputStreamReader。* InputStreamReader(InputStream in, CharsetDecoder dec)* 创建使用给定字符集解码器的 InputStreamReader。* InputStreamReader(InputStream in, String charsetName)* 创建使用指定字符集的 InputStreamReader。** 常用方法:* int read()* 读取单个字符。读取的字符,如果已到达流的末尾,则返回 -1* int read(char[] cbuf)* 按照字符数组读取。读取的字符,如果已到达流的末尾,则返回 -1* int read(char[] cbuf, int offset, int length)* 将字符读入数组中的某一部分。读取的字符,如果已到达流的末尾,则返回 -1*/public class Demo03 {public static void main(String[] args) {InputStreamReader reader = null;try {/** InputStreamReader对象的字符编码指定成什么,应该根据读取的文件* 内容的字符编码决定。*/reader = new InputStreamReader(new FileInputStream("D://a.txt"),"GBK");int c;while((c = reader.read()) != -1){System.out.println((char)c);}} catch (FileNotFoundException exception) {exception.printStackTrace();} catch (IOException exception) {exception.printStackTrace();}finally {// close}}}
按照字符数组读取数据
package com.powernode.p2;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStreamReader;// 按照字符数组读取数据public class Demo04 {public static void main(String[] args) {InputStreamReader reader = null;try {/** InputStreamReader对象的字符编码指定成什么,应该根据读取的文件* 内容的字符编码决定。*/reader = new InputStreamReader(new FileInputStream("D://a.txt"),"GBK");// 按照字符数组读取数据int len;char[] cs= new char[1024];while((len = reader.read(cs)) != -1){System.out.println(new String(cs,0,len));}} catch (FileNotFoundException exception) {exception.printStackTrace();} catch (IOException exception) {exception.printStackTrace();}finally {// close}}}
注意
字符流不是万能的。
字符流可以读写哪些格式的文件?
文件使用操作系统的记事本打开后没有出现乱码,字符流就可以。
便捷类
FileWriter ⭐⭐⭐
public class FileWriter extends OutputStreamWriter
用来写入字符文件的便捷类。
package com.powernode.p2;import java.io.FileOutputStream;import java.io.FileWriter;import java.io.IOException;import java.io.OutputStreamWriter;/*** FileWriter类:*构造方法摘要** FileWriter(File file)* 根据给定的 File 对象构造一个 FileWriter 对象。* FileWriter(File file, boolean append)* 根据给定的 File 对象构造一个 FileWriter 对象。* FileWriter(String fileName)* 根据给定的文件名构造一个 FileWriter 对象。* FileWriter(String fileName, boolean append)* 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象。***/public class Demo06 {public static void main(String[] args) {FileWriter writer = null;try {/** 便捷类:便捷体现在:* 1. 构造中不再需要手动创建OutputStream对象,* FileWriter底层源码中自动帮我们创建了FileOutputStream对象** 2. 不需要手动指定字符编码格式了,会自动使用默认的字符编码** 缺点:* 字符编码格式固定了,也就是便捷类只能操作一种编码格式。**/writer = new FileWriter("D://c.txt");writer.write("真的是好便捷哦");writer.flush();} catch (IOException exception) {exception.printStackTrace();}finally {// close}}}
FileReader ⭐⭐⭐
public class FileReader extends InputStreamReader
用来读取字符文件的便捷类。
package com.powernode.p2;import java.io.FileNotFoundException;import java.io.FileReader;import java.io.IOException;/*** FileReader类:** 构造方法摘要* FileReader(File file)* 在给定从中读取数据的 File 的情况下创建一个新 FileReader。* FileReader(String fileName)* 在给定从中读取数据的文件名的情况下创建一个新 FileReader。*/public class Demo07 {public static void main(String[] args) {FileReader reader = null;try {/** 使用FileReader的前提是:* 文件的字符编码和平台的默认编码格式一致才能使用;否则中文乱码*/reader = new FileReader("D://a.txt");int len;char[] cs = new char[1024];while((len = reader.read(cs)) != -1){System.out.println(new String(cs,0,len));}} catch (IOException exception) {exception.printStackTrace();}finally {// close}}}
BufferedWriter-缓冲字符输出流 ⭐⭐⭐
将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
该类提供了 newLine() 方法,它使用平台自己的行分隔符概念。(跨平台分隔符)
package com.powernode.p3;import java.io.*;/*** BufferedWriter-缓冲字符输出流** 构造方法摘要* BufferedWriter(Writer out)* 创建一个使用默认大小输出缓冲区的缓冲字符输出流。* BufferedWriter(Writer out, int sz)* 创建一个使用给定大小输出缓冲区的新缓冲字符输出流。** 方法摘要* void close()* 关闭此流,但要先刷新它。* void flush()* 刷新该流的缓冲。* void newLine()* 写入一个行分隔符。* void write(char[] cbuf, int off, int len)* 写入字符数组的某一部分。* void write(int c)* 写入单个字符。* void write(String s, int off, int len)* 写入字符串的某一部分。*/public class Demo01 {public static void main(String[] args) {BufferedWriter writer = null;try {writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("D://x.txt",true)));writer.write("java是世界上最好的语言");// newLine() 根据系统的分隔符来决定writer.newLine();writer.flush();} catch (IOException exception) {exception.printStackTrace();}finally {// close}}}
BufferedReader-缓冲字符输入流 ⭐⭐⭐
从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
package com.powernode.p3;import java.io.*;/*** BufferedReader-缓冲字符输入流** 构造方法摘要* BufferedReader(Reader in)* 创建一个使用默认大小输入缓冲区的缓冲字符输入流。* BufferedReader(Reader in, int sz)* 创建一个使用指定大小输入缓冲区的缓冲字符输入流。** 方法摘要* void close()* 关闭该流并释放与之关联的所有资源。* int read()* 读取单个字符。* int read(char[] cbuf, int off, int len)* 将字符读入数组的某一部分。* String readLine()* 读取一个文本行。如果已到达流末尾,则返回 null*/public class Demo02 {public static void main(String[] args) {BufferedReader reader = null;try {reader = new BufferedReader(new InputStreamReader(new FileInputStream("D://x.txt")));String line;while((line = reader.readLine()) != null){System.out.println(line);}} catch (IOException exception) {exception.printStackTrace();}finally {// close}}}
什么情况使用字节流;什么情况使用字符流
- 字节流是万能的,什么情况下都可以使用。
- 字符流只能用于记事本打开后不乱码的文件。
- 如果字节流和字符流都可以操作文件的时候,想读取文件中的内容使用字符流。因为字节流读取出来的是字节,遇到中文的时候,我们自己处理会很麻烦的。
数据流
DataOutputStream-数据字节输出流
数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。
package com.powernode.p3;import java.io.DataOutputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;/*** DataOutputStream-数据字节输出流* 数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中** 构造方法摘要* DataOutputStream(OutputStream out)* 创建一个新的数据输出流,将数据写入指定基础输出流。** 方法摘要* void writeBoolean(boolean v)* 将一个 boolean 值以 1-byte 值形式写入基础输出流。* void writeByte(int v)* 将一个 byte 值以 1-byte 值形式写出到基础输出流中。* void writeChar(int v)* 将一个 char 值以 2-byte 值形式写入基础输出流中,先写入高字节。* void writeDouble(double v)* 使用 Double 类中的 doubleToLongBits 方法将 double 参数转换为一个 long 值,然后将该 long 值以 8-byte 值形式写入基础输出流中,先写入高字节。* void writeFloat(float v)* 使用 Float 类中的 floatToIntBits 方法将 float 参数转换为一个 int 值,然后将该 int 值以 4-byte 值形式写入基础输出流中,先写入高字节。* void writeInt(int v)* 将一个 int 值以 4-byte 值形式写入基础输出流中,先写入高字节。* void writeLong(long v)* 将一个 long 值以 8-byte 值形式写入基础输出流中,先写入高字节。* void writeShort(int v)* 将一个 short 值以 2-byte 值形式写入基础输出流中,先写入高字节。* void writeUTF(String str)* 以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流。*/public class Demo04 {public static void main(String[] args) {DataOutputStream dos = null;try {dos = new DataOutputStream(new FileOutputStream("D://data.txt"));// DataOutputStream 是将数据按照每一个字节的方式写入文件的dos.writeByte(97);dos.writeShort(100);dos.writeInt(200);dos.writeLong(1000);dos.writeChar('我');dos.writeFloat(3.14f);dos.writeDouble(13.14);dos.writeBoolean(true);// writeUTF写入的字符串字节总数不能超过65535dos.writeUTF("你说啥呢");} catch (IOException exception) {exception.printStackTrace();}finally {// close}}}
DataInputStream-数据字节输入流
数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。应用程序可以使用数据输出流写入稍后由数据输入流读取的数据。
package com.powernode.p3;import java.io.DataInputStream;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;/*** DataInputStream-数据字节输入流* 构造方法摘要* DataInputStream(InputStream in)* 使用指定的底层 InputStream 创建一个 DataInputStream。** 方法摘要* boolean readBoolean()* byte readByte()* char readChar()* double readDouble()* float readFloat()* int readInt()* long readLong()* short readShort()* String readUTF()**/public class Demo05 {public static void main(String[] args) {DataInputStream dis = null;try {dis = new DataInputStream(new FileInputStream("D://data.txt"));// 读取数据的顺序必须和写数据的顺序一致;否则数据错乱System.out.println(dis.readByte());System.out.println(dis.readShort());System.out.println(dis.readInt());System.out.println(dis.readLong());System.out.println(dis.readChar());System.out.println(dis.readFloat());System.out.println(dis.readDouble());System.out.println(dis.readBoolean());System.out.println(dis.readUTF());} catch (IOException exception) {exception.printStackTrace();}finally {// close}}}
数据流的作用:
- 可以使用IO流直接操作基本数据类型的数据
- 可以用来将基本类型的数据和字符串数据进行序列化
序列化:将数据转字节的过程
反序列化:将字节转数据的过程
将数据持久化存储或者在网络中传输,数据是必须序列化的。
对象流⭐⭐⭐
当两个程序通信时,彼此可以发送任意数据类型的数据。 无论是何种类型的数据,都会以二进制序列的形式在网络上传送(序列化)。比如,我们可以通过发送字符串、基本数据类型的数据信息,我们也可以在网络上直接发送Java对象。发送方需要把这个Java对象转换为字节序列(序列化),才能在网络上传送;接收方则需要把字节序列再恢复为Java对象(反序列)。
ObjectOutputStream-对象序列化流
对象序列化:将对象转字节的过程
只能将支持 java.io.Serializable 接口的对象写入流中。每个 serializable 对象的类都被编码,编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象。
注意:java中的对象要序列化必须实现Serializable 接口。
package com.powernode.p4;import java.io.Serializable;/*** 如果一个接口只有声明,没有任何成员信息,那么这个接口叫做标记接口* 也就是说这个接口的作用仅仅是用作标记。** 对象的默认序列化机制写入的内容是:对象的类,类签名,以及非瞬态和非静态字段的值。** 瞬态字段和静态字段是不会被序列化的。** 瞬态字段:被关键字transient修饰的字段* transient:表示短暂的意思** 如果对象中的某些字段不希望被持久化,那么可以这些字段可以变成瞬态字段和静态字段**/public class Student implements Serializable {/** serialVersionUID是根据当前类的信息生成的值* 如果序列化和反序列后serialVersionUID不一致会出现无效类的异常** 在开发中我们都是手动显式的给出serialVersionUID值,这样就可以避免无效类的异常的发生*/private static final long serialVersionUID = -3174090006999218597L;// 瞬态字段private transient String name;// 静态字段private static int age;private String sex;public Student() {}public Student(String name, int age, String sex) {this.name = name;this.age = age;this.sex = sex;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getSex() {return sex;}public void setSex(String sex) {this.sex = sex;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", sex='" + sex + '\'' +'}';}}
package com.powernode.p4;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectOutputStream;/*** ObjectOutputStream对象序列化流* 作用: 将对象转字节** 构造方法摘要* ObjectOutputStream(OutputStream out)* 创建写入指定 OutputStream 的 ObjectOutputStream。** 常用方法:* void writeObject(Object obj)* 将指定的对象写入 ObjectOutputStream。**/public class Demo01 {public static void main(String[] args) {ObjectOutputStream oos = null;try {oos = new ObjectOutputStream(new FileOutputStream("D://obj.txt"));Student student = new Student("张三", 22, "male");// java中的对象要序列化必须实现Serializable 接口oos.writeObject(student);} catch (IOException exception) {exception.printStackTrace();}finally {// close}}}
ObjectInputStream-对象反序列化流
ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
package com.powernode.p4;import java.io.FileInputStream;import java.io.IOException;import java.io.ObjectInputStream;/*** ObjectInputStream对象反序列化流* 作用:将字节数据转对象**/public class Demo02 {public static void main(String[] args) {ObjectInputStream ois = null;try {ois = new ObjectInputStream(new FileInputStream("D://obj.txt"));Student obj = (Student) ois.readObject();System.out.println(obj);} catch (IOException exception) {exception.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}finally {// close}}}
serialVersionUID
serialVersionUID是根据当前类的信息生成的值
如果序列化和反序列后serialVersionUID不一致会出现无效类的异常
在开发中我们都是手动显式的给出serialVersionUID值,这样就可以避免无效类的异常的发生
transient关键字
transient:表示短暂的意思
瞬态字段:被关键字transient修饰的字段
瞬态字段不能被序列化
字节数组流(内存流)
ByteArrayOutputStream 内存输出流 ⭐⭐
此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。
关闭 ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。
package com.powernode.p1;import java.io.ByteArrayOutputStream;import java.io.IOException;/*** 构造方法摘要* ByteArrayOutputStream()* 创建一个新的 byte 数组输出流。* ByteArrayOutputStream(int size)* 创建一个新的 byte 数组输出流,它具有指定大小的缓冲区容量(以字节为单位)。**方法摘要* int size()* 返回缓冲区的当前大小。* byte[] toByteArray()* 创建一个新分配的 byte 数组。* String toString()* 使用平台默认的字符集,通过解码字节将缓冲区内容转换为字符串。* String toString(String charsetName)* 使用指定的 charsetName,通过解码字节将缓冲区内容转换为字符串。* void write(byte[] b, int off, int len)* 将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此 byte 数组输出流。* void write(int b)* 将指定的字节写入此 byte 数组输出流。**/public class Demo01 {public static void main(String[] args) {// 缓冲区的最大值 2GByteArrayOutputStream bos = new ByteArrayOutputStream();try {bos.write("好嗨哟".getBytes());} catch (IOException exception) {exception.printStackTrace();}// 可使用 toByteArray() 和 toString() 获取数据。System.out.println(bos.toString());}}
使用场景演示
package com.powernode.p1;import java.io.*;/*** 需求: 将图片显示加载(微信头像,朋友圈)** 本地有一张图,需要将这个图片加载显示* 1. 将图片读取出来 --- 字节缓冲输入流* 2. 字节数据转图片,需要获取到整张图片的字节数据后再转图片* 不能拿到图片的一部分数据就开始转图片,这样图片会损坏的。**/public class Demo02 {public static void main(String[] args) {ByteArrayOutputStream baos = null;BufferedInputStream bis = null;try {bis = new BufferedInputStream(new FileInputStream("D://mm.webp"));baos = new ByteArrayOutputStream();int len;byte[] bs = new byte[1024];while((len = bis.read(bs)) != -1){// 读取图片数据 -- 将图片的字节数据保存到内存流中baos.write(bs,0,len);}/** while循环完成后,内存流中就保存了整张图片的字节数据*/// 获取图片的字节数据byte[] bytes = baos.toByteArray();// 我们就可以使用相关的工具类将字节数组转图片显示} catch (IOException exception) {exception.printStackTrace();}finally {// bis close}}}
ByteArrayInputStream 内存输入流
ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。
关闭 ByteArrayInputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。
package com.powernode.p1;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;/*** ByteArrayInputStream 内存输入流**构造方法摘要* ByteArrayInputStream(byte[] buf)* 创建一个 ByteArrayInputStream,使用 buf 作为其缓冲区数组。* ByteArrayInputStream(byte[] buf, int offset, int length)* 创建 ByteArrayInputStream,使用 buf 作为其缓冲区数组。** 方法摘要* int read()* 从此输入流中读取下一个数据字节。* int read(byte[] b, int off, int len)* 将最多 len 个数据字节从此输入流读入 byte 数组。*/public class Demo03 {public static void main(String[] args) {// 缓冲区的最大值 2GByteArrayOutputStream bos = new ByteArrayOutputStream();try {bos.write("abc".getBytes());// 读取内存流中的数据ByteArrayInputStream bais = new ByteArrayInputStream(bos.toByteArray());int b = bais.read();System.out.println(b);} catch (IOException exception) {exception.printStackTrace();}}}
打印流
打印流只有输出流没有输入流。
PrintStream 字节打印流
public class PrintStream extends FilterOutputStream implements Appendable, Closeable
PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
与其他输出流不同,PrintStream 永远不会抛出 IOException
package com.powernode.p1;import java.io.*;/*** PrintStream 字节打印流** 构造方法摘要* PrintStream(File file)* 创建具有指定文件且不带自动行刷新的新打印流。。* PrintStream(OutputStream out)* 创建新的打印流。* PrintStream(OutputStream out, boolean autoFlush)* 创建新的打印流。* PrintStream(String fileName)* 创建具有指定文件名称且不带自动行刷新的新打印流。*/public class Demo04 {public static void main(String[] args) {PrintStream ps = null;try {// autoFlush表示自动刷新,字节打印流中需要配合缓冲字节输出流使用。节点流使用该参数无效。ps = new PrintStream(new BufferedOutputStream(new FileOutputStream("D://a.txt")),true);ps.print("这是字节打印流");} catch (FileNotFoundException exception) {exception.printStackTrace();}finally {// ps.close();}}}
PrintWriter 字符打印流
public class PrintWriter extends Writer
与 [PrintStream](../../java/io/PrintStream.html) 类不同,如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作。
PrintWriter 构造中既能接受字节流也能接受字符流。
package com.powernode.p1;import java.io.FileWriter;import java.io.IOException;import java.io.PrintWriter;/*** PrintWriter字符打印流** 与 PrintStream 类不同,如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作。*/public class Demo05 {public static void main(String[] args) {PrintWriter pw = null;try {pw = new PrintWriter(new FileWriter("D://b.txt"), true);// 如果启用了自动刷新,则只有在调用 println、printf 或 format 的其中一个方法时才可能完成此操作。pw.println("字符打印流");} catch (IOException exception) {exception.printStackTrace();}finally {pw.close(); // close也会刷新数据}}}
标准流
标准输入流(System.in):计算机中的最重要的输入设备是键盘,所以标准输入流就是读取键盘输入的数据
标准输出流(System.out):计算机中的最重要的输出设备是显示器,所以标准输出流就是将数据写到显示器上(控制台输出)
static PrintStream |
**err**“标准”错误输出流。 |
|---|---|
static InputStream |
**in**“标准”输入流。 |
static PrintStream |
**out**“标准”输出流。 |
package com.powernode.p1;import java.io.*;/*** 需求:接受键盘录入的数据。不允许使用Scanner类。** 1. 接受键盘录入的数据需要用到标准输入流* System.in --> InputStream* 2. 假设键盘录入的数据是中文,字节流处理起来有点麻烦。* 键盘录入的数据只会是字符串,不可能出现图片、音频、视频* 所以这里使用字符流会更合适*/public class Demo06 {public static void main(String[] args) {System.out.println("请输入数据:");InputStream in = System.in;BufferedReader reader = null;BufferedWriter writer = null;try {reader = new BufferedReader(new InputStreamReader(in));writer = new BufferedWriter(new FileWriter("D://in.txt"));String line = reader.readLine();System.out.println(line);// 将键盘录入的数据保存到文件中writer.write(line);writer.flush();} catch (IOException exception) {exception.printStackTrace();}finally {// close}}}
IO流关闭的工具类
package com.powernode.p1;/*** IO工具类**/public class IOUtils {private IOUtils(){}/*** 关闭流,释放资源*/public static void close(AutoCloseable... closeable){for (AutoCloseable autoCloseable : closeable) {try {autoCloseable.close();} catch (Exception e) {e.printStackTrace();}}}}
Properties-属性集⭐⭐⭐
public class Properties extends Hashtable<Object,Object>
通过以上的继承关系我们可以知道,Properties中的数据是kv键值对格式的。
Properties 类表示了一个持久的属性集。Properties 可保存在流中或从流中加载。
属性列表中每个键及其对应值都是一个字符串。
将属性集的数据持久化
package com.powernode.p2;import java.io.FileWriter;import java.io.IOException;import java.util.Properties;/*** 构造方法摘要* Properties()* 创建一个无默认值的空属性列表。** 方法摘要* --------------------------------- 操作kv的方法* String getProperty(String key)* 用指定的键在此属性列表中搜索属性。* String getProperty(String key, String defaultValue)* 用指定的键在属性列表中搜索属性。* Object setProperty(String key, String value)* 调用 Hashtable 的方法 put。* Set<String> stringPropertyNames()* 返回此属性列表中的键集,其中该键及其对应值是字符串,如果在主属性列表中未找到同名的键,则还包括默认属性列表中不同的键。** --------------------------------- 读取数据到属性集中* void load(InputStream inStream)* 从输入流中读取属性列表(键和元素对)。* void load(Reader reader)* 按简单的面向行的格式从输入字符流中读取属性列表(键和元素对)。** --------------------------------- 将集合中的数据保存成文件* void store(OutputStream out, String comments)* 以适合使用 load(InputStream) 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。* void store(Writer writer, String comments)* 以适合使用 load(Reader) 方法的格式,将此 Properties 表中的属性列表(键和元素对)写入输出字符。***/public class Demo01 {public static void main(String[] args) {Properties prop = new Properties();// 设置kv数据prop.setProperty("name","zhangsan");prop.setProperty("age","20");prop.setProperty("sex","male");// 将属性集中的数据持久化,文件的扩展名就是.properties// .properties文件中使用#表示注释try {// 第二个参数是文件的说明信息prop.store(new FileWriter("day23/user.properties"),"用户信息");} catch (IOException exception) {exception.printStackTrace();}}}
将文件的数据加载到属性集中
package com.powernode.p2;import java.io.FileReader;import java.io.IOException;import java.util.Properties;import java.util.Set;public class Demo02 {public static void main(String[] args) {Properties properties = new Properties();try {properties.load(new FileReader("day23/user.properties"));System.out.println(properties);// 如果name1不存在,就返回默认值abcSystem.out.println(properties.getProperty("name1","abc"));Set<String> set = properties.stringPropertyNames();for (String key : set) {System.out.println(key+"->"+properties.getProperty(key));}} catch (IOException exception) {exception.printStackTrace();}}}
复制多级文件夹
package com.powernode.p2;import com.powernode.p1.IOUtils;import java.io.*;/*** 复制多级文件夹** 分析:* 1. 判断源文件路径是文件还是文件夹* 文件:直接复制* 文件夹:* a. 在目标路径上创建该文件夹* b. 获取文件夹下的所有内容* 回到1** 用过以上的分析发现需要使用递归*/public class CopyFloderDemo {public static void main(String[] args) {copyFloder(new File("D://a"),new File("E://"));}/*** 复制多级文件夹* @param src 源路径* @param dest 目标路径*/private static void copyFloder(File src, File dest){// 1. 判断源文件路径是文件还是文件夹// src = D://a.txt dest = E://if(src.isFile()){// 文件:直接复制// 将目标路径和源文件的文件名组成新的文件路径// newFile = E://a.txtFile newFile = new File(dest,src.getName());copyFile(src,newFile);}else{// 是 文件夹:// src = D://a dest = E://// a. 在目标路径上创建该文件夹// newFloder = E://aFile newFloder = new File(dest,src.getName());// 创建文件夹newFloder.mkdirs();// b. 获取源文件夹下的所有内容File[] files = src.listFiles();for (File file : files) {// file = D://a/a.txt newFloder = E://a// 回到1copyFloder(file,newFloder);}}}/*** 复制文件* @param src 源路径* @param dest 目标路径*/private static void copyFile(File src, File dest){BufferedInputStream bis = null;BufferedOutputStream bos = null;try {bis = new BufferedInputStream(new FileInputStream(src));bos = new BufferedOutputStream(new FileOutputStream(dest));int len;byte[] bs = new byte[1024];while((len = bis.read(bs)) != -1){bos.write(bs,0,len);}bos.flush();} catch (IOException exception) {exception.printStackTrace();}finally {IOUtils.close(bos,bis);}}}
