JAVA IO
- Java IO可以让我们用标准的读写操作来完成对不同设备的读写数据工作。
- Java将IO按照方向划分为输入与输出,参照点是我们写的程序。
- 输入:用来读取数据的,是从外界到程序的方向,用于获取数据。
- 输出:用来写出数据的,是从程序到外界的方向,用于发送数据。
java将IO比喻为“流”,即:stream。 就像生活中的“电流”,“水流”一样,它是以同一个方向顺序移动的过程。只不过这里流动的是字节(2进制数据),所以在IO中有输入流和输出流之分,我们理解他们是连接程序与另一端的“管道”,用于获取或发送数据到另一端。
Java定义了两个超类(抽象类)
java.io.InputStream
:所有字节输入流的超类,其中定义了读取数据的方法。因此将来不管读取的是什么设备(连接该设备的流)都有这些读取的方法,因此我们可以用相同的方法读取不同设备中的数据。java.io.OutputStream
:所有字节输出流的超类,其中定义了写出数据的方法。
Java将流分为两类:节点流
和处理流
- 节点流:也称为低级流。节点流的另一端是明确的,是实际读写数据的流,读写一定是建立在节点流基础上进行的。
- 处理流:也称为高级流。处理流不能独立存在,必须连接在其他流上,目的是当数据流经当前流时对数据进行加工处理来简化我们对数据的该操作。
实际应用中,我们可以通过串联一组高级流到某个低级流上以流水线式的加工处理对某设备的数据进行读写,这个过程也成为流的连接,这也是IO的精髓所在。
文件流
文件流是一对低级流,用于读写文件数据的流。用于连接程序与文件(硬盘)的“管道”,负责读写文件数据。
文件输出流:java.io.FileOutputStream
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文件内容:
00000001
*/
fos.write(1);
/*
vvvvvvvv
00000000 00000000 00000000 00000010
demo.dat文件内容
00000001 00000010
*/
fos.write(2);
fos.close();
System.out.println("执行完了!");
}
}
文件输入流:java.io.FileInputStream
public class FISDemo {
public static void main(String[] args) throws IOException {
/*
demo.dat文件内容
00000001 00000010
*/
FileInputStream fis = new FileInputStream("./demo.dat");
/*
int read()
读取一个字节,并一int型返回。返回的整数中读取的字节部分在该整数2进制的最后8位上
如果返回值为整数-1,则表示流读取到了末尾。对于读取文件而言就是EOF(end of file
文件末尾)
第一次调用read():
int d = fis.read();
demo.dat文件内容
00000001 0000000
^^^^^^^^
读取该字节
返回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 00000010
^^^^^^^^
读取该字节
返回int值时,2进制样子:
00000000 00000000 00000000 00000010
^^^^^^^^
|-----补充24个0(3字节)-----| 读取的字节
返回的int值d就是上述内容
*/
d = fis.read();
System.out.println(d);
/*
第三次调用read()
d = fis.read();
fos.dat文件内容
00000001 00000010
^^^^^^^^
文件末尾了
返回int值时,2进制样子:
11111111 11111111 11111111 11111111
^^^^^^^^
|-----补充32个1(4字节,来表示-1)-----|
返回的int值d就是上述内容
*/
d = fis.read();
System.out.println(d);
fis.close();
}
}
文件的复制
public class CopyDemo {
public static void main(String[] args) throws IOException {
/*
创建文件输入流与输出流
*/
FileInputStream fis = new FileInputStream("./image.png");
FileOutputStream fos = new FileOutputStream("./image_copy.png");
// 开始系统时间
long start = System.currentTimeMillis();
// 每次读取的字节
int d;
// 循环读写
while ((d = fis.read()) != -1) {
fos.write(d);
}
// 结束时间
long end = System.currentTimeMillis();
/*
关闭文件输入输出流
*/
fis.close();
fos.close();
System.out.println(String.format("文件复制结束,耗时%dms", end - start));
}
}
块读写的文件操作
int read(byte[] data)
一次性从文件中读取给定的字节数组总长度的字节量,并存入到该数组中。 返回值为实际读取到的字节量。若返回值为-1则表示读取到了文件末尾。
块写操作 void write(byte[] data)
一次性将给定的字节数组所有字节写入到文件中
void write(byte[] data,int offset,int len)
一次性将给定的字节数组从下标offset处开始的连续len个字节写入文件
public class CopyDemo2 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("./jri.jpg");
FileOutputStream fos = new FileOutputStream("./jri_copy.jpg");
/*
InputStream中定义了块读字节的方法:
int read(byte[] data)
一次性读取给定的字节数组总长度的字节量并存入到该数组中,返回值为实际读取到的字节量
如果返回值为-1说明已经是流的末尾了。
原文件内容(6字节):
11110000 10101010 01010101 11001100 00110011 00001111
byte[] data = new byte[4];
data:{00000000,00000000,00000000,00000000} 每个元素用2进制表示
第一次调用read()块读:
int len = fis.read(data);
文件内容:
11110000 10101010 01010101 11001100 00110011 00001111
^^^^^^^^ ^^^^^^^^ ^^^^^^^^ ^^^^^^^^
data:{11110000,10101010,01010101,11001100}
len:4 4表示实际读取到了4个字节并存入到了data数组的前4个字节位置上
第二次调用read()块读:
len = fis.read(data);
文件内容:
11110000 10101010 01010101 11001100 00110011 00001111
^^^^^^^^ ^^^^^^^^ ^^^^^^^^ ^^^^^^^^
希望读取4字节,实际只有2个字节可以读取了
data:{00110011,00001111,01010101,11001100}
^^^^^^^^ ^^^^^^^^
|-第二次读取的数据-||--- 旧数据 ---|
len:2 2表示本次实际读取到了2个字节并存入到data数组的前2个字节的位置上
第三次调用read()块读:
len = fis.read(data);
文件内容:
11110000 10101010 01010101 11001100 00110011 00001111
^^^^^^^^
文件末尾了
data:{00110011,00001111,01010101,11001100}
|---------没有任何变化--------------|
len:-1 -1表示本次一个字节都没有读取到,已经是末尾了!
OutputStream定义了块写文本数据:
void write(byte[] data)
一次性将给定的字节数组中所有字节写出
void write(byte[] data,int offset,int len)
一次性将给定数组从下标offset处开始的连续len个字节写出
*/
/*
00000000 8位2进制为1byte(1字节)
1024byte 1kb
1024kb 1mb
1024mb 1gb
*/
// 10kb
byte[] data = new byte[1024 * 10];
// 每次实际读取的字节数
int len;
// 开始时间
long start = System.currentTimeMillis();
while ((len = fis.read(data)) != -1) {
fos.write(data, 0, len);
}
long end = System.currentTimeMillis();
System.out.println(String.format("复制完成!耗时%dms", end - start));
/*
关闭流
*/
fis.close();
fos.close();
}
}
写文本数据
String提供方法: byte[] getBytes(String charsetName)
将当前字符串转换为一组字节
参数为字符集的名字,常用的是UTF-8。 其中中文字3字节表示1个,英文1字节表示1个。
public class WriteStringDemo {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("demo.txt");
String str = "找一个和弦开始唱";
/*
支持中文的常见字符集有:
GBK:国标编码。英文每个字符占1个字节,中文每个字符占2个字节
UTF-8:内部是unicode编码,在这个基础上不同了少部分2进制信息作为长度描述
英文每个字符占1字节
中文每个字符占3字节
String提供了将字符串转换为一组字节的方法
byte[] getBytes(String charsetName)
参数为字符集的名字,名字不缺分大小写,但是拼写错误会引发异常:
UnsupportedEncodingException
不支持 字符集 异常
*/
byte[] data = str.getBytes(StandardCharsets.UTF_8);
fos.write(data);
fos.write("那故事以往的时光".getBytes(StandardCharsets.UTF_8));
System.out.println("写出完毕!");
fos.close();
}
}
文件输出流-追加模式
重载的构造方法可以将文件输出流创建为追加模式
FileOutputStream(String path,boolean append)
FileOutputStream(File file,boolean append)
当第二个参数传入true时,文件流为追加模式,即:指定的文件若存在,则原有数据保留,新写入的数据会被顺序的追加到文件中。
public class FileAppendDemo {
public static void main(String[] args) throws IOException {
/*
FileOutputStream默认创建方式为覆盖模式,即:如果连接的文件存在,
则会将该文件原有数据全部删除。然后将通过当前流写出的内容保存到文件中。
重载的构造方法允许我们再传入一个boolean型参数,如果这个值为true,则
文件流为追加模式,即:若连接文件时该文件存在,原有数据全部保留,通过当前
流写出的数据会顺序的追加到文件中。
*/
FileOutputStream fos = new FileOutputStream("demo.txt", true);
fos.write("突然好想你".getBytes(StandardCharsets.UTF_8));
fos.write("你会在哪里".getBytes(StandardCharsets.UTF_8));
System.out.println("写出完毕!");
fos.close();
}
}
读取文本数据
public class ReadStringDemo {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("demo.txt");
/*
int available()
获取当前流可以读取到的字节数
*/
int len = fis.available();
// 将文件中所有字节读取回来并存入到data数组上
byte[] data = new byte[len];
fis.read(data);
//使用String的构造器按照UTF-8编码将字节还原为字符串
String str = new String(data, StandardCharsets.UTF_8);
System.out.println(str);
fis.close();
}
}