它既可以表示具体文件、也可以表示文件夹。在这里不做太深入介绍。File一般结合FileInputStream、FileOutputStream使用、当然也有其他使用的地方。这里仅列出一般
与fis、fos结合使用的时候使用的方法。
File(File dir, String name) 根据文件夹名 抽象路径名和想要创建的文件的路径名字符串创建一个新 File 实例。 File(String path) 通过将指定的路径名转换为抽象路径名来创建一个新 File 实例。 File(String dirPath, String name) 根据文件夹路径名和想要创建的文件名来创建一个新 File 实例。 File(URI uri) 将给定的URI转换成抽象路径名来创建一个新File实例。
boolean canExecute() 测试应用程序是否可以执行此抽象路径名表示的文件。 boolean canRead() 测试应用程序是否可以读取此抽象路径名表示的文件。 boolean canWrite() 测试应用程序是否可以修改此抽象路径名表示的文件。 int compareTo(File pathname) 按字母顺序比较两个抽象路径名。 boolean createNewFile() 当且仅当不存在具有此抽象路径名指定名称的文件时,不可分地创建一个新的空文件。 static File createTempFile(String prefix, String suffix) 在默认临时文件目录中创建一个空文件,使用给定前缀和后缀生成其名称。 static File createTempFile(String prefix, String suffix, File directory) 在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。 boolean delete() 删除此抽象路径名表示的文件或目录。 void deleteOnExit() 在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。 boolean equals(Object obj) 测试此抽象路径名与给定对象是否相等。 boolean exists() 测试此抽象路径名表示的文件或目录是否存在。 File getAbsoluteFile() 返回此抽象路径名的绝对路径名形式。 String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。 File getCanonicalFile() 返回此抽象路径名的规范形式。 String getCanonicalPath() 返回此抽象路径名的规范路径名字符串。 long getFreeSpace() 返回此抽象路径名指定的分区中未分配的字节数。 String getName() 返回由此抽象路径名表示的文件或目录的名称。 String getParent() 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回 null。 File getParentFile() 返回此抽象路径名父目录的抽象路径名;如果此路径名没有指定父目录,则返回 null。 String getPath() 将此抽象路径名转换为一个路径名字符串。 long getTotalSpace() 返回此抽象路径名指定的分区大小。 long getUsableSpace() 返回此抽象路径名指定的分区上可用于此虚拟机的字节数。 int hashCode() 计算此抽象路径名的哈希码。 boolean isAbsolute() 测试此抽象路径名是否为绝对路径名。 boolean isDirectory() 测试此抽象路径名表示的文件是否是一个目录。 boolean isFile() 测试此抽象路径名表示的文件是否是一个标准文件。 boolean isHidden() 测试此抽象路径名指定的文件是否是一个隐藏文件。 long lastModified() 返回此抽象路径名表示的文件最后一次被修改的时间。 long length() 返回由此抽象路径名表示的文件的长度。 String[] list() 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中的文件和目录。 String[] list(FilenameFilter filter) 返回一个字符串数组,这些字符串指定此抽象路径名表示的目录中满足指定过滤器的文件和目录。 File[] listFiles() 返回一个抽象路径名数组,这些路径名表示此抽象路径名表示的目录中的文件。 File[] listFiles(FileFilter filter) 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。 File[] listFiles(FilenameFilter filter) 返回抽象路径名数组,这些路径名表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。 static File[] listRoots() 列出可用的文件系统根。 boolean mkdir() 创建此抽象路径名指定的目录。 boolean mkdirs() 创建此抽象路径名指定的目录,包括所有必需但不存在的父目录。 boolean renameTo(File dest) 重新命名此抽象路径名表示的文件。 boolean setExecutable(boolean executable) 设置此抽象路径名所有者执行权限的一个便捷方法。 boolean setExecutable(boolean executable, boolean ownerOnly) 设置此抽象路径名的所有者或所有用户的执行权限。 boolean setLastModified(long time) 设置此抽象路径名指定的文件或目录的最后一次修改时间。 boolean setReadable(boolean readable) 设置此抽象路径名所有者读权限的一个便捷方法。 boolean setReadable(boolean readable, boolean ownerOnly) 设置此抽象路径名的所有者或所有用户的读权限。 boolean setReadOnly() 标记此抽象路径名指定的文件或目录,从而只能对其进行读操作。 boolean setWritable(boolean writable) 设置此抽象路径名所有者写权限的一个便捷方法。 boolean setWritable(boolean writable, boolean ownerOnly) 设置此抽象路径名的所有者或所有用户的写权限。 String toString() 返回此抽象路径名的路径名字符串。 URI toURI() 构造一个表示此抽象路径名的 file: URI。 URL toURL() 已过时。 此方法不会自动转义 URL 中的非法字符。建议新的代码使用以下方式将抽象路径名转换为 URL:首先通过 toURI 方法将其转换为 URI,然后通过 URI.toURL 方法将 URI 装换为 URL。
FileInputStream(File file); 通过打开一个到实际文件的连接来创建一个文件字节输入流、该文件通过文件系统中的File对象file指定 FileInputStream(String file); 通过打开一个到实际文件的连接来创建一个文件字节输入流、该文件通过文件系统中的路径名name来指定 FileInputStream((FileDescriptor fdObj) 通过使用文件描述符fdObj创建一个FileInputStream、该文件描述符表示到文件系统中某个实际文件的现有连接
int avaliable(); 返回连接文件中的可供读取的字节数 void close(); 关闭fis、并释放所有与此流有关的资源 int read(); 读取文件中的一个字节并以整数形式返回 int read(byte[] b, int off, int len)将文件中len个字节读取到下标从off开始的字节数组b中 int read(byte[] b) 读取b.length个字节到字节数组b中 long skip(long n); 跳过文件中n个字节
/** * 关于File的输入输出流中的关键实现方法都是系统方法、所以对于fis、fos的使用、知道中间的过程就行。 */ public class FileInputStream extends InputStream { java.io.FileInputStream //一个打开到指定文件的连接或者句柄。 private FileDescriptor fd; //文件夹通道 private FileChannel channel = null; /** * 通过“文件路径名”创建一个对应的“文件输入流”。 */ public FileInputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null); } /** * 通过“文件对象”创建一个对应的“文件输入流”。 */ 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(); } fd = new FileDescriptor(); //打开到文件名指定的文件的连接、要对文件进行操作、当然要先获得连接。 open(name); } /** * 通过“文件描述符”创建一个对应的“文件输入流”。 * 很少用! */ public FileInputStream(FileDescriptor fdObj) { SecurityManager security = System.getSecurityManager(); if (fdObj == null) { throw new NullPointerException(); } if (security != null) { security.checkRead(fdObj); } fd = fdObj; } /** * 通过文件名打开到“文件名指定的文件”的连接 */ private native void open(String name) throws FileNotFoundException; /** * 底层————读取“指定文件”下一个字节并返回、若读取到结尾则返回-1。 */ public native int read() throws IOException; /** * 底层————读取“指定文件”最多len个有效字节、并存放到下标从 off 开始长度为 len 的 byte[] b 中、返回读取的字节数、若读取到结尾则返回-1。 */ private native int readBytes(byte b[], int off, int len) throws IOException; /** * 调用底层readbytes(b, 0, b.length)读取b.length个字节放入b中、返回读取的字节数、若读取到结尾则返回-1。 */ public int read(byte b[]) throws IOException { return readBytes(b, 0, b.length); } /** * 调用底层readbytes(b, off, len)读取b.length个字节放入下标从 off 开始长度为 len 的 byte[] b 中、返回读取的字节数、若读取到结尾则返回-1。 */ public int read(byte b[], int off, int len) throws IOException { return readBytes(b, off, len); } /** * 底层————从源文件中跳过n个字节、n不能为负、返回值是实际跳过的字节数。 * 当n >= fis.available();时返回-1; */ public native long skip(long n) throws IOException; /** * 查询此文件字节输入流对应的源文件中可被读取的有效字节数。 */ public native int available() throws IOException; /** * 关闭此流、并释放所有与此流有关的资源、 * 并且根据此流建立的channel也会被关闭。 */ public void close() throws IOException { if (channel != null) channel.close(); close0(); } /** * 获取此流对应的文件系统中实际文件的“文件描述符”。 */ public final FileDescriptor getFD() throws IOException { if (fd != null) return fd; throw new IOException(); } /** * 获取与此流有关的FileChannel。 */ public FileChannel getChannel() { synchronized (this) { if (channel == null) channel = FileChannelImpl.open(fd, true, false, this); return channel; } } private static native void initIDs(); //关闭 private native void close0() throws IOException; static { initIDs(); } /** * 当此流不再被使用、关闭此流。 */ protected void finalize() throws IOException { if (fd != null) { if (fd != fd.in) { close(); } } } }
因为FileInputStream、一般与FileOutputStream结合使用、所以两个实例就整合在一起了、最下面的实例不但有两者分开的测试方法、也有结合使用的测试方法。
FileOutputStream(File file) 创建一个向指定File表示的文件中写入数据的文件字节输出流 fos、每次写入会替换掉File中原有内容 FileOutputStream(File file, boolean append) 创建一个向指定File表示的文件中写入数据的 fos、每次写入会根据append的值判断是否会在File中原有内容之后追加写入的内容。 FileOutputStream(String name) 创建一个向name指定的文件中写入数据的fos、每次写入会替换掉File中原有内容 FileOutputStream(String name, boolean append) 创建一个向name指定的文件中写入数据的fos、每次写入会根据append的值判断是否会在File中原有内容之后追加写入的内容。 FileOutputStream(FileDescriptor fdObj) 创建一个向指定文件描述符处写入数据的输入流fos、该文件描述符表示一个到文件系统中的某个实际文件的现有连接。
void close(); 关闭当前流、释放与此流有关的所有资源 void finalize(); 清理到文件的连接、并确保不再使用此流的时候关闭此流 FileChannel getChannel(); 返回与此文件流唯一有关的FileChannel FileDescriptor getFD(); 返回与此流有关的文件描述符 void write(byte b); 将一个字节写入到fos中 void write(byte[] b); 将一个字节数组写入到fos中 void write(byte[] b,int off, int len) 将字节数组b的一部分写入到fos中
public class FileOutputStream extends OutputStream { private FileDescriptor fd; private FileChannel channel= null; //用于标识写入的内容是追加还是替换。 private boolean append = false; /** *根据具体文件名打开一个文件字节输出流。name中表示的文件夹必须存在、文件可以自动创建 *比如String name = "D:\\directory1\\directory2\\fos.txt"; 则在D盘中必须有个目录 *名为directory1且还有下一层目录directory2。至于是否有fos.txt没有关系、有:fos直接写入 *没有:fos写入之前会创建一个新的。 *实质是调用下面的 public FileOutputStream(File file)来创建fos; */ public FileOutputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null, false); } /** * 同上面一个构造方法、只是比上面一个构造方法多了一个参数 boolean append、 * 1)若append 值为 true 则表示保留原文件中数据、在其后面添加新的数据。 * 2)若append 值为false 如果文件存在、则清空文件内容、如果文件不存在则创建新的。 * 也可看出、上面一个构造方法就是默认append为false的这个构造方法的特例。 * 实质是调用下面的public FileOutputStream(File file, boolean append)来创建fos; */ public FileOutputStream(String name, boolean append) throws FileNotFoundException { this(name != null ? new File(name) : null, append); } /** * 根据指定的File来创建一个到此文件的文件字节输出流。 */ public FileOutputStream(File file) throws FileNotFoundException { this(file, false); } /** * 根据指定的File来创建一个到此文件的文件字节输出流。可以指定是追加还是替换。 */ public FileOutputStream(File file, boolean append) throws FileNotFoundException { String name = (file != null ? file.getPath() : null); SecurityManager security = System.getSecurityManager(); if (security != null) { security.checkWrite(name); } if (name == null) { throw new NullPointerException(); } fd = new FileDescriptor(); this.append = append; //调用底层方法真正的建立到指定文件的字节输出流。 if (append) { openAppend(name); } else { open(name); } } /** * 通过文件描述符打开一个到实际文件的文件字节输出流。 */ public FileOutputStream(FileDescriptor fdObj) { SecurityManager security = System.getSecurityManager(); if (fdObj == null) { throw new NullPointerException(); } if (security != null) { security.checkWrite(fdObj); } fd = fdObj; } /** * 底层————打开一个准备写入字节的文件、模式为替换。 */ private native void open(String name) throws FileNotFoundException; /** * 底层————打开一个准备写入字节的文件、模式为追加。 */ private native void openAppend(String name) throws FileNotFoundException; /** * 底层————向文件中写入一个字节 */ public native void write(int b) throws IOException; /** * 底层————将一个起始下标为 off 长度为len的字节数组 b 文件中 */ private native void writeBytes(byte b[], int off, int len) throws IOException; /** *底层————将整个字节数组 b 文件中 */ public void write(byte b[]) throws IOException { writeBytes(b, 0, b.length); } /** * 调用底层writerBytes(byte b, int off, int len)将一个起始下标为 off 长度为len的字节数组 b 文件中 */ public void write(byte b[], int off, int len) throws IOException { writeBytes(b, off, len); } /** * 关闭此流、并释放所有与此流有关的资源。同时将建立在此流基础上的FileChannel也关闭。 */ public void close() throws IOException { if (channel != null) channel.close(); close0(); } /** * 获得此流对应的文件描述符。 */ public final FileDescriptor getFD() throws IOException { if (fd != null) return fd; throw new IOException(); } /** * 获得与此流相关的FileChannel。 public FileChannel getChannel() { synchronized (this) { if (channel == null) channel = FileChannelImpl.open(fd, false, true, this, append); return channel; } } */ /** *确保此流不再被引用时、将此流中的数据flush到指定文件中并且关闭此流。 */ @SuppressWarnings("static-access") protected void finalize() throws IOException { if (fd != null) { if (fd == fd.out || fd == fd.err) { //调用父类OutputStream的flush(); flush(); } else { close(); } } } private native void close0() throws IOException; private static native void initIDs(); static { initIDs(); } }
package com.chy.io.original.test; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class FileStreamTest { private static final byte[] byteArray = { 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A }; public static void testFileOutputStream() throws IOException{ //创建一个模式为替换(即在写入前会将文件中的所有内容清空、如果文件存在的话)的文件字节输出流。 FileOutputStream fosAppendFalse = new FileOutputStream(new File("D:\\fosAppendFalse.txt")); /** * 方法说明: * 1)写入一个字节; * 2)写入一个换行符; * 3)写入byteArray的前五个字节; * 4)写入换行符; * 5)写入整个byteArray; * 6)关闭流释放资源; * 注意:你写入文件的字节都会被底层按照系统默认编码转换成字符存到文件中、 * 当使用文件字节输出来的时候会被反转成字节!!!自己可以动手试试! */ fosAppendFalse.write(byteArray[0]); fosAppendFalse.write("\r\n".getBytes()); fosAppendFalse.write(byteArray, 0, 5); fosAppendFalse.write("\r\n".getBytes()); fosAppendFalse.write(byteArray); fosAppendFalse.close(); /** * 运行结果: * 在D盘下生成一个名为fosAppendFalse.txt的文本文件 * 无论运行多少次fosAppendFalse.txt只有: * a * abcde * abcdefghijklmnopqrstuvwxyz * 这些内容。 */ //创建一个模式为追加的文件字节输出流 FileOutputStream fosAppendTure = new FileOutputStream("D:\\fosAppendTure.txt", true); /** * 方法说明: * 1)向指定文件追加一个字节; * 2)向指定文件追加一个换行符; * 3)向指定文件追加byteArray的前五个字节; * 4)向指定文件追加换行符; * 5)向指定文件追加整个byteArray; * 6)关闭流释放资源; */ fosAppendTure.write(byteArray[0]); fosAppendTure.write("\r\n".getBytes()); fosAppendTure.write(byteArray, 0, 5); fosAppendTure.write("\r\n".getBytes()); fosAppendTure.write(byteArray); fosAppendTure.write("\r\n".getBytes()); fosAppendTure.close(); /** * 运行结果: * 在D盘下生成一个名为fosAppendFalse.txt的文本文件 * 每运行一次fosAppendFalse.txt中都会追加下面内容: * a * abcde * abcdefghijklmnopqrstuvwxyz * 这些内容。注意换行符对结构的影响。 */ } public static void testFileInputSteram() throws IOException{ //上面的FileOutputStream为了测试、写入文件的东西比较凌乱、下面为了方便观察结果、从新弄一个:将a-z写入文件fis.txt中、将fis.txt作为文件字节输入流的源。 FileOutputStream fos = new FileOutputStream("D:\\fis.txt"); fos.write(byteArray); //创建fis.txt的输入流 FileInputStream fis = new FileInputStream(new File("D:\\fis.txt")); /** * 简单说明: * 下面写的有些乱、主要就是测试available()、read()、read(byte[] b ,int off, int len)、read(byte[] b) * 四个方法的使用、以及调用同一个流的后三个方法进行读取会有什么效果。 */ int availableCount = fis.available(); System.out.println(availableCount); int n = 0; byte[] buf = new byte[availableCount]; n = fis.read(); System.out.print("read()读取的字节 : " + byteToString((byte)n)); System.out.println("\r\n----------------------------------"); fis.skip(5); availableCount = fis.available(); System.out.println(availableCount); n = fis.read(buf, 0, availableCount-1); System.out.println("read(byte[] b,int off, int len)读取的字节数: " +n); printByteValue(buf); System.out.println("\r\n----------------------------------"); availableCount = fis.available(); System.out.println(availableCount); byte[] buf2 = new byte[1024]; n = fis.read(buf2); System.out.println("read(byte[] b)读取的字节数: " +n); printByteValue(buf2); System.out.println("\r\n----------------------------------"); availableCount = fis.available(); System.out.println(availableCount); byte[] buf3 = new byte[1024]; n = fis.read(buf3, 0, 5); System.out.println("read(byte[] b,int off, int len): " +n); printByteValue(buf3); System.out.println("\r\n----------------------------------"); System.out.println("是否支持mark?" + fis.markSupported()); /*System.out.println(fis.available()); fis.reset();*/ /** * 结果说明: * 1)result : 26 //是实际文件D:\fis.txt中有多少有效的可读取的字节数; * 2)result : read()读取的字节 : a //实际文件D:\fis.txt中第一个字节、并且标记读取位置的偏移量+1、偏移量指的是下一个被读取的字节位置; * 3)result : 20 //实际文件D:\fis.txt中剩余有效可读取字节数、原因1、read()读取一个字节、偏移量+1、2、fis.skip(5)使得偏移量 +5 即是从第七个开始读取。 * 4)result : read(byte[] b,int off, int len)读取的字节数: 19 //读取的字节数取决于两个方面:1、传入的参数中的len、2、文件中剩余的有效字节数、两则取最小的那个; * 5)result : g h i j k l m n o p q r s t u v w x y //将取出的24个字节打印出来、并且标记读取位置的 偏移量 = 偏移量+实际读取字节数; * 6)result : read(byte[] b)读取的字节数: 1 //同结果4的说明 * 7)result : z //同结果5的说明 * 8)result : 0 //实际文件中有效可读取字节为0 、即读取到文件最后了。偏移量 = available; * 9)result : read(byte[] b,int off, int len): -1 //读取到文件结尾、返回-1作为标志; * 10) result : 是否支持mark?false //表示文件输入流不支持mark 所以后面的mark(10)不会有任何效果、并且如果调用父类的 reset()会报错 :mark/reset not supported */ } private static void printByteValue(byte[] buf) { for(byte b : buf){ if(b != 0){ System.out.print(byteToString(b) + " "); } } } private static String byteToString(byte b){ byte[] bAray = {b}; return new String (bAray); } public static void copyFile() throws IOException{ //结合使用、实现复制文件、这里没有对流油任何装饰、后面再说。 FileInputStream fis = new FileInputStream(new File("D:\\fosAppendFalse.txt")); FileOutputStream fosAppendFalse = new FileOutputStream(new File("D:\\CopeOfFosAppendFalse.txt")); byte[] b = new byte[1024]; int n = 0; while((n = fis.read(b)) != -1){ fosAppendFalse.write(b, 0, n); } } /** * 测试的时候最好一个一个方法的测试、在动手自己修改、看与自己想的结果是否一样? * 多试、加深理解! * @param args * @throws IOException */ public static void main(String[] args) throws IOException { //testFileOutputStream(); testFileInputSteram(); //copyFile(); } }
FileInputStream(fis)、FileOutputStream(fos)、本质是和文件打交道、两者在构造方法中都会通过传入的File对象或者表示File对象的路径名来创建实例、创建实
例的过程中会根据不同的需求打开到指定文件对象的连接(核心是open(String filename)、openAppend(String filename)追加模式)方法、并且一般情况下类中的对单个字节
操作的方法read()或者write()方法是读写的核心、但是此两类中不是、他们的读/写字节、读/写字节数组、是分别实现的、都是用系统的方法实现的、其中
read(byte[] b, int off, int len)和write(byte[] b, intoff, int len)是调用自己本身私有方法readBytes(byte[] b, int off, int len) 和write(byte[] b, intoff, int len)来实现的、而非循环调用read()、write()。