客官,小板凳坐好,开始啦!

要聊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的全部内容!不知客官欣赏的是否尽兴,评论区聊聊?
本文主要介绍了JDK1.7时代诞生的接口,它可实现资源自动关闭,无需手动操作。还阐述了FileInputStream的构造方法、相关API,如available、skip等,以及其读取方法的三种重载形式,包括返回值含义和使用注意事项。
398

被折叠的 条评论
为什么被折叠?



