JAVA IO
1.java io可以让我们用标准的读写操作来完成对不同设备的读写数据工作.
2.java将IO按照方向划分为输入与输出,参照点是我们写的程序(内存).
3.输入(读):用来读取数据的,是从外界到程序(内存)的方向,用于获取数据.
4.输出(写):用来写出数据的,是从程序(内存)到外界的方向,用于发送数据.
5.java将IO比喻为"流",即stream,就像"电流","水流"一样,是以同一个方法顺序移动的过程,只不过这里流动的是字节(二进制数据),所以在IO中有输入流和输出流之分,我们理解他们是连接程序与另一端的"管道",用于获取或发送数据到另一端
一.流的分类
1. 第一种方式按照流的方向进行分类,以程序(内存)作为参照物
- 往程序(内存)中进去,叫做输入(Input)或者叫做读(Read)
- 从程序(内存)中出来,叫做输出(Output)或者叫做写(Write)
2. 第二种方式是按照读取数据的方式进行分类
注意:在java中只要"类名"以Stream结尾的都是字节流.以"Reader/Writer"结尾的都是字符流
- 有的流是按照字节读取的方式读取数据,一次读取一个字节byte,等同于一次读取8个二进制的位,这种流是万能的,什么类型的文件都可以读取,包括:文本文件,图片,声音文件,视频等等,
- 有的流时按照字符的方式读取数据的,一次读取一个字符char,这种流是为了方便读取普通文本而存在的(能用记事本编辑的都是普通文本文件,不一定都是.txt),这种流不能读取:图片,声音,视频等文件.只能读取纯文本文件.连word文件都无法读取(因为word不是纯文本,里面好含有格式)
3. 流分为两类:节点流(低级流)和处理流(高级流)
- 节点流:也称为低级流.节点流的另一端是明确的,是实际读写数据的流,读写一定是建立在节点流基础上进行的.
- 处理流:也称为高级流(包装流).处理流不能独立存在,必须连接在其他流上,目的是当数据流经当前流时对数据进行加工处理来简化我们对数据的该操作.
- 当一个流的构造方法中需要一个流的时候,这个被传进来的流叫做:节点流
外部负责包装的这个流,叫做:包装流,还有一个名字叫做:处理流
实际应用中,我们可以通过串联一组高级流到某个低级流上以流水线式的加工处理对某设备的数据进行读写,这个过程也成为流的连接,这也是IO的精髓所在.
补充:对字符流和字节流的理解
假设文件file.txt,采用字节流的话是可以这样读的:
a中国bc张三fe
第一次读:一个字节,正好读到'a'
第二次读:一个字节,正好读到'中'字符的一半
第三次读:一个字节,正好读到'中'字符的另外一半
字节流直接读取的是8个二进制位
假设文件file.txt,采用字符流的话是这样读的:
a中国bc张三fe
第一次读:'a'字符('a'字符在windows系统中占用1个字节)
第二次读:'中'字符('中'字符在windows系统中占用3个字节,第三个字节存储的是长度信息,实际是一个字节)
字符流可以识别,字符流可以一个字符一个字符检测出来
二.流的四大家族
java.io流有四大家族的首领(四个流都是顶级流,且都是抽象类,需要重写所有的抽象方法,也是继承Object)
1. java.io.InputStream 字节输入流
2. java.io.outputStream 字节输出流
3. java.io.Reader 字符输入流
4. java.io.Writer 字符输出流
四大家族流的知识点
- 四大家族的首领都实现了java.io.Closeable,都是可关闭的,都有close()方法
- 流是一个管道,是内存和硬盘之间的通道,用完以后一定要关闭,不然会耗费(占用)很多资源
- Closeable中有个方法close()方法,释放资源
- 所有的输出流(OutputStream和Writer)都实现了java.io.Flushable接口,都是可刷新的,都有flush()方法,养成好习惯,输出流在最终的输出之后,一定要记得flush()刷新一下,这个刷新表示将通道/管道当中剩余为输出的数据强行输出完(清空管道)
- flush():将管道当中剩余的数据全部写到文件中,管道清空,如果不flush,可能会导致丢失数据
三.java.io包下需要掌握的流有16个
文件专属:
java.io.FileIntputStream
java.io.FileOutputStream
java.io.FileReader
java.io.FileWriter
转换流:(将字节流转换成字符流)
java.io.InputStreamReader
java.io.OutputStreamWriter
缓冲流专属:
java.io.BufferedReader
java.io.BufferedWriter
java.io.BufferedIntputStream
java.io.BufferedOutputStream
数据流专属:
java.io.DataIntputStream
java.io.DataOutputStream
标准输出流
java.io.PrintWriter
java.io.PrintStream
对象专属流
java.io.ObjectInputStream
java.io.ObjectOutputStream
四.文件流
文件流是一对低级流,用于读写文件数据的流.用于连接程序与文件(硬盘)的"管道".负责读写文件数据.
理解:
- FileInputStream流必须有真实存在的路径
- FileOutputStream流文件不存在的时候会自动新建!
如果文件存在会把原来的内容清空然后再写
五.文件输出流流:java.io.FileOutputStream
FileOutputStream提供的常用构造器
1. FileOutputStream(String path)
2. FileOutputStream(File file)
package io;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* JAVA IO Input&Output 输入和输出
* java程序与外界交换数据是基于IO完成的,这里输入与输出一个负责读一个负责写
* 输入:是从外界到我们写的程序的方向,是用来从外界获取信息的。因此是"读"操作
* 输出:是从我们写的程序到外界的方向,是用来向外界发送信息的。因此是"写"操作
*
* java将IO比喻为"流",可以理解为是程序与外界连接的"管道",内部流动的是字节,并且
* 字节是顺着同一侧方向顺序移动的。
*
* java.io.InputStream 输入流,这个类是所有字节输入流的超类,规定了所有字节输入
* 流读取数据的相关方法。
* java.io.OutputStream 输出流,这个类是所有字节输出流的超类,规定了所有字节输出
* 流写出数据的相关方法。
*
* 实际应用中,我们连接不同的设备,java都专门提供了用于连接它的输入流与输出流,而
* 这些负责实际连接设备的流称为节点流,也叫低级流。是真实负责读写数据的流。
* 与之对应的还有高级流,高级流可以连接其他的流,目的是当数据流经它们时,对数据做某
* 种加工处理,用来简化我们的操作。
*
*
* 文件流
* java.io.FileInputStream和FileOutputStream
* 这是一对低级流,继承自InputStream和OutputStream。用于读写硬盘上文件的流
*
*/
public class FOSDemo {
public static void main(String[] args) throws IOException {
//向当前目录下的demo.dat文件中写入数据
/*
FileOutputStream提供的常用构造器
FileOutputStream(String path)
FileOutputStream(File file)
*/
//文件流创建时,如果该文件不存在会自动将其创建(前提是该文件所在目录必须存在!)
FileOutputStream fos = new FileOutputStream("./demo.dat");
/*
void write(int d)
向文件中写入1个字节,写入的内容是给定的int值对应的2进制的"低八位"
int值 1: vvvvvvvv
二进制:00000000 00000000 00000000 00000001
demo.dat文件内容:
00000000
*/
fos.write(1);
/*
vvvvvvvv
00000000 00000000 00000000 00000010
demo.dat文件内容
00000001 00000010
*/
fos.write(2);
fos.close();
System.out.println("执行完了!");
}
}
六文件输入流:java.io.FileInputStream
知识点:
1. 字节读取的方式:一次读取一个字节byte,这样内存和硬盘交互太频繁,基本上时间/资源都耗费在交互上面了
2. IDEA默认的当前路径是哪里?工程project的根就是IDEA的默认当前路径
3. 快读写:开始读,采用byte数组,一次读取多个字节。最多读取"数组.length"个字节,这个方法的返回值是:读取到的字节数量(不是字节本身)
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* end 结尾
* read 读
*
*
* 文件字节输入流,用于从文件中读取字节
*/
public class FISDemo {
public static void main(String[] args) throws IOException {
/*
fos.dat文件内容
00000001 00000011
*/
FileInputStream fis = new FileInputStream("fos.dat");
/*
int read()
读取一个字节,并一int型返回。返回的整数中读取的字节部分在该整数2进制的最后8位上
如果返回值为整数-1,则表示流读取到了末尾。对于读取文件而言就是EOF(end of file
文件末尾)
第一次调用read():
int d = fis.read();
fos.dat文件内容
00000001 00000011
^^^^^^^^
读取该字节
返回int值时,2进制样子:
00000000 00000000 00000000 00000001
^^^^^^^^
|-----补充24个0(3字节)-----| 读取的字节
返回的int值d就是上述内容
*/
int d = fis.read();
System.out.println(d);
/*
第二次调用read()
d = fis.read();
fos.dat文件内容
00000001 00000011
^^^^^^^^
读取该字节
返回int值时,2进制样子:
00000000 00000000 00000000 00000011
^^^^^^^^
|-----补充24个0(3字节)-----| 读取的字节
返回的int值d就是上述内容
*/
d = fis.read();
System.out.println(d);
/*
第三次调用read()
d = fis.read();
fos.dat文件内容
00000001 00000011
^^^^^^^^
文件末尾了
返回int值时,2进制样子:
11111111 11111111 11111111 11111111
^^^^^^^^
|-----补充32个1(4字节,来表示-1)-----|
返回的int值d就是上述内容
*/
d = fis.read();
System.out.println(d);
fis.close();
}
}
七.FileInputStream类的其他常用方法
- 数组转换成字符串:
new String(bytes,0,bytes.length)
把byte数组转换成字符串,读到多少个转换多少个
public static void main(String[] args) {
byte[] bytes = new byte[]{97,98,99,100};
System.out.println(new String(bytes,0,bytes.length));
}
- 返回流当中剩余的没有读到的字节数量:
int available()
这种方法不太适合太大的文件,因为byte[]数组不能太大 - 跳过几个字节不读:
long skip(long n)
返回的是跳过之后读取到的单个字节
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 文件的复制
*/
public class CopyDemo {
public static void main(String[] args) throws IOException {
//创建文件输入流读取原文件
FileInputStream fis = new FileInputStream("image.jpg");
//创建文件输出流写入复制文件
FileOutputStream fos = new FileOutputStream("image_cp.jpg");
int d;//保存每次读取到的字节
/*
原文件数据:
11000011 10101010 00001111 11001100 00110011 ...
^^^^^^^^
d = fis.read();
d:00000000 00000000 00000000 10101010
fos.write(d);
复制文件的数据:
11000011 10101010
*/
long start = System.currentTimeMillis();//获取当前系统时间的毫秒值(UTC时间)
while((d = fis.read()) != -1) {
fos.write(d);
}
long end = System.currentTimeMillis();//获取当前系统时间的毫秒值(UTC时间)
System.out.println("复制完毕!耗时:"+(end-start)+"ms");
fis.close();
fos.close();
}
}
补充知识点:
- 硬盘上只能存二进制,所以在文件中也只能写二进制
- 后缀不用纠结是啥。后缀其实是帮助windows大概识别文件中的数据是什么,以便让你知道大概用什么软件打开
- 流在使用完毕后要调用close关闭,保证释放资源
- 没有关闭流可能会出现的情况:当觉得一个文件没有用,然后右键删除。会出现该文件正在被其他程序所使用,不能删除。原因是:这种文件正拿流读着文件或者写着文件,没调用close()方法对流进行关闭,意味着这个文件在操作前没释放,别的程序是不能把它关掉的
- 不要用记事本把文件打开看看,因为记事本会把里面的二进制当成文字来翻译的
八.块读写的文件复制操作
int read(byte[] data)
1. 一次性从文件中读取给定的字节数组总长度的字节量,并存入到该数组中。
2. 返回值为实际读取到的字节量。若返回值为-1则表示读取到了文件末尾。
void write(byte[] data)
一次性将给定的字节数组所有字节写入到文件中
void write(byte[] data,int offset,int len)
一次性将给定的字节数组从下标offset处开始的连续len个字节写入文件
在判断输出和输入流不为空的时候,分开捕捉异常
一起try的时候,其中一个发生异常,可能会影响另一个流的关闭
if(fis ! = null){
try{
fos.close();
}catch(IOException e){
e.printStackTrace();
}
}
if(fos ! = null){
try{
fis.close();
}catch(IOException e){
e.printStackTrace();
}
}
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 通过提高每次读写的数据量,减少实际读写的次数,可以提高读写效率。
* 单字节读写是一种随机读写形式。而一组一组字节的读写是块读写形式。
*/
public class CopyDemo2 {
public static void main(String[] args) throws IOException {
//使用块读写形式完成文件复制
//创建文件输入流读取原文件
FileInputStream fis = new FileInputStream("wnwb.exe");
//创建文件输出流写复制文件
FileOutputStream fos = new FileOutputStream("wnwb_cp.exe");
/*
流提供了块读写的方法
int read(byte[] data)
一次性从文件中读取给定的字节数组总长度的字节量,并存入到该数组中。
返回值为实际读取到的字节量。若返回值为-1则表示读取到了文件末尾。
文件数据
11001100 11110000 10101010 00001111 00110011
^^^^^^^^ ^^^^^^^^ ^^^^^^^^
int d;
byte[] data = new byte[3];
[00000000 00000000 00000000]
第一次调用
d = fis.read(data);
[11001100 11110000 10101010]
d = 3 本次读取到了3个字节
文件数据
11001100 11110000 10101010 00001111 00110011
^^^^^^^^ ^^^^^^^^
第二次调用
d = fis.read(data);//仅读取了最后两个字节
[00001111 00110011 10101010]//前两个字节为本次读取的内容
^^^^^^^^ ^^^^^^^^
d = 2 本次读取到了2个字节
文件数据
11001100 11110000 10101010 00001111 00110011 文件末尾!
^^^^^^^^
第三次调用
d = fis.read(data);//一个字节都没有读取到
[00001111 00110011 10101010]数组没变化
d = -1 文件末尾
块写操作
void write(byte[] data)
一次性将给定的字节数组所有字节写入到文件中
void write(byte[] data,int offset,int len)
一次性将给定的字节数组从下标offset处开始的连续len个字节写入文件
*/
int len;//记录每次实际读取的字节量
/*
00000000 1byte 8位2进制称为1字节
1024byte 1kb
1024kb 1mb
1024mb 1gb
*/
byte[] data = new byte[1024*10];//10kb
long start = System.currentTimeMillis();
while((len = fis.read(data))!=-1){
fos.write(data,0,len);//读取多少就写多少
}
long end = System.currentTimeMillis();
System.out.println("复制完毕!耗时:"+(end-start)+"ms");
fis.close();
fos.close();
}
}
第二种文件复制写法:
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("temp.txt");
fos = new FileOutputStream("temp_cp.txt");
fos.flush();
byte[] bytes = new byte[1024*1024];
int readCount = 0; //定义一个读到的数量
while((readCount = fis.read(bytes))!= -1){
fos.write(bytes,0, readCount);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字符流和字节流的区别,快读写的时候传进去的数组是char数组
九.写文本数据
String提供方法,将当前字符串转换为一组字节:
byte[] getBytes(String charsetName)
package io;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
/**
* 向文件中写入文本数据
*/
public class WriteStringDemo {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("demo.txt");
String str = "super idol的笑容都没你的甜,";
/*
支持中文的常见字符集有:
GBK:国标编码。英文每个字符占1个字节,中文每个字符占2个字节
UTF-8:内部是unicode编码,在这个基础上不同了少部分2进制信息作为长度描述
英文每个字符占1字节
中文每个字符占3字节,第三个字节存的是长度信息
String提供了将字符串转换为一组字节的方法
byte[] getBytes(String charsetName)
参数为字符集的名字,名字不缺分大小写,但是拼写错误会引发异常:
UnsupportedEncodingException
不支持 字符集 异常
*/
byte[] data = str.getBytes("UTF-8");
fos.write(data);
fos.write("八月正午的阳光,都没你耀眼。".getBytes("UTF-8"));
System.out.println("写出完毕!");
fos.close();
}
}
补充:写的换行操作:fos.read("\n")
;
十.文件输出流-追加模式
文件输出流的两种创建模式:追加模式和覆盖模式
1. FileOutputStream默认创建方式为覆盖模式,即:如果连接的文件存在,则会将该文件原有数据全部删除。然后将通过当前流写出的内容保存到文件中
2. 重载的构造方法允许我们再传入一个boolean型的参数,如果这个值为true,则文件流为追加模式,即:若连接文件时该文件存在,原有数据全部保留,通过当前流写出的数据会顺序的追加到文件中
追加模式的两种创建模式
- FileOutputStream(String path,boolean append)
- FileOutputStream(File file,boolean append)
当第二个参数传入true时,文件流为追加模式,即:指定的文件若存在,则原有数据保留,新写入的数据会被顺序的追加到文件中
package io;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 文件流的追加写模式
*/
public class FileAppendDemo {
public static void main(String[] args) throws IOException {
/*
FileOutputStream默认创建方式为覆盖模式,即:如果连接的文件存在,
则会将该文件原有数据全部删除。然后将通过当前流写出的内容保存到文件中。
重载的构造方法允许我们再传入一个boolean型参数,如果这个值为true,则
文件流为追加模式,即:若连接文件时该文件存在,原有数据全部保留,通过当前
流写出的数据会顺序的追加到文件中。
*/
FileOutputStream fos = new FileOutputStream(
"demo.txt",true
);
fos.write("热爱105°的你,".getBytes("UTF-8"));
fos.write("滴滴清纯的蒸馏水。".getBytes("UTF-8"));
System.out.println("写出完毕!");
fos.close();
}
}
十一.读取文本数据
package io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
/**
* 从文件中读取文本数据
*/
public class ReadStringDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("fos.txt");
byte[] data = new byte[1024];
int len = fis.read(data);//块读操作
System.out.println("实际读取到了"+len+"个字节");
/*
String提供了将字节数组转换为字符串的构造方法:
String(byte[]data,String charsetName)
将给定的字节数组中所有字节按照指定的字符集转换为字符串
String(byte[]data,int offset,int len,String charsetName)
将给定的字节数组从下标offset处开始的连续len个字节按照指定的字符集转换为字符串
*/
String line = new String(data,0,len,"UTF-8");
System.out.println(line);
System.out.println(line.length());
fis.close();
}
}
十二.高级流
流连接示意图
十三缓冲流
java.io.BufferedOutputStream和BufferedInputStream.
- 缓冲流是一对高级流,作用是提高读写数据的效率
- 缓冲流内部有一个字节数组,默认长度为8k,缓冲流读写数据时一定是将数据的读写方式转换为块读写来保证读写效率
使用缓冲流完成文件复制操作
package io;
import java.io.*;
public class CopyDemo3 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("ppt.pptx");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("ppt_cp.pptx");
BufferedOutputStream bos = new BufferedOutputStream(fos);
int d;
long start = System.currentTimeMillis();
while((d = bis.read())!=-1){//使用缓冲流读取字节
bos.write(d);//使用缓冲流写出字节
}
long end = System.currentTimeMillis();
System.out.println("耗时:"+(end-start)+"ms");
bis.close();//关闭流时只需要关闭高级流即可,它会自动关闭它连接的流
bos.close();
}
}
补充:
- 高级流好比家里常见的对水做加工的设备,比如“净化器”,“热水器”。有了它们我们就不必再自己对水进行加工了
- 实际开发中我们经常会串联一组最终连接到低级流上,在读写操作时以流水线式的加工完成复杂IO操作。这个过程也称为"流的连接"。
- 所有的高级流都没有无参构造,需要把连接的流传进去
- 所有的高级流也都有传进去的流的方法
- 关流:关操作的最外层的高级流,因为高级流的方法里面,会调用连接的流的close方法