IO流学习
I/O(input/output)流,即输入输出流。
流的分类
1、根据操作的数据单位的不同,分为字节流、字符流。
2、根据流传输的方向不同,分为输入流、输出流。
输入流(读取):将文件内容读取到java程序的内存进行存储;
输出流(写出):java程序将文件内容写出到其他文件。
3、根据流功能的不同,分为节点流、处理流。

字节流(常用)
在计算机中的文件都是以二进制(字节)形式存储的,IO流中针对字节的输入输出提供了一系列的流,统称为字节流。
测试一下:windows下创建一个txt文件,右键属性,大小是0字节,输入一个英文是大小是1字节,然后在追加一个汉字,再看大小为3字节。
JDK中,所有的字节输入流都继承自InputStream,所有字节输出流都继承自OutputStream
InputStream 和 OutputStream 都可以看成一个管道,实现数据传输的介质。


字节流读写文件
文件输出入输出流
read() 从输入流读取一个8位的字节,把它转换为0~255之间的整数,并返回这一整数。当没有可用字节时,将返回-1
close() 闭释放资源,占用系统资源
write(int b) 向输出流写入一个字节
FileOutputStream输出流,向已存在文件写入数据,文件首先被清空。加入想追加,不请清空,可调用另一个可追加的构造。
复制Javapackage com.test.io;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Arrays;
/**
* @Desc -累行客
* @Author luolei
* @Web http://www.leixing.xyz/
* @Date 2020/05/21 10:08
* <p>
* 字节流
* 文件输出入输出流
* read() 从输入流读取一个8位的字节,把它转换为0~255之间的整数,并返回这一整数。当没有可用字节时,将返回-1
* close() 闭释放资源,占用系统资源
* write(int b) 向输出流写入一个字节
* FileOutputStream输出流,向已存在文件写入数据,文件首先被清空。加入想追加,不请清空,可调用另一个可追加的构造。
*/
public class FileStreamTest {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("F:\\file_test\\test.txt");
FileOutputStream fos = new FileOutputStream("F:\\file_test\\test2.txt", true); /*可追加的构造*/
int i = fis.read();
System.out.println((char) i);
fos.write(i);
byte[] bytes = "字符串".getBytes();
System.out.println(Arrays.toString(bytes));
fos.write(bytes);
while ((i = fis.read()) != -1) { /*获取不到返回-1*/
System.out.println((char) i);
fos.write(i);
}
/* 关闭释放资源*/
fis.close();
fos.close();
}
}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
字节流的缓冲区
字节流的缓冲区
复制Javapackage com.test.io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* @Desc -累行客
* @Author luolei
* @Web http://www.leixing.xyz/
* @Date 2020/05/21 10:58
*
* 字节流的缓冲区
* public int read(byte b[]); FileInputStream传入一个字节数组,读取的内容写入数组,并返回长度
*
*/
public class BufferedCopyTest {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
/*文件输入流(字节流)*/
FileInputStream fis = new FileInputStream("F:\\file_test\\test.txt");
/*文件输出流(字节流)*/
FileOutputStream fos = new FileOutputStream("F:\\file_test\\test2.txt");
byte[] buff = new byte[1024];
int len =0;
while((len = fis.read(buff)) != -1){
fos.write(buff, 0, len);
}
fis.close();
fos.close();
long end = System.currentTimeMillis();
System.out.println("字节缓冲区读写文件用时:"+(end-start));
}
}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
字节缓冲流
字节缓冲流
与字节流缓冲区类似,字节缓冲流更快。应用程序是通过缓冲流来完成数据读写的,缓冲流是通过底层的字节流与设备进行关联。
BufferedInputStream和BufferedOutputStream两个流内部都创建了一个8192的字节数组,当调用read或write()时,
首先将读写的数据存入到定义的数组中,然后将字节数组中的数据一次性读写到文件中。这个与字节流缓冲区类似,对比更高效。

复制Javapackage com.test.io;
import java.io.*;
/**
* @Desc -累行客
* @Author luolei
* @Web http://www.leixing.xyz/
* @Date 2020/05/21 16:40
*
* 字节缓冲流
* InputStream OutputStream
* FileInputStream FileOutputStream
* BufferedInputStream BufferedOutputStream
*/
public class BufferedStreamTest {
/**
* 字节缓冲流
* 与字节流缓冲区类似,字节缓冲流更快。应用程序是通过缓冲流来完成数据读写的,缓冲流是通过底层的字节流与设备进行关联。
* BufferedInputStream和BufferedOutputStream两个流内部都创建了一个8192的字节数组,当调用read或write()时,
* 首先将读写的数据存入到定义的数组中,然后将字节数组中的数据一次性读写到文件中。这个与字节流缓冲区类似,对比更高效。
* @throws IOException
*/
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
/*文件输入流(字节流)*/
FileInputStream fis = new FileInputStream("F:\\file_test\\test.txt");
/*输入字节缓冲流*/
BufferedInputStream bis = new BufferedInputStream(fis);
/*文件输出流(字节流)*/
FileOutputStream fos = new FileOutputStream("F:\\file_test\\test2.txt");
/*输出字节缓冲流*/
BufferedOutputStream bos = new BufferedOutputStream(fos);
int len =0;
while((len = bis.read()) != -1){
bos.write(len);
}
fis.close();
fos.close();
long end = System.currentTimeMillis();
System.out.println("字节缓冲流读写文件用时:"+(end-start));
}
}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
其它字节流
InputStream(抽象类:表示所有字节输入流的父类)
|-FileInputStream(主要用于文件之类的原始字节流)
|-FilterInputStream(简单的重写InputStream方法)
|-BufferedInputStream(提供缓冲等功能)
|-PipedInputStream(主要用于多线程)
OutputStream(抽象类:表示所有字节输出流的父类)
|-FileOutputStream(主要用于文件之类的原始字节流)
|-FilterOutputStream(简单的重写OutputStream方法)
|-BufferedOutputStream(提供缓冲等功能)
|-PrintStream(打印各种数据值得表示形式)
|-PipedOutputStream(主要用于多线程)
字节数组输入输出流
ByteArrayInputStream
从缓冲区读取数据
ByteArrayOutputStream
在创建对象时创建byte型数组的缓冲区
当向数组中写数据时,该对象会把所有的数据先写入缓冲区,最后一次性写入文件
合并流
将几个输入流串联在一起合并为一个输入流,依次从所有被串联的输入流中读取数据。
SequenceInputStream
格式输入输出流
用于多线程之间数据的传输,必须先建立连接才能彼此间通信
DataInputStream
DataOutputStream
管道流
用于多线程之间数据的传输,必须先建立连接才能彼此间通信
PipedOutputStream:向管道中写入数据
PipedInputStream:从管道中读取数据
打印流 PrintStream
PrintStream:打印流,将基本数据类型的数据或引用数据类型的对象格式化成字符串后再输出
标准输入输出流
System.in:标准输入流 InputStream类型,用于读取键盘输入的数据
常使用包装类Scanner
System.out:标准输出流,PrintStream类型,将数据输出到命令行窗口
System.err:PrintStream类型,将应用程序运行时的错误信息输出到控制台
字符流
对于字节流能够操作所有文件,但是操作中文的话会出现乱码。所以有了字符流。
字符流有两个抽象的顶级父类
Reader字符输入流,从源设备读取字符
Writer字符输出流,向目标设备写字符
字符流操作文件
字符流
作用:操作文本文件的时候,可以解决中文乱码问题。
字符流只能操作纯文本文件,不能出来图片、音频等文件。
原理:字符流(字节码+编码表)读取
GBK -> 一个中文占2个字节
UTF-8 -> 一个中文占3个字节
根据编码表,决定一次读取几个字节,并且在内存中,转换成字符。
字节流底层会判断,如果读取到的是中文字符,一次读取3个字节(UTF-8),如果读取到的是非中文,一次读取1个字节。
怎么判断的? 中文字符一般都是负数的字节(部分中文第一个字节为负数,其他为正数),非中文的都是正数的。
文件字符流操作文件FileReader 和FileWriter
文件字符输入流 FileReader
文件字符输出流 FileWriter
读取txt文件乱码了?不是字符流就是解决乱码的吗?
不要以为字符流读取文本文档有中文,就不会有任何问题,如果你写(存)的编码和取(读)的编码不一致的话,也会造成乱码的现象。
你存的时候用的UTF-8,你读的时候必须也用UTF-8。
文本文档txt的默认编码从ANSI,流的默认读取编码是UTF8
复制Javapackage com.test.io;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
/**
* @Desc -累行客
* @Author luolei
* @Web http://www.leixing.xyz/
* @Date 2020/05/21 14:44
*
* 字符流
* 作用:操作文本文件的时候,可以解决中文乱码问题。
*
* 原理:字符流(字节码+编码表)读取
* GBK -> 一个中文占2个字节
* UTF-8 -> 一个中文占3个字节
*
* 根据编码表,决定一次读取几个字节,并且在内存中,转换成字符。
*
* 字节流底层会判断,如果读取到的是中文字符,一次读取3个字节(UTF-8),如果读取到的是非中文,一次读取1个字节。
* 怎么判断的? 中文字符一般都是负数的字节(部分中文第一个字节为负数,其他为正数),非中文的都是正数的。
*
*/
public class FileReaderTest {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("F:\\file_test\\test.txt");
FileWriter fw = new FileWriter("F:\\file_test\\test2.txt");
int i = fr.read();
System.out.println((char)i);
System.out.println(fr.getEncoding());
/**
* 读取txt文件乱码了?不是字符流就是解决乱码的吗?
* 不要以为字符流读取文本文档有中文,就不会有任何问题,如果你写(存)的编码和取(读)的编码不一致的话,也会造成乱码的现象。
* 你存的时候用的UTF-8,你读的时候必须也用UTF-8。
* 文本文档txt的默认编码从ANSI,,流的默认读取编码是UTF8
*/
char[] buff = new char[1024];
while ((i = fr.read(buff)) != -1){
fw.write(buff, 0, i);
}
fr.close();
fw.close();
/*中文字符一般都是负数的字节(部分中文第一个字节为负数,其他为正数),非中文的都是正数的。*/
System.out.println(Arrays.toString("测试一下".getBytes()));
System.out.println(Arrays.toString("abc123".getBytes()));
}
}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
字符缓冲流操作文件BufferedReader和BufferedWriter
字符缓冲流
复制Javapackage com.test.io;
import java.io.*;
/**
* @Desc -累行客
* @Author luolei
* @Web http://www.leixing.xyz/
* @Date 2020/05/21 15:44
*
* 字符缓冲流
* BufferedReader
* BufferedWriter
*
* 特殊方法
* String readLine(); 读取一行字符,返回读取的字符
* void newLine(); 输出换行符,具有跨平台性
*/
public class BufferedReaderTest {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("F:\\file_test\\test.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("F:\\file_test\\test2.txt"));
String str = null;
while ((str = br.readLine()) != null){
bw.write(str);
bw.newLine();
}
/*刷新此输出流并强制写出所有缓冲区的的输出字节*/
bw.flush();
br.close();
bw.close();
}
}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
LineNumberReader类
LineNumberReader类
行编号读写器
Java程序在编译或运行时会出现一些错误,在错误中通常会报告出错的行号,需要在代码中跟踪行号
JDK中提供了可以跟踪行号的输入流LineNumberReader是BufferedReader的直接子类,实现在拷贝一个文件时为文件加上行号
复制Javapackage com.test.io;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
/**
* @Desc -累行客
* @Author luolei
* @Web http://www.leixing.xyz/
* @Date 2020/05/21 15:44
*
* 字符缓冲流
* BufferedReader
* ---> LineNumberReader
*
* 方法
* int getLineNumber()
* 获取当前的行数。
* void mark(int readAheadLimit)
* 标记流中的当前位置。
* int read()
* 读一个字符。
* int read(char[] cbuf, int off, int len)
* 将字符读入一个数组的一部分。
* String readLine()
* 读一行文本。
* void reset()
* 重置流到最近的标记。
* void setLineNumber(int lineNumber)
* 设置当前行数。单并不能将指针跳到这样一行
* long skip(long n)
* 跳过的字符。
*
*/
public class LineNumberReaderTest {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("F:\\file_test\\test.txt"));
LineNumberReader ln = new LineNumberReader(br);
System.out.println(ln.getLineNumber());
System.out.println(ln.readLine());
ln.setLineNumber(3);
System.out.println(ln.getLineNumber());
ln.mark(1);
System.out.println(ln.readLine());
ln.skip(3);
System.out.println(ln.readLine());
ln.reset();
System.out.println(ln.readLine());
ln.close();
}
}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
转换流InputStreamReader和OutputStreamWriter
为什么要有转换流?
字符流有默认编码,假如和文件的编码不一致,就会出现乱码,要么控制文件的编码,要么流转换成文件的编码。
转换流也是一种包装流
OutputStreamWriter
InputStreamReader
InputStreamReader(InputStream in)
创建一个inputstreamreader使用默认字符集。
InputStreamReader(InputStream in, Charset cs)
创建一个inputstreamreader使用给定的字符集。
InputStreamReader(InputStream in, CharsetDecoder dec)
创建一个inputstreamreader使用给定的字符集解码。
InputStreamReader(InputStream in, String charsetName)
创建一个inputstreamreader使用指定的字符集。
转换流功能:
将字节输入流包装成字符输入流(按照指定编码表),方便读取字符,然后字符输出流被转换成字节输出流,写到目标。
BufferedReader和BufferedWriter对转换流进行包装

复制Javapackage com.test.io;
import java.io.*;
/**
* @Desc -累行客
* @Author luolei
* @Web http://www.leixing.xyz/
* @Date 2020/05/21 16:04
*
* 转换流
* 转换流也是一种包装流
* InputStreamReader
* OutputStreamWriter
*
* 为什么要有转换流?
* 字符流有默认编码,假如和文件的编码不一致,读取就会出现乱码,要么控制文件的编码,要么流转换成文件的编码。
*
* 转换流功能:
* 将字节输入流包装成字符输入流(按照平台指定编码表),方便读取字符,然后字符输出流被转换成字节输出流,写到目标。
*
* InputStreamReader(InputStream in)
* InputStreamReader(InputStream in, String charsetName)
* BufferedReader(Reader in)
*
*/
public class InputStreamReaderTest {
public static void main(String[] args) throws IOException {
InputStreamReader isr = new InputStreamReader(new FileInputStream("F:\\file_test\\test.txt"), "utf-8");
OutputStreamWriter osw =new OutputStreamWriter(new FileOutputStream("F:\\file_test\\test2.txt"), "utf-8");
/* 结合字符缓冲流*/
BufferedReader br = new BufferedReader(isr);
System.out.println(br.readLine());
osw.write("测试一下");
br.close();
osw.close();
}
}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
File类
File类,将一个路径封装成对象,便于操作。该路径可以是从系统盘符开始的绝对路径,也可以是相对于当前目录而言的相对路径。可以指向一个文件,也可以指向一个目录。
遍历目录下的文件
list()方法:遍历指定目录下所有文件名
重载list(FilenameFilter filter)方法,
得到指定类型的文件,接收一个FilenameFilter类型的参数
有抽象方法accept(File dir,String name)
在调用list()方法时,需要实现文件过滤器,在accept() 中做出判断,获取指定类型的文件
使用listFiles()方法,
得到所有子目录下的File类型对象,该方法返回一个File对象数组
删除文件及目录
delete()方法
删除一个指定的文件
删除指定的空目录
将非空目录进行删除
删除目录下的所有文件和子目录
使用递归实现
综合demo
复制Javapackage com.test.io;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.Arrays;
/**
* @Desc -累行客
* @Author luolei
* @Web http://www.leixing.xyz/
* @Date 2020/05/21 17:40
*
* File类
* File类,将一个路径封装成对象,便于操作。
* I/O流可以对文件内容进行读写,单不能创建、删除或重命名文件、判断硬盘上某个文件是都存在。针对文件这类操作,JDK提供了File类。
*/
public class FileClassTest {
static String BasePath= "F:\\file_test";
public static void main(String[] args) throws IOException {
File file = null;
//file = new File(BasePath+"test.txt"); //如果不存在,并未创建resource2.txt文件
file = new File(BasePath,"test.txt");
if(!file.exists())
file.createNewFile();
printFileInfo(file);
//遍历目录下的文件,过滤txt文件
eachFileList();
//目录下还有子目录使用listFiles()+递归
File file2 = new File(BasePath);
eachListFiles(file2);
}
/**
* 打印文件信息
* @param file
*/
public static void printFileInfo(File file){
System.out.println(".............................");
System.out.println("getName="+file.getName());
System.out.println("getAbsolutePath="+file.getAbsolutePath());
System.out.println("length="+file.length());
System.out.println("getParent="+file.getParent());
System.out.println("getParentFile="+file.getParentFile());
System.out.println("isFile="+file.isFile());
System.out.println("isDirectory="+file.isDirectory());
System.out.println(".............................");
}
/**
* 目录下还有子目录使用listFiles()+递归
* 删除类似,但注意:删除操作是通过jvm直接删除的,不可恢复。
*/
private static void eachListFiles(File file) {
//是否是目录
if(file.isDirectory()){
System.out.println("is a Directory.");
File[] listFiles = file.listFiles();
for (File files: listFiles) {
if(file.isDirectory()){
eachListFiles(files);
}
//输出文件路径
System.out.println(files.getAbsolutePath());
//删除操作
//files.delete();
}
}else{
//System.out.println("is not a Directory.");
}
}
/**
* 遍历目录下的文件,过滤txt文件
*/
private static void eachFileList() {
File file = new File(BasePath);
//是否是目录
if(file.isDirectory()){
System.out.println("is a Directory.");
String[] list = file.list();
//文件过滤器FilenameFilter
FilenameFilter filter = new FilenameFilter() {
@Override
public boolean accept(File file, String s) {
if(s.endsWith("txt"))
return true;
return false;
}
};
filter.accept(file, ".txt");
String[] list2 = file.list(filter);
//String[] list2 = file.list((dir, name)-> name.endsWith(".txt"));
System.out.println(Arrays.toString(list2));
System.out.println(".............................");
File[] files = file.listFiles(filter);
for (int i = 0; i < files.length; i++) {
System.out.println(files[i].getAbsolutePath());
}
System.out.println(".............................");
}else{
System.out.println("is not a Directory.");
}
}
}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
RandomAccessFile
特点:具有读写文件数据的功能,可以随机从文件任何位置开始执行读写操作,可以将文件以只读或者读写等权限的方式操作。和C语言类似。
原理:包含一个记录指针来标识当前读写处的位置;
新建对象时,文件记录指针会在文件开始处,读写n个字节后,记录指针向后移动n个字节,可以自由的移动记录指针。

参数mode用于指定访问文件的模式,也就是文件的操作权限。参数mode取值:
r:以只读的方式打开文件,若执行写操作,会报IOException异常。
rw:以“读写”的方式打开文件,如果文件不存在,会自动创建该文件。
rws:以“读写”方式打开文件,要求对文件的内容或元数据的每个更新都同步写入到底层的存储设备。
rwd:以“读写”方式打开文件,要求对文件的内容的每个更新都同步写入到底层的存储设备。

更多实例:https://www.cnblogs.com/lijianli/p/9680265.html
复制Javapackage com.test.io;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* @Desc -累行客
* @Author luolei
* @Web http://www.leixing.xyz/
* @Date 2020/05/21 17:55
*
* RandomAccessFile
* 这个类的实例支持对随机存取文件的读和写。直接父类是Object
*
* RandomAccessFile 默认有尾部追加的功能
*
* long getFilePointer() 返回当前读写指针所处的位置
*
* void seek(long pos) 设定读写指针的位置,与文件开头相隔pos个字节数
*
* int skipBytes(int n) 使读写指针从当前位置开始,跳过n个字节
*
* void write(byte[] b) 将指定的字节数组写入到这个文件,并从当前文件指针开始
*
* void setLength(long newLength)设置此文件的长度
*
* final String readLine()从指定文件当前指针读取下一行内容
*/
public class RandomAccessFileTest {
public static void main(String[] args) throws IOException {
RandomAccessFile raf = new RandomAccessFile("F:\\file_test\\test.txt", "rw");
int count = Integer.parseInt(raf.readLine()) -1;
System.out.println(raf.getFilePointer());
if(count > 0){
System.out.println("你还有"+count+"次使用机会");
raf.seek(0);
raf.write((count+"").getBytes());
}else{
System.out.println("你的使用次数已完");
}
raf.close();
}
}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
对象序列化
对象序列化:Serializable
为了将对象保存到磁盘中,或允许在网络中直接传输对象,将一个Java对象转换成一个I/O流中字节序列的过程
Java对象转换成与平台无关的二进制流
对象反序列化:Deserialize,二进制流恢复成原来的Java对象
复制Javapackage com.test.io;
import java.io.*;
/**
* @Desc -累行客
* @Author luolei
* @Web http://www.leixing.xyz/
* @Date 2020/05/21 18:/10
*
* 序列化Serializable
* 对象序列化:Serializable
* 为了将对象保存到磁盘中,或允许在网络中直接传输对象,将一个Java对象转换成一个I/O流中字节序列的过程
* Java对象转换成与平台无关的二进制流
* 对象反序列化:Deserialize,二进制流恢复成原来的Java对象
*/
public class SerializableTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("F:\\file_test\\test.txt"));
/*ObjectOutputStream 对象输出流,将 Person对象存储到磁盘的文件中,完成对Person对象的序列化操作*/
oos.writeObject(new Person("001", "luo", 22));
oos.writeObject(new Person("002", "xing", 21));
oos.writeObject(new Person("003", "leixing", 22));
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F:\\file_test\\test.txt"));
/*输出三个对象*/
for(int i=0;i<3;i++){
/*ObjectInputStream 对象输入流,完成对Person对象的序列化操作*/
Person pp = (Person) ois.readObject();
System.out.println(pp);
}
ois.close();
}
}
class Person implements Serializable{
/**
* 序列化ID
*/
private static final long serialVersionUID = -5809782578272943999L;
String id;
String name;
int age;
public Person(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"id='" + id + '\'' +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
NIO(New I/O)
从JDK1.4开始,java提供了一系列用于处理输入输出的新功能,这些新的功能称为NIO(New I/O)。
NIO主要是为了提高效率。
NIO采用内存映射文件的按时来处理输入/输出。他将文件文件一段区域映射到内存,这样就可以像访问内存一样来访问文件。
在NIO中使用的是Channel(通道)和Buffer(缓冲器)。数据总是从通道读入缓冲器,或者从缓冲器写入通道。
================
NIO相关包:
java.nio :主要包含各种与Buffer相关的类。
java.nio.channels:主要包含与Channel和Selector (多线程相关选择器)相关的类。
java.nio.channels.spi :主要包含与Channel相关的服务提供者编程接口。0java.nio.charset :主要包含与字符集相关的类。
java.nio.charset.spi :主要包含与字符集相关的服务提供者编程接口。
================
NIO概述三大核心
Buffer (缓冲器)
Buffer本质是一个数组 缓冲区,读取或写出到Channel中的所有对象都会先放在Buffer中。
Channel (通道)
Channel是对传统的输入/输出流的模拟,在NIO中, 所有的数据都需要通过通道流的形式传输。
Channel通道本身不能完成运输。传输必须依赖于缓冲区,缓冲区是双向的。
Selecter(选择器)
Selecter用于监听多个通道的事件(例如:连接打开、数据到达等),主要用于多线程处理。
Buffer (缓冲器)
NIO中的Buffer用于和Channel进行交互配合,交互时,数据会从Channel读取到Buffer中,或者从Buffer写到Channel中,Channel通道本身不能完成运输。传输必须依赖于Buffer缓冲区,缓冲区是双向的。
Buffer类似于一个数组,它可以保存多个类型相同的数据。
Buffer是一个抽象类,其子类有CharBuffer、ByteBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBufer. 、DoubleBuffer(八大基本数据类型除了Boolean类型没有其他都有)
Buffer子类中最常用的是ByteBuffer和CharBuffer
Buffer类的子类中并没有提供构造方法,因此不能通过构造方法来创建对象。
创建Buffer对象,通常会通过子类中的
XxxBuffer 的 static allocate(int capacity)
方法来实现(其中Xxx表示不同的数据类型,而capacity表示容量)
示例(创建一个容量为10的CharBuffer对象):
CharBuffer buffer = CharBuffer.allocate(10);
所有方法
-
Modifier and Type
Method and Description
abstract Objectarray()返回数组,支持这个缓冲区 (可选操作)。abstract intarrayOffset()返回在缓冲 第一元这个缓冲区支持数组的偏移(可选操作)。intcapacity()返回此缓冲区的容量。clear()清除此缓冲区。flip()翻转这个缓冲区。abstract booleanhasArray()告诉是否这个缓冲区是由一个可访问的数组支持的。booleanhasRemaining()告诉当前位置和极限之间是否有任何元素。abstract booleanabstract booleanisReadOnly()告诉是否该缓冲区是只读的。intlimit()返回此缓冲区的限制。limit(int newLimit)设置此缓冲区的限制。mark()设置此缓冲区的标记位置。intposition()返回此缓冲区的位置。position(int newPosition)设置此缓冲区的位置。intremaining()返回当前位置和极限之间的元素的数目。reset()重置此缓冲区的位置之前标记的位置。rewind()将此缓冲区。
Buffer缓冲器demo
复制Javapackage com.test.nio;
import java.nio.Buffer;
import java.nio.ByteBuffer;
/**
* @Desc -累行客
* @Author luolei
* @Web http://www.leixing.xyz/
* @Date 2020/05/21 20:55
*
* Buffer缓冲器
* NIO中的Buffer用于和Channel进行交互配合,完成数据的传输。
*
* Buffer是一个抽象类,其子类有CharBuffer、ByteBuffer、ShortBuffer、IntBuffer、LongBuffer、FloatBufer. 、DoubleBuffer(八大基本数据类型除了Boolean类型没有其他都有)
*
* Buffer子类中最常用的是ByteBuffer和CharBuffer
*
* Buffer类的子类中并没有提供构造方法,因此不能通过构造方法来创建对象。
*
* 创建Buffer对象,通常会通过子类中的allocate方法去创建
* capacity() 容量
* limit() 可以操作哪个索引(界限)
* position() 当前准备操作的索引
* mark() 标记,标记当前position的值
* reset() 如果position的值发生变化了,通道会随着reset返回到mark那个位置。
*
* flip() 翻转这个缓冲区。可切换读写模式
*
*/
public class NewBufferTest {
public static void main(String[] args) {
int capacity = 1024;
ByteBuffer buff = ByteBuffer.allocate(capacity);
print(buff);
/* 向缓冲区写数据*/
buff.put("abcde".getBytes());
print(buff);
buff.mark();
buff.put("fg".getBytes());
print(buff);
buff.reset();
print(buff);
/* 切换读写模式*/
buff.flip();
print(buff);
/* 获取数据*/
byte[] bytes = new byte[2];
buff.get(bytes);
System.out.println("获取数据:"+new String(bytes));
print(buff);
buff.get(bytes);
System.out.println("获取数据:"+new String(bytes));
print(buff);
/*java.nio.BufferUnderflowException*/
buff.get(bytes);
System.out.println("获取数据:"+new String(bytes));
print(buff);
}
static void print(Buffer buff){
System.out.println("===================");
System.out.println("容量:"+buff.capacity());
System.out.println("界限:"+buff.limit());
System.out.println("索引:"+buff.position());
}
}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
Channel (通道)
Channel可以异步的执行IO读写操作。Channel的读写操作是双向的,既可以从Channel中读取数据,又可以写数据到Channel,而流的读写操作通常都是单向的。(读和写各有一个Channel各司其职,他们方向是单向的)
Channe可以直接将指定文件的部分或者全部直接映射成Buffer
Channel只能与Buffer进行交互,程序不能直接读写Channel中的数据。
Channel接口的实现类主要包括DatagramChannel、 FileChannel、Pipe.SinkChannel、Pipe.SourceChannel、ServelrSocketChannel、SocketChannel等。
DatagramChannel用于支持UDP网络通信;
FileChannel用于从文件中读写数据;
Pipe.SinkChannel和Pipe. SourceChanne|用于支持线程之间的通信;
ServerSocketChannel和SocketChannel用于支持TCP网络通信。
使用:
Channel对象并不是通过构造方法来创建的,而是通过传统IO的getChannel()方法来获取对应的Channel。
不同的流所获取的Channel是不同的,例如FilelnputStream和FileOutputStream获取的是FileChannel,同时还可以使用RandomAccessFile获取该对象。
PipedInputStream和PipedOutputStream所获得的是Pipe.SinkChannel和Pipe.SourceChannel。
常用方法


复制Javapackage com.test.nio;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* @Desc -累行客
* @Author luolei
* @Web http://www.leixing.xyz/
* @Date 2020/05/21 21:38
*
* 通道Channel
* 通过NIO完成文的读写
*/
public class ChannelTest {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis();
/* 输入输出流*/
FileInputStream fis = new FileInputStream("F:\\file_test\\test.txt");
FileOutputStream fos = new FileOutputStream("F:\\file_test\\test2.txt");
/* 获取输入输出流通道*/
FileChannel fisChannel = fis.getChannel();
FileChannel fosChannel = fos.getChannel();
/* 创建缓冲器*/
ByteBuffer buff = ByteBuffer.allocate(1024);
while(fisChannel.read(buff) != -1){
/* 切换读写模式*/
buff.flip();
fosChannel.write(buff);
buff.clear();
}
fis.close();
fos.close();
fisChannel.close();
fosChannel.close();
long end = System.currentTimeMillis();
System.out.println("NIO读写文件用时:"+(end-start));
}
}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
三种缓冲效率对比
复制Javapackage com.test.nio;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* @Desc -累行客
* @Author luolei
* @Web http://www.leixing.xyz/
* @Date 2020/05/21 22:20
*
* 三种方式效率对比
* 1、字节缓冲区读写文件用时:2411
* 2、字节缓冲流读写文件用时:2380
* 3、NIO读写文件用时:1660
*
* 第二次
* 1、字节缓冲区读写文件用时:3367
* 2、字节缓冲流读写文件用时:2677
* 3、NIO读写文件用时:1942
*
* 第三次
* 1、字节缓冲区读写文件用时:2550
* 2、字节缓冲流读写文件用时:2742
* 3、NIO读写文件用时:1770
*
*/
public class Compare {
public static void main(String[] args) throws IOException, InterruptedException {
BufferedCopyTest();
Thread.sleep(10000);
BufferedStreamTest();
Thread.sleep(10000);
ChannelTest();
}
public static void BufferedCopyTest() throws IOException {
long start = System.currentTimeMillis();
/*文件输入流(字节流)*/
FileInputStream fis = new FileInputStream("F:\\file_test\\test.txt");
/*文件输出流(字节流)*/
FileOutputStream fos = new FileOutputStream("F:\\file_test\\test2.txt");
byte[] buff = new byte[1024];
int len =0;
while((len = fis.read(buff)) != -1){
fos.write(buff, 0, len);
}
fis.close();
fos.close();
long end = System.currentTimeMillis();
System.out.println("1、字节缓冲区读写文件用时:"+(end-start));
}
public static void BufferedStreamTest() throws IOException {
long start = System.currentTimeMillis();
/* 文件输入流(字节流)*/
FileInputStream fis = new FileInputStream("F:\\file_test\\test.txt");
/*输入字节缓冲流*/
BufferedInputStream bis = new BufferedInputStream(fis);
/*文件输出流(字节流)*/
FileOutputStream fos = new FileOutputStream("F:\\file_test\\test2.txt");
/*输出字节缓冲流*/
BufferedOutputStream bos = new BufferedOutputStream(fos);
int len =0;
while((len = bis.read()) != -1){
bos.write(len);
}
fis.close();
fos.close();
long end = System.currentTimeMillis();
System.out.println("2、字节缓冲流读写文件用时:"+(end-start));
}
public static void ChannelTest() throws IOException {
long start = System.currentTimeMillis();
/* 输入输出流*/
FileInputStream fis = new FileInputStream("F:\\file_test\\test.txt");
FileOutputStream fos = new FileOutputStream("F:\\file_test\\test2.txt");
/* 获取输入输出流通道*/
FileChannel fisChannel = fis.getChannel();
FileChannel fosChannel = fos.getChannel();
/* 创建缓冲器*/
ByteBuffer buff = ByteBuffer.allocate(1024);
while(fisChannel.read(buff) != -1){
/* 切换读写模式*/
buff.flip();
fosChannel.write(buff);
buff.clear();
}
fis.close();
fos.close();
fisChannel.close();
fosChannel.close();
long end = System.currentTimeMillis();
System.out.println("3、NIO读写文件用时:"+(end-start));
}
}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
NIO.2
JDK1.7引入新的IO的API,对原油IO的API中内容的改进,这个的改进之后的NIO就就成为NIO.2
NIO.2最大的改进就是提供了全面的文件输入/输出以及文件系统的访问与支持;并且心在你挣了java.nio.file包及其子包;还提供了基于异步的Channel的输入输出。
Path接口
作用:Path接口是一个共用在文件系统中定位文件的对象,通常表示一个依赖于系统的文件路径。效率更高。说明:
除此之外,NIO.2还提供了Paths和Files两个工具类;
Paths类中提供了两个返回Path的静态方法,通过这两个方法可以创建Path对象;
Files类中提供了大量的静态方法来操作文件。
Path常用方法

复制Javapackage com.test.nio2;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* @Desc -累行客
* @Author luolei
* @Web http://www.leixing.xyz/
* @Date 2020/05/22 22:07
*
* NIO.2还提供了Paths和Files两个工具类;
* Paths类中提供了两个返回Path的静态方法,通过这两个方法可以创建Path对象;
*
*/
public class PathTest {
public static void main(String[] args) {
Path path = Paths.get("F:\\file_test\\test.txt");
/* 获取根路径*/
System.out.println(path.getRoot());
/* 获取根路径*/
System.out.println(path.getParent());
/* 获取路径名称数(层数)*/
System.out.println(path.getNameCount());
/* 获取文件系统路径*/
System.out.println(path.getFileSystem());
for (int i = 0; i < path.getNameCount(); i++) {
Path name = path.getName(i);
System.out.println(name);
}
/* 绝对路径*/
System.out.println(path.toAbsolutePath());
/* Uri*/
System.out.println(path.toUri());
}
}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
Files常用方法

复制Javapackage com.test.nio2;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
/**
* @Desc -累行客
* @Author luolei
* @Web http://www.leixing.xyz/
* @Date 2020/05/22 22:19
*
* Files 工具类
*/
public class FilesTest {
public static void main(String[] args) throws IOException {
Path path = Paths.get("F:\\file_test\\test\\new");
/* 创建多级文件夹*/
Files.createDirectories(path);
/* 创建文件*/
Path path1 = Paths.get("F:\\file_test\\test\\new\\aa.txt");
Files.createFile(path1);
/* 将文本写入文件,并传入指定读写模式。 文件已经存在会爆 java.nio.file.FileAlreadyExistsException*/
ArrayList list = new ArrayList();
list.add("一行文本");
list.add("一行文本");
list.add("一行文本");
Files.write(path1, list, StandardOpenOption.APPEND);
/* 创建文件*/
List<String> list1 = Files.readAllLines(path1);
System.out.println(list1);
/* 返回文件大小,单位字节*/
long size = Files.size(path1);
System.out.println(size);
}
}- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42