IO流
File类
理解
-
File类的一个对象,代表一个文件或一个文件目录(俗称:文件夹)
-
File类声明在java.io包下
-
File类中涉及到关于文件或文件目录的创建、删除、重命名、修改时间、文件大小等方法,并未涉及到写入或读取文件内容的操作。
如果需要读取或写入文件内容,必须使用IO流来完成。
-
后续File类的对象常会作为参数传递到流的构造器中,指明读取或写入的"终点".
构造器
File(String filePath)
以pathname为路径创建File对象,可以是绝对路径或者相对路径
绝对路径:是一个固定的路径,从盘符开始
相对路径:是相对于某个位置开始
File(String parentPath,String childPath)
以parent为父路径,child为子路径创建File对象
File(File parentFile,String childPath)
根据一个父File对象和子文件路径创建File对象
方法
IO流概述
流的分类
- 操作数据单位:字节流、字符流
- 数据的流向:输入流、输出流
- 流的角色:节点流、处理流
流的体系结构
节点流
读(字符)
说明点:
1. read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1
异常的处理:为了保证流资源一定可以执行关闭操作。需要使用try-catch-finally处理
2. 读入的文件一定要存在,否则就会报FileNotFoundException。
3. 擅长处理 文本文件,使用 字符流 处理
//将day09下的hello.txt文件内容读入程序中,并输出到控制台
@Test
public void testFileReader(){
FileReader fr = null;
try {
//1.实例化File类的对象,指明要操作的文件
File file = new File("hello.txt");//相较于当前Module
//2.提供具体的流
fr = new FileReader(file);
//3.数据的读入
//方式一:read():
//返回读入的一个字符。如果达到文件末尾,返回-1
int data;
while((data = fr.read()) != -1){
System.out.print((char)data);
}
//方式二:read(char[] cbuf)(重载方法):
//返回每次读入 自定义的char数组 中的字符的个数。如果达到文件末尾,返回-1
char[] cbuf = new char[5];
int len;
while((len = fr.read(cbuf)) != -1){
//方式一:
//错误的写法
// for(int i = 0;i < cbuf.length;i++){
// System.out.print(cbuf[i]);
// }
//方式二:
//错误的写法,对应着方式一的错误的写法
// String str = new String(cbuf);
// System.out.print(str);
//正确的写法
// for(int i = 0;i < len;i++){
// System.out.print(cbuf[i]);
// }
//正确的写法
String str = new String(cbuf,0,len);//从零读取cbuf数组,每次读取len个
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.流的关闭操作
if(fr != null){//读入的文件一定要存在,否则就会报FileNotFoundException
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
写(字符)
说明:
1. 输出操作,对应的File可以不存在的,并不会报异常
2. File对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。
File对应的硬盘中的文件如果存在:
如果流使用的构造器是:FileWriter(file,false) / FileWriter(file):对原有文件的覆盖
如果流使用的构造器是:FileWriter(file,true):不会对原有文件覆盖,而是在原有文件基础上追加内容
2. 擅长处理 文本文件,使用 字符流 处理
//读取一个文件的内容,将内容写到另一个文件中
@Test
public void testFileReaderFileWriter() {
FileReader fr = null;
FileWriter fw = null;
try {
//1.创建File类的对象,指明读入和写出的文件
File srcFile = new File("hello.txt");
File destFile = new File("hello2.txt");
//不能使用字符流来处理图片等字节数据
// File srcFile = new File("爱情与友情.jpg");
// File destFile = new File("爱情与友情1.jpg");
//2.创建输入流和输出流的对象
fr = new FileReader(srcFile);
fw = new FileWriter(destFile);
//3.数据的读入和写出操作
char[] cbuf = new char[5];
int len;//记录每次读入到cbuf数组中的字符的个数
while((len = fr.read(cbuf)) != -1){
//每次写出len个字符
fw.write(cbuf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流资源
//方式一:
// try {
// if(fw != null)
// fw.close();
// } catch (IOException e) {
// e.printStackTrace();
// }finally{
// try {
// if(fr != null)
// fr.close();
// } catch (IOException e) {
// e.printStackTrace();
// }
// }
//方式二:
try {
if(fw != null)
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fr != null)
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
读(字节)
FileInputStram
擅长处理非文本文件,使用字节流处理
//简写
File file = new File("123.txt");//1.造文件
FileInputStream fis = new FileInputSteam(file);//2.造流
byte[] buffer = new byte[5];//字节型,因此选byte
int len;
while((len = fis.read(buffer))!=-1){
String str = new String(buffer,0,len);
System.out.println(str);
}
写(字节)
FileOutputStream
擅长处理非文本文件,使用字节流处理
使用字节流处理文本文件可能会出现乱码
(要是将文本文件以字节流的方式在程序上读取显示出来很大可能会乱码,
但是只是读取一个文件的内容,并将内容写到另一个文件中,以字节流或字符流都可以来操作都不会乱码)
//简写,读取一个文件内容,将内容写到另一个文件中
public void copyFile(String srcPath,String destPath){
FileInputStream fis = null;//需要声明在外面,因为关闭数据流的语句声明在catch,需要用到变量fis
FileOutputStream fos = null;
try{
File src = new File(srcPath);
File dest = new File(destPath);
fis = new FileInputStream(src);
fos = new FileOutputStream(dest);
byte[] buffer = new byte[1024];//字节型,因此选byte
int len;
while((len = fis.read(buffer)) != -1){
fos.write(buffer,o,len);
}
}
catch(IOException e) {
e.printStackTrace();
}
finally {
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
处理流
读写(字符)
BufferedReader
BufferedWriter
擅长处理文本文件
public void BufferedReaderWriterTest(String src,String dest){
BufferedReader br = null;
BufferedWriter bw = null;
try{
br = new BufferedReader(new FileReader(new File(src)));
bw = new BufferedWriter(new FileWriter(new File(dest)));
//读写操作
//方式一:使用char[]数组
// char[] cbuf = new char[1024];
// int len;
// while((len = br.read(cbuf)) != -1){
// bw.write(cbuf,0,len);
// // bw.flush();
// }
//方式二:使用String
String data;
while((data = br.readLine()) != null){//readLine()全部写在一行
//方法一:
// bw.write(data + "\n");//data中不包含换行符
//方法二:
bw.write(data);//data中不包含换行符
bw.newLine();//提供换行的操作
}
}
读写(字节)
BufferedInputStream
BufferedOutputStream
擅长处理非文本文件
public void BufferedStreamTest(String src,String dest){
FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try{
//1.造文件
File srcFile = new File(src);
File destFile = new File(dest);
//2.1造节点流
fis = new FileInputStream(srcFile);
fos = new FileOutputStram(destFile);
//2.2造缓冲流
bis = new BufferedInputStream(fis);
bos = new BUfferedOutputStream(fos);
//3.复制
byte buffer = new byte[1024];
int len;
while((len = bis.read(buffer)) != -1){
bos.write(buffer,0,len);
bos.flush();//刷新缓冲区
}catch (IOException e) {
e.printStackTrace();
} finally {
//4.资源关闭
//要求:先关闭外层的流,再关闭内层的流
if(bos != null){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(bis != null){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
//说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略.
// fos.close();
// fis.close();
}
}
}
转换流
OutputStreamWriter
构造器:
public OutputStreamWriter(OutputStream out)
public OutputSreamWriter(OutputStream out,String charsetName)
字节流转为字符流
InputStreamReader:将一个字节的输入流转换为字符的输入流
//读操作
@Test
public void test1() throws IOException {
FileInputStream fis = new FileInputStream("dbcp.txt");
// InputStreamReader isr = new InputStreamReader(fis);//使用系统默认的字符集
//参数2指明了字符集,具体使用哪个字符集,取决于文件dbcp.txt保存时使用的字符集
InputStreamReader isr = new InputStreamReader(fis,"UTF-8");//使用系统默认的字符集
char[] cbuf = new char[20];
int len;
while((len = isr.read(cbuf)) != -1){
String str = new String(cbuf,0,len);
System.out.print(str);
}
isr.close();
}
字符流转为字节流
InputStreamReader:将一个字节的输入流转换为字符的输入流
OutputStreamWriter:将一个字符的输出流转换为字节的输出流
@Test
public void test2() throws Exception {
//1.造文件、造流
File file1 = new File("dbcp.txt");
File file2 = new File("dbcp_gbk.txt");
FileInputStream fis = new FileInputStream(file1);
FileOutputStream fos = new FileOutputStream(file2);
InputStreamReader isr = new InputStreamReader(fis,"utf-8");
OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");
//2.读写过程
char[] cbuf = new char[20];
int len;
while((len = isr.read(cbuf)) != -1){
osw.write(cbuf,0,len);
}
//3.关闭资源
isr.close();
osw.close();
}
}
编码集
1.常见的编码表
- ASCII:美国标准信息交换码。
用一个字节的7位可以表示。 - ISO8859-1:拉丁码表。欧洲码表
用一个字节的8位表示。 - GB2312:中国的中文编码表。最多两个字节编码所有字符
- GBK:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码
- Unicode:国际标准码,融合了目前人类使用的所字符。为每个字符分配唯一的字符码。所有的文字都用两个字节来表示。
- UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。
2.对后面学习的启示
客户端/浏览器端 <----> 后台(java,GO,Python,Node.js,php) <----> 数据库
要求前前后后使用的字符集都要统一:UTF-8.
其他流(了解)
标准的输入输出流
System.in:标准的输入流,默认从键盘输入,它的类型是InputStream
System.out:标准的输出流,默认从控制台输出,它是OutputStream的子类
修改默认的输入和输出行为:
System类的setIn(InputStream is) / setOut(PrintStream ps)方式重新指定输入和输出的流。
例题:
从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续 进行输入操作,直至当输入“e”或者“exit”时,退出程序。
打印流
PrintStream 和PrintWriter
说明:
提供了一系列重载的print()和println()方法,用于多种数据类型的输出
System.out返回的是PrintStream的实例
数据流
DataInputStream 和 DataOutputStream
用于读取或写出基本数据类型的变量或字符串
/*练习:将内存中的字符串、基本数据类型的变量写出到文件中。
注意:处理异常的话,仍然应该使用try-catch-finally.
*/
@Test
public void test3() throws IOException {
//1.
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
//2.
dos.writeUTF("刘建辰");
dos.flush();//刷新操作,将内存中的数据写入文件
dos.writeInt(23);
dos.flush();
dos.writeBoolean(true);
dos.flush();
//3.
dos.close();
}
/*
将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中。
注意点:读取不同类型的数据的顺序要与当初写入文件时,保存的数据的顺序一致!
*/
@Test
public void test4() throws IOException {
//1.
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
//2.
String name = dis.readUTF();
int age = dis.readInt();
boolean isMale = dis.readBoolean();
System.out.println("name = " + name);
System.out.println("age = " + age);
System.out.println("isMale = " + isMale);
//3.
dis.close();
}
对象流
ObjectInputStream:内存中的对象–>存储中的文件、通过网络传输出去:序列化过程
ObjectputStream:存储中的文件、通过网络接收过来–>内存中的对象:反序列化过程
序列化机制
对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。
当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
例子
//序列化代码实现:
@Test
public void testObjectOutputStream(){
ObjectOutputStream oos = null;
try {
//1.
oos = new ObjectOutputStream(new FileOutputStream("object.dat"));//将对象输出到这个文件中去
//2.
oos.writeObject(new String("我爱北京天安门"));//特有方法writeObject()
oos.flush();//刷新操作
oos.writeObject(new Person("王铭",23));//自定义类实现序列化,要求需要实现Serializable接口
oos.flush();
oos.writeObject(new Person("张学良",23,1001,new Account(5000)));
oos.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(oos != null){
//3.
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//反序列化
@Test
public void testObjectInputStream(){
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("object.dat"));//从文件中读取,恢复原有对象
Object obj = ois.readObject();//特有的方法
String str = (String) obj;//要求转为对应类类型
Person p = (Person) ois.readObject();
Person p1 = (Person) ois.readObject();
System.out.println(str);
System.out.println(p);
System.out.println(p1);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(ois != null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
总结
实现序列化的对象所属的类需要满足:
-
需要实现接口:Serializable
-
当前类提供一个全局变量:serialVersionUID
(通过在运行时判断类的serialVersionUID来验证版本一致性的。)
(在反序列化时,JVM会把传来的字节流中的 serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。)(InvalidCastException)
-
除了当前类需要实现接口,还必须保证其内部所有属性也是可序列化的。(创建的内部类也需要实现接口)
(默认情况下,基本数据类型可序列化)
-
ObjectOutputStream和ObjectInputStream 不能序列化 static和transient修饰的成员变量
随机存储文件流
RandomAccessFile
说明
-
RandomAccessFile直接继承于java.lang.Object类,实现了DataInput和DataOutput接口
-
它既可以作为输入流,也可以作为输出流
2.1当作为输出流时,若写出的文件不存在,则执行期间自动创建
2.2如果存在,则对原文件内容覆盖(默认情况下,从头开始覆盖,可以用seek(int pos)指定指针位置来覆盖)
-
构造器:
-
public RandomAccessFile(File file, String mode)
-
public RandomAccessFile(String name, String mode)
mode:
r: 以只读方式打开
**rw:打开以便读取和写入 **
rwd:打开以便读取和写入;同步文件内容的更新
rws:打开以便读取和写入;同步文件内容和元数据的更新
-
//读取一个文件的内容,写到另一个文件中
@Test
public void test1() {
RandomAccessFile raf1 = null;
RandomAccessFile raf2 = null;
try {
//1.
raf1 = new RandomAccessFile(new File("爱情与友情.jpg"),"r");
raf2 = new RandomAccessFile(new File("爱情与友情1.jpg"),"rw");
//2.
byte[] buffer = new byte[1024];
int len;
while((len = raf1.read(buffer)) != -1){
raf2.write(buffer,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.
if(raf1 != null){
try {
raf1.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(raf2 != null){
try {
raf2.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*
使用RandomAccessFile实现数据的插入效果
*/
@Test
public void test3() throws IOException {
RandomAccessFile raf1 = new RandomAccessFile("hello.txt","rw");
raf1.seek(3);//将指针调到角标为3的位置
//保存指针3后面的所数据到StringBuilder中
StringBuilder builder = new StringBuilder((int) new File("hello.txt").length());
byte[] buffer = new byte[20];
int len;
while((len = raf1.read(buffer)) != -1){
builder.append(new String(buffer,0,len)) ;
}
//调回指针,写入“xyz”
raf1.seek(3);
raf1.write("xyz".getBytes());
//将StringBuilder中的数据写入到文件中
raf1.write(builder.toString().getBytes());
raf1.close();
//思考:将StringBuilder替换为ByteArrayOutputStream
}
Path、Paths、Files的使用
NIO
- Java NIO (New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java
IO AP。 - NIO与原来的IO同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的(IO是面向流的)、基于通道的IO操作。
- NIO将以更加高效的方式进行文件的读写操作。
- 随着 JDK 7 的发布,Java对NIO进行了极大的扩展,增强了对文件处理和文件系统特性的支持,以至于我们称他们为 NIO.2。
Path
Path替换原有的File类。
实例化
常用方法
Files
作用:操作文件或文件目录的工具类