Java基础——对象序列化+管道流+RandomAccessFile+操作基本数据类型的DataStream等

来源:互联网 发布:4g通信模块数据发送 编辑:程序博客网 时间:2024/04/30 04:07


1.    IO流

对象的序列化

ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。可以使用ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。

 

ObjectInputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。

 

用ObjectOutputStream保存的对象只能用ObjectInputStream读取

 

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

Object readObject():从 ObjectInputStream 读取对象。

 

请看如下示例:

class Person implements Serializable//标记接口,没有方法{        //UID是给类添加固定标识,为了序列化方便    //自动生成UID时,class文件的代码变动,无法使用原有对象,所以手动生成唯一UID    publicstaticfinallongserialVersionUID = 42L;    private String name;    transientintage;//transient修饰的变量,不能被序列化    static String country = "cn";//静态是不能被序列化的,;因为他在方法区中(共享区)    Person(String name,int age,String country)    {       this.name = name;       this.age = age;       this.country = country;    }    public String toString()    {       returnname+":"+age+":"+country;    }}  import java.io.*; class ObjectStreamDemo {    publicstaticvoid main(String[] args) throws Exception    {       //writeObj();       readObj();    }    publicstaticvoid readObj()throws Exception    {       ObjectInputStream ois = new ObjectInputStream(new FileInputStream ("obj.txt"));        Person p = (Person)ois.readObject();        System.out.println(p);       ois.close();    }     publicstaticvoid writeObj()throws IOException    {       ObjectOutputStream oos =            new ObjectOutputStream(new FileOutputStream("obj.txt"));        oos.writeObject(new Person("lisi0",399,"kr"));        oos.close();    }} 

 

 

管道流

1.概念:

PipedInputStream:管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从 PipedInputStream对象读取,并由其他线程将其写入到相应的 PipedOutputStream。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。

void connect(PipedOutputStream src):使此管道输入流连接到管道输出流 src。

void connect(PipedInputStream snk) : 将此管道输出流连接到接收者。

 

2.  特点

管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。

 

涉及多线程的IO流是管道流,涉及IO流的集合是properties

3.  怎么使用?

class Read implements Runnable{    private PipedInputStream in;    Read(PipedInputStream in)    {       this.in = in;    }    publicvoid run()    {       try       {           byte[] buf = newbyte[1024];            System.out.println("读取前。。没有数据阻塞");           int len = in.read(buf);//read()是阻塞式的方法           System.out.println("读到数据。。阻塞结束");            String s= new String(buf,0,len);            System.out.println(s);            in.close();        }       catch (IOException e)       {           thrownew RuntimeException("管道读取流失败");       }    }} class Write implements Runnable{    private PipedOutputStream out;    Write(PipedOutputStream out)    {       this.out = out;    }    publicvoid run()    {       try       {           System.out.println("开始写入数据,等待6秒后。");           Thread.sleep(6000);           out.write("piped lai la".getBytes());           out.close();       }       catch (Exception e)       {           thrownew RuntimeException("管道输出流失败");       }    }} class  PipedStreamDemo{    publicstaticvoid 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();    }} 

 

RandomAccessFile

1.  概念

随机访问文件,自身具备读写的方法。

通过skipBytes(int x),seek(int x)来达到随机访问。

 

int read(byte[] b):将最多 b.length个数据字节从此文件读入 byte 数组。

int readInt():从此文件读取一个有符号的 32位(4字节)整数。

void seek(long pos): 设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。调整对象中指针。前后都能跳。

skipBytes(int n):尝试跳过输入的 n个字节以丢弃跳过的字节。不能往回跳。

 

2.  特点

该类不算是IO体系中子类。

而是直接继承自Object

 

但是它是IO包中成员。因为它具备读和写功能。

内部封装了一个数组,而且通过指针对数组的元素进行操作。

可以通过getFilePointer获取指针位置,

同时可以通过seek改变指针的位置。

 

其实完成读写的原理就是内部封装了字节输入流和输出流。(操作数据必然是流)

 

通过构造函数可以看出,该类只能操作文件。

而且操作文件还有模式:只读r,,读写rw等。

 

如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。

如果模式rw。操作的文件不存在,会自动创建。如果存在则不会覆盖。

3.  怎么使用?

class RandomAccessFileDemo {    publicstaticvoid main(String[] args) throws IOException    {       //writeFile_2();       //readFile();        //System.out.println(Integer.toBinaryString(258));     }     publicstaticvoid readFile()throws IOException    {       RandomAccessFile raf = new RandomAccessFile("ran.txt","r");              //调整对象中指针。       //raf.seek(8*1);        //跳过指定的字节数       raf.skipBytes(8);        byte[] buf = newbyte[4];//一个中文两个字节,刚好读取到两个中文        raf.read(buf);        String name = new String(buf);        int age = raf.readInt();//一次读取四个字节,32位         System.out.println("name="+name);       System.out.println("age="+age);        raf.close();      }     publicstaticvoid writeFile_2()throws IOException    {       RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");       raf.seek(8*0);       raf.write("周期".getBytes());       raf.writeInt(103);//writer()方法只写int类的最低八位,其他会丢失。writeInt()写INT类型的4字节32位       raf.close();    }     publicstaticvoid writeFile()throws IOException    {       RandomAccessFile raf = new RandomAccessFile("ran.txt","rw");        raf.write("李四".getBytes());       raf.writeInt(97);       raf.write("王五".getBytes());       raf.writeInt(99);        raf.close();    }} 

4.  什么时候使用?

实现数据的分段写入,每一段自己拥有一个线程,如:下载软件原理,每个软件下载独立执行,分段下载,实现多线程下载

 

操作基本数据类型的流对象DataStream

1.  概念

可以用于操作基本数据类型的数据的流对象。

 

gbk:一个中文占2字节

utf-8:一个中文占3字节

utf-8修改版:一个中文占4字节

不同编码写的文件,需要用不同的编码读取,否则读取错误

 

double readDouble() :读取八个输入字节并返回一个 double值。

int readInt():读取四个输入字节并返回一个 int值。

void writeUTF(String s):将表示长度信息的两个字节写入输出流,后跟字符串 s中每个字符的 UTF-8 修改版表示形式。

 

EOFException - 如果此输入流在读取所有字节之前到达末尾。既没读完数据就到结尾了

 

2.  特点

基本数据类型写入和取出的顺序要保持一致

3.  怎么使用?

class test {    publicstaticvoid main(String[] args) throws IOException    {       //基本数据类型的读写操作       write();       read();                     //按照固定字符编码格式写入字符       OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("c:/t2.txt"),"GBK");       osw.write("你是傻蛋");       osw.close();       BufferedReader  osr = new BufferedReader(new InputStreamReader(new FileInputStream("c:/t2.txt")));       String line=null;       while((line=osr.readLine())!=null)       {           sop(line);        }              //writeUTF()方法读写文件       writeUTF();       readUTF();    }    publicstaticvoid readUTF()throws IOException    {       DataInputStream dif= new DataInputStream(new FileInputStream("c:/t3.txt"));              sop(dif.readUTF());//只能读取writeUTF()方法写的文件       dif.close();          }        publicstaticvoid writeUTF()throws IOException    {       DataOutputStream dof = new DataOutputStream(new FileOutputStream("c:/t3.txt"));       dof.writeUTF("我很帅");//英文不涉及编码,用中文       dof.close();    }    publicstaticvoid read()throws IOException    {       DataInputStream dif= new DataInputStream(new FileInputStream("c:/t1.txt"));       sop(dif.readDouble());       sop(dif.readInt());       sop(dif.readBoolean());       sop(dif.readByte());              dif.close();          }        publicstaticvoid write()throws IOException    {       DataOutputStream dof = new DataOutputStream(new FileOutputStream("c:/t1.txt"));       dof.writeDouble(33.333333);       dof.writeInt(35);       dof.writeBoolean(true);       dof.writeByte(88);        dof.close();    }    publicstaticvoid sop(Object obj)    {       System.out.println(obj);    }}

 

4.什么时候使用?

当需要操作基本数据类型的时候

 

操作字节数组-ByteArrayStream

1.  概念

用于操作字节数组的流对象。

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

 

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

 

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

 

在流操作规律讲解时:

源设备,

    键盘 System.in,硬盘 FileStream,内存 ArrayStream。

目的设备:

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

 

2.  特点

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

 

因为这两个流对象都操作的数组,并没有使用系统资源。

所以,不用进行close关闭。

 

3.  怎么使用?

class test {    publicstaticvoid main(String[] args)    {       //读取数据源,在内存中会难过       ByteArrayInputStream bas = new ByteArrayInputStream("asdfqwer".getBytes());              //数据目的,在内存中       ByteArrayOutputStream bos = new ByteArrayOutputStream();       int len=0;       while((len=bas.read())!=-1)           {              bos.write(len);              sop("写入的数据是:"+(char)len);           }       //默认数组(缓冲区)的大小       sop(bos.size());       //取出缓冲区数据       sop(bos.toString());       //bos.writeTo(new FileOutputStream("a.txt"));//只有此方法会抛出异常    }        publicstaticvoid sop(Object obj)    {       System.out.println(obj);    }}

4.  什么时候使用?

当要将硬盘文件读到内存中,放到可变长度的数组里存储起来时(用流的思想操作数组,读和取)

操作字符数组-CharArrayReaderCharArrayWrite

用法与操作字节数组相似

操作字符串数组-StringReader StringWriter  

用法与操作字节数组相似

 

 

转换流的字符编码

1.  概念

 

编码表

计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个

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

 

常见的编码表

ASCII:美国标准信息交换码。

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

 ISO8859-1:拉丁码表。欧洲码表

• 用一个字节的8位表示。

 GB2312:中国的中文编码表。两个字节的8位表示,且两个字节的高位都是1。兼容ASCII码。容纳6000-7000个字。

 GBK:中国的中文编码表升级,融合了更多的中文文字符号。容纳20000多个字。

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

• 所有文字都用两个字节来表示,Java语言使用的就是unicode。

 UTF-8:最多用三个字节来表示一个字符。(能用一个字节的就用一个字节存储,两个字节的就用两个字节。减少空间)每个字节开头都有标识头,容易区分UTF-8。

 

当两个编码表都能识别中文,但是同一文字在两张码表中对应的数字不同。所以涉及到了编码转换问题

 

不同编码进行写出和读取操作原理:

 

2.         特点

可以将字符以指定编码格式存储。

可以对文本数据指定编码格式来解读。

指定编码表的动作由构造函数完成。

3.         怎么使用?

class EncodeStream {    publicstaticvoid main(String[] args) throws IOException     {           //writeText();           readText();    }     publicstaticvoid readText()throws IOException     {       InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"gbk");        char[] buf = newchar[10];       int len = isr.read(buf);        String str = new String(buf,0,len);        System.out.println(str);        isr.close();    }    publicstaticvoid writeText()throws IOException     {       OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");         osw.write("你好");        osw.close();    }} 

字符编码

1.         概念

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

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

 

编码正确,解码错误,解决方法原理:

 

注意:

当用UTF-8解码》重新编码》解码,结果错误;因为GBKUTF-8都识别中文。UTF-8有未知编码区域,当UTF-8解码时,查不到指定的中文时就在未知区域返回相似的字符?,重新编码时,返回相似字符的数字,此时数字已经变化了。

 

2.         特点

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

byte[] -->String: new String(byte[],charsetName);

 

Array.toString(byte[]):此方法可以将字节数组转变为字符串,但是不能指定编码格式。

 

中文不能用ISO8859-1进行编码,出错

 

3.         怎么使用?

class  EncodeDemo{    publicstaticvoid main(String[] args)throws Exception     {       String s = "哈哈";       //编码       byte[] b1 = s.getBytes("GBK");       System.out.println(Arrays.toString(b1));       //解码       String s1 = new String(b1,"iso8859-1");//String s1 = new String(b1,"utf-8");       System.out.println("s1="+s1);        //对s1进行iso8859-1编码。       byte[] b2 = s1.getBytes("iso8859-1");//byte[] b2 = s1.getBytes("utf-8");       System.out.println(Arrays.toString(b2));              //对b2进行gbk解码。       String s2 = new String(b2,"gbk");       System.out.println("s2="+s2);        }}

4.         什么时候使用?

当需要指定格式对数据进行编码,解码时。

 

 

字符编码-联通(特殊文字)

UTF-8GBK编码表的特点

Utf-8根据标识头来判断一次是读一个字节还是两个字节还是三个字节,如下图:

 请看如下案例:

class test {    publicstaticvoid main(String[] args) throws Exception    {       String s = "联通";        byte[] by = s.getBytes("gbk");        for(byte b : by)       {           //将十进制的String类型数据转换成二进制,而且只取后八位           System.out.println(Integer.toBinaryString(b&255));       }    }}

产生乱码的原因:

"联通"用GBK编码表产生的二进制数,与UTF-8的二进制数一致,当在记事本写入"联通"两字保存后,重新打开,进行解码。此时因为编码产生的二进制数与utf-8的一致,所以默认用utf-8编码表进行解码。导致解码错误;

 

解决方法:

"联通"两字前面加上其他的中文;

 

练习-将学生信息写到文件中

 

/*有五个学生,每个学生有3门课的成绩,从键盘输入以上数据(包括姓名,三门课成绩),输入的格式:如:zhagnsan,30,40,60计算出总成绩,并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.txt"中。 1,描述学生对象。2,定义一个可操作学生对象的工具类。 思想:1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象。2,因为学生有很多,那么就需要存储,使用到集合。因为要对学生的总分排序。    所以可以使用TreeSet。3,将集合的信息写入到一个文件中。 */publicclass test{    publicstaticvoid main(String args[]) throws IOException    {       //默认排序,升序       Set<Student> set = StudentTool.getStudent();       StudentTool.writeStudent(set);       //定义比较器,降序排序,所以要逆转指定比(默认)较器的顺序       /*Comparator<Student> cmp = Collections.reverseOrder();       Set<Student> set1 = StudentTool.getStudent(cmp);       StudentTool.writeStudent(set1);*/    }} //定义学生类。因为要排序,所以要实现comparable类,复写hashcode(),equals(),compareTo()方法class Student implements Comparable<Student>{    private String name;    privateintch,ma,en;    privateintsum;    Student(String name,int ch,int ma,int en)    {       this.name = name;       this.ch = ch;       this.ma = ma;       this.en = en;       sum = ch+ma+en;    }    public String getname()    {       returnname;    }    publicint getsum()    {       returnsum;    }        publicint HashCode()    {       returnname.hashCode()+sum*39;    }        publicboolean equals(Object obj)    {       if(!(obj instanceof Student))           thrownew ClassCastException("类型不匹配");       Student s = (Student)obj;       returnthis.name.equals(s.name)&&this.sum==s.sum;    }        publicint compareTo(Student s)    {       int num = new Integer(this.sum).compareTo(new Integer(s.sum));//按分数排序,分数相同按照姓名       if(num==0)           returnthis.name.compareTo(s.name);       return num;    }        public String toString()    {       return"Student["+name+", "+ch+", "+ma+", "+en+"]";    }}//定义工具类,操作学生对象class StudentTool{    //将数据保存到学生对象中,将学生对象保存到TreeSet集合中        //按照自定义比较器排序,降序    publicstatic Set<Student> getStudent(Comparator<Student> cmp) throws IOException    {       BufferedReader buf =  new BufferedReader(new InputStreamReader(System.in));       String line = null;              //因为要按照分数排序,而且要将学生对象存储,所以定义TreeSet集合       Set<Student> set = null;       if(cmp==null)           set = new TreeSet<Student>();       else           set = new TreeSet<Student>(cmp);              while((line = buf.readLine())!=null)       {           if("over".equals(line))              break;           String s[] = line.split(",");           //将数据保存到学生对象中           Student stu = new Student(s[0],                            Integer.parseInt(s[1]),                            Integer.parseInt(s[2]),                            Integer.parseInt(s[3]));                      set.add(stu);       }       buf.close();       return set;    }    //按照默认排序,升序    publicstatic Set<Student> getStudent() throws IOException    {       return getStudent(null);     }        //将集合中的数据写到文件中    publicstaticvoid writeStudent(Set<Student> s) throws IOException    {       BufferedWriter bw = new BufferedWriter(new FileWriter("c:/Student1.txt"));       for(Student stu:s)       {           bw.write(stu.toString()+"\t");           bw.write(stu.getsum()+"");//因为write方法只识别INT类型数据的后八位,所以转换成字符串类型           bw.newLine();           bw.flush();       }       bw.close();    }}<span style="font-family:Calibri;font-size:14px;"> </span>

 

 

 

0 0
原创粉丝点击