1
Chp14 I/O框架
在程序运行的过程当中,JVM 的内存中必然会存放很多数据,包括基本类型和对象类
型。但是当程序结束,JVM 关闭的时候,这些数据必然会随之消失。我们可能希望通过某
种方式,让这些数据能够保存下来,以备在此使用。因此我们会把数据存入文件,或通过网
络发送出去,或存入数据库。反之,我们当然也需要用某种方式,把保存的数据重新读回
JVM。这些,都涉及到 JVM与外部进行数据交换。
将 JVM中的数据写出去,我们称为数据的输出。反之,将数据读入 JVM,我们称之为
数据的输入。因此,Java中解决这部分问
题
快递公司问题件快递公司问题件货款处理关于圆的周长面积重点题型关于解方程组的题及答案关于南海问题
的 API 被称为 I/O。(I 是英语 Input 的首字母,
表
关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf
示输入,O是英语 Output的首字母,表示输出)
1 Java I/O概览
1.1 文件系统和 File 类
首先介绍一下 Java中的 File类。这个类在 java. io包中,对于一个 File对象来说,它能
够代表硬盘上的一个文件或者文件夹。在这句描述中,有两个要点值得注意:
1、File对象不仅能够代表一个文件,还能够代表一个文件夹。
2、File对象是“代表”一个文件或者文件夹。怎么来理解“代表”两个字呢?
首先,当我们创建一个 File对象时,指的是在内存中分配了一块数据区域,也就是说,
创建一个 File对象并不会在系统中真的创建一个文件或者文件夹,而只是在 JVM的内存中
创建了一个对象。通过这个对象能够跟磁盘打交道,从而操作底层的文件。
其次,既然 File 对象是“代表”磁盘中的文件,因此并不要求 File 对象所代表的文件
或者文件夹在磁盘中一定存在。也就是说,File对象所代表的文件或者文件夹可能不存在。
下面我们来结合 JDK的文档,来看一下 File类中都有哪些值得注意的
方法
快递客服问题件处理详细方法山木方法pdf计算方法pdf华与华方法下载八字理论方法下载
,以及如何
使用 File类。
首先是 File类的构造方法。File对象有四个构造方法,其中三个构造方法比较常用。罗
列如下:
File(String pathname) : 利用字符串作为参数,表示一个路径名。用来创建一个代表给定
参数的文件或者文件夹。
File(String parent, String child) : parent 表示父目录,child表示在 parent目录下的路径。
这种写法用来代表名字为 parent/child的文件或者文件夹。
File(File parent, String child) : 同样用 parent表示父目录,只不过这个 parent是用 File类
型来表示。
需要注意的是,在创建 File 对象的时候,需要指定文件的路径,指定的时候,可以用
绝对路径,也可以用相对路径。
另外,要注意路径分隔符的问题。在 Windows 中,路径分隔符使用的是反斜杠“\”,
而在 Java中反斜杠是用来转义的,因此如果要使用反斜杠的话,必须使用“\\”来表示一个
反斜杠。
例如,如果要表示 D盘的 abc 目录,则在 Java中的字符串应当这样写:
"D:\\abc"
另外,也可以用一个正斜杠用来做Windows 中的路径分隔,这样就不需要转义。因此,
也可以使用下面的表示方式:
2
"D:/abc"
这两种路径的写法都是正确的。
其次是 File类中的一些基本操作。
createNewFile() : 这个方法可以用来创建一个新文件。需要注意的是,如果这个文件在
系统中已经存在,createNewFile方法不会覆盖原有文件。
mkdir() / mkdirs() : 这两个方法都可以用来创建文件夹。所不同的是,mkdir 只能创建一
层文件夹,而 mkdirs 能够创建多层文件夹。
例如,如果当前目录下已经存在一个 abc 目录,则下面的代码能够成功创建一个目录:
File dir = new File("abc/def");
dir.mkdir();
由于已经有了 abc目录,所以需要创建的仅仅是一层def目录而已。此时可以调用mkdir()
方法来创建目录。
而如果当前目录下不存在 abc 目录,创建时需要先创建一个 abc 目录,然后创建 def 目
录,属于创建两层目录。因此,这就需要调用 mkdirs()方法来创建多层文件夹。
delete() : 这个方法能够删除 File所代表的文件或者文件夹。
deleteOnExit() : 这个方法也能用来删除文件或者文件夹。所不同的是,delete()方法被调
用时,这个文件或者文件夹会被立刻删除,而 deleteOnExit()方法被调用后,文件或者文件
夹并不会立刻被删除,而会等到程序退出以后再删除。
getPath() : 返回路径
getName() : 返回文件名
getParent() : 返回所在的文件夹
对于上面这三个方法,我们用一段代码来说明。我们创建一个 File 对象,表示当前目
录的 abc 目录下的 test.txt文件,并分别调用上述三个方法。代码如下:
import java.io.*;
public class TestFile{
public static void main(String args[]){
File f = new File("abc/test.txt");
System.out.println(f.getPath());
System.out.println(f.getParent());
System.out.println(f.getName());
}
}
输出结果如下:
可以看出,getPath()返回的是我们给出的路径,getParent()返回的所在的父文件夹,而
getName()返回的是文件名。
3
getAbsolutePath() : 返回绝对路径。
getCanonicalPath() : 返回规格化以后的路径。需要注意的是这个方法会抛出一个异常。
我们同样可以使用一个程序来说明这两个方法。代码如下:
import java.io.*;
public class TestFile{
public static void main(String args[]) throws Exception{
File f = new File("./abc/test.txt");
System.out.println(f.getAbsolutePath());
System.out.println(f.getCanonicalPath());
}
}
上述代码运行结果如下:
getAbsolutePath和 getCanonicalPath都会返回绝对路径,只不过,如果在路径中存在“.”
以及“..”这两个符号的话,则 getCanonicalPath 会修正路径,而不让返回值中存在“.”和
“..”这两个符号。
下面是一些判断:
exists() : 这个方法用来判断 File对象表示的文件或者文件夹是否存在
isFile() : 判断 File对象是否代表的是一个文件
isDirectory() : 判断 File对象是否代表的是一个文件夹
最后,是一个文件夹特有的操作:
listFiles() : 这个方法能够返回一个 File数组,这个数组表示 File对象所代表文件夹下所
有的内容。例如,如果 D:\eclipse目录下的内容如下图:
我们可以创建一个表示这个目录的 File对象,并对其调用 listFiles 方法,代码如下:
import java.io.*;
public class TestListFiles{
public static void main(String args[]){
4
File f = new File("D:/eclipse");
File[] fs = f.listFiles();
for(int i = 0; i
标准
excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载
镜、消声器
等等。
从类的设计上来说,除了武器类之外,手枪、步枪等类都是真正完成射击功能的类,也
就是真正完成功能的类。在 I/O框架中对应的概念就是节点流。而零件的那些子类,是为枪
械增强功能的,本身并不能完成射击的功能,因此对应于 I/O的概念,就是过滤流。
2 字节流
介绍完流的分类之后,我们首先来介绍一下字节流。字节流的特点是传输的数据单位是
字节,也意味着字节流能够处理任何一种文件。
2.1 InputStream/OutputStream的基本操作
首先,介绍一下所有字节流的父类,也就是在装饰模式中扮演“武器”这个角色的类。
所有输入字节流的父类是 InputStream,所有输出字节流的父类是 OutputStream,他们都处于
java. io 包下。要学习这两个类,就需要创建这两个类的对象。但是,查阅 JDK文档,我们
可以知道这两个类都是抽象类,无法创建对象。因此,为了学习这两个类的用法,我们需要
先获得这两个类的某个子类。
我们将使用 FileInputStream 和 FileOutputStream 这两个类来学习 InputStream 和
OutputStream。FileInputStream 是文件输入流,从功能上说,这是一个节点流,能够读取硬
7
盘上的文件;而 FileOutputStream 是文件输出流,能够写入文件。
下面,我们就以 FileInputStream / FileOutputStream 为例,来学习一下字节流。
2.1.1 FileInputStream
首先我们研究一下 FileInputStream 这个类。这个类由若干个构造方法,其中两个比较常
用,罗列如下:
FileInputStream(String filename) : 通过文件路径,获得文件输入流
FileInputStream(File file) :通过文件对象,获得文件输入流。
需要注意的是,FileInputStream 在创建对象的时候,当要读取的文件不存在时,就会抛
出异常:FileNotFoundException。由于这是一个已检查异常,因此必须要处理。
除此之外,FileInputStream 还有一些值得注意的方法:
close() : 这个方法顾名思义,可以用来关闭文件流,释放资源。当我们结束输入操作时,
应该调用该方法来关闭流。
int read() : 无参的 read方法。这个方法每次都从文件中读取一个字节,并且把读到的内
容返回。要强调的是,虽然返回值是一个 int类型,但是每次读文件时只读取一个字节,这
个字节作为 int四个字节中的最低位返回。
而当读到流末尾时,返回-1。
我们可以结合代码来理解 read 方法。假设我们在当前目录下准备一个文件 abc.txt,这
个文件中保存了三个英文字母:ABC。如下所示:
保存这个文件,本质上是保存了三个字节,这三个字节分别代表了三个英文字母 A、B、
C的底层编码。根据 ASCII 编码的
规范
编程规范下载gsp规范下载钢格栅规范下载警徽规范下载建设厅规范下载
,这三个字母的编码分别为 65、66、67。
然后有下面的代码:
import java.io.*;
public class TestInputStream{
public static void main(String args[]) throws Exception{
FileInputStream fin = new FileInputStream("abc.txt");
System.out.println(fin.read());
System.out.println(fin.read());
System.out.println(fin.read());
System.out.println(fin.read());
fin.close();
}
}
我们总共调用了四次 read方法。
当我们调用第一次 read方法时,会从文件中读取一个字节,因此会输出 65;第二次时,
会读取下一个字节,输出 66,第三次输出 67;当第四次调用 read方法时,由于此时已经读
8
到了流的末尾,因此此时 read方法返回值为-1。运行结果如下:
下面,我们把 abc.txt增加内容,让这个文件中保存 26个英文字母,如下:
如果我们要读取这个文件中的所有内容,此时再一次一次调用 read 方法显然不现实,
我们应当用 while循环来完成。同时,由于 read方法返回值为 int类型,为了能够输出正常
的字母,我们需要把 read方法的返回值强制转换为 char 类型。典型代码如下:
import java.io.*;
public class TestInputStream{
public static void main(String args[]) throws Exception{
FileInputStream fin = new FileInputStream("abc.txt");
int ch = 0;
while( (ch=fin.read()) != -1){
System.out.print((char)ch);
}
fin.close();
}
}
这个代码利用 while循环读取文件内容,并且把文件中所有内容都输出。运行结果如下:
介绍完无参的 read方法之后,下面我们来介绍有参的 read方法。首先是带 byte数组参
数的 read方法:
int read(byte[] bs) : 这个方法的特点是,每次调用这个方法的时候,会把读取到的数据
9
放入 bs数组中,一次调用尽量读取 bs.length个字节。为什么说尽量读取 bs.length个字节呢?
假设 bs.length长度为 6,如果流中剩余的字节数超过 6个,那么这时候,调用这个 read方
法会把 bs 数组读满;而如果流中剩余的字节数不到 6个的时候,那么调用 read方法会把流
中剩下所有的数据都读入到数组中。由于剩余的数据少于 6个字节,因此数组不会被读满。
我们可以这么来理解:read 方法就好像是去食堂打饭一样。无参的 read 方法,相当于
每次去食堂都只打一粒饭。而对于 read(byte[])来说,就相当于每次去食堂打饭时都带着一个
饭盒。比如这个饭盒能打 6 两饭,每次调用 read 方法,都尽量能够把这一个饭盒打满。如
果食堂里面剩下的饭超过 6两,那么要去打满一盒饭没有问题;而如果去食堂的时候已经比
较晚了,食堂里只剩下 3两饭了,这个时候,那只能打三两饭,即使饭盒不满也没办法。
理解了上面的问题,返回值也就好理解了:int read(byte[] bs) 这个方法的返回值,返回
的是读到的字节数。对于大多数情况来说,读到的字节数和 bs.length 相同;而对于流中剩
下的字节数少于 bs.length的情况,read方法返回的就是实际读到的字节数。
特别要注意的是,当读到流末尾时,返回-1。
我们可以利用这个方法,改写一下之前的 TestInputStream 程序。修改过的代码和运行
结果如下:
import java.io.*;
public class TestInputStream{
public static void main(String args[]) throws Exception{
FileInputStream fin = new FileInputStream("abc.txt");
byte[] bs = new byte[6];
int len = 0;
while( (len=fin.read(bs))!=-1){
for(int i = 0; I < len; i++){
System.out.print((char)bs[i]);
}
System.out.println();
}
fin.close();
}
}
需要注意的是,上面的代码我们使用了一个 len变量用来保存每次 read方法的返回值。
读者可以思考一下为什么。
10
最后,简单介绍一下带多个参数的 read方法:
int read(byte[] bs, int off, int len) : 这个方法同样是把数据读入数组中,只不过这个方法
读取数据的时候,会让数据在数组中以下标为 off 的地方开始,并且最多只读取 len个。也
就是说,这个方法并没有使用整个 bs 数组来存放读到的数据,而是使用了数组的一部分。
究竟是哪部分,是由 off 和 len这两个参数决定的。这就好比我们去食堂打饭的时候,使用
的是分过格子的饭盒,我们要打的也不是一盒饭,而是一格饭。
返回值是真正读取的字节数,当读到流末尾时,返回-1。
2.1.2 FileOutputStream
相对于 FileInputStream, FileOutputStream 更简单一些。首先同样是研究一下
FileOutputStream 的构造方法,罗列如下:
FileOutputStream(String path) : 根据路径创建文件输出流
FileOutputStream(File file) : 根据文件对象创建文件输出流
还有其他的构造方法,会在后面的内容中再次提到。
此外,下面是一些基本操作:
close(): 关闭流
FileOutputStream 最重要的是 write方法,其 write方法罗列如下:
void write(int v) : 这个write方法每次调用时写入一个字节。注意,虽然这个 write方法
接受的参数类型是 int类型,虽然 int类型有 4个字节,但是这个 write方法每次只写入 int
类型中最后的那个字节。
void write(byte[] bs) : 写入一个 byte数组。
void write(byte[] bs, int off, int len) : 同样是写入一个 byte数组,所不同的是,写入这个
byte数组的时候,数据内容从数组下标 off 的位置开始,写入 len个字节。
下面我们演示一下如何使用 write方法。假设我们要写入一个字符串:Hello World,由
于 FileOutputStream 中没有一个类能够接受一个字符串作为参数,因此我们必须要把字符串
转化为 byte数组。转换的方式也很简单,String类中有一个方法叫做 getBytes(),签名如下:
public byte[] getBytes()
这个方法没有参数,并且返回一个 byte数组,这就是字符串转为 byte数组的方法。
我们利用这个方法,编写代码如下:
import java.io.*;
public class TestOutputStream{
public static void main(String args[]) throws Exception{
String hello = "Hello World";
byte[] bs = hello.getBytes();
FileOutputStream fout= new FileOutputStream("test.txt");
fout.write(bs);
fout.close();
}
}
运行这个代码之前,当前目录下并不存在 test.txt 文件。
运行之后,在当前目录下产生 test.txt 文件,其中的内容为:Hello World。这也就提示
11
我们:如果文件不存在,FileOutputStream 会创建新文件。
那么文件已存在会怎么样呢?修改 test.txt 文件的内容并保存,然后再次运行程序,你
会发现 test.txt 文件中的内容依然是 Hello World。
这说明:默认情况下,当文件已存在时,FileOutputStream 会覆盖同名文件。
我们也可以使用 FileOutputStream 另外两个构造方法:
FileOutputStream(String path, boolean append)
FileOutputStream(File file, boolean append)
这两个构造方法与原来的构造方法相比,增加了一个 boolean参数:append。这个参数
表示什么含义呢?
当这个参数为 false 的时候,这两个构造方法与只有一个参数的构造方法用起来相同:
如果文件不存在,则 FileOutputStream 会创建新文件;如果文件已存在,则覆盖原文件。
如果这个参数为 true,则:当如果文件不存在,依然会创建新文件;而如果文件已存在,
则会用追加的方式写文件。
我们可以写一个实验代码:
import java.io.*;
public class TestOutputStream{
public static void main(String args[]) throws Exception{
String hello = "Hello World";
byte[] bs = hello.getBytes();
FileOutputStream fout
= new FileOutputStream("test.txt", true);
fout.write(bs);
fout.close();
}
}
把上面的例子进行修改,在创建 FileOutputStream 的时候增加一个新的参数 true。如果
test.txt文件不存在,第一次运行时,会创建这个文件,文件内容为:Hello World;反复运行
这个程序多次,就会出现多个 Hello World。这就是用追加的方式写文件。
2.1.3 处理异常
之前,我们的程序处理异常的时候,都是使用 throws 的方式处理。下面,我们对
TestInputStream 程序进行修改,使用相对比较积极的 try-catch的方式进行处理。
首先,在 FileInputStream 这个类中,有下面这些部分是需要进行异常处理的:
1、创建对象时
2、调用 read方法时
3、调用 close方法时
另外,要注意的是,close方法属于释放资源的代码,应当写在 finally代码块中。
修改之后的 TestInputStream 代码如下:
import java.io.*;
public class TestInputStream{
public static void main(String args[]) {
FileInputStream fin = null;
try{
fin = new FileInputStream("abc.txt");
12
byte[] bs = new byte[6];
int len = 0;
while( (len=fin.read(bs))!=-1){
for(int i = 0; i
本文档为【Chp14 IO框架】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑,
图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。