您的位置:首页 > 职场人生

黑马程序员——IO流(其他流)

2015-09-20 09:34 330 查看

-------android培训java培训、期待与您交流!
---------

一、
ObjectInputStream 和 ObjectOutputStream
对象存储在堆内存中,当使用完了就会被当做垃圾回收,对象就不存在了

可以通过流的方式将堆内存对象存储在硬盘上,即使程序结束,

对象也不会消失,想要使用时,直接读取存储对象的文件即可

这种方式被称为对象的序列化
操作对象

ObjectInputStream与ObjectOutputStream

被操作的对象需要实现Serializable(标记接口)

ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。

可以使用 ObjectInputStream 读取(重构)对象。

构造方法:ObjectOutputStream(OutputStream out)

创建写入指定 OutputStream 的 ObjectOutputStream。

方法:writeObject(Object obj) 将指定的对象写入 ObjectOutputStream。

当要序列化的对象不能实现java.io.Serializable 接口。

方法抛出NotSerializableException

Serializable:类通过实现 java.io.Serializable 接口以启用其序列化功能。

未实现此接口的类将无法使其任何状态序列化或反序列化。

可序列化类的所有子类型本身都是可序列化的。

序列化接口没有方法或字段,仅用于标识可序列化的语义。

ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
//将一个对象写入到一个文件中

public static void writeObj()throws IOException

{ //所存储对象的文件一般不存成.txt,这里应该存成 Person.Object

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));

oos.writeObject(new Person("lisi",39));//调用写对象方法(已建立好的Person类)


oos.close();

}

//将写入文件的对象读出来

public static void readObj()throws Exception

{

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));

//调用读对象方法,返回值类型是Object,因为对象是Person,返回值类型可以直接写Person

Person p = (Person)ois.readObject();//需要强制转换成类类型

//此方法抛出 ClassNotFoundException(类没有找到异常)

System.out.println(p);

ois.close();

}


二、
DataInputStream 和 DataOutputStream
可以用于操作基本数据类型的数据的流对象
DataOutputStream:

writeUTF(String str)

以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流。

DataInputStream:

readUTF(DataInput in)

从流 in 中读取用 UTF-8 修改版格式编码的 Unicode 字符格式的字符串;然后以 String 形式返回此字符串。

抛出:EOFException - 如果此输入流在读取所有字节之前到达末尾。



public static void writeUTFDemo()throws IOException

{

DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdata.txt"));

dos.writeUTF("你好");

dos.close();

}

public static void readUTFDemo()throws IOException

{

DataInputStream dis = new DataInputStream(new FileInputStream("utfdata.txt"));

String s = dis.readUTF();

System.out.println(s);

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

}

public static void readData()throws IOException

{

DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));

//读取时要按顺序读取

int i = dis.readInt();

boolean b = dis.readBoolean();

double d = dis.readDouble();

System.out.println("i="+i);

System.out.println("b="+b);

System.out.println("d="+d);

dis.close();

}


三、
PrintStream 和 PrintWriter
打印流:

该流提供了打印方法,可以将各种数据类型的数据原样打印

字节打印流:

PrintStream

构造函数可以接收的参数类型:

1,file对象。File

2,字符窜路径。String

3,字节输出流。OutputStream

字符打印流:

PrintWriter

1,file对象。File

2,字符窜路径。String

3,字节输出流。OutputStream

4,字符输出流。writer


public static void main(String[] args) throws IOException

{

BufferedReader bufr =

new BufferedReader(new InputStreamReader(System.in));

//PrintWriter out = new PrintWriter(new FileWriter("a.txt"),true); //写入到文件

PrintWriter out = new PrintWriter(System.out,true);//定义一个打印流,输出在控制台上

String line = null; //此处用true下面就不用flush刷新,只有println,printf,format刷新缓冲区

while((line=bufr.readLine())!=null)

{

if("over".equals(line))

break;

out.println(line.toUpperCase());//打印流的输出方法

out.flush();

}

out.close();

bufr.close();

}


四、
PipedInputStream 和 PipedOutputStream
管道流

PipedInputStream和PipedOutputStream

输入输出可以直接进行连接,通过结合线程使用

通常,数据由某个线程从 PipedInputStream 对象读取,

并由其他线程将其写入到相应的 PipedOutputStream。

不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。

//管道输入流线程

class Read implements Runnable

{

private PipedInputStream in;

Read(PipedInputStream in)

{

this.in = in;

}

public void run()

{

try

{

//管道流读数据

byte[] buf = new byte[1024];

int len = in.read(buf);

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

{

//管道流写数据

out.write("piped lai la".getBytes());

out.close();

}

catch (IOException e)

{

throw new RuntimeException("写出失败!");

}

}

}


五、
ByteArrayInputStream 和 ByteArrayOutputStream
操作字节数组的流对象

ByteArrayInputStream和ByteArrayOutputStream

ByteArrayInputStream:

ByteArrayInputStream 包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪 read 方法要提供的下一个字节。

关闭 ByteArrayInputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。

在构造的时候,需要接受数据源,而且数据源是一个字节数组

ByteArrayOutputStream:

此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。

关闭 ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。

在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组,这就是数据的目的地

因为这两个流对象都操作的是数组,并没有使用体统资源,所以,不用进行close关闭
在流操作规律时讲解:

源设备:

键盘 System.in 硬盘 FileStream 内存 ArrayStream

目的设备:

控制台 System.out 硬盘 FileStream 内存 ArrayStream

用流的读写思想来操作数组
同理:

操作字符数组:

CharArrayReader CharArrayWriter

操作字符串:

StringReader StringWriter

public static void main(String[] args)

{

//数据源 //此处接受的是字节数组

ByteArrayInputStream bis = new ByteArrayInputStream("abcdefg".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());

}



六、

SequenceInputStream

序列流:SequenceInputStream对多个流进行合并

作用:表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,

直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。

就是将多个流对象拼成一个流对象

需求:将三个文件复制到一个文件中

用SequenceInputStream将三个文件合并,再写入另一个文件

public static void main(String[] args) throws IOException

{

Vector<FileInputStream> v = new Vector<FileInputStream>();

//使用集合Vector将三个流对象添加进去

v.add(new FileInputStream("1.txt"));

v.add(new FileInputStream("2.txt"));

v.add(new FileInputStream("3.txt"));

Enumeration<FileInputStream> en = v.elements();

//构造函数接受Enumeration,集合中只有Vector使用Enumeration

SequenceInputStream sis = new SequenceInputStream(en);

//上面代码既实现了多个流的合并,形成了一个流对象,下面用输出流将其写入文件

FileOutputStream fos = new FileOutputStream("4.txt");

byte[] buf = new byte[1024];

int len = 0;

while((len = sis.read(buf))!=-1)

{

fos.write(buf,0,len);

}

fos.close();

sis.close();

}

七、

字符编码

字符编码

字符流的出现为了方便操作字符

更重要的是加入了编码转换

通过子类转换流来完成

InputStreamReader

OutputStreamWriter

在两个对象进行构造的时候可以加入字符集

编码表的由来

计算机只能识别二进制数据,早期由来是电信号

为了方便应用计算机让它可以识别各个国家的文字

就将各个国家的文字用数字来表示,并一一对应,形成一张表

这就是编码表

常见的编码表

ASCII:美国标准信息交换码

用一个字节的7位可以表示

ISO8859-1:拉丁码表,欧洲码表

用一个字节的8位表示

GB2312:中国的中文编码表

GBK:中国的中文编码表升级,融合更多的中文文字符号

Unicode:国际标准码,融合了多种文字

所有文字都用两个字节来表示,java语言使用的就是Unicode

UTF-8:最多用三个字节来表示一个字符

/*

编码:字符串变成字节数组

String-->byte[]:str.getBytes(charseName);

解码:字节数组变成字符串

byte[]-->String:new String(byte[].charseName);

*/

import java.util.*;

class EncodeDemo

{

public static void main(String[] args) throws Exception

{

demo_2();

}

public static void demo_1()throws Exception

{

String s = "你好";

//编码。默认情况下是GBK

byte[] b = s.getBytes("GBK");//UnsupportedEncodingException抛出异常

System.out.println(Arrays.toString(b));//打印编码对应的数

//解码,就是将字符数组变成字符串

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

System.out.println("s="+s);

}

public static void demo_2()throws Exception

{

String s = "你好";

//按照GBK进行编码

byte[] b1 = s.getBytes("GBK");

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

//按照ISO8859-1解码,解码错误,会出现乱码

String s1 = new String(b1,"ISO8859-1");

System.out.println("s1="+s1);

//出现乱码就要解决:将得到的乱码再进行编码

byte[] b2 = s1.getBytes("ISO8859-1");

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

//然后再按照GBK解码,就能得到原来的数据

String s2 = new String(b2,"GBK");

System.out.println("s2="+s2);

//上诉方法对于出现乱码的解决是通用的,但当解码错误时,是按照UTF-8解的码

//就不能使用一上面的方法,因为GBK和UTF-8都识别中文,所以用上诉方法还是会出现乱码

//Tomcat服务器默认使用的是ISO8859-1编码表

}

}

/*

新建一个记事本文件,写入'联通',保存在打开以后发现出现了乱码,

这是因为解码时出现了问题,当存储的时候是按照GBK来存储的,

再次打开时,记事本软件是按照UTF-8来解码的,所以出现了乱码

那么为什么会出现这种现象呢?

UTF-8的码表从最低一个字节表示一个字符到最高三个字节表示一个字符

当它解码时怎么确定这一个字符是几个字节表示的呢?

怎么确定此时是读一个字节、两个字节还是三个字节,来返回这个字符呢?

GBK用两个字节代表一个字符,而UTF-8最高用三个字节来代表一个字符

当解码时,UTF-8有它自己的编码标志,符合这一标志,记事本就自动按照

UTF-8来解得码,那么UTF-8的编码是什么样的呢?

Utf-8的码表其实对它的字节都加了标识头信息,用此信息就能识别

解码时是读一个、两个还是三个字节

'\u0001' 到 '\u007F' 范围内的所有字符都是用单个字节表示的:

位值

字节 1 0 位 6-0

null 字符 '\u0000' 以及从 '\u0080' 到 '\u07FF' 的范围内的字符用两个字节表示:

位值

字节 1 1 1 0 位 10-6

字节 2 1 0 位 5-0

'\u0800' 到 '\uFFFF' 范围内的 char 值用三个字节表示:

位值

字节 1 1 1 1 0 位 15-12

字节 2 1 0 位 11-6

字节 3 1 0 位 5-0

想要解决这个问题很简单,只要“联通”前面还有别的文字就可以了

*/

class EncodeDemo2

{

public static void main(String[] args) throws Exception

{

String s = "联通";

byte[] by = s.getBytes("GBK");//编码

for(byte b : by)

{

//打印编码是得到的每个字节的十进制数

//System.out.println(b);

//打印结果:-63 -86 -51 -88

//用二进制打印一下,保留最低八位,&255

System.out.println(Integer.toBinaryString(b&255));

/*打印结果:11000001 打印结果显示“联通”所对应的二进制数

10101010 正好和UTF-8的编码标识头对应上了,所以

11001101 在解码的时候,记事本就按照UTF-8码表了

10101000*/

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: