JavaSE 拾遗(12)——JavaSE IO...(3)


操作对象的流 

ObjectInputStream 与 ObjectOutputStream  被操作的对象需要实现 Serializable (序列化接口); 


ObjectOutputStream:

构造方法
protected  ObjectOutputStream()  为完全重新实现 ObjectOutputStream 的子类提供一种方法,让它不必分配仅由 ObjectOutputStream 的实现使用的私有数据。
ObjectOutputStream(OutputStream out)  创建写入指定 OutputStream 的 ObjectOutputStream。
常用方法
void writeObject(Object obj) 
将指定的对象写入 ObjectOutputStream,主要是对象堆中的数据,所以静态成员字段不会被序列化。
其他参考API java.io包.


Object InputStream:
构造方法
protected  ObjectInputStream()    为完全重新实现 ObjectInputStream 的子类提供一种方式,让它不必分配仅由 ObjectInputStream 的实现使用的私有数据。

ObjectInputStream(InputStream in)   创建从指定 InputStream 读取的 ObjectInputStream。

常用方法
Object readObject() 从 ObjectInputStream 读取对象。

 其他参与API java.io包.


Serializable:接口

类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。序列化接口没有方法或字段,仅用于标识可序列化的语义。其实 Serializable 就是给类加了个ID号:ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L,这个 ID 号用来保证每个序列化和反序列化的对象和类是匹配的;每一个类被序列化的话。都希望有一个 UID 标识,UID 标识通常都是给编译器使用的。因为一个类产生了一个而对象以后(被持久化了以后),这个类还可以改,才从新编译的时候会产生新的序列号,原来持久化回合修改后的不匹配。所以用UID号,来判断修改类了之后UID会改变.如果这是想还使用这个类。就自己定义一个UID。

class Person{
     public static final long serialVersionUID = 42L;
     private String name;
     ......
     ...
}


 注意:
    1、静态不可以被序列化
    2、如果非静态也不想被序列号的话.加上关键字 transient。
    3、一般ObjectInputStream与ObjectOutputStream是成对使用的。

import java.io.*;

class ObjectStreamDemo {
	public static void main(String[] args) throws Exception {
		// writeObj();
		readObj();
	}

	// 读取
	public static void readObj() throws Exception {
		// 和文件相关联
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream(
				"Object.obj"));
		// 转成对象
		Object obj = ois.readObject();
		// 打印
		System.out.println(obj);
		// 关闭流
		ois.close();
	}

	// 将对象存放在硬盘上
	public static void writeObj() throws Exception {
		// 建立对象并指定文件
		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
				"Object.obj"));
		// 将指定的对象写入 ObjectOutputStream。
		oos.writeObject(new Person("zhangsan", 32));
		// 关闭流
		oos.close();
	}
}

PipedInputStream和PipedOutputStream

输入输出可以直接进行连接,通过结合线程使管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。
 
构造方法:
   PipedInputStream() 
       创建尚未连接的 PipedInputStream。
   PipedInputStream(int pipeSize) 
       创建一个尚未连接的 PipedInputStream,并对管道缓冲区使用指定的管道大小。
   PipedInputStream(PipedOutputStream src) 
       创建 PipedInputStream,使其连接到管道输出流 src。
   PipedInputStream(PipedOutputStream src, int pipeSize) 
       创建一个 PipedInputStream,使其连接到管道输出流 src,并对管道缓冲区使用指定的管道大小。

  常用方法:
    void | connect(PipedOutputStream src) 
      使此管道输入流连接到管道输出流 src。
   其他参阅API

演示:

import java.io.*;

class Read implements Runnable// 多线程
{
	private PipedInputStream in;// 将PipedInputStream私有

	Read(PipedInputStream in) {
		this.in = in;// 初始化
	}

	public void run()// 覆盖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);// 等待6秒
			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);// 传入in
		Write w = new Write(out);
		new Thread(r).start();// 开启线程
		new Thread(w).start();
	}
}


RandomAccessFile

 此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。
 存在指向该隐含数组的光标或索引,称为文件指针;输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针
 。如果随机访问文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。
 写入隐含数组的当前末尾之后的输出操作导致该数组扩展。该文件指针可以通过 getFilePointer 方法读取,并通过 seek 方法设置。 
 通常,如果此类中的所有读取例程在读取所需数量的字节之前已到达文件末尾,则抛出 EOFException(是一种 IOException)
 。如果由于某些原因无法读取任何字节,而不是在读取所需数量的字节之前已到达文件末尾,则抛出 IOException,而不是 EOFException。
 需要特别指出的是,如果流已被关闭,则可能抛出 IOException。
 
 该类不是算是IO体系中子类。 而是直接继承自Object。 但是它是IO包中成员。因为它具备读和写功能。内部封装了一个数组,而且通过
 指针对数组的元素进行操作。可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置。其实完成读写的原理就是内部封装了字节输
 入流和输出流。通过构造函数可以看出,该类只能操作文件。而且操作文件还有模式:只读r,,读写rw等。如果模式为只读 r。不会创建文件。会去
 读取一个已存在文件,如果该文件不存在,则会出现异常。如果模式rw。操作的文件不存在,会自动创建。如果存则不会覆盖。
 
构造方法:
 
  RandomAccessFile(File file, String mode) 
    创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。


  RandomAccessFile(String name, String mode) 
    创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。 
    值
 
   mode的含意
    "r" 以只读方式打开。调用结果对象的任何 write 方法都将导致抛出 IOException。 
    "rw" 打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。 
    "rws" 打开以便读取和写入,对于 "rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
    
    "rwd"   打开以便读取和写入,对于 "rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
    
   例如:
    RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");
 
常用方法:
   void writeInt(int v) 
    按四个字节将 int 写入该文件,先写高字节。
 
   void seek(long pos) 
    设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。
 
   int skipBytes(int n) 跳过指定的字节数

    尝试跳过输入的 n 个字节以丢弃跳过的字节。 


示例:
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();
	}
}

DataInputStream与DataOutputStream 

操作基本数据类型

DataOutputStream:
  允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,应用程序可以使用数据输入流将数据读入。


构造方法:

   DataOutputStream(OutputStream out) 
    创建一个新的数据输出流,将数据写入指定基础输出流。
 
   例:
    DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
 
常用方法
   void | writeInt(int v) 
    将一个 int 值以 4-byte 值形式写入基础输出流中,先写入高字节。
 
   void | writeBoolean(boolean v) 
    将一个 boolean 值以 1-byte 值形式写入基础输出流。
 
   void | writeDouble(double v) 
    使用 Double 类中的 doubleToLongBits 方法将 double 参数转换为一个 long 值,然后将该 long 值以 8-byte 值形式写入基础输出流中,先写入高字节。

   void | writeUTF(String str) 
     以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流。
 
   例:
    dos.writeInt(234);
    dos.writeBoolean(true);
    dos.writeDouble(9887.543);
    其他方法参阅API  java.io .


  
DataInputStream:

构造方法:
    DataInputStream(InputStream in) 
     使用指定的底层 InputStream 创建一个 DataInputStream。
 
   例:
    DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
   
常用方法:
    static String readUTF(DataInput in) 
      从流 in 中读取用 UTF-8 修改版格式编码的 Unicode 字符格式的字符串;然后以 String 形式返回此字符串。
 
    例:
     int num = dis.readInt();
     boolean b = dis.readBoolean();
    double d = dis.readDouble();
   注意:UTF-8是6个字节的UTF-8修改版是8个字节,
    用UTF-8修改版写出去的只能用UTF-8修改版的读
   其他方法参阅API  java.io .


//-----------------------------------------------------------------------------

操作字节数组

 ByteArrayInputStream与ByteArrayOutputStream
 
操作字符数组
 CharArrayReader与CharArrayWrite
 
操作字符串
 StringReader与StringWriter
 
用于操作字节数组的流对象。
 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());// 打印

	}
}

操作字符数组和操作字符串和操作字节数组大致相同.详情参阅API.
//-----------------------------------------------------------------------------


字符编码

字符流的出现为了方便操作字符。
更重要是的加入了编码转换。
通过子类转换流来完成。
InputStreamReader
OutputStreamWriter
在两个对象进行构造的时候可以加入字符集。
 
编码表的由来
计算机只能识别二进制数据,早期由来是 电信号。
为了方便应用计算机,让它可以识别各个 国家的文字。
就将各个国家的文字用数字来表示,并一 一对应,形成一张表。
这就是编码表。
 
常见的编码表
ASCII:美国标准信息交换码。
用一个字节的7位可以表示。
ISO8859-1:拉丁码表。欧洲码表
用一个字节的8位表示。
GB2312:中国的中文编码表。
GBK:中国的中文编码表升级,融合了更多的中文文字符号。
Unicode:国际标准码,融合了多种文字。
所有文字都用两个字节来表示,Java语言使用的就是unicode
UTF-8:最多用三个字节来表示一个字符。
 
转换流的编码应用
可以将字符以指定编码格式存储。
可以对文本数据指定编码格式来解读。
指定编码表的动作由构造函数完成。
字符编码
 
编码:字符串  字节数组
解码:字节数组  字符串
示例:
import java.io.*;
import java.util.*;

class EncodeDemo {
	public static void main(String[] args) {
		String s = "老师最漂亮了我爱你";
		try {
			byte[] b1 = s.getBytes("GBK");

			System.out.println(Arrays.toString(b1));

			String s1 = new String(b1, "GBK");

			System.out.println(s1);
		} catch (UnsupportedEncodingException uue) {
			throw new RuntimeException("解码错误");
		}
	}
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值