概述:
- IO流就是用来处理设备间数据传输问题的.常见的应用: 文件复制; 文件上传; 文件下载
- IO的数据传输,可以看做是一种数据的流动,按照流动的方向,已内存为参照物,进行读写操作
- IO可以保存到文件,其实就是内存在读取,内存在写入,而且不会消失,字节流可以操作任何类型的文件(音频、图文)都可以
IO流分类:
根据数据的流向分为:
- 输入流(Input) :把数据从其他设备上读取到内存中的流。
- 输出流(Output) :把数据从内存中写出到其他设备上的流。
根据数据的类型分为:
- 字节流 :以字节为单位,读写数据的流。(通用的,可以操作任何类型的数据)
- 字符流 :以字符为单位,读写数据的流。(方便用户操作文本的)
注意:
计算中所有的数据视频,音频,图片,文字…都是以字节的形式存储的
什么场景用什么流
用记事本打开看得懂的就可以用字符,看不懂的可以用字节(字节是万能流)
字节流:
- InputStream:这个抽象类是表示字节输入流的所有类的超类
- OutputStream:这个抽象类是表示字节输出流的所有类的超类
- 子类名特点:子类名称都是以其父类名作为子类名的后缀
字节输出流:
FileOutputStream(String name):创建文件输出流以指定的名称写入文件
方法名 | 说明 |
---|---|
void write(int b) | 将指定的字节写入此文件输出流 一次写一个字节数据 |
void write(byte[] b) | 将数组中的数据写入流中 一次写一个字节数组数据 |
void write(byte[] b, int off, int len) | 将数组的一部分数据写入流中 int off: 从哪个索引开始 int len: 写多少个 |
FileOutputStream(File file, boolean append) | 当append设置为true时,不会删除之前的文件,追加写数据 |
代码演示:
public class Demo01 {
public static void main(String[] args) throws IOException {
// 创建字节输出流:文件不存在就创建,文件存在就清空原文件,
// 第二个参数是续写开关,默认为false不续写,为true就续写,续写不会删除原来的文件内容
FileOutputStream fos = new FileOutputStream("/Users/itzhuzhu/Desktop/Java/ideaTest/io/a.txt",true);
// 一次写一个字节数组:写整数传到文件里的是码表对应值
byte [] bytes = {97,98,99,100};
fos.write(bytes);
// 换行
fos.write("\r\n".getBytes());
// 从指定索引开始写数据(数据源,哪里开始,哪里结束)
// getBytes(): 使用平台的默认字符集将字符串编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
fos.write(bytes,1,2);
// 释放资源:告诉系统这个文件使用完毕
fos.close();
}
}
换行:
- windows:\r\n
- linux:\n
- mac:\r
字节流写数据异常处理:
finallly:被finally控制的语句一定会执行,除非JVM退出
public static void main(String[] args) {
FileOutputStream fos = null;
try {
System.out.println(2 / 0); // 制造异常
fos = new FileOutputStream("/Users/itzhuzhu/Desktop/Java/ideaTest/io/a.txt", true);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
fos.write(99);
} catch (IOException e) {
e.printStackTrace();
} // 为了避免空指针异常在这里要加判断,不为空才走
finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字节输入流:
FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名
方法名 | 说明 |
---|---|
public int read() | 从输入流读取下一个字节数据, 返回读取到的字节 |
public int read(byte[] b) | 从输入流读取最多b.length个字节的数据 |
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("/Users/itzhuzhu/Desktop/Java/ideaTest/file/a.txt");
int len;
// 不是-1就可以一直读,是-1说明没了
while ((len = fis.read()) != -1) {
System.out.println((char) len);
}
fis.close();
}
复制文件:
public class Demo03 {
public static void main(String[] args) throws IOException {
// 创建字节输入/输出流
FileInputStream fis = new FileInputStream("/Users/itzhuzhu/Desktop/Java/ideaTest/io/a.txt");
FileOutputStream fos = new FileOutputStream("/Users/itzhuzhu/Desktop/Java/ideaTest/io/a2.txt");
// 数组可以装多个字节,一次读一个数组比一次读一个字节快
byte[] bytes = new byte[1024];
// 循环读取,len表示现在读了多少字节
int len;
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
fis.close();
fos.close();
}
}
复制文件执行流程图
字节缓冲流:
- 缓冲流只提供缓冲区,也就是数组,存在的使命就是提高效率,速度飞快。
- 底层会创建一个默认8192的数组,
主要是在硬盘和内存之间数据传递的次数,提高效率
- 字节缓冲流要传入的参数是字节流,不能直接传入文件路径
构造方法:
方法名 | 说明 |
---|---|
BufferedOutputStream(OutputStream out) | 创建字节缓冲输出流对象 |
BufferedInputStream(InputStream in) | 创建字节缓冲输入流对象 |
缓冲流复制文件:
用一次一个数组,不要玩一个字节,哪个牛逼用哪个
public static void main(String[] args) throws IOException {
// 创建字节缓冲输入流、缓冲流会在底层创建一个8192字节数组
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("/Users/itzhuzhu/Desktop/Java/ideaTest/io/a.txt"));
// 创建字节缓冲输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("/Users/itzhuzhu/Desktop/Java/ideaTest/io/a2.txt"));
byte[] bytes = new byte[1024];
int len;
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes, 0, len);
}
bis.close();
bos.close();
}
字符流:
有了万能的字节流为啥还出现了字符流?
- 因为字节流读写中文可能会出现乱码,原因是因为编码表
- 把字符存计算机要转二进制,叫编码
- 把计算机中的二进制数据解析出来,叫解码
- 编码和解码的方式必须一致,否则就会乱码
为什么字节流读中文乱码:
- 字节流一次都一个字节,但是中文在GBK和UTF-8中都大于一个字节,也就是读不完一个完整的文字
编码表:
ASCll
编码只有一些基础的文字,码表查看:http://ascii.911cha.com/Unicode
编码表是统一的万国码,容纳世界上所有的常见文字和符号,经UTF-8编码后一个中文占3个字节存储GBK
是中国推出的码表,每个国家都有自己的码表,但是比较乱,就定义了一个规范就是UnicodeUTF-8
不是码表是一个格式- win系统默认使用GBK,一个字符占2个字节,idea默认用UTF-8,一个字符占3个字节
字符串中的编码解码:
方法名 | 说明 |
---|---|
byte[] getBytes() | 使用平台的默认字符集将该 String编码为一系列字节 |
byte[] getBytes(String charsetName) | 使用指定的字符集将该 String编码为一系列字节 |
String(byte[] bytes) | 使用平台的默认字符集解码指定的字节数组来创建字符串 |
String(byte[] bytes, String charsetName) | 通过指定的字符集解码指定的字节数组来创建字符串 |
public class Demo01 {
public static void main(String[] args) throws UnsupportedEncodingException {
//定义一个字符串
String s = "放眼整个中国,谁特么有我帅";
// 编码
byte[] bys = s.getBytes("GBK");
byte[] bys2 = s.getBytes("UTF-8");
System.out.println(Arrays.toString(bys));
System.out.println(Arrays.toString(bys2));
// 解码
byte [] byte1 = {-73, -59, -47, -37, -43, -5, -72, -10, -42, -48, -71, -6, -93, -84, -53, -83, -52, -40, -61, -76, -45, -48, -50, -46, -53, -89};
byte [] byte2 = {-26, -108, -66, -25, -100, -68, -26, -107, -76, -28, -72, -86, -28, -72, -83, -27, -101, -67, -17, -68, -116, -24, -80, -127, -25, -119, -71, -28, -71, -120, -26, -100, -119, -26, -120, -111, -27, -72, -123};
System.out.println(new String(byte1, "GBK"));
System.out.println(new String(byte2, "UTF-8"));
}
}
字符流读中文过程:
- 不管在哪个码表中,中文的第一个字节一定是负数。
- 用字符读的时候,当看到是负数就会看看是什么格式,UTF8就直接读三个,GBK就读2个
字符流=字节流+编码表
字节流字符流怎么选:
要拷贝就用字节流&字节缓冲流
把文本数据读到内存用字符输入,内存写到文件用字符输出
字符流写数据:
构造方法
方法名 | 说明 |
---|---|
FileWriter(File file) | 根据给定的 File 对象构造一个 FileWriter 对象 |
FileWriter(File file, boolean append) | 根据给定的 File 对象构造一个 FileWriter 对象 |
FileWriter(String fileName) | 根据给定的文件名构造一个 FileWriter 对象 |
FileWriter(String fileName, boolean append) | 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象 |
成员方法:
方法名 | 说明 |
---|---|
void write(int c) | 写一个字符 |
void write(char[] cbuf) | 写入一个字符数组 |
void write(char[] cbuf, int off, int len) | 写入字符数组的一部分 |
void write(String str) | 写一个字符串 |
void write(String str, int off, int len) | 写一个字符串的一部分 |
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("/Users/itzhuzhu/Desktop/Java/ideaTest/io/a.txt");
// void write(int c) 写一个字符
fw.write(99);
// void write(char[] cbuf) 写入一个字符数组
// 写的是int整数打印出来就是对应的ASCll码表,String类型就是原样输出
char[] chars = {97, 98, 99, 100};
fw.write(chars);
// void write(char[] cbuf, int off, int len) 写入字符数组的一部分
fw.write(chars, 0, chars.length);
// void write(String str) 写一个字符串
String s = "彭于晏都不着急结婚我急个锤子";
fw.write(s);
// void write(String str, int off, int len) 写一个字符串的一部分
fw.write(s, 0, 2);
fw.close();
}
刷新和关闭的方法:
方法名 | 说明 |
---|---|
flush() | 刷新流,之后还可以继续写数据 |
close() | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
public class Demo03 {
public static void main(String[] args) throws IOException {
FileWriter fw = new FileWriter("/Users/itzhuzhu/Desktop/Java/ideaTest/io/a.txt");
fw.write("活到老,学到老");
fw.flush();
fw.write("走我了吗flush");
fw.close();
// fw.write("走我了吗close"); 当场异常
}
}
字符流读取:
构造方法
方法名 | 说明 |
---|---|
FileReader(File file) | 在给定从中读取数据的 File 的情况下创建一个新 FileReader |
FileReader(String fileName) | 在给定从中读取数据的文件名的情况下创建一个新 FileReader |
成员方法
方法名 | 说明 |
---|---|
int read() | 一次读一个字符数据 |
int read(char[] cbuf) | 一次读一个字符数组数据 |
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("/Users/itzhuzhu/Desktop/Java/ideaTest/io/a.txt");
// int read():一次读一个字符数据
int ch;
while ((ch = fr.read()) != -1) {
System.out.print((char) ch);
}
//int read(char[] cbuf):一次读一个字符数组数据
int len;
// 字符流不能用byte,打印也和字节流有点差别
char[] chars = new char[1024];
while ((len = fr.read(chars)) != -1) {
System.out.println(new String(chars, 0, len));
}
fr.close();
}
字符缓冲流:
构造方法
方法名 | 说明 |
---|---|
BufferedWriter(Writer out) | 创建字符缓冲输出流对象,可以指定缓冲区大小,或者可以接受默认大小 |
BufferedReader(Reader in) | 创建字符缓冲输入流对象,可以指定缓冲区大小,或者可以接受默认大小 |
字符缓冲流特有功能:
方法名 | 说明 |
---|---|
void newLine() | 写一行行分隔符,行分隔符字符串由系统属性定义 |
String readLine() | 读一行文字。 结果包含行的内容的字符串,不包括任何行终止字符如果流的结尾已经到达,则为null |
public class Demo07 {
public static void main(String[] args) throws IOException, IOException {
BufferedWriter bw = new BufferedWriter(new FileWriter("/Users/itzhuzhu/Desktop/Java/ideaTest/io/a.txt"));
bw.write("活到老");
bw.newLine();
bw.write("学到老");
BufferedReader br = new BufferedReader(new FileReader("/Users/itzhuzhu/Desktop/Java/ideaTest/io/a.txt"));
//一次读取一个字符数据
/**
int ch;
while ((ch=br.read())!=-1) {
System.out.print((char)ch);
}
*/
//一次读取一个字符数组数据
char[] chs = new char[1024];
int len;
while ((len = br.read(chs)) != -1) {
System.out.print(new String(chs, 0, len));
}
String s;
// 一次读一行,返回的null不是-1,所以这里的判断条件为null
while ((s = br.readLine()) != null) {
System.out.println(s);
}
br.close();
}
}