1.IO概述
1.1什么是IO
生活中,你肯定经历过这样的场景。当你编辑一个文本文件,忘记了 ctrl+s,可能文件就白白编辑了。当你电脑上插入一个U盘,可以把一个视频,拷贝到你的电脑硬盘里。那么数据都是在哪些设备上的呢?键盘、内存、硬盘、外接设备等等。
我们把这种数据的传输,可以看做是一私数据的流动,按照流动的方向,以内存为基准,分为 输入input 和 输出output,即流向内存是输入流,流出内存的输出流。
Java中I/O操作主要是指使用 java.io 包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写出数据。
1.2IO的分类
根据数据的流向分为:输入流和输出流。
- 输入流:把数据从 其他设备 上读取到 内存 中的流。
- 输出流 : 把数据从 内存 中写出到 其他设备 上的流。
格局数据的类型分为:字节流和字符流。
- 字节流 : 以字节为单位,读写数据的流。
- 字符流 : 以字符为单位,读写数据的流。
1.3 IO的流向说明图解
1.4顶级父类们
输入流 | 输出流 | |
字节流 | 字节输入流 InputStream | 字节输出流 OutputStream |
字符流 | 字符输入流 Reader | 字符输出流 Writer |
2.字节流
2.1一切皆为字节
一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。
2.2字节输出流【OutputStream】
java.io.outputstream 抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。
- public void close() : 关闭此输出流并释放与此流相关联的任何系统资源。
- public void flush() : 刷新此输出流并强制任何缓冲的输出字节被写出。
- public void write(byte[] b) :将 b.length字节从指定的字节数组写入此输出流。
- public void write(byte[] b, int off,int len) :从指定的字节数组写入 len字节,从偏移量 off开始输0出到此输出流。
- public abstract void write(int b) : 将指定的字节输出流。
小贴士:close方法,当完成流的操作时,必须调用此方法,释放系统资源。
OutputStream 类及其子类的对象表示一个字节输出流。OutputStream 类的常用子类如下。
- ByteArrayOutputStream 类:向内存缓冲区的字节数组中写数据。
- FileOutputStream 类:向文件中写数据。
- PipedOutputStream 类:连接到一个 PipedlntputStream(管道输入流)。
- ObjectOutputStream 类:将对象序列化。
利用 OutputStream 类的方法可以从流中写入一个或一批字节。
2.3 FileOutputStream类
outputStream 有很多子类,我们从最简单的一个子类开始。
java.io.FileOutputstream 类是文件输出流,用于将数据写出到文件。
构造方法
- public File0utputStream(File file) :创建文件输出流以写入由指定的 File对象表示的文件。
- public FileOutputStream(String name) : 创建文件输出流以指定的名称写入文件。
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。
- 构造举例,代码如下:
package Java2;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
public class InputTest4 {
public static void main(String[] args) throws FileNotFoundException {
//使用File对象创建流对象
File file=new File("a.txt");
FileOutputStream fos=new FileOutputStream(file);
//使用文件名称创建流对象
FileOutputStream fos1=new FileOutputStream("b.txt");
}
}
写出字节数据
- 写出字节 : write(int b) 方法,每次可以写出一个字节数据,代码使用演示:
package Java2;
import java.io.*;
public class OutPutTest1 {
public static void main(String[] args) throws IOException {
File f1=new File("d:/aa");
File f2=new File(f1,"a.txt");
f1.mkdirs();
f2.createNewFile();
//使用OutPutStream输出流完成对文件的写入操作内存输出到文件
OutputStream out=new FileOutputStream(f2);
//out.write(97); //按照解析数字 写入文件中 写入的是97对应的 a
//out.write(bytes); //按照ASCII解析数组 写入文件 写入的: abcdefg
// 写出数据
out.write(97);//写出第1个字节
out.write(98);//写出第2个字节
out.write(99);//写出第3个字节
}
}
输出结果:
abc
- 写出指定长度字节数组: write(byte[] b,int off,int len) ,每次写出从off索引开始,len个字节,代码使用演示:
package Java2;
import java.io.*;
public class OutPutTest2 {
public static void main(String[] args) throws IOException {
File f1=new File("d:/");
File f2=new File(f1,"a.txt");
f1.mkdirs();
f2.createNewFile();
//使用OutPutStream输出流完成对文件的写入操作内存输出到文件
OutputStream out=new FileOutputStream(f2);
//out.write(97); //按照解析数字 写入文件中 写入的是97对应的 a
byte[] bytes={97,98,99,100,101,102,103};
//out.write(bytes); //按照ASCII解析数组 写入文件 写入的: abcdefg
out.write(bytes,2,3);
}
}
输出结果:
cde
数据追加续写
经过以上的演示,每次程序运行,创建输出流对象,都会清空目标文件中的数据。如何保留目标文件中数据,还能继续添加新数据呢 ?
- public FileoutputStream(File file boolean append) : 创建文件输出流以写入由指定的 File对象表示的文件。
- public Fileoutputstream(String name, boolean append) : 创建文件输出流以指定的名称写入文件。
这两个构造方法,参数中都需要传入一个boolean类型的值,true 表示追加数据,fase 表示清空原有数据。这样创建的输出流对象,就可以指定是否追加续写了,代码使用演示:
package Java2;
import javax.sound.midi.Soundbank;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
public class OutPutTest3 {
public static void main(String[] args) throws IOException {
File f1=new File("d:/春.txt");
f1.createNewFile();
//第二个参数指定为true表示追加数据,否则是清空后写入数据(默认)
OutputStream out=new FileOutputStream(f1,true);
String msg="盼望着";
byte[] bytes=msg.getBytes();//获取字符串 解析后的 byte数组
System.out.println(Arrays.toString(bytes));
out.write(bytes);//把byte数组写入文件,相当于把字符串写入文件中
}
}
输出结果:
[-25, -101, -68, -26, -100, -101, -25, -99, -128]
写出换行
Windows系统里,换行符号是\r\n.
把以指定是否追加续写了,代码使用演示:
public class OutPutTest3 {
public static void main(String[] args) throws IOException {
File f1=new File("d:/春.txt");
f1.createNewFile();
OutputStream out=new FileOutputStream(f1);
String msg="盼望着";
byte[] bytes=msg.getBytes();//获取字符串 解析后的 byte数组
out.write(bytes);//把byte数组写入文件,相当于把字符串写入文件中
out.write("\n.".getBytes());
out.write("第二行".getBytes());
}
}
输出结果:
盼望着
第二行
回车符\r和换行符\n:
- 回车符:回到一行的开头(return)。
- 换行符:下一行(newline)。
系统中的换行:
- Windows系统里,每行结尾是 回车+换行 ,即\r\n ;
- Unix系统里,每行结尾只有 换行 ,即\n ;
- Mac系统里,每行结尾是 回车 ,即 \r 。从 Mac OS X开始与Linux统一。
2.4字节输入流【InputStream】
java.io.InputStream 抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。
- public void close() : 关闭此输入流并释放与此流相关联的任何系统资源。
- public abstract int read() : 从输入流读取数据的下一个字节。
- public int read(byte[] b) : 从输入流中读取一些字节数,并将它们存储到字节数组 b中。
InputStream 类及其子类的对象表示字节输入流,InputStream 类的常用子类如下:
- ByteArrayInputStream 类:将字节数组转换为字节输入流,从中读取字节。
- FileInputStream 类:从文件中读取数据。
- PipedInputStream 类:连接到一个 PipedOutputStream(管道输出流)。
- SequenceInputStream 类:将多个字节输入流串联成一个字节输入流。
- ObjectInputStream 类:将对象反序列化。
小贴士:close方法,当完成流的操作时,必须调用此方法,释放系统资源。
使用 InputStream 类的方法可以从流中读取一个或一批字节。
InputStream类的常用方法
方法名及返回值类型 | 说明 |
int read() | 从输入流中读取一个 8 位的字节,并把它转换为 0~255 的整数,最后返回整数。 如果返回 -1,则表示已经到了输入流的末尾。为了提高 I/O 操作的效率,建议尽量 使用 read() 方法的另外两种形式 |
int read(byte[] b) | 从输入流中读取若干字节,并把它们保存到参数 b 指定的字节数组中。 该方法返回 读取的字节数。如果返回 -1,则表示已经到了输入流的末尾 |
int read(byte[] b, int off, int len) | 从输入流中读取若干字节,并把它们保存到参数 b 指定的字节数组中。其中,off 指 定在字节数组中开始保存数据的起始下标;len 指定读取的字节数。该方法返回实际 读取的字节数。如果返回 -1,则表示已经到了输入流的末尾 |
void close() | 关闭输入流。在读操作完成后,应该关闭输入流,系统将会释放与这个输入流相关 的资源。注意,InputStream 类本身的 close() 方法不执行任何操作,但是它的许多 子类重写了 close() 方法 |
int available() | 返回可以从输入流中读取的字节数 |
long skip(long n) | 从输入流中跳过参数 n 指定数目的字节。该方法返回跳过的字节数 |
void mark(int readLimit) | 在输入流的当前位置开始设置标记,参数 readLimit 则指定了最多被设置标记的字 节数 |
boolean markSupported() | 判断当前输入流是否允许设置标记,是则返回 true,否则返回 false |
void reset() | 将输入流的指针返回到设置标记的起始处 |
注意:在使用 mark() 方法和 reset() 方法之前,需要判断该文件系统是否支持这两个方法,以避免对程序造成影响。
代码如下:
import java.io.*;
public class InputTest1 {
public static void main(String[] args) throws IOException {
//输出流:内存的内容输出到文件(写操作)输入流:文件内容输入到内存中(读操作)
File f1=new File("d:/春.txt");
//创建一个输入流,读取f1这个文件
InputStream input=new FileInputStream(f1);
//读取文件的一个字符,然后把字符转换为对应的数字返回。如果读取到文件的末尾,返回的是-1
int n;
while ((n=input.read())!=-1){
System.out.println((char)n);
}
}
}
2.5FilelnputStream类
java.io.FileInputstream 类是文件输入流,从文件中读取字节。
FileInputStream 是 Java 流中比较常用的一种,它表示从文件系统的某个文件中获取输入字节。通过使用 FileInputStream 可以访问文件中的一个字节、一批字节或整个文件。
在创建 FileInputStream 类的对象时,如果找不到指定的文件将拋出 FileNotFoundException 异常,该异常必须捕获或声明拋出。
FileInputStream 常用的构造方法主要有如下两种重载形式。
构造方法
- FileInputStream(File file) : 通过打开与实际文件的连接来创建一个FilelnputStream,该文件由文件系统中的 File对象 file命名。
- FileInputstream(String name) : 通过打开与实际文件的连接来创建一个 FilelnputStream,该文件由文件系统中的路径名 name命名。
当你创建一个流对象时,必须传入一个文件路径。该路径下,如果没有该文件,会抛出 FileNotFoundException。
构造举例,代码如下 :
例:假设有一个D:\my\HelloJava.java 文件,下面使用 FileInputStream 类读取并输出该文件的内容。代码如下:
public class Test1 {
public static void main(String[] args) {
File f = new File("D:/my/HelloJava.java");
FileInputStream fis = null;
try {
// 因为File没有读写的能力,所以需要有个InputStream
fis = new FileInputStream(f);
// 定义一个字节数组
byte[] bytes = new byte[1024];
int n = 0; // 得到实际读取到的字节数
System.out.println("D:\\my\\HelloJava.java文件内容如下:");
// 循环读取
while ((n = fis.read(bytes)) != -1) {
String s = new String(bytes, 0, n); // 将数组中从下标0到n的内容给s
System.out.println(s);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
注意:FileInputStream 类重写了父类 InputStream 中的 read() 方法、skip() 方法、available() 方法和 close() 方法,不支持 mark() 方法和 reset() 方法。
2.6复制原理截图
案例实现
package Java2;
import java.io.*;
public class CopyFileTest1 {
public static void main(String[] args) throws IOException {
//1.定义源文件和目的文件的文件对象
File f1=new File("D:/a.jpg");
File newFile=new File("D:/aa/copy.jpg");
//2.创建目的文件
newFile.createNewFile();
//3.定义输入输出流:使用输入流读取内容 使用输出流写入内容
InputStream in=new FileInputStream(f1);
OutputStream out=new FileOutputStream(newFile);
//练习是容易理解的方式
byte[] buffe=new byte[10];
int n=0;
while (true){
n=in.read(buffe);//读取文件,内容放入buffer数组中,返回的是实际读取的字节数
if (n!=-1){
out.write(buffe,0,n);//把buffer数组从0开始,截取读取到有效字节数n 写入到目的文件中
}else {
break;
}
}
}
}