【Java基础】——IO流(下)

来源:互联网 发布:管螺纹怎么编程 编辑:程序博客网 时间:2024/05/16 11:21

 一、对象序列化

1、概述

①对象序列化概念
程序运行时,会在内存中创建多个对象,如果希望永久保存这些对象,则可以将对象转化为字节数据写入到硬盘,这个过程称为对象的序列化。
②对象序列化前提
当对象要序列化,必须保证该对象所属类实现 Serializable 接口,以启用其序列化功能。
其中Serializable类无任何方法,属于 标记接口。
③UID:能序列化的对象所属类编译后都有一个UID,当对象属性修改后再编译对象(指类)会产生一个新序列号
(即UID,UID是根据类中成员算的),想要属性修改后对象的序列号不变,加 static final long serialVersionUID = 42L;修饰。
注意三点:
  • 静态是不能被序列化的,因为静态存在与方法区,对象存在于堆内存中。
  • 如果非静态想被序列化可以加 transient 修饰。
  • 想被序列化的对象要实现 Serializable 接口。

2、ObjectInputStream 和 ObjectOutputStream

①ObjectOutputStream:将 Java 对象的基本数据类型和图形写入流中
  • 构造方法:ObjectOutputStream(OutputStream out) 
  • 方法:writeObject(Object obj)//将对象写入流中,称为对象的序列化
②ObjectInputStream :对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
  • 构造方法:ObjectInputStream(InputStream in)
  • 方法:Object readObject()将对象反序列化

二、管道流(PipedInputStream 和 PipedOutputStream)

1、概述

管道流是一种特殊的流,必须先建立连接才能进行彼此间通信。通常,数据由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream 。
不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。

2、连接方法

①void connect(PipedOutputStream src)//使此管道输入流连接到管道输出流 src。
②void connect(PipedInputStream snk)//将此管道输出流连接到接收者。

代码演示如下所示:
package com.huang.stream;import java.io.IOException;import java.io.PipedInputStream;import java.io.PipedOutputStream;/** * @author huangxiang * @date 创建时间:2015年5月26日上午10:10:21 * @version 1.0 */public 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();}}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("管道输出流失败");}}}

三、随机访问文件(RandomAccessFile)

1、概述

①该类直接继承自Object类,不属于流类,但是它是IO包中成员,具备读写文件数据的功能。 内部封装了一个数组和记录指针,而且可通过指针对数组的元素进行操作。
②具备读写数据功能原理:是其内部封装了字节输入和输出流。

2、构造方法

①构造方法
  • RandomAccessFile(File file,String mode)
  • RandomAccessFile(String name, String mode)
②从构造方法中我们可以看出:
  • file:被访问的文件
  • name:被访问文件的路径
  • mode:指定访问文件的模式,其中只读"r"不会创建文件,会去读取一个已存在文件,如果文件不存在或执行写入操作,则会出现异常。
  • "rw"表示读写文件,如果文件不存在,会自动创建。如果存在,不会覆盖原文件

3、常用方法

①void seek(long pos)//设定读写指针的位置,与文件开头(0处)相隔POS个字节数。
②int skipBytes(int n)//使读写指针从当前位置开始,跳过n个字节。
③void writeInt(int v)//按4个字节将 int 写入该文件,先写高字节。

代码演示如下:
package com.huang.stream;import java.io.IOException;import java.io.RandomAccessFile;/** * @author huangxiang * @date 创建时间:2015年5月26日上午12:06:52 * @version 1.0 *//* * RandomAccessFile 该类不是算是IO体系中子类。 而是直接继承自Object。 *  * 但是它是IO包中成员。因为它具备读和写功能。 内部封装了一个数组,而且通过指针对数组的元素进行操作。  * 可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置。 *  * 其实完成读写的原理就是内部封装了字节输入流和输出流。 通过构造函数可以看出,该类只能操作文件。  * 而且操作文件还有模式:只读r,,读写rw等。 *  * 如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。  * 如果模式rw。操作的文件不存在,会自动创建。如果存则不会覆盖。 */public 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();}}

四、基本数据操作流(DataInputStream 与 DataOutputStream)

1、概述

是两个与平台无关的数据操作流,不仅提供了读写各种基本数据类型的方法,还提供了readUTF()和writeUTF()方法。

2、方法

a、void writeUTF(String str):使用 UTF-8 修改版编码将一个字符串写入输出流
b、String readUTF():读取writeUTF方法写入的字节   

代码演示如下:
package com.huang.stream;import java.io.DataInputStream;import java.io.DataOutputStream;import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.io.ObjectOutputStream;import java.io.OutputStreamWriter;/* DataInputStream与DataOutputStream 可以用于操作基本数据类型的数据的流对象。 *//** * @author huangxiang * @date 创建时间:2015年5月27日上午12:13:26 * @version 1.0 */public 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();}@SuppressWarnings("null")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();ObjectOutputStream oos = null;oos.writeObject(new Object());}}    

五、字节数组操作流(ByteArrayInputStream 和 ByteArrayOutputStream)

1、概述

此类源和目的都是内存,不会调用系统资源,故不需要关流,此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException.    

2、方法摘要

①ByteArrayInputStream:包含一个内部缓冲区,该缓冲区包含从流中读取的字节
a、字段    
  • protected  byte[] buf 由该流的创建者提供的 byte 数组。
  • protected  int count  计数器,记录缓冲区的字节数。
  • protected  int mark 流中当前的标记位置。
  • protected  int pos    要从输入流缓冲区中读取的下一个字符的索引。
b、构造函数
ByteArrayInputStream(byte[] buf)//创建一个 ByteArrayInputStream,使用 buf 作为其缓冲区数组。此缓冲区和字段的定义的缓冲区不同,该缓冲区表示要接受的数据源,而字段的缓冲区是流从源中读取的数据。
②ByteArrayOutputStream:其数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。
a、字段    
  • protected  byte[] buf 存储数据的缓冲区。。
  • protected  int count  缓冲区中的有效字节数。
b、构造函数
ByteArrayOutputStream()  创建一个新的 byte 数组输出流。其构造函数不许接收目的,原因是其在创建对象时就创建一个byte性数组的缓冲区,即数据目的。
小结:其实字符操作流和字符串操作流同字节数组操作流原理一样。

代码实例如下所示:
package com.huang.stream;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.FileOutputStream;import java.io.IOException;/** * @author huangxiang * @date 创建时间:2015年6月26日上午12:22:13 * @version 1.0 *//* * 用于操作字节数组的流对象。 *  * ByteArrayInputStream :在构造的时候,需要接收数据源,。而且数据源是一个字节数组。 *  * ByteArrayOutputStream: 在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字 * 节数组。这就是数据目的地。 *  * 因为这两个流对象都操作的数组,并没有使用系统资源。 所以,不用进行close关闭。 *  * 在流操作规律讲解时: *  * 源设备, 键盘 System.in,硬盘 FileStream,内存 ArrayStream。 目的设备: 控制台 * System.out,硬盘FileStream,内存 ArrayStream。 *  * 用流的读写思想来操作数据。 */public class ByteArrayStream {public static void main(String[] args) throws IOException {// 数据源。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"));}}

六、字符编码

1、字符码表概述

①含义:是一种可以方便计算机识别的特定字符集,它是将每一个字符和一个唯一的数字对应而形成的一张表。
②常见字符码表
  • ASCII:美国标准信息交换码。用一个字节的7位二进制数表示。
  • ISO8859-1:拉丁码表,兼容ASCII,欧洲码表用一个字节的8位表示。
  • GB2312:中文编码表,兼容ASCII,英文占一个字节,中文占2和字节(2个字节都为负数)。
  • GBK:中文编码表升级,融合了更多的中文文字符号。用两个字节来表示(第一个字节为负数)。
  • Unicode:国际标准码,融合了多种文字。所有文字都用2个字节来表示,Java语言使用的就是unicode
  • UTF-8:Unicode的可变长编码,英文占1字节,中文占3。

2、字符编码和解码

①编码:把字符串变成计算机识别的字节序列。 byte[] getBytes();  byte[] getBytes(String charsetName);
②解码:把字节数组变成字符串。 new String(byte[]); new String(byte[],String charsetName); 当中文用gbk编码,再用ISO8859-1解码会出现乱码,因为ISO8859-1不识别中文,可以继续用ISO8859-1编码再用gbk解码解决。

代码实例如下:
/*编码:字符串变成字节数组。解码:字节数组变成字符串。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,"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);}}
0 0
原创粉丝点击