关于张老师java视频学习(十五)

2011-01-18

 

Java高级视频:

输入与输出I/O :

一、知识点:

 (8)、字符编码的操作体验:
  1、查看中文字符的GB2312码、UTF-8码、Unicode码:在Windows记事本程序中用不同的编码格式存储文本文件。
     UltraEdit编码操作体验:
  2、字符编码的一个奇怪现象:
     · 微软与联想的关系不错,但是与联通则有过节:用Windows记事本程序创建三个文件,其中分别输入“联通”、“联想”、“联”,然后打开这
       三个文件,内容为“联通”和“联”的文件显示异常。
       原因:在文本文件没有前面三个字节的编码标志的时候,应用软件会检查所有的字符编码,看其整体属于某种字符编码,然后按该编码规则
       解码。而“联想”属于本地编码规则,“联通”与“联”则不是UTF-8编码,故在显示的时候不能正确的显示。这是编码规则的一个BUG

 (9)、字符编码的操作体验:
  1、getBytes()方法:将Unicode字符串转换为实参指定的字符集,如果不指定实参,则为默认的本地字符集,并放回到字节数组中存储。
     该方法首先找到实参的字符编码器类,然后进行编码转换。 Unicode码转换为某种字符集的转换称为编码。
  2、在有些编译器总,for循环条件控制量如果在小括号内部声明,并不把它当做局部变量,即小括号外也有效。但是在JVM中,这种定义的控制
     变量只是局部的。
  3、byte整数-1在转换成int整数类型的时候,byte类-1为8位:1111 1111 = -1 ,而int型整数的-1为:1111 1111 1111 1111 = -1 ,故在由byte型
     -1转换成int型时,用十六进制表示则为:ffff。负数的转换也是一样。
  4、System.out是PrintStream流类,当调用该对象的println()函数的时候,将自动调用flush()函数,将缓冲区的数据刷新到屏幕上。
     该类有一个方法:System.out.println(String str),该方法先将传递给方法的GB2312实参字符串转换为字节数组,然后调用write方法,写入到
     流PrintStream中。当调用System.out.write()方法的时候,如果写的是字节,则不会自动调用flush刷新;如果写入的是字节数组,则会自动刷新。
  5、如何查看当前系统所使用的字符编码集: 该字符编码集作为一个JVM的环境而返回。
     System.getProperties().list(System.out); //该语句将JVM环境在屏幕中输出。
  6、在Eclipse中,如何修改编译所用到的运行环境中的字符集?
     System.setProperty("file.encoding","字符集名"), 该语句将设置实参指定的字符集为本地字符集。
     但是,为何在Eclipse中,通过该语句运行得到的结果,本地字符集并未发生改变。
     原因如何,有待追究。但是,要改变Eclipse的运行默认字符集,可以通过配置其运行环境:Run as --> Run configuration ,进入环境配置菜单
     的Common选项,将 Encoding 设置为ios8859-1即可,这样便是在该字符集下运行。
  7、在实际中,toHexString()方法显示的字符中,如果高字节为0,则不显示高字节。

 (10)、过滤流与包装类:
  1、包装类的概念与作用:
     (1)、通过FileOutputStream类直接将浮点数写入到文件中是不可能的,因为该类中的写方法只能写字节或者字节数组,只能将浮点数转换为字节数
   组,然后才能通过类FileOutputStream来写入文件。 将浮点数转换为字节数组,则使用floatToIntBits(),该方法返回浮点数在内存中的存储
   形式。那么,为了更方便,我们定义一个数据写入类DataOutputStream,该类往各种输出流对象中写入各种类型的数据(包括浮点小数),那么
   我们所需要做的就是:传递一个FileOutputStream对象给DataOutputStream对象,并调用它用于写浮点数的方法。
     (2)、DataOutputStream并没有对应到任何具体的流设备,一定要给它传递一个对应具体设备的输出流对象,完成类似DataOutputStream功能的类就是
   一个包装类,也叫过滤流类或处理流类。(特点是:没有对应到具体的设备流),它对原始流进行了包装,使得程序员使用的更加方便,包装类
   也可以被包装类包装。
     (3)、DataOutputStream包装类的构造函数语法: public DataOutputStream(OutputStream out),形参为OutputStream类型。
     (4)、包装流类与节点流类、目的文件、应用程序间的关系:
   程序 ---(调用)---> 包装流类(方法)---(写入)---> 节点流类(方法) ---(写入)---> 目标。

  2、BufferedInputStream与BufferedOutputStream类: 对I/O缓冲
     (1)、缓冲流为I/O增加了内存缓冲区,增加缓冲区的目的:
   · 允许Java程序一次不只操作一个字节,提高了程序的性能。
   · 有了缓冲区,使得在流上执行skip、mark和reset方法都成为可能(对缓冲区内容进行处理)。
     (2)、BufferedInputStream与BufferedOutputStream类是Java提供的两个缓冲包装类,不管底层系统是否使用了缓冲区,这两个类在自己的实例对象中
   创建缓冲区。这种缓冲区称为间接缓冲区,与底层系统提供的缓冲区的区别:
   · 底层系统创建的缓冲区直接与目标设备交换数据
   · 包装类中创建的缓冲区,需要调用包装类所包装的类的输出流对象,将缓冲区的数据读写到目标设备或者底层缓冲区中。
   · 两个构造函数:BufferedInputStream(InputStream in),创建一个具有32个字节的缓冲区
     BufferedInputStream(InputStream in, int size),按照size创建缓冲区。size大小应为硬盘或者其他设备一次所能读取的字节
           的整数倍。
   · 两个构造函数:BufferedOutputStream(OutputStream out):其本身的fulsh()方法不会写入底层输出设备,而是通过OutputStream流类的flush()
          方法来写入底层输出设备。创建512字节的输出缓冲区。
     BufferedOutputStream(OutputStream out, size):利用实参size创建输出缓冲区。
     (3)、BufferReader和BufferWriter类:用于对字符流类进行缓冲区包装。
   BufferReader的readLine()方法可以一次读取一行文本,BufferWriter类的newLine()方法可以向字符流中写入不同操作系统下的换行符。  

 (11)、3、DataInputStream与DataOutputStream类:
     (1)、DataOutputStream类提供的三个写字符串的方法:
   · public static void writeBytes(String s):一次写入字符串的一个字节
   · public static void writeChars(String s):一次写入字符串的两个字节
   · public static void writeUTF(String str):以UTF编码写入到目标设备,带有标志字头(开始两个字节,有长度)。可以在readUTF来读取。
   但是,DataInputStream类中,没有提供readBytes()和readChars()来读取字符串,这是因为:
   要在一个连续的字节流中读取一个字符串,这个字符串只是该流的一个段内容,如没有特殊的标记作为一个字符串的结尾,而且事先也不知道该
   字符串的长度,那么无法知道该从哪里读取哪里结束读取该字符串。由于writeUTF()方法向目标设备中写入了字符串的长度,故可以使用readUTF()
   方法来读取该字符串。
     (2)、编程实例:分别使用DataOutputStream类的writeUTF、writeBytes和writeChars方法,比较这几个方法的差异。

  4、PrintStream类:提供了一系列的print和println方法,可以将基本数据类型的数据格式化成字符串输出。
     (1)、对于基本类型数据,这两方法将基本数据类型转换成字符串,而不是输出原始的字节内容。而对于输出内容为非基本类型的对象,将首先调用该对
   象重载的toString()方法,返回该方法返回的字符串。
     (2)、格式化输出:即将一个数据按照字符串的格式输出。如将数据97输出,即将‘9’和‘7’这两个数字以ASCII码写入文件中,他们的ASCII码分别为
   0x39和0x37,他们在记事本中显示的效果就是'9'和 '7'。在内存中表示为:0011 1001 和 0011 0111 ,数字在ASCII码中的显示为 0011 数字(4位)
     (3)、PrintStream类的3个构造函数:
   · PrintStream(OutputStream out):该构造函数将不自动刷新,即不将实参写入out流中。
   · PrintStream(OutputStream out, boolean autoflush):
   · PrintStream(OutputStream out, boolean autoflush, String encoding):
     (4)、PrintWriter类,即使遇到了文本换行标识符(/n),PrintWriter类也不会自动清空缓冲区。只有设置了自动刷新缓冲区、调用了println()方法,
   才会刷新缓冲区。即在PrintWriter类中,print("/n")并不等于println()方法。PrintWriter的println()方法,能根据操作系统的不同而生成
   相应的文本换行标识符。在Windows下的文本换行标识符为"/r /n",而在Linux下则为“/n”。
  

 (12)、5、ObjectInputStream与ObjectOutputStream类:这两个类主要用于从底层流中读写对象类型的数据。  P407
     (1)、这两个包装流类所操作到的对象,必须实现了接口Serializable(可序列化)。可以自动读写对象中的成员变量,除了transient(临时的)和
   static(全局的)成员变量,这两个类型成员变量不会被读写。接口Serializable中并未定义任何方法,仅仅作为一种标记。
     (2)、这种流类所包装的序列化对象,能够应对不同的操作系统的差异。
   实例:创建一个可序列化的学生对象,并用ObjectOutputStream类把它存储到一个文件(mytext.txt)中,然后再用ObjectOutputStream类把存储
   的数据读取到一个学生对象中,即恢复保存的学生对象。
     (3)、使用ObjectOutput类写入文件的数据,只有ObjectInput类对象的readObject()对象才能理解文件里数据格式。
   要理解这种文件数据,必须事先知道这种数据的格式,然后才能按有效信息读取。   

 (13)、6、字符流与字节流的转换:
     (1)、如何找到一种简单的方式来读取键盘上输入的一行字符:readLine()
   该方法属于BufferedReader类,而给类的构造方法中只接受Reader的类型,而键盘对应的是System.in,是InputStream类型的,那么就需要另外
   一个Reader的包装类来包装System.in对象,就是InputStreamReader类。如:
      BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
     (2)、InputStreamReader和OutputStreamWriter类:是用于字节流转换成字符流来读写的两个类,InputStreamReader可将一个字节流中的字节解码成
   字符读取,OutputStreamWriter将字符编码成字节后写入一个字节流中。
     (3)、InputStreamReader的两个主要构造函数:
   · InputStreamReader(InputStream in):
   · InputStreamReader(InputStream in, String CharsetName):转换时所需要的字符集。
     (4)、OutputStreamReader的两个主要构造函数:
   · OutputStreamReader(OutputStream out):
   · OutputStreamReader(OutputStream out, String CharsetName):转换时所需要的字符集。
     (5)、在实际应用中,避免频繁的在字符与字节中进行转换。最好不要直接使用InputStreamReader和OutputStreamWriter。
   应该使用BufferStreamReader类包装InputStreamReader,使用BufferStreamWriter包装OutputStreamWriter类。
   因为:InputStreamReader和OutputStreamWriter用于实现字符间的转换,如果读到一个字节,就将之转换,这样当需要读取的字符较多的时候,则
   需要频繁的转换,这样效率就会比较低。而使用BufferStreamWriter和BufferStreamReader,则是读取一定数量的字节或者字符时,才进行转换。
   这样提高了效率。
     (6)、实际上,所有的字符流都是包装类。

 (14)、Java程序与其他进程的数据通信:(线程、管道流、输入输出流)
     (1)、在Java程序中,可以通过Process类的实例对象来表示并启动子进程,子进程的标准输入输出不在连接到键盘和显示器,而是以管道流的形式连接到
   父进程(JVM)的一个输出流和输入流对象上。
     (2)、调用Process类对象的getOutputStream和 getInputStream方法可以获得连接到子进程的输出流和输入流对象。
   子进程从标准输入读到的对象,就是从父进程对应子进程的输出流中读取的对象。子进程向标准输出写的对象,就是父进程从对应子进程的输入流
   读取到的对象。
     (3)、编程实例:在TestInOut类中启动java.exe命令执行另外一个MyTest类,TestInOut类和MyTest通过进程间的管道相互传送数据。
     (4)、为何BufferedReader类的read()方法返回的是字符的编码值,而不是字符本身?
   为何BufferedReader类的readLine()方法返回的是字符串的本身,而不是字符的编码值?
   根据实验:
    1、read()方法返回的是BufferedReader缓冲流的字符串第一个字节的编码值,范围为0~65535。
    2、read()方法返回的是BufferedReader缓冲流的整个字符串本身。
    3、并且在使用这两个方法的时候,都会对流做标记,当下一次从该流中读取数据的时候,就从该标记处开始读取。如果标记到达了
       流末尾,则不再执行该读取操作。如:
       BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));
       System.out.println( bfr.read());
       System.out.println(bfr.readLine());
       那么,结果便是:read()方法读取到System.in的一个字节,换行,然后readLine()方法打印剩余的字节。
       但是,如果这个方法交换,则readLine()方法读取的是整个System.in的字符串,而buf.read()语句将不再执行。
       注:在BufferedReader类对象中,使用read()和readLine()方法,如果多次使用,到达了流的末尾,流中没有数据可读,则该读取
           操作发生堵塞,进程挂起,不执行堵塞语句后面的代码。
     (5)、在进程间的通信中,使用Process类的getOutputStream()和getInputStream()方法,通过管道流,间接的将父进程与子进程连接起来传输数据。而
          这个管道流,拥有一定的缓冲区,getOutputStream方法得到的对象向该管道流中写入数据,而getInputStream方法得到的对象从该管道流中读取数
   据,如果在读取数据的时候,子进程向管道流缓冲区输入数据过块,满了之后还在输送,那么,都将使得后面的数据挤掉前面的数据,从而父进程
   读取的时候发生数据丢失。
     (6)、在编程中,每一个事物都当做一个对象,当我们new一个对象之后,不仅仅创建一个Java的对象,同时也在系统内创建了相对应的资源。如果我们
   使用一个匿名对象,但是并没有关闭该对象,那么系统内资源并不会被释放,这样长此下去,将导致资源的严重不足从而出现问题。
   故,上面的实例中,临时流对象BufferedReader用完之后,并不关闭,从而造成系统中资源未被释放,一开始系统资源还足够分配,不会出问题,
   但是随着循环之后,便开始出现问题,资源分配不足,开始出现数据因排挤而丢失。
     (7)、在上述事例中,如果调用BufferedReader对象的close()方法,将BufferedReader流对象关闭,同时也将底层流System.in关闭,从而导致管道流
   破裂,抛出异常。
     (8)、程序代码放置的位置是非常重要的,特别是循环块和条件语句块中的代码块的变量。调用Process类的destroy()方法结束子进程的运行。
   
     (9)、提高运行程序的效率:
   1、在循环中,尽量把相同的、每次循环所用到一样的东西的定义,放在循环外面,这样就不用每次循环都在循环体内定义该变量。

 (15)、1、字节流输入类:所有的字节输入流的父类都是InputStream类,
         底层流类:   包装类:
      |FileInputStream
      |ObjectInputStream
      |PipedInputStream       |DataInputStream 
   InputStream类<---|SequenceInputStream       |PushbackInputStream
      |FilterInputStream  <--------|BufferedInputStream
      |StringBufferedInputStream   |LineNumberInputStream
      |ByteArrayInputStream     

  2、字节输出流类:所有的输出流类都是OutputStream类的子类
   
          底层流类:   包装类:
      |FileOutputStream
      |ObjectOutputStream
      |PipedOutputStream       |DataOutputStream 
         OutputStream类<---|FilterOutputStream <--------|BufferedOutputStream
      |ByteArrayOutputStream       |PrintStream
         
          
  3、字符输入流类的继承关系:
         底层流类:   包装类:
      |BufferedReader   <----------|LineNumberReader (读取输入流中一行一行的数据并放回行号)
      |CharArrayReader (处理字符数组) 
      |StringReader  (处理字符串)
      Reader类 <----|InputStreamReader  <--------|FileReader
      |PipedReader
      |FilterReader  <-------------|PushbackReader (将读出的字符退回到输入流中)

  4、字符输出流类的继承关系:

         底层流类:   包装类:
      |BufferedWriter 
      |CharArrayWriter (处理字符数组) 
      |StringWriter  (处理字符串)
      Writer类 <----|OutputStreamWriter  <--------|FileWriter
      |PrintWriter
      |PipedWriter
      |FilterWriter 

  5、Decorator(装饰、包装)设计模式:在程序中用一个对象(The Decorators)包装另外一个对象,这是一种被称为Decorator的设计模式。
     (1)、可见,包装类提供了一个更高层次的类的应用。
     (2)、若要设计自己的I/O包装类,这个类需要继承以FilterXXX命名,如:设计一个对输入输出包装的类:RecordInputStream和RecordOutputStream
          来完成从数据库文件中读取记录和往数据库文件中写入记录。 一般所设计的输入输出包装类,都是成对出现的,因为有输出,必须要有对应的
   输入才能读取该输出类数据。
     (3)、包装类的设计与应用很巧妙:如
   Exception类从Throwable类继承的三个printStackTrace方法的定义如下:
      · public void printStackTrace()
      · public void printStackTrace(PrintStream s)
      · public void printStackTrace(PrintWriter s)
   那么,如何将printStackTrace方法打出的详细异常信息存储在一个字符串中?
   字符串与输出流的桥梁:StringWriter
  6、思考与实践:
     (1)、编写一个程序,将一个目录及其子目录下的所有txt类的文本文件中的内容合并到若干个新的文本文件中,当第一个新产生的文件中存储的内容
   达到1Mbytes是,剩下的内容存储到第二个新的文件中,依次往下,新产生的文本文件名依次为1.txt、2.txt、…………
     (2)、用自己的话叙述什么是流,什么是节点流,什么是包装类流。
     (3)、编写一个函数,把StringReader输入流中所有英文字母编程大写字母,并将结果写入到一个StringWriter输出流对象中。然后,用这个函数将
   一个字符串中所有的字符转换成大写。
     (4)、用记事本程序创建一个内容只有"中国"这两个字符的文本文件,然后用不同的编码格式分别保存这个文本文件。用UltralEdit打开这些不同的
   编码格式的文件,并用十六进制方式查看他们的内容,结合你所看到的内容来叙述各种编码之间的差异。
     (5)、编写下述程序代码,分析和观察程序的运行结果:
   import java.io.*;
   public class InputReader{
    public static void main(String[] args) throw Exception{
     InputStreamReader isr = new
      InputStreamReader(System.in, "iso8859-1");
     BufferedReader br = new BufferedReader(isr);
     String strLine = br.readLine();
     for(int i = 0; i<strLine.length(); i++){
      System.out.println(
       Integer.toHexString((int)strLine.charAt(i)));
     }
     isr.close();
     System.out.println(strLine);
    }
   }
   输入“中国”后,运行结果如下:
   d6
   d0
   b9
   fa
   ???u
   请按下面的两种要求修改上面的程序代码,让程序能正常的打印出输入的中文字符:
    1、修改程序中的语句:
     InputStreamReader isr = new
       InputStreamReader(System.in, "iso8859-1");
    2、不修改上面的语句,而是修改下面的语句:
     System.out.println(strLine);


二、问题与收获:
 
 (1)、在上述视频(9)中第六点,疑惑的是,通过setProperty()方法设置的字符集,在getProperties()方法,能显示是出file.encoding已经修改为iso8859-1,
        为何在Console中显示的时候,仍然是采用GBK字符集作为运行环境的字符集。

 (2)、某些工具中如JCreator,在某些块中,不允许定义变量,如switch语句块。
        在变量的定义中,尽量不放在循环块中,因为这样会使得每循环一次,变量就重新定义一次。

 (3)、注:在BufferedReader类对象中,使用read()和readLine()方法,如果多次使用,到达了流的末尾,流中没有数据可读,则该读取
     操作发生堵塞,进程挂起,不执行堵塞语句后面的代码。
 
 (4)、Java对缓冲区满是如何处理的:、缓冲区已满,是将向缓冲区输入数据的进程阻塞,还是继续向缓冲区输送数据并将前面的数据排挤掉,还是抛出异常??
  1、新的数据写入,将最前面写入的数据排挤出去,从而发生数据丢失:
  2、与PipedInputStream相连的PipedOutputStream无法再写入新的数据,PipedOutputStream.write()方法处于阻塞状态:
  3、与PipedInputStream相连的PipedOutputStream无法再写入新的数据,PipedOutputStream.write()方法抛出异常:
  经验证:该管道流write方法不抛出异常,当缓冲区写满之后,管道流write方法发生堵塞,缓冲区的大小因计算机而异。
 (5)、像JCreator、JBuilder和Eclipse等IDE开发工具,程序本身就是一个父进程,然后从父进程中调用子进程Java程序,这样,子进程的输入输出就对应着父
  进程(即IDE开发工具),即IDE开发工具调用子进程的输出并显示在IDE工具中,IDE工具调用子进程的出入并从IDE工具从Console控制台向子进程输入
  数据。

你可能感兴趣的:(关于张老师java视频学习(十五))