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


 一、对象序列化

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);

	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值