字符流,其他流,编码解码和装饰模式

来源:互联网 发布:我是大美人淘宝官方店 编辑:程序博客网 时间:2024/05/21 09:51


字符流的抽象基类:
 Reader , Writer。
 由这些类派生出来的子类名称都是以其父类名作为子类名的后缀,如FileReader、FileWriter。

Reader类:
 void close()    关闭该流并释放与之关联的所有资源。
 int read()     读取单个字符。
 int read(char[] cbuf)     将字符读入数组。
 int read(char[] cbuf, int off, int len) 将字符读入数组的某一部分。
 说明:
  在执行read操作时,如果已到达流的末尾,则返回-1。
  
 

Writer类:
 void close()   关闭此流,但要先刷新它。
 void flush()   刷新该流的缓冲。
 void write(int c)  写入单个字符。
 void write(char[] cbuf) 写入字符数组。
 void write(String str) 写入字符串。
 void write(char[] cbuf, int off, int len) 写入字符数组的某一部分。
 void write(String str, int off, int len) 写入字符串的某一部分。

 

操作文件的字符流:
 FileReader、FileWriter。
 读写文件时采取的是平台默认编码(视操作系统而定)。

 

 


使用字符流拷贝文件

 public static void copyFile(String path1, String path2) throws Exception {
  Reader reader = new FileReader(path1);
  Writer writer = new FileWriter(path2);

  int ch = -1;
  while ((ch = reader.read()) != -1) {
   writer.write(ch);
  }

  reader.close();
  writer.close();
 }

 


使用字符流拷贝文件,有完善的异常处理

 public static void copyFile2(String path1, String path2) {
  Reader reader = null;
  Writer writer = null;
  try {
   // 打开流
   reader = new FileReader(path1);
   writer = new FileWriter(path2);

   // 进行拷贝
   int ch = -1;
   while ((ch = reader.read()) != -1) {
    writer.write(ch);
   }
  } catch (Exception e) {
   throw new RuntimeException(e);
  } finally {
   // 关闭流,注意一定要能执行到close()方法,所以都要放到finally代码块中
   try {
    if (reader != null) {
     reader.close();
    }
   } catch (Exception e) {
    throw new RuntimeException(e);
   } finally {
    try {
     if (writer != null) {
      writer.close();
     }
    } catch (Exception e) {
     throw new RuntimeException(e);
    }
   }
  }
 }
}

 

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

 

常见的编码表
 ASCII:  美国标准信息交换码。用一个字节的7位可以表示。
 ISO8859-1: 拉丁码表。欧洲码表,用一个字节的8位表示。又称Latin-1(拉丁编码)或“西欧语言”。
     ASCII码是包含的仅仅是英文字母,并且没有完全占满256个编码位置,
     所以它以ASCII为基础,在空置的0xA0-0xFF的范围内,加入192个字母及符号,
     藉以供使用变音符号的拉丁字母语言使用。从而支持德文,法文等。
     因而它依然是一个单字节编码,只是比ASCII更全面。
 GB2312: 中国的中文编码表。
 GBK:  中国的中文编码表升级,融合了更多的中文文字符号。
 Unicode: 国际标准码,融合了多种文字。所有文字都用两个字节来表示,Java语言使用的就是unicode。
 UTF-8:  最多用三个字节来表示一个字符。
 ......
 (接触最多的是iso8859-1、gbk、utf-8)

 

编码与解码:
 编码:字符串 -->?字节数组,使用String中的getBytes(String encoding)实现。
 解码:字节数组  -->?字符串,使用new String(byte[] buf, String encoding)实现。

 

乱码:
 编码和解码的码表必须一致。
 当不一致时,就很有可能出理乱码问题。
 开发者在项目中应该使用统一的编码方式,推荐采用UTF-8编码。

 

Java的编码原则:
 Java语言底层采用Unicode编码,处理字符通常有三个步骤:
 按照指定的编码形式从源设备中读取数据       文件设备----》内存设备
 以Unicode码形式将数据存储在内存中
 再将内存中的数据按照指定的编码形式写入到目标设备

 

字节流与编码:
 问题:
  如果使用字节输入流读取任何一个文件中的中文数据。
  直接读取一个就转换为char输出在控制台上,这只是转换了中文字符的高八位或低八位,这会导致乱码。
 解决方案:
  使用字符数组去读取。
  然后进行解码。

 

编码与Windows控制台(了解):
 操作系统上的软件默认是GBK编码。
 如果一个记事本默认存储了”中国”,使用字节数组,直接可以输出。
 创建字符串默认也是本地编码GBK,相当与new String(chs,"GBK");
 如果将文件另存为UTF-8,必须显示的new String(chs, "UTF-8");
 但是如果要直接显示在控制台上亦然是乱码,控制台编码是GBK。
 所以需要new String( new String(bs,"UTF-8").getBytes("GBK") ) 。

 

其他:
 在中文的操作系统下ANSI默认是GB2312,默认保存“联通”后再次打开乱码。解决方案:该问题是ANSI的bug,可以另存为其他编码即可。
 InputStream中的字节编码取决文件本身的编码,而OutputStream生成文件的编码取决于字节的编码。
 out.write("我们".getBytes());           // GBK
 out.write("我们".getBytes(“UTF-8”)); // UTF-8

编码操作与解码操作。
 编码操作:字符串 -->?字节数组,使用String中的getBytes(String encoding)实现。
 解码操作:字节数组  -->?字符串,使用new String(byte[] buf, String encoding)实现。
 注意:
  字符集就是编码表
  用什么字符集进行编码,就一定要再用什么字符集进行解码,否则,就很有可能出现乱码。 
  (兼容的字符集之间混用是不会出现乱码的,如gb2312与gbk)


默认编码
 在使用str.getBytes()或是FileReader或是FileWriter等且没有指定编码时,会使用系统默认的编码。
 系统默认的编码在Windows上一般为GBK。
 在Java程序中可以使用System.getProperty("file.encoding")方式得到当前的默认编码。
 可以使用以下方法改变这个环境参数值:
  java -Dfile.encoding=utf-8 Testxx

 


编码和解码操作
public class IoTest4_Encoding {

 // 中
 // UTF-8:E4B8AD
 // GBK:D6D0
 
 // 编码为系统默认的编码,在Windows上一般为GBK
 // 在Java程序中可以使用System.getProperty("file.encoding")方式得到当前的默认编码
 
 // 字符集就是编码表
 // 用什么字符集进行编码,就一定要再用什么字符集进行解码,否则,就很有可能出现乱码。
 
 // 编码操作与解码操作。
 public static void main(String[] args) throws Exception {
  String value = System.getProperty("file.encoding");
  System.out.println("系统默认的编码为 " + value);

  String str = "中";

  // 编码操作
  byte[] bytes = str.getBytes();
  byte[] bytes2 = str.getBytes("gbk");// d6d0
  byte[] bytes3 = str.getBytes("utf-8");// e4b8ad

  // for (int i = 0; i < bytes3.length; i++) {
  // System.out.println(bytes3[i]);
  // }

  // 解码操作
  String str2 = new String(bytes2, "utf-8");
  System.out.println(str2);

  str2 = new String(bytes3, "gbk");
  System.out.println(str2);
  
  str = new String("中国".getBytes("gb2312"), "gbk");
  System.out.println(str);
 }
}

 

转换流
 作用:
  把字节流转为字符流:InputStreamReader、OutputStreamWriter
 详细:
  InputStreamReader = InputStream + 字符集
   使用字节流读取二进制的文件内容,并自动进行解码,然后返回字符。
  OutputStreamWriter = OutputStream + 字符集
   把字符串自动的进行编码,然后把编码后的二进制数据通过字节流写出去。


 当多个流进行包装使用时,最终只需要关闭最外面的那个流就可以了。


缓冲流
       为了提高java对文件的操作效率,那么读写文件可以先读取到缓冲区中便于后面一次性操作。
特点:
 继承自字符流
 缓冲区要结合流才可以使用。
 在流的基础上对流的功能进行了增强。
原理:
 缓冲流中都内部维护了一个私有字符流对原有功能进行了增强
 缓冲流中都内部维护了一个缓冲数组
 当数组符合条件了,在将数组中的数据一次性操作(写、读)
  private char cb[];
   private Reader in;
    public BufferedReader(Reader in, int sz) {
        super(in);
        if (sz <= 0)
            throw new IllegalArgumentException("Buffer size <= 0");
        this.in = in;
        cb = new char[sz];
        nextChar = nChars = 0;
     }


装饰器模式:
 使用分层对象来动态透明的向单个对象中添加责任(功能)。
 装饰器指定包装在最初的对象周围的所有对象都具有相同的基本接口。
 某些对象是可装饰的,可以通过将其他类包装在这个可装饰对象的四周,来将功能分层。
 装饰器必须具有和他所装饰的对象相同的接口。

 

JavaIO中的应用:
 Java I/O类库需要多种不同的功能组合,所以使用了装饰器模式。
 FilterXxx类是JavaIO提供的装饰器基类,即我们要想实现一个新的装饰器,就要继承这些类。

 

装饰器与继承:
 问题:
  修饰模式做的增强功能按照继承的特点也是可以实现的,为什么还要提出修饰设计模式呢?
  继承实现的增强类和修饰模式实现的增强类有何区别?
 继承实现的增强类:
  优点:代码结构清晰,而且实现简单
  缺点:对于每一个的需要增强的类都要创建具体的子类来帮助其增强,这样会导致继承体系过于庞大。
 修饰模式实现的增强类:
  优点:内部可以通过多态技术对多个需要增强的类进行增强
  缺点:需要内部通过多态技术维护需要增强的类的实例。进而使得代码稍微复杂。

缓冲流使用了修饰模式,源代码:
  private int read1(char[] cbuf, int off, int len) throws IOException {
        if (nextChar >= nChars) {
            if (len >= cb.length && markedChar <= UNMARKED && !skipLF) {
  //before
  //调用被修饰类的方法
                return in.read(cbuf, off, len);
  //after
            }
            fill();
        }


//模拟修饰模式类

//在读取每一行加上双引号的类
public class MyQuotBufferReader extends BufferedReader{
 private BufferedReader br;
 public  MyQuotBufferReader(BufferedReader br){
  super(br);
  this.br = br;
 }
 public String readLine()throws IOException{
  String line = br.readLine();
  if(line==null){
   return null;
  }
  return "“"+line+"”";
 }
 
}

//在读取每一行时加上行号的类
public class MyLineNumBufferedReader2 extends BufferedReader {

 private int lineNum = 0;

 // 记下了这个bufferReader
 private BufferedReader bufferReader;

 public MyLineNumBufferedReader2(BufferedReader bufferReader) {
  super(bufferReader);
   // 记下了这个bufferReader
  this.bufferReader = bufferReader;
 }

 public String readLine() throws IOException {
   // 调用自己记住的那个BufferedReader进行读一行的操作
  String line = bufferReader.readLine();
  if (line == null) {
   return null;
  } else {
   lineNum++;
   return lineNum + ":" + line;
  }
 }
}


 

原创粉丝点击