客官,小板凳坐好,开始啦!
要聊FileInputStream
对象,不妨先说说AutoCloseable
吧,也算是个新成员,它诞生于JDK1.7的时代。
/**
* @since 1.7
*/
public interface AutoCloseable {
void close() throws Exception;
}
接口AutoCloseable
是JDK1.7
版本开始的,它的作用是提供另一种资源关闭
的方式。
自定义一个测试类,实现AutoCloseable
接口:
public class TestAutoCloseable implements AutoCloseable {
public void service() {
System.out.println("TestAutoCloseable实例的service方法被调用");
}
@Override
public void close() throws Exception {
System.out.println("TestAutoCloseable实例的close方法由jvm自动调用");
}
}
close()方法是重写的AutoCloseable
接口。service()方法是TestAutoCloseable
类中的一个普通成员方法。
public class Test {
public static void main(String[] args) {
try (
TestAutoCloseable test1 = new TestAutoCloseable();
TestAutoCloseable test2 = new TestAutoCloseable()
) {
test1.service();
test2.service();
} catch (Exception exception) {
System.out.println("发生异常");
} finally {
System.out.println("善后处理");
}
}
}
写一个测试类进行测试:
TestAutoCloseable实例的service方法被调用
TestAutoCloseable实例的service方法被调用
TestAutoCloseable实例的close方法由jvm自动调用
TestAutoCloseable实例的close方法由jvm自动调用
善后处理
还记得以前,你是如何创建一个FileInputStream
对象的吗?
public class Test {
public static void main(String[] args) {
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream("");
} catch (FileNotFoundException e) {
// TODO
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
// TODO
} finally {
// TODO
}
}
}
}
}
为了关闭FileInputStream
资源,我们不得不在try-catch-finally
块外部声明FileInputStream inputStream
不然在finally
中无法获取inputStream
引用,我说的没错吧?
有了AutoCloseable
接口,我们可以将资源对象的创建放入try
后的(...)
中,多个资源可以用分号;
分隔。当try中的代码执行完后,由JVM
自动调用重写的close()
方法。这样资源就不用再手动的关闭了!
假设,在try
代码块中发生了异常,如何处理呢?还能自动关闭吗?
public class Test {
public static void main(String[] args) {
try (
TestAutoCloseable test1 = new TestAutoCloseable();
TestAutoCloseable test2 = new TestAutoCloseable()
) {
test1.service();
int i = 1 / 0;
test2.service();
} catch (Exception exception) {
System.out.println("发生异常");
} finally {
System.out.println("善后处理");
}
}
}
测试结果:
TestAutoCloseable实例的service方法被调用
TestAutoCloseable实例的close方法由jvm自动调用
TestAutoCloseable实例的close方法由jvm自动调用
发生异常
善后处理
从结果中,我们可以看出:
无论是否发生异常,一旦跳出try(...){代码块}
,那么jvm
就会在跳出之前自动调用重写的close()
方法。
好,接下来进入正题:聊聊FileInputStream
文件输入流的二三事。
明白了AutoCloseable
接口,那么你对FileInputStream
对象应该也有了一点了解,因为FileInputStream
对象实现了AutoCloseable
接口,所以可以用新的资源关闭的方式。
FileInputStream有3个构造方法:
FileInputStream(String name); // ①
FileInputStream(File file); // ②
FileInputStream(FileDescriptor fdObj); // ③
其中,①、②两种构造器,其实是属于同一种构造器,都是通过传入一个File
对象来构造的。第①种构造器,在FileInputStream
内部会构造一个File
对象的。
由于FileInputStream也实现了AutoCloseable
接口,所以可以用try-catch
新语法来管理资源的关闭。
public class Test {
private static final String filePath = "C:\\Users\\Disney\\Desktop\\test.txt";
public static void main(String[] args) {
try (FileInputStream inputStream = new FileInputStream(filePath)) {
// TODO
} catch (Exception exception) {
System.out.println("发生IO异常");
}
}
}
以上代码就是FileInputStream
对象创建的大概样子。
先介绍两个简单的API:int available()
和long skip(long n)
:
-
available
可以从该输入流中读取(或跳过)而不阻塞的剩余字节数的估计。可以理解为剩余字节数统计。如果文件流刚建立还有开始读操作,那么调用
available
方法,返回值就是文件的字节大小。
public class Test {
public static void main(String[] args) {
try (FileInputStream inputStream = new FileInputStream(filePath)) {
int typeCount = inputStream.available();
System.out.println("typeCount = " + typeCount);
} catch (Exception exception) {
System.out.println("发生IO异常");
}
}
}
typeCount = 14
-
skip
跳过指定的
n
个字节数。比如,当读到第20个字节,skip(10)之后,就开始从第31个字节开始读了。当然,文件要有31个字节以上,不然实际上有多少个字节就跳过多少个字节了。返回值就是实际跳过的字节数。如果long n
是负数就是往前跳喽,如果流不支持往前跳就会抛出IO异常。
public class Test {
public static void main(String[] args) {
try (FileInputStream inputStream = new FileInputStream(filePath)) {
int data = inputStream.read();
System.out.println("data = " + data);
data = inputStream.read();
System.out.println("data = " + data);
System.out.println("往前跳2个字节 = " + inputStream.skip(-2));
data = inputStream.read();
System.out.println("data = " + data);
data = inputStream.read();
System.out.println("data = " + data);
} catch (Exception exception) {
System.out.println("发生IO异常");
}
}
}
data = 65
data = 66
往前跳2个字节 = -2
data = 65
data = 66
-
markSupported
由于
FileInputStream
并没有重写InputStream
的markSupported
方法,而方法的默认返回值是false
,所以也就不支持mark
和reset
方法。
public boolean markSupported() {
return false;
}
输入流最重要的方法当然是read
方法,FileInputStream
有3中重载的read
方法,我们先看第①种:
public class Test {
public static void main(String[] args) {
try (FileInputStream inputStream = new FileInputStream(filePath)) {
int read;
while ((read = inputStream.read()) != -1) {
System.out.println(read);
}
} catch (Exception exception) {
System.out.println("发生IO异常");
}
}
}
没有输入参数,返回值是读取的数据,即一个字节。如果返回值是-1
,那么代表文件读取到了末尾位置。
为什么要用int
作为返回值呢?用byte
不就可以了吗?
原因是:字节输入流可以操作任意类型的文件,如图片、音频、视频等,这些文件底层都是二进制存储的;如果用byte
作为返回值类型的话,当读取到11111111
时,计算机就认为读到了-1
.因为-1
的原码是10000001
,反码是11111110
,补码是11111111
。计算机存的就是补码11111111
,所以一旦读到-1
就结束了,但是事实上并没有结束哇!因为11111111
是文件的数据,不是表示文件的结束。因此,改用int
类型接收,前面再加3×8=24
个0,写的时候write
方法会去掉这24个0,就可以获取到-1
这个数据了(如果看不明白,也无所谓,不影响使用)
。
接下来,我们看第②种read
方法:
public class Test {
public static void main(String[] args) {
try (FileInputStream inputStream = new FileInputStream(filePath)) {
byte[] container = new byte[4];
int read;
while ((read = inputStream.read(container)) != -1) {
System.out.println("本次读取的字节数 = " + read);
System.out.println(Arrays.toString(container));
}
} catch (Exception exception) {
System.out.println("发生IO异常");
}
}
}
输入参数是一个字节数组,用于存放读取的数据。返回值是实际读取的字节数,当值为-1
时,表示文件结束。
本次读取的字节数 = 4
[65, 66, 67, 68]
本次读取的字节数 = 4
[69, 70, 71, 72]
本次读取的字节数 = 4
[73, 74, 75, 76]
本次读取的字节数 = 2
[77, 78, 75, 76]
从运行结果中,可以看出:
每次读取我们定义的4
个字节的数据,在最后一次读取时,仅剩2
个字节了,所以字节数组container
,仅前两个元素被修改了,最后的75, 76
没有变动,因此,我们在读取数据时,一定要根据返回值int
来判断实际读取了多少个字节。
接下来,我们再看最后一个read
方法的使用:
public class Test {
public static void main(String[] args) {
try (FileInputStream inputStream = new FileInputStream(filePath)) {
byte[] container = new byte[4];
int len = inputStream.read(container, 2, 1);
System.out.println("本次读取的字节数 = " + len);
System.out.println(Arrays.toString(container));
} catch (Exception exception) {
System.out.println("发生IO异常");
}
}
}
运行结果:
本次读取的字节数 = 1
[0, 0, 65, 0]
可以看出:
第①个参数:依然是我们用于接收数据的容器。
第②个参数:指从容器的第几个位置开始存放数据,坐标从0开始。如果偏移量超过了数组最大索引值,抛异常。
第③个参数:每次读取多少个字节放入我们定义的容器container
中。如果容器放不下,抛异常。
返回值:依然是,实际上读取的字节数量。
关于文件描述符相关的内容,暂不做解释,一般使用较少。
FileDescriptor fd = inputStream.getFD();
FileChannel channel = inputStream.getChannel();
以上就是关于FileInputStream
的全部内容!不知客官欣赏的是否尽兴
,评论区聊聊?