首页 > 代码库 > 黑马程序员——IO篇

黑马程序员——IO篇

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


IO(Input Output)流

   1、IO流用来处理设备之间的数据传输

   2、Java对数据的操作是通过流的方式

   3、Java用于操作流的对象都在IO包中

   4、流按操作数据分为两种:字节流与字符流 。

       字符流的数据字节流其实都能处理,因为无论什么在计算机上最后都是以字节存在的,但是其中有一部分数据它是以文本形式存在的,所以为了方便有了字符流,字符流里融合了编码表,而只有文字用到了编码表,所以字符流用来处理文本形式的数据。


   5、按方向分的话有输入流和输出流之分。

             字符流:        ||           字节流:

        Writer   Reader      ||   InputStream  OutputStream

       这四个基类的子类名称都是以父类的名称为后缀,如FileReader,FileInputStream,前缀为子类的功能名称:如FileWriter为字符流操作文件的子类;FileInputStream字节流操作文件的子类。

   6、在写有关IO流程序的时候,首先要注意:

        1.要注意一定要记得导包,import java.Io.*;//自己经常忘记导包。

        2.记得要进行异常处理,和IO流有关的都会抛异常,原因:例如ileWrite("C:\\abc.txt");因为括号里的文件名会有可能出错,比如写成(K:\\abc.txt)

       3.最后一定要进行关闭流的操作。因为它底层调用的其实是window资源,所以要关闭。

--------------------------------------------------------------------------------------


一般IO异常处理方式:

例如;

//定义全局引用,在try内初始化,使得变量fw作用于整个程序中,方便在finally语句中对流进行关闭操作。

FileWriter fw = null;

try

{  

   fw = new FileWriter(c:\\abc.txt); //对fw进行初始化。

   fw.Write("abcdef");  //调用Writer方法,写入数据。

}

catch(IOException e),

{

   /*异常处理,如果异常的出现不能让程序正常运行,可使用throw new RuntimeException("相关信息");使程序停止运行。*/

}

finaly

{

   try

   {

       //一定要对关闭的流对象进行判断是否为空

       if(fw != null)//因为如果fw初始化时,出现异常被处理后,那么写入操作不能进行。                              //fw没有任何指向,被视为垃圾,被垃圾回收机制处理。

       fw.close();//如果为空的话则不能调用这句,所以要进行下是否为空判断

   }

   catch(IOException e),

   {

      //异常处理

   }

}

-------------------------------------------------------------------------------------


字符流:writer(写)

   通过子类FileWriter来演示数据的操作。

import java.io.*;
class  FileWriterDemo
{
    public static void main(String[] args) throws IOException
    {
        //创建一个FileWriter对象,该对象一被初始化就必须要明确被操作的文件。
        //而且该文件会被创建到指定的目录下。如果该目录下已有同名文件,将被覆盖。
        //其实该步就是在明确数据要存放的目的地。
        FileWriter fw = new FileWriter("Demo.txt");
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        //调用write方法,将字符串写入到流中。
        fw.write("affaf");
        //刷新流对象中的缓冲中的数据。
        //将数据刷到目的地中。
        //fw.flush();
        //关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据。
        //将数据刷到目的地中。
        //和flush的区别:flush刷新后,流可以继续使用;close刷新后,会将流关闭。
        fw.close();
        //fw.write("afjowieap");报错。IOException: Stream closed


   windows系统本身就具有操作文件的功能,java能往windows的文件系统中写入数据,就是在调用系统的这个功能,也就是占用系统的流资源。所以操作完,close动作是一定要做的。


   文件的续写:

       建个文件往里面写数据,但是那个文件里已经有数据了该怎么往里续写数据呢?写一次刷新一次,通过查看API发现FileWriter类有一种构造函数

       FileWriter(String fileName, boolean append)

     参数: fileName - 一个字符串,表示与系统有关的文件名。

      append - 一个 boolean 值,如果为 true,则将数据写入文件末尾处。也就是续写。

-----------------------------------------------------------------------------------


字符流 Reader(读)

   字符流---读取文件1,一次读取单个字符。

import java.io.*;
class FileReaderDemo
{
    public static void main(String[] args) throws IOException
    {
        //创建一个文件读取流对象,和指定名称的文件相关联。
        //要保证该文件已经存在,如果不存在,会发生异常FileNotFoundException
        FileReader fr = new FileReader("demo.txt");
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
        //调用读取流对象的read方法。
        //read():一次读一个字符。而且会自动往下读。
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
        int ch= 0;
        while ((ch = fr.read())!=-1)
        {
            System.out.println((char)ch);
        }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
        /*
        while(true)
        {
            int ch = fr.read();
            if (ch==-1)
                break;
            System.out.println("ch="+ch);
        }
        */
        fr.close();
    }
}


   字符流---读取文件2 通过字符数组进行读取。

import java.io.*;
class FileReaderDemo2
{
    public static void main(String[] args) throws IOException
    {
        FileReader fr = new FileReader("demo.txt");
        //定义一个字符数组,用于存储读到的字符。
        //该read(char[])返回的是读到的字符的个数。
        char[] buf = new char[1024];
        int num = 0;
        while ((num = fr.read(buf))!=-1)
        {
            System.out.println(new String(buf,0,num));
        }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  
        fr.close();
    }
}

注意:

 定义文件路径时,可以用“/”或者“\\”。?

 在创建一个文件时,如果目录下有同名文 件将被覆盖。 ?

 在读取文件时,必须保证该文件已存在, 否则出异常。


练习:复制文件

/*
将F盘一个文本文件复制到E盘。
思路:
1,在E盘创建一个文件,用于存储F盘文件中的数据。
2,定义读取流和c盘文件关联。
3,通过不断的读写完成数据存储。
4,关闭资源。
*/
import java.io.*;
class CopyText
{
    public static void main(String[] args) throws IOException
    {
        //copy_1();
        copy_2();
    }
    //
    public static void copy_2()
    {
        FileReader fr = null;
        FileWriter fw = null;
        try
        {
            fr = new FileReader("f:\\java bolg.txt");
            fw = new FileWriter("e:\\java bolg.txt");
            char[] buf = new char[1024];
            int num = 0;
            while ((num=fr.read(buf))!=-1)
            {
                fw.write(buf,0,num);
            }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        }
        catch (IOException e)
        {
            System.out.println(e.toString());
        }
        finally
        {
            try
            {
                if (fr!=null )
                    fr.close();
            }
            catch (IOException e)
            {
                System.out.println(e.toString());
            }
            try
            {
                if (fw!=null)
                    fw.close();
            }
            catch (IOException e)
            {
                System.out.println(e.toString());
            }
        }
    }
    //读一个字符就存一个。
    public static void copy_1()throws IOException
    {
        FileReader fr = new FileReader("f:\\Dota imba.txt");
        FileWriter fw = new FileWriter("e:\\Dota imba.txt");
        int ch = 0;
        while ((ch = fr.read())!=-1)
        {
            fw.write(ch);
        }
        fw.close();
        fr.close();
    }
}

-------------------------------------------------------------------------

字符流的缓冲区: BufferedWriter  BufferedReader

   

   缓冲区的出现提高了对数据的读写效率。

   对应类

   BufferedWriter

   BufferedReader

   缓冲区要结合流才可以使用。

   在流的基础上对流的功能进行了增强。

示例演示

/*
通过缓冲区复制一个.java文件。
*/
import java.io.*;
class CopyTextByBuf
{
    public static void main(String[] args)
    {
        BufferedReader bufr = null;
        BufferedWriter bufw = null;
        try
        {          
            bufr = new BufferedReader(new FileReader("DateDemo.java"));
            bufw = new BufferedWriter(new FileWriter("e:\\DateDemo.java"));
            String line = null;
            while ((line = bufr.readLine())!=null)
            {
                bufw.write(line);
                bufw.newLine();//因为readLine方法不包含终止符。
                bufw.flush();
            }
        }
        catch (IOException e)
        {
            throw new RuntimeException("读写失败");
        }
        finally
        {
            try
            {
                if (bufr!=null)
                    bufr.close();
            }
            catch (IOException e)
            {
                throw new RuntimeException("读写关闭失败");
            }
            try
            {
                if (bufw!=null)
                    bufw.close();
            }
            catch (IOException e)
            {
                throw new RuntimeException("读写关闭失败");
            }
        }
    }
}


--------------------------------------------------------------------------------

装饰设计模式:

 

/*
装饰设计模式:
当想要对已有的对象进行功能增强时,
可以定义类,将已有对象传入,基于已有对象的功能,并提供加强功能。
那么自定义的该类称为装饰类。
装饰类通常会通过构造方法接受被装饰的对象。
并基于被装饰的对象的功能,通过更强的功能。
*/
class Person
{
    public void chifan()
    {
        System.out.println("吃饭");
    }
}
class SuperPerson
{
    private Person p;
    SuperPerson(Person p)
    {
        this.p = p;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      
    }
    public void superChifan()
    {
        System.out.println("开胃酒");
        p.chifan();
        System.out.println("甜点");
        System.out.println("来一根");
    }
}
class  PersonDemo
{
    public static void main(String[] args)
    {
        Person p = new Person();
        SuperPerson sp = new SuperPerson(p);
        sp.superChifan();
    }
}

   以前是通过继承将每一个子类都具备缓冲功能。那么继承体系会复杂,并不利于扩展。

   现在优化思想。单独描述一下缓冲内容。将需要被缓冲的对象。传递进来。也就是,谁需要被缓冲,谁就作为参数传递给缓冲区这样继承体系就变得很简单。优化了体系结构。


   装饰模式比继承要灵活。避免了继承体系臃肿。而且降低了类于类之间的关系。装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能所以装饰类和被装饰类通常是都属于一个体系中的。


LineNumberReader: 也是一个装饰类,BufferedReader的子类,所以它也有readLine()方法,

                  另外还有setLineNumber()和getLineNumber(),意为设置和获取行号。


使用方法如下(没有进行异常处理):

FileReader fr = new FileReader("Demo.txt");

   LineNumberReader lnr = new LineNumberReader(fr);

String s = null;

lnr.setLineNumber(10);//可以设置行号的起始值

while((x=lnr.readLine()) != null)

{

 System.out.println(lnr.getLineNumber()+"::"+x);

}


-----------------------------------------------------------------------

字节流:InputStream   OutputStream

   基本操作与字符流类相同,但它不仅可以操作字符,还可以操作其他,如:媒体文件

/*
复制一个图片。
思路:
1,用字节读取流对象和图片关联。
2,用字节写入流对象创建一个图片文件。用于存储获取到的图片数据。
3,通过循环读写,完成数据的存储。
4,关闭资源。
*/
import java.io.*;
class CopyPic
{
    public static void main(String[] args)
    {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try
        {
            fis = new FileInputStream("f:\\01.jpg");
            fos = new FileOutputStream("e:\\01.jpg");
            byte[] buf = new byte[1024];//定义一个缓冲数组。
            int len=0;
            while ((len=fis.read(buf))!=-1)
            {
                fos.write(buf,0,len);//将数组中的数据写入。
            }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
        }
        catch (IOException e)
        {
            throw new RuntimeException("复制文件失败");
        }
        finally
        {
            try
            {
                if(fis!=null)
                    fis.close();
            }
            catch (IOException e)
            {
                throw new RuntimeException("读取关闭失败");
            }
            try
            {
                if(fos!=null)
                    fos.close();
            }
            catch (IOException e)
            {
                throw new RuntimeException("写入关闭失败");
            }
        }
    }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     
}

 

注意:

   为什么字节流的读一个字节的read方法返回值类型不是byte,而是int。

   因为有可能会读到连续8个二进制1的情况,8个二进制1对应的十进制是-1。

   那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。

   所以,为了避免这种情况将读到的字节进行int类型的提升。

   并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。


-------------------------------------------------------------------------------

字节流缓冲区

   通过字节流的缓冲区来完成 Mp3的复制(部分代码):

   BufferedInputStream bufis =

                   new BufferedInputStream(new FileInputStream("c:\\0.mp3"));

   BufferedOutputStream bufos =

                   new BufferedOutputStream(new FileOutputStream("c:\\1.mp3"));

   int by=0;

   while((by=bufis.read())!=-1)

   {

       fos.write(by);//把从文件中读取的数据写进目的文件

   }


   fos.close();//所有流都要关闭。

   fis.close();


--------------------------------------------------------------------------

自定义字节流缓冲区:

   字节流缓存区的原理是:把一些数据先存进一个数组中,然后在一下取出来。

import java.io.*;
class MyBufferedInputStream
{
    private InputStream in;
    private byte[] buf = new byte[1024];
    private int pos = 0,count=0;
    MyBufferedInputStream(InputStream in)
    {
        this.in=in;
    }
    //一次读一个字节,从缓冲区(字节数组)获取
    public int myRead()throws IOException
    {
        //通过in对象读取硬盘上数据,并存储到buf中。
        if (count==0)
        {
            count =in.read(buf);//将字节写入到buf中,并将值赋给count
            if (count<0)
                return -1;
            pos =0;//指针归零。
            byte b = buf[pos];
            count--;
            pos++;
            return b;
            return b&255;
        }
        else if (count>0)
        {
            byte b = buf[pos];
            count--;
            pos++;
            return b;
            return b&0xff;
        }
        return -1;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           
    }
    public void myClose()throws IOException
    {
        in.close();
    }
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       
}

-----------------------------------------------------------------------------

读取键盘录入。

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

Sytem.in:对应的标准输入设备:键盘。


需求:

通过键盘录入数据。

当录入一行数据后,就将该行数据进行打印。

如果录入的数据是over,那么停止录入。

import java.io.*;
class  ReadIn
{
    public static void main(String[] args) throws IOException
    {
        InputStream in =System.in;
        StringBuilder sb = new StringBuilder();
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
        while (true)
        {
            int ch = in.read();
            if (ch==‘\r‘)
                continue;
            if(ch==‘\n‘)
            {  
                String s = sb.toString();
                if("over".equals(s))
                    break;
                System.out.println(s.toUpperCase());
                sb.delete(0,sb.length());
            }
            else
            sb.append((char)ch);
        }
    }
}

-------------------------------------------------------------------------------

转换流

   转换流是字符流与字节流之间的桥梁,是为了方便字符流与字节流之间的操作。

   当字节流中都是字符时,转成字符流操作更为高效,

   字符流:

   FileReader

   FileWriter

   BufferedReader

   BufferedWriter

   字节流:

   FileInputStream

   FileOutputStream

   BufferedInputStream

   BufferedOutputStream

   通过刚才的键盘录入一行数据并打印其大写,发现其实就是读一行数据的原理。也就是readLine方法。能不能直接使用readLine方法来完成键盘录入的一行数据的读取呢?

   readLine方法时字符流BufferedReader类中的方法;那么能不能将字节流转成字符流在使用字符流缓冲区的readLine方法呢?

import java.io.*;
class TransStreamDemo
{
    public static void main(String[] args)throws IOException
    {
        //获取键盘录入对象。
        //InputStream in = System.in;
        //将字节流对象转成字符流对象,使用转换流。InputStreamReader
        //InputStreamReader isr = new  InputStreamReader(in);
        //为了提高效率,将字符流进行缓冲技术高效操作。使用BufferedReader
        //BufferedReader bufr = new BufferedReader(isr);
        BufferedReader bufr =
            new BufferedReader(new InputStreamReader(System.in));
        //控制台输出
        //OutputStream out = System.out;
        //OutputStreamWriter osw = new OutputStreamWriter(out);
        //BufferedWriter bufw = new BufferedWriter(osw);
        BufferedWriter bufw =
            new BufferedWriter(new OutputStreamWriter(System.out));
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                
        String line = null;
        while ((line =bufr.readLine())!=null)
        {
            if ("over".equals(line))
                break;
            bufw.write(line.toUpperCase());
            bufw.newLine();
            bufw.flush();
        }
    }
}

流操作的基本规律:

   最痛苦的就是流对象有很多,不知道用哪一个。

   通过三个明确来完成。

   1,明确源和目的。

       源:输入流。InputStream  Reader

       目的:输出流。 OutputStream  Writer

   2,操作的数据是否是纯文本。

       是:字符流。

       不是:字节流。

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

       通过设备来进行区分:

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

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

例1:

将一个文本文件中的数据存储到另一个文件中。复制文件。

源:因为是源,所以使用读取流。InputStream   Reader

   是不是操作文本文件。

   是:这时就可以选择Reader

   这样体系就明确了。

   接下来明确要使用该体系中的哪个对象。

   明确设备:硬盘,一个文件。

   Reader体系中可以操作文件的对象时FileReader

   是否需要提高效率:是!加入Reader体系中的缓冲区 BufferedReader

   FileReader fr = newFileReader("a.txt");

   BufferedReader bufr = new BufferedReader(fr);

目的:OutputStream Writer

   目的是否是纯文本。

   是:Writer。

   设备:硬盘,一个文件。

   Writer体系中key操作文件的对象是FileWriter

   是否需要提高效率:是!加入Reader体系中的缓冲区 BufferedWriter

   FileWriter fw = new FileWriter("b.txt");

   BufferedWriter bufw = new BufferedWriter(fw);

-----------------------------------------------------------------------

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

这个需求中有源和目的都存在。

那么分别分析:

源:InputSteam  Reader

   是不是纯文本?是!Reader

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

   不是选择Reader吗?System.in对应的不是字节流吗?

   为了操作键盘的文本数据方便,转成字符流按照字符串操作时最方便的。

   所以既然明确了Reader,那么就将System.in转换成Reader。

   用Reader体系中转换流,InputStreamReader

   InputStreamReader isr = new InputStreamReader(System.in);

   需要提高效率吗?需要!BufferedReader

   BufferedReader bufr = new BufferedReader(isr);

目的:

   OutputStream   Writer

   是否是纯文本?是Writer。

   设备:硬盘。一个文件。FileWriter

   FileWriter  fw = new FileWriter("c.txt");

   需要提高效率吗?需要:

   BufferedWriter bufw = new BufferedWriter(fw);



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

目的:

   OutputStream   Writer

   是否是纯文本?是Writer。

   设备:硬盘。一个文件。FileWriter

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

   但是存储时,需要加入指定编码表。而制定的编码表只有转换流可以指定。

   所以要使用的对象时OutputStreamWriter。

   而该转换流对象要接收一个字节输出流。而且还可以操作的文件的字节输出流。FileOutputStream

   OutputStreamWriter osw =

                new OutputStreamWriter(new fileOutputStream("d.txt"),"UTF-8");

   需要高效吗?需要。

   BufferedWriter bufw = new BufferedWriter(osw);

   所以,记住:转换流什么时候使用,字符和字节之间桥梁,通常,涉及到字符编码转换时,

需要用到转换流。

---------------------------------------------------------------------

File类

   用来将文件或者文件夹封装成对象

   方便对文件与文件夹的属性信息进行操作 。

   File对象可以作为参数传递给流的构造函数。

   File类常见方法:

       1,创建。

           boolean createNewFile();在指定位置创建文件,如果该文件已经存在,则不创建,返回false。和输出流不一样,输出流对象一建立就创建文件。而且文件已经存在,会覆盖.

           boolean mkdir();只能创建一级目录。

           boolean mkdirs() ;创建多级目录。

       2,删除。

           boolean delete(); 删除失败返回false

           void deleteOnExit(); 在程序退出时删除指定文件。

       3,判断。

           boolean canExecute();是否可执行。

           boolean exists();文件是否存在。

           boolean isDirectory() ;判断是否是目录。

           boolean isFile();是否是文件。

           boolean isAbsolute() ;判断是否是绝对路径。

           boolean isHidden() 判断是否是隐藏。

       4,获取信息。

           String getName()  获取名称。

           String getParent()  

           String getPath()

           String getAbsolutePath()

           long lastModified() //获取最后一次修改的时间。

           boolean renameTo(File dest)// 重新命名此抽象路径名表示的文件。

       

-------------------------------------------------------------------------

递归:

函数自己调用自己。

注意:递归时一定要明确结束条件。

应用场景:当某一功能要重复使用时。


列出指定目录下文件或者文件夹,包含子目录中的内容。也就是列出指定目录下所有内容。

因为目录中还有目录,只要使用同一个列出目录功能的函数完成即可。在列出过程中出现的还是目录的话,还可以再次调用本功能。也就是函数自身调用自身。这种表现形式,或者编程手法,称为递归。

递归要注意:

1,限定条件。

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

import java.io.*;
class FileDemo3
{
    public static void main(String[] args)
    {
        File dir = new File("e:\\java_class");
        showDir(dir,0);
                                                                                                                                                                                                                                                               
    }
    public static String getLevel(int level)
    {
        StringBuilder sb = new StringBuilder();
        sb.append("|--");
        for (int x=0;x<level ;x++ )
        {
            //sb.append("|--");
            sb.insert(0,"  ");
        }
        return sb.toString();
    }
                                                                                                                                                                                                                                                           
    public static void showDir(File dir,int level)
    {
        System.out.println(getLevel(level)+dir.getName());
        level++;
        File[] files = dir.listFiles();
        for (int x=0;x<files.length ;x++ )
        {
            if (files[x].isDirectory())
                showDir(files[x],level);
            else
                System.out.println(getLevel(level)+files[x]);
        }
    }
    public static int getSum(int n)
    {
        if(n==1)
            return 1;
        return n+getSum(n-1);
    }
}

 

--------------------------------------------------------------------------

Properties类:

Properties是hashtable的子类。

   也就是说他具备map集合的特点。而且他里面存储的键值对都是字符串。是集合中和IO技术结合的集合容器。

该对象的特点:可以用于键值对形式的配置文件。那么在加载数据时,需要数据有固定格式:键=值。

    特点:

    1:可以持久化存储数据。

    2:键值都是字符串。

    3:一般用于配置文件。

load():将流中的数据加载进集合。

   原理:其实就是将读取流和指定文件相关联,并读取一行数据,因为数据是规则的key=value,所以获取一行后,通过 = 对该行数据进行切割,左边就是键,右边就是值,将键、值存储到properties集合中。

store():写入各个项后,刷新输出流。

list():将集合的键值数据列出到指定的目的地。

--------------------------------------------------------

打印流

   PrintWriter与PrintStream 可以直接操作输入流和文件。该流提供了打印方法,可以将各种数据类型的数据都原样打印。

   字节打印流:PrintStream  

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

       1,file对象。File

       2,字符串路径。String

       3,字节输出流。OutputStream

   字符打印流:PrintWriter

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

       1,file对象。File

       2,字符串路径。String

       3,字节输出流。OutputStream

       4,字符输出流,Writer。

---------------------------------------------------------------------------

序列流  

   SequenceInputStream 对多个流进行合并。它的构造函数接收的是两个输入流,或者是接收一个输入流的枚举。比如说要把三个文件的内容复制到另一个文件中,要一个一个的重复Copy文件的动作,就是每复制一个文件都要创建一个写入流和输出流,然后再进行读写操作,复制三个文件就要重复三次,并且创建目的文件的时候还要写成可续写格式,不然就会被覆盖掉,而序列流就解决了这个问题,他可以把多个流合并在一起,然后再进行其他操作。


 

import java.io.*;
import java.util.*;
class SequenceDemo
{
    public static void main(String[] args) throws IOException
    {
        Vector<FileInputStream> v = new Vector<FileInputStream>();
        v.add(new FileInputStream("c.\\1.txt"));
        v.add(new FileInputStream("c.\\2.txt"));
        v.add(new FileInputStream("c.\\3.txt"));
        Enumeration<FileInputStream> en = v.elements();
        SequenceInputStream sis = new SequenceInputStream(en);
        FileOutputStream fos = new FileOutputStream("4.txt");
        byte[] buf = new byte[1024];
        int len = 0;
        while ((len=sis.read(buf))!=-1)
        {
            fos.write(buf,0,len);
        }
        fos.close();
        sis.close();
    }
}

--------------------------------------------------------------

操作对象的流

     ObjectInputStream与ObjectOutputStream 被操作的对象需要实现Serializable (标记接口);

     流是可以操作数据的,现在数据被封装成对象了,因为对象是存放在堆内存中的,用完之后就成了垃圾,一被清理之后这个对象就不存在了。而现在这个流就可以操作对象,把这个对象的数据存放在硬盘上,对象里都封装了一些数据,那这些数据就随着对象也存到了硬盘上了。这样即使这个程序结束了对象被清理了,仍然能够获得这个对象里的数据,因为它已经被存放到硬盘上了。另外操作的那个对象的类要能被序列化,某个类要想能被序列化,就得实现java.io.Serializable 接口。


import java.io.*;
class ObjectStreamDemo
{
    public static void main(String[] args) throws Exception
    {
        //writeObj();//写入到硬盘上的obj文件中。
        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(new FileOutputStream("obj.txt"));
        oos.writeObject(new Person("lisi",39,"kr"));
        oos.close();
    }
}

--------------------------------------------------------------------------------------

管道流

   PipedInputStream和PipedOutputStream

   输入输出可以直接进行连接,通过结合线程使用。

   管道输入流应该连接到管道输出流;管道输入流提供要写入管道输出流的所有数据字节。通常,数据由某个线程从 PipedInputStream 对象读取,并由其他线程将其写入到相应的 PipedOutputStream。不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。

import java.io.*;
class Read implements Runnable
{
    private PipedInputStream in;
    Read(PipedInputStream in)
    {
        this.in = in;
    }
    public void run()
    {
        try
        {
            byte[] buf = new byte[1024];
            int len = in.read(buf);
            String s= new String(buf,0,len);
            System.out.println(s);
        }
        catch (IOException e)
        {
            throw new RuntimeException("读取管道流失败。");
        }
    }
                                                                 
}
class Write implements Runnable
{
    private PipedOutputStream out;
    Write(PipedOutputStream out)
    {
        this.out = out;
    }
    public void run()
    {
        try
        {
            out.write("Piped来了".getBytes());
            out.close();
        }
        catch (IOException e)
        {
            throw new RuntimeException("读取管道流失败。");
        }
    }
}
class PipedStreamDemo
{
    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 huoq 指针位置。

   同时可以通过seek改变指的位置。

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

   通过构造函数可以看出,该类只能操作文件。

   而且操作文件还有模式。只读r,读写rw等。

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

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

   而且该对象的构造函数要操作的文件不存在,会自动创建。如果存在则不会覆盖。

 

   java语言提供了一个RandomAccessFile类来处理对一部分文件的输入/输出。RandomAccessFile类创建的流于前面的输入输出流不一样,它既不是输入流InputStream的子类也不是输出流类OutputStream的子类,但是RandomAccessFile类创建的流的指向既可以做为源也可以作为目的,换句话说如果相对一个文件进行读取操作时,可以创建一个指向该文件的RandomAccessFile流,这样既可以从这个流中读取文件的数据,也可以从这个流中写入数据到文件。

-----------------------------------------------------------------

字符编码

 字符流的出现为了方便操作字符。更重要是的加入了编码转换。

 通过子类转换流来完成。 InputStreamReader OutputStreamWriter

 在两个对象进行构造的时候可以加入字符 集。

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

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

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

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

import java.io.*;
class Read implements Runnable
{
    private PipedInputStream in;
    Read(PipedInputStream in)
    {
        this.in = in;
    }
    public void run()
    {
        try
        {
            byte[] buf = new byte[1024];
            int len = in.read(buf);
            String s= new String(buf,0,len);
            System.out.println(s);
        }
        catch (IOException e)
        {
            throw new RuntimeException("读取管道流失败。");
        }
    }
      
}
class Write implements Runnable
{
    private PipedOutputStream out;
    Write(PipedOutputStream out)
    {
        this.out = out;
    }
    public void run()
    {
        try
        {
            out.write("Piped来了".getBytes());
            out.close();
        }
        catch (IOException e)
        {
            throw new RuntimeException("读取管道流失败。");
        }
    }
}
class PipedStreamDemo
{
    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();
    }
}


本文出自 “离歌丶D” 博客,转载请与作者联系!