一、IO流
把这种数据的传输,可以看做是一种数据的流动,按照流动的方向,以内存为基准,分为 输入
input
和 输出output
,即流向内存是输入流,流出内存的输出流。
Java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作。
- 输入 也叫作 读取数据;
- 输出 也叫作 写出数据。
根据数据的流向分为。
- 输入流:把数据从 其他设备 上读取到 内存 中的流。
- 输出流:把数据从 内存 中写出到 其他设备 上的流。
根据数据的类型分为
- 字节流:以字节为单位,读写数据的流。
- 字符流:以字符为单位,读写数据的流
写入数据的原理(内存-->硬盘):java程序-->JVM(java虚拟机)-->OS(操作系统)-->OS调用写数据的方法-->把数据写入到文件中
顶级父类抽象类
输入流 | 输出流 | |
---|---|---|
字节流 | 字节输入流(InputStream) | 字节输出流(OutputStream) |
字符流 | 字符输入流(Reader) | 字符输出流(Writer) |
二、字节输出流【OutputStream】顶级父类
无论使用什么样的流,底层都是二进制数据。
StringObj.getBytes()
:得到字节字符串对应的字节数组
java.io.OutputStream
抽象类 字节输出流超类定义了基本共性的方法(忽略public)
-
void close()
: 关闭此输出流并释放与此流相关联的任何系统资源。 -
void flush()
: 刷新此输出流并强制任何缓冲的输出字节被写出。 -
void write(byte[] b)
: 将b.length字节从指定的字节数组写入此输入流。 -
void write(byte[] b, int off, int len)
: 从指定的字节数组写入len字节,从偏移量为off的位置开始输出到此输出流 -
abstract void write(int b)
:将指定的 字节 输出流,就是写入一个字节
close() 方法,当完成流操作后,必须调用此方法,释放系统资源。
FileOutputStream 字节输出流
- 属于OutputStream的子类
-
java.io.FileOutputStream
类是文件输出流,将数据写出到文件。
构造方法
-
public FileOutputStream(File file)
: 创建文件输出流 写入指定File对象表示的文件 -
public FileOutputStream(String name)
: 创建文件输出流以指定的名称写入文件。
创建一个流对象,必须传入一个文件路径,该路径下,如果没有这个文件,会创建该文件。
如果这个文件已存在,会清空这个文件的数据
// 使用File对象创建流对象
File file = new File(".\\day_09\\test.txt");
FileOutputStream fos = new FileOutputStream(file);
// 使用文件名创建流对象
FileOutputStream fos2 = new FileOutputStream(".\\day_09\\test2");
1.写出字节数据:write(int b)
: 每次写出一个字节数据
FileOutputStream fos2 = new FileOutputStream(".\\day_09\\test2.txt");
fos2.write(100);
fos2.close(); // 必须关闭资源
int类型在内存中为4个字节,但是上面的只需要一个字节就能存储,所以只会保留一个字节(字节流没编码那么麻烦)。
使用完必须释放资源
3.写出字节数组 write(byte[] b)
FileOutputStream fos2 = new FileOutputStream(".\\day_09\\test2.txt");
fos2.write("我是一个兵,来自老百姓".getBytes());
fos2.close(); // 必须关闭资源
写多个字节,文本方式打开的时候,查询的规则:
- 第一个字节是正数(0-127),查询Ascii表
- 第一个字节是负数,(winGBK)查询第一个与第二个组合找中文。
4.写出指定长度字节数组:write(byte[] b, int off, int len)
FileOutputStream fos2 = new FileOutputStream(".\\day_09\\test2.txt");
fos2.write("天王盖地虎,宝塔镇河妖".getBytes(), 18, 15);
// utf-8编码 宝塔镇河妖
fos2.close(); // 必须关闭资源
5.数据追加续写
保留数据并进行追加操作,构造方法需要传入一个是否追加的参数
-
public FileOutputStream(File file, boolean append)
: 文件对象形式,追加操作 -
public FileOutputStream(String name, boolean append)
: 字符串路径形式, 追加操作。
如果要追加数据就传入true,清空就传入false(默认是false)
FileOutputStream fos2 = new FileOutputStream(".\\day_09\\test2.txt", true);
fos2.write("天王盖地虎,宝塔镇河妖".getBytes(), 18, 15);
fos2.close();
6.写出换行
FileOutputStream fos2 = new FileOutputStream(".\\day_09\\test2.txt", true);
fos2.write("\r\n".getBytes());
fos2.write("windows下换行了".getBytes());
fos2.close();
- 回车符 \r 和换行符 \n :
- 回车符:回到一行的开头(return)。
- 换行符:下一行(newline)。
- 系统中的换行:
- Windows系统里,每行结尾是 回车+换行 ,即 \r\n
- Unix系统里,每行结尾只有 换行 ,即 \n
- Mac系统里,每行结尾是 回车 ,即 \r 。从 Mac OS X开始与Linux统一。
三、字节输人流【InputStream】顶级父类
读取数据的原理(硬盘-->内存):java程序-->JVM-->OS-->OS读取数据的方法-->读取文件
先介绍字节输出流的超类InputStream
字节输出流
java.io.InputStream
抽象类 总超类 ,字节信息->读到内存,共性方法如下(省略public)
-
void close()
: 【必须使用】关闭此输入流并释放与此流相关联的任何系统资源。 -
abstract int read()
: 从输入流读取的下一个字节, -
int read(byte[] b)
: 从输入流中读取一些字节数,并将它们存储大字节数组中。
必须使用close方法释放资源
FileInputStream 字节输出流
java.io.FileInputStream
类 读取字节
1.构造方法(省略public)
-
FileInputStream(File file)
:通过打开一个与实际文件的连接 创建一个FileInputStream,该文件由文件由文件系统中的File对象file命名。 -
FileInputStream(String name)
: 通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名。
创建一个流对象,必须传入一个文件路径,如果该路径下没有改文件(文件不存在),会抛出
FileNotFondException
// 使用File对象创建流对象
File file = new File(".\\day_09\\a.txt");
new FileOutputStream(file).write("还是写一点东西吧".getBytes());
FileInputStream fos = new FileInputStream(file);
// 使用文件名称创建流对象
FileInputStream fos2 = new FileInputStream(".\\day_09\\a.txt");
2.读取字节数据:int read()
方法,每次读取一个字节的数据提升为int类型,读到文件末尾,返回-1
// 使用文件名称创建流对象
FileInputStream fos2 = new FileInputStream(".\\day_09\\a.txt");
int b;
while ((b = fos2.read()) != -1)
System.out.println((char)b);
fos2.close();
- 虽然读取了一个字节,但是会自动提升为int类型。
- 流操作完毕后,调用close方法,必须释放系统资源。
3.使用字节数组读取【开发中建议的方式】:int read(byte[] b)
每次读取b的长度个字节到数组中,返回读取代的有效字节个数,读取到末尾时,返回-1。
1. 方法的参数byte[]的作用:
- 起到缓冲作用,存储每次读取到的多个字节
- 数组的长度一把定义为1024(1kb)或者1024的整数倍
2. 方法的返回值int是每次读取的有效字节个数
3. String类的构造方法
- String(byte[] bytes)
:把字节数组转换为字符串
- String(byte[] bytes, int offset, int length)
把字节数组的一部分转换为字符串 offset:数组的开始索引 length:转换的字节个数
FileInputStream fos2 = new FileInputStream(".\\day_09\\a.txt");
int len;
byte[] b = new byte[7];
while ((len = fos2.read(b)) != -1) {
System.out.println(new String(b).substring(0, len));
// 也可以使用 new String(b, 0, len);
}
fos2.close();
使用数组读取,每次读取多少个字节,减少了系间的IO操作次数,从而提高了读写效率,建议开发中使用
复制图片案例,带有多线程的进度条
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Random;
// Courier进行数据传递
class Courier {
long size;
long receive;
public Courier(long size){
this.size = size;
receive = 0;
}
}
public class Main {
public static void main(String[] args) throws IOException, InterruptedException {
int len; // 记录返回的字节大小。
byte[] temp = new byte[1000]; // 记录字节数据。
Random random = new Random();
// 使用文件名称创建流对象
File file = new File(".\\day_09\\feifei.jpg");
FileInputStream in = new FileInputStream(file);
FileOutputStream out = new FileOutputStream(".\\day_09\\newfeifei.jpg", true);
// new 一个数据传递对象。
Courier courier = new Courier(file.length());
Runnable r = () -> {
Courier cour = courier;
int n = 25; // 进度条的长度
while (true) {
long computd = courier.receive;
long exists = computd * n / courier.size;
long i = 0;
System.out.print("[");
for(;i <= exists; i++) System.out.print(">");
for(;i < n; i++) System.out.print("-");
System.out.printf("]%5.2f%%",(computd * 1.0 / courier.size) * 100.0);
if(courier.receive == courier.size) {
System.out.println("\n传输完毕");
break;
}
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print("\r");
}
};
// 开启线程
new Thread(r,"进度条").start();
// 写入数据
while ((len = in.read(temp)) != -1) {
out.write(temp, 0, len);
courier.receive += len;
Thread.sleep(random.nextInt(100));
}
}
}