Java编程那些事儿7_io

Java编程那些事儿84——IO简介

I/O处理技术是Java语言中实现文件操作、内存操作、控制台输入以及网络编程的基础,但是由于I/O技术本身的设计原因,也使得I/O处理技术的难度比较大,需要花费比较大的精力进行学习。

  11.1 I/O简介

在程序中,输入和输出都是相对于当前程序而言的,例如从硬盘上读取一个配置文件的内容到程序中,则相当于将文件的内容输入到程序内部,因此输入和对应,而将程序中的内容保存到硬盘上,则相当于将文件的内容输出到程序外部,因此输出和对应。熟悉输入和输出的对应关系,将有助于后续内容的学习。

Java语言中,输入和输出的概念要比其它语言的输入和输出的概念涵盖的内容广泛得多,不仅包含文件的读写,也包含网络数据的发送,甚至内存数据的读写以及控制台数据的接收等都由IO来完成。

    为了使输入和输出的结构保持统一,从而方便程序员使用IO相关的类,在Java语言的IO类设计中引入了一个新的概念——Stream(流)

    由于在进行IO操作时,需要操作的种类很多,例如文件、内存和网络连接等,这些都被称作数据源(data source),对于不同的数据源处理的方式是不一样的,如果直接交给程序员进行处理,对于程序员来说则显得比较复杂。

    所以在所有的IO类设计时,在读数据时,JDK API将数据源的数据转换为一种固定的数据序列,在写数据时,将需要写的数据以一定的格式写入到数据序列,由JDK API完成将数据序列中的数据写入到对应的数据源中。这样由系统完成复杂的数据转换以及不同数据源之间的不同的变换,从而简化程序员的编码。

    IO的这种设计就和城市中的供水和排水系统设计是一样的,在供水的时候,水源有江河水、湖水和地下水等不同类型,由自来水公司完成把水源转换为对应的水流。而在排水系统设计时,只需要将污水排入污水管道即可,至于这些污水是怎么被处理的,则不需要关心,这样也简化了家庭用水的处理。

    IO设计中这种数据序列被形象的称作流(Stream。通过使用流的概念,使程序员面对不同的数据源时只需要建立不同的流即可,而底层流实现的复杂性则由系统完成,从而使程序员不必深入的了解每种数据源的读写方式,从而降低了IO编程的复杂度。

    在整个IO处理中,读数据的过程分为两个步骤:1、将数据源的内容转换为流结构,该步骤由JDK API完成,程序员只需要选择合适的流类型即可。2、从流中读取数据,该步骤由程序员完成,流中数据的顺序和数据源中数据的存储顺序保持一致。

    写数据的过程也分为两个步骤:1、为连接指定的数据源而建立的专门的流结构,该步骤由JDK API完成,程序员只需要选择合适的流类型即可。2、将数据以一定的格式写入到流中,该步骤由程序员完成,写入流中的数据的顺序就是数据在数据源中的存储顺序。最后,当数据写入流中以后,可以通过一定的方式把流中的数据写入数据源,或者当流被关闭时,系统会自动将流中的数据写入数据源中。

    这样,在整个IO类设计时,将最复杂的和数据源操作的部分由JDK API进行完成,而程序员进行编程时,只需要选择合适的流类型,然后进行读写即可。

和现实的结构一样,IO中的流也是有方向的,用于读的流被称作输入流(Input Stream),用于写的流被称作输出流(Output Stream。则进行读写的时候需要选择合适的流对象进行操作。

Write写入文件 ,写入到流中

Read读出文件,从流中写出来。

    而由于Java语言使用面向对象技术,所以在实现时,每个流类型都使用专门的类进行代表,而把读或写该类型数据源的逻辑封装在类的内部,在程序员实际使用时创建对应的对象就完成了流的构造,后续的IO操作则只需要读或写流对象内部的数据即可。这样IO操作对于Java程序员来说,就显得比较简单,而且比较容易操作了。

 总结:1.输入/输出是相对于程序来说的。

2. IO的这种设计就和城市中的供水和排水系统设计是一样的,在供水的时候,水源有江河水、湖水和地下水等不同类型,由自来水公司完成把水源转换为对应的水流。而在排水系统设计时,只需要将污水排入污水管道即可,至于这些污水是怎么被处理的,则不需要关心,这样也简化了家庭用水的处理。

3.流的概念。

Java编程那些事儿85——IO类体系

 11.2 I/O类体系

    JDK API中,基础的IO类都位于java.io,而新实现的IO类则位于一系列以java.nio开头的包名中,这里首先介绍java.io包中类的体系结构。

    按照前面的说明,流是有方向的,则整个流的结构按照流的方向可以划分为两类:

    1、输入流:

    该类流将外部数据源的数据转换为流,程序通过读取该类流中的数据,完成对于外部数据源中数据的读入。

    2、输出流:

    该类流完成将流中的数据转换到对应的数据源中,程序通过向该类流中写入数据,完成将数据写入到对应的外部数据源中。

    而在实际实现时,由于JDK API历史的原因,在java.io包中又实现了两类流:字节流byte stream)和字符流(char stream)。这两种流实现的是流中数据序列的单位,在字节流中,数据序列以byte为单位,也就是流中的数据按照一个byte一个byte的顺序实现成流,对于该类流操作的基本单位是一个byte,而对于字节流,数据序列以char为单位,也就是流中的数据按照一个char一个插入的顺序实现成流,对于该类流操作的基本单位是一个char.

    另外字节流是从JDK1.0开始加入到API中的,而字符流则是从JDK1.1开始才加入到API中的,对于现在使用的JDK版本来说,这两类流都包含在API的内部。在实际使用时,字符流的效率要比字节流高一些。

    在实际使用时,字符流中的类基本上和字节流中的类对应,所以在开始学习IO类时,可以从最基础的字节流开始学习。

    SUN设计JDKIO类时,按照以上的分类,为每个系列的类设计了一个父类而实现具体操作的类都作为该系列类的子类,则IO类设计时的四个体系中每个体系中对应的父类分别是:

    11.2.1 字节输入流InputStream

    该类是IO编程中所有字节输入流的父类,熟悉该类的使用将对使用字节输入流产生很大的帮助,下面做一下详细的介绍。

    按照前面介绍的流的概念,字节输入流完成的是按照字节形式构造读取数据的输入流的结构,每个该类的对象就是一个实际的输入流,在构造时由API完成将外部数据源转换为流对象的操作,这种转换对程序员来说是透明的。在程序使用时,程序员只需要读取该流对象,就可以完成对于外部数据的读取了。

    InputStream是所有字节输入流的父类,所以在InputStream类中包含的每个方法都会被所有字节输入流类继承,通过将读取以及操作数据的基本方法都声明在InputStream类内部,使每个子类根据需要覆盖对应的方法,这样的设计可以保证每个字节输入流子类在进行实际使用时,开放给程序员使用的功能方法是一致的。这样将简化IO类学习的难度,方便程序员进行实际的编程。

    默认情况下,对于输入流内部数据的读取都是单向的,也就是只能从输入流从前向后读,已经读取的数据将从输入流内部删除掉。如果需要重复读取流中同一段内容,则需要使用流类中的mark方法进行标记,然后才能重复读取。这种设计在使用流类时,需要深刻进行体会。

    InputStream类中,常见的方法有:

    aavailable方法

    public int available() throws IOException

    该方法的作用是返回当前流对象中还没有被读取的字节数量。也就是获得流中数据的长度。

    假设初始情况下流内部包含100个字节的数据,程序调用对应的方法读取了一个字节,则当前流中剩余的字节数量将变成99个。

    另外,该方法不是在所有字节输入流内部都得到正确的实现,所以使用该方法获得流中数据的个数是不可靠的。

    bclose方法

    public void close() throws IOException

    该方法的作用是关闭当前流对象,并释放该流对象占用的资源。

    IO操作结束以后,关闭流是进行IO操作时都需要实现的功能,这样既可以保证数据源的安全,也可以减少内存的占用。

    cmarkSupported方法

    public boolean markSupported()

    该方法的作用是判断流是否支持标记(mark)。标记类似于读书时的书签,可以很方便的回到原来读过的位置继续向下读取。

    dreset方法

    public void reset() throws IOException

    该方法的作用是使流读取的位置回到设定标记的位置。可以从该位置开始继续向后读取。

    emark方法

    public void markint readlimit

    为流中当前的位置设置标志,使得以后可以从该位置继续读取。变量readlimit指设置该标志以后可以读取的流中最大数据的个数。当设置标志以后,读取的字节数量超过该限制,则标志会失效。

    fread方法

    read方法是输入流类使用时最核心的方法,能够熟练使用该方法就代表IO基本使用已经入门。所以在学习以及后期的使用中都需要深刻理解该方法的使用。

    在实际读取流中的数据时,只能按照流中的数据存储顺序依次进行读取,在使用字节输入流时,读取数据的最小单位是字节(byte)。

    另外,需要注意的是,read方法是阻塞方法,也就是如果流对象中无数据可以读取时,则read方法会阻止程序继续向下运行,一直到有数据可以读取为止。

    read方法总计有三个,依次是:

    public abstract int read() throws IOException

    该方法的作用是读取当前流对象中的第一个字节。当该字节被读取出来以后,则该字节将被从流对象中删除,原来流对象中的第二个字节将变成流中的第一个字节,而使用流对象的available方法获得的数值也将减少1.如果需要读取流中的所以数据,只要使用一个循环依次读取每个数据即可。当读取到流的末尾时,该方法返回-1.该返回值的int中只有最后一个字节是流中的有效数据,所以在获得流中的数值时需要进行强制转换。返回值作成int的目的主要是处理好-1的问题。

    由于该方法是抽象的,所以会在子类中被覆盖,从而实现最基础的读数据的功能。

    public int readbyte[] b throws IOException

    该方法的作用是读取当前流对象中的数据,并将读取到的数据依次存储到数组bb需要提前初始化完成)中,也就是把当前流中的第一个字节的数据存储到b[0],第二个字节的数据存储到b[1],依次类推。流中已经读取过的数据也会被删除,后续的数据会变成流中的第一个字节。而实际读取的字节数量则作为方法的返回值返回。

    public int readbyte[] b int off int len throws IOException

    该方法的作用和上面的方法类似,也是将读取的数据存储到b中,只是将流中的第一个数据存储到b中下标为off的位置,最多读取len个数据,而实际读取的字节数量则作为方法的返回值返回。

    gskip方法

    public long skiplong n throws IOException

    该方法的作用是跳过当前流对象中的n个字节,而实际跳过的字节数量则以返回值的方式返回。

    跳过n个字节以后,如果需要读取则是从新的位置开始读取了。使用该方法可以跳过流中指定的字节数,而不用依次进行读取了。

    从流中读取出数据以后,获得的是一个byte数组,还需要根据以前的数据格式,实现对于该byte数组的解析。

    由于InputStream类是字节输入流的父类,所以该体系中的每个子类都包含以上的方法,这些方法是实现IO流数据读取的基础。

11.2.2 字节输出流OutputStream

    该类是所有的字节输出流的父类,在实际使用时,一般使用该类的子类进行编程,但是该类内部的方法是实现字节输出流的基础。

    该体系中的类完成把对应的数据写入到数据源中,在写数据时,进行的操作分两步实现:第一步,将需要输出的数据写入流对象中,数据的格式由程序员进行设定,该步骤需要编写代码实现;第二步,将流中的数据输出到数据源中,该步骤由API实现,程序员不需要了解内部实现的细节,只需要构造对应的流对象即可。

    在实际写入流时,流内部会保留一个缓冲区,会将程序员写入流对象的数据首先暂存起来,然后在缓冲区满时将数据输出到数据源。当然,当流关闭时,输出流内部的数据会被强制输出。

    字节输出流中数据的单位是字节,在将数据写入流时,一般情况下需要将数据转换为字节数组进行写入。

    OutputStream中,常见的方法有:

    aclose方法

    public void close() throws IOException

    该方法的作用是关闭流,释放流占用的资源。

    bflush方法

    public void flush() throws IOException

    该方法的作用是将当前流对象中的缓冲数据强制输出出去。使用该方法可以实现立即输出。

    cwrite方法

    write方法是输出流中的核心方法,该方法实现将数据写入流中。在实际写入前,需要实现对应的格式,然后依次写入到流中。写入流的顺序就是实际数据输出的顺序。

    write方法总计有3个,依次是:

    public abstract void writeint b throws IOException

    该方法的作用是向流的末尾写入一个字节的数据。写入的数据为参数b的最后一个字节。在实际向流中写数据时需要按照逻辑的顺序进行写入。该方法在OutputStream的子类内部进行实现。

    public void writebyte[] b throws IOException

    该方法的作用是将数组b中的数据依次写入当前的流对象中。

    public void writebyte[] b int off int len throws IOException

    该方法的作用是将数组b中从下标为off(包含)开始,后续长度为len个的数据依次写入到流对象中。

    在实际写入时,还需要根据逻辑的需要设定byte数值的格式,这个根据不同的需要实现不同的格式。

    11.2.3 字符输入流Reader

    字符输入流体系是对字节输入流体系的升级,在子类的功能上基本和字节输入流体系中的子类一一对应,但是由于字符输入流内部设计方式的不同,使得字符输入流的执行效率要比字节输入流体系高一些,在遇到类似功能的类时,可以优先选择使用字符输入流体系中的类,从而提高程序的执行效率。

    Reader体系中的类和InputStream体系中的类,在功能上是一致的,最大的区别就是Reader体系中的类读取数据的单位是字符(char),也就是每次最少读入一个字符(两个字节)的数据,在Reader体系中的读数据的方法都以字符作为最基本的单位。

    Reader类和InputStream类中的很多方法,无论声明还是功能都是一样的,但是也增加了两个方法,依次介绍如下:

    aread方法

    public int readCharBuffer target throws IOException

    该方法的作用是将流内部的数据依次读入CharBuffer对象中,实际读入的char个数作为返回值返回。

    bready方法

    public boolean ready() throws IOException

    该方法的作用是返回当前流对象是否准备完成,也就是流内部是否包含可以被读取的数据。

    其它和InputStream类一样的方法可以参看上面的介绍。

11.2.2 字节输出流OutputStream

    该类是所有的字节输出流的父类,在实际使用时,一般使用该类的子类进行编程,但是该类内部的方法是实现字节输出流的基础。

    该体系中的类完成把对应的数据写入到数据源中,在写数据时,进行的操作分两步实现:第一步,将需要输出的数据写入流对象中,数据的格式由程序员进行设定,该步骤需要编写代码实现;第二步,将流中的数据输出到数据源中,该步骤由API实现,程序员不需要了解内部实现的细节,只需要构造对应的流对象即可。

    在实际写入流时,流内部会保留一个缓冲区,会将程序员写入流对象的数据首先暂存起来,然后在缓冲区满时将数据输出到数据源。当然,当流关闭时,输出流内部的数据会被强制输出。

    字节输出流中数据的单位是字节,在将数据写入流时,一般情况下需要将数据转换为字节数组进行写入。

    OutputStream中,常见的方法有:

    aclose方法

    public void close() throws IOException

    该方法的作用是关闭流,释放流占用的资源。

    bflush方法

    public void flush() throws IOException

    该方法的作用是将当前流对象中的缓冲数据强制输出出去。使用该方法可以实现立即输出。

    cwrite方法

    write方法是输出流中的核心方法,该方法实现将数据写入流中。在实际写入前,需要实现对应的格式,然后依次写入到流中。写入流的顺序就是实际数据输出的顺序。

    write方法总计有3个,依次是:

    public abstract void writeint b throws IOException

    该方法的作用是向流的末尾写入一个字节的数据。写入的数据为参数b的最后一个字节。在实际向流中写数据时需要按照逻辑的顺序进行写入。该方法在OutputStream的子类内部进行实现。

    public void writebyte[] b throws IOException

    该方法的作用是将数组b中的数据依次写入当前的流对象中。

    public void writebyte[] b int off int len throws IOException

    该方法的作用是将数组b中从下标为off(包含)开始,后续长度为len个的数据依次写入到流对象中。

    在实际写入时,还需要根据逻辑的需要设定byte数值的格式,这个根据不同的需要实现不同的格式。

    11.2.3 字符输入流Reader

    字符输入流体系是对字节输入流体系的升级,在子类的功能上基本和字节输入流体系中的子类一一对应,但是由于字符输入流内部设计方式的不同,使得字符输入流的执行效率要比字节输入流体系高一些,在遇到类似功能的类时,可以优先选择使用字符输入流体系中的类,从而提高程序的执行效率。

    Reader体系中的类和InputStream体系中的类,在功能上是一致的,最大的区别就是Reader体系中的类读取数据的单位是字符(char),也就是每次最少读入一个字符(两个字节)的数据,在Reader体系中的读数据的方法都以字符作为最基本的单位。

    Reader类和InputStream类中的很多方法,无论声明还是功能都是一样的,但是也增加了两个方法,依次介绍如下:

    aread方法

    public int readCharBuffer target throws IOException

    该方法的作用是将流内部的数据依次读入CharBuffer对象中,实际读入的char个数作为返回值返回。

    bready方法

    public boolean ready() throws IOException

    该方法的作用是返回当前流对象是否准备完成,也就是流内部是否包含可以被读取的数据。

    其它和InputStream类一样的方法可以参看上面的介绍。

Java编程那些事儿86——文件操作之File类使用

 11.3 I/O类使用

    由于在IO操作中,需要使用的数据源有很多,作为一个IO技术的初学者,从读写文件开始学习IO技术是一个比较好的选择。因为文件是一种常见的数据源,而且读写文件也是程序员进行IO编程的一个基本能力。本章IO类的使用就从读写文件开始。

    11.3.1 文件操作

    文件(File)是最常见的数据源之一,在程序中经常需要将数据存储到文件中,例如图片文件、声音文件等数据文件,也经常需要根据需要从指定的文件中进行数据的读取。当然,在实际使用时,文件都包含一个的格式,这个格式需要程序员根据需要进行设计,读取已有的文件时也需要熟悉对应的文件格式,才能把数据从文件中正确的读取出来。

    文件的存储介质有很多,例如硬盘、光盘和U盘等,由于IO类设计时,从数据源转换为流对象的操作由API实现了,所以存储介质的不同对于程序员来说是透明的,和实际编写代码无关。

    11.3.1.1 文件的概念

    文件是计算机中一种基本的数据存储形式,在实际存储数据时,如果对于数据的读写速度要求不是很高,存储的数据量不是很大时,使用文件作为一种持久数据存储的方式是比较好的选择。

    存储在文件内部的数据和内存中的数据不同,存储在文件中的数据是一种持久存储,也就是当程序退出或计算机关机以后,数据还是存在的,而内存内部的数据在程序退出或计算机关机以后,数据就丢失了

    在不同的存储介质中,文件中的数据都是以一定的顺序依次存储起来,在实际读取时由硬件以及操作系统完成对于数据的控制,保证程序读取到的数据和存储的顺序保持一致。

    每个文件以一个文件路径和文件名称进行表示,在需要访问该文件的时,只需要知道该文件的路径以及文件的全名即可。在不同的操作系统环境下,文件路径的表示形式是不一样的,例如在Windows操作系统中一般的表示形式为C/windows/system,而Unix上的表示形式为/user/my.所以如果需要让Java程序能够在不同的操作系统下运行,书写文件路径时还需要比较注意。

    11.3.1.1.1 绝对路径和相对路径

    绝对路径是指书写文件的完整路径,例如d/java/Hello.java,该路径中包含文件的完整路径d/java以及文件的全名Hello.java.使用该路径可以唯一的找到一个文件,不会产生歧义。但是使用绝对路径在表示文件时,受到的限制很大,且不能在不同的操作系统下运行,因为不同操作系统下绝对路径的表达形式存在不同。

    相对路径是指书写文件的部分路径,例如/test/Hello.java,该路径中只包含文件的部分路径/test和文件的全名Hello.java,部分路径是指当前路径下的子路径,例如当前程序在d/abc下运行,则该文件的完整路径就是d/abc/test.使用这种形式,可以更加通用的代表文件的位置,使得文件路径产生一定的灵活性。

    Eclipse项目中运行程序时,当前路径是项目的根目录,例如工作空间存储在d/javaproject,当前项目名称是Test,则当前路径是:d/javaproject/Test.在控制台下面运行程序时,当前路径是class文件所在的目录,如果class文件包含包名,则以该class文件最顶层的包名作为当前路径。

    另外在Java语言的代码内部书写文件路径时,需要注意大小写,大小写需要保持一致,路径中的文件夹名称区分大小写。由于‘/’Java语言中的特殊字符,所以在代码内部书写文件路径时,例如代表“c/test/java/Hello.java”时,需要书写成“c//test//java//Hello.java“c/test/java/Hello.java”,这些都需要在代码中注意。

    11.3.1.1.2 文件名称

    文件名称一般采用文件名。后缀名的形式进行命名,其中文件名用来表示文件的作用,而使用后缀名来表示文件的类型,这是当前操作系统中常见的一种形式,例如“readme.txt”文件,其中readme代表该文件时说明文件,而txt后缀名代表文件时文本文件类型,在操作系统中,还会自动将特定格式的后缀名和对应的程序关联,在双击该文件时使用特定的程序打开。

    其实在文件名称只是一个标示,和实际存储的文件内容没有必然的联系,只是使用这种方式方便文件的使用。在程序中需要存储数据时,如果自己设计了特定的文件格式,则可以自定义文件的后缀名,来标示自己的文件类型。

    和文件路径一样,在Java代码内部书写文件名称时也区分大小写,文件名称的大小写必须和操作系统中的大小写保持一致。

    另外,在书写文件名称时不要忘记书写文件的后缀名。

11.3.1.2 File

    为了很方便的代表文件的概念,以及存储一些对于文件的基本操作,在java.io包中设计了一个专门的类——File类。

    File类中包含了大部分和文件操作的功能方法,该类的对象可以代表一个具体的文件或文件夹,所以以前曾有人建议将该类的类名修改成FilePath,因为该类也可以代表一个文件夹,更准确的说是可以代表一个文件路径。

    下面介绍一下File类的基本使用。

    1File对象代表文件路径

    File类的对象可以代表一个具体的文件路径,在实际代表时,可以使用绝对路径也可以使用相对路径。

    下面是创建的文件对象示例。

    public FileString pathname

    该示例中使用一个文件路径表示一个File类的对象,例如:

    File f1 = new File“d//test//1.txt);

    File f2 = new File“1.txt”);

    File f3 = new File“e//abc);

    这里的f1f2对象分别代表一个文件,f1是绝对路径,而f2是相对路径,f3则代表一个文件夹,文件夹也是文件路径的一种。

    public FileString parent String child

    也可以使用父路径和子路径结合,实现代表文件路径,例如:

    File f4 = new File“d//test//“1.txt”);

    这样代表的文件路径是:d/test/1.txt.

    2File类常用方法

    File类中包含了很多获得文件或文件夹属性的方法,使用起来比较方便,下面将常见的方法介绍如下:

    acreateNewFile方法

    public boolean createNewFile() throws IOException

    该方法的作用是创建指定的文件。该方法只能用于创建文件,不能用于创建文件夹,且文件路径中包含的文件夹必须存在。

    bdelect方法

    public boolean delete()

    该方法的作用是删除当前文件或文件夹。如果删除的是文件夹,则该文件夹必须为空。如果需要删除一个非空的文件夹,则需要首先删除该文件夹内部的每个文件和文件夹,然后在可以删除,这个需要书写一定的逻辑代码实现。

    cexists方法

    public boolean exists()

    该方法的作用是判断当前文件或文件夹是否存在。

    dgetAbsolutePath方法

    public String getAbsolutePath()

    该方法的作用是获得当前文件或文件夹的绝对路径。例如c/test/1.t则返回c/test/1.t.

    egetName方法

    public String getName()

    该方法的作用是获得当前文件或文件夹的名称。例如c/test/1.t,则返回1.t.

    fgetParent方法

    public String getParent()

    该方法的作用是获得当前路径中的父路径。例如c/test/1.t则返回c/test.

    gisDirectory方法

    public boolean isDirectory()

    该方法的作用是判断当前File对象是否是目录。

    hisFile方法

    public boolean isFile()

    该方法的作用是判断当前File对象是否是文件。

    ilength方法

    public long length()

    该方法的作用是返回文件存储时占用的字节数。该数值获得的是文件的实际大小,而不是文件在存储时占用的空间数。

    jlist方法

    public String[] list()

    该方法的作用是返回当前文件夹下所有的文件名和文件夹名称。说明,该名称不是绝对路径。

    klistFiles方法

    public File[] listFiles()

    该方法的作用是返回当前文件夹下所有的文件对象。

    lmkdir方法

    public boolean mkdir()

    该方法的作用是创建当前文件文件夹,而不创建该路径中的其它文件夹。假设d盘下只有一个test文件夹,则创建d/test/abc文件夹则成功,如果创建d/a/b文件夹则创建失败,因为该路径中d/a文件夹不存在。如果创建成功则返回true,否则返回false.

    mmkdirs方法

    public boolean mkdirs()

    该方法的作用是创建文件夹,如果当前路径中包含的父目录不存在时,也会自动根据需要创建。

    nrenameTo方法

    public boolean renameToFile dest

    该方法的作用是修改文件名。在修改文件名时不能改变文件路径,如果该路径下已有该文件,则会修改失败。

    osetReadOnly方法

    public boolean setReadOnly()

    该方法的作用是设置当前文件或文件夹为只读。

 

总结:file类代表文件和文件夹的对象

       File f2=new File("1.txt");只是新建了文件对象,并没有新建1.txt这个文件。如果想新建这个文件,要用 boolean b = f3.createNewFile();

       File f3=new File("c://javaprograms ");如果想新建这个文件夹要用 boolean b1 = f6.mkdir();

Java编程那些事儿87——文件操作之读取文件

IO的这种设计就和城市中的供水和排水系统设计是一样的,在供水的时候,水源有江河水、湖水和地下水等不同类型,由自来水公司完成把水源转换为对应的水流,自来水公司再通过管道把水送到你家。而在排水系统设计时,只需要将污水排入污水管道即可,至于这些污水是怎么被处理的,则不需要关心,这样也简化了家庭用水的处理。

过程和现实生活中的例子进行比较。管道相当于数组,

Java编程那些事儿92——IO使用注意问题

 

    11.3.4 注意问题

    上面介绍了IO类的基本使用,熟悉了实体流和装饰流的基本使用,但是在IO类实际使用时,还是会遇到一系列的问题,下面介绍一些可能会经常遇到的问题。

    11.3.4.1 类的选择

    对于初次接触IO技术的初学者来说,IO类体系博大精深,类的数量比较庞大,在实际使用时经常会无所适从,不知道该使用那些类进行编程,下面介绍一下关于IO类选择的一些技巧。

    选择类的第一步是选择合适的实体流。

    选择实体流时第一步是按照连接的数据源种类进行选择,例如读写文件应该使用文件流,如FileInputStream/FileOutputStreamFileReader/FileWriter,读写字节数组应该使用字节数组流等,如ByteArrayInputStream/ByteArrayOutputStream.

    选择实体流时第二步是选择合适方向的流。例如进行读操作时应该使用输入流,进行写操作时应该使用输出流。

    选择实体流时第三步是选择字节流或字符流。除了读写二进制文件,或字节流中没有对应的流时,一般都优先选择字符流。

    经过以上步骤以后,就可以选择到合适的实体流了。下面说一下装饰流的选择问题。

    在选择IO类时,实体流是必需的,装饰流是可选的。另外在选择流时实体流只能选择一个,而装饰流可以选择多个。

    选择装饰流时第一步是选择符合要求功能的流。例如需要缓冲流的话选择BufferedReader/BufferedWriter等,有些时候也可能只是为了使用某个装饰流内部提供的方法。

    选择装饰流时第二步是选择合适方向的流,这个和实体流选择中的第二步一致。

    当选择了多个装饰流以后,可以使用流之间的多层嵌套实现要求的功能,流的嵌套之间没有顺序。

    11.3.4.2  非依次读取流数

    据由于IO类设计的特点,在实际读取时,只能依次读取流中的数据,而且在通常情况下,已经读取过的数据无法再进行读取。如果需要重复读取流中某段数据时,一般的做法是将从流中读取的数据使用数组存储起来,然后根据需要读取数组中的内容即可,但是有些时候,还是有一些特殊的情况的,IO类对于这些都进行了支持。

    1、间断性的读取流中的数据

    对于某些特殊格式的文件,例如字体文件等,在实际读取数据时不需要顺序进行读取,而只需要根据内容的位置进行读取。这样可以使用流中的skip方法实现。例如:

    int n = fis.skip100);

    该行代码的作用是,以流fis当前位置为基础,当前位置可以是流中的任何位置,向后跳过100个单位(字节流单位为字节,字符流单位是字符),如果再使用read方法继续读取,就是读取跳跃以后新位置的内容,也就相当于跳过了100个单位的内容。

    而实际在使用时,实际真正跳过的单位数量作为skip方法的返回值返回。

2、重复读取流中某段数据

    当必须重复读取流中同一段数据时,如果对应的流支持mark(标记)的话,则可以重复读取同一段数据。

    下面以重复读取控制台输入流System.in为例子,来介绍mark的使用,示例代码如下:

         import java.io.*;

/**

 * mark使用示例

 */

public class MarkUseDemo {

         public static void main(String[] args) {

                   byte[] b = new byte[1024];

                   try{

                            //读取数据

                            int data = System.in.read();

                            //输出第一个字节的数据

                            System.out.println("第一个字节:" + data);

                            //判断该流是否支持mark

                            if(System.in.markSupported()){

                                     //记忆当前位置,可以从当前位置

                                     //向后最多读取100个字节

                                     System.in.mark(100);

                                     //读取数据

                                     int n = System.in.read(b);

                                     //输出读取到的内容

                                     System.out.print("第一次读取到的内容:");

                                     for(int i = 0;i < n;i++){

                                               System.out.print(b[i] + " ");

                                     }

                                     System.out.println();

                                     //回到标记位置

                                     System.in.reset();

                                     //重复读取标记位置以后的内容

                                     n = System.in.read(b);

                                     //输出读取到的内容

                                     System.out.print("第二次读取到的内容:");

                                     for(int i = 0;i < n;i++){

                                               System.out.print(b[i] + " ");

                                     }

                                     System.out.println();

                            }

                   }catch(Exception e){

                            e.printStackTrace();

                   }

         }

}

    在该示例中,首先调用System.in流中的read方法,读取流中的第一个字节,并把读取到的数据赋值给data,然后将读取到的第一个字节的数据输出出来。

    然后调用System.in流中的markSupported判断该流是否支持mark功能,如果支持的话则markSupported方法将返回true.

    如果流System.in支持mark,则标记当前位置,并允许从当前位置开始最多读取后续100个字节的数据,其实IO类内部的只读取这些数据,而不真正从流中将这些数据删除。

    后续继续读取流中的数据,如果读取的数据超过100个字节,则mark标记失效,并把读取到的有效数据输出到控制台。

    如果需要从标记位置重复读取已经读取过的数据,则只需要调用流对象中的reset方法重置流的位置,使流可以回到mark的位置,如果继续读取的话,则从该位置开始可以向后读取。这样就可以从mark的位置开始,再次读取后续的数据了。

    例如在控制台输入123456789,则该程序的执行结果是:

    第一个字节:49

    第一次读取到的内容:50 51 52 53 54 55 56 57 13 10

    第二次读取到的内容:50 51 52 53 54 55 56 57 13 10

    其中输入的第一个字节是1,读取时该字符的编码是49,而后续的内容就是流的结构,其中流末尾的1310是在输入时,添加的回车和换行字符。

    11.3.4.3 中文问题

    由于JDK设计时,对于国际化支持比较好,所以JDK在实际实现时支持很多的字符集,这样在进行特定字符集的处理时就需要特别小心了。

    其实在进行中文处理时,只需要注意一个原则就可以了,这个原则就是将中文字符转换为byte数组时使用的字符集,需要和把byte数组转换为中文字符串时的字符集保持一致,这样就不会出现中文问题了。

    当然,如果不想手动实现字符串和byte数组的转换,可以使用DataInputStreamDataOutputStream中的readUTFwriteUTF实现读写字符串。

    11.4 总结

    关于IO类的使用,还需要在实际开发过程中多进行使用,从而更深入的体会IO类设计的初衷,并掌握IO类的使用。

    另外,IO类是Java中进行网络编程的基础,所以熟悉IO类的使用也是学习网络编程必须的一个基础。

//package ch10; JoinFile_Example练习题总结:

1. while((ch=fis.read())!=-1)

       {

           fos.write(ch);

       }读入的数据可以不用存在数组里面就直接写出到文件中;

2. FileInputStream fis=new FileInputStream(args[0]);可以用args[0]来输入参数。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值