Java-IO流学习

Posted z-dey

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java-IO流学习相关的知识,希望对你有一定的参考价值。

IO流学习

流的分类

  • 根据操作单位不同分为字节流和字符流
  • 根据流向不同分为输入流和输出流
  • 根据角色不同分为节点流和处理流

以下四个类是IO流中最基础的类,都是抽象类。其他流都是继承他们的。

分类 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer

字节流:操作的对象是字节数组
字符流:操作的对象是字符数组

节点流:作用在文件上的流,直接对文件进行读写操作
处理流:作用在节点流上的流,可以加速流的传输

输入流和输入流是相对的,主要看你站在那个角度来看待,如果站在程序的角度,读入数据较输入流,写出数据较输出流。

IO流体系结构

分类 字节输入流 字节输出流 字符输入流 字符输出流
抽象基类 InputStream OutputStream Reader Writer
访问文件 FileInputStream FileOutputStream FileReader FileWriter
访问数组 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArayWriter
访问管道 PipedInputStream PipedOutputStream PipedReader PipedWriter
访问字符串 StringReader StringWriter
缓冲流 BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter
转换流 InputStreamReader OutputStreamWriter
对象流 ObjectInputStream ObjectOutputStream
过滤流 FilterInputStream FileOutputStream FilterReader FileWriter
打印流 PrintStream PrintWriter
推回输入流 PushbackInputStream PushbackReader
特殊流 DataInputStream DataOutputStream

访问文件的流都是节点流,其他除了抽象基类外都是处理流

File类

想要进行IO操作,肯定离不来文件,所以学习IO流的前提就是要学会File类的操作。

File类的介绍

File是用来操作文件或目录的,但是不能对文件的内容进行操作,主要是用于文件和目录的创建、文件的删除、文件的查找。

File类构造方法

File类表示的是硬盘中实际存在的目录或文件,可以通过以下三种方式创建一个File类的对象。

File(String pathname)//通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。 常用

File(File parent, String child) //从父抽象路径名和子路径名字符串创建新的 File实例。常用

File(String parent, String child) //从父路径名字符串和子路径名字符串创建新的 File实例。 常用

File(URI uri) //通过将给定的 file: URI转换为抽象路径名来创建新的 File实例。

代码举例:

    @Test
    public void test(){

        //根据路径创建
        String path = "d:\\file.txt";
        File file = new File(path);

        //根据父文件和子类路径创建
        File parentFile = new File( "d:\\io");
        File file1 = new File(parentFile,"file1.txt");

        //根据父路径和子路径创建
        String parentPath = "d:\\io";
        String childPath = "file2.txt";
        File file2 = new File(parentPath,childPath);

    }

注意:在Windows中路径分割使用‘’,在Linux中使用‘/’,为了方便区分在File类中有一个separator 属性,可以用来代替分隔符。
在Java中 是用来转义的,所以这里需要使用两个 字符

路径问题

相对路径:相对于当前的项目路径,不带盘符。如: "hello.txt"
绝对路径:相对于硬盘的物理路径,是一个带有盘符的完整路径。如:"d:hi.txt"

如代码所示,目前文件只是存在于内存中,还没有在硬盘上创建实际的文件,需要使用一些方法来创建,下面介绍File类中的常用方法。

File类常用方法

1、获取的方法

public String getAbsolutePath() //绝对路径名字符串。
public String getPath() //将此抽象路径名转换为路径名字符串。
public String getName() //返回由此抽象路径名表示的文件或目录的名称。
public String getParent() //返回上层文件目录路径,没有则返回null。
public long length() //返回由此抽象路径名表示的文件的长度。 长度指的是文件的字节数,不能获取目录的长度。
public String[] list() //获取指定目录下的所有文件或文件目录的名称数组
public File[] listFiles() //获取指定目录下的所有文件或文件目录的File数组

    @Test
    public void test3(){
        File file = new File("f:\\io\\hello.txt");

        System.out.println("绝对路径:"+file.getAbsolutePath());
        System.out.println("路径:"+file.getPath());
        System.out.println("名字:"+file.getName());
        System.out.println("上层路径:"+file.getParent());
        System.out.println("长度"+file.length());
        System.out.println("文件目录:"+file.list());
        System.out.println("文件目录"+file.listFiles());
    }

/*
运行结果:
绝对路径:f:iohello.txt  
路径:f:iohello.txt
名字:hello.txt
上层路径:f:io
长度0  -->因为文件中没有内容,所以是0
文件目录:null  --> 因为当前是一个文件,所以没有目录,如果是一个目录就会有
文件目录null  --> 因为当前是一个文件,所以没有目录,如果是一个目录就会有
*/

进行判断的方法

public boolean exists()//判断文件或目录是否存在
public boolean isDirectory()//判断是否是目录
public boolean isFile()//判断是否是文件

    @Test
    public void test5(){
        File file = new File("hello.txt");//该文件没建过
        System.out.println("文件是否存在"+file.exists());//false
        System.out.println("是否是目录"+file.isDirectory());//false
        System.out.println("是否是文件"+file.isFile());//false,因为连文件都没有,所以是false

        File file1 = new File("hello.txt");//该文件我已经创建过了
        System.out.println("文件是否存在"+file1.exists());//true
        System.out.println("是否是目录"+file1.isDirectory());//false
        System.out.println("是否是文件"+file1.isFile());//true
    }

进行创建的方法

public boolean createNewFile() //如果文件不存在则创建一个新的空文件返回true,如果存在,不创建,返回false;
public boolean mkdir()//创建文件目录,如果文件目录存在,不创建,如果文件的上层目录不存在,也不创建;
public boolean mkdirs()//创建文件目录,如果上层文件目录不存在,也一起创建

    @Test
    public void test4() throws IOException {
        File file = new File("test.txt");
        
        System.out.println(file.createNewFile());//true
        
        File file1 = new File("f:\\test\\hello");
        file1.mkdir();//test和hello目录都不存在,不会创建
        
        File file2 = new File("f:\\test\\hello\\java");
        file2.mkdirs();//test和hello目录都不存在,会全部创建
        
    }

进行删除的方法

public boolean delete()//删除文件或目录

File类练习

打印指定文件目录下的所有文件-递归实现

//递归打印文件
    @Test
    public void test6(){
        File file = new File("f:\\io");
        printFile(file);
    }

    public void printFile(File file){
        File[] files = file.listFiles();
        for (File f : files) {
            if(f.isDirectory()) printFile(f);
            System.out.println(f.getName());
        }
    }

字符流

纯文本文件都用字符流来处理

字符输入流

FileReader

1、构造方法

FileReader(File file) //根据file创建一个对象
FileReader(String fileName)//根据文件目录创建一个对象

2、常用方法

public int read()//从文件中读取一个字符,如果文件中没有字符了返回-1
public int read(char[] cbuf)//从文件中读取多个字符,如果文件中没有字符了返回-1,如果有返回读了多少个字符
public void close();//关闭流

使用FileReader读取hello.txt中的内容
hello.txt
技术图片

    @Test
    public void test1()   {
        FileReader reader = null;
        try {
            File file = new File("hello.txt");
            reader = new FileReader(file);
            char[] cbuf = new char[5];
            int len = 0;
            // 方式一,每次读一个字符
            while((len = reader.read()) != -1){//每次读一个字符
                System.out.print((char)len);

            }
            /*方式二,每次读多个字符
            while ((len = reader.read(cbuf)) != -1){
                for (int i = 0; i < len; i++) {//这里不能使用cbuf.length;因为在最后可能字符不够5个,所以使用len
                    System.out.print(cbuf[i]);
                }
            }
            */
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
/*
运行结果:
      hellojava
*/

使用IO流都有可能会出现异常,所以在这里需要使用trycatchfinally来处理异常

字符输出流

FileWriter

1、构造方法

FileWriter(File file) //根据file创建一个对象
FileWriter(File file, boolean append) // 根据file创建一个对象,并根据append判断在多次写入数据是否覆盖,默认false,选择覆盖
FileWriter(String fileName) // 根据字符串创建一个对象
FileWriter(String fileName, boolean append) //同上,true表示不覆盖,false表示覆盖,默认false

2、常用方法

public void wirte(int c)//写入一个字符
public void wirte(char[] cbuf)//写入多个字符
public void wirte(char[] cbuf, int off, int len)//写入字符的长度由len决定,off表示从那开始。
public void wirte(String str)//写入字符串
public void wirte(String str, int off, int len)//写入部分字符串,有off和len决定,off表示开始的下标,len表示长度
public void flush()//刷新缓存区,如果没有关闭流则数据不会写入文件
public void close()//关闭流,关闭流之后会自动刷新缓存

使用FileWriter向hello.txt中写入数据

    @Test
    public void test8(){
        FileWriter fw = null;
        try {
            File file = new File("hello.txt");
            fw = new FileWriter(file);
            String str = "i love java";
            fw.write(str);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fw != null) {
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

结果:
技术图片

注意:字符流不能处理非文本的数据

字节流

非纯文本文件使用字节流来处理

字节输入流

FileInputStream

1、构造方法

FileInputStream(File file)//根据file创建一个字节输入流对象
FileInputStream(String name)//根据文件路径创建一个字节输入流对象

2、常用方法

public int read()//从文件中读取一个字节,如果文件中没有字节了返回-1
public int read(b[] b)//从文件中读取多个字节,如果文件中没有字节了返回-1,如果有返回读了多少个字节
public void close();//关闭流

使用FileInputStream读取hello.txt中的内容

    @Test
    public void test9(){
        FileInputStream fis = null;
        try {
            File file = new File("hello.txt");
            fis = new FileInputStream(file);
            int len;
            while((len = fis.read()) != -1){//每次读一个字节
                System.out.print((char)len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fis != null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

字节输出流

FileOutputStream

1、构造方法

FileOutputStream(File file)//根据file创建一个字节输出流对象
FileOutputStream(File file, boolean append)//通过append判断是否覆盖,默认为false,覆盖,true为不覆盖
FileOutputStream(String name) //根据文件路径创建一个字节输出流对象
FileOutputStream(String name, boolean append)//同上

2、常用方法

public void wirte(int c)//写入一个字节
public void wirte(byte[] b)//写入多个字节
public void wirte([] b, int off, int len)//写入字节的长度由len决定,off表示从那开始。
public void close()//关闭流,

使用FileOutputStream向hello.txt中写入数据

    @Test
    public void test10(){
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(new File("hello.txt"));
            String str = "hellojava";
            fos.write(str.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fos != null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

运行结果:
技术图片

因为我都选择默认不append,所以每次都是将之前的覆盖掉

缓冲流

缓冲流的主要目的就是提高之前的流的读取或写入的速度。
缓冲流的使用方法和字符流或字节流一样。这里就介绍一下如何声明一个缓冲流。

字符缓冲流

1、构造方法

BufferedReader(Reader in)
BufferedWriter(Writer out)
注意:构造方法的参数是一个抽象类,在传参的时候应该传该抽象类的实现类
使用

BufferedReader br = new BufferedReader(new FileReader(new File("hello.txt")));
BufferedWriter bw = new BufferedWriter(new FileWriter(new File("hello.txt")));

2、特有方法

public String readLine()//读一行
public void newLine()//换行

字节缓冲流

1、构造方法

BufferedInputStream(InputStream in)
BufferedOutputStream(OutputStream out)
注意:构造方法的参数是一个抽象类,在传参的时候应该传该抽象类的实现类
使用

BuffededInputStream bis = new BufferedInputStream(new FileInputStream("hello.txt"));
BufferedOutputStream bos = new BufferedOutputStream(new FileInputStream("hello.txt"));

转换流

转换流就是将字节流转换为字符流或将字符流转换为字节流。

InputStreamReader(将字节流转换为字符流)

1、构造方法

InputStreamReader(InputStream in) //创建一个默认字符集的转换流
InputStreamReader(InputStream in, String charsetName) //创建一个指定字符集的转换流

2、使用

@Test
    public void test11(){
        InputStreamReader isr = null;
        InputStreamReader isr1 = null;
        try {
            isr = new InputStreamReader(new FileInputStream("hello.txt"));//默认字符集
            isr1 = new InputStreamReader(new FileInputStream("hello.txt"),"GBK");//默认GBK字符集,会出现乱码
            int len = 0;
            while((len = isr.read()) != -1){
                System.out.print((char)len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(isr != null) {
                try {
                    isr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
/*
运行结果:
hellojava
*/

OutputStreamWriter(将字符流转换为字节流)

1、构造方法

OutputStreamWriter(OutputStream out)//创建一个默认字符集的转换流
OutputStreamWriter(OutputStream out, String charsetName) //创建一个指定字符集的转换流

2、使用

@Test
    public void test12(){
        OutputStreamWriter osw = null;
        try {
            osw = new OutputStreamWriter(new FileOutputStream("hello.txt",true));
            String str = "hellojava";
            osw.write(str);

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

运行结果:
技术图片

序列化流

序列化流分为:序列化和反序列化
序列化:就是将自己写的一个对象持久化到硬盘上,
反序列化:将序列化到硬盘上的对象取出来
要求:序列化的类必须实现Serializable接口,而且这个类的属性也必须是可序列化的。8个基本类型是可序列化的。**

重点:被static和transient修饰的属性是不会被序列化的。

序列化-ObjectOutputStream

1、构造方法

ObjectOutputStream(OutputStream out) //参数是一个抽象类,需要传一个它的子类

2、使用

    @Test
    public void test13(){
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("hello.txt"));
            oos.writeObject(new User("Tom",3));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

运行结果:
技术图片

报错了,为什么呢?因为User类没有实现Serializable接口。当实现了接口后就能正常的序列化了。

ObjectInputStream-反序列化

1、构造方法

ObjectInputStream(InputStream in)

2、使用

    @Test
    public void test14(){
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("hello.txt"));
            User user = (User) ois.readObject();
            System.out.println(user);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            if(ois != null){
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }  

运行结果:
技术图片

反序列化后,得到了刚才序列化的内容。

写在最后

花了一天时间,总算是自己把IO的部分总结了一下,怎么说呢,总结的肯定是不够完整和细致的,以后在实践中慢慢的在完善吧!
自己还是太菜了。需要学习的地方还很多!不学习就找不到工作!!!!!

























































以上是关于Java-IO流学习的主要内容,如果未能解决你的问题,请参考以下文章

Java-IO流实验

Java-io流

java-IO流-其他流

java-IO流

Java-IO流

Java-IO流总结