参考:
https://www.jianshu.com/p/c58ed5ec7e4a
https://www.jianshu.com/p/80f8a74d4662
https://www.jianshu.com/p/360e37539266
https://www.jianshu.com/p/021081c84662
一.背景知识
1.字符与字节
- 一个字符表示一个汉字或英文字母,不同编码方式一个字符占用的字节数不同
- Java采用unicode编码,两个字节表示一个字符
- String(byte[] bytes, String encoding) 表示将bytes中数据以encoding编码方式转换为unicode编码
- getBytes(String charsetName) 表示使用指定编码方式将此String编码为byte序列
2.File类
- 代表与平台无关的文件和目录,Java通过File类在程序中操作文件或目录
- 构造函数
//构造函数File(String pathname)
File f1 =new File("c:\\abc\\1.txt");
//File(String parent,String child)
File f2 =new File("c:\\abc","2.txt");
- 创建和删除
//如果文件存在返回false,否则返回true并且创建文件
boolean createNewFile();
//创建一个File对象所对应的目录,成功返回true,否则false。且File对象必须为路径而不是文件。只会创建最后一级目录,如果上级目录不存在就抛异常。
boolean mkdir();
//创建一个File对象所对应的目录,成功返回true,否则false。且File对象必须为路径而不是文件。创建多级目录,创建路径中所有不存在的目录
boolean mkdirs() ;
//如果文件存在返回true并且删除文件,否则返回false
boolean delete();
//在虚拟机终止时,删除File对象所表示的文件或目录。
void deleteOnExit();
- 判断
boolean canExecute() ;//判断文件是否可执行
boolean canRead();//判断文件是否可读
boolean canWrite();//判断文件是否可写
boolean exists();//判断文件是否存在
boolean isDirectory();//判断是否是目录
boolean isFile();//判断是否是文件
boolean isHidden();//判断是否是隐藏文件或隐藏目录
boolean isAbsolute();//判断是否是绝对路径 文件不存在也能判断
- 获取
String getName();//返回文件或者是目录的名称
String getPath();//返回路径
String getAbsolutePath();//返回绝对路径
String getParent();//返回父目录,如果没有父目录则返回null
long lastModified();//返回最后一次修改的时间
long length();//返回文件的长度
File[] listRoots();// 列出所有的根目录(Window中就是所有系统的盘符)
String[] list() ;//返回一个字符串数组,给定路径下的文件或目录名称字符串
String[] list(FilenameFilter filter);//返回满足过滤器要求的一个字符串数组
File[] listFiles();//返回一个文件对象数组,给定路径下文件或目录
File[] listFiles(FilenameFilter filter);//返回满足过滤器要求的一个文件对象数组
其中包括文件过滤器接口FileNameFilter,实现其accept(File dir, String name)方法可过滤出满足条件的所有文件。
例如,过滤出file目录下所有后缀名是.mp3的文件
// 文件过滤
File[] files = file.listFiles(new FilenameFilter() {
@Override
public boolean accept(File file, String filename) {
return filename.endsWith(".mp3");
}
});
二. IO流框架体系
1.流的概念
- 一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象
- 流从数据源流向程序称为输入流,从程序流向数据库称为输出流
- 数据源可以是内存、管道、文件、网络或程序等
2.流的分类
- 输入流和输出流
-
字节流和字符流
字符流在基于字节流读取时查询了指定的码表
读写单位和处理对象不同 -
节点流和处理流
处理流通过封装已存在的流使程序可采用完全相同的代码来访问不同数据源
//节点流,直接传入的参数是IO设备
FileInputStream fis = new FileInputStream("test.txt");
//处理流,直接传入的参数是流对象
BufferedInputStream bis = new BufferedInputStream(fis);
3. IO流框架
IO流框架
分别适用于
- 文件访问
- 网络访问
- 内存缓存访问
- 线程内部通信(管道)
- 缓冲
- 过滤
- 解析
- 读写文本
- 读写基本类型数据
- 读写对象
IO适用类
4. IO流四大基类
- InputStream
抽象类,所有输入字节流的父类
//读取一个字节并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。
int read() ;
//读取一系列字节并存储到一个数组buffer,返回实际读取的字节数,如果读取前已到输入流的末尾返回-1。
int read(byte[] buffer) ;
//读取length个字节并存储到一个字节数组buffer,从off位置开始存,最多len, 返回实际读取的字节数,如果读取前以到输入流的末尾返回-1。
int read(byte[] buffer, int off, int len) ;
- Reader
抽象类,所有输入字符流的父类
//读取一个字符并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。
int read();
//读取一系列字符并存储到一个数组buffer,返回实际读取的字符数,如果读取前已到输入流的末尾返回-1。
int read(char[] cbuf);
//读取length个字符,并存储到一个数组buffer,从off位置开始存,最多读取len,返回实际读取的字符数,如果读取前以到输入流的末尾返回-1。
int read(char[] cbuf, int off, int len);
- OutputStream
抽象类,所有输出字节流的父类
//向输出流中写入一个字节数据,该字节数据为参数b的低8位。
void write(int b) ;
//将一个字节类型的数组中的数据写入输出流。
void write(byte[] b);
//将一个字节类型的数组中的从指定位置(off)开始的,len个字节写入到输出流。
void write(byte[] b, int off, int len);
//将输出流中缓冲的数据全部写出到目的地。
void flush();
- Writer
抽象类,所有输出字符流的父类
//向输出流中写入一个字符数据,该字节数据为参数c的低16位。
void write(int c);
//将一个字符类型的数组中的数据写入输出流,
void write(char[] cbuf)
//将一个字符类型的数组中的从指定位置(offset)开始的,length个字符写入到输出流。
void write(char[] cbuf, int offset, int length);
//将一个字符串中的字符写入到输出流。
void write(String string);
//将一个字符串从offset开始的length个字符写入到输出流。
void write(String string, int offset, int length);
//将输出流中缓冲的数据全部写出到目的地。
void flush();
- 注意
程序里打开的IO资源不属于内存资源,GC无法回收,需显式关闭
输出流在关闭时将自动调用flush方法清空缓冲区
三.RandomAccessFile
1.简介
- 可实现文件随机读写
- 仅能读写文件
- 多用于网络请求中的多线程下载及断点续传
2.方法
- 构造方法
使用1) String/File参数指定文件本身,2) mode参数指定访问模式
r 以只读方式打开,任何写操作将抛出IOException
rw 打开以便读和写 - long getFilePointer()
返回文件记录指针的当前位置 - void seek(long pos)
将文件记录指针定位到pos位置
3.RandomAccessFile的使用
- 一个多线程写文件的示例
/**
* 测试利用多线程进行文件的写操作
*/
public class Test {
public static void main(String[] args) throws Exception {
// 预分配文件所占的磁盘空间,磁盘中会创建一个指定大小的文件
RandomAccessFile raf = new RandomAccessFile("D://abc.txt", "rw");
raf.setLength(1024*1024); // 预分配 1M 的文件空间
raf.close();
// 所要写入的文件内容
String s1 = "第一个字符串";
String s2 = "第二个字符串";
String s3 = "第三个字符串";
String s4 = "第四个字符串";
String s5 = "第五个字符串";
// 利用多线程同时写入一个文件
new FileWriteThread(1024*1,s1.getBytes()).start(); // 从文件的1024字节之后开始写入数据
new FileWriteThread(1024*2,s2.getBytes()).start(); // 从文件的2048字节之后开始写入数据
new FileWriteThread(1024*3,s3.getBytes()).start(); // 从文件的3072字节之后开始写入数据
new FileWriteThread(1024*4,s4.getBytes()).start(); // 从文件的4096字节之后开始写入数据
new FileWriteThread(1024*5,s5.getBytes()).start(); // 从文件的5120字节之后开始写入数据
}
// 利用线程在文件的指定位置写入指定数据
static class FileWriteThread extends Thread{
private int skip;
private byte[] content;
public FileWriteThread(int skip,byte[] content){
this.skip = skip;
this.content = content;
}
public void run(){
RandomAccessFile raf = null;
try {
raf = new RandomAccessFile("D://abc.txt", "rw");
raf.seek(skip);
raf.write(content);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
raf.close();
} catch (Exception e) {
}
}
}
}
}
妆罢低声问夫婿,画眉深浅入时无