JavaIO流学习笔记

来源:互联网 发布:js 修改title标题 编辑:程序博客网 时间:2024/05/18 00:40
IO流用来处理设备之间的数据传输
Java对数据的操作都是通过流的方式
Java用于操作的流对象都在IO包中。
流按流向分:输入流,输出流
按操作数据分:字节流,字符流。
字符流里融合了编码表,所以只有文字才用字符流,图片就得用字节流了。  字符流的出现主要是解决文字编码的问题。

IO流常用基类
字节流:  InputStream  OutputStream
字符流: Reader  Writer
由这4个类派生的子类名称都是以其父类名作为子类名的后缀
如:inputstream的子类 FileInputStream
如:Reader的子类FileReader.

————————字符流------
例子:  
需求 在硬盘上创建一个文件并写入一些文字数据。   字符流只能操作文字。 而字节流是通用的。 
找到一个专门用于操作文件的Writer子类对象,FileWriter, 后缀名是父类名,前缀名是该流对象的功能。
步骤:

1.创建一个FileWriter对象,该对象一被初始化就必须要有被操作的文件,而且该文件会被创建到指定的目录下。 
如果该目录下已有同名文件,将被覆盖。 

  1 . FileWriter fw = new FileWriter(“demo.txt”);

这句话之后就会在目录下生成demo.txt的文件。

其实该句就是在明确数据存放的目的地。(想写字得先有纸,写文字当然必须得先有文件了)
 
创建完整个文件之后就可以往里写了。

  1. 调用write();方法 , 写字符串没有直接写到文件里,而是先写到流里面去, 也就是内存里。    
fw.write(“abc”);
     3.刷新流对象缓冲中的数据,将数据刷到目的地中。 
fw.flush();

还有一种方法也能刷新流对象, 就是close方法。 
但是调用close之后就不能再往里面写数据了,因为流已经被关闭了,而flush则可以一直往里面写,这是两者最大的区别。 
这里有一点要注意的是,java本身并不能做到写数据的功能,他是调用了系统的,比如windows里面的写功能,所以在写数据之后要关闭掉流对象。 

fw.close();

注意的是在这些操作中会出现IO异常, 在IO中有很多的异常,对于这些异常我们必须要做处理,接下里就来看看如何去处理这些IO异常。 

首先明确一点就是出现IO异常如果抛肯定是不合适的,


上面的代码就是一个处理异常的示例代码,关闭流的动作要写在finally里,并且要对流对象进行判断。因为当上面的代码发生异常的时候,对象初始化就失败了, 这个对象就为空,如果不判断则会报空指针错误。

对于文件的续写只要在构造方法里传入一个true参数就可以了

/**
 * Created by 御轩 on 16/8/24 下午2:48.
 * 演示对已有文件的数据续写
 */
public class Iodemo {
    public static void main(String[] args) {
        FileWriter fw = null;
        try {
            //传递一个true参数,代表不覆盖已有的文件,并在已有文件的末尾处进行数据的续写。
            fw = new FileWriter("demo.txt" , true);
            fw.write("haha");
       catch (IOException e) {
            e.printStackTrace();
        }finally {
            if (fw != null) {
                try {
                    fw.close();
               catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}


—————文件的读取--------

/**
 * Created by 御轩 on 16/8/24 下午2:48.
 * 演示对已有文件的数据续写
 */
public class Iodemo {
    public static void main(String[] args) {
        FileReader read = null;
        try {
            //创建一个文件读取流对象和一个指定名称的文件相关联
            //要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundException
            read = new FileReader("demo.txt");
            //调用read方法
            //read方法一次读一个字符,而且会自动往下读
            //read方法返回一个int值,范围在0到65535之间,如果已达到流的末尾,则返回-1.
            int ch = 0;
            while ((ch = read.read()) != -1) {
                System.out.printf("ch= " + ch);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
       catch (IOException e) {
            e.printStackTrace();
       finally {
            if (read != null) {
                try {
                    read.close();
               catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }

    }
}

这里只要注意一点就是读到末尾返回-1就行了,其他都很简单了。

接下来看读取的第二种方式 ,通过字符数组进行读取

核心方法是read(char[ ] ); 返回的是读到字符的个数。 


定义一个字符数组,然后往里存就行了,这里要注意的就是这个数组的长度了,一般定义为1024的倍数。 一个char两个字节,所以这个定义的数组为2k.

接下来练习一下。
读取一个.java文件,并打印在控制台上。 
我们知道.java是文本文件,所以我们可以用字符流来读。
/**
 * Created by 御轩 on 16/8/24 下午2:48.
 * 读取.java文件并打印
 */
public class Iodemo {
    public static void main(String[] args) {
        FileReader reader = null;
        try {
            reader = new FileReader("demo.java");
            char[] chars = new char[1024];
            int num = 0;
            try {
                while ((num = reader.read(chars)) != -1) {
                    System.out.print(new String(chars num));
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }finally {
            if (reader != null) {
                try {
                    reader.close();
               catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}


这个示例也很简单,核心就是read(char[])这个方法,返回个数,当已经是文件末尾的时候返回-1. 所以利用这个特性去进行循环读取并写入到char数组中,打印的时候转换成string就可以了。 

接下里看另一个示例,如何将C盘的文件拷贝到D盘呢?

首先我们要想想复制的原理是什么?  其实就是将C盘下的文件数据存放到D盘的一个文件中。
步骤:1.在D盘创建一个文件,用于存储C盘文件中的数据。
2.定义一个读取流和C盘文件关联。
3.通过不断的读写完成数据的存储。
4.关闭资源。 



先看第一种,就是读一个字符,写一个字符。 虽然这种方式比较慢,但是有助于理解。

/**
 * Created by 御轩 on 16/8/24 下午2:48.
 * 将haha.java文件复制到demo.java中
 */
public class Iodemo {
    public static void main(String[] args) {
        FileWriter writer = null;
        FileReader reader = null;
        try {
            //我们要从haha.java复制到demo.java
            //创建一个新的文件 demo.java
            writer = new FileWriter("demo.java");
            //指定要读取的文件名haha.java
            reader = new FileReader("haha.java");
            int ch = 0;
            //循环去读,读一个再去写一个
            while ((ch = reader.read()) != -1) {
                writer.write(ch);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {

            try {
                writer.close();
                reader.close();
           catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}


这里的读方法跟写方法都是对单个字符操作,所以相对比较慢,不推荐。 
我们可以将要读取的文件都读取完,再存到流对象里,都读完之后再写入到目标文件中。这样就快了,思路有了,接下来看代码:

/**
 * Created by 御轩 on 16/8/24 下午2:48.
 * 将haha.java文件复制到demo.java中
 */
public class Iodemo {
    public static void main(String[] args) {
        FileWriter writer = null;
        FileReader reader = null;

        try {
            reader = new FileReader("haha.java");
            writer = new FileWriter("demo.java");
            //定义一个缓冲区的数组,这里大小我们一般都设置为1024
            char[] chars = new char[1024];
            int len = 0//流读取后返回的数字,读到最后一个的时候会返回-1,用于循环判断
            while ((len = reader.read(chars)) != -1) {
                //这里写的时候要注意不能把整个缓冲区数组都写进去,而是写到读取的个数位置
                writer.write(chars len);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
       catch (IOException e) {
            e.printStackTrace();
        }finally {

            try {
                if (reader != null)
                reader.close();
             
           catch (IOException e) {
                e.printStackTrace();
            }
            if (writer != null) {
                try {
                    writer.close();
               catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}





————————字符流的缓冲区-----------------
缓冲区的出现提高了对数据的读写效率。
对应类:
BufferWriter    BufferReader 
缓冲区要结合流才能使用,在流的基础上对流的功能进行了增强。 

要明确的一点是缓冲区的出现是为了提高流的效率而出现的,所以在创建缓冲区之前,必须要先有流对象。

基本的写法如下:

public class Iodemo {
    public static void main(String[] args) {
        FileWriter fw = null;
        BufferedWriter bufferedWriter = null;
        try {
            //创建一个字符写入流对象
            fw = new FileWriter("demo.txt");

            //为了提高字符写入流效率,加入了缓冲技术,只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可
            bufferedWriter = new BufferedWriter(fw);

            bufferedWriter.write("abc");
//                记住只要用到缓冲区就要刷新
            bufferedWriter.flush();

       catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
//                其实关闭缓冲区,就是关闭缓冲区中的流对象
                bufferedWriter.close();
           catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}


可以看到缓冲区对象BufferWriter的构造方法里传入了FileWriter的对象, 接下来缓冲区进行写操作,其实底层调用的还是FileWriter,所以最后关闭的时候可以看到只关闭了缓冲区对象,因为这个时候关闭缓冲区其实就是关闭了FileWriter。
bufferedWriter.close();

———newLine -----

接下里我们再来看看换行, 我们知道linux下跟windows下的换行符是不一样的,为了跨平台性, JAVA封装了一个newLine方法,这个方法里帮我们封装好了换行符,所以我们不用关心是哪个平台,只要调用这个方法就能换行,来看个示例代码:

 
bufferedWriter = new BufferedWriter(fw);

for (int i = 0i < 7i++) {
    bufferedWriter.write("abcde" + i);
    bufferedWriter.newLine();
    bufferedWriter.flush();
}


这个循环里我们操作了换行,调用newLine方法。 要知道newLine方法内部其实就帮我们做了一件事write( \n\r ) ,所以我们只要调用这个方法就可以换行了。
要记住的是这个newLine方法是缓冲区特有的方法

so清爽~

缓冲区写的功能已经了解了,我们来看缓冲区读的功能

——————BufferReader -------------

我们知道字符流是对文本文件操作的,那么文本文件有个比较大的特点就是行,bufferReader除了能读单个,读数组,还有个特别牛的方法就是读行。 
readLine 读取一个文本行。该方法返回的是字符串对象 String
这个方法执行一次,读取一行。
所以我们最好就是给他来个循环,循环的读,并且读到流的末尾,该方法返回null。

public class Iodemo {    public static void main(String[] args) {        FileReader fileReader = null;        BufferedReader bufferedReader = null;        try {            fileReader = new FileReader("demo.txt");            bufferedReader = new BufferedReader(fileReader);            String line = null;            while ((line = bufferedReader.readLine()) != null) {                System.out.println(line);            }        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }finally {            try {                bufferedReader.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}
 





我们来练习一遍用缓冲区来复制文本文件: 


public class Iodemo {
    public static void main(String[] args) {
        FileWriter writer = null;
        FileReader reader = null;
        BufferedReader bufferedReader = null;
        BufferedWriter bufferedWriter = null;
        try {
            writer = new FileWriter("demo.java");
            reader = new FileReader("haha.java");
            bufferedWriter = new BufferedWriter(writer);
            bufferedReader = new BufferedReader(reader);
            String line = null;
            while ((line = bufferedReader.readLine()) != null) {
                bufferedWriter.write(line);
                bufferedWriter.newLine();
                bufferedWriter.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if (bufferedReader != null)
                bufferedReader.close();
           catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (bufferedWriter != null)
                bufferedWriter.close();
           catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

这里值得注意的点是几句代码

bufferedWriter.write(line);
                bufferedWriter.newLine();
这里如果我们不写newLine可以吗, 答案是当然可以,但是如果这里不换行,这个文本就是全挤在一起很乱,所以我们要加上这句换行方法。 

原理就是readLine方法返回的时候只返回回车符之前数据的内容,并不返回回车符,所以我们得自己加换行。

readLine方法的原理:
无论是读一行,还是读取多个字符,其实最终都是在硬盘上一个一个的读取。所以最终使用的还是read();一次读取一个的方法。


bufferReader其实是对FileReader的增强,这种方式叫做装饰设计模式。

装饰设计模式:
当想要对已有的对象进行功能增强时,可以定义一个类,将已有对象传入,基于已有对象的功能,并提供加强功能,那么自定义的该类就称为装饰类。 

装饰类通常会通过构造方法接收被装饰的对象,并基于被装饰的对象的功能,提供更强的功能。
BufferReader就是装饰模式

装饰模式比继承要灵活,避免了继承的臃肿。 而且降低了类与类之间的关系。装饰类因为是增强已有对象,具备的功能和已有对象是相同的,只不过提供了更强的功能。  





0 0