分享

Java笔记(七 I/O操作与字节流)

 依笑乘風涼 2007-05-09

Java笔记(七 I/O操作与字节流)

 Sun公司提供的Java源代码在安装目录下的scr压缩包中\java下
 
java.io包中
  File类An abstract representation of file and directory pathnames.
    一个File类的对象,表示了磁盘上的文件或目录
    File类提供了与平台无关的方法来对磁盘上的文件或目录进行操作
 
import java.io.*;
class FileTest
{
    public static void main(String[] args) throws Exception
    {
        /*相对路径
        File f=new File("1.txt");
        f.createNewFile();//boolean createNewFile():Atomically creates a new, empty file named by this abstract pathname if and only if a file with this name does not yet exist.
        //f.mkdir();//boolean mkdir():Creates the directory named by this abstract pathname.在当前目录下
        */
       
        /*Windows平台下
        File f=new File("D:\\JAVA\\L6\\1.txt");
        f.createNewFile();
        */
       
        File fDir=new File(File.separator);//static String separator:The system-dependent default name-separator character, represented as a string for convenience. 这里fDir表示当前程序所在根目录
        String strFile="JavaLesson"+File.separator+"Lesson07"+File.separator+"1.txt";
        File f=new File(fDir,strFile);//File(File parent, String child):Creates a new File instance from a parent abstract pathname and a child pathname string.
       
        //f.createNewFile();
        //f.delete();//boolean delete():Deletes the file or directory denoted by this abstract pathname.
        f.deleteOnExit();//void deleteOnExit():Requests that the file or directory denoted by this abstract pathname be deleted when the virtual machine terminates.
       
        /*
        for(int i=0;i<5;i++)
        {
             File f=File.createTempFile("1",".txt");//static File createTempFile(String prefix, String suffix):Creates an empty file in the default temporary-file directory, using the given prefix and suffix to generate its name.
             f.deleteOnExit();
        }
        */
       
        /*
        File fDir=new File(File.separator);
        String strFile="Java"+File.separator+"L6";
        File f=new File(fDir,strFile);
        String[] names=f.list();
        for(int i=0;i<names.length;i++)
        {
              System.out.println(names[i]); 
        }
        */
       
        File fDir=new File(File.separator);
        String strFile="Java"+File.separator+"L6";
        File f=new File(fDir,strFile);
        String[] names=f.list(new FilenameFilter()// String[] list(FilenameFilter filter):Returns an array of strings naming the files and directories in the directory denoted by this abstract pathname that satisfy the specified filter;Interface FilenameFilter
        {
            public boolean accept(File dir, String name)//boolean accept(File dir, String name):Tests if a specified file should be included in a file list
            {
               return name.indexOf(".java")!=-1;//int indexOf(String str):Returns the index within this string of the first occurrence of the specified substring.  
            }  
        });
        for(int i=0;i<names.length;i++)
        {
              System.out.println(names[i]); 
        }
    }
}

  流式I/O
    流(Stream)是字节的源或目的
    两种基本的流是:输入流(Input Stream)和输入流(Output Stream)。可从中读出一系列字节的对象称为输入流。而能向其中写入一系列字节的对象称为输入流
 
  流的分类:
    节点流:从特定的地方读写的流类,例如:磁盘或一块内存区域
    过滤流:使用节点流作为输入或输出。过滤流是使用一个已经存在的输入流或输出流连接创建的
 
  抽象基类InputStream
    三个基本的读方法
      abstract int read():读取一个字节数据(但返回的是整型,整型是四个字节的,也就是说这个整型中只有第一个字节是有用的),并返回读到的数据,如果返回-1,表示读到了输入流的末尾
      int read(byte[] b):将数据读入一个字节数组,同时返回实际读取的字节数。如果返回-1,表示读到了输入流的末尾
      int read(byte[] b,int off,int len):将数据读入一个字节数组,同时返回实际读取的字节数。如果返回-1,表示读到了输入流的末尾。off指定在数组b中存放数据的起始偏移位置;len指定读取的最大字节数
    其他方法
      long skip(long n):在输入流中跳过n个字节,并返回实际跳过的字节数
      int available():返回在不发生阻塞的情况下,可读取的字节数
      void close():关闭输入流,释放和这个流相关的系统资源
      void mark(int readlimit):在输入流的当前位置放置一个标记,如果读取的字节数多于readlimit设置的值,则流忽略这个标记
  抽象基类OutputStream
    三个基本的写方法
      abstract void write(int b):往输出流中写入一个字节(写入的是整型b的第一个字节)
      void write(byte[] b):往输出流中写入数组b中的所有字节
      void write(byte[] b,int off,int len):往输出流中写入数组b中从偏移量off开始的len个字节的数据
    其他方法
      void flush():刷新输出流,强制缓冲区中的输出字节被写出
      void close():关闭输出流,释放和这个流相关的系统资源
 
  基本的流类:
    FileInputStream和FileOutputStream:节点流,用于从文件中读取或往文件中写入字节流。如果在构造FileOutputStream时,文件已经存在,则覆盖这个文件
    BufferedInputStream和BufferedOutputStream:过滤流,需要使用已经存在的节点流来构造,提供带缓冲的读写,提高了读写的效率(用它们来构造缓冲输入流和缓冲输出流)
    DataInputStream和DataOutputStream:过滤流,需要使用已经存在的节点流来构造,提供了读写Java中的基本数据类型的功能
    PipedInputStream和PipedOutputStream:管道流,用于线程间的通信。一个线程的PipedInputStream对象从另一个线程的PipedOutputStream对象读取输入。要使管道流有用,必须同时构造管道输入流和管道输出流
 
import java.io.*;
class StreamTest
{
   public static void main(String[] args) throws Exception
   {
     /*从屏幕输入,输入什么回车后会直接在屏幕上输出,ctrl+C退出
     int data;
     while((data=System.in.read())!=-1)
     {
       System.out.write(data); 
     }
     */
    
     /*往文件中写入
     FileOutputStream fos=new FileOutputStream("1.txt");//FilterOutputStream(OutputStream out):Creates an output stream filter built on top of the specified underlying output stream
     fos.write("http://lilac0610.spaces.".getBytes());//FileOutputStream的write方法只接受byte和int型 void write(byte[] b):Writes b.length bytes to this output stream;byte[] getBytes():Encodes this String into a sequence of bytes using the platform‘s default charset, storing the result into a new byte array.
     fos.close();//void close():Closes this output stream and releases any system resources associated with the stream.
     */
    
     /*文件输入流读取数据,输出到屏幕上
     FileInputStream fis=new FileInputStream("1.txt");//protected  FilterInputStream(InputStream in):Creates a FilterInputStream by assigning the argument in to the field this.in so as to remember it for later use.
     byte[] buf=new byte[100];
     int len=fis.read(buf);//int read(byte[] b):Reads up to byte.length bytes of data from this input stream into an array of bytes.
     System.out.println(new String(buf,0,len));//void println(String x):Print a String and then terminate the line;String(byte[] bytes, int offset, int length):Constructs a new String by decoding the specified subarray of bytes using the platform‘s default charset.
     fis.close();// void close():Closes this input stream and releases any system resources associated with the stream.
     */
    
     /*BufferedOutputStream缓冲输出流,需要使用已经存在的节点流来构造(从 FilterOutputStream继承)
     FileOutputStream fos=new FileOutputStream("1.txt");
     BufferedOutputStream bos=new BufferedOutputStream(fos);//BufferedOutputStream(OutputStream out):Creates a new buffered output stream to write data to the specified underlying output stream with a default 512-byte buffer size.
     bos.write("http://lilac0610.spaces.".getBytes());//数据写入缓冲区
     bos.flush();//void flush():Flushes this buffered output stream.将缓冲区中数据写入文件
     //bos.close();//也可将缓冲区中数据写入文件,但此时就不能再继续写入数据了
     */
    
     /*BufferedInputStream缓冲输入流
     FileInputStream fis=new FileInputStream("1.txt");
     BufferedInputStream bis=new BufferedInputStream(fis);
     byte[] buf=new byte[100];
     int len=bis.read(buf);
     System.out.println(new String(buf,0,len));//void println(String x):Print a String and then terminate the line;String(byte[] bytes, int offset, int length):Constructs a new String by decoding the specified subarray of bytes using the platform‘s default charset.
     bis.close();
     */
    
     /*DataOutputStream提供了读写Java中的基本数据类型的功能
     FileOutputStream fos=new FileOutputStream("1.txt");
     BufferedOutputStream bos=new BufferedOutputStream(fos);
     DataOutputStream dos=new DataOutputStream(bos);//此时相当于把三个流链接了起来,把bos链接到了fos,又把dos链接到了bos,此时带缓冲并可写入基本数据类型
     byte b=3;
     int i=78;
     char ch=‘a‘;
     float f=4.5f;
     dos.writeByte(b);
     dos.writeInt(i);
     dos.writeChar(ch);
     dos.writeFloat(f);
     dos.close();
     */
    
     /*DataInputStream
     FileInputStream fis=new FileInputStream("1.txt");
     BufferedInputStream bis=new BufferedInputStream(fis);
     DataInputStream dis=new DataInputStream(bis);
     System.out.println(dis.readByte());//读取的顺序要与写入的一致
     System.out.println(dis.readInt());
     System.out.println(dis.readChar());
     System.out.println(dis.readFloat());
     dis.close();
     */
   }
}
 
---------------------------------------------------------------------------------------
import java.io.*;
class PipedStreamTest
{
   public static void main(String[] args)
   {
      PipedOutputStream pos=new PipedOutputStream();
      PipedInputStream pis=new PipedInputStream();
      try
      {
          pos.connect(pis);
          new Producer(pos).start();
          new Consumer(pis).start();
      }
      catch(Exception e)
      {
              e.printStackTrace();
      }
   }
}
class Producer extends Thread
{
     private PipedOutputStream pos;
     public Producer(PipedOutputStream pos)
     {
        this.pos=pos;
     }
     public void run()
     {
         try
         {
            pos.write("Hello".getBytes());
            pos.close();
         } 
         catch(Exception e)
         {
              e.printStackTrace();
         }
     }
}
class Consumer extends Thread
{
     private PipedInputStream pis;
     public Consumer(PipedInputStream pis)
     {
        this.pis=pis;
     }
     public void run()
     {
         try
         {
            byte[] buf=new byte[100];
            int len=pis.read(buf);
            System.out.println(new String(buf,0,len));
            pis.close();
         } 
         catch(Exception e)
         {
              e.printStackTrace();
         }
     }
}
 
 
  Java I/O库的设计原则
    Java的I/O库提供了一个称作链接的机制,可以将一个流与另一个流首尾相接,形成一个流管道的链接。这种机制实际上是一种被称为Decorator(装饰)设计模式的应用
    通过流的链接,可以动态的增加流的功能,而这种功能的增加是通过组合一些流的基本功能而动态获取的
    我们要获取一个I/O对象,往往需要产生多个I/O对象(例如InputStream链:FileInputStream从文件中获取输入字节—BufferedInputStream增加了缓冲的功能—DataInputStream增加了读取Java基本数据类型的功能;OutputStream链:DataOutputStream可以往输出流中写入Java基本数据类型—BufferedInputStream提供数据写入到缓冲区的功能—FileInputStream将数据写入到文件中),这也是Java I/O库不太容易掌握的原因,但在I/O库中Decorator模式的运用,给我们提供了实现上的灵活性
 
InputStream、OutputStream是对字节流的操作,Reader、Writer是对字符流的操作
  Java程序语言使用Unicode来表示字符串和字符
  Reader和Writer这两个抽象类主要用来读写字符流
 
import java.io.*;
class StreamTest
{
   public static void main(String[] args) throws Exception
   {
     /*
     FileOutputStream fos=new FileOutputStream("1.txt");
     OutputStreamWriter osw=new OutputStreamWriter(fos);
     BufferedWriter bw=new BufferedWriter(osw);
     bw.write("http://lilac.spaces.");
     bw.close();
    
     FileInputStream fis=new FileInputStream("1.txt");
     InputStreamReader isr= new InputStreamReader(fis);
     BufferedReader br=new BufferedReader(isr);
     System.out.println(br.readLine());
     br.close();
     */
    
     InputStreamReader isr= new InputStreamReader(System.in);
     BufferedReader br=new BufferedReader(isr);
     String strLine;
     while((strLine=br.readLine())!=null)//从输入设备上读入一行数据,没有输入数据则为null
     {
        System.out.println(strLine);
     }
     br.close();
   } 
}

  字符集的编码:
    ASCII(American Standard Code for Information Interchange,美国信息互换标准代码),是基于常用的英文字符的一套电脑编码系统。我们知道英文中经常使用的字符、数字符号被计算机处理时都是以二进制码的形式出现的。这种二进制码的集合就是所谓的ASCII码。每一个ASCII码与一个8位(bit)二进制数对应。其最高位是0,相应的十进制数是0-127。如,数字“0”的编码用十进制数表示就是48。另有128个扩展的ASCII,最高位都是1,由一些指标符和其他符号组成。ASCII是现今最通用的单字节编码系统。
    GB2312:GB2312码是中华人民共和国国家汉字信息交换用编码,全称《信息交换用汉字编码字符集—基本集》。主要用于给每一个中文字符制定相应的数字,也就是进行编码。一个中文字符用两个字节的数字来表示,为了和ASCII码有所区别,将中文字符每一个字节的最高位置都用1来表示
    GBK:为了对更多的字符进行编码,国家又发布了新的编码系统GBK(GBK的K是“扩展”的汉语拼音第一个字母)。在新的编码系统中,除了完全兼容GB2312外,还对繁体中文、一些不常用的汉字和许多符号进行了编码
    ISO-8859-1:是西方国家所使用的字符编码集,是一种单字节的字符集,而英文实际上只用了其中数字小于128的部分
    Unicode:这是一种通用的字符集,对所有语言的文字进行了统一编码,对每一个字符都用2个字节来表示,对于英文字符采取前面加"0"字节的策略实现等长兼容。如"a"的ASCII码为0x61,UNICODE就为0x00,0x61
    UTF-8:Eight-bit UCS Transformation Format,(UCS,Universal Character Set,通用字符集,UCS是所有其他字符集标准的一个超集)。一个7位的ASCII码值,对应的UTF码是一个字节。如果字符是0x0000,或在0x0080与0x007f之间,对应的UTF码是两个字节,如果字符在0x0800与0xffff之间,对应的UTF码是三个字节
   
    在Java中字符都是用Unicode表示的。我们获取一段Unicode,是解码;将字符或字符串转换为字节就是编码过程
------------------------------------------------------------------------------------------
import java.util.*;
import java.nio.charset.*;
class CharsetTest//解决乱码显示问题
{
   public static void main(String[] args) throws Exception
   {
          /*输出JVM所支持的字符集
          Map m=Charset.availableCharsets();
          Set names=m.keySet();
          Iterator it=names.iterator();//迭代器
          while(it.hasNext())
          {
                System.out.println(it.next());
          }
          */
         
          Properties pps=System.getProperties();//static Properties getProperties():Determines the current system properties.
          pps.list(System.out);
          pps.put("file.encodeing","ISO-8859-1");
          int data;
          byte[] buf=new byte[100];
          int i=0;
          while((data=System.in.read())!=‘q‘)
          {
             buf[i]=(byte)data;
             i++; 
          }
          String str=new String(buf,0,i);//Java会按ISO-8859-1把得到的字符解码成Unicode,但输入的时候是按中文操作系统的字符集GBK码读入的 String(byte[] bytes, int offset, int length):Constructs a new String by decoding the specified subarray of bytes using the platform‘s default charset.
          System.out.println(str);
          String strGBK=new String(str.getBytes("ISO-8859-1"),"GBK");//把内容按ISO-8859-1编码回去,再按GBK解码回来
          System.out.println(strGBK);
    }
}
 
-----------------------------------------------------------------------------------------------
 
    RandomAccessFile类
      RandomAccessFile类同时实现了DataInput和DataOutput接口(所以可以读取和写入),提供了对文件随机存取的功能,利用这个类可以在文件的任何位置读取或写入数据
      RandomAccessFile类提供了一个文件指针,用来标志要进行读写操作的下一数据的位置
import java.io.*;
class RandomFileTest
{
   public static void main(String[] args) throws Exception
   {
      Student s1=new Student(1,"zs",98.5);
      Student s2=new Student(2,"ls",99.5);
      Student s3=new Student(3,"ww",95.5);
      RandomAccessFile raf=new RandomAccessFile("student.txt","rw");
      s1.writeStudent(raf);
      s2.writeStudent(raf);
      s3.writeStudent(raf);
     
      Student s=new Student();
      raf.seek(0);//写入第三个Student信息的时候,指针已经指向末尾下面还要进行读取,所以要设置一下文件指针
      for(long i=0;i<raf.length();i=raf.getFilePointer())//long length():Returns the length of this file;long getFilePointer():Returns the current offset in this file.
      {
          s.readStudent(raf);
          System.out.println(s.num+":"+s.name+":"+s.score); 
      }
      raf.close();
   }
}
class Student
{
 int num;
 String name;
 double score;
 public Student()
 {}
 public Student(int num,String name,double score)
 {
   this.num=num;
   this.name=name;
   this.score=score; 
 }
 public void writeStudent(RandomAccessFile raf) throws IOException
 {
     raf.writeInt(num);
     raf.writeUTF(name);//writeBytes(String str):……by discarding its high eight bits不建议使用,中文字符只写入第一个字节无法识别;writeChars(String str)和writeUTF(String str)都可以,但是writeUTF会在一开始记录写入数据长度,所以RandomAccessFile还提供了readUTF()
     raf.writeDouble(score);
 }
 public void readStudent(RandomAccessFile raf) throws IOException
 {
  num=raf.readInt();
  name=raf.readUTF();
  score=raf.readDouble();
 }
}
--------------------------------------------------------------------------------------

    对象序列化
      将对象转换为字节流保存起来,并在日后还原这个对象,这种机制叫做对象序列化(可用于在网络上发送对象)
      将一个对象保存到永久存储设备上称为持续性
      一个对象要想能够实现序列化,必须实现Serializable接口(标识接口)或Externalizable接口
      当一个对象被序列化时,只保存对象的非静态成员变量,不能保存任何的成员方法和静态的成员变量
      如果一个对象的成员变量是一个对象,那么这个对象的数据成员也会被保存
      如果一个可序列化的对象包含对某个不可序列化的对象的引用,那么整个序列化操作将会失败,并且会抛出一个NotSerializableException。我们可以将这个引用标记为transient,那么对象仍然可以序列化
 
import java.io.*;
class ObjectSerialTest
{
   public static void main(String[] args) throws Exception
   {
       Employee e1=new Employee("zs",25,3000.50);
       Employee e2=new Employee("ls",24,3200.40);
       Employee e3=new Employee("ww",27,3800.55);
      
       FileOutputStream fos=new FileOutputStream("employee.txt");
       ObjectOutputStream oos=new ObjectOutputStream(fos);//ObjectOutputStream实现了DataOutput,所以它提供了往输出流当中写入Java基本数据类型的功能
       oos.writeObject(e1);//writeObject()完成对象序列化
       oos.writeObject(e2);
       oos.writeObject(e3);
       oos.close();
      
       FileInputStream fis=new FileInputStream("employee.txt");
       ObjectInputStream ois=new ObjectInputStream(fis);
       Employee e;
       for(int i=0;i<3;i++)
       {
          e=(Employee)ois.readObject();//Object readObject()返回的是Object,所以要做类型转换,否则报错;当反序列化的时候,并不会调用该对象任何的构造函数,只是根据先前所保存的信息在内存中重新还原对象 
          System.out.println(e.name+":"+e.age+":"+e.salary);
       }
       ois.close();
   }
}
class Employee implements Serializable
{
 String name;
 int age;
 double salary;//若不像让某个变量被序列化保存起来,就可以也将其声明为transient
 transient Thread t=new Thread();//Thread本身不可序列化,标记为transient后就不会再参与到序列化中
 public Employee(String name,int age,double salary)
 {
   this.age=age;
   this.name=name;
   this.salary=salary;  
 }
 //写如下两个方法,可以在序列化的时候调用自己写的方法,这里两个方法虽声明为private,但是可以在类外调用,是个特例
 private void writeObject(java.io.ObjectOutputStream oos) throws IOException
 {
    oos.writeInt(age);
    oos.writeUTF(name);
    System.out.println("Write Object");
 }
  private void readObject(java.io.ObjectInputStream ois) throws IOException, ClassNotFoundException
  {
     age=ois.readInt();
     name=ois.readUTF();
     System.out.println("Read Object");
  }
}
 

总结:
    InputStream和OutputStream:字节流的输入和输出。从这两个类派生出了一些类:FileInputStream、BufferedInputStream、DataInputStream、PipedInputStream、ObjectInputStream;FileOutputStream、BufferedOutputStream、DataOutputStream、PipedOutputStream、ObjectOutputStream
    Reader和Writer:字符流的输入和输出。从这两个类派生出了一些类:BufferedReader、InputStreamReader;BufferedWriter、OutputStreamWriter
    流的链接(Java I/O库的设计原则)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多