黑马程序员---IO(输入输出)

----------------------ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------

1.概述:

    在java中,通过不同输入输出设备(键盘,内存,显示器,网络等)之间的数据传输抽象表述为流,程序允许通过流的方式与输入输出设备进行数据传输。java中的"流"都位于java.io包中,称为IO(输入输出)流。
    IO流有很多种,按照操作数据的不同,可以分为字节流和字符流。在IO包中,字节流的输入输出流分别用java.io.InputStream和java.io.OutputStream表示。
    字符流的输入输出流分别用java.io.Reader和java.io.Writer表示。

                                                            
2.字节流
    a.IO流中针对字节的输入输出提供了一系列的流,统称为字节流。字节流是程序中最常用的流,根据数据的传输方向可将其分为字节输入流和字节输出流。在JDK中,提供了两个抽象类InputStream和OutputStream,它们是字节流的顶级父类,所有的字节输入流都继承自InputStream,所有的字节输出流都继承自OutputStream.
                                                                            InputStream常用方法
        ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
                        方法声明                                                            功能描述
            int read()                                           从输入流读取一个8位的字节,把它转换为0-255之间的整数,并返回这一整数                                            
            int read(byte[] b)                                从输入流读取若干字节,把它们保存到参数b指定的字节数组中,返回的整数表示读取的字节数
            int read(byte[] b,int off,int len)              从输入流读取若干字节,把它们保存到参数b指定的字节数组中,off指定字节数组开始保存数
                                                                          据的起始下标,len表示读取的字节数目。
            void close()                                        关闭此输入流并释放与该流关联的所有系统资源
        -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

        注:前三个read()方法都是用来读取数据的,其中,第一个read()方法是从输入流中逐个读入字节,而第二个和第三个read()方法则将若干字节以字节数组的形式一次性读入,从而提高数据读取效率。
                                                            OutputStream常用方法
        ----------------------------------------------------------------------------------------------------------------------------------------------------------------------
                        方法声明                                        功能描述
            int write(int b)                                     向输出流写入一个字节                                            
            int write(byte[] b)                                把参数b指定的字节数组的所有字节写到输出流
            int write(byte[] b,int off,int len)              将指定byte数组中从偏移量off开始的len个字节写入输出流
            void flush()                                         刷新此输出流并强制写出所有缓冲的输出字节。
            void close()                                        关闭此输出流并释放与该流关联的所有系统资源
        ------------------------------------------------------------------------------------------------------------------------------------------------------------------------

        注:前三个是重载的write()方法,都是用来向输出流写入字节,其中,第一个write()方法是逐个写入字节,后两个方法则是将若干字节以字节数组的形式一次性写入,从而提高写数据效率。flush()方法用来将当前输出流缓冲区(通常是字节数组)中的数据强制写入目标设备,此过程称为刷新。
        InputStream和OutputStream这两个类虽然提供了一系列和读写数据有关的方法,但是这两个类是抽象类,不能被实例化,因此,针对不同的功能,InputStream和OutputStream提供了不同的子类。
            

                                                                     InputStream      OutputStream     子类结构图


                                                   
    b.字节流读写文件
        针对文件的读写,JDK专门提供了两个类,分别是FileInputStream和FileOutputStream。FileInputStream是InputStream的子类,它是操作文件的字节输入流,专门用于读取文件中的数据。由于从文件读取数据是重复的操作,因此需要通过循环语句来实现数组的持续读取。
        Demo:在项目根目录下(包之外)创建src.txt文件,并写入itcast

public class FileInputStreamDemo {
    public static void main(String[] args) throws Exception {
        InputStream in = new FileInputStream("src.txt");
        int b = 0;
        while((b = in.read()) != -1){
            System.out.println(b);
        }
        in.close();
    }
}
        注:输出的数据位数字,通常情况下读取文件应该是输出字符,之所以输出数字是因为硬盘上的文件是以字节的形式存在的,最终显示的就是文件的六个字节所对应的十进制数。
        与FileInputStream对应的是FileOutputStream。FileOutputStream是OutputStream的子类,它是操作文件的字节输出流,专门用于把数据写入文件。
        Demo:

public class FileOutputStreamDemo {
    public static void main(String[] args) throws Exception {
        OutputStream out = new FileOutputStream("dest.txt");
        String str = "itcase___韩旭辉";
        byte[] b = str.getBytes();
        for(int i=0; i<b.length; i++){
            out.write(b[i]);
        }
        out.close();
    }
}
        注:如果通过FileOutputStream向一个已存在的文件中写入数据,那么该文件中的数据首先会被清空,再写入新的数据。若希望在已存在的文件内容之后追加新内容,则可以使用FileOutputStream的构造方法FileOutputStream(String fileName,boolean append)来创建文件输出流对象。
        Demo:

public class FileOutputStreamDemo {
    public static void main(String[] args) throws Exception {
        OutputStream out = new FileOutputStream("dest.txt",true);
        String str = "itcase___韩旭辉------";
        byte[] b = str.getBytes();
        for(int i=0; i<b.length; i++){
            out.write(b[i]);
        }
        out.close();
    }
}
    c.文件的拷贝
        文件的拷贝需要输入流来读取文件中的数据,通过输出流将数据写入文件。
        Demo:

public class FileCopyDemo {
    public static void main(String[] args) throws Exception {
        InputStream in = new FileInputStream("Source/7_Collection.txt");
        OutputStream out = new FileOutputStream("Target/target.txt");
        int len;
        long start = System.currentTimeMillis();
        while((len = in.read()) != -1){
            out.write(len);
        }
        long end = System.currentTimeMillis();
        System.out.println("花费时间:"+(end - start) + "ms");
        in.close();
        out.close();
    }
}
    d.字节流缓冲区
        上例中实现了文件的拷贝,但是一个字节一个字节的读写,需要频繁的操作文件,效率非常低。当通过流的方式拷贝文件时,为了提高效率可以定义一个字节数组作为缓冲区。在拷贝文件时,可以一次性的读取多个字节的数据,并保存在字节数组中,然后将字节数组中的数据一次性写入文件。
        Demo:

public class FileCopyDemo {
    public static void main(String[] args) throws Exception {
        InputStream in = new FileInputStream("Source/7_Collection.txt");
        OutputStream out = new FileOutputStream("Target/target.txt");
        byte[] buffer = new byte[1024];
        long start = System.currentTimeMillis();
        int len;        
        while((len = in.read(buffer)) != -1){        //一次读1024个字节
            out.write(buffer,0,len);                //从第一个字节开始,向文件中写入len个字节
        }
        long end = System.currentTimeMillis();
        System.out.println("花费时间:"+(end - start) + "ms");
        in.close();
        out.close();
    }
}
    e.装饰设计模式
        装饰设计模式就是通过包装一个类,动态的为它增加功能的一种设计模式。
        Demo:

public class DecotareDesignerPatternDemo {
    public static void main(String[] args) {
        Car benz = new Car("benz");
        System.out.println("-----------------包装前--------------------");
        benz.show();
        RadarCar decoratedCar_benz = new RadarCar(benz);
        System.out.println("-----------------包装后--------------------");
        decoratedCar_benz.show();
    }
}

class Car{
    private String name;
    public Car(String name){
        this.name = name;
    }
    public void show(){
        System.out.println("我是"+name+",具有基本功能");
    }
}

class RadarCar{
    public Car myCar;
    public RadarCar(Car myCar){
        this.myCar = myCar;
    }
    public void show(){
        myCar.show();
        System.out.println("具有倒车雷达功能");
    }
}
    f.字节缓冲流
        在IO包中提供了两个带缓冲的字节流,分别是BufferedInputStream和BufferedOutputStream,这两个流都使用了装饰设计模式。他们的构造方法中分别接收InputStream和OutputStream类型的参数作为被包装的对象,在读写数据时提供缓冲功能。
        Demo:

public class BufferedIn_OutputStreamDemo {
    public static void main(String[] args) throws Exception {
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("src.txt"));
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("dest.txt"));
        int len;
        while((len = bis.read()) != -1){        //一次读一个字节
            bos.write(len);                        //每次将一个字节写入bos定义的数组中,当bos定义的数组满了之后一次性写入
        }
        bis.close();
        bos.close();
    }
}
        注:BufferedInputStream和BufferedOutputStream这两个流内部都定义了一个大小为8192的字节数组,当调用read()或者write()方法读写数据时,首先将读写的数据存入定义好的字节数组,然后将字节数组的数据一次性读写到文件中。
    
3.字符流
    a.同字节流一样,字符流也有两个抽象的顶级父类,分别是Reader和Writer。其中Reader是字符输入流,用于从某个源设备读取字符,Writer是字符输出流,用于向目标设备写入字符。Reader和Writer作为字符的顶级父类,也有许多子类。
        
                                                                           Reader  Writer子类结构图

                                                        
        注:其中FileReader和FileWriter用于读写文件,BufferedReader和BufferedWriter是具有缓冲功能的流,他们可以提高读写效率。
    b.字符流操作文件
        1)在程序开发中,经常需要对文本文件的内容进行读取,如果想从文件中直接读取字符便可以使用字符输入流FileReader,通过此流可以从关联的文件中读取一个或一组字符。
            Demo:

public class FileReaderDemo {
    public static void main(String[] args) throws Exception {
        Reader reader = new FileReader("src.txt");
        int ch;
        while((ch = reader.read()) != -1){
            System.out.print((char)ch);
        }
        reader.close();
    }
}
            注:while循环每次从文件中读取一个字符并打印。字符输入流的read()方法返回的是int类型的值,如果想获得字符就需要进行强制转换。
        2)FileWriter是Writer是Writer的一个子类,用于向文件中写入字符。
            Demo:

public class FileWriterDemo {
    public static void main(String[] args) throws Exception {
        Writer writer = new FileWriter("dest.txt");
        String str = "我是韩旭辉,很高兴见到你。。。";
        writer.write(str);
        writer.write("\n\r");
        writer.close();
    }
}
             注:FileWriter同FileOutputStream一样,如果指定的文件不存在,就会先创建文件,在写入数据;如果文件存在,则会首先清空文件中的内容,在进行写入。如果想在文件末尾追加数据,需要调用重载的构造方法。
            Demo:

public class FileWriterDemo {
    public static void main(String[] args) throws Exception {
        Writer writer = new FileWriter("dest.txt",true);
        String str = "我是韩旭辉,很高兴见到你。。。";
        writer.write(str);
        writer.write("\n\r");
        writer.close();
    }
}
        3)字符缓冲流
            字符流提供了带缓冲区的包装流,分别是BufferedReader和BufferedWriter,其中BufferedReader用于对字符输入流进行包装,BufferedWriter用于对字符输出流进行包装。需要注意的是,在BufferedReader中有一个重要的方法readLine(),该方法用于一次读取一行文本。
            Demo:

public class BufferedReader_WriterDemo {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new FileReader("src.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("dest.txt"));
        String str;
        while((str = br.readLine()) != null){
            bw.write(str);
            bw.newLine();
        }
        br.close();
        bw.close();
    }
}
            注:由于包装流内部使用了缓冲区,在循环中调用BufferedWriter的write()方法写字符时,这些字符首先会被写入缓冲区,当缓冲区写满时或调用close()方法时,缓冲区中的字符才会被写入目标文件。因此在循环结束时一定要调用close()方法,否则极有可能会导致部分存在缓冲区中的数据没有被写入目标文件。
    c.LineNumberReader
        JDK提供了一个可以跟踪行号的输入流LineNunberReader,他是BufferedReader的直接子类。
        Demo:

public class LineNumberReaderDemo {
    public static void main(String[] args) throws Exception {
        LineNumberReader lnr = new LineNumberReader(new FileReader("src.txt"));
        BufferedWriter bw = new BufferedWriter(new FileWriter("dest.txt"));
        String str;
        while((str = lnr.readLine()) != null){
            bw.write(lnr.getLineNumber()+ ": " +str);
            bw.newLine();
        }
        lnr.close();
        bw.close();
    }
}
    d.转换流
        JDK中提供了两个类可以将字节流转换为字符流,他们分别是InputStreamReader和OutputStreamWriter。
        转换流也是一种包装流,其中OutputStreamWriter是Writer的子类,他可以将一个字节输出流包装成字符输出流,方便直接写入字符,而InputStreamReader是Reader的子类,它可以将一个字节输入流包装成字符输入流,方便直接读取字符。
        Demo:

public class SwitchStreamDemo {
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("src.txt")));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("dest.txt")));
        String str;
        while((str = br.readLine()) != null){
            bw.write(str);
            bw.newLine();
        }
        br.close();
        bw.close();
    }
}
         注:InputStreamReader是一个中转器,将字节流装换为字符流,在new对象时,执行从内到外的封装顺序:字节流读入对象->装换器对象->字符流读入对象在使用转换流时,只能针对文本文件的字节流进行转换,如果字节流操作的是一张图片,此时转换为字符流就会造成数据丢失。
            
4.其他IO流
    a.ObjectInputSteam和ObjectOutputStream
    b.DataInputStream和DataOutputStream
    c.PrintStream
        在IO包中提供了一个PrintStream类,它提供了一系列用于打印数据的print()和println()方法,被称作打印流。
    d.标准输入输出流
        1)在System类总定义了三个常量,in、out和err,他们被习惯性的称为标准输入输出流。其中,in为InputStream类型,它是标准输入流,默认情况下用于读取键盘输入的数据;out为PrintStream类型,它是标准输出流,默认将数据输出到命令行窗口;err也是PrintStream类型,它是标准错误流,它和out一样也是将数据输出到控制台。不同的是,err通常输出的是应用程序运行时的错误信息。
        2)有时候,程序会向命令行窗口输出大量的数据,由于输出数据滚动的太快,会导致无法阅读,这是可以将标准输出流重定向到其他的输出设备,例如输出到一个文件中。在System类中定义了一些静态方法,这些静态方法允许对标准输入流和输出流进行重定向。

                                                                重定向流常用的静态方法
            -------------------------------------------------------------------------------------------------------------------------------------
                                方法声明                                                 功能描述
                void setIn(InputStream in)                            对标准输入流重定向
                void setOut(PrintStream out)                        对标准输出流重定向
                void setErr(PrintStream out)                        对标准错误输出流重定向
            -------------------------------------------------------------------------------------------------------------------------------------
             Demo:
public class PrintStreamDemo {
    public static void main(String[] args) throws Exception {
        System.setIn(new FileInputStream("src.txt"));
        System.setOut(new PrintStream("dest.txt"));
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String str;
        while((str = br.readLine()) != null){
            System.out.println(str);
        }
    }
}
     e.PipedInputStream和PipedOutputStream
        JDK提供了一种管道流,管道流分为管道输入流(PipedInputStream)和管道输出流(PipedOutputStream),它是一种比较特殊的流,必须先建立连接才能进行彼此间通信。PipedInputStream用于想管道中写入数据,PipedOutputStream用于从管道中读取写入的数据。
    f.ByteArrayInputStream和ByteArrayOutputStream
        ByteArrayOutputStream类会在创建对象时就创建一个byte型数组的缓冲区,当向数组中写数据时,该对象会把所有的数据先写入缓冲区,最后一次性写入文件。
    g.CharArrayReader和CharArrayWriter
        将字符型数据临时存入缓冲区中。
    h.SequenceinputStream
        SequenceinputStream类可以将几个输入流串联在一起,合并为一个输入流。

5.File类
    a.JDK中提供了一个File类,该类封装了一个路径,并提供了一系列的方法用于操作该路径所指向的文件。
    b.File类常用方法
        File类用于封装一个路径,这个路径可以从系统盘符开始的绝对路径也可以是相对于当前目录而言的相对路径。File类内部封装的路径可以指向一个文件,也可以指向一个目录,在File类中提供了针对这些文件或目录的一些常规操作。

                                                            File类常用的构造方法
        --------------------------------------------------------------------------------------------------------------------------------------
                                方法声明                                                功能描述
            File(String pathname)                      通过指定的一个字符串类型的文件路径来创建一个新的File对象
            File(String parent,String child)        根据指定的一个字符串类型的父路径和一个字符串类型的子路径(包括文件名)创建一个File对象
            File(File parent,String child)            根据指定的File类的父路径和字符串类型的子路径(包括文件名)创建一个File对象
        --------------------------------------------------------------------------------------------------------------------------------------
         File类中提供了一系列的方法,用于操作其内部封装的路径指向的文件或者目录。
                                                            File类的常用方法
        --------------------------------------------------------------------------------------------------------------------------------------
                方法声明                                               功能描述
            boolean exists()                           判断File对象对应的文件或目录是否存在
            boolean delete()                           删除File对象对应的文件或目录
            boolean createNewFile()            当File对象对应的文件不存在时,该方法将创建一个此File对象所指定的新文件
            String getName()                          返回File对象表示的文件或文件夹名称
            String getPath()                             返回File对象对应的路径
            String getAbsolutePath()            返回File对象对应的绝对路径
            String getParent()                        返回File对象对应目录的父目录
            boolean canRead()                    判断File对象对应的文件或目录是否可读
            boolean canWrite()                     判断File对象对应的文件或目录是否可写
            boolean isFile()                           判断File对象对应的是否是文件
            boolean isDirectory()                  判断File对象对应的是否是目录
            boolean isAbsolute()                  判断File对象对应的文件或目录是否是绝对路径
            long lastModified()                      返回1970年1月1日0分0秒到文件最后修改时间的毫秒值
            long length()                                返回文件内容的长度
            String[] list()                                 列出指定目录的全部内容,只列出名称
            File[] listFiles()                            返回一个包含了File对象所有子文件和子目录的File数组
        --------------------------------------------------------------------------------------------------------------------------------------
     c.遍历目录下的文件
        File类有一个方法list()用于遍历某个指定目录下所有文件的名称。
        Demo:

public class FileDemo {
    public static void main(String[] args) {
        File file = new File("E:\\black_horse\\base_class");
        if(file.isDirectory()){
            String[] strs = file.list();
            for(String str : strs){
                System.out.println(str);
            }
        }
    }
}

        遍历目录下所有的文件,有时程序只是需要得到指定类型的文件,如获得指定目录下所有的".java"文件,该方法接受一个FilenameFilter类型的参数。

        FilenameFilter是一个接口,被称为文件过滤器,当中定义了一个抽象方法accept(File dir,String name),在调用list()方法时,需要实现文件过滤器,在accept()方法中作出判断,从而获得指定类型的文件。

        Demo:
public class FilenameFilterDemo {
    public static void main(String[] args) {
        File file = new File("E:\\black_horse\\base_class\\class_info");
        FilenameFilter filter = new FilenameFilter(){                //匿名内部类
            public boolean accept(File dir, String name) {
                File currFile = new File(dir,name);
                if(currFile.isFile()&&name.endsWith(".doc")){
                    return true;
                }else{
                    return false;
                }
            }            
        };
        String[] strs = file.list(filter);
        for(String str : strs){
            System.out.println(str);
        }
    }
}
         如果想得到所有子目录下的File类型对象,这是list()方法显然不能满足要求,这是需要使用File类提供的另一个方法listFiles()。此方法返回一个File对象数组,当对数组中的元素进行遍历时,如果元素还有子目录需要遍历,则需要使用递归。
        Demo:

public class ListFilesDemo {
    public static void main(String[] args) {
        File file = new File("E:\\black_horse\\base_class\\class_info");
        showAllFilesInfo(file);
    }
    private static void showAllFilesInfo(File file) {
        File[] files = file.listFiles();
        for(File f : files){
            if(f.isDirectory()){
                showAllFilesInfo(f);
            }else{
                System.out.println(f.getName());
            }
        }
    }
}
     d.删除文件及目录
        File类的delete()方法只是删除一个指定的文件,加入File对象代表目录,并且目录下包含子目录或文件,File类的delete()方法不允许对这个目录直接删除。这是我们可以通过递归的方式将整个目录以及其中的文件全部删除。
        Demo:

public class DeleteFileDirectoryDemo {
    public static void main(String[] args) {
        File file = new File("E:\\black_horse\\base_class\\delete_test_file\\113");
        deleteDir(file);
    }
    private static void deleteDir(File file) {
        File[] files = file.listFiles();
        for(File f : files){
            if(f.isDirectory()){
                deleteDir(f);
            }
            f.delete();
        }
    }
}
           
6.RandomAccessFile类
    在IO包中,提供了一个类RandomAccessFile,它不属于流类,但具有读写文件数据的功能,可以随机的从文件的任何位置开始执行读写数据的操作。
    RandomAccessFile可以将文件已只读或者读写的方式打开,具体使用哪种方式,取决于创建它所采用的构造方法。

                                                    RandomAccessFile的构造方法
    -----------------------------------------------------------------------------------------------------------------------------------
                        方法声明                                                                           功能描述
        RandomAccessFile(File file,String mode)                         参数file指定被访问的文件
        RandomAccessFile(String naem,String mode)                参数name指定被访问文件的路径
    -----------------------------------------------------------------------------------------------------------------------------------
    参数mode有四个值,最常用的有两个,分别是"r"和"rw",其中"r"表示已只读的方式打开文件,"rw"表示以读写的方法打开文件,如果文件不存在,则会自动创建该文件。
    RandomAccessFile类针对文件的随机访问操作,提供了一些用于定位文件位置的方法。

                                                    RandomAccessFile定位文件位置的方法
    -------------------------------------------------------------------------------------------------------------------------------------
                        方法声明                                    功能描述
        long getFilePointer()                        返回当前读写指针所处的位置
        void seek(long pos)                            设定读写指针的位置,与文件开头相隔pos个字节数
        int skipBytes(int n)                        使读写指针从当前位置开始,跳过n个字节
        void setLength(long newLength)                设置此文件的长度
    -------------------------------------------------------------------------------------------------------------------------------------
    Demo:
public class RandomAccessFileDemo {
    public static void main(String[] args) throws Exception {
        RandomAccessFile raf = new RandomAccessFile("time.txt","rw");
        int times = 0;
        times = Integer.parseInt(raf.readLine());
        if(times > 0){
            System.out.println("您还可以试用"+times--+"次!");
            raf.seek(0);
            raf.writeBytes(times+"");
        }else{
            System.out.println("软件试用次数已到!");
        }
        raf.close();
    }
}

----------------------ASP.Net+Unity开发.Net培训、期待与您交流! ----------------------


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值