字节流与字符流
1、流的基本概念
在java.io_包里面File类是唯一一个与文件本身有关的程序处理类,但是 File类只能操作文件本身,不能操作文件的内容,或者说在实际的IO操作的核心意义在于:输入与输出操作。
所有的流操作都应该采用如下统一的步骤进行,下面以文件处理的流程为例:
- 如果现在要进行的是文件的读写操作,则一定要通过File类找打一个文件路径
- 通过字节流的子类对父类对象实例化
- 利用字节流或字符流中的方法实现数据输入与输出操作
- 流的从操作属于资源操作,资源操作必须进行关闭处理
2、OutputStream字节输出流
字节的数据是以byte类型为主实现的操作,在进行字节内容输出的时候可以使用OutputStream来完成,基本定义如下:
- public abstract class OutputStream extends Object implements Colseable,Flushable
首先可以发现这个类实现了两个接口,于是基本的对应关系如下:
- Closeable:
public interface Closeable extends AutoCloseable {
void close() throws IOException;
}
- Flushable:
public interface Flushable {
public void flush() throws IOException;
}
OutputStream是一个定义的公共的输出操作标准,而这个操作标准里一共定义有三个输出的方法
No | 方法名称 | 类型 | 描述 |
---|---|---|---|
01 | public abstract void write(int b)throws IOExcption | 普通 | 输出单个字节数据 |
02 | public void write(byte[] b)throws IOException | 普通 | 输出一组字节数据 |
03 | public void write(byte[] b,int off,int len)throws IOException | 普通 | 输出部分字节数据 |
但是需要注意的是,OutputStream类毕竟是一个抽象类,而这个抽象类如果想要获得数理化对象,按照传统的认识应该通过子类实例的向上转型完成。如果进行的是文件处理操作,则可以使用FIleOutputStream的子类进行
因为最终都需要发生向上转型的处理关系,所以对于此时的FileOutputStream子类的核心关注点就可以放在构造方法上了:
- <覆盖>构造方法:public FIleOutputStream(File file) throws FileNotFoundException
- <追加>构造方法:public FileOutputStream(File file , boolean append) throws FileNotFoundException
例:使用OutputStream类实现内容输出
public class JavaDemo {
public static void main(String[] args) throws IOException {
File file = new File("E:" + File.separator + "hello" + File.separator + "666.txt"); //指定操作文件的路径
if (!file.getParentFile().exists()) { //文件不存在
file.getParentFile().mkdirs(); //创建父目录
}
FileOutputStream fileOutputStream = new FileOutputStream(file); //通过子类实例化
String str = "666"; //要输出的文件内容
fileOutputStream.write(str.getBytes()); //将字符串变为字节数组并输出
fileOutputStream.close(); //关闭资源
}
}
本程序是采用了最为标准的形式实现了输出的操作处理,并且在整体的处理之中,只是创建了文件的父目录,但是并没有创建文件,而是执行后文件自动被创建了
由于OutputStream是AutoCloseable的子类,所以close()方法也可由简化使用
例:自动关闭处理
public class JavaDemo {
public static void main(String[] args) throws IOException {
File file = new File("E:" + File.separator + "hello" + File.separator + "666.txt"); //指定操作文件的路径
if (!file.getParentFile().exists()) { //文件不存在
file.getParentFile().mkdirs(); //创建父目录
}
try(FileOutputStream fileOutputStream = new FileOutputStream(file , true);) {
String str = "666"; //要输出的文件内容
fileOutputStream.write(str.getBytes()); //将字符串变为字节数组并输出
} catch (IOException e) {
e.printStackTrace();
}
}
}
是否使用自动的关闭取决于你项目的整体结构,整个程序里面最终输出了一组的字节数据,但是OutputStream类之中定义的输出方法一共有三个
3、InputStream字节输入流
InputStream类主要实现的就是字节输入,该类定义如下:
- public abstract class InputStream extends Object implements Closeable
在InputStream类连定义有如下的几个核心方法:
No | 方法名称 | 类型 | 描述 |
---|---|---|---|
01 | public abstract int read()throws IOExcption | 普通 | 读取单个字节数据 |
02 | public intread(byte[] b)throws IOException | 普通 | 读取一组字节数据,返回的是读取的个数,如果没有数据则返回-1 |
03 | public int read(byte[] b,int off,int len)throws IOException | 普通 | 读取一组部分字节数据 |
InputStream类属于一个抽象类,这时应该依靠它的子类来实例化对象,如果要从文件读取一定要使用FileInputStream子类,对于子类而言只关心父类对象实例化,构造方法:
- public FileInputStream(File file) throws FileNotFoundException
读取数据
public class JavaDemo {
public static void main(String[] args) throws IOException {
File file = new File("E:" + File.separator + "hello" + File.separator + "666.txt");
InputStream input = new FileInputStream(file);
byte[] data = new byte[1024]; //开辟缓冲区读取数据
int len = input.read(data);//读取数据,数据全部保存在数组之中,返回读取个数
System.out.println(new String(data , 0 , len));
input.close();
}
}
对于字节输入流里面最为麻烦的问题就在于:使用read()方法读取的时候只能够以字节数组为主进行接收
特别需要注意的是从JDK1.9开始在 InputStream类里面增加了一个新的方法:
- public byte[] readAllBytes() throws IOException
如果读取的内容很大,这种读取会直接击毙程序
4、Writer字符输出流
字符输入流Writer:定义如下:
public abstract class Writerextends Object implements Appendable,Closeable,Flushable
在Writer类里面提供有许多的输出操作方法,重点看两个:
- 输出字符数组:public void write(char[] cbuf)throws IOException
- 输出字符串:public void write(String str)throws IOException
使用Writer输出:
public class JavaDemo {
public static void main(String[] args) throws IOException {
File file = new File("E:" + File.separator + "hello" + File.separator + "666.txt");
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs(); //父目录必须存在
}
Writer writer = new FileWriter(file);
String str = "666666.666666";
writer.write(str);
writer.append("123");
writer.close();
}
}
使用Writer输出的最大优势在于可以直接利用字符串完成。Writer是字符流,字符流处理的优势在于中文数据上
5、Reader字符输入流
Reader是实现字符输入流的一种类型,其本身属于一个抽象类,这个类的定义如下:
public abstract class Reader extends Object implements Readable, Closeable
Reader类里面并没有像 Writer类一样提供有整个字符串的输入处理操作,只能利用字符数组进行接收
- 接收数据:public int read(char[] cbuf) throws IOException
实现数据读取:
public class JavaDemo {
public static void main(String[] args) throws IOException {
File file = new File("E:" + File.separator + "hello" + File.separator + "666.txt");
if (file.exists()) { //文件存在进行读取
Reader in = new FileReader(file);
char[] data = new char[1024];
int len = in.read(data);
System.out.println(new String(data ,0 , len));
}
}
}
字符流读取的时候只能够按照数组的形式来实现处理操作
6、字节流与字符流的区别
OutputStream和Writer输出的最后都使用了close()方法进行关闭处理
在使用OutputStream_类输出的时候如果现在没有使用close()方法关闭输出流,内容依然可以实现正常的输出。但是在使用Writer的时候没有市容close()方法关闭输出流,那么这个时候将无法进行输出吗,因为Writer使用到了缓冲区,在使用close()方法是时候实际上会出现强制刷新缓冲区的情况,如果没有关闭,那么就不能输出。
如果想要在不关闭的情况下将内容输出,可以市容flush()方法强制刷 新,然后输出
字节流在济宁处理的时候并不会使用到缓冲区,而字符流会使用到缓冲区。使用缓冲区的字符流更加适合进行中文数据的处理。所以在日后的程序开发之中,如果要设计到包含中文信息的输出一般都会使用字符流处理。
字节流和字符流的基本处理形式是相似的,IO大多情况都是进行数据传输
7、转换流
转换流是实现字节流和字符流操作功能的转换
类 | OutputStreamWriter | InputStreamReader |
---|---|---|
定义 | public class OutputStreamWriter extends Writer | public class InputStreamReader extends Reader |
构造方法 | public OutputStreamWriter(OutputStream out) | public InputStreamReader(InputStream in) |
所谓的转换处理就是将接收到的字节流对象通过向上转型变为字符流对象
8、综合案例:文件拷贝
public class FileUtil {
private File srcFile;
private File desFile;
public FileUtil(String src, String des) {
this(new File(src), new File(des))
}
public FileUtil(File srcFile, File desFile) {
this.srcFile = srcFile;
this.desFile = desFile;
}
public boolean copy() throws IOException { //文件拷贝处理
if (!srcFile.exists()) { //文件必须存在
return false;
}
if (desFile.getParentFile().exists()) { //创建父目录
desFile.getParentFile().mkdirs();
}
byte[] data = new byte[1024]; //开辟缓冲区
FileOutputStream output = null;
FileInputStream input = null;
try {
input = new FileInputStream(srcFile);
output = new FileOutputStream(desFile);
int len = 0;
while ((len = input.read(data)) != -1) {
output.write(data);
}
return true;
} catch (IOException e) {
throw e;
} finally {
if (input != null) {
input.close();
}
if (output != null) {
output.close();
}
}
}
}
以上是拷贝的最原始的实现,从JDK1.9开始InputStream和Reader类中都最追加了数据转存的操作方法
下一篇:IO 操作深入