该源码适用于windows环境下

最近做了个小系统,用到了File相关的类,如下:

<span style="font-size:14px;">InputStreamReader in = new InputStreamReader(new FileInputStream(new File(UrlUtil.getRootUrl() + "config/mango.properties")), "GBK");</span>

其中,getRootUrl定义如下:

<span style="font-size:14px;">        /*** @return 获取根目录 其实是classpath*/public static String getRootUrl() {URL rootUrl = UrlUtil.class.getClassLoader().getResource("");// 针对打包成jar包后获取rootUrl为null的问题if (null == rootUrl) {return "";}String rootUrlS = rootUrl.getPath();if (rootUrlS.contains("/bin/")) {return rootUrlS.substring(0, rootUrlS.length() - 4);}return rootUrlS;}</span>

文件结构分布如下:

从new File(UrlUtil.getRootUrl() + "config/mango.properties")开始分析

一、File

An abstract representation of file and directory pathnames. 该类的对象可以代表一个具体的文件或文件夹

<span style="font-size:14px;">    /*** Creates a new <code>File</code> instance by converting the given* pathname string into an abstract pathname.  If the given string is* the empty string, then the result is the empty abstract pathname.** @param   pathname  A pathname string* @throws  NullPointerException*          If the <code>pathname</code> argument is <code>null</code>*/public File(String pathname) {if (pathname == null) {throw new NullPointerException();}this.path = fs.normalize(pathname);               //将文件路径转换为正常路径this.prefixLength = fs.prefixLength(this.path);   //正常路径下的前缀长度}</span>

1.1其中fs为文件系统,在windows下获取的是Win32FileSystem

<span style="font-size:14px;">    /*** The FileSystem object representing the platform's local file system.*/static private FileSystem fs = FileSystem.getFileSystem();</span>

1.2 fs.normalize(pathname)

<span style="font-size:14px;">    /* Check that the given pathname is normal.  If not, invoke the realnormalizer on the part of the pathname that requires normalization.This way we iterate through the whole pathname string only once. */public String normalize(String path) {int n = path.length();char slash = this.slash;char altSlash = this.altSlash;char prev = 0;for (int i = 0; i < n; i++) {char c = path.charAt(i);if (c == altSlash)return normalize(path, n, (prev == slash) ? i - 1 : i);if ((c == slash) && (prev == slash) && (i > 1))return normalize(path, n, i - 1);if ((c == ':') && (i > 1))return normalize(path, n, 0);prev = c;}if (prev == slash) return normalize(path, n, n - 1);return path;}</span>
<span style="font-size:14px;">    private final char slash;private final char altSlash;private final char semicolon;public Win32FileSystem() {slash = AccessController.doPrivileged(new GetPropertyAction("file.separator")).charAt(0);semicolon = AccessController.doPrivileged(new GetPropertyAction("path.separator")).charAt(0);altSlash = (this.slash == '\\') ? '/' : '\\';}</span>

转换前/E:/MyEclipse/workspace/FreightSystem/config/mango.properties

转换后E:\MyEclipse\workspace\FreightSystem\config\mango.properties

1.3 fs.prefixLength(this.path)

<span style="font-size:14px;">    public int prefixLength(String path) {char slash = this.slash;int n = path.length();if (n == 0) return 0;char c0 = path.charAt(0);char c1 = (n > 1) ? path.charAt(1) : 0;if (c0 == slash) {if (c1 == slash) return 2;  /* Absolute UNC pathname "\\\\foo" */return 1;                   /* Drive-relative "\\foo" */}if (isLetter(c0) && (c1 == ':')) {if ((n > 2) && (path.charAt(2) == slash))return 3;               /* Absolute local pathname "z:\\foo" */return 2;                   /* Directory-relative "z:foo" */}return 0;                       /* Completely relative */}</span>

关于前缀的解释,看完后就能理解上述代码了

<span style="font-size:14px;"><li> For UNIX platforms, the prefix of an absolute pathname is always* <code>"/"</code>.  Relative pathnames have no prefix.  The abstract pathname* denoting the root directory has the prefix <code>"/"</code> and an empty* name sequence.** <li> For Microsoft Windows platforms, the prefix of a pathname that contains a drive* specifier consists of the drive letter followed by <code>":"</code> and* possibly followed by <code>"\\"</code> if the pathname is absolute.  The* prefix of a UNC pathname is <code>"\\\\"</code>; the hostname and the share* name are the first two names in the name sequence.  A relative pathname that* does not specify a drive has no prefix.</span>

对于E:\MyEclipse\workspace\FreightSystem\config\mango.properties获取到的前缀长度为3,也即是E:\都为前缀

二、FileInputStream

A <code>FileInputStream</code> obtains input bytes from a file in a file system. What files are  available depends on the host environment. <code>FileInputStream</code> is meant for reading streams of raw bytes such as image data. For reading streams of characters, consider using<code>FileReader</code>.

通过FileInputStream可以从文件中获得字节流,如果要获得字符流需要适用FileReader,下面会介绍InputStreamReader,用来获取字符流。

<span style="font-size:14px;">    /*** Creates a <code>FileInputStream</code> by* opening a connection to an actual file,* the file named by the <code>File</code>* object <code>file</code> in the file system.* A new <code>FileDescriptor</code> object* is created to represent this file connection.* <p>* First, if there is a security manager,* its <code>checkRead</code> method  is called* with the path represented by the <code>file</code>* argument as its argument.* <p>* If the named file does not exist, is a directory rather than a regular* file, or for some other reason cannot be opened for reading then a* <code>FileNotFoundException</code> is thrown.** @param      file   the file to be opened for reading.* @exception  FileNotFoundException  if the file does not exist,*                   is a directory rather than a regular file,*                   or for some other reason cannot be opened for*                   reading.* @exception  SecurityException      if a security manager exists and its*               <code>checkRead</code> method denies read access to the file.* @see        java.io.File#getPath()* @see        java.lang.SecurityManager#checkRead(java.lang.String)*/public FileInputStream(File file) throws FileNotFoundException {String name = (file != null ? file.getPath() : null);SecurityManager security = System.getSecurityManager();if (security != null) {security.checkRead(name);}if (name == null) {throw new NullPointerException();}if (file.isInvalid()) {throw new FileNotFoundException("Invalid file path");}fd = new FileDescriptor();fd.incrementAndGetUseCount();this.path = name;open(name);    //打开文件,进行读取  这个是本地方法}</span>

其中的read方法,从其中的read方法也可看出只能读取字节

<span style="font-size:14px;">    /*** Reads a byte of data from this input stream. This method blocks* if no input is yet available.** @return     the next byte of data, or <code>-1</code> if the end of the*             file is reached.* @exception  IOException  if an I/O error occurs.*/public int read() throws IOException {Object traceContext = IoTrace.fileReadBegin(path);int b = 0;try {b = read0();} finally {IoTrace.fileReadEnd(traceContext, b == -1 ? 0 : 1);}return b;}private native int read0() throws IOException;</span></span>

三、InputStreamReader

An InputStreamReader is a bridge from byte streams to character streams: It reads bytes and decodes them into characters using a specified{@link java.nio.charset.Charset <code>charset</code>}.  The charset that it uses may be specified by name or may be given explicitly, or the platform's default charset may be accepted.

InputStreamReader是从字节流到字符流转换的桥梁,它读出字节并按照提供的编码格式解码成字符。而其又委托给StreamDeCoder去做。

<span style="font-size:14px;">    /*** Creates an InputStreamReader that uses the default charset.** @param  in   An InputStream*/public InputStreamReader(InputStream in) {super(in);try {sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object} catch (UnsupportedEncodingException e) {// The default encoding should always be availablethrow new Error(e);}}/*** Creates an InputStreamReader that uses the named charset.** @param  in*         An InputStream** @param  charsetName*         The name of a supported*         {@link java.nio.charset.Charset </code>charset<code>}** @exception  UnsupportedEncodingException*             If the named charset is not supported*/public InputStreamReader(InputStream in, String charsetName)throws UnsupportedEncodingException{super(in);if (charsetName == null)throw new NullPointerException("charsetName");sd = StreamDecoder.forInputStreamReader(in, this, charsetName);  //创建了一个StreamDecoder}</span>
<span style="font-size:14px;"><pre class="java" name="code">StreamDecoder位于sun.nio.cs包中</span>
<span style="font-size:14px;"> public static StreamDecoder forInputStreamReader(InputStream in,Object lock,String charsetName)throws UnsupportedEncodingException{String csn = charsetName;if (csn == null)csn = Charset.defaultCharset().name();   //默认编码格式为UTF-8try {if (Charset.isSupported(csn))    //检查jvm是否支持该编码格式,否则跑出异常return new StreamDecoder(in, lock, Charset.forName(csn));} catch (IllegalCharsetNameException x) { }throw new UnsupportedEncodingException (csn);}</span>

最终如下,初始化其中的cs,decoder,ch

<span style="font-size:14px;">StreamDecoder(InputStream in, Object lock, Charset cs) {this(in, lock,cs.newDecoder().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE));}StreamDecoder(InputStream in, Object lock, CharsetDecoder dec) {super(lock);this.cs = dec.charset();this.decoder = dec;// This path disabled until direct buffers are fasterif (false && in instanceof FileInputStream) {ch = getChannel((FileInputStream)in);if (ch != null)bb = ByteBuffer.allocateDirect(DEFAULT_BYTE_BUFFER_SIZE);}if (ch == null) {this.in = in;this.ch = null;bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE);}bb.flip();                      // So that bb is initially empty}</span>

解码最终交给了CharsetDecoder。

然后看其read方法

<span style="font-size:14px;">    /*** Reads a single character.** @return The character read, or -1 if the end of the stream has been*         reached** @exception  IOException  If an I/O error occurs*/public int read() throws IOException {return sd.read();}</span>

调用到StreamDecoder中的read方法

<span style="font-size:14px;">public int read() throws IOException {return read0();}private int read0() throws IOException {synchronized (lock) {// Return the leftover char, if there is oneif (haveLeftoverChar) {haveLeftoverChar = false;return leftoverChar;}// Convert more byteschar cb[] = new char[2];int n = read(cb, 0, 2);switch (n) {case -1:return -1;case 2:leftoverChar = cb[1];haveLeftoverChar = true;// FALL THROUGHcase 1:return cb[0];default:assert false : n;return -1;}}}</span>

继续read

<span style="font-size:14px;">public int read(char cbuf[], int offset, int length) throws IOException {int off = offset;int len = length;synchronized (lock) {ensureOpen();if ((off < 0) || (off > cbuf.length) || (len < 0) ||((off + len) > cbuf.length) || ((off + len) < 0)) {throw new IndexOutOfBoundsException();}if (len == 0)return 0;int n = 0;if (haveLeftoverChar) {// Copy the leftover char into the buffercbuf[off] = leftoverChar;off++; len--;haveLeftoverChar = false;n = 1;if ((len == 0) || !implReady())// Return now if this is all we can produce w/o blockingreturn n;}if (len == 1) {// Treat single-character array reads just like read()int c = read0();if (c == -1)return (n == 0) ? -1 : n;cbuf[off] = (char)c;return n + 1;}return n + implRead(cbuf, off, off + len);}}</span>
<span style="font-size:14px;">int implRead(char[] cbuf, int off, int end) throws IOException {// In order to handle surrogate pairs, this method requires that// the invoker attempt to read at least two characters.  Saving the// extra character, if any, at a higher level is easier than trying// to deal with it here.assert (end - off > 1);CharBuffer cb = CharBuffer.wrap(cbuf, off, end - off);if (cb.position() != 0)// Ensure that cb[0] == cbuf[off]cb = cb.slice();boolean eof = false;for (;;) {CoderResult cr = decoder.decode(bb, cb, eof);  //最终的解码if (cr.isUnderflow()) {if (eof)break;if (!cb.hasRemaining())break;if ((cb.position() > 0) && !inReady())break;          // Block at most onceint n = readBytes();if (n < 0) {eof = true;if ((cb.position() == 0) && (!bb.hasRemaining()))break;decoder.reset();}continue;}if (cr.isOverflow()) {assert cb.position() > 0;break;}cr.throwException();}if (eof) {// ## Need to flush decoderdecoder.reset();}if (cb.position() == 0) {if (eof)return -1;assert false;}return cb.position();}</span>

四、关于new FileInputStream(file)及new FileOutputStream(file)时java.io.FileNotFoundException问题

原因可能有三:

(1)文件名乱码。你工程的编码是 utf-8还是 gbk,如果是gbk那么小心文件名如果有中文,会乱码。

(2)没有创建文件夹,在没有文件夹情况下,创建文件将报错.解决方案为先创建文件夹,具体代码见下面。

(3)有文件夹,但文件所在位置无文件夹,即文件路径太深,超过二级。解决方案同2

要明确FileInputStream及FileOutputStream并不会帮你创建不存在的路径(从上述的源码中也可看出,检查完路径的合法性,直接open的),所以要先创建路径,再创建文件。

<span style="font-size:14px;">File cacheDir = new File("data/data/com.kavinapps.androidk.pcexam/databases/");//设置目录参数  cacheDir.mkdirs();//新建目录  Log.i("copySd2phone","新建data/data目录成功");  String filename;  //获得文件名的长度  filename = "carnum.db";   Log.i("createNewFile","filename= "+filename);  //文件名  cacheFile = new File(cacheDir,filename);//设置参数  cacheFile.createNewFile();//生成文件  Log.i("createNewFile","生成文件成功"+cacheFile.getName());  output = new FileOutputStream( "data/data/com.kavinapps.androidk.pcexam/databases/carnum.db" );
</span>









Java File相关源码分析:File、FileInputStream、InputStreamReader相关推荐

  1. java.util.ServiceLoader源码分析

    java.util.ServiceLoader源码分析 回顾: ServiceLoader类的使用(具体参考博客http://blog.csdn.net/liangyihuai/article/det ...

  2. 【Android 电量优化】JobScheduler 相关源码分析 ( JobSchedulerService 源码分析 | 任务检查 | 任务执行 )

    文章目录 一.回调 StateChangedListener 接口 二.JobHandler 处理 ( 任务检查 ) 三.maybeRunPendingJobsH 方法 四.assignJobsToC ...

  3. Ansroid系统(262)---MTK安卓sim卡相关源码分析

    MTK安卓sim卡相关源码分析 原文地址:http://m.blog.csdn.net/article/details?id=50039589 最近由于一个sim卡相关的需求,就去了解了一下Andro ...

  4. 并发编程5:Java 阻塞队列源码分析(下)

    上一篇 并发编程4:Java 阻塞队列源码分析(上) 我们了解了 ArrayBlockingQueue, LinkedBlockingQueue 和 PriorityBlockingQueue,这篇文 ...

  5. Java集合类框架源码分析 之 LinkedList源码解析 【4】

    上一篇介绍了ArrayList的源码分析[点击看文章],既然ArrayList都已经做了介绍,那么作为他同胞兄弟的LinkedList,当然必须也配拥有姓名! Talk is cheap,show m ...

  6. kafka-分区重分配及相关源码分析

    tips:本文源码对应的kafka版本是2.3.1,源码分支2.3 分区重分配及相关源码分析 简单示例 kafka消费者分区重分配 消费者分区重分配简介 分区重分配的策略 RangeAssignor ...

  7. Java并发-ReentrantReadWriteLock源码分析

    ReentrantLock实现了标准的互斥重入锁,任一时刻只有一个线程能获得锁.考虑这样一个场景:大部分时间都是读操作,写操作很少发生:我们知道,读操作是不会修改共享数据的,如果实现互斥锁,那么即使都 ...

  8. freeRTOS滴答时钟相关源码分析

    最近学习白问网韦东山老师在B站开源的freeRTOS课程,网址:韦东山直播公开课:RTOS实战项目之实现多任务系统 第1节:裸机程序框架和缺陷_哔哩哔哩_bilibili和7天物联网训练营[第2期]7 ...

  9. Java中ArrayList源码分析

    一.简介 ArrayList是一个数组队列,相当于动态数组.每个ArrayList实例都有自己的容量,该容量至少和所存储数据的个数一样大小,在每次添加数据时,它会使用ensureCapacity()保 ...

最新文章

  1. PyQt5 技术篇-QWidget、QDialog程序窗口关闭closeEvent()触发事件方法重写
  2. sql int 转string_SQL智能代码补全引擎【sql-code-intelligence】介绍
  3. Rabbitmq消息的Confirm确认机制
  4. leetcode面试题 16.19. 水域大小(深度优先搜索)
  5. 论文浅尝 | 神经符号推理综述(下)
  6. 这不关我的事,别烦我!
  7. abb机器人工具坐标系设定方式_设定abb机器人工具坐标的方法
  8. 2022年计算机软件水平考试网络工程师(中级)练习题及答案
  9. 重读浙大版《概率论与数理统计》
  10. HTML静态网页作业-餐饮美食网页(HTML+CSS+JavaScript)
  11. c35是什么意思_什么是C35混凝土?
  12. 查查自己五行属什么命
  13. 认识3DMAX的各种插件
  14. android一行三列,Android -- listview实现一行多列效果
  15. 采用顺序结构存储串,设计实现求串S和串T的一个最长公共子串的算法。
  16. 成熟男人的处事说话技巧
  17. 碳足迹、碳核查、碳盘查三者的区别是什么?
  18. 您的 MAD 得分是多少?| MAD Skills
  19. 简陋无比的 Python 抠图方案,好像还挺像回事儿?
  20. Python Show-Me-the-Code 第 0020题 统计通话时长

热门文章

  1. C# Microsoft Speech 选择音频输出设备
  2. 批量检测百度云分享链接有效性方法
  3. 第四章 采购与供应链管理中的利益相关者
  4. sql server 数据库表中插入带有英文 单引号 或双引号 的数据
  5. 计算机桌面有个方框,电脑屏幕的白色方框怎么清除
  6. max232c语言,串行口通信(STC89C52+MAX232):串行口通信硬件设计详解
  7. HDU_4379_The More The Better
  8. 百度百聘企业简单信息获取
  9. 'CALayer position contains NaN: [nan nan]'异常
  10. 【Android App】在线语音识别功能实现(使用云知声平台与WebSocket 超详细 附源码)