--------------------ASP.Net+Android+IOS开发、.Net培训、期待与您交流! --------------------
1. 序列化对象
1.概述
对象被序列化后,然后存到硬盘的文件中,然后读取的时候可以方便的读取,要被存到硬盘文件中的对象类,必须实现接口Serializable接口,此接口中没有抽象方法,是为了标识类的。
ObjectOutputStream的方法writeObject()和ObjectInputStream的方法readObject(),这两个方法是成对出现的,一个是写,一个是读。
注意点:
1.当被序列化的对象存到了硬盘上后,如果原来的类更改了,那么再次读取的时候,会读取失败,因为他们的标识ID被更改了,所以要向更改后,仍可以读取的话,那么在序列化的时候,指定固定的标识ID。static final long serialVersionUID = 42L;
2.静态成员不能被序列化,因为序列化的都是在堆中,而静态的成员实在方法区中,如果非静态成员要向也不被序列化,那么由关键字:transient修饰,那么就会不被序列化。
3.当文件中存储了多个对象,那么读取的时候没调用一次readObject方法,就读取一个对象。
2.示例
用序列标识号来标识类,那么原来的类更改了一些操作,那么也可以继续使用硬盘中被序列化的文件对象。
import java.io.Serializable; public class Person implements Serializable { static final long serialVersionUID = 42L; private String name; int age; public Person(String name, int age) { this.name = name; this.age = age; } public String toString() { return name + ":" + age; } } 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 SerDemo { public static void main(String[] args) throws Exception { // WritePerson(); ReadPerson(); } /* 写 */ private static void ReadPerson() throws IOException,FileNotFoundException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream( "F:\\per.txt")); Person p = (Person) ois.readObject(); System.out.println(p); } /* 读 */ private static void WritePerson() throws IOException,FileNotFoundException { ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream( "F:\\per.txt")); oos.writeObject(new Person("zhangsan", 23)); oos.close(); } } 结果: zhangsan:23
至于静态成员和非静态成员被transient修饰,自己看以测试一下。
2. 管道流
将输入流和输出流。连接起来有两种方式:利用构造函数,或者用connect()方法。
public class Write implements Runnable { private PipedOutputStream out = null; public Write(PipedOutputStream out) { super(); this.out = out; } public void run() { try { System.out.println("开始写入数据,等待"); Thread.sleep(4000); out.write("guandao".getBytes()); out.close(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } } import java.io.IOException; import java.io.PipedInputStream; public class Read implements Runnable { private PipedInputStream in = null; public Read(PipedInputStream in) { this.in = in; } public void run() { try { System.out.println("没有数据可读"); byte [] b=new byte[1024]; int len=in.read(b); System.out.println("读到数据"); System.out.println(new String(b,0,len)); } catch (IOException e) { e.printStackTrace(); } } } import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; public class PideDemo { public static void main(String[] args)throws IOException { PipedOutputStream pds=new PipedOutputStream(); PipedInputStream pis=new PipedInputStream(); pds.connect(pis);//链接 Write w=new Write(pds); Read r=new Read(pis); new Thread(w).start(); new Thread(r).start(); } } 结果: 开始写入数据,等待 没有数据可读 读到数据 guandao
3. RandomAccessFile
随机访问文件,自身具备读写方法
1.特点:
不是IO包中的子类,直接继承Object但是是IO包成员,既可以读也可以写。内部封装一个数组,通过指针进行操作。
可以通过getFilePointer()和seek()方法设置指针的位置
2. 读写原理:就是内部分装了字节输入流和输出流。
3.通过构造函数知,只能操作文件。有模式:r,rw,rws,rwdbv
4. 跳过指定的字节数skipBytes(n);只能向后跳
5.如果模式是r,不会创建文件,会去读一个已经存在的文件,不存在,则抛出异常。如果模式是rw,操作文件不存在,则自动创建,要是文件存在,则不会覆盖。
import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; public class RandomAccessFileDemo { public static void main(String[] args) throws IOException { RandomAccessFile raf = new RandomAccessFile(new File("F:\\demo.txt"), "rw");// 可读可写 /*写*/ raf.write("张三".getBytes()); raf.write(23); raf.close(); raf = new RandomAccessFile(new File("F:\\demo.txt"), "rw");// 可读可写 /*读*/ byte[] b = new byte[4]; int l = raf.read(b); System.out.println(new String(b, 0, l)); } }
4. DataInputStream和DataOutputStream
是用来操作基本数据类型的类。
注意点:
writeUTF方法是用来操作指定写入的编码是utf-8方式,但是他是修改版本,在此写入的文件,只能应此对象来读取,其他的对象是读取不出来的,因为是修改版的。
public static void main(String[] args) throws IOException { DataWrite(); DataRead(); } public static void DataWrite() throws IOException{ DataOutputStream out=new DataOutputStream(new FileOutputStream("F:\\data.txt")); out.writeInt(123); out.writeBoolean(true); out.writeDouble(34.87); out.writeDouble(1.1); out.close(); } public static void DataRead() throws IOException{ DataInputStream in=new DataInputStream(new FileInputStream("F:\\data.txt")); int a=in.readInt(); boolean b=in.readBoolean() ; double c=in.readDouble(); in.close(); System.out.println(a); System.out.println(b); System.out.println(c); } 结果: 123 true 34.87
5. ByteArrayInputStream和ByteArrayOutputStream
用于操作字节数组
这两个流对象操作的数组,没有使用系统资源(没有创建文件),所以不用关闭。
ByteArrayInputStream:不用定义数据目的,因为封装了一个可变的字节数组
在流操作的规律中:
源和目的除了:键盘和文件,还有内存,内存就是ByteArrayInputStream和ByteArrayOutputStream,就是操作字节数组流。
public static void main(String[] args) throws IOException { ByteArrayInputStream in=new ByteArrayInputStream("abcd".getBytes()); ByteArrayOutputStream out=new ByteArrayOutputStream(); int ch=0; while((ch=in.read())!=-1){ out.write(ch); } System.out.println(out.size()); System.out.println(out.toString()); }
|
CharArrayReader和CharArrayWriter 这两个类和ByteArrayInputStream,ByteArrayOutputStream使用一样,只是一个操作字符数组另一个操作字节数组,
StringReader与StringWriter用法也一样,只是操作的字符串
注意点:writeTo(OutputStream out)方法可以把数组中的数据直接写到流中。
6.编码表
ISO8859-1:属于单字节编码方式,主要是在0~255字符范围,只要在英语在英文上。
GBK/GBK2312:中文的编码方式,只要是标识汉字,
Unicod:使用的是16进制的编码方式。
UTF:可以是字符的长度是1-6字符,这样可以节约空间。兼容所有字符。
转换流之间的编码
编码表:就是把各个国家的文字全部用数字表示出来,应用于计算机,那么这样就形成了一个表,就是编码表
GBK:一个汉字站两个字节
UTF-8:一个汉字占三个字节
同样的编码方式要用同样的变法方式读取,否则会出现乱码
public class Text { public static void main(String[] args) throws IOException { //writeTxt(); readTxt(); } public static void writeTxt() throws IOException{ OutputStreamWriter writer=new OutputStreamWriter(new FileOutputStream("F:\\utf.txt"),"utf-8"); writer.write("大家好"); writer.close(); } public static void readTxt() throws IOException{ InputStreamReader reader=new InputStreamReader(new FileInputStream("F:\\utf.txt"),"utf-8"); char []bu=new char[20]; int len=reader.read(bu); System.out.println(new String(bu,0,len)); reader.close(); } }
7. 字符的编码和解码
编码:字符串变成字节数组String—》byte[]String.getBytes(charsetName);
解码:字节数组变成字符串byte[]->String new String(byte[],chatsename)
public class Text { public static void main(String[] args) throwsUnsupportedEncodingException { String s1="你好"; byte [] b1=s1.getBytes("GBK"); System.out.println(Arrays.toString(b1));//编码表 //假如编码方式变了 String s2=new String(b1,"ISO8859-1"); System.out.println(s2); byte [] b2=s2.getBytes("ISO8859-1"); System.out.println(Arrays.toString(b2));//编码表 System.out.println(new String(b1,"GBK")); } } 结果: [-60, -29, -70, -61] ???? [-60, -29, -70, -61] 你好
注意:如果是GBK和UTF-8,之间编码方式乱了,就不能还原,因为GBK和UTF-8编码的时候,一个汉字对应的字节数不一样,所以还原后的字节数不一样,所仍然会出现乱码。
8.字符编码-联通
因为”联通” 用GBK存的产生的二进制和UTF-8编码方式一致(巧合) 11000001 10101010 11001101 10101000 用UTF-8存储的话, public class Text { public static void main(String[] args) throwsUnsupportedEncodingException { String s1="联通"; byte [] b1=s1.getBytes("GBK"); for(byte b:b1){ System.out.println(Integer.toBinaryString(b&255));//将其转换成二进制,然后取后八位 } } } 结果: 11000001 10101010 11001101 10101000
从结果可以看出:原本是用GBK编码,但是而进行却符合UTF-8的编码方式,所以直接用UTF-8进行解码,那么解析出来的结果当然会是乱码。
解决方式:在联通前面加任意汉字。
UTF-8编码方式字节读取个数