11.1 File类的使用
File类是文件和目录的抽象表示形式。File能新建、删除、重命名文件和目录,但不能访问文件内容。
public File(String pathname)
public File(File parent,String child)
Windows和DOS系统使用“\”,UNIX和URL使用“/”,File提供静态常量separator
根据操作系统动态的提供分隔符。
获取功能:
public String getAbsolutePath()
public String getPath()
public String getName()
public String getParent()
public long length() :获取文件长度(即:字节数)。
public long lastModified() :获取最后一次的修改时间,毫秒值
public String[] list() :获取指定目录下的所有文件或者目录的名称数组
public File[] listFiles() :获取指定目录下的所有文件或者目录的File数组
判断功能:
public boolean isDirectory()
public boolean isFile()
public boolean exists()
public boolean canRead()
public boolean canWrite()
public boolean isHidden()
创建、删除、重命名:
public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false
public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。
public boolean mkdirs() :创建文件目录。如果上层文件目录不存在,一并创建
public boolean delete():要删除的文件目录内不能包含文件或者文件目录
public boolean renameTo(File dest):把文件重命名为指定的文件路径
File dir1 = new File("D:/IOTest/dir1");
if (!dir1.exists()) {
dir1.mkdirs();
}
// 创建以dir1为父目录,名为"test.txt"的File对象
File file = new File(dir1, "test.txt");
if (!file.exists()) {
file.createNewFile();
}
11.2 IO流及流的分类
I/O技术用来处理设备之间的数据传输,如读写文件、网络通讯等。
- 输入input:读取外部数据到内存中。
- 输出output:将内存数据输出。
按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)。字节流用于处理非文本数据,字符流用于处理文本数据。
- 字节流操作字节,比如:.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt, .excel
- 字符流操作字符,比如:.txt,.java,.c,.cpp
按流的角色的不同分为:节点流,处理流。节点流(文件流):直接从数据源或目的地读写数据;处理流:不直接连接到数据源或目的地,而是“连接”在已存在的节点流或处理流之上,通过对数据的处理为程序提供更为强大的读写功能。在如下所示的IO流体系表中,仅访问文件的流是节点流,其他流均为套在其他流上的处理流。
程序中打开的文件 IO 资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件 IO 资源。
InputStream:
int read():读取下一个字节,返回[0, 255]内的 int 字节值。如果到达流末尾则返回 -1。
int read(byte[] b)将最多 b.length 个字节的数据读入 byte 中,以整数形式返回实际读取的字节数。如果到达流末尾则返回值 -1。
int read(byte[] b, int off,int len)将输入流中最多 len 个数据字节读入 byte 数组。
Reader:
int read()读取单个字符。返回[0, 65535]内的 int 字节值,如果已到达流的末尾,则返回 -1
int read(char[] cbuf)
int read(char[] cbuf,int off,int len)
OutputStream
void write(int b)向输出流写入一个字节。要写入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。
void write(byte[] b,int off,int len)将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。
public void flush()throws IOException 刷新此输出流并强制写出所有缓冲的输出字节
Writer
void write(int c)写入 16 个低位,16 高位被忽略。 即写入0 到 65535 之间的Unicode码。
void write(String str)写入字符串。
public void close() throws IOException 关闭该流并释放与该流关联的所有系统资源。
11.3 节点流(或文件流)
FileReader fr = null;
try {
fr = new FileReader(new File("c:\\test.txt")); // 1.建立一个流对象,将已存在的一个文件加载进流
char[] buf = new char[1024]; // 2.创建一个临时存放数据的数组
int len; // 这里不可用buf.length,因为可能读不满
while ((len = fr.read(buf)) != -1) // 3.调用流对象的读取方法将流中的数据读入到数组中
System.out.print(new String(buf, 0, len));
} catch (IOException e) {
System.out.println("read-Exception :" + e.getMessage());
} finally {
if (fr != null) {
try {
fr.close(); // 必须关闭流
} catch (IOException e) {
System.out.println("close-Exception :" + e.getMessage());
}
}
}
如果使用构造器FileOutputStream(file,true),则目录下的同名文件不会被覆盖, 在文件内容末尾追加内容。当不表明第二个参数时默认为false,即目录下同名文件将被覆盖。在读取文件时,必须保证该文件已存在,否则报异常。
11.4 缓冲流
为了提高数据读写的速度,缓冲流会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区。
当使用BufferedInputStream读取字节文件时,会一次性从文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个8192个字节数组。
向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满, BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法 flush()可以强制将缓冲区的内容全部写入输出流。
关闭流时要从外层流向内层流逐层关闭。在该方法中,关闭最外层流也会相应关闭内层节点流。如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷新缓冲区。
11.5 转换流与字符编码
转换流提供了在字节流和字符流之间的转换,使得在向外部存储设备操作时为字节流,程序内操作时以字符流处理。字节流中的数据都是字符时,转成字符流操作更高效。很多时候我们使用转换流来处理文件乱码问题。实现编码和解码的功能。
- InputStreamReader:将InputStream转换为Reader
- OutputStreamWriter:将Writer转换为OutputStream
public InputSreamReader(InputStream in,String charsetName) // charsetName指明转换编码方式
常见的编码表:
ASCII:美国标准信息交换码。用一个字节的7位可以表示。
GBK:中国的中文编码表升级。最多两个字节编码
Unicode:国际标准码,融合了目前人类使用的所有字符。所有的文字都用两个字节来表示。
UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。
11.6 标准输入输出流
默认输入设备是:键盘,输出设备是:显示器。 System.in的类型是InputStream,System.out的类型是PrintStream。
重定向:通过System类的setIn,setOut方法对默认设备进行改变。
// 例题:从键盘输入字符串,将读取到的字符串转成大写输出。然后继续进行输入操作,直至输入“e”或“exit”时,退出程序
// 把"标准"输入流(键盘输入)这个字节流包装成字符流,再包装成缓冲流
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
String s = null;
try {
while ((s = br.readLine()) != null) { // 读取用户输入的一行数据 --> 阻塞程序
if ("e".equalsIgnoreCase(s) || "exit".equalsIgnoreCase(s)) {
System.out.println("安全退出!!");
break;
}
// 将读取到的整行字符串转成大写输出
System.out.println("-->:" + s.toUpperCase());
System.out.println("继续输入信息");
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (br != null) {
br.close(); // 关闭过滤流时,会自动关闭它包装的底层节点流
}
} catch (IOException e) {
e.printStackTrace();
}
}
11.7 对象流
可以把对象写入到数据源中,也能把对象从数据源中还原回来。ObjectOutputStream和ObjectInputStream不能序列化static(不属于对象,属于类)和transient修饰的成员变量。
对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,该类必须实现Serializable或 Externalizable接口。
凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量: serialVersionUID; 这个变量表明类的不同版本间的兼容性。如果类没有显示定义这个静态常量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的实例变量(即非静态成员变量)做了修改,serialVersionUID 可能发生变化。故建议,显式声明。
使用对象流序列化对象:
1.创建一个 ObjectOutputStream
2.调用 ObjectOutputStream 对象的 writeObject(对象) 方法输出可序列化对象
3.注意写出一次,操作flush()一次
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("data.txt"));
Person p = new Person("韩梅梅", 18, "中华大街", new Pet());
oos.writeObject(p);
oos.flush();
oos.close();
反序列化:
1.创建一个 ObjectInputStream
2.调用 readObject() 方法读取流中的对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("data.txt"));
Person p1 = (Person)ois.readObject();
ois.close();
11.8 随机存取文件流
RandomAccessFile 支持 “随机访问” ,可以直接跳到文件的任意地方来读、写文件。在写文件时,若文件不存在,将自动创建文件;若文件存在,则写入数据默认会从文件第一个数据开始依次进行数据覆盖,可通过seek方法修改覆盖起始位置。通过getFilePointer
获取文件记录指针的当前位置;seek
将文件记录指针定位到指定位置。
public RandomAccessFile(File file, String mode)
public RandomAccessFile(String name, String mode)
r: 以只读方式打开,该文件必须存在
rw:以读写方式打开,文件不存在将自动创建
rwd:以读写方式打开;同步文件内容的更新
rws:以读写方式打开;同步文件内容和元数据的更新
在实现如下载功能时,若中途下载失败则仅能从头开始,而使用该类可实现多线程断点下载的功能,下载前建立两个临时文件,一个是与被下载文件大小相同的空文件,另一个是记录文件指针的位置文件,每次暂停时都保存指针。
// 实现在定点位置输入xyz
RandomAccessFile raf = new RandomAccessFile("test.txt", "rw");
raf.seek(5);
//先读出来
String temp = raf.readLine(); // 每次要将插入位置后的所有数据读出,效率较低,因此在实现时要规避插入,尽量使用追加操作
raf.seek(5);
raf.write("xyz".getBytes());
raf.write(temp.getBytes());
raf.close();
11.9 Scanner类
Scanner scan = new Scanner(System.in); // 从键盘接收数据
if (scan.hasNext()) { // 判断是否还有输入
String str1 = scan.next(); // 读取以空白符为结束符的字符串
String str2 = scan.nextLine(); // 读取以回车为结束符的字符串
}
scan.close(); // 关闭流
scan.hasNextInt()
scan.nextInt()
scan.hasNextFloat()
scan.nextFloat()
scan.hasNextDouble()
scan.nextDouble()