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



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>

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值