您的位置:首页 > 编程语言 > Java开发

java_IO流

2014-03-14 01:32 369 查看
IO流
IO流:

       一、字节流:

              1、InputStream 读入字节数据。

              2、OutputStream 写出字节数据。

       二、字符流:

              1、Reader 读入字符数据。

              2、Writer 写入字符数据。

字符流:

FileWriter练习:

public
class
WriterTest{
   //创建文件流对象,必须处理或者抛出异常。
   public
static void
main(String[] args){
      //在外部创建文件流引用,不指定对象,方便内部处理异常。
      FileWriter fw = null;
      try{
         //创建一个文件流对象,并初始化路径。必须初始化,FileWriter没有空数构造函数。
         fw = new FileWriter("d:\\WriterTest.txt"); 
         fw.write("fanbotest");//向流中写入数据。些时写入流中,并没写入指定文件中。
         fw.flush();//刷新流中的数据至指定文件中。
      }catch(IOException e){
         System.out.println(e.toString());
      }finally{
         try{
            if(fw !=null)
                fw.close();//关闭流,并将流中数据刷新到指定文件中。
         }catch(IOExceptione){
            System.out.println(e.toString());
         }
      } 
   }
}

此例中,fw = new FileWriter("d:\\WriterTest.txt");如果此路径下已存在指定文件,那么此时创建的FileWriter流对象会将原有的文件覆盖掉。

要在原文件中续写内容,需要修改构造参数,如:

fw = new FileWriter("d:\\WriterTest.txt",true);  这时续写的内容会自动添加到原有文件的末尾。

注意:在向文件中续写内容时,要想换行,Windows只识别\r\n。

FileRader:

       read()、read(char[] cbuf)、read(char[]
int off,int len)、read(CharBuffer target):read方法的返回值都是int。参数是字符数组的,返回值是字符个数。当文件读到尾末时,返回值为-1。

文件读入方式一:读一个打印一个。

例如:

   // 创建文件读入流,并指定路径需读的文件。
      FileReader fr = new FileReader("F:\\abc.txt");
      //循环读,直到读到尾末返回-1时结束,此时文件读完。
      while(fr.read()!=-1){
         System.out.println((char)fr.read());
      }

      fr.close();

文件读入方式二:读一个存一个,存满了一次全打印。

例如:

// 创建文件读入流,并指定路径需读的文件。
      FileReader fr = new FileReader("F:\\abc.txt"
4000
);
      //定义一个字符数组,将取到的字符存入。
      char[] ch =new
char
[1024];
      int len = 0;
      //循环读,直到读到末尾返回-1。
      while((len =fr.read(ch))!=-1){
         //将字符数组中的字符变成字符串,一次打印。
         System.out.println(len+"  "+new String(ch,0, len));
      }
      fr.close();

文件的拷贝练习:

//练习拷贝一个txt文件。
package IO_Test;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
 
public
class
ReaderTest_01 {
   public
static void
main(String[] args) {
      //创建读和写的文件流引用。
      FileWriter fw = null;
      FileReader fr = null;
      try{
         //创建目的文件和源文件。
         fw = new FileWriter("D:\\copytest.txt");
         fr = new FileReader("F:\\abc.txt");
         //字符数组用来存储源文件中的字符数据。
         char[] ch =new
char
[1024];
         int len = 0;
         while((len = fr.read(ch))!=-1){
            fw.write(ch, 0, len);//将字符数组数据写入目的文件。
         }
      }catch(IOException e){
         //捕到异常时,说明文件读写失败了,这时就让程序停止,抛出去让程序员处理。
         throw
new
RuntimeException("文件错误。");
      }finally{
         if(fw !=null){
            //注意:读入流和写出流在关闭时,要分别try。
            try{
                fw.close();
            }catch(IOException e){
            }
         }
         if( fr !=null){
            try{
                fr.close();
            }catch(IOException e){              
            }
         }
      }
   } 
}
BufferedWriter:
缓冲区的出现是为了提高效率。
缓冲区在创建之前,必须先有流对象,也就是缓冲区没有空参数的构造函数。
只将流对象传入缓冲区的构造参数即可。
记住:只要用到缓冲区,就要刷新。
关闭缓冲区时,同时也关闭了流对象。
缓冲区多了一个newLine()方法:行分隔符,在Windows和linux里通用,跨平台的。Windows只识别\r\n,linux只识别\n。
BufferedReader:

BufferReader有一个readLind方法,可以一次读取一行数据。非常效率。返回值为String,返回的数据只包括回车符以前的数据,并不包含量终止符。如果读到末尾,返回null

示例:

   //创建读入流对象,指定操作文件。
      FileReader fr = new FileReader("d:\\BufferedTest.txt");
      //定义缓冲区,将流传入构造参数。
      BufferedReader br = new BufferedReader(fr);
      String str = null;
      //读出缓冲区数据,一行一行的读。
      while((str = br.readLine())!=null){
         System.out.println(str);
      }
      br.close();

装饰设计模式:

       对已有的对象进行功能增强。

       定义类,将已有的对象传入,基于已有的功能,添加更强的功能。这个自定义的类叫做装饰类。

       装饰类通常通过构造方法,接收被装饰的对象。

示例:

package ZhuangShiSheJi;
 
public
class
ZhuangShiTest {
   public
static void
main(String[] args) {
      //建立原有类对象。
      Person p = new Person();
      //建立装饰类对象,将原类对象传进来。
      SuperPerson sp = new SuperPerson(p);
      sp.superEat();//调用新类新方法。
   }
}
//原有的类。
class Person{
   public
void
eat(){
      System.out.println("吃饭");
   }
}
//装饰类。
class SuperPerson{
   //建立原有类引用。
   private Personp;
   //将原类引用作为参数传进来。
   SuperPerson(Person p){
      this.p = p;
   }
   //建立新的装饰函数。比原有类的函数功能强大。
   public
void
superEat(){
      System.out.println("喝点小酒");
      p.eat();
      System.out.println("吃点心");
      System.out.println("抽根烟");
   }
}
装饰类和继承的区别:

装饰设计模式比继承更灵活,辟免了继承体系的臃肿,而且降低了类与类之间的关系。

装饰类因为增强已有对象,具备的功能和已有对象是相同的,只不过提供了更强功能,所以装饰类和被装饰类通常都是属于一个体系中的。

如:

       MyReader//专门用于读取数据的类。

              |--MyTestReader

                     |--MyBufferTextReader

              |--MyMediaReader

                     |--MyBufferMediaReader

              |--MyDataReader

                     |--MyBufferDataReader

       MyReader//专门用于读取数据的类。

              |--MyTestReader

              |--MyMediaReader

              |--MyDataReader

              |--MyBufferReader

LineNumberReader:

它是一个装饰类,跟踪行号的缓冲字符输入流。

setLineNumber(int):设置当前行号,从传入的参数int开始计起。

getLineNumber():获取当前行号。返回值为int。

// 创建读入流对象。
FileReader fr = new FileReader("D: \\ExceptionTest.java");

      //创建行号跟踪流对象,并将读入流对象传入。
      LineNumberReader lnr = new LineNumberReader(fr);
      String line = null;
      //设置行号从100开始递加。
      lnr.setLineNumber(100);
      while((line = lnr.readLine())!=null){
         //打印出指定文件的每行内容,并将行号打印在前面。
         System.out.println(lnr.getLineNumber()+"  "+line);
      }
      lnr.close();
   }

字节流:

InputStream读入    OutputStream写出

在使用字节流写出数据时,不用刷新,不关闭流,也可以写出数据。

OutputStream:

//创建字节文件写出流,并指定目的文件。
      FileOutputStream os = new FileOutputStream("d:\\OutputStreamTest.txt");
      //将字符串转换成字节数组,并写入指定文件中。
      os.write("fanbo_test_OutputStream".getBytes());
   os.close();

使用字节流读入文件有三种方式:

一、一个字节一个字节的读。

//创建文件读入流,并指定源文件。
      FileInputStream fis = new FileInputStream("d:\\OutputStreamTest.txt");
      int ch = 0;
      //一个字节一个字节的读。
      while((ch=fis.read())!=-1){
         //将读到的每个字节转换成字符。
         System.out.println((char)ch);
      }
      fis.close();

二、将源文件按字节读入数组中,再一次取出。

   //创建文件读入流,并指定源文件。
      FileInputStream fis = new FileInputStream("d:\\OutputStreamTest.txt");
      //定义一个字节数组,将源文件的数据读到这里面。
      byte[] b =new
byte
[1024];    
      int len = 0;//用len取得读到数据的长度。
      while((len = fis.read(b))!=-1){
         //将读入b字节数组的数据转换成字符串打印出。
         System.out.println(new String(b,0,len));
      }
      fis.close();

三、用available方法定义一个刚好够的字节数组。

      //创建文件读入流,并指定源文件。
      FileInputStream fis = new FileInputStream("d:\\OutputStreamTest.txt");
      //avalable()方法可以返回源文件中的字节数。
      int num = fis.available();
      //将字节数组的长度定义为源文件数据的长度。
      byte[] b =new
byte
[num];
      //直接把刚好长度的数组读进流。
      fis.read(b);
      //再把数据变成字符串打印出来。
      System.out.println(new String(b));
      fis.close();
   此第三种方法,虽然最简便,但是如果操作一个巨大的文件,比如1G多的电影,这时内存会溢出。不安全。

    所以,这三种读入方法,第二种最适用

拷贝图片:

    用字节流,用字符流也可以拷贝,但是很可能打不开,凡同操作媒体数据,都用字节流,不要用字符流。

代码:

package IO_Test;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
 
public
class
CopyPictureTest {
   public
static void
main(String[] args) {
      FileInputStream fis = null;
      FileOutputStream fos = null;     
      try{       
         fis = new FileInputStream("e:\\fanbo.jpg");//源图片。
         fos = new FileOutputStream("d:\\樊波.jpg");//目的图片。
         byte[] b =new
byte
[1024];
         int len = 0;
         //读取图片,并写到目的。
         while((len = fis.read(b))!=-1){
            fos.write(b,0,len);
         }
      }catch(IOException e){
         throw
new
RuntimeException("拷贝图片失败");
      }finally{
         try{
            if(fis!=null)
                fis.close();
         }catch(IOException e){
            System.out.println("读取流关闭失败");
         }
         try{
            if(fos!=null)
                fos.close();
         }catch(IOException e){
            System.out.println("写出流关闭失败");
         }
      }
   }
}
拷贝Mp3:

       通过字节流缓冲区拷贝。

部分代码:

   BufferedInputStream bis = new BufferedInputStream(new FileInputStream("e:\\流浪记.mp3"));
      BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("d:\\copytest.mp3"));
      int ch = 0;
      while((ch = bis.read())!=-1){
         bos.write(ch);
      }
      bis.close();
      bos.close();

read和write的特点:

read在做类型提升,write在做强转,保证了原数据的不变化。

读取键盘录入:

       System.out:对应的是标准输出设备,控制台。

       System.in:对应的标准输入设备,键盘。返回值类型为:InputStream

回车符:\r-->13    \n-->10

 

练习:需求:通过键盘录入数据,录入一行打印一行,如果录入over,就停止录入。
import java.io.IOException;
import java.io.InputStream;
 
public
class
SystemInTest {
   public
static void
main(String[] args)throws IOException {
      //创建读入流,指定键盘录入。
      InputStream is = System.in;
      //创建缓冲区,记录读到的数据。
      StringBuilder sb = new StringBuilder();
      //循环操作读到的数据。
      while(true){
         int ch = is.read();//一个一个读。
         if(ch=='\r')
            continue;//如果读到\r继续循环读。
         if(ch=='\n'){
            String s = sb.toString();//读到\n时,就将读到的所有数据变成字符串。
            if("over".equals(s))
                break;//判断一下,如果字符串是over,就跳出结束。
            System.out.println(s.toUpperCase());//打印字符串。
            sb.delete(0, sb.length());//清空缓冲区。
         }
         else
             sb.append((char)ch);//将读到的数据存到缓冲区里。 
      }
   }
}
此方法较复杂,可以通过使用转换流InputStreamReader,使用readLine方法,更简便。

例如:

//创建键盘录入对象。
      InputStream is = System.in;
      //创建读入转换流,字节流转换为字符流。
      InputStreamReader isr = new InputStreamReader(is);
      //建立字符流缓冲区,它有readLine方法。
      BufferedReader br = new BufferedReader(isr);
      String line = null;
      //循环录入数据,并读入。
      while((line = br.readLine())!=null){
         if("over".equals(line))//判断over结束。
            break;
         System.out.println(line.toUpperCase());//打印出来。
      }
      br.close();
   }

写出流转换:

public
class
OutputStreamWriterTest {
   public
static void
main(String[] args)throws IOException {
      //字符流缓冲区-->字节读入转换流-->键盘录入。源:键盘。
      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
      //字符流缓冲区-->字节写出转换流-->输出。目的:控制台。
      BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));    
      String line = null;
      //循环录入一行,输出一行。
      while((line=br.readLine())!=null){
         if("over".equals(line))//结束标记。
            break;
         bw.write(line.toUpperCase());//写出。
         bw.newLine();//换行。
         bw.flush(); //刷新。    
      }
      bw.close();
      br.close();
   }
}

键盘的最常见写法:

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

操作流的基本规律:

       三个明确:

一、明确源和目的。

       源:输入流,InputStream    Reader

       目的:输出流,OutputStream    Writer

二、操作的数据是不是纯文本。

       是:字符流。

       不是:字节流。

三、当体系明确后,在明确要使用哪个具体的对象。

       通过设备来进行区分:

       源设备:内存、硬盘、键盘。

       目的设备:内存、硬盘、控制台。

是否要提高效率?是:加入缓冲区。

需求:将键盘录入的数据保存到一个文件中。

       分析:

       源:InputStream  Reader

          是否为纯文件?是,Reader

       设备:键盘,对应的对象是System.in。

           System.in对应的是字节流,选Reader是为了操作方便,将字节流转成字符流。

           那么就将System.in转成Reader。用到InputStreamReader。

              InputStreamReaderisr = new InputStreamReader(System.in);

       要提高效率吗?要。BufferedReader

              BufferedReader= br = new BufferedReader(isr);

       目的:OutputStream  Writer

           是否为纯文件?是。Writer

       设备:硬盘。使用FileWriter。

           FileWriter fw = newFileWriter("d:\\cc.txt");

       需要提高效率:

           BufferedWriter bw = new BufferedWriter(fw);

扩展:

       要想把录入的数据按指定的编码表(utf-8),将数据存到文件中。

       FileWriter是使用的默认编码表,GBK。

       这时要使用的对象是转换流。OutputStreamWriter,只有转换流,在传参数时,才可以指定编码表。

   OutputStreamWriter osw = new OutputStreamWriter("d:\\aa.txt","utf-8");

记住:转换流是字符和字节之间的桥梁,通常,涉及到字符编码转换时,需要用到转换流。

    当使用转换流,将字定编码的流数据写入到某文件时,要想读取此文件,也得指定同样的编码才可以读取。同样,通过转换流指定编码:InputStreamWriter osw =new
InputStreamWriter("d:\\aa.txt","utf-8");
System中有两不常用的方法:(介绍)

一、System.setIn(InputStream  in):将键盘录入更改为指定读取流对象。
    例:System.setIn(“d:\\fanbo.txt”);这时,数据就不在是键盘录入了,而是fanbo.txt中的数据内容。

二、System.setOut(printStream ps):将控制台更改为指定写入流对象。

   例:System.setOut(“e:\\cc.txt”):这时,原来打印在控制台的数据,就存到了cc.txt里面了。

异常的日志信息:

   

系统信息:

 

File类

separator :跨平台的分隔符,在windows的绝对路径中\\表示分隔符,如c:\\fanbo\\fan.txt。但是\\不跨平台。可以用separator代替。如”c:”+File.separator+”fanbo”+File.separator+”fan.txt”。

创建File对象:

1、File f1 = newFile("c:\\abc\\a.txt");

2、File f2 = newFile("c:\\abc","a.txt");

3、File d = newFile("c:\\abc");

     File f3 = new File(d,"a.txt");

1、2、3种创建方式都指向同一个路径。打印f1、f2、f3,得到所指的路径。

File类常见方法:

、创建

       booleancreatNewFile():在指定位置创建文件,如果该文件已经存在,则不创建,返回false。和输出流相反,输出流对象一建立就创建文件,如果文件存在,会覆盖。

     static File createTempFile(String XX,StringXX):创建临时文件。

、删除

       booleandelete():删除文件或者目录,目录必须为空才能删除,失败则返回false。

       voiddeleteOnExit():在程序退出时删除指定文件

、判断

       booleancanExecute():判断文件是否可以执行。

       booleanexists():文件是否存在。

       booleanmkdir():创建一级目录,就是创建文件夹,只能创建一级。"abc\\ab"就可以,"fdsk\\fdf\\dfsf\\fdfsd"就不可以。

       booleanmkdirs():创建多级文件夹。

       booleanisDirectory():是否是目录。

       booleanisFile():是否是文件。

       对一个文件或者目录判断时,必须要判断该文件对象是否存在,通过exists判断。(很容易出错) 注意:目录也可以是xxx.txt,它就不是文件。

       booleanisHidden():是否是隐藏文件。

       booleanisAbsolute():判断是不是绝对路径,文件不存在也可以判断。

、获取

       StringgetName():获取文件名字。

       StringgetPath():获取相对路径。封装的什么路径,得到的就是什么路径。文件不存在也可以获取。

       StringgetParent():获取父目录。返回的是绝对路径中的父目录,如果是相对路径,返回null。就是上一层目录,只返回一级

       StringgetAbsolutePath():获取绝对路径。文件不存在也可以获取。

       longlastModified():返回时间,最后一次修改时间。

       longlength():返回文件大小。

       booleanrenameTo():改名字,类似剪切

       示例:

       Filef1 = new File("c:\\Test.java");

       Filef2 = new File("c:\\haha.java");

       f1.renameTo(f2);将Test.java的名字改为haha.java

       如果File f2 = new File("d:\\haha.java");这时f1调用renameTo方法,就将Test.java改名后,移到了d盘,就是又改名又移动,类似剪切。

File[] listRoots():返回的是File[],可以列出电脑里的有效盘符。

例:File[]
f2 =File.listRoots();
      for(File f :f2){
         sop(f);
   }

String[] list():列出指定目录下的所有文件或文件夹名称,包含隐藏文件,调用它必须是该对象封装的是目录。

例:File f1 =
new
File("e:");
      String[] str = f1.list();
      for(String s : str){
         sop(s);
   }

String[] list(FilenameFilter filter)过滤器文件名。

例:File f1 =
new
File("f:");
      String[] str = f1.list(new FilenameFilter(){
         public
boolean
accept(File dir,String name){
            return name.endsWith("docx");
         }
   });

File[] listFiles():返回的是文件,可以对文件进行操作,String[] list()只是返回的文件名,不能进行操作。

例:File f1 =
new
File("f:");
      File[] str = f1.listFiles();
      for(File s : str){
         sop(s.getName()+"    "+s.length());
      }

练习:列出目录下所有内容。(递归)

递归要注意:

       1、限定条件。

       2、要注意递归的次数。尽量避免内存溢出。

package IO_Test;
import
java.io.File;
public
class
DiGuiTest {
   public
static void
main(String[] args) {
      Filef = newFile("E:");//指定要列出所有目录的父目录。
      getDrictory(f);
   }
   //创建获取方法:获取指定目录下的所有文件。
   public
static void
getDrictory(File dir){
      File[]files = dir.listFiles();//将指定目录的子目录存到File数组中。
      System.out.println(dir);//打印父目录。
      //循环打印子目录及文件。
      for(int x=0; x<files.length; x++){
         if(files[x].isDirectory())//如果有子目录,就使用递归,将子目录的文件打印。
            getDrictory(files[x]);
         else
            System.out.println(files[x]);//打印子目录。
      }
   }
}
练习:删除带内容的目录。(用到递归)

package IO_Test;
import java.io.File;
public
class
DeleteTest {
   public
static void
main(String[] args) {
      File file = new File("d:\\fanbo");//指定要删除的目录及里面的所有内容。
      deleteFile(file);//调删除方法。
   }
   //定义删除方法。
   public
static void
deleteFile(File f){
      //传入要删除的目录,再将些目录的子目录装进File[]数组。
      File[] files = f.listFiles();
      for(int x=0; x<files.length;x++){
         //判断,如果是目录,就递归。
         if(files[x].isDirectory()){
            deleteFile(files[x]);
         }
         Else
            System.out.println(files[x]+"::--::"+files[x].delete());//删除文件。
      }
      System.out.println(f+"::--::"+f.delete());//将已删掉子文件的空目录也删掉。
   }
}
练习:创建java文件列表。(和毕老师的代码不一样)

package IO_Test;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
 
public
class
GetFileDirectoryTest {
   public
static void
main(String[] args)throws IOException {     

      //定义要创建列表的目录。
      File file = new File("E:\\中关村-黑马程序员\\Java零基础辅导班
- 李若亮");
      //将目录存到当前的txt中。
      FileWriter fw = new FileWriter("E:\\中关村-黑马程序员\\Java零基础辅导班
- 李若亮\\li.txt");    
      getFileDirectory(file,fw);//调用自定义获取方法。
   }
   //制作获取方法,参数为要创建的目录,和要写出的数据流,
   public
static void
getFileDirectory(File file,FileWriter fw)throws IOException{
      File[] files = file.listFiles();
      for(File f : files){
         if(f.isDirectory())//如果是目录,就递归。
            getFileDirectory(f,fw);
         fw.write(f.getAbsolutePath());
//获取绝对路径,并写到目的txt文件中。
         fw.write("\r\n");//存一个就换行。
      }
   }
}
Propertise

Properties是hashtable的子类。

它具备map集合的特点,它里面存储的健值对都是字符串。就不用泛型了。

是集合和IO技术相结合的集合容器

该对象的特点,可以用于键值对形式的配置文件ini(就是软件的一些属性,比如设置颜色corlor=50)。(文本=红色,背景=绿色)

Properties的存取方法:

       setProperty(Stringstr1,String str2):直接存入键和值。

       getProperty(Stringstr):str为键。返回value值。

       stringPropertyNames():返回值的是Set集合。将键存入到Set集合中。

Properties已经封装好了将配置文件(键值对的文件)中的数据通过流加载进集合。

在加载数据时,需要数据有固定格式:键=值。

直接用load(InputStream is)和load(Reader
read)两个方法就能将流数据加载进集合。

list(PrintStream ps)将属性列表输出到指定的输出流。如:XXX.list(System.out)就将键值信息打印到控制台上。

store(OutputStream out, String comments)方法,能将集合中修改过的数据存储到流的目的文件中。

练习:用于记录应用程序运行次数,使用次数已到,那么给出注册提示。

 

打印流:

PrintStream:字节打印流。

    构造函数可以接收的参数类型:

    1、file对象。File

    2、字符串路径。String

    3、字节输出流。OutputStream

PrintWriter:字符打印流。很常用。

    构造函数可以接收的参数类型:

    1、file对象。File

    2、字符串路径。String

    3、字节输出流。OutputStream

    4、字符输出流。Writer

打印流:该流提供了打印方法,可以将各种数据类型都原样打印。

示例:

public
class
PrintStream {
   public
static void
main(String[] args)throws IOException {
      //创建键盘录入。
      BufferedReader bfr = new BufferedReader(new InputStreamReader(System.in));
      //PrintWriter pw = newPrintWriter(System.out);//打印流,直接传入控制台,需要刷新。
      //PrintWriter pw = newPrintWriter(System.out,true);//加true参数,可以不用刷新,直接输出。
      //PrintWriter pw = newPrintWriter("d:\\fanbo.txt");//直接传入文件,不能刷新。
      PrintWriter pw = new PrintWriter(new FileWriter("d:\\fanbo.txt"),true);//加上文件写出流,就可以加true自动刷新了。
      String line = null;
      while((line = bfr.readLine())!=null){
         if("over".equals(line))
            break;
         pw.println(line.toUpperCase());
         pw.flush();
      } 
      bfr.close();
      pw.close();
   }

合并流:SequenceInputStream

    将多个流合并成一个流。例如:把三个txt文档的内容合到一个txt文档中,先建立3个读取流,再使用SequenceInputStream流把3个读取流合成一个流。

它有两种构造函数:

1、SequenceInputStream(InputStream
s1,InputStream s2):将2读取流合成一个流。

2、SequenceInputStream(Enumeration<InputStream> e):把多个读取流合成一个,参数是枚举,就是先将多个流存到Vector集合中,然后再合成一个。

示例:三流合一流。

public
class
SequenceInputStreamTest {
   public
static void
main(String[] args)throws IOException {
      //建立Vector集合,泛型指定存入文件读入流对象。
      Vector<FileInputStream> v = new Vector<FileInputStream>();
      //添加3个文件读入流对象。
      v.add(new FileInputStream("d:\\完美国际名字.txt"));
      v.add(new FileInputStream("d:\\题谷公司地址.txt"));
      v.add(new FileInputStream("d:\\MySQL路径.txt"));
      //枚举。
      Enumeration<FileInputStream> en = v.elements();
      //定义合并流,将en传入,三流变成一流。
      SequenceInputStream sis = new SequenceInputStream(en);
      //定义文件写出流,并指定目的。
      FileOutputStream bis = new FileOutputStream("d:\\三合一.txt");
       byte[] b =new
byte
[1024];
       int len = 0;
       while((len = sis.read(b))!=-1){
          bis.write(b,0,len);          
       }
       sis.close();
       bis.close();
   }
}
切割文件:

       和合并流相反,将某文件切割成几份。

示例:
public
class
SplitFileTest {
   public
static void
main(String[] args)throws IOException {
      //定义读取流,确定要被切割的源文件。
      FileInputStream fis = new FileInputStream("d:\\许佳慧-预谋.mp3");
      FileOutputStream fos = null;//先声明一个文件写出流。    
      byte[] b =new
byte
[1024*1024];//定义一个1M的容器。
      int len = 0;
      int count = 1;
      while((len = fis.read(b))!=-1){
         //将刚好1M的容器数据写入到指定目的。计数器count自增后,下次就另存一个目的文件。
         fos = new FileOutputStream("d:\\"+(count++)+".part");
         fos.write(b, 0, len);
         fos.close();
      }
      fis.close();
   }
}

IO包中的其他类:

ObjectInputStream   ObjectOutputStream

ObjectOutputStream构造时必须有一个流对象。

writeObject和readerObjet成对使用。

要序列化的对象必须实现Serializable接口,Serializable是一个标记接口,没有方法。

当某个对象已经持久化存到硬盘了,再改动过代码,再存,这时就读不出改过后的对象了,因为改动前已经有一个序列号UID,改动后生成了新的UID,不一样就识别不出来。UID是根据成员属性来生成的序列号。

如果你想改动后仍是原来的对象,那么自定义固定的UID,不让虚拟机自动生成。

如:public static final long serialVersionUID= 42L;

如果对象中有静态成员,将不能被序列化。

如果不是静态成员,又不想被序列化,就加transient修辞。

一般将对象序列存为xxx.object。

写出示例:

//创建对象写出流。
      ObjectOutputStreamoos =new
ObjectOutputStream(new FileOutputStream("d:\\person.object"));
      oos.writeObject(new Person("樊波",20));//指定要写出的对象。
      oos.close();//关流。

读取示例:

ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:\\person.object"));
      Person p = (Person)ois.readObject();//读取对象。
      System.out.println(p);
      ois.close();

管道流:

PipedInputStream和PipedOutputStream

涉及到多线程的IO流,输出流和输入流同时进行。

如何连接?

1、构造函数就对方的流传进来。如:PipedInputStream(PipedOutputStream src);

2、通过方法connect(PipedOutputStreamsrc)连接。

示例:

import java.io.*;
 
class Read
implements
Runnable
{
   private PipedInputStreamin;
   Read(PipedInputStream in)
   {
      this.in = in;
   }
   public
void
run()
   {
      try
      {
         byte[] buf =new
byte
[1024];
         System.out.println("读取前。。没有数据阻塞");
         int len =in.read(buf);
         System.out.println("读到数据。。阻塞结束");
 
         String s= new String(buf,0,len);
 
         System.out.println(s);
 
         in.close();
 
      }
      catch (IOException e)
      {
         throw
new
RuntimeException("管道读取流失败");
      }
   }
}
 
class Write
implements
Runnable
{
   private PipedOutputStreamout;
   Write(PipedOutputStream out)
   {
      this.out = out;
   }
   public
void
run()
   {
      try
      {
         System.out.println("开始写入数据,等待6秒后。");
         Thread.sleep(6000);
         out.write("piped lai la".getBytes());
         out.close();
      }
      catch (Exception e)
      {
         throw
new
RuntimeException("管道输出流失败");
      }
   }
}
 
class  PipedStreamTest
{
   public
static void
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:随机读取访问。(重点掌握)

随机访问文件,结尾没有父类名,自成一派。

该类不算是IO体系中的子类,而是直接继承自Object。但它是IO包中成员,因为它具备读和写功能。

内部封装了一个数组,而且通过指针对数组中的元素进行操作。

可以通过getFilePointer获取指针位置,同时可以通过seek改变指针的位置。

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

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

如果模式为只读r,不会创建文件,会去读取一个已存在的文件,如果该文件不存在,则会出现异常。

如果模式为读写rw,操作的文件不存,会自动创建,如果存在则不会覆盖。

可以对数据进行分段存储,比如三条线程,分段写进某一文件。

示例:

import java.io.*;
 
class RandomAccessFileDemo
{
   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.seek(8*1);
      //跳过指定的字节数
      raf.skipBytes(8);
 
      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*0);
      raf.write("周期".getBytes());
      raf.writeInt(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();
   }
}
 

DataStream:

DataInputStream和DataOutputStream

操作基本数据类型的流对象。

示例:
import java.io.*;
class DataStreamDemo
{
   public
static void
main(String[] args)throws IOException
   {
      //writeData();
      //readData();
      //writeUTFDemo();
//    OutputStreamWriter osw = new OutputStreamWriter(newFileOutputStream("gbk.txt"),"gbk");
//    osw.write("你好");
//    osw.close();
//    readUTFDemo();
   }
   public
static void
readUTFDemo()throws IOException
   {
      DataInputStream dis = new DataInputStream(new FileInputStream("utf.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
readData()throws IOException
   {
      DataInputStream dis = new DataInputStream(new FileInputStream("data.txt"));
      int num = dis.readInt();
      boolean b = dis.readBoolean();
      double d = dis.readDouble();
      System.out.println("num="+num);
      System.out.println("b="+b);
      System.out.println("d="+d);
      dis.close();
   }
   public
static void
writeData()throws IOException
   {
      DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.txt"));
      dos.writeInt(234);
      dos.writeBoolean(true);
      dos.writeDouble(9887.543);
      dos.close();
      ObjectOutputStream oos = null;
      oos.writeObject(new O());     
   }
}
ByteArrayStream:

初始化时必须有源数据。关闭此流无效,此类中的方法在关闭此流后仍可被使用,而不会产生任何IOException。

ByteArrayInputStream:构造时,需接收数据源,数据源是一个字节数组。

ByteArrayOutputStream:构造时,不用定义数据目的,因为内部封装了长度可变的字节数组。这就是数据目的。

这两个流对象都操作数组,并没有使用系统资源,所以不用进行close关闭。

源设备:

       键盘 System.in,硬盘FileStream,内存
ArrayStream。

目的设备:

       控制台 System.out,硬盘FileStream,内存ArrayStream。

        //数据源。
      ByteArrayInputStream bis = new ByteArrayInputStream("ABCDEFD".getBytes());
      //数据目的
      ByteArrayOutputStream bos = new ByteArrayOutputStream();
      int by = 0;
      while((by=bis.read())!=-1){
         bos.write(by);
      }
      System.out.println(bos.size());
      System.out.println(bos.toString());

与之对应的类还有:

1、CharArrayReader、CharArrayWriter

2、StringReader、StringWriter

功能同理。

转换流的字符编码:

//编码。向指定流文件中写入“你好”,按UTF-8编码。
OutputStreamWriter osw = newOutputStreamWriter(new FileOutputStream("utf.txt"),"UTF-8");
      osw.write("你好");
      osw.close();   
      //解码。读取流文件中的数据,按gbk解码。
      InputStreamReader isr = new InputStreamReader(new FileInputStream("utf.txt"),"gbk");
      char[] buf =new
char
[10];
      int len = isr.read(buf);
      String str = new String(buf,0,len);
      System.out.println(str);
      isr.close();

    表编码后再用另一种码表解码,会出现乱码。

    表:2个字节一个字符。

    8码表:3个字节一个字符。

字符编码:

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

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

String-->byte[]; str.getBytes(charsetName);字符串变成字节数组,按参数charseName编码。

byte[] -->String: new String(byte[],charsetName);字节数组变成字符串,按参数charseName解码。

注意:

       用GBK编码,若用iso8859-1解码,无凝出现乱码,这时将乱码用iso8859-1编码,再用GBK解码,获得原有数据,情况出现在网页向Tomcat服务器传数据。

       若用GBK编码,再用UTF-8解码,无凝出现????乱码,再将乱码用UTF-8编码,再用GBK解码,不能获得原有数据。GBK和UTF-8都能识别中文。

      String s = "你好";//原数据。
      byte[] b1 = s.getBytes("GBK");//用GBK编码。
      System.out.println(Arrays.toString(b1));
      String s1 = new String(b1,"utf-8");//用UTF-8解码。
      System.out.println("s1="+s1);
      byte[] b2 = s1.getBytes("utf-8");//用UTF-8编码。
      System.out.println(Arrays.toString(b2));
      String s2 = new String(b2,"gbk");//和GBK解码。
      System.out.println("s2="+s2);

联通:

以“联通”两个字打头存入记事本,这时是以默认GBK码表存入数据。当再次打开记事本时,这时去以UTF-8码表解码,出现乱码。

原因:“联通”这两个字在编码时存入时的二进制数字第一位为110****,第二位10****,符合了UTF-8码表的头识别,在打开记事本时,就自动用UTF-8解码了。

 

练习:

/*
有五个学生,每个学生有3门课的成绩,
从键盘输入以上数据(包括姓名,三门课成绩),
输入的格式:如:zhagnsan,30,40,60计算出总成绩,
并把学生的信息和计算出的总分数高低顺序存放在磁盘文件"stud.txt"中。
1,描述学生对象。
2,定义一个可操作学生对象的工具类。
思想:
1,通过获取键盘录入一行数据,并将该行中的信息取出封装成学生对象。
2,因为学生有很多,那么就需要存储,使用到集合。因为要对学生的总分排序。
   所以可以使用TreeSet。
3,将集合的信息写入到一个文件中。
*/
 
package IO_Test;
import java.io.*;
import java.util.*;
public
class
SaveStudentsTest {
   public
static void
main(String[] args)throws NumberFormatException,IOException {
      TreeSet<Student> trse = new TreeSet<Student>();//创建TreeSet集合,用来存学生。
      StudentTool.setStudent(trse);//调用添加学生信息的函数。
      StudentTool.saveStudent(trse);//调用将学生信息存到指定文档中的函数。
   }
}
//学生工具类,有添加学生信息的功能和将学生信息存到指定文档中的功能。
class StudentTool{
   //添加学生信息的函数
   public
static void
setStudent(TreeSet<Student> ts)throwsNumberFormatException, IOException{
      //键盘录入。
      BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
      String line = null;
      while((line = br.readLine())!=null){
         if("over".equals(line))//over为结束标记。
            break;
         String[] s = line.split(",");//将录入的信息用,号分割。
         //将录入的学生信息包成对象。
         Student stu = new Student(s[0],Integer.parseInt(s[1]),Integer.parseInt(s[2]),Integer.parseInt(s[3]));
         ts.add(stu);//将学生对象存入集合。
      }
   }
   //将学生信息存到指定文档中的函数
   public
static void
saveStudent(TreeSet<Student> ts)throws IOException{
      //指定写出流对象,指定要写入的文档。
      BufferedWriter bw = new BufferedWriter(new FileWriter("d:\\student.txt"));
      for(Student s : ts){
         bw.write("姓名:"+s.getName()+"   总分:"+s.getSum());//向文档中写入学生姓名和总分。
         bw.newLine();
         bw.flush();
      }
      bw.close();       
   }
}
//学生类。
class Studentimplements Comparable<Student>{
   //属性有姓名,数学分数、语文分数、英语分数,总分。
   private Stringname;
   private
int
math;
   private
int
chinese;
   private
int
english;
   private
int
sum;
   //构造函数。
    Student(String na,int ma,int ch,int en){
      this.name = na;
      this.math = ma;
      this.chinese = ch;
      this.english = en;
      this.sum = ma+ch+en;
   }
   public String getName(){
      return
name;
   }
   public
int
getSum(){
      return
sum;
   }
   public
int
hashCode(){
      return
name.hashCode()+sum*22;
   }
   public
boolean
equals(Object obj){
      if(!(objinstanceof Student))
         throw
new
ClassCastException("类型不符");
      Student s = (Student)obj;
      return
this
.name.equals(s.name) &&this.sum==s.sum; 
   } 
   public
int
compareTo(Student s) {
      int num =new Integer(this.sum).compareTo(new
Integer(s.sum));
      if(num == 0)
         return
this
.name.compareTo(s.name);
      return num;
   }
}
 
 

 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: