转载,务必写上原文链接 !(尊重与你分享知识的人)
目录
文件
数据在磁盘中的唯一最小描述就是 文件
,也就是说应用程序只能通过操控 文件
来操作磁盘上的数据;
File
对象 VS FileDescriptor
对象
初学 java
IO
流的时候,基本都被告诉 java
里面用 File
来表示一个文件对象;
其实 File
并不代表一个真实存在的文件对象,当我们给定路径字符串的时候,返回的 File
对象仅仅是代表这个路径下的 虚拟对象
,至于这个路径是一个文件还是一个文件夹,或者文件存在不存在,File
根本不关心 ;
只要当真正要读取这个文件的时候,才会检查给定路径的文件存在不存在(这个判断发生在读取流的构造器里面) ;
那么 java
里面,用什么来描述磁盘的文件 —— FileDescriptor
对象,它代表一个磁盘上的真实文件对象;
文件讲解java访问磁盘文件过程
// 创建一个字符读取流
FileReader fileReader = new FileReader("xxxxxx");
0、这一行代码后面都发生了什么事情;
我们要看下,这个 FileReader
到底是怎么工作的,跟进去看下源代码 ;
下面开始源码跟进分析
public FileReader(String fileName) throws FileNotFoundException {
// 调用父类构造器,传进去的参数是 FileInputStream
super(new FileInputStream(fileName));
}
----------------------------------------
// FileReader 的父类是 字节转换流
public class FileReader extends InputStreamReader
1、发现 FileReader
构造器中,是调用其父类 InputStreamReader
的构造器;
分析父类构造器参数发现,给父类构造器传递的参数是一个 FileInputStream
对象;我们同样需要看下这个类的构造器源代码;
public FileInputStream(String name) throws FileNotFoundException {
// 跟进传进来的字符串,创建对应的 虚拟对象 File
this(name != null ? new File(name) : null);
}
2、可以看见 FileInputStream
根据传进来的字符串,创建了 File
对象,然后将这个 File
对象,传给了同类的其他构造器 ;
public FileInputStream(File file) throws FileNotFoundException {
// 获取文件的路径
String name = (file != null ? file.getPath() : null);
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(name);
}
// 如果name为null,说明传进来的file就是null。抛出空指针异常
if (name == null) {
throw new NullPointerException();
}
// 判断文件是否有效,也就是判断给File对象的路径下面的文件是否真的存在的;
if (file.isInvalid()) {
throw new FileNotFoundException("Invalid file path");
}
// 创建一个代表真实文件的对象
fd = new FileDescriptor();
fd.incrementAndGetUseCount();
this.path = name;
// 打开路径下面的文件,是一个本地方法
open(name);
}
3、经过上面的代码分析,我们可以发现 FileInputStream
创建对象的时候,会对 File
构造器的路径参数进行检查,判断路径对应的文件到底存在与否;并且还会创建一个代表真实文件的 FileDescriptor
对象 ;
创建完 FileInputStream
对象以后,继续回到 FileReader
构造器上面 ;我们在上面的代码,知道 FileReader
是调用了父类 InputStreamReader
的构造器,现在我们关注一下它的父类构造器;
public FileReader(String fileName) throws FileNotFoundException {
// 调用父类构造器,传进去的参数是 FileInputStream
super(new FileInputStream(fileName));
}
----------------------------------------
public InputStreamReader(InputStream in) {
// 调用了父类构造器,里面没啥好看的,就加了锁
// 加锁的原因是因为,同一时刻一个文件只允许一个人来读取
super(in);
try {
// 创建一个 StreamDecoder 解码对象,将字节解码为字符
sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
} catch (UnsupportedEncodingException e) {
// The default encoding should always be available
throw new Error(e);
}
}
4、查看源码,知道创建 InputStreamReader
对象,会同时创建 StreamDecoder
对象,因为我们是用字节流,最后需要的字符,所有需要它来解码
其中关于
StreamDecoder
笔者之前分析过—— StreamDecoder 对象分析(可点击)
fileReader.read()
分析完 上面,我们再看下 read()
方法;看看到底是谁实现了read()接口 ;
FileReader fileReader = new FileReader("xxxxxx");
// 跟进去看看 read()方法到底是谁实现的;
fileReader.read();
---------------------------------
// 跟进去发现是调用了 一个 sd变量的read方法;
public int read() throws IOException {
return sd.read();
}
-------------------------------------
// sd的read()方法实现
private int read0() throws IOException {
Object var1 = this.lock;
synchronized(this.lock) {
if (this.haveLeftoverChar) {
this.haveLeftoverChar = false;
return this.leftoverChar;
} else {
char[] var2 = new char[2];
int var3 = this.read(var2, 0, 2);
switch(var3) {
case -1:
return -1;
case 0:
default:
assert false : var3;
return -1;
case 2:
this.leftoverChar = var2[1];
this.haveLeftoverChar = true;
case 1:
return var2[0];
}
}
}
}
---------------------------------
// 跟进发现 这个sd 是StreamDecoder 对象
private final StreamDecoder sd;
至此,我们发现底层工作都是 StreamDecoder
完成的,这个在背后默默工作的低调者,我们应该记住它;