IO流
通过IO可以完成硬盘文件的读和写
往内存中去,叫输入(input),或者叫读
从内存中出来,叫输出(output),或者叫写。
流的分类:
字节流:一次读取一个字节,等同于一次读取八个二进制位。比如读取英文字母,一个字母一个字节,一个中文在Java中理论上占两个字节,这个指的是在JVM虚拟机中一个中文字符占两个字节。但在数据存储层面,一个中文字符是按照UTF-8的规定,以3个字节的方式保存在文件中,但是当中文字符被读到JVM内存中,该字符会被转为UTF-16,并以2个字节的方式保存在JVM内存中。
java.io.InputStream 字节输入流
java.io.OutputStream 字节输出流
字符流:一次读取一个字符,这种流是为了方便读取普通文本文件而存在的。
java.io.Reader 字符输入流
java.io.Writer 字符输出流
每个字符在计算机语言中都有一个ASCII值对应,通过ASCII值建立起字符与字节的联系。
字符流读取数据和字节流读取数据:
相同点:在Java中的操作步骤相似,以及所调用的API相同
不同点:效率不同,字符流效率更高,但字节流可以读取任意文件(视频,音频,图片),字符流只能读取普通文本(普通文本不是单指txt文件,指只包含文本的文件,比如java文件)。
所有的流都实现了java.io.Closeable接口,都是可关闭的,都有close()方法
所有的输出流都实现了java.io.Flushable接口,都是可刷新的,都有flush()方法。从内存中把数据写到硬盘中是通过管道进行的,管道会刷新,所以输出流都可刷新。
ps:存在一个类:OutputStreamWriter(此类属于字符流),是字符流通向字节流的桥梁:可以使用charset将要写入的字符编码成字节。它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
一、文件专属流:
java.io.FileIntputStream
java.io.FileOutputStream
java.io.FileReader
java.io.FileWriter
java.io.FileReader、java.io.FileWriter与java.io.FileIntputStream、java.io.FileOutputStream的使用方法相同,字符流只能读取普通文本。
1、java.io.FileIntputStream
创建对象时其参数可以是File对象。
读取方法为read(),read 如果是空参方法,返回的是ASCII值,且一次一个字节,如果是有参且传递的是字节数组时,返回的是读取的字节的个数。
创建字节数组进行读取时,是将对应字符的ASCII值传入数组,然后通过创建字符串对象,将数组传入字符串对象来转换为字符串。读取时会进行覆盖。
举个例子:
数据为abcdefg,七个字节,用长度为4的字节数组读取,那么第一次读取出来的数据是abcd,第二次读取时还剩3个字母没读,所以第二次读出的数据是efg,这是因为efg将abc覆盖了,如果将读取长度写死为4,那个读出的是efgd。
代码如下:
/**
* FileIntputStream 文件专属流,字节输入流
*/
public class IOTest02 {
public static void main(String[] args) {
FileInputStream fileInputStream = null;
try {
//创建流对象
fileInputStream = new FileInputStream("E:\\Temp\\test.txt");
//创建一个字节数组
//将ASCII值转换为字符时就是通过字节数组
byte[] bytes = new byte[4];
//传入字节数组,读取时会按照字节数组的长度来读取字节
//read 如果是空参方法,返回的是ASCII值,如果是有参且传递的是字节数组时,返回的是读取的字节的个数
int read = fileInputStream.read(bytes);
//创建字符串对象,并且传入需要转换的字节数组,会将字节数组的ASCII值转换为对应的字符串
String s = new String(bytes,0,4);
System.out.println(s);
//此时再读,就是读取剩下的字节
read = fileInputStream.read(bytes);
//剩下3个,此时read值为3
System.out.println(new String(bytes,0,read));
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
此时第二次输出为efg
将第二次输出字节长度写死为4时:
//此时再读,就是读取剩下的字节
read = fileInputStream.read(bytes);
System.out.println(new String(bytes,0,4));
此时第二次输出为efgd
FileIntputStream的available()方法 返回的是所读取文件剩余的字节个数
可以通过调用这个方法作为字节数组的长度,这样就不用写while循环了。且转换时直接传入数组,不用设定从哪一位开始传,传多长字节。(不适用大文件)
byte[] bytes = new byte[fileInputStream.available()];
System.out.println(new String(bytes));
//传入字节数组,读取时会按照字节数组的长度来读取字节
/*read 如果是空参方法,返回的是ASCII值,如果是有参且传递的是字节数组时,返回的是读取的字节的个数*/
int read = fileInputStream.read(bytes);
System.out.println(new String(bytes));
2、java.io.FileOutputStream
输出流得有刷新(flush)
package com.jy.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* FileOuttputStream 文件专属流,字节输出流
*/
public class IOTest03 {
public static void main(String[] args) {
FileOutputStream fileOutputStream = null;
try {
//创建输出流对象
fileOutputStream = new FileOutputStream("E:\\Temp\\test02.txt");
//abcd对应的ASCII值
byte[] bytes = {97,98,99,100};
fileOutputStream.write(bytes);
/**
* 将字符串转换为字节数组进行输出
*/
//定义一个字符串
String str = "你好,柿子!";
//将字符串转换为字节数组
byte[] bytes1 = str.getBytes();
fileOutputStream.write(bytes);
//刷新
fileOutputStream.flush();
} catch (Exception e) {
e.printStackTrace();
}finally {
if(fileOutputStream != null){
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
输出流提供了两种构造,
第一种,只需要传文件路径,输出时会覆盖之前的数据。
fileOutputStream = new FileOutputStream("E:\\Temp\\test02.txt");
第二种,追加,加上true,这时输出时会接着原有数据的后面追加。
fileOutputStream = new FileOutputStream("E:\\Temp\\test02.txt",true);
案例:copy文件或者视频
/**
* 通过输入流和输出流完成视频copy
*/
public class IOTest06 {
public static void main(String[] args) {
FileInputStream fileInputStream = null;
FileOutputStream fileOutputStream = null;
try {
//copy视频
fileInputStream = new FileInputStream("E:\\Temp\\test.mp4");
fileOutputStream = new FileOutputStream("E:\\Temp\\copy.mp4");
//copy文件
/*fileInputStream = new FileInputStream("io02.txt");
fileOutputStream = new FileOutputStream("io03.txt",true);*/
byte[] bytes = new byte[1024*1024];//1MB
//读取文件
int read = 0;
while ((read = fileInputStream.read(bytes)) != -1){
//将字节数组转换成字符串
//String s = new String(bytes, 0, read);
fileOutputStream.write(bytes,0,read);
}
//刷新数据流
fileOutputStream.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
if(fileInputStream != null){
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fileOutputStream != null){
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
同理:FileReader和FileWriter的用法与上面相似,除了字节数组换成字符数组,无变化
try {
FileReader fileReader = new FileReader("io01.txt");
char[] chars = new char[4];
int read = 0;
while ((read = fileReader.read(chars)) != -1){
System.out.println(new String(chars,0,read));
}
}
fileWriter.write("\n")//换行符