1.File类
2.I/O概念
3.字节流
4.字符流
5.包装方式的灵活性
6.转换流
7.其它流
8.Properties
9.IO中保证流对象关闭的标准格式
一、File类
1.概念
java.io包下代表平台无关的文件和目录。也就是说如果希望在程序中操作文件或目录都可以通过File类来完成。
File可以新建、删除、和重命名文件和目录。但是File不能访问文件本身,如果需要访问文件的内容,则需要I/O。
File类的实例表示一个文件或者目录(文件夹)。
构造一个File实例并不是创建这个目录或文件夹,而是该路径的一个抽象,它可能真实存在也可能不存在(就是指向这个文件或目录)。
2.相对路径和绝对路径
路径分类:绝对路径、相对路径
绝对路径:从根目录开始的路径,称为绝对路径。
Windows系统中:盘符目录就是根目录,C:、D:。
Linux系统中:/就是根目录,从/开始的路径就是绝对路径
相对路径:相对于某个路径而言的路径
相对于不同的路径,同样的相对路径,表达的是不同的路径
Dos命令行中:相对于当前路径
Eclipse中:相对于当前工程的根目录
3.File类的构造方法
File(File parent, String child) 从父抽象路径名和子路径名字符串创建新的 File实例。 |
File(String pathname) 通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。 |
File(String parent, String child) 从父路径名字符串和子路径名字符串创建新的 File实例。 |
代码示例
import java.io.File;
public class Test {
public static void main(String[] args) {
File parent = new File("test/a/b");
File file1 = new File(parent,"c.txt");//test\a\b\c.txt
System.out.println(file1);
File file2 = new File("E:/test/a/b/c.txt");
System.out.println(file2);//E:\test\a\b\c.txt 绝对路径指向文件
File file3 = new File("c.txt");
System.out.println(file3);//c.txt 相对路径,相对于当前项目的根目录
File file4 = new File("test/a/b","c.txt");
System.out.println(file4);//test\a\b\c.txt
}
}
4.File类的常用方法
(1)创建的方法
boolean | 当且仅当具有该名称的文件尚不存在时,原地创建一个由该抽象路径名命名的新的空文件。 | ||
boolean | mkdir() 创建由此抽象路径名命名的目录。 | ||
boolean | mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。 |
示例代码
import java.io.File;
public class Test {
public static void main(String[] args) throws Exception{
/*
//创建一个文件,父路径(test/a/b)不存在
File f1 = new File("E:/test/a/b/c.txt");
f1.createNewFile();
//报错,系统找不到指定的路径
*/
//创建一个文件,父路径存在
File f2 = new File("E:/test/a/b/c.txt");
f2.createNewFile();
//文件创建成功
//创建一个文件,相对于当前项目的根目录
File f3 = new File("c.txt");
f3.createNewFile();
//项目test里面创建了一个c.txt文件
//创建目录,只能创建单级目录
File parent1 = new File("E:/z");
parent1.mkdir();
//创建成功,E盘下创建了z文件夹
//创建目录,创建多级目录
File parent2 = new File("E:/x/xx");
parent2.mkdirs();
//创建成功,E盘下创建了x文件夹,x文件夹里有xx文件夹
}
}
(2)删除的方法
boolean | delete() 删除由此抽象路径名表示的文件或目录。 |
注:
在删除文件夹的时候,只能删除空的文件夹。
delete方法不走回收站
代码示例
import java.io.File;
public class Test {
public static void main(String[] args) throws Exception{
//E:\test\a\b\c.txt
//删除文件夹a
File f1 = new File("E:/test/a");
boolean b1 = f1.delete();
System.out.println(b1);//false
//删除不成功,a文件夹非空
//删除文件c.txt
File f2 = new File("E:/test/a/b/c.txt");
boolean b2 = f2.delete();
System.out.println(b2);//true
//删除文件夹b
File f3 = new File("E:/test/a/b");
boolean b3 = f3.delete();
System.out.println(b3);//true
}
}
(3)重命名的方法
boolean | renameTo(File dest) 重命名由此抽象路径名表示的文件。 |
注:
如果在同一个文件夹下,修改路径,就是重命名。
如果在不同文件夹下,修改路径,就是剪切。
代码示例
import java.io.File;
public class Test {
public static void main(String[] args) throws Exception{
/*
//E盘的test文件夹下a.txt和b.txt都存在
File f1 = new File("E:/test/a.txt");
File f2 = new File("E:/test/b.txt");
f1.renameTo(f2);
//a.txt没有重命名
*/
/*
//E盘的test文件夹下只有a.txt,b.txt文件夹不存在
File f3 = new File("E:/test/a.txt");
File f4 = new File("E:/test/b.txt");
f3.renameTo(f4);
//a.txt被重命名为b.txt
*/
/*
//E盘的test文件夹下有a.txt,E盘下没有b.txt这个文件
File f5 = new File("E:/test/a.txt");
File f6 = new File("E:/b.txt");
f5.renameTo(f6);
//a.txt被重命名为b.txt,并且位置移到了E盘下
*/
/*
//E盘的test文件夹下有a.txt,E盘下有b.txt这个文件
File f7 = new File("E:/test/a.txt");
File f8 = new File("E:/b.txt");
f7.renameTo(f8);
//a.txt没有被重命名,也没有被移动位置
*/
}
}
(4)判断功能的方法
boolean | exists() 测试此抽象路径名表示的文件或目录是否存在。 | ||
boolean | 测试此抽象路径名表示的文件是否为目录。 | ||
boolean | isFile() 测试此抽象路径名表示的文件是否为普通文件。 |
代码示例
import java.io.File;
public class Test {
public static void main(String[] args) throws Exception{
File f1 = new File("E:/test");
File f2 = new File("E:/test/test.java");
System.out.println(f1.exists());//true
System.out.println(f2.exists());//true
System.out.println(f1.isDirectory());//true
System.out.println(f2.isDirectory());//false
System.out.println(f1.isFile());//false
System.out.println(f2.isFile());//true
}
}
(5)获取功能的方法
String | 返回此抽象路径名的绝对路径名字符串。 | ||
String | getPath() 将此抽象路径名转换为路径名字符串。 | ||
String | getName() 返回由此抽象路径名表示的文件或目录的名称。 | ||
long | length() 返回由此抽象路径名表示的文件的长度。 | ||
String[] | list() 获取当前文件夹下的所有文件和文件夹的名称,到一个字符串数组中。 | ||
File[] | 获取当前文件夹下的所有文件和文件夹的File对象,到一个File对象数组中。 |
代码示例
import java.io.File;
public class Test {
public static void main(String[] args) throws Exception{
//E:/test/a/b/....
File f = new File("a/b");
System.out.println(f.getAbsolutePath());//E:\test\a\b
System.out.println(f.getPath());//a\b
System.out.println(f.getName());//b
System.out.println(f.length());//0
String[] list = f.list();
for(String ff:list){
System.out.println(ff);
}
/*c.txt
D.txt
f.mp4
g
*/
File[] fileList = f.listFiles();
for(File fff:fileList){
System.out.println(fff.length()+"..."+fff.getName());
}
/*6...c.txt
66...D.txt
20123312...f.mp4
0...g
*/
}
}
练习:键盘录入一个字符串,表示一个文件夹路径,如果不是文件夹提示重新录入,打印当前文件夹下,所有的大于10M的后缀名名为.txt文件的绝对路径。
import java.io.File;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
boolean flag = true;
while(flag) {
String str = sc.nextLine();
File dir = new File(str);
if(dir.isDirectory()) {
File[] listFiles = dir.listFiles();
for(File f:listFiles) {
if(f.length()>10*1024*1024 && f.getName().endsWith(".txt")) {
System.out.println(f.getAbsolutePath());
}
}
flag = false;
}else {
System.out.println("请重新输入");
continue;
}
}
}
}
二、I/O概念
1.概念
File对象可以表示存在的文件或文件夹,也可以表示不存在的。我们想要得到文件怎么办?File只是操作文件,文件的内容就需要使用I/O流技术。
例如:在C盘下有一个名称为a.txt的文本文件。想要通过java程序读出来文件中的内容,需要使用IO流技术。同样想要将程序中的数据,保存到硬盘的文件中,也需要IO流技术。
IO:Input和Output两个单词的缩写,input是输入,output是输出
输入、输出,都是相对应用程序来说的。数据流向程序,叫做输入流。数据从程序流出叫做输出流。
站在内存的角度看待方向,从其它设备进入到内存的,都是输入,从内存到其它设备,都是输出。
Java中用于操作设备之间的数据传输的对象,都是IO流对象,都在io包下。
2.分类
分类的方式有两种:按照功能可以分类,按照流向也可以分类。
按照功能分类:
字节流:可以直接操作字节的流对象
字符流:可以直接操作字符的流对象
按照流向分类:
输入流:其他设备流到内存的流对象
输出流:内存流到其他设备的流对象
IO流的体系结构,根据分类,有四种流对象的类型。
字节流:
字节输入流:InputStream
字节输出流:OutputStream
字符流:
字符输入流:Reader
字符输出流:Writer
三、字节流
1.概述
可以直接操作字节信息的流对象
根据流向,可以分成字节输入流和字节输出流
顶层抽象父类分别是:InputStream和OutputStream
根据交互设备的不同,有不同的具体子类
2.InputStream
字节输入流的顶层父类。InputStream是一个抽象类,不能直接创建对象,只能由子类创建对象,最常用子类 FileInputStream,用于和磁盘上的文件进行交互。
(1)构造方法
FileInputStream(File file) 通过打开与实际文件的连接创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。 |
FileInputStream(String name) 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。 |
(2)常用方法
abstract int | read() 从输入流读取数据的下一个字节。 结束标志为-1 |
int | read(byte[] b) 从输入流读取一些字节数,并将它们存储到缓冲区 b 。 结束标志为-1 |
int | read(byte[] b, int off, int len) 从输入流读取最多 len字节的数据到一个字节数组。 |
void | close() 关闭此文件输入流并释放与流相关联的任何系统资源。 |
read:一次读到一个字节。输出这个数据到控制台上发现,显示的是数字而不是字母。为什么?因为显示的是字母对应的码值,如果需要转成字符,可以强转成char类型。当read()的值为 -1 时,表示读到文件的末尾。
read(byte[] buf):返回值为覆盖字节数组的长度。
使用read方法的时候,流需要读一次就处理一次,可以将读到的数据装入到字节数组中,一次性的操作数组,可以提高效率。
代码示例
import java.io.File;
import java.io.FileInputStream;
import java.util.Arrays;
public class Test {
public static void main(String[] args) throws Exception{
//a.txt里面的内容为abc
File f = new File("E:/test/a.txt");
FileInputStream fis = new FileInputStream(f);
/*
System.out.println(fis.read());//97
System.out.println(fis.read());//98
System.out.println(fis.read());//99
System.out.println(fis.read());//-1
fis.close();
*/
/*
int i = 0;
while((i=fis.read())!=-1){
System.out.print(i+" ");//97 98 99
}
fis.close();
*/
/*
byte[] buf = new byte[2];
int length = fis.read(buf);//length为返回的覆盖的长度
System.out.println(length+"..."+Arrays.toString(buf));//2...[97, 98]
length = fis.read(buf);
System.out.println(length+"..."+Arrays.toString(buf));//1...[99, 98] // fis.read读取的a.txt里面的内容,放到了数组buf里面,但是读取的只有c,所以长度为1。数组buf里面b没有被覆盖掉
length = fis.read(buf);
System.out.println(length+"..."+Arrays.toString(buf));//-1...[99, 98] //c,b没有被覆盖掉
fis.close();
*/
/*
byte[] buf = new byte[2];
int length = 0;
while((length=fis.read(buf))!=-1){
for(byte b:buf){
System.out.print((char)b);//abcb
}
}
fis.close();
*/
/* byte[] buf = new byte[2];
int length = 0;
while((length=fis.read(buf))!=-1){
String str = new String(buf,0,length);
System.out.println(str);
}
//ab
//c
fis.close();
*/
}
}
(3)InputStream类型的read()方法和read(byte[] buf)方法的比较
读取字节个数的区别
read()方法一次只读取一个字节
read(buf)方法一次可以读取多个字节,取决于数组的大小
返回值不同
read()读取到的是文件字节信息是作为返回值进行返回的。
read(buf):读取到的字节存储到数组中,返回的是覆盖数组的长度。
3.OutputStream
字节输出流的顶层抽象父类,最常用子类FileOutputStream。
(1)构造方法
FileOutputStream(File file) 创建文件输出流以写入由指定的 File对象表示的文件。 |
FileOutputStream(File file, boolean append) 创建文件输出流以写入由指定的 File对象表示的文件。 |
FileOutputStream(String name) 创建文件输出流以指定的名称写入文件。 |
FileOutputStream(String name, boolean append) 创建文件输出流以指定的名称写入文件。 |
(2)常用方法
void | write(byte[] b) 将 b.length字节从指定的字节数组写入此输出流。 | ||
void | write(byte[] b, int off, int len) 从指定的字节数组写入 len个字节,从偏移 off开始输出到此输出流。 | ||
abstract void | write(int b) 将指定的字节写入此输出流。 | ||
void | close() 关闭此文件输出流并释放与此流相关联的任何系统资源。 |
代码示例
import java.io.File;
import java.io.FileOutputStream;
public class Test {
public static void main(String[] args) throws Exception{
//输出流关联的文件若不存在,会自动创建
//输出流关联的文件存在,则还会创建一个新的文件,默认覆盖原来的文件
// File f = new File("E:/a.txt");
// FileOutputStream fos = new FileOutputStream(f);
/*
fos.write(97);
fos.write(98);
fos.close(); //ab a.txt不存在,自动创建,写上数据,a.txt的内容为ab
*/
/*
fos.write(99);
fos.close();//c a.txt存在且原来的内容为ab,写入数据c之后,a.txt被覆盖内容变为c
*/
//不覆盖原来的文件
// File f2 = new File("E:/a.txt");
// FileOutputStream fos2 = new FileOutputStream(f2,true);
/*
fos2.write(100);
fos2.write(101);//cde a.txt存在原来的内容为c,写入数据后,没有被覆盖,内容变为cde
fos2.close();
*/
/*
byte[] buf = {102,103,104};
fos2.write(buf);
fos2.close();//cdefgh a.txt存在原来的内容为cde,写入数据后,内容变为cdefgh
*/
/*
byte[] buf = {105,106,107};
fos2.write(buf,0,2);
fos2.close();//cdefghij a.txt存在原来的内容为cdefgh,写入数据后,内容变为cdefghij
*/
}
}
练习:使用字节流完成文件拷贝
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class Test {
public static void main(String[] args) throws Exception{
FileInputStream fis = new FileInputStream("E:/test/a.jpg");
FileOutputStream fos = new FileOutputStream("E:/a.jpg");
int i = 0;
while((i=fis.read())!=-1){
fos.write(i);
}
fis.close();
fos.close();
}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class Test {
public static void main(String[] args) throws Exception{
FileInputStream fis = new FileInputStream("E:/test/a.jpg");
FileOutputStream fos = new FileOutputStream("E:/a.jpg");
byte[] b = new byte[1024];
int length=0;
while((length=fis.read(b))!=-1){
fos.write(b,0,length);
}
fis.close();
fos.close();
}
}
练习扩展-拷贝的时候显示进度百分比
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class Test {
public static void main(String[] args) throws Exception{
File srcFile = new File("E:/test/a.jpg");
FileInputStream fis = new FileInputStream(srcFile);
double srcLength = srcFile.length();
File desFile = new File("E:/a.jpg");
FileOutputStream fos = new FileOutputStream(desFile);
byte[] buf = new byte[1024];
int length = 0;
while((length=fis.read(buf))!=-1){
fos.write(buf, 0, length);
String str = String.valueOf(desFile.length()/srcLength*100);
str = (String) str.subSequence(0, 5);
System.out.println(str+"%");
}
fis.close();
fos.close();
}
}
4.字节型缓冲流BufferedInputStream和BufferedOutputStream
(1)概念
BuffereInputStream和BufferedOutputStream类可以通过减少读写次数来提高输入和输出的速度。它们内部有一个缓冲区,用来提高处理效率。查看API文档,发现可以指定缓冲区的大小。其实内部也是封装了字节数组。没有指定缓冲区大小,默认的字节是8192。
显然缓冲区输入流和缓冲区输出流要配合使用。首先缓冲区输入流会将读取到的数据读入缓冲区,当缓冲区满时,或者调用flush方法,缓冲输出流会将数据写出。
注意:当然使用缓冲流来进行提高效率时,对于小文件可能看不到性能的提升。但是文件稍微大一些的话,就可以看到实质的性能提升了。
(2)原理
BufferedInputStream高效的原理:在该类型中准备了一个数组,存储字节信息,当外界调用read()方法想获取一个字节的时候,该对象从文件中一次性读取了8192个字节到数组中,只返回了第一个字节给调用者。将来调用者再次调用read方法时,当前对象就不需要再次访问磁盘,只需要从数组中取出一个字节返回给调用者即可,由于读取的是数组,所以速度非常快。当8192个字节全都读取完成之后,再需要读取一个字节,就得让该对象到文件中读取下一个8192个字节了。
BufferedOutputStream高效的原理:在该类型中准备了一个数组,存储字节信息,当外界调用write方法想写出一个字节的时候,该对象直接将这个字节存储到了自己的数组中,而不刷新到文件中。一直到该数组所有8192个位置全都占满,该对象才把这个数组中的所有数据一次性写出到目标文件中。如果最后一次循环过程中,没有将数组写满,最终在关闭流对象的时候,也会将该数组中的数据刷新到文件中。
(3)构造方法
BufferedInputStream(InputStream in) 创建一个 BufferedInputStream并保存其参数,输入流 in ,供以后使用。 |
BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。 |
练习:拷贝mp4文件
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
public class Test {
public static void main(String[] args) throws Exception{
FileInputStream fis = new FileInputStream("E:/test/a.mp4");//打开输入流
FileOutputStream fos = new FileOutputStream("E:/b.mp4");//打开输出流
BufferedInputStream bis = new BufferedInputStream(fis);//使用缓冲流
BufferedOutputStream bos = new BufferedOutputStream(fos);//使用缓冲流
/*
int len = 0;
while((len=bis.read())!=-1){
bos.write(len);
}
bis.close();//关闭流
bos.close();//关闭流
*/
/*
byte[] buf = new byte[1024];
int len = 0;
while((len=bis.read(buf))!=-1){
bos.write(buf,0,len);//数据写入缓存区,不会写入文件,字节流会自动将缓冲区的数据刷新到文件中,字符流不会自动将缓冲区的数据刷新到文件中。另外close方法在调用前,会默认调用flush方法,将缓冲区的数据刷新到文件中。
}
bis.close();//关闭流
bos.close();//关闭流
*/
}
}
四、字符流
字节流虽然也可以操作字符,但是在操作中英文混合的时候,判断起来较麻烦,需要判断中英文的标识,如果是英文的标识,直接读取一个字节,如果是中文的标识,需要再次读取一个字节拼接成两个字节,一块读取,这种操作判断起来非常 麻烦,恰巧字符流帮咱们封装这种判断标识的操作,所以直接可以使用字符流来操作中英文混合的字符。
1.字符输入流Reader
Reader是抽象类,常用实现类FileReader。
int | read() 读一个字符 结束标志-1 |
int | read(char[] cbuf) 将字符读入数组。 结束标志-1 |
练习:读取文件中全部内容,输出到控制台。
import java.io.FileReader;
public class Test {
public static void main(String[] args) throws Exception{
//a.txt中的内容为:abcd
FileReader fr = new FileReader("E:/test/a.txt");
/*
int a = fr.read();
System.out.println(a);//97
a = fr.read();
System.out.println(a);//98
a = fr.read();
System.out.println(a);//99
a = fr.read();
System.out.println(a);//100
a = fr.read();
System.out.println(a);//-1 表示读到文件末尾
fr.close();
*/
/*
int len = 0;
while((len=fr.read())!=-1){
System.out.print((char)len);//abcd
}
fr.close();
*/
/*
char[] cs = new char[5];
fr.read(cs);
for(char c:cs){
System.out.print(c);//abcd
}
fr.close();
*/
}
}
2.字符输出流Writer
Writer是抽象类,常用实现类FileWriter。
void | write(char[] cbuf) 写入一个字符数组。 | ||
abstract void | write(char[] cbuf, int off, int len) 写入字符数组的一部分。 | ||
void | write(int c) 写一个字符 | ||
void | write(String str) 写一个字符串 | ||
void | write(String str, int off, int len) 写一个字符串的一部分。 | ||
abstract void | close() 关闭流,先刷新。 | ||
abstract void | flush() 刷新流。 |
案例:使用字符流完成文件拷贝。
import java.io.FileReader;
import java.io.FileWriter;
public class Test {
public static void main(String[] args) throws Exception{
FileReader fr = new FileReader("E:/test/a.txt");
FileWriter fw = new FileWriter("E:/a.txt");
/*
int i=0;
while((i=fr.read())!=-1) {
fw.write(i);//数据写入到缓冲区中
fw.flush();//将缓冲区的数据刷新到文件中
}
fr.close();
fw.close();
*/
/*
char[] chs = new char[10];
int len = 0;
while((len=fr.read(chs))!=-1){
fw.write(chs,0,len);
fw.flush();//如果不刷新,文件看不到数据
}
fr.close();
fw.close();//close()方法在调用前默认先调用flush方法
*/
}
}
3.输出流中的close和flush的区别
close方法在调用的时候默认先会调用flush
close方法把流对象关闭掉了
flush只是把缓冲区的数据刷新到文件中
4.字符型缓冲流BufferedReader和BufferedWriter
(1)原理
BufferedReader:每次调用read方法,只有第一次从磁盘中读取了8192个字符,存储到该类型对象的缓冲区数组中,将其中一个返回给调用者,再次调用read方法时,就不需要再访问磁盘,直接从缓冲区中拿一个出来即可,效率提升了很多。
BufferedWriter:每次调用write方法,不会直接将字符刷新到文件中,而是存储到字符数组中,等字符数组写满了,才一次性刷新到文件中,减少了和磁盘交互的次数,提升了效率。
(2)特有方法
String | readLine() 读一行文字。 结束标记null | ||
void | newLine() 写一行行分隔符。 |
代码示例
import java.io.FileReader;
import java.io.BufferedReader;
public class Test {
public static void main(String[] args) throws Exception{
FileReader fr = new FileReader("E:/test/a.txt");
BufferedReader br = new BufferedReader(fr);
/*
String line = br.readLine();
System.out.println(line);//1111
line = br.readLine();
System.out.println(line);//2222
line = br.readLine();
System.out.println(line);//3333
line = br.readLine();
System.out.println(line);//4444
line = br.readLine();
System.out.println(line);//null
br.close();
*/
String line2 = null;
while((line2=br.readLine())!=null){
System.out.println(line2);
}
br.close();
//1111
//2222
//3333
//4444
}
}
import java.io.FileWriter;
import java.io.BufferedWriter;
public class Test {
public static void main(String[] args) throws Exception{
//a.txt文件不存在,自动创建
FileWriter fw = new FileWriter("E:/test/a.txt");
BufferedWriter bw = new BufferedWriter(fw);
bw.write("abcd");
bw.newLine();//换行
bw.write("1234");
bw.close();
//abcd
//1234
}
}
默认的FileWriter方法新值会覆盖旧值,想要实现追加功能需要使用如下构造函数创建输出流 ,append值为true即可。
FileWriter(File file, boolean append) 给一个File对象构造一个FileWriter对象。 |
FileWriter(String fileName, boolean append) 构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。 |
代码示例
import java.io.FileWriter;
import java.io.BufferedWriter;
public class Test {
public static void main(String[] args) throws Exception{
//a.txt文件存在,如果写数据,不被覆盖
//a.txt原来的内容为1234
FileWriter fw = new FileWriter("E:/test/a.txt",true);
BufferedWriter bw = new BufferedWriter(fw);
bw.write("zxc");
bw.close();
//a.txt中的内容为1234zxc
}
}
练习:使用字符型缓冲流完成文件拷贝
import java.io.FileReader;
import java.io.FileWriter;
import java.io.BufferedReader;
import java.io.BufferedWriter;
public class Test {
public static void main(String[] args) throws Exception{
FileReader fr = new FileReader("E:/test/a.txt");
FileWriter fw = new FileWriter("E:/a.txt");
BufferedReader br = new BufferedReader(fr);
BufferedWriter bw = new BufferedWriter(fw);
char[] chs = new char[10];
int len = 0;
while((len=br.read(chs))!=-1){
bw.write(chs,0,len);
}
br.close();
bw.close();
}
}
练习:自己定义一个类实现readLine()方法
import java.io.Reader;
import java.io.FileReader;
public class Test {
public static void main(String[] args) throws Exception{
FileReader fr = new FileReader("E:/test/a.txt");
MyBufferedReader mr = new MyBufferedReader(fr);
/*
String line = mr.myReadLine();
System.out.println(line);
line = mr.myReadLine();
System.out.println(line);
*/
String line = null;
while((line=mr.myReadLine())!=null){
System.out.println(line);
}
}
}
class MyBufferedReader{
Reader reader;
public MyBufferedReader(Reader reader){
this.reader = reader;
}
public String myReadLine() throws Exception{
StringBuilder sb = new StringBuilder();
int i = 0;
while((i=reader.read())!=-1){
char c = (char)i;
if(c=='\r'){
continue;
}
if(c=='\n'){
break;
}
sb.append(c);
}
if(sb.length()==0){
return null;
}
return sb.toString();
}
}
五、包装方式的灵活性
System.in:标准输入流,默认关联键盘
通过转换流InputStreamReader 可以将 System.in包装成字符流。
通过BufferedReader将InputStreamReader包装成带有缓冲区的字符流,可以调用readLine().
代码示例
import java.io.InputStreamReader;
import java.io.InputStream;
import java.io.BufferedReader;
public class Test {
public static void main(String[] args) throws Exception{
//源:键盘 目的:文件
//Scanner sc = new Scanner(Sytsem.in);
//实现键盘录入
InputStream is = System.in;
//字节流转换为字符流
InputStreamReader isr = new InputStreamReader(is);
//高效
BufferedReader br = new BufferedReader(isr);
String str = br.readLine();
System.out.println(str);
}
}
六、转换流
有时,我们需要将字节流转换为字符流,使用InputStreamReader和OutputStreamWriter。
1.编码表
GBK:国标码 gb2312,定义的英文字母和中文字符,在GBK编码表中,英文字符占一个字节,中文字符占两个字节。
UTF-8:万国码,定义了全球所有的语言的所有的符号,定义了这些符号和数字的对应关系,英文字母使用一个字节进行存储,中文字符使用三个字节进行存储。
乱码:编码格式和解码格式不一致。
2.输入转换流InputStreamReader
构造方法
InputStreamReader(InputStream in) 创建一个使用默认字符集的InputStreamReader。 |
InputStreamReader(InputStream in, Charset cs) 创建一个使用给定字符集的InputStreamReader。 |
示例代码
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
public class Test {
public static void main(String[] args) throws Exception{
InputStream is = new FileInputStream("E:/test/a.txt");
//字节流转换诶字符流
InputStreamReader isr = new InputStreamReader(is);
//InputStreamReader isr = new InputStreamReader(is,"utf-8");
//InputStreamReader isr = new InputStreamReader(is,"GBK");
//高效缓冲流
BufferedReader br = new BufferedReader(isr);
//特有方法
System.out.println(br.readLine());
System.out.println(br.readLine());
br.close();
}
}
3.输出转换流OutputStreamWriter
构造方法
OutputStreamWriter(OutputStream out) 创建一个使用默认字符编码的OutputStreamWriter。 |
OutputStreamWriter(OutputStream out, Charset cs) 创建一个使用给定字符集的OutputStreamWriter。 |
示例代码
import java.io.OutputStream;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.BufferedWriter;
public class Test {
public static void main(String[] args) throws Exception{
OutputStream os = new FileOutputStream("E:/a.txt");
//转换流
OutputStreamWriter osr = new OutputStreamWriter(os);
//高效缓冲流
BufferedWriter bw = new BufferedWriter(osr);
bw.write("aa");
bw.write("11");
//特有方法
bw.newLine();
bw.write("中国");
bw.close();
}
}
七、其它流
1.LineNumberReader
带有行号的流对象,同时也是BufferedReader子类对象,带有高效缓冲区。
常用方法:
int | 获取当前行号。 | ||
void | setLineNumber(int lineNumber) 设置当前行号。 |
示例代码
import java.io.Reader;
import java.io.FileReader;
import java.io.LineNumberReader;
public class Test {
public static void main(String[] args) throws Exception{
Reader r = new FileReader("E:/a.txt");
//高效缓冲流
LineNumberReader inr = new LineNumberReader(r);
System.out.println(inr.getLineNumber()+":"+inr.readLine());
System.out.println(inr.getLineNumber()+":"+inr.readLine());
System.out.println(inr.getLineNumber()+":"+inr.readLine());
//设置行号
inr.setLineNumber(5);
System.out.println(inr.getLineNumber()+":"+inr.readLine());
System.out.println(inr.getLineNumber()+":"+inr.readLine());
inr.close();
}
}
2.ByteArrayOutputStream
名字是输出流,体系也在IO体系中,但是其实没有在内存和其它设备之间进行交互,仅仅是在内存中做了拷贝,所以没有实现真的IO操作
作用:当从某个文件汇总,读取了一部分不完整的字节信息的时候,需要找一个容器存储这些不完整的信息,ByteArrayOutputStream就可以充当容器,因为这个容器可以自动增长。
本质:一个可自动增长的字节数组,并且提供一些常用的方法。
常用方法
byte[] | 获取对象中的字节信息,返回一个字节数组 | ||
String | toString() 使用平台的默认字符集将缓冲区内容转换为字符串解码字节。 |
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
public class Test {
/*
* 有一个文件,a.txt,有一句话:你好你好你好呀
* 使用一个大小为3的字节数组,将文件中的内容,打印在控制台
* */
public static void main(String[] args) throws Exception {
//method01(); //你?媚?好?愫?呀
//method02(); //你好你好你好呀
//[-60, -29, -70, -61, -60, -29, -70, -61, -60, -29, -70, -61, -47, -67]
}
private static void method01() throws Exception {
FileInputStream fis = new FileInputStream("E:/a.txt");
byte[] buf = new byte[3];
int len = 0;
while((len=fis.read(buf))!=-1) {
System.out.print(new String(buf,0,len));
}
fis.close();
}
private static void method02() throws Exception {
FileInputStream fis = new FileInputStream("E:/a.txt");
//准备一个用缓存不完整字节信息的容器,容器可以自动增长,不需要指定大小
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[3];
int len = 0;
while((len=fis.read(buf))!=-1) {
//System.out.println(new String(buf,0,len));
//本次读取到多个字节,就写到字节输出流中
baos.write(buf, 0, len);
}
//将对象中的字节信息,转成字符串
System.out.println(baos.toString());
//将对象中的完整的字节信息返回
byte[] arr = baos.toByteArray();
System.out.println(Arrays.toString(arr));
baos.close();
}
}
3.随机访问流RandomAccessFile
支持对文件进行非顺序的、随机的访问。
不是流对象,不在IO体系中,但是比较流对象更加的强大,既可以读,也可以写,又可以随机访问。
(1)构造方法
RandomAccessFile(File file, String mode) 创建一个随机访问文件流从File参数指定的文件中读取,并可选地写入文件。 |
RandomAccessFile(String name, String mode) 创建随机访问文件流,以从中指定名称的文件读取,并可选择写入文件。 |
mode:操作文件的模式:r只读模式,rw读写模式
(2)常用方法
read():读取一个字节
readXxx():可以将基本类型直接从文件中读取
read(buf):读取的内存存储到数组中
write(int b):将一个字节的内容写到文件中
write(buf):将字节数组的数据写文件中
writeXxx():可以将基本数据类型写到文件中
readLine():读取一行
seek(long pos):用于设定光标的位置,从指定位置开始读写
它实现了DataInput 和DataOutput 接口,并提供了三个额外方法:
int skipBytes(int) 向前移动文件指针指定的字节数
void seek(long) 文件指针定位,参数为偏移量
long getFilePointer()返回当前文件指针的位置(相对文件首的偏移量)
代码示例
import java.io.RandomAccessFile;
public class Test {
public static void main(String[] args) throws Exception{
//abcdefg
RandomAccessFile raf = new RandomAccessFile("a.txt","rw");
raf.skipBytes(2);//向后移动2个字节,再开始读数据
System.out.println(raf.read());//99
raf.seek(5);//直接把指针定位的某个位置
System.out.println(raf.read());//102
long p = raf.getFilePointer();
System.out.println(p);//6
raf.close();
}
}
4.PrintStream和PrintWriter
(1)PrintStream
打印字节流:System.out就是这个类型的流对象。
在该类型中,提供了很多的print和println的方法,可以将各种数据类型转换成字符串进行输出。
注意事项:
1、如果直接使用System.out的方式来获取PrintStream的对象,这个对象默认关联的输出目的地就是控制台;如果手动创建PrintStream的对象,那么关联的就是指定的设备。
2、PrintStream中重载了很多print和println的方法,有两个比较特殊,print(char[] arr)、print(Object obj)。一般的数组使用print方法,会直接打印数组的地址,唯独字符数组打印之后出现的是数组的内容。普通的数组调用的都是println(Object obj)方法,先获取了数组的toString的内容,也就是数组的地址,而字符数组调用的是println(char[] arr)方法,没有运行String.valueOf方法,没有获取数组的toString内容,而是直接调用了write方法用于输出字符内容。
构造方法
PrintStream(File file) 使用指定的文件创建一个新的打印流,而不需要自动换行。 |
PrintStream(OutputStream out) 创建一个新的打印流。 |
PrintStream(OutputStream out, boolean autoFlush) 创建一个新的打印流。 |
代码示例
import java.io.FileOutputStream;
import java.io.PrintStream;
public class Test {
public static void main(String[] args) throws Exception{
//FileOutputStream fis = new FileOutputStream("E:/test/a.txt");//a.txt存在,内容被覆盖
FileOutputStream fis = new FileOutputStream("E:/test/a.txt",true);//a.txt存在,追加不覆盖
PrintStream ps = new PrintStream(fis);
ps.println("Hello");
ps.println(true);
ps.println(123);
ps.close();
}
}
(2)PrintWriter
构造方法
PrintWriter(File file) |
PrintWriter(File file, String csn) |
PrintWriter(OutputStream out) |
PrintWriter(OutputStream out, boolean autoFlush) |
PrintWriter(String fileName) |
PrintWriter(String fileName, String csn) |
PrintWriter(Writer out) |
PrintWriter(Writer out, boolean autoFlush) |
注:
1.该类中除了有write方法外,还提供了print方法和println方法,用来写数据。
2.autoFlush:代表是否自动刷新,默认为flase。如果不刷新,写入的数据存在缓冲区中,不会在文件中。但是close方法在调用前默认先调用flush方法。
FileWriter fw = new FileWriter("1.txt",true);
第二个参数代表是否追加,如果设置true,代表追加,如果设置为false,直接产生覆盖,默认为false。
练习:使用BufferedReader和PrintWriter完成文件拷贝。
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.PrintWriter;
public class Test {
public static void main(String[] args) throws Exception{
FileReader fr = new FileReader("E:/test/a.txt");
BufferedReader br = new BufferedReader(fr);
PrintWriter pw = new PrintWriter("E:/a.txt");
String str = null;
while((str=br.readLine())!=null){
pw.println(str);
}
br.close();
pw.close();
}
}
5.标准输入流和输出流
标准输入流:System.in
InputStream 字节输入流
设备:键盘
包装:BufferedInputStream
标准输出流:System.out
PrintWriter
设备:默认关联到控制台
print println()
6.序列流SequenceInputStream(也称合并流)
构造方法
SequenceInputStream(Enumeration<? extends InputStream> e) 初始化新创建 SequenceInputStream通过记住参数,它必须是一个 Enumeration产生对象,它们的运行时类型是 InputStream 。 |
SequenceInputStream(InputStream s1, InputStream s2) 通过记住两个 SequenceInputStream来初始化新创建的SequenceInputStream,这些参数将按顺序读取,首先是 s1 ,然后是 s2 ,以提供要从此 SequenceInputStream读取的字节。 |
示例代码:合并两个流
import java.io.SequenceInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
class Test {
public static void main(String[] args) throws Exception{
FileInputStream fis1 = new FileInputStream("E:/a.txt");
FileInputStream fis2 = new FileInputStream("E:/b.txt");
SequenceInputStream s1 = new SequenceInputStream(fis1,fis2);
int len = 0;
byte[] byt = new byte[1024];
FileOutputStream fos = new FileOutputStream("E:/c.txt");
while((len=s1.read(byt))!=-1){
fos.write(byt,0,len);
}
s1.close();
}
}
示例代码:合并多个流
import java.io.InputStream;
import java.io.FileInputStream;
import java.util.LinkedHashSet;
import java.util.Iterator;
import java.util.Enumeration;
import java.io.SequenceInputStream;
import java.io.FileOutputStream;
class Test {
public static void main(String[] args) throws Exception{
InputStream in1 = new FileInputStream("E:/a.txt");
InputStream in2 = new FileInputStream("E:/b.txt");
InputStream in3 = new FileInputStream("E:/c.txt");
LinkedHashSet<InputStream> set = new LinkedHashSet<InputStream>();
set.add(in1);
set.add(in2);
set.add(in3);
final Iterator<InputStream> iter = set.iterator();
SequenceInputStream sin = new SequenceInputStream(
new Enumeration<InputStream>(){
@Override
public boolean hasMoreElements(){
return iter.hasNext();
}
@Override
public InputStream nextElement(){
return iter.next();
}
});
FileOutputStream out = new FileOutputStream("E:/d.txt");
for(int b=-1;(b=sin.read())!=-1;){
out.write(b);
}
sin.close();
out.close();
}
}
7.数据流
数据流支持二进制的IO--基本数据类型的值(boolean,char,byte,double等)以及String值。所有的数据流,实现了DataInput/DataOutput接口。
(1)DataOutputStream
常见方法
void | write(int b) 将指定的字节(参数 b的低8位)写入底层输出流。 |
void | writeInt(int v) 将底层输出流写入 int作为四字节,高位字节。 |
void | writeBoolean(boolean v) 将 boolean写入底层输出流作为1字节值。 |
void | writeShort(int v) 将 short写入底层输出流作为两个字节,高字节优先。 |
void | writeLong(long v) 将 long写入底层输出流,为8字节,高字节为首。 |
void | writeUTF(String str) 使用 modified UTF-8编码以机器无关的方式将字符串写入基础输出流。 |
代码示例
import java.io.DataOutputStream;
import java.io.FileOutputStream;
class Test {
public static void main(String[] args) throws Exception{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("a.txt"));
//dos.write(12);//只会写int的后八位,前面的都舍弃了
dos.writeInt(23);
dos.writeBoolean(true);
dos.writeUTF("hello");
}
}
(2)DataInputStream
常见方法
int | readInt() 一次读取四个字节,并将其转成int值。 |
short | 一次读取两个字节,并将其转成int值。 |
long | readLong() 一次读取八个字节,并将其转成int值。 |
String | readUTF() 按照utf-8修改版读取字符。注意,它只能读writeUTF()写入的字符数据。 |
代码示例
import java.io.DataInputStream;
import java.io.FileInputStream;
class Test {
public static void main(String[] args) throws Exception{
DataInputStream dis = new DataInputStream(new FileInputStream("a.txt"));
int x = dis.readInt();
boolean f = dis.readBoolean();
String str = dis.readUTF();
}
}
8.对象序列化(对象流)
当创建对象时,程序运行时它就会存在,但是程序停止时,对象也就消失了.但是如果希望对象在程序不运行的情况下仍能存在并保存其信息,将会非常有用,对象将被重建并且拥有与程序上次运行时拥有的信息相同。可以使用对象的序列化。
对象的序列化:将内存中的对象直接写入到文件设备中或者在网络上传输。
对象的反序列化:将文件设备中持久化的数据转换为内存对象。
基本的序列化由两个方法产生:一个方法用于序列化对象并将它们写入一个流,另一个方法用于读取流并反序列化对象。
(1)ObjectOutputStream
OutputStream的子类
构造方法
| ObjectOutputStream(OutputStream out) 将一个普通的字节输出流包装成对象输出流 |
最常用方法
void | writeObject(Object obj) 将指定的对象写入ObjectOutputStream。 |
代码示例:序列化
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.io.File;
class Test {
public static void main(String[] args) throws Exception{
Cat cat = new Cat("tom",3);
FileOutputStream fos = new FileOutputStream(new File("E:/cat.txt"));
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(cat);
System.out.println(cat);
oos.close();
}
}
class Cat implements Serializable{
String name;
int age;
public Cat(){}
public Cat(String name,int age){
this.name=name;
this.age=age;
}
public String toString(){
return "name:"+name+"...age:"+age;
}
}
注意:Cat类的对象,想要被保存到文件中,Cat类必须实现Serializable接口
(2)ObjectInputStream
常用方法
Object | 从ObjectInputStream读取一个对象。 |
代码示例:反序列化
import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.io.File;
class Test {
public static void main(String[] args) throws Exception{
FileInputStream fis = new FileInputStream(new File("E:/cat.txt"));
ObjectInputStream ois = new ObjectInputStream(fis);
Object readObject = ois.readObject();
Cat cat2 = (Cat) readObject;
System.out.println(cat2);
fis.close();
}
}
class Cat implements Serializable{
String name;
int age;
public Cat(){}
public Cat(String name,int age){
this.name=name;
this.age=age;
}
public String toString(){
return "name:"+name+"..."+"age:"+age;
}
}
(3)注意事项
连续序列化对象:当连续两次序列化同一对象引用时,并不会有两个对象被序列化。第2次只是输出一个序列号编号。即使当第一序列化完成后,修改了对象的值,再次进行序列化,被序列化对象的值也不会发生变化。
示例代码
import java.io.ObjectOutputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.FileInputStream;
import java.io.Serializable;
class Test {
public static void main(String[] args) throws Exception{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("object.txt"));
Student stu = new Student(1,"zhangsan",20);
oos.writeObject(stu);
stu.name="李四";
//和第一次写的是同一个对象,第二次序列化时并不会有两个对象被序列化。第二次只是输出一个序列号编号
oos.writeObject(stu);
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("object.txt"));
Student stu1 = (Student)ois.readObject();
System.out.println(stu1);
Student stu2 = (Student)ois.readObject();
System.out.println(stu2);
//Student stu3 = (Student)ois.readObject();
//System.out.println(stu3);//只存了两次,第3次报错
ois.close();
/*id:1...name:zhangsan...age:20
id:1...name:zhangsan...age:20
*/
}
}
class Student implements Serializable{
int id;
String name;
int age;
public Student(){}
public Student(int id,String name,int age){
this.id=id;
this.name=name;
this.age=age;
}
public String toString (){
return "id:"+id+"...name:"+name+"...age:"+age;
}
}
9.压缩流
压缩流 ZipInputStream 和 解压缩流 ZipOutputStream 位于java.util包
(1)压缩流ZipOutputStream
代码示例
package com.xj.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class Zos {
ZipOutputStream zos = null;
public Zos (){
try {
zos= new ZipOutputStream(new FileOutputStream("D:/xyz.zip"));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
File f = new File("E:/aaaa");
Zos zzz = new Zos ();
zzz.zip(f);
zzz.close();
}
// 传入要压缩的文件夹
public void zip(File file){
File[] fs = file.listFiles();
for(File f:fs){
if(f.isFile()){
zipFile(f);
}else{
zip(f); // 是文件夹,递归调用
}
}
}
//压缩文件
public void zipFile(File f){
try {
// 如果传入ZipEntry的文件名带有目录,在压缩时,压缩包中就会生成目录
zos.putNextEntry(new ZipEntry(f.getPath().substring(3)));
// 一个ZipEntry是一个要被压缩的项
InputStream is = new FileInputStream(f);
byte[] b = new byte[1024];
int len = 0;
while((len=is.read(b))!=-1){
zos.write(b,0, len);
}
zos.closeEntry();
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void close(){
try {
zos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
(2)解压缩流ZipInputStream(了解)
代码示例
package com.xj.Test;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
public class UnZip {
public static void main(String[] args) throws Exception{
File file = new File("D:/xyz.zip");// 指向要被解压的zip文件
ZipInputStream zis = new ZipInputStream(new FileInputStream(file)); // 读取zip格式压缩的文件
ZipFile zf = new ZipFile(file); // 用来获取流 读取被压缩的文件
ZipEntry entry = null;
InputStream is = null;
OutputStream os = null;
while((entry=zis.getNextEntry())!=null){
if(entry.isDirectory()){ // entry是一个文件夹
File ff = new File(entry.getName());
ff.mkdirs(); // 创建文件夹
}else{
System.out.println("解压缩" + entry.getName() + "......");
File outFile = new File("D:/" + entry.getName()); // 定义输出文件
if (!outFile.getParentFile().exists()) {
outFile.getParentFile().mkdirs();
}
if (!outFile.exists()) {
outFile.createNewFile();
}
is = zf.getInputStream(entry);
os = new FileOutputStream(outFile);
byte[] b = new byte[1024];
int len = 0;
while ((len = is.read(b)) != -1) {
os.write(b, 0, len);
}
}
}
is.close();
os.close();
zis.close();
}
}
八、Properties
1.概述
Properties对象表示一个持久的属性集
属性集:属性名称和属性值的对应关系,其实还是一个双列集合
持久的:可以保存到流中,也可以从流中读取。可以很方便和文件进行交互
Properties没有泛型,因为表示一个配置文件中的信息,而配置文件中都是字符串,所以Properties类型不需要使用广泛的类型,存储的键和值都是字符串类型
Properties是Hashtable的子类,所以可以当做普通的Map来使用。
2.特有的方法
String | getProperty(String key) 使用此属性列表中指定的键搜索属性。 | ||
Object | setProperty(String key, String value) 致电 Hashtable方法 put 。 | ||
Enumeration<?> |
代码示例
import java.util.Properties;
import java.util.Enumeration;
public class Test {
public static void main(String[] args) throws Exception{
Properties props = new Properties();
props.setProperty("01","zz");
props.setProperty("02","xx");
props.setProperty("03","cc");
System.out.println(props.get("01"));//zz
Enumeration enumeration = props.propertyNames();
while(enumeration.hasMoreElements()){
String key = (String)enumeration.nextElement();
System.out.println(key+".."+props.getProperty(key));
}
/*
03..zz
02..xx
01..cc
*/
}
}
3.Properties和配置文件交互的方式
void | load(InputStream inStream) 从输入字节流读取属性列表(键和元素对)。 |
void | store(Writer writer, String comments) 将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式输出到输出字符流。 |
load:输入流到集合中
store:集合到输出流
注意事项
1、使用prop修改了属性值之后,文件中的属性值不会发生变化,因为prop只修改了内存中的对象数据,没有同步到文件中。必须调用store方法之后,才能将变化同步到文件。
2、调用store方法时,一般需要指定更新属性的原因,即第二个参数comments,如果没有注释,可以传入null;如果有注释,必须是纯英文注释。
代码示例
import java.util.Properties;
import java.io.FileInputStream;
import java.io.FileWriter;
public class Test {
public static void main(String[] args) throws Exception{
Properties props = new Properties();
FileInputStream fis = new FileInputStream("E:/test/config.properties");
//将流中的内容,加载到属性集合容器中
props.load(fis);
System.out.println(props);
props.setProperty("01","GG");
FileWriter fw = new FileWriter("E:/test/config.properties");
//将集合中的数据加载到输出流-->文件
props.store(fw, "zz-->GG");
}
}
案例:一个软件,免费使用3次,超过3次要续费。
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.util.Properties;
public class Test {
public static void main(String[] args) throws Exception {
Properties props = new Properties();
FileInputStream fis = new FileInputStream("count.properties");
props.load(fis);
String value = props.getProperty("count");
int count = 0;
if(value==null) {
//第一次使用
props.setProperty("count", "1");
count = 1;
}else {
value = props.getProperty("count");
if("3".equals(value)) {
System.out.println("亲,您好,软件免费次数已使用完毕,请续费!");
return;
}
count = Integer.parseInt(value)+1;
props.setProperty("count", count+"");
}
System.out.println("欢迎您第"+count+"次使用本软件!");
FileWriter fw = new FileWriter("count.properties");
props.store(fw,null);
}
}
九、IO中保证流对象关闭的标准格式
1.捕获异常和声明异常的原则
如果知道如何处理异常,那么就使用try...catch来进行处理;如果不知道如何处异常,那么就使用throws来对异常进行声明,或者当将当前的异常对象封装成其它对象,优化处理。
如果你希望程序出现异常之后,继续运行下去,那么就使用try...catch处理,如果出现异常之后,希望当前的方法代码进行停止,那么就使用throws。
2. IO中保证流对象关闭的格式(jdk1.7之前)
将流对象创建使用与流对象的关闭,分离开,分别放在try...finally,就算流对象在创建或者使用的过程发生异常,流对象照样关闭。
为了使对象的引用泛型更大一些,定义在try...catch..finally之外
为了避免在关闭流对象的时候,出现空指针异常,做一个判断为空的操作
为让保证同时让两个流对象在关闭时互不影响,分别写在try...finally块中
代码示例
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
class Test {
public static void main(String[] args){
FileReader fr = null;
FileWriter fw = null;
try {
fr = new FileReader("1.txt");
fw = new FileWriter("2.txt");
int i = 0;
while((i=fr.read())!=-1) {
fw.write(i);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if(fr!=null) {
fr.close();
}
} catch (IOException e) {
e.printStackTrace();
}finally {
if(fw!=null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}
3.IO中保证流对象关闭的格式(jdk1.7之后)
格式:try(
流对象的创建;
){
流对象的使用;
}
流对象使用之后,不需要手动关闭,因为这个格式已经帮助咱们自动闭了流对象。
代码示例
import java.io.FileReader;
import java.io.FileWriter;
public class Test {
public static void main(String[] args) throws Exception {
try(
FileReader fr = new FileReader("1.txt");
FileWriter fw = new FileWriter("2.txt");
){
int i = 0;
while((i=fr.read())!=-1) {
fw.write(i);
}
}
}
}