黑马程序员-学习日记13(IO流 3 对象的序列化)

来源:互联网 发布:桌面工作计划安排软件 编辑:程序博客网 时间:2024/06/05 09:17

 

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

 


 1.OutPutStream ---> 类 ObjectOutputStream(java,io包)

 1)ObjectOutputStream 将 Java 对象的基本数据类型和图形写入 OutputStream。
  可以使用 ObjectInputStream 读取(重构)对象。通过在流中使用文件可以实现对象的持久存储。
  如果流是网络套接字流,则可以在另一台主机上或另一个进程中重构对象。

  只能将支持 java.io.Serializable 接口的对象写入流中。每个 serializable 对象的类都被编码,
  编码内容包括类名和类签名、对象的字段值和数组值,以及从初始对象中引用的其他所有对象的闭包。
 2)构造方法摘要
   <1>protected  ObjectOutputStream()
     为完全重新实现 ObjectOutputStream 的子类提供一种方法,
     让它不必分配仅由 ObjectOutputStream 的实现使用的私有数据。
   <2>ObjectOutputStream(OutputStream out) 
     创建写入指定 OutputStream 的 ObjectOutputStream。
  3)特有方法:
   <1>void writeObject(Object obj)
     将指定的对象写入 ObjectOutputStream。
   <2>void writeInt(int val)
     写入一个 32 位的 int 值。

 2.InputStream ---> 类 ObjectInputStream (java.io包)

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

  ObjectOutputStream 和 ObjectInputStream 分别与 FileOutputStream 和 FileInputStream 一起使用时,
  可以为应用程序提供对对象图形的持久存储。ObjectInputStream 用于恢复那些以前序列化的对象。
  其他用途包括使用套接字流在主机之间传递对象,或者用于编组和解组远程通信系统中的实参和形参。

  objectInputStream 确保从流创建的图形中所有对象的类型与 Java 虚拟机中显示的类相匹配。
  使用标准机制按需加载类。

  只有支持 java.io.Serializable 或 java.io.Externalizable 接口的对象才能从流读取。
  2)构造方法摘要
  protected  ObjectInputStream()
     为完全重新实现 ObjectInputStream 的子类提供一种方式,让它不必分配仅由 ObjectInputStream 的实现使用的私有数据。
  ObjectInputStream(InputStream in)
     创建从指定 InputStream 读取的 ObjectInputStream。
  3)特有方法:
  Object readObject()
     从 ObjectInputStream 读取对象。
    
 3. 接口 Serializable (java.io包)
   1)public interface Serializable类通过实现 java.io.Serializable 接口以启用其序列化功能。
  未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。
  序列化接口没有方法或字段,仅用于标识可序列化的语义。
   2)可序列化类可以通过声明名为 "serialVersionUID" 的字段
    (该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID:


  ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
  如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值
 4.注意:
 1)实现了Serializable接口的类都会有一个UID的序列号(java自动生成)标示,类改变,序列号也会改变。
    要想改变类而不改变序列号,可手动给类定义一个固定的序列号(方便序列化)。方法就是:
    显式声明其自己的 serialVersionUID:

  public static final long serialVersionUID = 42L;

 2)静态变量不能被序列化,因为静态在方法区中,而对象是在堆里。序列化是对堆中的对象进行的。
 3)如果对堆中的成员也不想进行序列的化的话。只需加上关键字transient修饰就可以了。
 4)因为对象文件看不懂,也没必要打开看。所以文件名一般不存成obj.txt,而存成Person.object  。
 5)当Person类改变时,会发生InvalidClassException:异常,同时抛出的还有IOException。为方便演示故只抛出个Exception.


02-IO流(管道流)


 1.InputStream ---> PipedInputStream类(java.io包)

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

 2.OutputStream ---> PipedOutputStream类(java.io包)

  1)可以将管道输出流连接到管道输入流来创建通信管道。管道输出流是管道的发送端。通常,
  数据由某个线程写入 PipedOutputStream 对象,并由其他线程从连接的 PipedInputStream 读取。
  不建议对这两个对象尝试使用单个线程,因为这样可能会造成该线程死锁。
  如果某个线程正从连接的管道输入流中读取数据字节,但该线程不再处于活动状态,
  则该管道被视为处于 毁坏 状态
  2)构造方法摘要
  <1>PipedOutputStream()
     创建尚未连接到管道输入流的管道输出流。
  <2>PipedOutputStream(PipedInputStream snk)
     创建连接到指定管道输入流的管道输出流。
  3)特有方法:
  void connect(PipedInputStream snk)
     将此管道输出流连接到接收者。

03-IO流(RandomAccessFile)

 1. RandomAccessFile类:
  随机访问文件,自身具备读写的方法。
  通过skipBytes(int x),seek(int x)来达到随机访问。

  该类不算是IO体系中的子类。
  而是直接继承Object.

  但是它是IO包中成员。因为它具备读写功能。
  内部封装了一个数组。而且通过指针对数组的元素进行操作。
  可以通过getFilePointer获取指针位置。
  同时可以通过seek改变指针的位置。

  实现完成读写的原理就是内部封装了字节输入流和输出流。

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

  如果模式为只读r.不会创建文件。会去读取一个已存在文件。如果该文件不存在。则会出现异常。
  如果模式为rw.操作的文件不存在,会自动创建,如果存在,则不会覆盖。

 2.Object ----> RandomAccessFile类:
  1)此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为类似存储在文件系统中的
  一个大型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针;输入操作从文件指针开始读取字节,
  并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也可用;
  输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。
  写入隐含数组的当前末尾之后的输出操作导致该数组扩展。该文件指针可以通过 getFilePointer 方法读取,
  并通过 seek 方法设置。

  通常,如果此类中的所有读取例程在读取所需数量的字节之前已到达文件末尾,
  则抛出 EOFException(是一种 IOException)。如果由于某些原因无法读取任何字节,
  而不是在读取所需数量的字节之前已到达文件末尾,则抛出 IOException,而不是 EOFException。
  需要特别指出的是,如果流已被关闭,则可能抛出 IOException
  2)构造方法摘要
  <1>RandomAccessFile(File file, String mode)
     创建从中读取和向其中写入(可选)的随机访问文件流,该文件由 File 参数指定。
  <2>RandomAccessFile(String name, String mode)
     创建从中读取和向其中写入(可选)的随机访问文件流,该文件具有指定名称。
  3)特有方法:
  <1>void writeInt(int v)
     按四个字节将 int 写入该文件,先写高字节。
  <2>int skipBytes(int n)
     尝试跳过输入的 n 个字节以丢弃跳过的字节。
  <3>void write(byte[] b)
     将 b.length 个字节从指定 byte 数组写入到此文件,并从当前文件指针开始。
  <4>void write(int b)
     向此文件写入指定的字节。
  <5>void writeBoolean(boolean v)
     按单字节值将 boolean 写入该文件。
  <6>void writeByte(int v)
     按单字节值将 byte 写入该文件。
  <7>void writeBytes(String s)
     按字节序列将该字符串写入该文件。
  <8>void seek(long pos)
     设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。
  <9>int readInt()
     从此文件读取一个有符号的 32 位整数。
  <10>String readLine()
     从此文件读取文本的下一行。
  <11>long getFilePointer()
     返回此文件中的当前偏移量。
  <12>long length()
     返回此文件的长度。
  <13>int read()
     从此文件中读取一个数据字节。
  <14>int read(byte[] b)
     将最多 b.length 个数据字节从此文件读入 byte 数组。
  <15>void close()
     关闭此随机访问文件流并释放与该流关联的所有系统资源。

 3.随机流原理如图:Day21-03-随机流的原理

 4.注意:随机流可多线程实现数据的分段下载。如:迅雷软件的下载


04-IO流(操作基本数据类型的流对象DataStream)


   DataInputStream与DataOutputStream

   可以用于操作基本数据类型的数据的流对象。
 1.OutputStream ---> DataOutputStream类

  1)数据输出流允许应用程序以适当方式将基本 Java 数据类型写入输出流中。然后,
  应用程序可以使用数据输入流将数据读入。
  2)构造方法摘要
  DataOutputStream(OutputStream out)
     创建一个新的数据输出流,将数据写入指定基础输出流。
  3)特有方法:
  void writeBoolean(boolean v)
     将一个 boolean 值以 1-byte 值形式写入基础输出流。
  void writeDouble(double v)
     使用 Double 类中的 doubleToLongBits 方法将 double 参数转换为一个 long 值,
     然后将该 long 值以 8-byte 值形式写入基础输出流中,先写入高字节。
  void writeInt(int v)
     将一个 int 值以 4-byte 值形式写入基础输出流中,先写入高字节。
  void writeUTF(String str)
     以与机器无关方式使用 UTF-8 修改版编码将一个字符串写入基础输出流。
 2.InputStream ---> DataInputStream类

  1)数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型。
  应用程序可以使用数据输出流写入稍后由数据输入流读取的数据。

  DataInputStream 对于多线程访问不一定是安全的。 线程安全是可选的,它由此类方法的使用者负责
  2)构造方法摘要
  DataInputStream(InputStream in)
     使用指定的底层 InputStream 创建一个 DataInputStream。
  3)特有方法:
  boolean readBoolean()
     参见 DataInput 的 readBoolean 方法的常规协定。
  double readDouble()
     参见 DataInput 的 readDouble 方法的常规协定。
  int readInt()
     参见 DataInput 的 readInt 方法的常规协定。
  String readUTF()
     参见 DataInput 的 readUTF 方法的常规协定。

 3.注意UTF与UTF-8的差别:
   UTF-8是6个字节的两字。而UTF是8个字节的两字。
   null 字节 '\u0000' 是用 2-byte 格式而不是 1-byte 格式编码的,因此已编码的字符串中决不会有嵌入的 null。

05-IO流(ByteArrayStream)

 1.IO包中的其他类:

  1)操作基本数据类型:
  DataInputStream与DataOutputStream
  2)操作字节数组
  ByteArrayInputStream与ByteArrayOutputStream
  3)操作字符数组
  CharArrayReader与CharArrayWriter
  4)操作字符串
  StringReader与StringWriter

 2.InputStream ---> ByteArrayInputStream类

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

  关闭 ByteArrayInputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。
  2)构造方法摘要
  ByteArrayInputStream(byte[] buf)
     创建一个 ByteArrayInputStream,使用 buf 作为其缓冲区数组。
  ByteArrayInputStream(byte[] buf, int offset, int length)
     创建 ByteArrayInputStream,使用 buf 作为其缓冲区数组。
  3)方法摘要
   int available()
     返回可从此输入流读取(或跳过)的剩余字节数。
   int read()
     从此输入流中读取下一个数据字节。
   int read(byte[] b, int off, int len)
     将最多 len 个数据字节从此输入流读入 byte 数组。
   long skip(long n)
     从此输入流中跳过 n 个输入字节。
 3.OutputStream ---> ByteArrayOutputStream类

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

  关闭 ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。
  2)构造方法摘要
  ByteArrayOutputStream()
     创建一个新的 byte 数组输出流。
  ByteArrayOutputStream(int size)
     创建一个新的 byte 数组输出流,它具有指定大小的缓冲区容量(以字节为单位)。
  3)方法摘要
  void close()
     关闭 ByteArrayOutputStream 无效。
  void write(byte[] b, int off, int len)
     将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此 byte 数组输出流。
  void write(int b)
     将指定的字节写入此 byte 数组输出流。
  void writeTo(OutputStream out)
     将此 byte 数组输出流的全部内容写入到指定的输出流参数中,这与使用 out.write(buf, 0, count) 调用该输出流的 write 方法效果一样。
  int size()
     返回缓冲区的当前大小。
  byte[] toByteArray()
     创建一个新分配的 byte 数组。
  String toString()
     使用平台默认的字符集,通过解码字节将缓冲区内容转换为字符串。

06-IO流(转换流的字符编码)

 1.字符编码

  字符流的出现为了方便操作字符。
  更重要的是加入了编码转换。
  通过子类转换流来完成。
   InputStreamReader
   OutputStreamWriter
  在两个对象进行构造的时候可以加入字符集。

 2.编码的由来:

  计算机只能识别二进制数据,早期由来是电信号。
  为了方便应用计算机,让它可以识别各个国家的文字。
  就将各个国家的文字用数字来表示,并一一对应,形成一张表。
  这就是编码表。

 3.常见的编码表

  1)ASCII:美国标准信息交换码。
    用一个字节的7位可以表示。
  2)ISO8859-1:拉丁码表,欧洲码表。
    用一个字节的8位表示。
  3)GB2312:中国的中文编码表。
  4)GBK:中国的中文编码表升级,融合了更多的中文文字符号。
  5)Unicode:国际标准码,融合了多种文字。
    所有文字都用两个字节来表示,Java语言使用的就是unicode.
  6)UTF-8:最多用三个字节来表示一个字符。
  ......

 4.转换流的编码应用

  1)可以将字符以指定编码格式存储。
  2)可以对文本数据指定编码格式来解读。
  3)指定编码表的动作由构造函数完成。

 5.编码原理如图:Day21-06-字符编码原理图

 6.注意。
  若utf.txt去查gbk的码表来读,"你好"读出来是:??
  若gbk.txt去查utf-8的码表来读。"你好"读出来是:浣犲ソ

07-字符编码

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

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

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

 2.用到的方法:
  1)String类(java.lang包)
   <1>构造方法摘要
  String()
     初始化一个新创建的 String 对象,使其表示一个空字符序列。
  String(byte[] bytes)
     通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
  String(byte[] bytes, Charset charset)
     通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。
   <2>特有方法:
  byte[] getBytes()
     使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
  byte[] getBytes(Charset charset)
     使用给定的 charset 将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。

  2)Arrays类(java.util包)
  static String toString(byte[] a)
     返回指定数组内容的字符串表示形式。
  static String toString(Object[] a)
     返回指定数组内容的字符串表示形式。
    
 3.原理分析:
   如图:
  Day21-07-字符编码原理图1
  Day21-07-字符编码原理图2

 4.注意:
  sohu---utf-8
  sina---gbk
  因为utf-8和gbk都可以解码中文字符,所以出现乱码。故常用iso8859-1

 5.不支持的编码异常:
  EncodeDemo.java:12: 未报告的异常 java.io.UnsupportedEncodingException;必须对其
  进行捕捉或声明以便抛出
     String s1 = new String(b1,"gbk");

 6.代码:
   String s = "你好";
  //String s = "哈哈"

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

  Sytem.out.println(Arrays.toString(b1));
  String s1 = new String(b1,"iso8859-1");
  //String s1 = new String(b1,"utf-8");//因为utf-8和gbk都可以解码中文字符,所以出现乱码。故常用iso8859-1
  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));

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

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


08-字符编码-联通


 1.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
 
 注:联通这个词比较特殊,同时符合gbk和utf-8的编码要求,默认会去找utf-8的码表,所以存入的联通词,读取失败。

 2.原理分析图如:
  Day21-08-联通编码原理图
 3.代码:
 class EncodeDemo2
 {
  public static void main(String[] args) throws Exception
  {
   String s = "联通";

   byte[] by = s.getBytes("gbk");

   for (byte b : by)
   {
    System.out.println(Integer.toBinaryString(b&255));
   }

   
  }
 }

 

09-练习

 有五个学生,每个学生有3门课的成绩,
 从键盘输入以上数据(包括姓名,三门课成绩),
 输入的格式:如:zhangsan,30,40,60计算出总成绩,
 并把学生的信息和计算出的总分高低顺序存放在磁盘文件"stud.txt"中。

 1,描述学生对象
 2,定义一个可操作学生对象的工具类。

 思路:
 1,通过获取键盘录入一行数据,并将该行吕的信息取出封装成学生对象。
 2,因为学生有很多,那么就需要存储,使用到集合,因为要对学生的总分排序。
  所以可以使用TreeSet.
 3,将集合的信息写入到一个文件中

 

 以下是我的程序代码 :

 

 

/*

DataInputStream DataOutputStream
//内存为源和目的的基本操作。
ByteArrayInputStream ByteArrayOutputStream
CharArrayReader  CharArrayWriter
StringReader  StringWriter
//UID是根据类中的成员算出来的,类中的成员都具备一个标识,根据
//这个标识

import java.io.*;
class SeralizibleDemo{
 public static void main(String args[])throws Exception{
  //writeObj();
  readObj();
 }
 public static void writeObj()throws IOException{
  ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("obj.txt"));
  oos.writeObject(new Person("lisi",39,"Kr"));
  oos.close();
 }
 public static void readObj()throws Exception{
  ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
  Object o = ois.readObject();
  Person p = (Person)o;
  System.out.println(p);
  ois.close();
 }
}

class Person implements Serializable{//该接口没有方法,(没有方法的接口称为:标记接口)。
  
  public static final long serialVersionUID = 42L;//给类定义一个固定标识。当类中成员变时,照样能读取出来。
            //新的类还可以去操作曾经被序列化的对象。

  String name;//序列号是根据成员来获取的,当此处加了private后,运行时,将报错,因为类中成员的ID变化了。
  transient int age;
  static String country = "cn";
  Person(String name,int age,String country){
   this.name = name;
   this.age = age;
   this.country = country;
  }
  public String toString(){
   return name+":"+age+"::"+country;
  }
}

 

 

//静态是不能被序列化的,它只能序列化堆内存中的内容不能序列化方法区中的内容。
//如果对非静态的成员也不想序列化,那么可加上一个关键字修饰一个就行了(transient),虽然它在堆内存中。
//它被transient修饰后就不能在被序列化了,保证其值在堆内存中存在,而不在文本文件中存在。
import java.io.*;
class SeralizibleDemo{
 public stattic void main(String args[]){
  //writeObj();
  readObj();
 }
 public static void readObj()throws Exception{
  ObjectInputStream ois = new ObjectInputStream(new FileInputStream("obj.txt"));
  Person p = (Person)ois.readObject();
  System.out.println(p);
  ois.close();
 }
 public static void writeObj()throws IOException{
  ObjectOutputStream oos = new ObjectOutputStream("obj.txt");
  oos.writeObject(new Person("lisi0",399,"kr"));
  oos.close();
 }
}

//RandomAccessFile 该类不算是IO体系中子类,而是直接继承自Object
//但它是IO包中成员,因为它具备读和写功能,内部封装了一个数组
//而且通过指针对数组的元素进行操作。可以通过getFilePointer获取
//指针位置,同时可以通过seek改变指针的位置。
//其实完成读写的原理就是内部封装了字节输入流和输出流。
//通过构造函数可以看出,该类只能操作文件,而且操作文件还有模式:
//只读r,读写rw等,而且该对象的构造函数要操作的文件不存在,会自动
//创建,如果不存在会不覆盖。
//如果模式为只读r,不会创建文件,会去读取一个已存在的文件,如果该
//文件不存在,则会出现异常,如果模式为rw。操作的文件不存在会自动创建,
//如果存在则不会覆盖。
//数据要分段,要有规律。

 

import java.io.*;
class SeralizibleDemo{
 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.write("haha".getBytes());//此模式会拒绝访问。
  //调整对象中的指针,存入时,应有规律。
  //raf.seek(8*0);//整倍的跳过指定的数。
  raf.skipBytes(8);//它不能往前跳
  //raf.skipBytes(4);
  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*3);
  raf.write("周期".getBytes());
  raf.write(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();
 }
}

 

 

//可以用于操作基本数据类型的数据流。
//记事本用的是GBK编码,它会拿两个字节去查表,查完后把里的字符显示出来。
import java.io.*;
class SeralizibleDemo{
 public static void main(String args[])throws IOException{
  //writeDate();
  //readData();
  //writeUTFDemo();
  //outputStreamWriter();
  readUTFDemo();
 }
 public static void outputStreamWriter()throws IOException{
  OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"gbk");
  osw.write("付玉光,你好");
  osw.close();
 }
 public static void readUTFDemo()throws IOException{
  DataInputStream dis = new DataInputStream(new FileInputStream("utfdate.txt"));
  String s  =dis.readUTF();
  System.out.println(s);
  dis.close();
 }
 public static void writeUTFDemo()throws IOException{
  DataOutputStream dos = new DataOutputStream(new FileOutputStream("utfdate.txt"));
  dos.writeUTF("付玉光,你好。");
  dos.close();
 }
 public static void writeDate()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 = n ew DataInputStream(new FileInputStream("data.txt"));
  int num = dis.readInt();
  boolean b = dis.readBoolean();
  double d = dis.readDouble();
  System.out.println(num+"....."+b+"....."+d);
 }
}

 

//用于操作字节数组的流对象。
//ByteArrayInputStream:在构造的时候需要接收数据源,而且数据是一个字节数组。
//ByteArrayOutputStream:在构造的时候,不用定义数据目的,因为该对象内部已经
//封装了可变长度的字节数组。这就是数据目的地。
//因为这两个流对象都操作数组,并没有使用系统资源。所以不用进行close()关闭。

//此类实现了一个输出流,其中的数据被写入一个 byte 数组。缓冲区会随着数据的不断写入而自动增长。可使用 toByteArray() 和 toString() 获取数据。
//关闭 ByteArrayOutputStream 无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何 IOException。


//ByteArrayInputStream和ByteArrayOutputStream:源和目的都是内存。!!!
//在流操作规律讲解时:
源设备,
 键盘:System.in 硬盘:FileStream, 内存ArrayStream
目的设备:
 控制台:System.out  硬盘:FileStream 内存:ArrayStream

 

//该方法没有调用到系统底层资源,所有没有IOException;
import java.io.*;
class SeralizibleDemo{
 public static void main(String args[])throws IOException{
  //数据源
  //ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEF".getBytes());
  //FileInputStream bis = new FileInputStream("SeralizibleDemo.java");
  //InputStreamReader bis = new InputStreamReader(new FileInputStream("SeralizibleDemo.java"));//有异常,因为用的是字符数组。
  BufferedInputStream bis = new BufferedInputStream(new FileInputStream("SeralizibleDemo.java"));
  //数据目的
  ByteArrayOutputStream bos = new ByteArrayOutputStream();
  int by = 0;
  //byte[] b = new byte[1024];//不用在定义数组了。
  //while((by=bis.read(b))!=-1){//此处代码产生的乱码问题。
  // bos.write(b,0,by);
  //}
  while((by=bis.read())!=-1){
   bos.write(by);
  }
  System.out.println(bos.size());
  System.out.println(bos.toString());
  //bos.writeTo(new FileOutputStream("a.txt"));//该方法有异常。一次性的写出去。@!!!
 } 
}


//字符流的出现为了方便操作字符,更重要的是加入了编码转换
//通过子类转换流来完成,InputStreamReader  OutputStreamWriter 
//在两个对象进行构造的时候可以加入字符集。
//PrintStream和PrintReader也可以加入编码表。但它们只能去打印,而不能去读取。
计算机只能识别二进制数据,早期是电信号。
为了方便应用计算机,让它可以识别各个国家的文字。就将各个
国 家的文字用数字来表示,并一一对应,形成一张表,这就是编码表。

ASCII:美国标准信息交换码,用一个字节的7位可以表示。
ISO8859-1:拉丁码表,欧洲码表。用一个字节的8位表示。
GB2312:中国的中文编码表。中国的码表兼容ASCII码。两个字节的高位都是1 。6~7k
GBK:中国的文件编码表。二万多。
Unicode:国际标准码,整合了多种文字,所有文字都用字节来表示,java语言使用的就是unicode.
  空间占用浪费。
UTF-8:最多用三个字节来表示一个字符。 它和GBK都识别中文,
  可是同一个中文文字却在两张码表中对应的数字不同。由此就产生了乱码,需要编码转换。
转换流的编码应用:
 *可以将字符以指定编码格式存储。
 *可以对文本数据指定编码格式来解读。
 *指定编码表的动作由构造函数完成。


硬盘里存储的是数字,但用文档打开后,显示的不是数字,
记事本是一个文本编辑器,当读取数据时,按指定的编码表
显示。

import java.io.*;
class SeralizibleDemo{
 public static void main(String args[])throws IOException{
  //writeText();
  readText();
 }
 public static void writeText()throws IOException{
  OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("gbk.txt"),"GBK");//;默认编码为GBK。
  osw.write("你好");
  osw.close();
 }
 public static void readText()throws IOException{
  //InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"utf-8");
  InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"gbk");
  char[] c = new char[10];
  int len = isr.read(c);
  String str = new String(c,0,len);
  System.out.println(str);
  isr.close();
 }
}

 


//编码,解码就是对数据的转换。
//编码:字符串变成字节数据。
//解码:字节数据变成字符串。
String-->byte[]; str.getBytes();//使用默认的GBK编码。
byte[] -->String: new String(byte[]);


String(byte[] bytes)
          通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。

String(byte[] bytes, String charsetName)
    通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String。

 byte[] getBytes()
          使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
 byte[] getBytes(Charset charset)
          使用给定的 charset 将此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。

 

Arrays 中
 static String toString(byte[] a)
          返回指定数组内容的字符串表示形式。
数组变字符串。

把文字数据存入硬盘是编码。变成数组是在编码。
用记事本打开硬盘中的文件是解码。

//编错了,就不要在解了!!!!,
//当编对了,解错了,,那么在用相应的码表编码后,在解码就行了。。


//当编码时,用ISO8859-1编码,由于该码表不识别中文,用该码表
//解码时,是解不出原有汉字的。(编码时,编错了,是解不了的)。
import java.util.*;
class SeralizibleDemo{
 public static void main(String args[])throws Exception{
  method_4();
 }
 public static void method_1(){
  String s = "你好";
  byte[] b = s.getBytes();//编码
  for(byte bb : b)
   System.out.println(bb);
  System.out.println(Arrays.toString(b));
 }
 public static void method_2()throws Exception{
  String s = "你好";
  //byte[] b = s.getBytes("GBK");//按指定的码表编码
  byte[] b = s.getBytes("utf-8");//按指定的码表编码
  System.out.println(Arrays.toString(b));
  String s1 = new String(b,"GBK");//解码。
   System.out.println("s1:"+s1);
 }
 //tomcat默认编码是ISO8859-1;
 //是get提交的方式,必须用这种方式,如果是post提交,可用一个方法setcharcingencoding(),设置解码方式。
 public static void method_3()throws Exception{
  String s = "你好";
  byte[] b1 = s.getBytes("GBK");//按指定的码表编码
  System.out.println(Arrays.toString(b1));
  String s1 = new String(b1,"ISO8859-1");//解码。
  System.out.println("s1:"+s1);
  //对s1进行iso8859-1编码。
  byte[] b2 = s1.getBytes("iso8859-1");//对s1再进行编码
  System.out.println(Arrays.toString(b2));
  String s2 = new String(b2,"GBK");//对s2再进行解码。
  System.out.println("s2="+s2);
 }
 //此处需要特别注意!!!!!
 //以下方式是不行的。
 public static void method_4()throws Exception{
  String s = "你好";
  byte[] b1 = s.getBytes("GBK");//按指定的码表编码
  System.out.println(Arrays.toString(b1));
  String s1 = new String(b1,"utf-8");//解码。
  System.out.println("s1:"+s1);
  //对s1进行iso8859-1编码。
  byte[] b2 = s1.getBytes("utf-8");//对s1再进行编码
  System.out.println(Arrays.toString(b2));
  String s2 = new String(b2,"GBK");//对s2再进行解码。
  System.out.println("s2="+s2);
 }
}


用FileReader()方法读两个字节查表一次,返回一个中文。
U8码表对字节都加了一个标识头信息,按着标识头就能知道
连续读几个字节。
而用字节流读取

u8有自己的头特征码。
//联通之两个字,它的二进制编码的形式,正好符合了U8的编码形式。

import java.util.*;
class SeralizibleDemo{
 public static void main(String args[])throws Exception{
  String s = "联通";
  byte[] by = s.getBytes("gbk");
  for(byte b : by)
   System.out.println(Integer.toBinaryString(b&255));
 }
}*/


import java.io.*;
class SeralizibleDemo{
 public static void main(String args[])throws Exception{
 
 }
 public static void method_4()throws Exception{
  String s = "联通";
  byte[] b1 = s.getBytes("GBK");//按指定的码表编码
  System.out.println(Arrays.toString(b1));
  String s1 = new String(b1,"utf-8");//解码。
  System.out.println("s1:"+s1);
  //对s1进行iso8859-1编码。
  byte[] b2 = s1.getBytes("utf-8");//对s1再进行编码
  System.out.println(Arrays.toString(b2));
  String s2 = new String(b2,"GBK");//对s2再进行解码。
  System.out.println("s2="+s2);
 }

}

 

原创粉丝点击