目录
对象的序列化 ObjectOutputStream,ObjectInputStream:
*转换流* InputStreamReader,OutputStreamWriter:
File类
数据一般是以文件的形式保存到硬盘上,sun使用了一个File类描述了文件或者文件夹的。
构造方法:
File File(String pathname) 指定文件或者文件夹的路径创建一个File文件。
File File(File parent, String child) 根据 parent 抽象路径名和 child 路径名字符串创建一个新 File 实例。
File File(String parent, String child) 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。
创建:
createNewFile() 在指定位置创建一个空文件,成功就返回true,如果已存在就不创建然后返回false
mkdir() 在指定位置创建目录,这只会创建最后一级目录,如果上级目录不存在就抛异常。
mkdirs() 在指定位置创建目录,这会创建路径中所有不存在的目录。
renameTo(File dest) 重命名文件或文件夹,路径不同时相当于文件的剪切,剪切时候不能操作文件夹。移动/重命名成功则返回true,失败则返回false。
删除:
delete() 删除文件或一个空文件夹,如果是文件夹且不为空,则不能删除,成功返回true,失败返回false。
deleteOnExit() 在jvm终止时,请求删除此抽象路径名表示的文件或目录,保证程序异常时创建的临时文件也可以被删除(一般用于删除临时文件)
判断:
exists() 文件或文件夹是否存在。
isFile() 是否是一个文件,如果是文件返回true,否则为false。
isDirectory() 是否是一个目录,如果是文件夹返回true,否则为false。
isHidden() 是否是一个隐藏的文件或目录。
isAbsolute() 测试此抽象路径名是否为绝对路径名。
获取:
getName() 获取文件或文件夹的名称,不包含上级路径。
getPath() 返回绝对路径,可以是相对路径,但是目录要指定
getAbsolutePath() 获取文件的绝对路径,与文件是否存在没关系
length() 获取文件的大小(字节数),如果文件不存在则返回0L,如果是文件夹也返回0L。
getParent() 返回此抽象路径名父目录的路径名字符串;如果此路径名没有指定父目录,则返回null。
lastModified() 获取最后一次被修改的时间(毫秒值)。
文件夹相关:
listRoots() 列出所有的根目录(Window中就是所有系统的盘符)
list() 把当前文件夹下面的所有子文件名与子文件夹名存储到一个String类型的数组中返回,包含隐藏文件。对于文件这样操作会返回null。
listFiles() 把当前文件夹下面的所有子文件与子文件夹都使用了一个File对象描述,然后把这些File对象存储到一个File数组中返回,包含隐藏文件。对于文件这样操作会返回null。
文件名过滤器:用FilenameFilter接口下accept(File dir, String name)方法判断,返回true则获取
list(FilenameFilter filter) 返回当前目录中符合过滤条件的子文件或子目录的String数组。对于文件这样操作会返回null。
listFiles(FilenameFilter filter) 返回当前目录中符合过滤条件的子文件或子目录的File数组。对于文件这样操作会返回null。
路径:
绝对路径:该文件在硬盘上的完整路径。绝对路径一般都是以盘符开头的。
相对路径:相对路径就是资源文件相对于当前程序所在的路径。
目录分隔符:在windows机器上的目录分隔符是 \ ,在linux机器上的目录分隔符是 /
注意:在windows上面 \ 与 / 都可以使用作为目录分隔符。 而且如果写 / 的时候只需要写一个即可。更专业的做法是使用File.separatorChar这个值就会根据系统得到的相应的分割符。
对于UNIX平台,绝对路径名的前缀是"/"。相对路径名没有前缀。
对于Windows平台,绝对路径名的前缀由驱动器号和一个":"组成,例"c:\\..."。相对路径没有盘符前缀。
. 当前路径
.. 上一级路径
注意: 如果程序当前所在的路径与资源文件不是在同一个盘下面,是没法写相对路径的。
IO流
通过File对象可以读取文件或者文件夹的属性数据,但如果需要读取文件的内容数据,那么要使用IO流技术。
IO技术主要的作用是解决设备与设备之间的数据传输问题。
按照处理的单位划分:
字节流: 字节流读取得都是文件中二进制数据,读取到二进制数据不会经过任何的处理。
字符流: 字符流读取的数据是以字符为单位的。字符流也是读取文件中的二进制数据,不过会把这些二进制数据转换成我们能识别的字符(字符流 = 字节流 + 解码)
按流向分为:输入流,输出流(以程序为参照物,输入到程序,或是从程序输出)
输入字节流 InputStream:
- |——InputStream 所有输入字节流的父类( 抽象类)
- |——FileInputStream 读取文件数据的输入字节流
- |——BufferedInputStream 缓冲输入字节流,主要是为了提高读取文件数据的效率(其实该类内部只不过是维护了一个8kb的字节数组)
-------------------------------FileInputStream---------------------------------------------------
FileInputStream读取文件数据的步骤:
- 找到目标文件。
- 建立数据的输入通道。
- 读取文件中的数据。(int read()方法,读到达文件末尾,则返回 -1)
- 关闭资源
//1.找到目标文件 File file = new File("文件路径"); //2.建立数据的输入通道 FileInputStream fileInputStream = new FileInputStream(file); //3.读取数据 //3.1 read() 读取返回下一个字节的数据,如果已到达文件末尾,则返回 -1 int content = fileInputStream.read(); //*3.2*(使用缓冲数组读取) 将最多buf.length个字节的数据读入byte数组中(覆盖存储)。返回本次读取字节总数,末尾返回-1 fileInputStream.read(byte[] buf); //3.3 将最多len个字节的数据读入一个 byte 数组中。返回读取字节总数,末尾返回-1 fileInputStream.read(byte[] buf,int off,int len); //4.关闭资源 fileInputStream.close();
注意://3.2读取文件的时候使用缓冲数组读取,效率会更加高。(缓冲数组的长度一般是1024的倍数,理论上缓冲数组越大效率越高)
---------------------------BufferedInputStream--------------------------------------------------
使用BufferedInputStream的步骤 :
- 找到目标文件。
- 建立数据的输入通道
- 建立缓冲输入字节流
- 读取文件数据
- 关闭资源
//1.找到目标文件 File file = new File("文件路径"); //2.建立数据输出通道 FileInputStream fileInputStream= new FileInputStream(file); //3.建立缓冲输入字节流 BufferedInputStream bufferedInputStream= new BufferedInputStream(fileInputStream); //4.读取文件数据 注:实际上还是借助FileInputStream的read方法读取文件数据 int content = bufferedInputStream.read();//读取文件数据,内部维护了一个8kb缓冲字节数组,提高效率 //5.关闭资源 bufferedInputStream.close();//调用BufferedInputStream的close方法实际上关闭的还FileinputStream
注意: 凡是缓冲流都不具备读写文件的能力。其实都是借助FileInputStream读取文件数据,缓冲流内部只不过是维护了一个8kb的字节数组而已。读取时会先用fill()方法往内存中的buf数组填充最大8kb的数据,然后从buf数组中一个一个字节读取数据到程序。所以读取效率比FileInputStream的3.1高(与3.2自己创建缓冲字节数组读取数据没区别)
输出字节流 OutputStream:
- |——OutputStream 所有输出字节流的父类( 抽象类)
- |——FileOutStream 向文件输出数据的输出字节流
- |——Bufferedoutputstream 缓冲输出字节流,目的是为了提高写数据的效率。(内部也是维护了一个8kb的字节数组而已)
------------------------------FileOutputStream--------------------------------------------------
FileOutputStream写入文件数据的步骤:
- 找到目标文件
- 建立数据的输出通道。
- 把数据转换成字节数组写出。
- 关闭资源
//1.找到目标文件 File file = new File("文件路径"); //2.建立数据输出通道 FileOutputStream fileOutputStream = new FileOutputStream(file); //3.把数据写出。 //3.1 一次写出一个字节 fileOutputStream.write(int b); //3.2 将b.length个字节从指定的byte数组写入此输出流中,使用缓冲提高效率 fileOutputStream.write(byte[] b); //3.3 将byte数组中从偏移量off开始的len个字节写入此文件输出流 fileOutputStream.write(byte[] b,int off,int len); //4.关闭资源 fileOutputStream.close();
注意细节:
- 使用FileOutputStream 的时候,如果目标文件不存在,那么会自动创建目标文件对象。但是创建不了多级目录
- 使用FileOutputStream写数据的时候,如果目标文件已经存在,那么会先清空目标文件中的数据,然后再写入数据,FileOutputStream每次创建新对象时输入指针默认指向文件开始位置,每写入一次,指向都会出现相应移动)
- 使用FileOutputStream写数据的时候, 如果目标文件已经存在,需要在原来数据基础上追加数据的时候应该使用new FileOutputStream(file,boolean)构造函数,第二参数为true。
- 使用FileOutputStream的write(int b)方法写数据的时候,虽然接收的是一个int类型的数据,但是真正写出的只是一个字节的数据,只是把低八位的二进制数据写出,24高位数据全部丢弃。
-----------------------------BufferedOutputStream--------------------------------------------
使用BufferedOutputStream的步骤:
- 找到目标文件。
- 建立数据的输出通道
- 建立缓冲输出字节流流
- 写出文件数据
- 关闭资源
//1.找到目标文件 File file = new File("文件路径"); //2.建立数据输出通道 FileOutputStream fileOutputStream = new FileOutputStream(file); //3.建立缓冲输出字节流流 BufferedOutputStream bufferedOutputStream= new BufferedOutputStream(fileOutputStream); //4.写出文件数据 bufferedOutputStream.write(int b); //只是把数据写到内部维护的字节数组中,不写进硬盘 bufferedOutputStream.write(byte[] b); bufferedOutputStream.flush(); //把内部字节数组中数据写进硬盘 //5.关闭资源 bufferedInputStream.close();//调用BufferedInputStream的close方法实际上关闭的还FileinputStream
注意细节:使用BufferedOutStream写数据的时候,它的write方法只把数据写到它内部维护的字节数组中,需要调用flush方法或close方法、 或者是内部维护的字节数组已经填满数据的时候,才会把数据真正的写到硬盘上面
字节流的异常处理
try {
//1.找到目标文件
//2.建立数据输入通道
//3.建立缓冲数组读取数据
} catch (IOException e) {
//处理代码用throw new RuntimeException(e);首先阻止后面的代码执行,而且通知调用者这里出错了
//把IOException传递给RuntimeException包装一层,然后再抛出,这样做的目的是为了让调用者使用变得更加灵活。
System.out.println("读取文件资源出错....");
throw new RuntimeException(e);
} finally {
try {
if (fileInputStream != null) {
fileInputStream.close();
System.out.println("关闭资源成功...");
}
} catch (IOException e) {
System.out.println("关闭资源失败...");
throw new RuntimeException(e);
}
}
字符流会自动解码,如果读写的数据都不需要转换成字符的时候,则使用字节流。例如读取图片不能用字符流
输入字符流 Reader :
- |——Reader 输入字符流的基类( 抽象类)
- |——FileReader 读取文件的输入字符流,读取+解码(默认使用GBK编码表)
- |——BufferedReader 缓冲输入字符流 。目的是为了提高读取文件的效率和拓展了FileReader的功能。(其实该类内部也是维护了一个字符数组)
------------------------------------FileReader------------------------------------------------------
FileReader的用法:
- 找到目标文件
- 建立数据的输入通道
- 读取数据
- 关闭资源
//1.找到目标文件 File file = new File("文件路径"); //2.建立数据的输入通道 FileReader fileReader = new FileReader(file); //3.读取数据 int content = fileReader.read(); //读取单个(字符),到达流的末尾返回-1。 每次只会读取一个字符,效率低 fileReader.read(char[] cbuf); //将字符读入数组,返回读取的字符总数,末尾返回-1。 建立缓冲字符数组读取,效率高 //4.关闭资源 fileReader.close();
注意:如果读到的数据在解码时找不到对应的字符编码,那么返回一个“未知字符”对应的数字编码,占一个字节
--------------------------------BufferedReader--------------------------------------------------
BufferedReader的使用步骤:
- 找到目标文件
- 建立数据的输入通道
- 建立缓冲输入字符流
- 读取数据
- 关闭资源
//1.找到目标文件 File file = new File("文件路径"); //2.建立数据的输入通道 FileReader fileReader = new FileReader(file); //3.建立缓冲输入字符流 BufferedReader bufferedReader = new BufferedReader(fileReader); //4.读取数据 int content = bufferedReader.read(); //4.1 读到一个字符。读取到的字符也是从Bufferedreader内部的字符数组中获取的,所以效率高 String line = bufferedReader.readLine();//4.2 一次读取一行文本,如果读到了文件的末尾返回null表示。 注意:返回值是不包含\r\n的 //5.关闭资源 bufferedReader.close();
输出字符流 Writer:
- |——Writer 输出字符流的基类(抽象类)
- |——FileWriter 向文件数据数据的输出字符流
- |——BufferedWriter 缓冲输出字符流,作用是提高FileWriter的写数据效率与拓展FileWriter的功能(内部提供了一个8kb的字符数组作为缓冲区,拓展了FileWriter的功能)
------------------------------------FileWrite----------------------------------------------------------
FileWriter的使用步骤:
- 找到目标文件
- 建立数据输出通道
- 写出数据
- 关闭资源
//1.找到目标文件 File file = new File("文件路径"); //2.建立数据输出通道 FileWriter fileWriter = new FileWriter(file,true); //3.写出数据 注:字符流具备编码的功能 fileWriter.write(int c); //只是把数据写到内部维护的字节数组中,不写进硬盘 fileWriter.write(char[] cbuf, int off, int len); fileWriter.write(String str, int off, int len); fileWriter.flush(); //4.关闭资源 fileWriter.close();
注意事项:
- 使用FileWriter写数据的时候,FileWriter内部是维护了一个1024个字符数组的,写数据的时候会先写入到它内部维护的字符数组中,如果需要把数据真正写到硬盘上,需要调用flush或者是close方法或者是填满了内部的字符数组。
- 使用FileWriter的时候,如果目标文件不存在,那么会自动创建目标文件。
- 使用FileWriter的时候, 如果目标文件已经存在了,那么默认情况会先情况文件中的数据,然后再写入数据 ,如果需要在原来的基础上追加数据,需要使用new FileWriter(File,boolean)构造方法,第二参数为true。
---------------------------BufferedWriter-----------------------------------------------------
BufferedWriter的使用步骤:
- 找到目标文件
- 建立数据的输出通道
- 建立缓冲输出流对象
- 写出数据
- 关闭资源
//1.找到目标文件 File file = new File("文件路径"); //2.建立数据输出通道 FileWriter fileWriter = new FileWriter(file,true); //3.建立缓冲输出流对象 BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); //4.写出数据 bufferedWriter.write(); bufferedWriter.newLine(); //写入一个行分隔符(\r\n) bufferedWriter.flush(); //5.关闭资源 bufferedWriter.close();
序列流 SequenceInputStream
也称合并流,对多个流进行合并
表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。
//使用SequenceInputStream合并文件。
//1.找到目标文件
File inFile1 = new File("F:\\a.txt");
File inFile2 = new File("F:\\b.txt");
File outFile = new File("F:\\c.txt");
//2.建立数据的输入输出通道
FileOutputStream fileOutputStream = new FileOutputStream(outFile);
FileInputStream fileInputStream1 = new FileInputStream(inFile1);
FileInputStream fileInputStream2 = new FileInputStream(inFile2);
//3.建立序列流对象
SequenceInputStream inputStream = new SequenceInputStream(fileInputStream1,fileInputStream2);
byte[] buf = new byte[1024];
int length = 0 ;
while((length = inputStream.read(buf))!=-1){
fileOutputStream.write(buf,0,length);
}
//4.关闭资源
inputStream.close();
fileOutputStream.close();
//把三个文件合并成一个文件
//1.找到目标文件
File file1 = new File("F:\\a.txt");
File file2 = new File("F:\\b.txt");
File file3 = new File("F:\\c.txt");
File file4 = new File("F:\\d.txt");
//2.建立对应的输入输出流对象
FileOutputStream fileOutputStream = new FileOutputStream(file4);
FileInputStream fileInputStream1 = new FileInputStream(file1);
FileInputStream fileInputStream2 = new FileInputStream(file2);
FileInputStream fileInputStream3 = new FileInputStream(file3);
//3.创建序列流对象
Vector<FileInputStream> vector = new Vector<FileInputStream>();
vector.add(fileInputStream1);
vector.add(fileInputStream2);
vector.add(fileInputStream3);
Enumeration<FileInputStream> e = vector.elements(); //通过Vector获取迭代器
SequenceInputStream sequenceInputStream = new SequenceInputStream(e);
//4.读取文件数据
byte[] buf = new byte[1024];
int length = 0;
while((length = sequenceInputStream.read(buf))!=-1){
fileOutputStream.write(buf,0,length);
}
//5.关闭资源
sequenceInputStream.close();
fileOutputStream.close();
对象的序列化 ObjectOutputStream,ObjectInputStream:
对象的序列化: 将内存中的对象直接写入到文件设备中
对象的反序列化: 将文件设备中持久化的数据转换为内存对象
基本的序列化由两个方法产生:一个方法用于序列化对象并将它们写入一个流,另一个方法用于读取流并反序列化对象。
对象的输入输出流主要的作用是用于写对象的信息与读取对象的信息。 对象信息一旦写到文件上那么对象的信息就可以做到持久化
- 对象的输出流:ObjectOutputStream writeObject(Object obj) 方法,将对象写入底层存储或流
- 对象的输入流:ObjectInputStream readObject() 方法,读取并返回对象
//创建user、Address对象
class Address implements Serializable{
String country;
String city;
public Address(String country,String city){
this.country = country;
this.city = city;
}
}
class User implements Serializable{
private static final long serialVersionUID = 1L;
String userName ;
String password;
transient int age; // transient 透明,不会进行序列化
Address address ;
public User(String userName , String passwrod,int age,Address address) {
this.userName = userName;
this.password = passwrod;
this.age = age;
this.address = address;
}
public String toString() {
return "用户名:"+this.userName+ " 密码:"+ this.password+" 年龄:"+this.age+" 地址:"+this.address.country+this.address.city;
}
}
//一.定义方法把对象的信息写到硬盘上------>对象的序列化。
public static void writeObj() throws IOException{
//1.把user对象的信息持久化存储。
Address address = new Address("中国","广州");
User user = new User("admin","123",15,address);
//2.找到目标文件
File file = new File("F:\\obj.txt");
//3.建立数据输出流对象
FileOutputStream fileOutputStream = new FileOutputStream(file);
//4.建立对象的输出流对象
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
//5.把对象写出
objectOutputStream.writeObject(user);
//6.关闭资源
objectOutputStream.close();
}
//二.把文件中的对象信息读取出来-------->对象的反序列化
public static void readObj() throws IOException, ClassNotFoundException{
//1.找到目标文件
File file = new File("F:\\obj.txt");
//2.建立数据的输入通道
FileInputStream fileInputStream = new FileInputStream(file);
//3.建立对象的输入流对象
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
//4.读取对象信息
User user = (User) objectInputStream.readObject(); //创建对象肯定要依赖对象所属的class文件。
System.out.println("对象的信息:"+ user);
}
//writeObj()在F盘创建obj.txt文件保存输出对象信息
//readObj()读取obj.txt文件,控制台显示“对象的信息:用户名:admin 密码:123 年龄:0 地址:中国广州”
注意细节:
- 如果对象需要被写出到文件上,那么对象所属的类必须要实现Serializable接口。 Serializable接口没有任何的方法,是一个标识接口而已。
- 对象的反序列化创建对象的时候并不会调用到构造方法的
- serialVersionUID 是用于记录class文件的版本信息的,serialVersionUID这个数字是通过一个类的类名、成员、包名、工程名算出的一个数字。
- 使用ObjectInputStream反序列化的时候,ObjeectInputStream会先读取文件中的serialVersionUID,然后与本地的class文件的serialVersionUID进行对比,如果这两个id不一致,那么反序列化就失败了。
- 如果序列化与反序列化的时候可能会修改类的成员,那么最好一开始就给这个类指定一个serialVersionUID,如果一类已经指定的serialVersionUID,然后在序列化与反序列化的时候,jvm都不会再自己算这个 class的serialVersionUID了。
- 如果一个对象某个数据不想被序列化到硬盘上,可以使用关键字transient修饰。
- 如果一个类维护了另外一个类的引用,那么另外一个类也需要实现Serializable接口。
- 序列化不适用于静态变量,只适用于对象。
*Properties*(配置文件类):
主要用于生产配置文件与读取配置文件的信息。属于Map集合
|--Hashtable
|--Properties
Properties类对应properties文件。文件内容是键值对,都是String类型,键值对之间使用"="或空格隔开。"#"的表示注释
//保存配置文件文件的信息。
//1.创建Properties
Properties properties = new Properties();
properties.setProperty("键","值");
//2.使用Properties生产配置文件。
properties.store(new FileWriter("路径\\文件名.properties"),"说明");//第一个参数是一个输出流对象,第二参数是描述这个配置文件的信息的字符串
//读取配置文件爱你的信息
//1.创建Properties对象
Properties properties = new Properties();
//2.加载配置文件信息到Properties中
properties.load(new FileReader("路径\\文件名.properties"));
注意细节:
- 如果配置文件的信息使用了中文,那么在使用store方法生成配置文件的时候只能使用字符流解决,如果使用字节流生成配置文件的话,默认使用的是iso8859-1码表进行编码存储,这时候会出现乱码。
- 如果Properties中的内容发生了变化,一定要重新使用Properties.store(···)生成配置文件,否则配置文件信息不会发生变化。
打印流(printStream)
打印流可以打印任意类型的数据,而且打印数据之前都会先把数据转换成字符串再进行打印。
//目标文件
File file = new File("路径");
//创建一个打印流
PrintStream printStream = new PrintStream(file);
//1.可以打印任意类型的数据
printStream.println(97);
printStream.println(3.14);
printStream.println('a');
printStream.println(true);
Animal a = new Animal("老鼠", "黑色");
printStream.println(a);
//默认标准的输出流是向控制台输出的,可重定向输出
System.setOut(printStream); //重新设置了标准的输出流对象,向文件输出
System.out.println("输出到文件");
//2.可用来收集异常的日志信息。例:
File logFile = new File("F:\\2015年1月8日.log");
PrintStream logPrintStream = new PrintStream( new FileOutputStream(logFile,true) );
try{
异常代码;
}catch(Exception e){
e.printStackTrace(logPrintStream);
}
*转换流* InputStreamReader,OutputStreamWriter:
- 输入字节流的转换流:InputStreamReader 是字节流通向字符流的桥梁,可以把输入字节流转换成输入字符流
- 输出字节流的转换流:OutputStreamWriter 是字符流通向字节流的桥梁,可以把输出字节流转换成输出字符流
它们有转换作用,而本身又是字符流。所以在构造的时候,需要传入字节流对象进来。
构造函数:
InputStreamReader(InputStream) 通过该构造函数初始化,使用的是本系统默认的编码表GBK。
InputStreamReader(InputStream,String charSet) 通过该构造函数初始化,可指定编码表。
OutputStreamWriter(OutputStream) 通过该构造函数初始化,使用的是本系统默认的编码表GBK。
OutputStreamWriter(OutputStream,String charSet) 通过该构造函数初始化,可指定编码表。
转换流的作用:
- 如果目前所获取到的是一个字节流需要转换字符流使用,这时候就可以使用转换流。 字节流----> 字符流
- 使用转换流可以指定编码表进行读写文件。