I/O流
I/O流概述
I/O(input/output)流,输入输出流,可以对数据进行输入输出操作。
I/O流从三个方面分类:
- 字符流和字节流;
- 输入流和输出流;
- 节点流和处理流;
Java中的 I/O 流主要定义咋java.io包下,有四个类 为 流 的顶级类;
分别为:InputStream, OutStream, Reader, Writer ; 前两种为字节流,后两种为字符流。 InputStream,Reader 是输入流,OutputStream,Writer 输入流;
四种类都是抽象类,是所有流的父类;
字节流
计算机中的文本、图片、视频都是以二进制字节形式存在的;
字节类的顶级父类为 InputStream,OutputStream; 所有的字节输入流都继承 InputStream,字节输出流继承 OutputStream;
输入输出是相对于程序来说的,向程序传输数据称为输入流,程序向外传输称为输出流;
InputStream OutputStream 的 常用方法;
// InputStream
int read() // 从输入流读取一个字节,并返回该字节的整数值;没有返回 -1
int read( byte[] b) // 从输入流读取若干字节 , 将他们存入 b 数组 中 , 返回的整数代表读取的数目;
int read( byte[] b, int off,int len)// 将读取的字节放入 b 数组 中, off 表示开始存储字节的下标, len 表示读取字节的数目;
//OutputStream
void write(int b); // 向输出流写入一个字节
void write(byte[] b) // 将 b 数组的所有字节写入输出流
void write(byte[] b, int off, int len) // 将 b 数组中 从 off 开始 输出 len 个字节
字节流读写文件
用来进行对文件的读写;两个类:FileInputStream , FileOutputStream;
FileInputStream:InputStream的子类,用来 操作文件的 字节输入流;
代码举例:
public static void main(String[] args) throws Exception{ //I/O 流必须要 处理异常
FileInputStream in = new FileInputStream("E:\\test.txt"); // 创建 文件字节输入流 in ,文件的 位置 E:\\test.txt
int b;
while ((b =in.read() ) != -1){ // read() 方法为 从输入流 读取一个8位的字节,将它转化为 0-255 之间的整数并返回,没有返回 -1
System.out.println(b);
}
in.close(); // 关闭输入流 并 释放系统资源;
}
FileOutoutStream:OutPutStream的子类,用来 操作文件的 字节输入流;
代码举例:
public static void main(String[] args) throws Exception{
FileOutputStream out = new FileOutputStream("E:\\out.txt"); // 创建的文件为 E:\out.txt
String str = "hello";
out.write(str.getBytes()); // 写入 输出流 , getBytes()方法 返回 str的 字节 数组; write( byte[] b) 将b数组写入流;
out.close(); //关闭输出流
}
注意点:若 写入 的 文件中本来就存在数据, 则会把 文件中的数据情况 在 存入 新的数据;
若想要在 文件中追加 数据,用下面的构造方法 实例化对象:
// FileOutputStream(String fileName , boolean append); 将 append 设置为 true
FileOutputStream out = new FileOutputStream("E:\\out.txt",true);
注意点:一般在 finally 代码块中 关闭 输入输出流,防止因出现异常 导致 输入输出流无法正常关闭;
拷贝文件代码举例:
public static void main(String[] args) throws Exception{
FileInputStream in = new FileInputStream("C:\\Users\\86185\\Pictures\\Saved Pictures\\3.jpg");//创建输入流
FileOutputStream out = new FileOutputStream("E:\\img.jpg"); //创建输出流对象
int len = 0;
long time1 = System.currentTimeMillis();
while ((len = in.read()) != -1) {
out.write(b);
}
long time2 =System.currentTimeMillis();
System.out.println("使用时间" + (time2 - time1)+ "毫秒" );
in.close();
out.close();
}
上述实现的字节流 是 一个字节一个字节进行的拷贝,效率极低 ,为了提高效率,我们需要用到缓冲区,来提高效率;
在拷贝文件时 一次性读取多个字节的数据,保存到字节数组中, 然后将字节数组一次性写入新文件;
byte[] buff = new byte[1024]; //创建一个 字节 数组 来 当作 缓冲区
int len = 0;
while ((len = in.read(buff)) != -1) { // read(byte[] b) 方法 ,从输入流 读 若干字节放入 b(buff) 数组中;
out.write(buff); // 将buff中的字节 写入 文件
}
字节缓冲流
BufferedInputStream, BufferedOunputStream; 带缓冲的字节流;他们的构造函数 接收 InputStream 和 OutputStream 类型的参数作为对象,在读写时提供缓冲功能; 继承 FileInputStream 和 FileOutputStream
public static void main(String[] args) throws Exception{
FileInputStream in = new FileInputStream("C:\\Users\\86185\\Pictures\\Saved Pictures\\3.jpg");//创建输入流
FileOutputStream out = new FileOutputStream("E:\\img.jpg"); //创建输出流对象
BufferedInputStream bis = new BufferedInputStream(in);// 创建字节缓冲输入流对象
BufferedOutputStream bos = new BufferedOutputStream(out);//创建字节缓冲输出流对象
int len = 0;
long time1 = System.currentTimeMillis();
while (( len = bis.read() )!= -1) {
bos.write(len);
}
long time2 =System.currentTimeMillis();
System.out.println("使用时间" + (time2 - time1)+ "毫秒" );
in.close();
out.close();
}
两个字节缓冲流内部都定义了一个大小为 8192的 byte[] ,当调用 read() 和 write() 方法时 ,会 先将数据 存入到定义好的字节数组,然后将数组一次性读写入文件;
字符流
顶级父类: Reader 和 Writer 字符流;用来 读写 字符;
字符流操作文件
FileReader 和 FileWriter 分别为 文件输入流 和 文件输入流;
这两个类的使用方法和 之前的 FileInputStream 和 FileOutputStream 基本一致;
也可以使用 一个 字符数组 实现 缓冲 ,用来拷贝文件;
字符缓冲流
BufferedReader 和 BufferedWrite 字符缓冲流;
一个重要的方法 readLine() 一次读取一行文本;
public static void main(String[] args) throws Exception {
FileReader reader = new FileReader("reader.txt"); //文件
FileWriter writer = new FileWriter("writer.txt");
BufferedReader br = new BufferedReader(reader); //字符缓冲输入流对象
BufferedWriter bw = new BufferedWriter(writer); //字符缓冲输出流对象
String str;
while ((str = br.readLine()) != null){ //readLine 用来读取一行,
bw.write(str); //
bw.newLine(); // 每写入一行之后 进行换行。
}
br.close();
bw.close();
}
转换流
Java中提供两个类实现 字节流转化为字符流; 分别为 InputStreamReader (Reader 的一个子类) 和 OutputStreamWriter (Writer的一个子类), 前者是 将字节输入流转化为字符输入流,后者是 将 字节输出流转化为 字符输出流;
public static void main(String[] args) throws Exception {
FileInputStream in = new FileInputStream("reader.txt"); //文件字节输入流
InputStreamReader is = new InputStreamReader(in); // 将字节输入流转化为 字符输入流
BufferedReader br = new BufferedReader(is); // 创建字符流缓冲对象
FileOutputStream out = new FileOutputStream("writer2.txt"); // 创建字节 输出流
OutputStreamWriter os = new OutputStreamWriter(out); //将字节输出流 转化为 字符输出流
BufferedWriter bw = new BufferedWriter(os); //创建 字符缓冲流对象;
}
注意点: 使用转化流时 ,只能将 文本文件的 字节流转化为字符流,若是图片视频,转换会照成数据丢失。
File类
I/O流可以操作文件内部数据,但是无法对整个个文件进行操作,File类可以就整个文件进行操作;
File类的常用方法举例:
public static void main(String[] args) throws Exception {
File file = new File("test\\writer2.txt"); //创建一个文件对象; 参数为文件的 相对 或者 绝对路径
file.getName(); //得到文件名称
file.getPath(); //得到文件的相对路径
file.getAbsolutePath(); //得到文件的绝对路径
file.getParent(); //得到文件的父路径
file.canRead(); //文件可读?true:false
file.canWrite(); //文件可写? true:false
file.isFile(); //是否为文件?true:false
file.isDirectory(); //是非为目录?true:false
file.isAbsolute(); //是否为绝对路径?true:false
file.lastModified(); //文件的最后修改时间;
file.length(); //文件的大小为多少字节
file.delete(); //是否成功删除文件
}
File类中 有一个 遍历目录中所有文件名称 的 list() 方法,
File file = new File("test"); //创建一个文件或者目录对象;
if(file.isDirectory()) { //判断是否为目录
0. String[] fileNames = file.list(); // list() 返回一个 String数组
// list()的一个重载方法,过滤文件;
1. String[] fileNames = file.list( (dir , name)-> name.endsWith(".txt") ) // 选择以 .txt 结尾的文件;
for (String name : fileNames){ //遍历数组
System.out.println(name);
}
}
listFile() 方法 , 返回 值 是一个 File对象数组,它一般用来 遍历目录中还存在目录的情况;
public static void main(String[] args) throws Exception {
File file = new File("test"); //创建一个file 对象;
fileDir(file);
}
public static void fileDir(File file){
File[] listFiles = file.listFiles(); //调用 listFiles()方法 ,返回一个File对象数组;
for (File f : listFiles) { // 遍历 File 对象 数组
if(f.isDirectory()){ // 判断 元素 是否为 目录。是 的 话 递归方法;
fileDir(f); // 递归调用
}
System.out.println(f.getAbsolutePath()); //输出文件绝对路径
}
}
delete()方法,使用该方法删除 文件夹时,需要判断 文件夹中是否有内容,若没有 才可以删除;有内容,必须先把文件夹清空才可以删除;需要用到上面遍历文件夹中含有文件夹的方法;
注意,删除时直接删除,不会存到回收站。
随机存取文件
RandomAccessFile类,可以从文件的任意位置对文件内容进行操作;不同于之前的I/O流,只能从开始操作,它不属于流类;
两种构造方法:
RandomAccessFile(File file ,String mode); //使用 File 指定被访问文件 mode 为 r读 rw只读 和 rws rwd
RandomAccessFile(String name,String mode); //使用name 指定 被访问文件的路径 mode为访问模式 四种 r rw rws rwd
常用方法举例:
long getFilePointer() // 返回当前读写指针所处的位置;
void seek(long pos) //设置指针的位置 距离 文件开发 pos个字节数
int skipBytes(int n) // 从当前指针开始,跳过n个字节
void write(byte[] b) // 将 b 字节数组 写入文件,从当前指针开始
final String readLine() // 从 当前指针读取下一行内容
对象序列化
对象的序列化 Serializable : 将一个 Java 对象转化成 一个 I/O流中的字节序列的过程; 这是 为了 可以 将对象保存到磁盘或者 在网络中传输Java对象;
反序列化 Deserialize : 将一个I/O流中的字节序列 转化为 一个Java对象的过程;
想让一个类称为可序列化的 必须 实现 Serializable 或者 Externalizable 接口
Serializable 系统自动存储必要信息 容易实现 只需要实现接口即可 性能较差;
Externalize 程序员决定所存储的信息 ;
实际开发中经常使用 Serializable接口
只要给一个类 实现 Serializable 接口 ,该类即可序列化
public static Person implements Serializable {
private static final long serializableVersionUID = 1L; // 表示序列化版本 ,不显示定义JVM会计算出一个serialVersionUID变量值
}
NIO
NIO(new I/O),为了替代传统的IO而出现, 它采用 内存映射的方式来处理输入和输出 ,将文件 或者 文件的一段 映射到内存中,这样就可以像访问内存一样来访问内存了;
标准I/O,使用字节流和字符流,在 NIO中,使用的是 通道(Channel) 和 缓冲区(Buffer)。数据从通道读入缓冲区,或从缓冲区写入通道;
NIO的三大核心部分:Buffer , Channel , Selector;
- Buffer: 一个容器,本质是一个数组缓冲区 , 读入和写出到 Channel中的所有对象会先放在 Buffer 中;
- Channel: 所有数据通过通道传输;
- Selector : 选择器 , 用于监听多个通道事件,主要用于多线程;
Buffer
缓冲器:
buffer类似于一个数组,可以保存多个相同的类型的数组。
是一个抽象类,子类有 ByteBuffer, CharBuffer ,DoubuleBuffer, FloatBuffer ,IntBuffer ,LongBuffer ,ShortBuffer ;
buffer的子类没有构造方法,通过一个方法 static XxxBuffer allocate(int capacity) 来 创建 对象;
CharBuffer charBuffer = CharBuffer.allocate(6); // 创建一个 容量为 6 的 CharBuffer 对象
capacity (容量) ; limit(界限) ; position (位置)
- capacity: 缓冲区能存入数据的最大容量,能存入多少数据;不能为 负, 不能改变;
- limit : 表示 缓冲区不可读取区域的第一个索引, 0 – limit 区域的索引都能读取。之后的不可读取操作; 不能为 负, 不大于 capacity
- position: 指定下一个被读取的区域索引; 新创建的 buffer position 为 0; 没执行一次自动加1;
Buffer 类中 一些方法距离:
CharBuffer charBuffer = CharBuffer.allocate(6); // 创建一个 容量为 6 的 CharBuffer 对象
charBuffer.capacity(); // 返回缓冲区的容量
charBuffer.limit(); //返回缓冲区的界限值
charBuffer.position(); //返回缓冲区的位置
charBuffer.put('x'); //像缓冲区内存入一个元素, 此时 position + 1
charBuffer.flip(); //反转缓冲区,先将 limit 和 position 值互换,然后 position设置为0;
charBuffer.get(); //取出 position 位置元素 , 取出后 position + 1 ; 若 带有索引,position不改变
charBuffer.clear(); //清除缓冲区 position设置为0 limit 设置为 capacity , 缓冲区 元素 还存在
Channel
通道:
Channel 是 一个接口对象 ;
实现类: FileChannel 用于从文件中读写数据;
DatagramChannel 用于支持 UDP网络通信;
Pipe.SinkChannel 和 Pipe.SoureceChannel 用于支持线程之间的通信;
ServerSocketChannel 和 SocketChannel 用于 支持 TCP网络通信;
Channel 对象 通过 传统I/O的 getChannel 方法 来获得对应 的 Channel。
FileChannel 的一些方法:
public static void main(String[] args) throws Exception{
RandomAccessFile infile = new RandomAccessFile("test\\time.txt", "rw");//创建一个随机存取文件对象,指定源文件
FileChannel inChannel = infile.getChannel(); // 获取 RandomAccessFile 对象 对应的 通道 对象;
RandomAccessFile outfile = new RandomAccessFile("test\\time1.txt","rw"); //创建对象,指定目标文件
FileChannel outChannel = outfile.getChannel(); // 获取 复制 目标文件的通道
long transferTo = inChannel.transferTo(0, inChannel.size(), outChannel);// transferTo()方法\
// long transferTO (long position , long count , WritableByteChannel target) 读取通道给定 位置 和大小,并写入目标通道
// long size() 方法 返回 通道的大小
if (transferTo > 0){
System.out.println(复制成功);
}
infile.close();
inChannel.close();
outChannel.close();
outfile.close();
}