Java I/O流
在程序中运行的数据,一旦程序关闭后,数据就会丢失,所以需要用持久化的方式把数据保存起来。
通常会把数据保存在文件中,然后放到硬盘、U盘等磁盘中去。然后就可以通过流把文件中的数据传到程序中来,再把程序中的数据传到文件中去。
File类
在程序中,我们通过java.io包中的File类来对文件的属性进行访问操作。可以通过创建File类的对象,把文件的路径传进来,就可以通过文件对象的各种方法来操作文件或目录的属性(包括路径、权限、日期和时间等)了。
File file=new File(String pathname);
创建File文件对象的构造方法中,可以放入文件或目录的路径,如“c:\test.txt”或"c:/test.txt".
File类中操作文件属性的常用方法如下:
返回值类型 | 方法名 | 说明 |
---|---|---|
boolean | exists() | 判断文件或目录是否存在 |
boolean | isFile() | 判断是否是文件 |
boolean | isDirectory() | 判断是否是目录 |
String | getPath() | 返回此对象表示的文件的相对路径名 |
String | getAbsolutePath() | 返回此对象表示的文件的绝对路径名 |
String | getName() | 返回此对象表示的文件或目录的名称 |
boolean | delete() | 删除此对象指定的文件或目录 |
boolean | createNewFile() | 创建名称的空文件,不创建文件夹 |
long | length() | 返回文件的长度,单位为字节,如果文件不存在,则返回0L |
public static void main(String[] args) {
File file =new File("test.txt");
System.out.println("文件是否存在"+file.exists());
System.out.println("是否是文件"+file.isFile());
System.out.println("是否是目录"+file.isDirectory());
System.out.println("此文件的相对路径名:"+file.getPath());
System.out.println("此文件的绝对路径名:"+file.getAbsolutePath());
System.out.println("此文件的文件名是:"+file.getName());
System.out.println("此文件的长度为:"+file.length());
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
file.delete();
}
流
流是一组有序的数据序列,它是以先进先出的方式发送信息的通道,程序通过流来读写文件。
Java中的流可以根据流向来分类,分为:
(1)输入流。(以InputStream类和Reader类作为基类)
(2)输出流。(以OutputStream类和Writer类作为基类)
注意:这里的输入输出是相对计算机内存而言。
按处理数据单元划分,流可以分为:
(1)字节流
字节流是8位通用字节流。(1字节=8位)
其中可以细分为:
1.字节输入流。(以InputStream类作为基类)
2.字节输出流。(以OutputStream类作为基类)
(2)字符流
字符流是16位Unicode字符流。(1字符=2字节)
其中可以细分为:
1.字符输入流。(以Reader类作为基类)
2.字符输出流。(以Writer类作为基类)
InputStream类
InputStream类是一个抽象类,常用方法如下:
返回值类型 | 方法名 | 说明 |
---|---|---|
int | read() | 把数据源的数据一个字节一个字节读进来,以一个整数返回(返回字节对应的整数) |
int | read(byte[] b) | 从输入流读取若干字节,把这些字节保存在数组b中,返回的是读取到的字节数,如果读到末尾,则返回-1 |
int | read(byte[]b,int off,int len) | 从输入流读取若干字节,把这些字节保存到数组b中,off指的是字节数组开始保存数据的起始下标,len指读取的字节数目,返回的是实际读取到的字节数,如果读到了末尾,则返回-1 |
void | close() | 关闭流 |
int | available() | 可以从输入流中读取的字节数目 |
因为抽象类无法实例化对象,所以实际我们使用的是它的一个子类FileInputStream类
FileInputStream类
是InputStream的子类,常用的构造方法有:
(1)FileInputStream(File file); 传输文件
(2)FileInputStream(String name); 传文件路径
用FileInputStream类读文本文件的步骤如下:
(1)引入相关的类,即导包
import java.io.*;
(2)构造文件输入流FileInputStream对象
InputStream is =new FileInputStream(”xxx.txt“);
(3)读取文本文件的数据
即调用FileInputStream类的read()方法
is.read()
(4)关闭文件流对象
is.close()
public static void main(String[] args) {
InputStream is =null;
try {
is =new FileInputStream("我的青春我做主.txt");
int read =-1;
// byte[] b =new byte[1024];
// while( (read=is.read(b))!=-1 ) {
// for(int i=0;i<read;i++) {
// System.out.print((char)b[i]);
// }
// }
while((read=is.read())!=-1) {
System.out.print((char)read);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
OutputStream类
OutputStream类是一个抽象类,常用方法如下:
返回值类型 | 方法名 | 说明 |
---|---|---|
void | write(int c) | 把字节对应的数字通过输出流写到目标文件中 |
void | write(byte[]b) | 把数组b中的字节数据写到文件中去 |
void | write(byte[]b,int off,int len) | 把数组b中的字节数据写到文件中去,off表示数组开始写的位置,len表示写多少个字节 |
void | close() | 关闭流 |
void | flush() | 强制把缓冲区的数据写到输出流中 |
因为抽象类无法实例化对象,所以实际我们使用的是它的一个子类FileIOutputStream类
FileOutputStream类
是OutputStream类的子类,常用构造方法如下:
(1)FileOutputStream(File file); 写到某个文件,会覆盖文件原有内容
(2)FileOutputStream(String name); 写到某个路径,会覆盖文件原有内容
(3)FileOutputStream(String name,boolean append); 写到某个路径,布尔值为true时,可以指定不覆盖文件原有内容,而是在后面追加新内容
使用FileOutputStream类写文本文件的步骤:
(1)引入相关的类,即导包
import java.io.*;
(2)构造文件输出流FileOutputStream对象
OutputStream os =new FileOutputStream(”xxx.txt“);
(3)把数据写入文本文件
即调用FileOutputStream类的write()方法
os.write(byte[]b)
(4)关闭文件流对象
os.close()
public static void main(String[] args) {
OutputStream os =null;
try {
os =new FileOutputStream("szxs.txt",true);
String msg = "您好呀,信狮教育";
byte[] b =msg.getBytes();
os.write(b);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Reader类
如果文本文件中有汉字,如果用字节流来输入,会出现乱码的情况。对于有汉字的文本,就需要使用字符流来进行读写。而字符的输入流用的就是Reader类。
Reader类是个抽象类,不能够实例化对象,通常是作为引用指向子类对象。Reader类中常用的方法有:
(1) read(); 一个字符一个字符读文件,返回值类型为int
(2) read(char[]c); 将文件读到数组c中,返回值类型为int
(3) read(char[]c ,int off, int len); 将文件读到数组c中,从第off位置开始存数据,读len长度,返回值类型为int
(4)close(); 关闭输入流,返回值类型为void
InputStreamReader类
是Reader的子类。当文件编码格式和程序环境编码格式不一致时,我们用字符流去读文件,就需要指定字符流的编码格式,否则可能会出现中文乱码的情况。而在字符流中,InputStreamReader类是可以在读文件的时候指定编码格式的。
InputStreamReader类的常用构造方法如下:
(1)InputStreamReader(InputStream in); //参数为字节输入流对象
(2)InputStreamReader(InputStream in , String charsetName); // 参数有两个,第一个是字节输入流的对象,第二个参数是指定编码格式。
由构造方法可以看出,InputStreamReader类可以在构造实例化对象的时候指定输入流的编码格式,并且是间接使用了字节流来进行操作的,即将字节流包成了字符流。
FileReader类
是InputStreamReader类的子类。该类只能按照本地平台(电脑当前系统的jdk编码格式)的字符编码来读取数据,用户不能指定其他的字符编码类型。
常用的构造方法如下:
(1)FileReader(File file); //传文件
(2)FileReader(String name); //传文件路径
FileReader类在创建对象的时候可以直接把文件当参数传进来,就不需要再创建一个字节输入流InputStream类型的对象了,但是不能自己指定字符编码格式。
如果想要获取本地平台的字符编码类型,就可以通过方法:System.getProperty(“file.encoding”);来查询。
BufferedReader类
字符流中还有一种更高效读取数据的方式,就是通过BufferedReader类这个带缓冲区的流来读取数据,他是先将文件读到缓冲区中,当缓冲区里存满了或执行了flush()指令时,再把数据一次性读取到程序中。
BufferedReader类是Reader类的子类,它带有缓冲区,这个类的特点就是可以通过readLine()方法,按行来读取文件内容。
使用BufferedReader读文本文件的步骤:
(1)引入相关的类
import java.io.FileReader;
import java.io.BufferedReader;
import java.io.IOException;
(2)构造BufferedReader对象和FileReader对象
Reader fr = new FileReader("c:\\myTest.txt");
BufferedReader br = new BufferedReader(fr);
(3)调用readLine()方法读取数据
br.readLine();
(4)关闭文件流对象
br.close();
fr.close();
Writer类
如果需要写一个带汉字的文件,就不能通过字节输出流OutputStream来写数据了,这时候就需要用到字符输出流Writer。
Writer类是个抽象类,它有一些常用方法:
(1)write(String str);
(2)write(String str ,int off , int len);
(3)close(); 关闭文件流对象,关闭前也会清空缓存
(4)flush(); 清空缓存区
OutputStreamWriter类
是Writer类的子类。与InputStreamReader类似,可以在实例化对象的时候指定字符编码格式。
常用构造方法如下:
(1)OutputStreamWriter(OutputStream out);
(2)OutputStreamWriter(OutputStream out, String charsetName);
构造方法可以看出,OutputStreamWriter类可以在构造实例化对象的时候指定输出流的编码格式,并且是间接使用了字节流来进行操作的,即将字节流包成了字符流。
FileWriter类
是OutputStreamWriter类的子类,常用构造方法如下:
(1)FileWriter(File file);
(2)FileWriter(String path);
以上两种构造方法都可以重载,指定一个boolean类型的参数,用来指定追加还是覆盖文件内容,即:
(1)FileWriter(File file, boolean append);
(2)FileWriter(String path, boolean append);
当append为true时,将在文件原内容末尾追加新写入的内容,否则将覆盖原文件内容。
BufferedWriter类
与字符输入流相同,在字符输出流中也有个比较高效的方式,即通过BufferedWriter类来写文件。
BufferedWriter类是Writer类的子类,它带有缓冲区,常用构造方法如下:
BufferedWriter(Writer out);
如果需要换行,可以使用newline()方法
使用BufferedWriter写文件的步骤:
(1)引入相关的类
import java.io.FileWriter;
import java.io.BufferedWriter;
import java.io.IOException;
(2)构造BufferedWriter对象和FileWriter对象
FileWriter fw =new FileWriter("c:\\myTest.txt");
BufferedWriter bw =new BufferedWriter(fw);
(3)调用write()方法写数据
bw.write("hello");
(4)关闭文件流对象
bw.close();
fw.close();
DataInputStream类
如果需要读写一些二进制文件,就可以使用DataInputStream和DataOutputStream类。
DataInputStream类是FileInputStream的子类,它通过与FileInputStream类结合使用来读取二进制文件。
步骤如下:
(1)引入相关的类
import java.io.FileInputStream;
import java.io.DataInputStream;
(2)构造数据输入流对象
FileInputStream fis =new FileInputStream("xxx.class");
DataInputStream dis =new DataInputStream(fis);
(3)调用read()方法读取二进制数据
dis.read();
(4)关闭数据输入流
dis.close();
fis.close();
与字节流FileInputStream类实现文本文件读取步骤极其相似。
DataOutputStream类
是FileOutputStream的子类,通过与FileOutputStream类结合使用来写二进制文件。
步骤如下:
(1)引入相关的类
import java.io.FileOutputStream;
import java.io.DataOutputStream;
(2)构造数据输出流对象
FileOutputStream fos =new FileOutputStream("c:\\temp.class");
DataOutputStream dos =new DataOutputStream(fos);
(3)调用write()方法写二进制文件的数据
dos.write();
(4)关闭数据输入流
dos.close();
fos.close();
序列化与反序列化
前面所用的输入输出流针对的都是一些基本类型和String类型的数据在文件中的读写,如果要对一个对象数据进行文件中的读写操作,就需要用到序列化和反序列化。
序列化
序列化是将对象的状态写入到特定的流中的过程。通过ObjectOutputStream类来实现。
序列化的步骤如下:
(1)实现Serializable接口
(2)创建对象输出流(ObjectOutputStream)
(3)调用writeObject()方法将对象写入文件
(4)关闭对象输出流
在序列化时常常会碰见异常:NotSerializableException。出现这种异常的原因是你要写的那个对象所在的类没有去实现Serializable接口,所以没有序列化的能力。
反序列化
反序列化是从特定的流中获取数据重新构建对象的过程。通过ObjectInputStream类来实现。
反序列化的步骤如下:
(1)实现Serializable接口
(2)创建对象输入流(ObjectInputStream)
(3)调用readObject()方法读取对象
(4)关闭对象输入流
在反序列化时同样容易碰见异常:NotSerializableException。出现这种异常的原因是你要写的那个对象所在的类没有去实现Serializable接口,所以没有序列化的能力。
实现学员对象的序列化和反序列化实例:
import java.io.Serializable;
public class Student implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String name;
private int age;
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
public Student() {
super();
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
import java.io.*;
public class Test6 {
public static void main(String[] args) {
OutputStream os=null;
ObjectOutputStream oos=null;
InputStream fis =null;
ObjectInputStream ois =null;
try {
Student stu =new Student("张三",18);
os = new FileOutputStream("Object.txt");
oos= new ObjectOutputStream(os);
oos.writeObject(stu);
fis =new FileInputStream("Object.txt");
ois=new ObjectInputStream(fis);
Student stu1 =(Student)ois.readObject();
System.out.println(stu1);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
if(ois!=null) {
ois.close();
}
if(fis!=null) {
fis.close();
}
if(oos!=null) {
oos.close();
}
if(os!=null) {
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}