IO流的分类与字节流:
- 流向:
- 输入流 读取数据
- 输出流 写出数据
- 数据类型:
- 字节流
- 字节输入流 读取数据 InputStream
- 字节输出流 写出数据 OutputStream
- 字符流
- 字符输入流 读取数据 Reader
- 字符输出流 写出数据 Writer
字符流存在的问题
* 字符流只能操作文本文件,如果操作非文本文件(图片,视频,音频)等文件,会出现数据丢失问题。
* 如果要操作非文本文件,只能使用字节流,因为字节流可以操作任意类型的文件。
OutputStream类概述
* Output:输出 * Stream:字节流 * 是一个字节流。 * 是一个抽象类,不能直接创建该类的对象。 * 是所有字节输出流的父类。
OutputStream类常用的子类
* FileOutputStream
* BufferedOutputStream
FileOutputStream类构造方法
* FileOutputStream(String pathname) 根据文件路径创建文件字节输出流对象
* FileOutputStream(File file) 根据文件对象创建文件字节输出流对象
* FileOutputStream(File file, boolean append)
* FileOutputStream(String pathname, boolean append)
* 可以通过 append 指定是否是追加输出
* append:true表示追加,false表示不追加。
字节输出流操作步骤:
* A:创建字节输出流对象
File file = new File("a.txt");
FileOutputStream fos = new FileOutputStream(file);
FileOutputStream fos = new FileOutputStream("a.txt");
创建字节输出流对象了做了几件事情:
1.调用系统功能去创建文件
2.创建fos对象
3.把fos对象指向这个文件
* B:写数据
fos.write("hello,IO".getBytes());
fos.write("12345678".getBytes());
* C:释放资源
fos.close();
字节输出流写数据
* public void write(int b):写一个字节
* public void write(byte[] b):写一个字节数组
* public void write(byte[] b,int off,int len):
* 将字节数组b的一部分内容输出到目标文件中
* off:数组的起始索引
* len:要输出的字节个数
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("test\\a.txt");
fos.write(97);
fos.write('\r');
fos.write('\n');
fos.write(65);
fos.write(256+66);
byte[] bys = { 97, 98, 99, 100, 101 };
fos.write(bys);
fos.write(bys, 1, 3);
fos.close();
}
* 如何实现数据的换行?
* 因为不同的系统针对不同的换行符号识别是不一样的?
* windows:\r\n
* linux:\n
* Mac:\r
* 如何实现数据的追加写入?
* 用构造方法带第二个参数是true的情况即可
加入异常处理的字节输出流操作
public static void main(String[] args) {
FileOutputStream fos = null;
try {
fos = new FileOutputStream("a.txt");
fos.write("java".getBytes());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("c:/b.txt");
byte[] buf = "i love java".getBytes();
fos.write(buf);
fos.close();
}
字节输入流操作步骤:
* A:创建字节输入流对象
FileInputStream fis = new FileInputStream("a.txt");
* B:调用read()方法读取数据,并把数据显示在控制台
int by = 0;
while ((by = fis.read()) != -1) {
System.out.print((char) by);
}
byte[] bys = new byte[1024];
int len = 0;
while ((len = fis.read(bys)) != -1) {
System.out.print(new String(bys, 0, len));
}
* C:释放资源
fis.close();
字节输入流读取数据:
* abstract int read(); 从流关联的目标文件中读取一个字节,返回读取到的字节
* int read(byte[] b);
* 从流关联的目标文件中读取一个字节数组的数据,
* 返回实际读取到字节个数。
* int read(byte[] b, int off, int len)
* 将读取到的字节存储到指定的字节数组b中
* off:存储字节的起始索引
* len:指定能够存储的字节个数。
int by = 0;
while ((by = fis.read()) != -1) {
System.out.print((char) by);
}
byte[] bys = new byte[1024];
int len = 0;
while ((len = fis.read(bys)) != -1) {
System.out.print(new String(bys, 0, len));
}
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("c:/a.png");
FileOutputStream fos = new FileOutputStream("d:/a.png");
byte[] buffer = new byte[1024];
int len = -1;
while((len = fis.read(buffer)) != -1) {
fos.write(buffer,0,len);
}
fis.close();
fos.close();
}
缓冲区类(高效类)
* 写数据:BufferedOutputStream
* 读数据:BufferedInputStream
构造方法可以指定缓冲区的大小,但是我们一般用不上,因为默认缓冲区大小就已经足够了。
为什么不传递一个具体的文件或者文件路径,而是传递一个OutputStream对象呢?
* 原因很简单,字节缓冲区流仅仅提供缓冲区,为高效而设计的。但是呢,真正的读写操作还得靠基本的流对象实现。
BufferedOutputStream类注意事项
* 使用缓冲字节输出流输出数据不是直接输出到目标文件中,而是先存储到内部的缓冲区
数组中,当缓冲区数组满了或调用了flush或close方法,则由FileOutputStream将缓冲
区数组的数据输出到目标文件中。
记忆技巧
* BufferedOutputStream的使用方式除了构造方法和FileOutputStream不一样之外,其他完全一样。
BufferedInputStream类注意事项
* 字节缓冲输入流读取数据不是直接从目标文件中读取,而是从内部的缓冲区数组中读取,
* 如果缓冲区数组没有内容或者读完了,则会通过FileInputStream从目标文件中
一次读取8192个字节数据到缓冲区数组中。
记忆技巧
* BufferedInputStream的使用方式除了构造方法和FileInputStream不一样之外,其他完全一样。
操作步骤:
* A:创建对象
BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream("a.txt"));
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
* B:读写操作
int by = 0;
while ((by = bis.read()) != -1) {
bos.write(by);
}
byte[] bys = new byte[1024];
int len = 0;
while ((len = bis.read(bys)) != -1) {
bos.write(bys, 0, len);
}
* C:释放资源
bos.close();
bis.close();
四种文件复制方式的效率比较
public static void main(String[] args) throws IOException{
System.out.println("字节流复制文件一次读写一个字节耗时:"+ copy01());
System.out.println("字节流复制文件一次读写一个字节数组耗时:"+ copy02());
System.out.println("高效流复制文件一次读写一个字节耗时:"+ copy03());
System.out.println("高效流复制文件一次读取一个字节数组耗时:"+ copy04());
}
public static long copy01() throws IOException{
long startTime = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("aaa.jpg");
FileOutputStream fos = new FileOutputStream("ttt.jpg");
int len = -1;
while((len = fis.read()) != -1) {
fos.write(len);
}
fis.close();
fos.close();
return System.currentTimeMillis() - startTime;
}
public static long copy02() throws IOException{
long startTime = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("aaa.jpg");
FileOutputStream fos = new FileOutputStream("eee.jpg");
byte[] buffer = new byte[1024];
int len = -1;
while((len = fis.read(buffer)) != -1) {
fos.write(buffer,0,len);
}
fis.close();
fos.close();
return System.currentTimeMillis() - startTime;
}
public static long copy03() throws IOException{
long startTime = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("aaa.jpg");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("jjj.jpg");
BufferedOutputStream bos = new BufferedOutputStream(fos);
int len = -1;
while((len = bis.read()) != -1) {
bos.write(len);
}
bis.close();
bos.close();
return System.currentTimeMillis() - startTime;
}
public static long copy04() throws IOException{
long startTime = System.currentTimeMillis();
FileInputStream fis = new FileInputStream("aaa.jpg");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("ddd.jpg");
BufferedOutputStream bos = new BufferedOutputStream(fos);
byte[] buffer = new byte[1024];
int len = -1;
while((len = bis.read(buffer)) != -1) {
bos.write(buffer,0,len);
}
bis.close();
bos.close();
return System.currentTimeMillis() - startTime;
}
结论:推荐使用缓冲流来操作文件。
flush方法和close方法区别
flush用来刷新缓冲区,将缓冲区中的数据输出到目标文件中,流还可以继续使用。
close用来关闭流释放资源,如果流是带缓冲区,则在关闭流之前会调用flush方法刷新缓冲区,流不可以再次使用。
计算机是如何识别什么时候该把两个字节转换为一个中文呢?
* 在计算机中中文的存储分两个字节:
* 第一个字节肯定是负数。
* 第二个字节常见的是负数,可能有正数。但是没影响。
* String(byte[] bytes, String charsetName):通过指定的字符集解码字节数组
* byte[] getBytes(String charsetName):使用指定的字符集合把字符串编码为字节数组
*
* 编码:把看得懂的变成看不懂的
* String -- byte[]
*
* 解码:把看不懂的变成看得懂的
* byte[] -- String
String s = "好好学习";
byte[] bys = s.getBytes();
System.out.println(Arrays.toString(bys));
String ss = new String(bys, "GBK");
System.out.println(ss);
编码读写数据:
* InputStreamReader(InputStream is):用默认的编码读取数据
* InputStreamReader(InputStream is,String charsetName):用指定的编码读取数据
* OutputStreamWriter(OutputStream out):根据默认编码把字节流的数据转换为字符流
* OutputStreamWriter(OutputStream out,String charsetName):根据指定编码把字节流数据转换为字符流
* 把字节流转换为字符流。
* 字符流 = 字节流 +编码表。
操作步骤:
* A:创建对象
InputStreamReader isr = new InputStreamReader(new FileInputStream("a.txt"), "UTF-8");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("a.txt"), "GBK");
* B:读写操作
int by = 0;
while ((by = isr.read()) != -1) {
osw.write(by);
}
byte[] bys = new byte[1024];
int len = 0;
while ((len = isr.read(bys)) != -1) {
osw.write(bys, 0, len);
}
* C:释放资源
osw.close();
isr.close();