|
今天学习FileInputStream与FileOutputStream。 FileInputStream是文件输入流,用于从文件系统中的某个文件中获得输入字节。FileInputStream用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用FileReader。 FileOutputStream是文件输出流,用于将数据写入File或FileDescriptor的输出流。FileOutputStream用于写入诸如图像数据之类的原始字节的流。要写入字符流,请考虑使用FileWriter。 FileInputStream先学习下FileInputStream的源码。 public class FileInputStream extends InputStream{ //FileInputStream的文件描述符 private final FileDescriptor fd; //引用文件的路径 private final String path; //文件通道 private FileChannel channel = null; //关闭锁 private final Object closeLock = new Object(); //标识流是否关闭 private volatile boolean closed = false; /** * 通过打开一个到实际文件的连接来创建一个FileInputStream,该文件通过文件系统中的路径名name指定。创建一个新FileDescriptor对象来表示此文件连接。 * * 首先,如果有安全管理器,则用name作为参数调用其checkRead方法。 * * @param name 文件系统中的路径名 * @exception FileNotFoundException 如果该文件不存在,或者它是一个目录,而不是一个常规文件, * 抑或因为其他某些原因而无法打开进行读取。 */ public FileInputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null); } /** * 通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的File对象file指定。 * * 参考FileInputStream(String name)的注释 */ public FileInputStream(File file) throws FileNotFoundException { //获取文件名 String name = (file != null ? file.getPath() : null); //获取安全管理器 SecurityManager security = System.getSecurityManager(); if (security != null) { //如果调用线程没有访问指定文件的权限,抛出SecurityException security.checkRead(name); } if (name == null) { throw new NullPointerException(); } if (file.isInvalid()) { throw new FileNotFoundException("Invalid file path"); } //fd表示此文件连接 fd = new FileDescriptor(); fd.attach(this); path = name; //打开文件 open(name); } /** * 通过使用文件描述符fdObj创建一个FileInputStream,该文件描述符表示到文件系统中某个实际文件的现有连接。 */ public FileInputStream(FileDescriptor fdObj) { SecurityManager security = System.getSecurityManager(); if (fdObj == null) { throw new NullPointerException(); } if (security != null) { security.checkRead(fdObj); } fd = fdObj; path = null; fd.attach(this); } /* * native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。 * Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。 * JNI是Java本机接口(Java Native Interface),是一个本机编程接口, * 它是Java软件开发工具箱(Java Software Development Kit,SDK)的一部分。 * JNI允许Java代码使用以其他语言编写的代码和代码库。 * Invocation API(JNI的一部分)可以用来将Java虚拟机(JVM)嵌入到本机应用程序中, * 从而允许程序员从本机代码内部调用Java代码。 * 所以想要了解open0方法的具体实现只能去查看JVM源码了。 */ private native void open0(String name) throws FileNotFoundException; // wrap native call to allow instrumentation /** * Opens the specified file for reading. * @param name the name of the file */ private void open(String name) throws FileNotFoundException { open0(name); } /** * 从此输入流中读取一个数据字节。 */ public int read() throws IOException { return read0(); } /** * 参考open0(String name) */ private native int read0() throws IOException; /** * 从此输入流中将最多len个字节的数据读入一个起始偏移量为off的byte数组中。 * * 参考open0(String name) */ private native int readBytes(byte b[], int off, int len) throws IOException; /** * 从此输入流中将最多b.length个字节的数据读入一个起始偏移量为0的byte数组中。 */ public int read(byte b[]) throws IOException { return readBytes(b, 0, b.length); } /** * 从此输入流中将最多len个字节的数据读入一个起始偏移量为off的byte数组中。 */ public int read(byte b[], int off, int len) throws IOException { return readBytes(b, off, len); } /** * 从输入流中跳过并丢弃n个字节的数据。 * * 参考open0(String name) */ public native long skip(long n) throws IOException; /** * 返回下一次对此输入流调用的方法可以不受阻塞地从此输入流读取(或跳过)的估计剩余字节数。 * 参考open0(String name) */ public native int available() throws IOException; /** * 关闭此文件输入流并释放与此流有关的所有系统资源。 * 如果此流有一个与之关联的通道,则关闭该通道。 */ public void close() throws IOException { synchronized (closeLock) { if (closed) { return; } closed = true; } //如果此流有一个与之关联的通道,则关闭该通道。 if (channel != null) { channel.close(); } fd.closeAll(new Closeable() { public void close() throws IOException { close0(); } }); } /** * 返回表示到文件系统中实际文件的连接的FileDescriptor对象,该文件系统正被此FileInputStream使用。 */ 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, path, true, false, this); } return channel; } } //参考open0(String name) private static native void initIDs(); //参考open0(String name) private native void close0() throws IOException; static { initIDs(); } /** * 确保在不再引用文件输入流时调用其close方法。 */ protected void finalize() throws IOException { if ((fd != null) && (fd != FileDescriptor.in)) { 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
- 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
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
FileOutputStreampublic class FileOutputStream extends OutputStream{ /** * 文件描述符 */ private final FileDescriptor fd; /** * 标识添加还是替换文件的内容 */ private final boolean append; /** * 关联的通道 * 懒加载 */ private FileChannel channel; //引用文件的路径 private final String path; //锁 private final Object closeLock = new Object(); //标识流是否关闭 private volatile boolean closed = false; /** * 创建一个向具有指定名称的文件中写入数据的输出文件流。 * 创建一个新FileDescriptor对象来表示此文件连接。 * 首先,如果有安全管理器,则用 name 作为参数调用checkWrite方法。 * * @param name 文件路径 * @exception FileNotFoundException 如果文件存在,但它是一个目录,而不是一个常规文件;或者该文件不存在,但无法创建它;抑或因为其他某些原因而无法打开它 * @exception SecurityException 如果存在安全管理器,且其checkWrite方法拒绝对文件进行写入访问 */ public FileOutputStream(String name) throws FileNotFoundException { this(name != null ? new File(name) : null, false); } /** * 创建一个向具有指定name的文件中写入数据的输出文件流。如果第二个参数为append为true,则将字节写入文件末尾处,而不是写入文件开始处。 */ public FileOutputStream(String name, boolean append) throws FileNotFoundException { this(name != null ? new File(name) : null, append); } /** * 创建一个向指定File对象表示的文件中写入数据的文件输出流。创建一个新FileDescriptor对象来表示此文件连接。 */ public FileOutputStream(File file) throws FileNotFoundException { this(file, false); } /** * 创建一个向指定File对象表示的文件中写入数据的文件输出流。如果第二个参数append为true,则将字节写入文件末尾处,而不是写入文件开始处。 */ 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(); } if (file.isInvalid()) { throw new FileNotFoundException("Invalid file path"); } this.fd = new FileDescriptor(); fd.attach(this); this.append = append; this.path = name; open(name, append); } /** * 创建一个向指定文件描述符处写入数据的输出文件流,该文件描述符表示一个到文件系统中的某个实际文件的现有连接。 */ public FileOutputStream(FileDescriptor fdObj) { SecurityManager security = System.getSecurityManager(); if (fdObj == null) { throw new NullPointerException(); } if (security != null) { security.checkWrite(fdObj); } this.fd = fdObj; this.append = false; this.path = null; fd.attach(this); } //参考FileInputStream.open0(String name) private native void open0(String name, boolean append) throws FileNotFoundException; private void open(String name, boolean append) throws FileNotFoundException { open0(name, append); } /** * 将指定字节写入此文件输出流。 * 参考FileInputStream.open0(String name) */ private native void write(int b, boolean append) throws IOException; /** * 将指定字节写入此文件输出流。 */ public void write(int b) throws IOException { write(b, append); } /** * 将b.length个字节从指定byte数组写入此文件输出流中 * 参考FileInputStream.open0(String name) */ private native void writeBytes(byte b[], int off, int len, boolean append) throws IOException; /** * 将指定byte数组中从偏移量off开始的len个字节写入此文件输出流。 */ public void write(byte b[]) throws IOException { writeBytes(b, 0, b.length, append); } /** * 将指定byte数组中从偏移量off开始的len个字节写入此文件输出流。 */ public void write(byte b[], int off, int len) throws IOException { writeBytes(b, off, len, append); } /** * 关闭此文件输出流并释放与此流有关的所有系统资源。此文件输出流不能再用于写入字节。 * 如果此流有一个与之关联的通道,则关闭该通道。 */ public void close() throws IOException { synchronized (closeLock) { if (closed) { return; } closed = true; } if (channel != null) { channel.close(); } fd.closeAll(new Closeable() { public void close() throws IOException { 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, path, false, true, append, this); } return channel; } } /** * 清理到文件的连接,并确保在不再引用此文件输出流时调用此流的close方法。 */ protected void finalize() throws IOException { if (fd != null) { if (fd == FileDescriptor.out || fd == FileDescriptor.err) { flush(); } else { close(); } } } ////参考FileInputStream.open0(String name) private native void close0() throws IOException; //参考FileInputStream.open0(String name) private static native void initIDs(); static { initIDs(); }}- 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
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
demoFileInputStream与FileOutputStream与用于操作诸如图像数据之类的原始字节流。要操作字符流,请考虑使用FileReader与FileWriter。 import java.io.File;import java.io.FileDescriptor;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;/** * FileInputStream 和FileOutputStream的API测试类 */public class FileStreamTest { private static final String FileName = "fileStream.txt"; public static void main(String[] args) { testFileOutputStream(); testFileInputStream(); } /** * FileOutputStream的API测试类 */ private static void testFileOutputStream() { try { // FileOutputStream fos = new FileOutputStream("fileStream.txt"); // 创建文件对应File对象 File file = new File(FileName); // 创建文件对应的FileOutputStream对象,默认是覆盖模式 FileOutputStream fos = new FileOutputStream(file); fos.write(new byte[] { 0x61, 0x62, 0x63, 0x64 });// abcd fos.write(new byte[] { 0x65, 0x66, 0x67, 0x68 });// efgh // 创建文件对应的FileOutputStream对象,默认是覆盖模式 FileOutputStream fos2 = new FileOutputStream(file); fos2.write(new byte[] { 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78 });// qrstuvwx // 创建文件对应的FileOutputStream对象,模式为追加模式 FileOutputStream fos3 = new FileOutputStream(file, true); fos3.write(new byte[] { 0x51, 0x52, 0x53, 0x54 });// QRST fos.close(); fos2.close(); fos3.close(); } catch (IOException e) { e.printStackTrace(); } } /** * FileInputStream的API测试函数 */ private static void testFileInputStream() { try { File file = new File(FileName); FileInputStream fis = new FileInputStream(file); FileDescriptor fd = fis.getFD(); // 根据文件描述符创建FileInputStream对象 FileInputStream fis2 = new FileInputStream(fd); // 测试read() System.out.println("使用read()读取一个字节:" + (char) fis.read()); System.out.println("使用available()获取当前可用字节数:" + fis.available()); // 测试read(byte[] b,int off,int len) byte[] b = new byte[5]; fis.read(b, 0, b.length); System.out.println("使用readread(byte[] b,int off,int len)读取5个字节到b中:" + new String(b)); System.out.println("使用available()获取当前可用字节数:" + fis.available()); // 测试skip(long byteCount) System.out.printf("使用skip(long n)跳过%s个字节\n", fis.skip(1)); System.out.println("使用available()获取当前可用字节数:" + fis.available()); fis.close(); fis2.close(); } catch (IOException e) { e.printStackTrace(); } }}- 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
testFileOutputStream执行后,fileStream.txt中的内容为 qrstuvwxQRSTfos.write(new byte[] { 0x61, 0x62, 0x63, 0x64 });// abcdfos.write(new byte[] { 0x65, 0x66, 0x67, 0x68 });// efgh执行后,文件内容为abcdefgh FileOutputStream fos2 = new FileOutputStream(file);执行后,文件内容为空 fos2.write(new byte[] { 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78 });// qrstuvwx执行后,文件内容为qrstuvwx FileOutputStream fos3 = new FileOutputStream(file, true);执行后,文件内容为qrstuvwx fos3.write(new byte[] { 0x51, 0x52, 0x53, 0x54 });// QRST执行后,文件内容为qrstuvwxQRST testFileInputStream执行后后控制台打印内容为: 使用read()读取一个字节:q使用available()获取当前可用字节数:11使用readread(byte[] b,int off,int len)读取5个字节到b中:rstuv使用available()获取当前可用字节数:6使用skip(long n)跳过1个字节使用available()获取当前可用字节数:5总结- FileInputStream是文件输入流,用于从文件系统中的某个文件中获得输入字节。FileInputStream用于读取诸如图像数据之类的原始字节流。要读取字符流,请考虑使用FileReader。
- FileOutputStream是文件输出流,用于将数据写入File或FileDescriptor的输出流。FileOutputStream用于写入诸如图像数据之类的原始字节的流。要写入字符流,请考虑使用FileWriter。
- Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。
- FileInputStream不支持mark方法与set方法。
|