JAVA IO详解
前言:
Java IO 操作主要指的是使用Java进行输入、输出操作,Java中的所有操作类都存放在java.io包中,而整个java.io包中最重要的就是5个类和一个接口,5个类指的是File、OutputStream、ImputStream、Writer、Reader。一个接口指的是Serializable。掌握了这些就等于掌握了IO操作的核心。
一、操作文件的类——File
1.File类的基本介绍
在整个io包中,唯一表示与文件本身有关的类就是File类。使用File类可以进行创建或删除文件等常用操作。要想使用File类,则首先要观察File类的构造方法,此类的常用构造方法如下所示:
public File(String pathname) ->实例化File类的时候,必须设置好路径
表1 File类中的常用方法和常量
2.使用File类操作文件
实例操作一:创建一个新文件
直接使用creatNewFile()创建一个新文件,但是此方法使用了throws关键字声明,所以使用中,必须使用try..cath进行异常处理。
注意:如果在不同的操作系统中,则路径的分隔符表示是不一样的,例如:
Windows 中使用反斜杠表示目录的分隔符 “\”
Linux 中使用正斜杠表示目录的分隔符 “/”
因此,为了达到可移植性的目的,在操作文件时,一定要学会使用File.separator表示分隔符
实例操作二:删除一个指定的文件
直接使用File类中的delete()方法
注意:在删除文件前应该保证文件存在,所以最好在删除之前判断一下文件是否存在,在此,可以使用File类提供的exists()方法,此方法返回boolean类型
//以下给出两个实例相结合的代码
import java.io.File;
import java.io.IOException;
/*
* 创建文件,注意文件路径,采用拼凑给出路径,以便适合不同操作系统
* 删除文件,创建新文件时要判断文件是否已经存在
*/
public class IoDemo1 {
public static void main(String[] args) {
//File file = new File("d:\\test.txt"); //普通创建文件路径,但不同系统分隔符不一样
File file = new File("d:"+ File.separator +"text.txt");
try {
if(file.exists()){ //判断文件是否存在
file.delete(); //删除文件,并且保证文件存在,需要加判断
}else {
file.createNewFile(); //创建一个文件
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
实例操作四:创建一个文件夹
直接使用mkdir()方法
实例操作五:列出指定目录的全部文件
在File类中,定义了两个列出文件夹内容的方法:
public String [] list() 列出全部名称,返回一个字符串数组
public File[] listFiles() 列出完整的路径,返回一个File对象数组
实例操作六:判断一个给定的路径是否是目录
直接使用isDirectory()方法判断
//以下给出以上三个实例的代码
import java.io.File;
/*
* 创建文件夹,直接使用 mkdir()方法
* 显示文件夹中所有文件名称,使用list()方法,返回一个字符串数组
* 显示文件夹中所有文件的完整路径,使用listFiles()方法,返回的是File文件数组
* 判断一个给定的路径是否是目录,使用isDirectory()方法,返回时boolean
*/
public class IoDemo2 {
public static void main(String[] args) {
File f = newFile("d:"+ File.separator+"mldn"); //声明文件夹地址
f.mkdirs(); //创建文件夹
File file = new File("d:"+File.separator);
String str[] = file.list();
File files[] = file.listFiles(); //列出所有文件
for (int i = 0; i < str.length; i++) {
// System.out.println(str[i]);
System.out.println(files[i]);
}
if (f.isDirectory()) { //判断路径是否为目录
System.out.println(f.getPath()+"路径是目录");
}else {
System.out.println(f.getPath()+"路径不是目录");
}
}
}
二、RandomAccessFile类
File类只是针对文件本身进行操作,而如果要对文件内容进行操作,则可以使用RandomAccessFile类,此类属于随机读取类,即可以随机读取一个文件中指定位置的数据。
例如:假设给定以下三个数据:
zhangsan, 30
lisi , 31
wangwu,32
表2:RandomAccessFile类的常用操作方法
注意:使用rw方式声明RandomAccessFile对象时,要写入的文件不存在,系统自动创建
//以下给出相应代码
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomAccessFileDemo1 {
public static void main(String[] args) throws IOException {
File file = new File("d:"+File.separator+"text.txt");
RandomAccessFile rdf = null;
rdf = new RandomAccessFile(file, "rw");
String name = "zhangsan";
int age = 30;
rdf.writeBytes(name);
rdf.writeInt(age);
rdf.close();
}
}
三、字节流
1.字节输出流:OutputStream
OutputStream 是整个IO包中字节输出流的最大父类,此类定义如下:
public abstreact class OutputStream
extends Object
implements Closeable,Flushable
从以上定义中可以看出,OutputStream 类是一个抽象类,即不能直接使用,需要通过子类实例化对象。如果现在操作的是一个文件,可以使用FileOutputStream类,通过向上转型后,可以为OutputStream 实例化。
表3:OutputStream类的常用方法
No. | 方法或常量 | 类型 | 描述 |
1 | public void close() throws IOException | 普通 | 关闭输出流 |
2 | public void flush() throws IOException | 普通 | 刷新缓冲区 |
3 | public void write(byte[] b) throws IOException | 普通 | 将一个byte数组写入数据流 |
4 | public void write(byte[] b,int off,int len) throws IOException | 普通 | 将一个指定范围的byte数组写入数据流 |
5 | public abstract void write(int b) throwsIOException | 普通 | 将一个字节数据写入数据流 |
2.字节输入流
既然程序可以向文件中写入内容,则就可以通过InputStream从文件中把内容读取进来,首先来看InputStream类的定义:
public abstract class InputStream
extends Object
implementsCloseable
与OutputStream类一样,InputStream本身也是一个抽象类,必须依靠其子类,如果现在是从文件中读取,子类肯定是FileInputStream
表4:InputStream类的常用方法
No. | 方法或常量 | 类型 | 描述 |
1 | public int available() throws IOException | 普通 | 可以取得输入文件的大小 |
2 | public void close() throws IOException | 普通 | 关闭输入流 |
3 | public abstract int read() throws IOException | 普通 | 读取内容,以数字的方式读取 |
4 | public int read(byte[] b) throws IOException | 普通 | 将内容读到byte数组之中,同时返回读入的个数 |
注意:追加新内容,则需要使用FileOutStream类的另外一个构造方法,OutputStream out = new FileOutputStream(file,true); //表示追加内容
InputStream类 同上
//以下给出实现代码
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/*
*字节流 :
*1.使用File类打开一个文件
*2.通过字节流的子类指定输出的位置
*3.进行读写操作
*4.关闭输入输出
*/
public class StreamDemo {
public static void main(String[] args) throws IOException {
File file = new File("d:"+File.separator+"text.txt");
//OutputStream out = new FileOutputStream(file); //参数不带true则再次运行时会取代原有文件内容
OutputStream out = new FileOutputStream(file,true); //表示追加内容
// String s = "hellow !";
String s = "\r\nhellow !"; //追加内容换行
byte b[] = s.getBytes(); //将字符串转换成字节数组
out.write(b);
out.close();
InputStream in = new FileInputStream(file);
byte b1[] = new byte[(int)file.length()]; //设置数组的大小
in.read(b1); //把内容取出,读到b1数组中
in.close();
System.out.println(new String(b1)); //输出
}
}
四、字符流
1.字符输出流Writer
Writer 本身就是第一个字符流的输出类,此类的定义如下:
public abstract class Writer
extends Object
implements Appendable ,Closeable ,Flushable
此类也是一个抽象类,应该使用FileWriter的子类,
表5:Writer 类的常用方法
No. | 方法或常量 | 类型 | 描述 |
1 | public abstract void close() throws IOException | 普通 | 关闭输出流 |
2 | public void write(String str) throws IOException | 普通 | 将字符串输出 |
3 | public void write(char[] cbuf) throws IOException | 普通 | 将字符数组输出 |
4 | public abstract void flush() throws IOException | 普通 | 强制性清空缓存 |
2.字符输入流Reader
Reader是使用字符的方式从文件之中取出数据,Reader类的定义如下;
extends Object
implements Readable, Closeable
表6:Reader类的常用方法
No. | 方法或常量 | 类型 | 描述 |
1 | public abstract void close() throws IOException | 普通 | 关闭输出流 |
2 | public int read() throws IOException | 普通 | 读取单个字符 |
3 | public int read(char[] cbuf) throws IOException | 普通 | 将内容读到字符数组之中,返回读入的长度 |
字符流与字节流的区别:
//以下给出相应代码
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
/*
* 字符流:与字节流使用步骤差不多
*/
public class WriterReaderDemo {
public static void main(String[] args) throws IOException {
File file = new File("d:"+File.separator+"text.txt");
// Writer writer = new FileWriter(file);
Writer writer = new FileWriter(file,true); //文件内容追加与字节流相同
String str = "\r\nzhangyang";
writer.write(str); //直接输入字符串,不需要进行转换
writer.close();
Reader reader = new FileReader(file);
char c[] = new char[(int)file.length()]; //这里是将内容读取到字符数组
reader.read(c);
reader.close();
System.out.println(new String(c));
}
}
五、转换流——OutputStreamWriter和InputStreamReader
是 Writer 的子类,将输出的字符流变为字节流,
即:将一个字符流的输出对象变为字节流输出对象。
是 Reader 的子类,将输入的字节流变为字符流,
即:将一个字节流的输入对象变为字符流的输入对象。
转换步骤
如果以文件操作为例,则在内存中的字符数据需要通过 OutputStreamWriter 变为字节流才能保存在文件之中,读取的时候需要将读入的字节流通过 InputStreamReader 变为字符流。
//将字节输出流变成字符输出流
importjava.io.File;
importjava.io.FileOutputStream;
importjava.io.OutputStreamWriter;
importjava.io.Writer;
publicclass OutputStreamWriterDemo01 {
publicstaticvoid main(String[]args) throws Exception {// 所有的异常抛出
File f = new File("d:" +File.separator +"test.txt");
Writerout = null;
out= newOutputStreamWriter(newFileOutputStream(f));// 字节流变为字符流
out.write("hello world"); // 使用字符流输出
out.close();
}
}
//将字节输入流变成字符输入流
importjava.io.File;
importjava.io.FileInputStream;
importjava.io.InputStreamReader;
importjava.io.Reader;
publicclass InputStreamReaderDemo01 {
publicstaticvoid main(String[]args) throws Exception {// 所有的异常抛出
File f = new File("d:" +File.separator +"test.txt");
Readerreader =null;
reader= newInputStreamReader(newFileInputStream(f));// 将字节流变为字符流
char c[] =newchar[1024];
intlen =reader.read(c);
reader.close();
System.out.println(new String(c, 0, len));
}
}
六、对象序列化
1.基本概念与Serializable接口
此接口定义如下:
//定义可序列化类
importjava.io.Serializable;
publicclass PersonimplementsSerializable { // 此类的对象可以被序列化
private Stringname; // 声明name属性
privateintage; // 声明age属性
public Person(String name,int age) { // 通过构造方法设置属性内容
this.name = name;
this.age = age;
}
public StringtoString() { // 覆写toString()方法
return"姓名:" +this.name +";年龄:" +this.age;
}
}
对象输出流: ObjectOutputStream
此类定义如下:
extends OutputStream
implements ObjectOutput , ObjectStreamConstants
表7 此类常用方法
No. | 方法或常量 | 类型 | 描述 |
1 | public ObjectOutputStream(OutputStream out) throws IOException | 构造 | 传入输出的对象 |
2 | public final void writeObject(Object obj) throws IOException | 普通 | 输出对象 |
对象输入流:ObjectInputStream
ObjectInputStream 的定义如下:
public class ObjectInputStream
extends InputStream
implements ObjectInput , ObjectStreamConstants
表8:此类常用方法
No. | 方法或常量 | 类型 | 描述 |
1 | public ObjectInputStream(InputStream in) throws IOException | 构造 | 构造输入对象 |
2 | public final Object readObject() throws IOException, ClassNotFoundException | 普通 | 从指定位置读取对象 |
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
/*
* 对象序列化:把一个对象变为二进制的数据流存储
* 被序列化的类要实现 java.io.Serializable 接口
* 还必须依靠对象输出流 (ObjectOutputStream)和对象输入流 (ObjectInputStream)
* 并且对象输入流输出流 的构造方法 参数为 输出流 输入流对象 ,OutputStream和InputStream的 对象
*/
public class ObjectStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
File file = new File("d:"+File.separator+"text.txt");
OutputStream out = new FileOutputStream(file); //文件输出流
ObjectOutputStream oos = new ObjectOutputStream(out); //实例化对象
oos.writeObject(new person("张三", "12")); //保存对象到文件
oos.writeObject(new person("李四", "11"));
oos.close(); //关闭输出
InputStream in = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(in);
Object o = ois.readObject();
ois.close();
System.out.println(o);
}
}
class person implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String name;
private String age;
@Override
public String toString() {
return "person [name=" + name + ", age=" + age + "]";
}
public person(String name, String age) {
super();
this.name = name;
this.age = age;
}
}
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
/*
* 序列化多个文件:
* 使用对象数组保存多个对象
*/
public class ObjectStreamDemo1 {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Student s[] = {new Student("战三", 19),new Student("lis ", 19)};
ser(s);
Object o[] = dlser();
for (int i = 0; i < o.length; i++) {
Student student = (Student)o[i];
System.out.println(student);
}
}
public static void ser(Object o[]) throws IOException{
File file = new File("d:"+File.separator+"text.txt");
OutputStream out = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(out);
oos.writeObject(o);
oos.close();
}
public static Object[] dlser() throws IOException, ClassNotFoundException{
File file = new File("d:"+File.separator+"text.txt");
InputStream in = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(in);
Object o[] = (Object[])ois.readObject();
ois.close();
return o;
}
}
class Student implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private String name;
private int age;
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
Externalizable接口与Serializable接口实现序列化的区别
No. | 区别 | Serializable | Externalizable |
1 | 实现复杂度 | 实现简单,Java对其有内建支持 | 实现复杂,由开发人员自己完成 |
2 | 执行效率 | 所有对象由JAVA统一保存,性能较低 | 开发人员决定那个对象保存,可能的速度提升 |
3 | 保存信息 | 保存时占用空间大 | 部分存储,可能的空间减少 |
transient关键字
例如: Student中name不想被序列化
class Student implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private transientString name;
private int age;
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
public Student(String name, int age) {
super();
this.name = name;
this.age = age;
}
}