IO流:存储和读取数据的解决方案。用于读写文件中的数据(可以读写文件,或数据中的网络)
output:写出数据,将程序保存到本地文件中。程序--->文件
input:读取数据,本地文件加载到程序中。文件--->程序
IO流中是以程序为参照物在读写,是程序在读取文件中的数据,是程序在往文件中写出数据,或者说是内存也是可以的,因为程序运行在内存中的。
1、IO流的分类
按照流的方向分为:输入(读取)流和输出(写出)流。
按照操作文件类型分:字节流和字符流。
字节流:可以操作所有类型的文件。
字符流:只能操作纯文本文件。纯文本文件是Windows自带的记事本打开能读懂,如txt、md、xml、lrc文件,而word、excel不是纯文本文件。
2、IO流体系
字节流:InputStream字节输入流、OutputStream字节输出流。
字符流:Reader字符输入流、Writer字符输出流。
InputStream、OutputStream、Reader、Writer都是抽象类,不能直接创建他们的对象。要看他们的子类。
3、FileOutputStream
FileOutputStream 操作本地文件的字节输出流,可以把程序中的数据写到本地文件中
//1.创建对象
FileOutputStream fos = new FileOutputStream("D:\\JavaTest\\myio\\myio\\a.txt");
//2.写出数据
fos.write(97);
//3.释放资源
fos.close();
字节输出流的细节:
1.创建字节输出流对象
参数是字符串表示的路径或者是File对象都是可以的
如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的
如果文件已经存在,则会清空文件
2.写数据
write方法的参数是整数,但是实际上写到本地文件中的是整数在ASCII上对应的字符
3.释放资源
每次使用完流之后都要释放资源
FileOutputStream原理:
FileOutputStream fos = new FileOutputStream("D:\\JavaTest\\myio\\myio\\a.txt"); 根据书写的路径让文件跟程序之间产生一个数据传送的通道,fos.write(97);通过write方法写出数据,相当于在通道中进行数据传输fos.close();关闭这个通道
FileOutputStream写数据的三种方式:
public class ByteStreamDemo3 {
public static void main(String[] args) throws IOException {
/*
void write(int b) 一次写一个字节数据
void write(byte[] b) 一次写一个字节数组数据
void write(byte[] b, int off, int len) 一次写一个字节数组的部分数据
参数一:
数组
参数二:
起始索引
参数三:
个数
*/
//1.创建对象
FileOutputStream fos = new FileOutputStream("myio\\a.txt");
//2.写出数据
/* //方式一:一次写一个字节数据
fos.write(97); // a
fos.write(98); // b*/
byte[] bytes = {97, 98, 99, 100, 101};
/* //方式二:一次写一个字节数组数据
fos.write(bytes);*/
//方式三:一次写一个字节数组的部分数据
fos.write(bytes,1,2);// b c
//3.释放资源
fos.close();
}
}
FileOutputStream写出数据的两个小问题:
public class ByteStreamDemo4 {
public static void main(String[] args) throws IOException {
/*
1、换行写:
再次写出一个换行符就可以了
windows: \r\n 回车换行,回车是把光标移到这行前面,换行是把光标移到下一行
Linux: \n
Mac: \r
细节:
在windows操作系统当中,java对回车换行进行了优化。
虽然完整的是\r\n,但是我们写其中一个\r或者\n,
java也可以实现换行,因为java在底层会补全。
建议:
不要省略,还是写全了。
2、续写:
如果想要续写,打开续写开关即可
开关位置:创建对象的第二个参数
默认false:表示关闭续写,此时创建对象会清空文件
手动传递true:表示打开续写,此时创建对象不会清空文件
*/
//1.创建对象
FileOutputStream fos = new FileOutputStream("myio\\a.txt",true);
//2.写出数据
String str = "kankelaoyezuishuai";
byte[] bytes1 = str.getBytes();
fos.write(bytes1);
//再次写出一个换行符就可以了
String wrap = "\r\n";
byte[] bytes2 = wrap.getBytes();
fos.write(bytes2);
String str2 = "666";
byte[] bytes3 = str2.getBytes();
fos.write(bytes3);
//3.释放资源
fos.close();
}
}
4、FileInputStream
FileInputStream 操作本地文件的字节输入流,可以把本地文件中的数据读取到程序中来
public class ByteStreamDemo1 {
public static void main(String[] args) throws IOException {
/*
* 演示:字节输入流FileInputStream
* 实现需求:读取文件中的数据。(暂时不写中文)
*
* 实现步骤:
* 创建对象
* 读取数据
* 释放资源
* */
//1.创建对象
FileInputStream fis = new FileInputStream("myio\\a.txt");//文本a中是:a b c d e
//2.读取数据
int b1 = fis.read();
System.out.println((char)b1);//a
int b2 = fis.read();
System.out.println((char)b2);//b
int b3 = fis.read();
System.out.println((char)b3);//c
int b4 = fis.read();
System.out.println((char)b4);//d
int b5 = fis.read();
System.out.println((char)b5);//e
int b6 = fis.read();
System.out.println(b6);//-1 读不到数据返回-1
//3.释放资源
fis.close();
}
}
字节输入流的细节:
1.创建字节输入流对象
细节1:如果文件不存在,就直接报错。Java为什么会这么设计呢?
输出流:不存在,创建 把数据写到文件当中
输入流:不存在,而是报错呢?
因为创建出来的文件是没有数据的,没有任何意义。所以Java就没有设计这种无意义的逻辑,文件不存在直接报错。程序中最重要的是:数据。
2.写数据
细节1:一次读一个字节,读出来的是数据在ASCII上对应的数字
细节2:读到文件末尾了,read方法返回-1。如果读取的是-1, - 1 分开读
3.释放资源
细节:每次使用完流之后都要释放资源
FileInputStream循环读取:
public class ByteStreamDemo3 {
public static void main(String[] args) throws IOException {
/*
字节输入流循环读取
*/
//1.创建对象
FileInputStream fis = new FileInputStream("myio\\a.txt");
//2.循环读取
int b;
while ((b = fis.read()) != -1) {
System.out.println((char) b);
}
//3.释放资源
fis.close();
/*以下错误代码:
//read :表示读取数据,而且是读取一个数据就移动一次指针
FileInputStream fis = new FileInputStream("myio\\a.txt");
//2.循环读取
while ((fis.read()) != -1) {
System.out.println(fis.read());//98 100 -1
}
//3.释放资源
fis.close();*/
}
}
5、文件拷贝
一次读取一个字节 速度慢
public class ByteStreamDemo4 {
public static void main(String[] args) throws IOException {
/*
* 练习:
* 文件拷贝
* 把D:\itheima\movie.mp4拷贝到当前模块下。
* 练习:
* 要求统计一下拷贝时间,单位毫秒
* */
long start = System.currentTimeMillis();
//1.创建对象
FileInputStream fis = new FileInputStream("D:\\itheima\\movie.mp4");
FileOutputStream fos = new FileOutputStream("myio\\copy.mp4");
//2.拷贝
//核心思想:边读边写
int b;
while((b = fis.read()) != -1){
fos.write(b);
}
//3.释放资源
//规则:先开的最后关闭
fos.close();
fis.close();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
一次读取一个字节数组的数据,每次读取会尽可能把数组装满
public class ByteStreamDemo5 {
public static void main(String[] args) throws IOException {
/*
public int read(byte[] buffer) 一次读一个字节数组数据
*/
//1.创建对象
FileInputStream fis = new FileInputStream("myio\\a.txt");//a:abcde
//2.读取数据
byte[] bytes = new byte[2];
//一次读取多个字节数据,具体读多少,跟数组的长度有关
//返回值:本次读取到了多少个字节数据
int len1 = fis.read(bytes);
System.out.println(len1);//2
String str1 = new String(bytes,0,len1);
System.out.println(str1);//ab
int len2 = fis.read(bytes);
System.out.println(len2);//2
String str2 = new String(bytes,0,len2);//内存中会覆盖之前读取的ab
System.out.println(str2);//cd
int len3 = fis.read(bytes);
System.out.println(len3);// 1
String str3 = new String(bytes,0,len3);
System.out.println(str3);// e
//3.释放资源
fis.close();
}
}
练习1:优化之前的代码
public class ByteStreamDemo6 {
public static void main(String[] args) throws IOException {
/* 练习:
文件拷贝
把D:\itheima\movie.mp4 (16.8 MB) 拷贝到当前模块下。*/
long start = System.currentTimeMillis();
//1.创建对象
FileInputStream fis = new FileInputStream("D:\\itheima\\movie.mp4");
FileOutputStream fos = new FileOutputStream("myio\\copy.mp4");//拷贝到
//2.拷贝
int len;
byte[] bytes = new byte[1024 * 1024 * 5];//5MB大小的数组
while((len = fis.read(bytes)) != -1){
fos.write(bytes,0,len);//读取多少写多少
}
//3.释放资源
fos.close();
fis.close();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
6、不同JDK版本的捕获异常处理
基本做法: 手动释放资源
try { 可能出现异常的代码; } catch (异常类名 变量名) { 异常的处理代码; } finally { 执行所有资源后释放操作; }注意:finally里面的代码一定被执行,除非虚拟机停止。
public class ByteStreamDemo7 {
public static void main(String[] args) {
//利用try...catch...finally捕获拷贝文件中代码出现的异常
//1.创建对象
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("D:\\itheima\\movie.mp4");
fos = new FileOutputStream("myio\\copy.mp4");
//2.拷贝
int len;
byte[] bytes = new byte[1024 * 1024 * 5];
while((len = fis.read(bytes)) != -1){
fos.write(bytes,0,len);
}
} catch (IOException e) {
//e.printStackTrace();
} finally {
//3.释放资源
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
接口:AutoCloseable 特定情况下,可以自动释放资源。
JDK7方案:仅了解 资源用完最终自动释放
try (创建流对象1;创建流对象2) { 可能出现异常的代码; } catch (异常类名 变量名) { 异常的处理代码; }注意:只有实现了AutoCloseable 接口的类,才能在小括号中创建流对象。
public class ByteStreamDemo8 {
public static void main(String[] args) {
/*JDK7:IO流中捕获异常的写法*/
//try后面的小括号中写创建对象的代码
//注意:只有实现了AutoCloseable接口的类,才能在小括号中创建对象。
try (FileInputStream fis = new FileInputStream("D:\\itheima\\movie.mp4");
FileOutputStream fos = new FileOutputStream("myio\\copy.mp4")) {
//2.拷贝
int len;
byte[] bytes = new byte[1024 * 1024 * 5];
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
JDK9方案:仅了解 资源用完最终自动释放
创建流对象1; 创建流对象2; try (流1;流2) { 可能出现异常的代码; } catch (异常类名 变量名) { 异常的处理代码; }
public class ByteStreamDemo9 {
public static void main(String[] args) throws FileNotFoundException {//异常抛出处理
//JDK9:IO流中捕获异常的写法
FileInputStream fis = new FileInputStream("D:\\itheima\\movie.mp4");
FileOutputStream fos = new FileOutputStream("myio\\copy.mp4");
try (fis;fos) {
//2.拷贝
int len;
byte[] bytes = new byte[1024 * 1024 * 5];
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}