首先对java中的IO作一个复习:
Java中IO流根据流的方向,流可分为两类:输入流和输出流。
输入流和输出流又可分为:
字节流:InputStream(输入)OutputStream(输出)
字符流:Reader (输入) Writer(输出)
一个简单的对文件实行读写:
<span style="font-family:Times New Roman;font-size:14px;color:#000000;">package cn.huncu.io.hello;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class Demo1 {
public static void main(String[] args) {
//writeDemo();
readDemo();
}
private static void readDemo() {
File file =new File("files\\a.txt");
if(!file.exists()){
System.out.println("文件不存在");
return ;
}
FileInputStream in=null;
byte[] b=new byte[512];
try {
in=new FileInputStream(file);
in.read(b);
// for(byte bb:b){这种方式,遇到中文会乱码
// System.out.print((char)b);
// }System.out.println();
String str=new String(b, "utf-8");//解码--指定编码表
System.out.println(str);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(in!=null){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
public static void writeDemo() {
FileOutputStream out=null;
String str="你好啊,你叫什么名字? ";
try {
out=new FileOutputStream("files/a.txt");
out.write(str.getBytes("utf-8"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(out!=null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
</span>
RandomAccessFile
★随机访问文件,自身具备读写的方法。
new RandomAccessFile()之后,若文件不存在会自动创建,存在则不创建。——该类其实内部既封装了字节输入流,又封装了字节输出流。
该类若用write()方法写整数,每次只写它的最后一个字节。而采用writeInt()方法,则可把一个整数完整地写入。
★通过skipBytes(int x),seek(int x)来达到随机访问。
通过seek方法设置数据的指针就可以实现对文件数据的随机读写。InputStream中的skip()方法只能从头往后跳,不能反向;而seek()方法可双向随便定位。
★数据修改方面的特点
用RandomAccessFile类可以实现数据的修改,当然文件中的数据一般要有规律,以方 便在编程时能够进行定位,让数据写对地方。
而用“流”实现数据修改时,则通常需要把数据从流读到数组当中,在数组中进行数据修改,然后再把修改后的数组再重新写到流中。
★ 序列化
将一个对象存放到某种类型的永久存储器上称为保持。如果一个对象可以被存放到磁盘或磁带上,或者可以发送到另外一台机器并存放到存储器或磁盘上,那么这个对象就被称为可保持的。(在Java中,序列化、持久化、串行化是一个概念。)
java.io.Serializable接口没有任何方法,它只作为一个“标记者”,用来表明实现了这个接口的类可以考虑串行化。类中没有实现Serializable的对象不能保存或恢复它们的状态。
★ 对象图
当一个对象被串行化时,只有对象的数据被保存;方法和构造函数不属于串行化流。如果一个数据变量是一个对象,那么这个对象的数据成员也会被串行化。树或者对象数据的结构,包括这些子对象,构成了对象图。
★ 瞬时 transient
防止对象的属性被序列化。
序列化和transient演示:
<span style="font-family:Times New Roman;font-size:14px;color:#000000;">package cn.hncu.io.serial;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerializableDemo {
public static void main(String[] args) throws Exception{
//writeDemo();
writeDemo2();
readDemo();
}
private static void readDemo() throws Exception{
ObjectInputStream in = new ObjectInputStream( new FileInputStream("files/persons.txt"));
//while(in.available()>0){//对象流不能以这种方式来判断是否有数据
while(true){//对象流的读取,应该用处理异常的方式来判断文件结束
try {
Person p = (Person) in.readObject();//到达文件结束,会抛异常
System.out.println(p);
} catch (Exception e) {
System.out.println("文件读取完毕!");
break;
}
}
}
private static void writeDemo() throws IOException, FileNotFoundException {
//序列化
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("files/persons.txt"));
out.writeObject( new Person("Jack",24) );
out.writeObject( new Person("张三",23) );
out.writeObject( new Person("Rose",25) );
}
private static void writeDemo2() throws IOException, FileNotFoundException {
//序列化
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("files/persons.txt"));
out.writeObject( new Person("Jack",24,new MyDate(1995, 1, 1)) );
out.writeObject( new Person("张三",23,new MyDate(1993, 10, 1)) );
out.writeObject( new Person("Rose",25,new MyDate(1992, 1, 21)) );
}
}</span>
<span style="font-family:Times New Roman;font-size:14px;color:#000000;">
</span>
Person类:
<span style="font-family:Times New Roman;font-size:14px;color:#000000;">package cn.hncu.io.serial;
import java.io.Serializable;
public class Person implements Serializable{
private static final long serialVersionUID = -8696927766135529411L;
private String name;
private int age;
private transient int num; //瞬时变量,不会序列化---如果不想让一个非静态变量序列化,那么就把它定义成瞬时变量
private static int count=0; //※静态变量是无法序列化
private MyDate birth;
public Person() {
num = ++count;
}
public Person(String name, int age) {
this();
this.name = name;
this.age = age;
}
public Person(String name, int age, MyDate birth) {
super();
this.name = name;
this.age = age;
this.birth = birth;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public MyDate getBirth() {
return birth;
}
public void setBirth(MyDate birth) {
this.birth = birth;
}
@Override
public String toString() {
//return count+":"+name + ", " + age ;
return num+":"+name + ", " + age +", "+birth;
}
}
</span>
MyDate类:
<span style="font-family:Times New Roman;font-size:14px;color:#000000;">package cn.hncu.io.serial;
import java.io.Serializable;
public class MyDate implements Serializable{
private int year, month, day;
public MyDate() {
super();
}
public MyDate(int year, int month, int day) {
super();
this.year = year;
this.month = month;
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
@Override
public String toString() {
return year + "年" + month + "月" + day + "日";
}
}
</span>
注意:ObjectOutputStream类的write(Object obj)不会写静态成员(数据),即非静态数据都会写入对象流中,而如果非静态数据中有部分不想被写入对象流,这时需把定义成瞬时成员变量,如:private transient int age;(该瞬时数据将不被写入对象流中)
用ObjectOutputStream类的write(Object obj)进行写对象时,其中的参数对象的类必须实现Serializable接口。(把光标定位在类名上,利用“Ctlr+1”,可自动为需序列化的类显式定义SerialVersionID,若自己未显式定义序列化ID,则会默认自动生成一个。 )
缓冲输入输出流
(BufferedInputStream和BufferedOutputStream)
方式一: DataInputStream in = new DataInputStream(
newBufferedInputStream(
new FileInputStream("Test.txt") );
方式二:DataInputStream in = new DataInputStream(
new FileInputStream("Test.txt") );
方式三:BufferedInputStream in = new BufferedInputStream(
new DataInputStream(
new FileInputStream("Test.java") );
在这里我们将对这三种方式读取速度做一个比较,看看哪种方法效率。
代码实现如下:
<span style="font-family:Times New Roman;font-size:14px;color:#000000;">package cn.huncu.io.buffer;
import java.io.BufferedInputStream;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class BufferedStreamDemo {
private static String FILE_NAME="files/buf.txt";
public static void main(String[] args) {
try {
//demo1();
//demo2();
demo3();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void demo3() throws IOException {
long t1=System.currentTimeMillis();
BufferedInputStream in=new BufferedInputStream(new DataInputStream(new FileInputStream(FILE_NAME)));
String str=null;
//in.readl
byte[] b=new byte[50];
int len=0;
while((len=in.read(b))!=-1){
System.out.print(new String(b,0,len));
};
System.out.println();
long t2=System.currentTimeMillis();
System.out.println("程序运行时间为(毫秒):"+(t2-t1));
}
private static void demo2() throws IOException {
long t1=System.currentTimeMillis();
DataInputStream in=new DataInputStream(new FileInputStream(FILE_NAME));
String str=null;
//in.readl
while((str=in.readLine())!=null){
System.out.println(str);
}
System.out.println();
long t2=System.currentTimeMillis();
System.out.println("程序运行时间为(毫秒):"+(t2-t1));
}
private static void demo1() throws IOException {
long t1=System.currentTimeMillis();
DataInputStream in=new DataInputStream(new BufferedInputStream(new FileInputStream(FILE_NAME)));
String str=null;
//in.readl
while((str=in.readLine())!=null){
System.out.println(str);
}
System.out.println();
long t2=System.currentTimeMillis();
System.out.println("程序运行时间为(毫秒):"+(t2-t1));
}
}
</span>
为了保证数据的有效性,我做了多次测试。
第一种方式结果如图:
第二种方式:
第三种方式:
小结:
方式一是最优的。
1)有buffer比没有更快;
2)buffer放在中间层包装比放在外层更快;
3)按行或按块操作比 按字节或字符操作更快(用Object流操作的速度比 字节字符方式 更快)
4)缓冲区要结合流才可以使用,在流的基础上对流的功能进行了增强。