----------------------ASP.Net+Android+IOS开发----------------------期待与您交流!
IO流4
l 对象的序列化
l 管道流
l RandomAccessFile
l 操作基本数据类型的流对象DataStream
l ByteArrayStream
l 转换流的字符编码
l 字符编码
对象的序列化
所谓的对象序列化(在某些书中也叫串行化),是指将对象转换成二进制数据流的一种实现手段。通过将对象序列化,可以方便地实现对象的传输及保存。
在Java中提供有ObjectInputStream与ObjectOutputStream这两个类用于序列化对象的操作。这两个类是用于存储和读取对象的输入输出流类,不难想象,只要把对象中的所有成员变量都存储起来,就等于保存了这个对象,之后从保存的对象之中再将对象读取进来就可以继续使用此对象。ObjectInputStream类与ObjectOutputStream类,用于帮助开发者完成保存和读取对象成员变量取值的过程,但要求读写或存储的对象必须实现了Serializable接口,但Serializable接口中没有定义任何方法,仅仅被用做一种标记,以被编译器作特殊处理
示例:
import java.io.*;
class Person implements Serializable {
public static final long serialVersionUID = 42L;
private String name;
transient int age;
static String country = "cn";
Person(String name, int age, String country) {
this.name = name;
this.age = age;
this.country = country;
}
public String toString() {
return name + ":" + age + ":" + country;
}
}
类Person实现了Serializable接口,所以此类的对象可序列化。下面的示例使用ObjectOutputStream与ObjectInputStream将Person类的对象保存在文件之中。
示例:
import java.io.*;
public class ObjectStreamDemo {
public static void main(String[] args) throws Exception {
// writeObj();
readObj();
}
public static void readObj() throws Exception {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
"obj.txt"));
Person p = (Person) ois.readObject();
System.out.println(p);
ois.close();
}
public static void writeObj() throws IOException {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
"obj.txt"));
oos.writeObject(new Person("lisi", 20, "kr"));
oos.close();
}
}
运行结果:lisi:0:cn
另外我们要知道的是,如果不希望类中的某个属性被序列化,可以在声明属性之前加上transient关键字。
管道流
管道流
PipedInputStream和pipedOutputStream:输入输出可以直接进行连接,通过结合线程使用。
管道流主要用于连接两个线程间的通信。管道流也分为字节流(PipedInputStream、PipedOutputStream)与字符流(PipedReader、PipedWriter)两种类型。
一个PipedInputStream对象必须和一个PipedOutputStream对象进行连接而产生一个通信管道,PipedOutputStream可以向管道中写入数据,PipedInputStream可以从管道中读取PipedOutputStream写入的数据。
示例:
import java.io.*;
class Read implements Runnable {
private PipedInputStream in;
Read(PipedInputStream in) {
this.in = in;
}
public void run() {
try {
byte[] buf = new byte[1024];
System.out.println("读取前。。没有数据阻塞");
int len = in.read(buf);
System.out.println("读到数据。。阻塞结束");
String s = new String(buf, 0, len);
System.out.println(s);
in.close();
} catch (IOException e) {
throw new RuntimeException("管道读取流失败");
}
}
}
class Write implements Runnable {
private PipedOutputStream out;
Write(PipedOutputStream out) {
this.out = out;
}
public void run() {
try {
System.out.println("开始写入数据,等待6秒后。");
Thread.sleep(6000);
out.write("piped lai la".getBytes());
out.close();
} catch (Exception e) {
throw new RuntimeException("管道输出流失败");
}
}
}
class PipedStreamDemo {
public static void main(String[] args) throws IOException {
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
in.connect(out);
Read r = new Read(in);
Write w = new Write(out);
new Thread(r).start();
new Thread(w).start();
}
}
运行结果:读取前。。没有数据阻塞
开始写入数据,等待6秒后。
读到数据。。阻塞结束
piped lai la
RandomAccessFile
RandomAccessFile
随机访问文件,自身具备读写的方法。
通过skipBytes(int x),seek(int x)来达到随机访问
该类不是算是IO体系中子类,而是直接继承自Object,但是它是IO包中成员。因为它具备读和写功能,内部封装了一个数组,而且通过指针对数组的元素进行操作,可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置,其实完成读写的原理就是内部封装了字节输入流和输出流。通过构造函数可以看出,该类只能操作文件,而且操作文件还有模式:只读r,,读写rw等,如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常,如果模式rw。操作的文件不存在,会自动创建,如果存则不会覆盖。
RandomAccessFile类可以说是Java语言中功能最为丰富的文件访问类,它提供了众多的文件访问方法。RandomAccessFile类支持:“随机访问”方式,可以跳转到文件的任意位置处读写数据。在要访问一个文件的使用,不想把文件从头读到尾,而是希望像访问一个数据库一样访问一个文本文件,这时使用RandomAccessFile类就是最佳选择。
RandomAccessFile对象类有个位置指示器,指向当前读写出的位置,当读写n个字节后,文件指示器将指向这n个字节后面的下一个子接触。刚打开文件时,文件指示器指向文件的开头处,可以移动文件指示器到新的位置,随后的读写操作将从新的位置开始。RandomAccessFile在数据等长记录格式文件的随机(相对顺序而言)读取时有很大的优势,但该类仅限于操作文件,不能访问其他IO设备,如网络、内存映象等。
示例:
import java.io.*;
class RandomAccessFileDemo {
public static void main(String[] args) throws IOException {
writeFile_2();
// readFile();
// System.out.println(Integer.toBinaryString(258));
}
public static void readFile() throws IOException {
RandomAccessFile raf = new RandomAccessFile("ran.txt", "r");
// 调整对象中指针。
// raf.seek(8*1);
// 跳过指定的字节数
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 * 0);
raf.write("周期".getBytes());
raf.writeInt(103);
raf.close();
}
public static void writeFile() throws IOException {
RandomAccessFile raf = new RandomAccessFile("ran.txt", "rw");
raf.write("李四".getBytes());
raf.writeInt(97);
raf.write("王五".getBytes());
raf.writeInt(99);
raf.close();
}
}
操作基本数据类型的流对象DataStream
操作基本数据类型
DataInputStream与DataOutputStream
操作字节数组
ByteArrayInputStream与ByteArrayOutputStream
操作字符数组
CharArrayReader与CharArrayWrite
操作字符串
StringReader与StringWriter
DataInputStream与DataOutputStream,可以用于操作基本数据类型的数据的流对象。
示例:
import java.io.*;
class DataStreamDemo {
public static void main(String[] args) throws IOException {
writeData();
// readData();
// writeUTFDemo();
// OutputStreamWriter osw = new OutputStreamWriter(new
// FileOutputStream("gbk.txt"), "gbk");
// osw.write("你好");
// osw.close();
// readUTFDemo();
}
public static void readUTFDemo() throws IOException {
DataInputStream dis = new DataInputStream(
new FileInputStream("utf.txt"));
String s = dis.readUTF();
System.out.println(s);
dis.close();
}
public static void writeUTFDemo() throws IOException {
DataOutputStream dos = new DataOutputStream(new FileOutputStream(
"utfdate.txt"));
dos.writeUTF("你好");
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);
dis.close();
}
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();
}
}
ByteArrayStream
用于操作字节数组的流对象。
ByteArrayInputStream:在构造的时候,需要接收数据源,而且数据源是一个字节数组。
ByteArrayOutputStream:在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组,这就是数据目的地。因为这两个流对象都操作的数组,并没有使用系统资源,所以,不用进行close关闭。
在流操作规律讲解时:
源设备,
键盘 System.in,硬盘 FileStream,内存 ArrayStream。
目的设备:
控制台 System.out,硬盘FileStream,内存 ArrayStream。
用流的读写思想来操作数据。
示例:
import java.io.*;
class ByteArrayStream {
public static void main(String[] args) {
// 数据源。
ByteArrayInputStream bis = new ByteArrayInputStream(
"ABCDEFD".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());
// bos.writeTo(new FileOutputStream("a.txt"));
}
}
运行结果:7
ABCDEFD
字符编码
字符编码
计算机里只有数组,计算机软件里的一切都是用数字来表示,屏幕上显示的一个个字符也不例外。最开始计算机是在美国使用,当时所用到的字符也就是现在键盘上的一些符号和少数几个特殊的符号,每一个字符都用一个数字来表示,一个字节所能表示的数字范围内足以容纳所有的字符,实际上表示这些字符的数字的自己最高位(bit)都为0,也就是说这些数字都在0到127之间,如字符a对应数字97,字符b对应数字98等,这种个字符与数字对应的编码固定下来后,这套编码规则被称为ASCII码(美国标准信息交换码)。
随着计算机在其他国家的逐渐应用和普及,许多国家都把本地的字符集引入了计算机,这大大地扩展了计算机中字符的范围。一个字节所能表示的数字范围是不能容纳所有的中文汉字的。中国大陆将每一个中文字符都用两个字节的数字来表示,所有的ASCII码字符的编码保持不变,仍用一个字节表示。为了将一个中文字符与两个ASCII码字符相区别,中文字符的每个字节的最高位(bit)都为1,中国大陆为每一个中文字符都指定了一个对应的数字,并作为标准的编码固定了下来,这套编码规则称为GBK(国码表),后来又在GBK的基础上对更多的中文字符(包括繁体)进行了编码,新的编码系统就是GB2312,而GBK则是GB2312的子集。使用中文的国家和地区很多,同样的一个字符,如“中国”的“中”字,在中国大陆的编码是十六进制的D6D0,而在中国台湾地区的编码则是十六进制的A4A4,台湾地区对中文字符集的编码规则称为BIG5(大五码)。
在一个国家的本地化系统出现的一个字符,通过电子邮件传送到另外一个国家的本地化系统中,看到的就不是那个原始字符了,而是另外那个国家的一个字符或乱码。这是以往内计算机里面并没有真正的字符,字符都是以数字的形式存在的,通过邮件传送一个字符,实际上传送的是这个字符对应的编码数字,同一个数字在不同的国家和地区代表的很可能是不同的符号。如十六进制的D6D0在中国大本地化系统中显示为“中”这个符号,但在伊拉克的本地化系统中就不知道对应的是一个什么样的伊拉克字符了,反正人们看到的不是“中”这个符号。随着世界各国在交往越来越密切,全球一体化的趋势越来越明显,人们不可能完全忘记母语,都去使用应为在不同的国家和地区间交换越来越多的电子文档。特别是人们开发的应用软件都希望能出过门、走向世界,可见,各个国家和地区都使用各自不同的本地化字符编码,已经给生活和工作带来了很多的不方便,严重制约了国家和地区间在计算机使用和技术方面的交流。
为了解决各个国家和地区使用各自不同的本地化字符编码带来的不便,人们将全世界所有的符号进行了统一编码,称之位Unicode编码。所有的字符不再区分国家和地区,都是人类共有的符号,如“中国”的“中”这个符号,在全世界的任何一个角落时钟对应的都是一个十六进制的数字4e2d。如果所有的计算机系统都使用这种编码方式,在中国大陆的本地化系统中显示“中”这个符号,发送到德国的本地化系统中,显示的任然是“中”这个符号,只有那个德国人能不能认识这个符号,就不是计算机所要解决的问题了。Unicode编码的字符都占用两个字节的大小,也就是说全世界所有的字符个数不会超过2的16此人放(65535),据推测一定是Unicode编码中没有包括诸如中国的藏文和满文这些少数民族的文字。
长期养成的保守习惯不可能一下子就能改变过来,特别是不可能完全推翻那些已经存在的运行良好的系统。新开发的软件要做到瞻前顾后,既能够在存在的系统上运行,又便于以后的战略扩张和适应新的形式。Unicode一统天下的局面暂时还难以形成,在相当长的一段时间内,人们看到的都是本地化字符编码与Unicode编码共存的景象。既然本地化字符编码与Unicode编码共存,那就少不了涉及两者之间的转换问题,而 Java中的字符使用的都是 Unicode编码,Java技术在通过Unicode保证跨平台特性的前提下也支持了全扩展的本地平台字符集,而显示输出和键盘输入则都是采用的本地编码。
编码表的由来
计算机智能识别二进制数据,早期由来是电信号。
为了方便应用计算机,让它可以识别各个国家的蚊子。
就将各个国家的蚊子用数字来表示,并一一对应,形成一张表。
这就是编码表。
常见的编码表
ASCII:美国标准信息交换码,用一个自己的7位可以表示。
ISO88591:拉丁码表,欧洲码表,用一个字节的8位来标识号。
GB2312:中国的中文编码表。
GBK:中国的中文编码表升级,融合了更多的中文文字符号。
Unicode:国家标准码,融合了多重文字,所有文字都用两个字节来表示,Java语言使用的就是Unicode。
UTF-8:最多用三个字节来表示一个字符。
示例:
/*
编码:字符串变成字节数组。
解码:字节数组变成字符串。
String-->byte[]; str.getBytes(charsetName);
byte[] -->String: new String(byte[],charsetName);
*/
package day21.IO;
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, "utf-8");
System.out.println("s1=" + s1);
// 对s1进行iso8859-1编码。
byte[] b2 = s1.getBytes("utf-8");
System.out.println(Arrays.toString(b2));
String s2 = new String(b2, "gbk");
System.out.println("s2=" + s2);
}
}
运行结果:[-71, -2, -71, -2]
s1=????
[-17, -65, -67, -17, -65, -67, -17, -65, -67, -17, -65, -67]
s2=锟斤拷锟斤拷
----------------------ASP.Net+Android+IOS开发----------------------期待与您交流!
详情请查看:http://edu.csdn.net