对象的序列化和持久化存储
import java.io.*;
import java.util.*;
class ObjectStreamDemo
{
public static void main(String[] args) throws Exception
{
readObj();
}
//读取一个Person对象并存储到obj.txt文件中。
public static void writeObj() throws IOException
{
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.object"));
oos.writeObject(new Person("lisi",39));
oos.close();
}
//读取obj.txt文件中的对象
public static void readObj() throws Exception
{
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.object"));
Person p = (Person)ois.readObject();
System.out.println(p);
ois.close();
}
}
//定义一个Person类,接口Serializable标记序列化
//静态不能序列化
//非静态成员加transient,也不被序列化。
class Person implements Serializable
{
//类定义时自定义UID
//public static final long serialVersionUID = 42L;
String name;
int age;
Person(String name,int age)
{
this.name = name;
this.age = age;
}
public String toString()
{
return name+" : "+age;
}
}
RandomAccessFile
该类不算是IO体系中的子类,而是直接继承自Object。但是它是IO包中的成员,因为具备读和写的功能。
内部封装了一个数组,而且通过指针对数组的元素进行操作。
可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置。
其实完成读写原理就是内部封装了字节输入流和输出流。
通过构造函数可以看出,该类只能操作文件。
而且操作文件还有模式:RandomAccessFile(String name, String mode)
mode 参数指定用以打开文件的访问模式。允许的值及其含意为:
"r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。
"rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
"rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
"rwd" 打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
如果模式为只读r,不会创建文件,会读取一个已存在的文件,如果文件不存在,则会出现异常。
如果模式为rw,那么操作的文件不存在会自动创建,存在则不会覆盖。
而且该对象的构造函数要操作的文件不存在,会自动创建;如果存在则不会覆盖。
可以在一个文件中的任意位置读取和写入,使用seek调整指针位置。
多线程下载就是这个原理。
import java.io.*;
class RandomAccessFileDemo
{
public static void main(String[] args) throws IOException
{
//writeFile();
//readFile();
writeFile_2();
}
//写
public static void writeFile() throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.write("李四".getBytes());
//raf.write(97);//只写最低8位
raf.writeInt(97);//写4个字节
raf.write("王五".getBytes());
raf.writeInt(99);
raf.close();
//结果:李四 a王五 c
}
//读
public static void readFile() throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","r");
//调整对象中的指针
raf.seek(8);
//跳过指定的字节数,只能向下走,不能往回走
raf.skipBytes(8);
byte[] buf = new byte[4];
raf.read(buf);
String name = new String(buf);
int age = raf.readInt();
System.out.println("name="+name);
System.out.println("age="+age);
raf.close();
}
//随机写入
public static void writeFile_2() throws IOException
{
RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
raf.seek(8*3);
raf.write("周七".getBytes());
raf.writeInt(103);
raf.close();
//结果:李四 a王五 c 周七 g
}
}
操作基本数据类型的流对象
DataInputStream和DataOutputStreamimport java.io.*;
class DataStreamDemo
{
public static void main(String[] args) throws IOException
{
//writeData();
//readData();
//writeUTFDemo();
readUTFDemo();
}
//写
public static void writeData() throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
dos.writeInt(234);
dos.writeBoolean(true);
dos.writeDouble(9887.543);
dos.close();
}
//读
public static void readData() throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
int num = dis.readInt();
boolean b = dis.readBoolean();
double d = dis.readDouble();
System.out.println("num="+num);
System.out.println("b="+b);
System.out.println("d="+d);
}
//UTF写文件方式,修改版UTF-8,文件大小为汉字数*2+2。
public static void writeUTFDemo() throws IOException
{
DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdata.txt"));
dos.writeUTF("你好");
dos.close();
}
//UTF读文件方式
public static void readUTFDemo() throws IOException
{
DataInputStream dis = new DataInputStream(new FileInputStream("utfdata.txt"));
String s = dis.readUTF();
System.out.println(s);
dis.close();
}
}
操作字节数组的流对象
ByteArrayInputStream和ByteArrayOutputStreamByteArrayInputStream:在构造的时候,需要接收数据源,而数据源是一个字节数组。
ByteArrayOutputStream:在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。这就是数据目的地。
因为两个流对象都操作数组,并没有使用系统资源。所以不用进行close关闭。
在流操作规律讲解时:
源设备:
键盘(System.in),硬盘(FileStream),内存(ArrayStream);
目标设备:
控制台(System.out),硬盘(FileStream),内存(ArrayStream)。
import java.io.*;
class ByteStreamDemo
{
public static void main(String[] args)
{
//数据源
ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFG".getBytes());
//数据目的
ByteArrayOutputStream bos = new ByteArrayOutputStream();
int by = 0;
while ((by=bis.read())!=-1)
{
bos.write(by);
}
System.out.println(bos.size());
System.out.println(bos.toString());
}
}
操作字符数组
CharArrayReader和CharArrayWriter操作字符串
StringReader和StringWriter转换流的字符编码
import java.io.*;
class EncodeStream
{
public static void main(String[] args) throws IOException
{
//writeText();
readText();
}
//写入数据
public static void writeText() throws IOException
{
//OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"GBK");
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("UTF-8.txt"),"UTF-8");
osw.write("你好");
osw.close();
}
//读取数据
public static void readText() throws IOException
{
InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"GBK");
char[] buf = new char[10];
int len = isr.read(buf);
String str = new String(buf,0,len);
System.out.println(str);
isr.close();
}
}
编码和解码
编码:字符串变成字节数组。
String -> byte[] : str.getBytes(charsetName);解码:字节数组变成字符串。
byte[] -> String : new String(byte[],charsetName);import java.util.*;
class EncodeDemo
{
public static void main(String[] args) throws Exception
{
//编码
String s = "你好";
byte[] b1 = s.getBytes("GBK");
System.out.println(Arrays.toString(b1));
//解码
String s1 = new String(b1,"ISO8859-1");
System.out.println("s1="+s1);
//对错误解码重新编码
byte[] b2 = s1.getBytes("ISO8859-1");
System.out.println(Arrays.toString(b2));
//解码
String s2 = new String(b2,"GBK");
System.out.println("s2="+s2);
}
}
“联通”编码问题
“联通”二字的GBK编码如下,记事本保存GBK编码后再打开会认为符合UTF-8的编码规则,自动用UTF-8编码显示,造成乱码。
11000001
10101010
11001101
10101000
import java.io.*;
class EncodeDemo2
{
public static void main(String[] args) throws IOException
{
String s = "联通";
byte[] by = s.getBytes("GBK");
for (byte b : by)
{
System.out.println(Integer.toBinaryString(b&255));
}
}
}