代码改变世界

黑马程序员-常见的一些流和编码表

2014-06-06 17:26  黑马程序员*  阅读(261)  评论(0编辑  收藏  举报

对象的序列化(对象要实现Serializable序列化接口,类似实现comparable )

1.操作对象的流:ObjectOutputStream ObjectInputStream

       ObjectOutputStream :

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

       通过在流中使用文件可以实现对象的持久存储。

       构造函数:ObjectOutputStream(OutputStream out)//初始化时要有目的

       方法:

       writeObject(Object obj);Object对象要实现Serializable接口。该接口无任何方法,被称为标记

       ObjectInputStream :将ObjectOutputStream写入的基本数据和对象进行反序列化。

       构造函数:

       ObjectInputStream(InputStream in) ;指定源

       Object】 readObject() ;读取类,返回对象。

注意:

       对象是字节码文件。

       读写object的对象必须是同一个对象。序列号相等(序列号可以自定义,

       如:static final long serialVersionUID = 42L)。

       静态是不能被序列化存储的,因为静态是在方法区,序列化只是将对内存的序列化。

       transient关键字,可以保证被修饰的变量只在堆中,不被序列化存储。

     //写

     File fi = new File("2101.txt");

              OutputStream os = new FileOutputStream(fi);//对象不是纯文本,是字节码文件

              ObjectOutputStream oos = new ObjectOutputStream(os);

              oos.writeObject(new person("lisi",20,"cn"));//是new person()//假设静态cn被改了,方法区静态变量会变吗 会

       //读

              File fi = new File("2101.txt");

              FileInputStream fis = new FileInputStream(fi);

              ObjectInputStream ois = new ObjectInputStream(fis);

              person p = (person)ois.readObject(); 

2.管道流:涉及多线程的IO流对象。(四个流对象都有,inputStream,outputStream,reader,writer)

       通常读取流和写入流之间是没有关系的,只有存在中转站的情况下,才存在联系。

       如:写入流写到数组中,读取流从数组读取。但是管道流可以实现不需要中转站,

       将读取流和写入流的管道接在一起,类似一根管道,一端读,一端写。

       但是会出问题:先读呢,还是先写?

       我们会用两个现场来操作,通常,数据由某个线程写入 PipedOutputStream 对象,并由其他线程从连接的PipedInputStream读取。

       不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。因为read方法是堵塞时方法,如果读不到数据,就等待。

与合并流(sequenceInputStream)的区别:合并流只有一个sequenceInputStream,是将多个输入流合并到一起。管道流是将输入流和输出流连接到一起, 多线程的实现:没写数据之前,即使读到也等待,直到读到数据。

构造方法:

       PipedOutputStream(PipedInputStream snk)

          创建连接到指定管道输入流的管道输出流。

       也可以这样

       PipedOutputStream()

          创建尚未连接到管道输入流的管道输出流。

       用connect(PipedInputStream snk) 连接两个流

          将此管道输出流连接到接收者。

       ObjectInputStream(InputStream in) //制定读取的文件

          创建从指定 InputStream 读取的 ObjectInputStream。

       常用方法:只能读取和写入字节或字节数组。 

       class writedemo implements Runnable{

              private  PipedInputStream pis;

              writedemo(PipedInputStream pis){//InputStream不可以放在这,否则this.InputStream=PipedInputStream;

              this.pis=pis;

              }

              public void run(){                            

                     byte[] bys = new byte[1024];

                     int len = 0;

                     while ((len=pis.read(bys))!=-1)   { //没有数据,线程等待。pipedoutputStream,写入,若再夺得线程,读取。

                            Thread.sleep(3000);

                            String str = new String(bys,0,len);//获取数组。                  

                            System.out.println(str);

                     }

                     pis.close();           

              }            

              class readdemo implements Runnable{

                     PipedOutputStream pos;

                     readdemo(PipedOutputStream pos){

                            this.pos= pos;      

                     }

                     public void run(){              

                            pos.write("管道来2122了".getBytes());        

                     }

                     }

              class  day2102{

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

                            PipedInputStream pis = new PipedInputStream();

                            PipedOutputStream pos = new PipedOutputStream();

                            pis.connect(pos);

                            writedemo wd = new writedemo(pis);

                            readdemo rd = new readdemo(pos);

                            Thread t1 = new Thread(wd);

                            Thread t2 = new Thread(rd);

                            t1.start();

                            t2.start();

                            }

                     }

3.随机访问文件 RandomAccessFile 不是IO子类

       功能:此类的实例支持对随机访问文件的读取和写入。可以用seek()方法来访问数据,并进行读写了。

                这些数据的大小不必相同;但是其大小和位置必须是可知的

       但它是IO包中的成员,因为它具备读写功能。部封装了一个byte数组,而且通过指针对数组的元素进行操作。

       可以通过getFilePointer获取真正位置,同时可以通过seek改变指针的位置。

       原理:内部封装了字节输入流和字节输出流。

构造函数:

RandomAccessFile(File file, String mode)

      创建从中读取和向其中写入(可选)的随机访问文件流,也可以指定文件名称。

       局限:通过构造函数可以发现,该类只能操作文件。而且操作文件还有模式Mode:r,只读  rw,读写等

       注意:

       1.如果模式为只读r,不会创建文件,只会去读取一个已经存在的文件,

         如果该文件不存在,则会出现异常

       2.如果模式为rw,操作的文件不存咋,会自动创建。如果有则【不会覆盖】。

       用处:可以通过多线程实现数据的分段写入。类似迅雷。通过分段规律实现多线程输入。

       IO中唯一一个。数据分段,有规律。

       常见方法:

        int readInt(),readDouble(),readLong().....

            writeInt(int v),write(long)......

       特殊方法:

       readLine()

       void seek(long pos)

    设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。

       skipBytes(int n) //只能往后走,不能回头

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

                RandomAccessFile ra1 = new RandomAccessFile("2014.txt","rw");

              //ra1.seek(8*2);//跳过8*2个字节

              //ra1.write("lisi".getBytes());

              //ra1.writeInt(97);//write()存储低八位

              //ra1.writeInt(97);//存入4个8位    

           ra1.skipBytes(8*2);

              String s =ra1.readLine();

             System.out.println(s);

4.可以操作基本数据类型的流对象:DataOutputStream, DataInputStream 。

数据输出流将基本 Java 数据类型写入输出流中

特点:通常数据类型输出流会按照一定的格式将数据输出,必须按指定好的格式保存才可以用输入流读取进来。

      DataOutput 接口、DataInput 接口,这两个接口的操作彼此对应

构造函数:

DataOutputStream(OutputStream out)

writeUTF(String str) //只能以dataInputStream读取。转换流不可以。String :要写入的字符串。

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

static 【String】 readUTF()//utf修改版只能由它来读。

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

              dos.writeBoolean(true);

       //     dos.writeBytes("lisi");

       //     dos.writeChars("zhansan");

              dos.writeInt(20);

              dos.close();

DataInputStream(InputStream in)

          使用指定的底层 InputStream 创建一个 DataInputStream。

boolean readBoolean()

          参见 DataInput 的 readBoolean 方法的常规协定。

byte readByte() char readChar() int readInt() double readDouble()

       :分别读取8,16,32,64个字节

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

              Boolean b = dis.readBoolean();

       //     byte s  = dis.readByte();

 //数据读反了,乱码。

       //     Byte s1 =

       //     char  b2 = dis.readChar();

              int s = dis.readInt();

              dis.close();

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

特有编码

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

              dos.writeUTF("你好");

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

              String str = dis.readUTF();//返回值

              //     System.out.println(str);

              dos.close();

(一) ByteArrayInputStream

操作字节数组:该流没有调用底层资 源,流关闭无效,不产生IOExcetion

       字节数组(基本流的数组?)--->字节数组(字节数组流)--->

构造方法:ByteArrayInputStream(byte[] buf)      

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

       ByteArrayInputStream: 包含一个内部缓冲区,该缓冲区包含从流中读取的字节

2.ByteArrayOutputStream:该输出流中,数据被写入一个缓冲区数组,该数组随数据的写入自动增加。  

 构造方法:ByteArrayOutputStream()  创建一个新的 byte 数组输出流。

 在构造的时候,不用定义目的,内部数组就是目的。

 输出流特有方法:

  int size()

          返回缓冲区的当前大小。

  byte[] toByteArray()

          创建一个新分配的 byte 数组。

  String toString()

          使用平台默认的字符集,通过解码字节将缓冲区内容转换为字符串。

  String toString(String charsetName)

           使用指定的 charsetName,通过解码字节将缓冲区内容转换为字符串。                

 void writeTo(OutputStream out):将数组流中的全部内容写到字节输出流中(文件等)

       例如:bos.writeTo(new FileOutputStream("2104.txt"))//此方法涉及异常。

特点:没有调用底层资源(windows?),不用关闭流,也不会产生IOException

        内部封装了数组,它是由字节流到字节流,始终都在内存中操作。

        作用:将一个文件读到内存中,先用数组存放起来。而且长度是可变的。 

源设备:

       键盘(System.in),硬盘(FileStream),内存(ArrayStream)

目的设备:

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

用处:将一个文件,读到内存,并放在数组先存取来。而且,数组长度可变, ArrayStream很方便。

对数组元素的操作:只有设置(设置值,改变)和获取。对应于流中就是写和读。

这就是用流读写的思想操作数组,以后遇到数组,可以封装的该流中。

(二)字符数组流和字符串流。CharArrayReader

CharArrayReader,CharArrayWriter 实现一个可用作字符输入流的字符缓冲区

       CharArrayReader(char[] buf)

          根据指定的 char 数组创建一个 CharArrayReader。  

       ByteArrayOutputStream()

          创建一个新的 byte 数组输出流。

StringReader,

       构造方法:StringReader(String s) 创建一个新字符串 reader。

       StringWriter()

          使用默认初始字符串缓冲区大小创建一个新字符串 writer。 

import java.io.*;

class day2105 {

       public static void arraydemo(){        

              //数据源

              byte[] bys = "adfdfdffa".getBytes() ;             

              ByteArrayInputStream bis = new ByteArrayInputStream(bys);

              //数据目的

              ByteArrayOutputStream bos = new ByteArrayOutputStream();

              //输入流:读取数据,读入流中,已经在流中的缓冲区,输入输出在管道的两头。

              int len =0;

              //本身具备数组,可以一个一个接受,read()方法一个一个读取,返回的是读取的字符。·

              while ((len =bis.read())!=-1){

                     bos.write(len);//表示将这个字符写入缓冲区中。

              }

       //     bis.close(); 不用关闭,不用抛

              System.out.println(bos.toString());

              System.out.println(bos.size());

       }

       public static void main(String[] args){

               arraydemo();

       }

}

 

/*必须懂

InputStreamReader,OutputStream转换流中加入了编码

PrintStream,printWriter也有,但是只能用于打印。

ASCII;美国标准信息交换吗 ,用一个字节7位

iso-8859-1:欧洲码表,用一个字节8位

GB2312:中国的中文编码

GBK:中文编码表升级版。2个字节 两个高位是1

Unicode:国际标准码,所有文字用两个字节。java语言使用的就是Unicode

UTF-8:最多用三个字节表示一个字节,一个字节可以表示,两个字节也可以

表示,三个也可以。 

字符编码:以转换流为主

        * printStream和PrintWriter也涉及编码表,但是只用于打印。

        * "你好"以gbk的编码表的存储。存的是字节。例如:你 查表对应"-45 -56",好 查表"-48 -89".存储的形式-45 -56  -48 -89

        *  读取的时候:读的是数字:-45 -56  -48 -89在GBK表中对应的是"你好"。但是如果使用的UTF-8读取时,-45 -56  -48一组,对应的是

        *  ?,而-48对应的也是?,都是未知字符。

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

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

        * String-->byte[]  .getBytes()

        * byte[]-->String   new String(buf) 

        /**UTF-8修改版标示头。UTF-8怎么决定使用一个字节,两个字节,三个字节。

        * 单个字节:头位:0 

        * 两个字节表示:第一个字节头位:1 1 0

        *                    第二个字节头位:1 0

        * 第三个字节表示:第一个字节头位:1 1 1 0

        *                        第二个字节头位:1 1 0

        *                        第三个字节:      1 0 

//联通使用Gbk编码,但是其二进制形式是U8表的表现形式。就去查U8的表。  

       //[-63, -86, -51, -88]

       //     11000001

       //     10101010

       //     11001101

       //     10101000

       //看表现形式可以发现,它符合两个字节表示的情况,而在u8中,汉字是以三个字节表示。所以出错。

练习:

        * 有五个学生,每个学生有3门功课的成绩,

              从键盘输入以上数据(包括姓名,三门成绩)

              输入格式:如:zhangsan,50,54,54,计算出总成绩。

              并把学生的信息和计算出的总分数按高低顺序存放在

              磁盘文件“stud.txt”中。