一文搞定Java的输入输出流等常见流

1.IO流分析

  • 什么是IO?
  • I:Input
  • O:Output
    通过IO可以完成对硬盘的读和写。
    在这里插入图片描述
  • IO流的分类。
    有多种分类方式:
  • 一种方式是:按照列的方向进行分类,以内存为参照物,往内存中去叫做输入(Input),或者叫做读(Read).往内存中出来叫做输出(Output),或者叫做写。
  • 第二种:以读取数据方式的不同进行分类:
    有的流是按照字节的方式读取数据。一次读取一个字节byte,等同于读取8个二进制。这种流是万能的,什么文件都可以读取,比如:文本,图片,视频…
    有的流是按照字符的方式读取数据的,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的,这种流不能读取:图片,视频等文件,连word文件都不能读取。
    例如:假设文本文件file.txt,采用字符流的话:
    a中国bo张三
    第一次读:‘a’字符('a’字符在win系统中占用1个字节)
    第二次读:’中’字符('中‘字符在win系统中占用2个字节)
    总结列的分类:
    输入流 ,输出流。
    字节流,字符流。
  • Java中char字符占用两个字节,但系统中占一个字节。
    注意:Java中的IO流都已经写好了,我们要会用就可以了。主要研究怎么new流对象,调用哪个对象的哪个方法读,哪个方法写。

2.常用的IO流

Java中所有的流在:java.io.*;
Java IO流的四大家族(都为抽象类):

  • java.io.InputStream 字节输入流
  • java.io.OutputStream 字节输出流
  • java.io.Reader 字符输入流。
  • java.io.Writer 字符输出流。
    Java中以Stream结尾的都是字节流,以Reader/Writer结尾的都是字符流。
  • 所有的流都是可关闭的,都有close()方法,流是一个管道,是内存和硬盘之间的管道,用完后一定要关闭,不然会浪费很多资源。
  • 所有的输出流:都实现了java.io.Flushiable接口,都是可刷新的,都有flush方法,输出流最后输出之后,一定要记得flush(),刷新一下,这个刷新表示将管道中剩余为输出的数据强行输出完(清空管道),刷新就是为了清空管道。如果没有flush()肯能会造成数据丢失。
  • java.io包下我们需要掌握的16个流:
    //文件
    java.io.FileInputStream
    java.io.FileOutputStream
    java.io.FileReader
    java.io.FileWriter
    //转换流:(将字节流转换成字符流)
    java.io.InputStreamReader
    java.io.OutputStreamWriter
    //缓存流
    java.io.BufferedReader
    java.io.BufferedWriter
    java.io.BufferedInputStream
    java.io.BufferedOutputStream
    数据流
    java.io.DateInputStream
    java.io.DateOutputStream
    标准流
    java.io.PrintWriter
    java.io.PrintStream
    对象流
    java.io.ObjectInputStream
    java.io.ObjectOutputStream

3.FileInputStream

  • 文件字节输入流,万能的,任何类型的文件都可以采用这个流来读。
  • read()方法读取一个字节的数据,到文件末尾返回-1。
public class FileInputStreamTest1 {
    public static void main(String[] args) {

//创建文件字节输入流对象
        FileInputStream fis = null;

        {
            try {
                fis = new FileInputStream("E:\\A/Cat.txt");//双斜杠代表一个斜杠或用反斜杠
                while (true) {
                    int readData = fis.read();//读取到字节本身
                        if(readData==-1)
                            break;
                    System.out.println(readData);
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {//在finally语句块中确保流一定关闭
                if (fis != null) {//关闭流的前提是,流不是空
                    try {
                        fis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
  • read(byte[] t):一次最多读取t.length个字节,返回读取的数量,注意在数组中第二次读的数据,将第一次读的数据覆盖,第一次没有覆盖的数据还在数组中。如果一个都没有读取到将返回-1。减少内存和硬盘的交互。
    可以利用String类的转换方法,将byte数组转换成String.
public class FileInputStreamTest2 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("Chapter1\\Team");
//            准备一个byte数组
            byte[] bytes = new byte[4];
            int readCount =0;
            while ((readCount=fis.read(bytes))!=-1){
                System.out.println(new String(bytes,0,readCount));//将数组转换成字符串
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • IDEA默认的当前路径是工程project的根,不是模块。
  • available()方法:返回流当中剩余的没有读到的字节数。
  • skip()方法:跳过几个字节不读。
public class FileInputStreamTest3 {
    public static void main(String[] args) {
        FileInputStream pis = null;
        try {
            pis = new FileInputStream("Chapter1\\Team");
           /* int readByte = pis.read();
//            剩余的字节数量,其作用为...
            System.out.println(pis.available());
//            作用
            byte[] bytes= new byte[pis.available()];
//            不需要循环了,因为数组不能太大所以不适合大文件
            int readCount = pis.read(bytes);
            System.out.println(new String(bytes));*/
//           skip跳过几个字节不读取
            pis.skip(3);
            System.out.println(pis.read());
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(pis != null){
                try {
                    pis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

4.FileOutputStream

  • 文件字节输出流,负责写。
  • 从内存到硬盘
  • write(int b)方法。
  • write(byte[] b)方法。
  • write(byte[] b,int off,int len)方法。
public class FileOutputStreamTest1 {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        try {
//            文件不存在会新建,将源文件清空再写入
//            fos=new FileOutputStream("Chapter1\\Team");
//            以追加的方式写入
            fos = new FileOutputStream("Chapter1\\Team",true);
            byte[] bytes = {97,98,99};
            String s ="我是一个中国人";
            byte[] aa= s.getBytes();//将字符串转换成byte[]数组
            fos.write(aa);
            fos.write(bytes);
//            写部分
            fos.write(bytes,0,1);
//            注意写完后要刷新

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

    }
}

5.通过Stream类实现文件复制

使用FileInputStream+ FileOutputStream完成文件的拷贝。

  • 适用于任何文件。
  • 练习:
public class Copy1 {
    public static void main(String[] args) {
        FileInputStream fis =null;
        FileOutputStream fos =null;
        try {
            fis = new FileInputStream("F:\\视频\\VID_20190425_125038.mp4");
            fos = new FileOutputStream("D:\\VID_20190425_125038.mp4");
//            一边读一边写
            byte[] bytes = new byte[1024*1024];//一次最多1M
            int  readCount =0;
//            读多少写多少
            while ((readCount=fis.read(bytes))!=-1){
                fos.write(bytes,0,readCount);//写
            }
//            记得刷新
            fos.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            if(fos!=null){
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(fis!=null){
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

6.FileReader

  • 文件字符输入流,只能读取普通文本。
  • 读取文本内容时,比较方便,快捷。
  • 操作跟FileInputStream类似,将byte数组,改为char数组即可,也可以用String的方法将其装换成字符串形式。

7.FileWriter

  • 文件字符输出流,写。
  • 只能输出普通文本。
    例子:
    char[] chars = {’我‘,’是‘,'中’}
  • write(chars)
  • writer(chars,0,2);
  • writer("我是中国人“);
  • 不想被清空再创建输出流对象是,在文件名后面的第二个可填项,添加true)
public class FileWriterTest1 {
    public static void main(String[] args) {
        FileWriter out = null;
        try {
            out = new FileWriter("file",true);
            out.write("我是中国人" + "哈哈");
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            if(out!=null){
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}
  • 通过Writer实现拷贝普通文件:操作跟Stream一样,只不过将byte数组改为Char数组。

8.BufferedReader缓冲流

  • 带有缓冲区的字符输入流。
  • 使用这个流的时候不需要自定义char数组,或byte数组,自带缓冲。
  • readLine()方法:读一行。
public class BufferedReaderTest1 {
    public static void main(String[] args)  {
        FileReader reader = null;
        try {
            reader = new FileReader("file");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        //当一个流的构造方法需要一个流的时候,这个被传进来的流叫做“节点流”。
        //外部负责包装的这个流,叫做:包装流,还有一个名字叫做:处理流
        //当前FileReader是节点流,BufferedReader流叫做包装流
        BufferedReader sc = new BufferedReader(reader);
        //对于包装流来说,只需要关闭外层流就行,里面的节点流会自动关闭
//        readLine方法
        String s =null;
        while (true) {
            try {
                if (!((s=sc.readLine())!=null)) break;
            } catch (IOException e) {
                e.printStackTrace();
            }
            System.out.println(s);
        }
        try {
            sc.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

练习二:涉及装换流

package Day1;


import java.io.*;

public class BufferedReaderTest2 {
    public static void main(String[] args) {

        FileInputStream in = null;
        BufferedReader ge=null;
        try {
            /*//字节流
            in = new FileInputStream("file");
            //        通过转换流转换,in是节点流,reader是包装流
            InputStreamReader reader = new InputStreamReader(in);
            // 这个构造方法只能传一个字符流,不能传字节流,reader是节点流
             ge = new BufferedReader(reader);*/
            String line = null;
//            将以上三个步骤合并
            ge= new BufferedReader(new InputStreamReader(new FileInputStream("file")));
            while((line=ge.readLine())!=null){
                System.out.println(line);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(ge!=null){
                try {
                    ge.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

9.DataInputStream和DataOutputStream数据流

  • DataOutputStream这个流可以将数据连同数据的类型一并写入文件。
  • 不是普通文档。
  • DateOutputStream写的文件,只能使用DataInputStream去读。并且度的时候需要提前知道写入的顺序。读的顺序要和写的顺序一致,才可以正常取出数量。
package Day2;

import javax.swing.*;
import java.io.DataOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class DateInputStreamTest1 {
    public static void main(String[] args) {

        DataOutputStream dos = null;

        {
            try {
                dos = new DataOutputStream(new FileOutputStream("Team"));
//            写数据
                byte b = 0;
                short s = 300;
                int i = 322;
                long c = 455L;
                double d = 3.4;
                boolean sex = false;
                char f = 'a';
//            写,包括类型
                dos.writeByte(b);
                dos.writeShort(s);
                dos.writeInt(i);
                dos.writeLong(c);
                dos.writeDouble(d);
                dos.writeBoolean(sex);
                dos.writeChar(f);
//            刷新
                dos.flush();

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

10.标准输出流PrintStream

  • 标准的字节输出流,默认输出流,默认输出到控制台。
  • 标准输出流不需要手动close关闭。
  • 存在节点流,和包装流。
package Day2;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;

public class PrintStreamTest1 {
    public static void main(String[] args) throws FileNotFoundException {
        System.out.println("Hello World!");
        PrintStream print = System.out;
        print.println("Oh my god!");
//        可以改变标准输出流的方向
       /* System.gc();
        System.currentTimeMillis();
        PrintStream print  = System.out;
        System.exit(0);
        System.arraycopy()*/
//       标准输出流指向file文件,不指向控制台
       PrintStream printStream=new PrintStream(new FileOutputStream("file"));
//         修改输出方向
        System.setOut(printStream);
        System.out.println("Hello SZ");
    }
}

练习2:

package Day2;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;

public class PrintStreamTest1 {
    public static void main(String[] args) throws FileNotFoundException {
        System.out.println("Hello World!");
        PrintStream print = System.out;
        print.println("Oh my god!");
//        可以改变标准输出流的方向
       /* System.gc();
        System.currentTimeMillis();
        PrintStream print  = System.out;
        System.exit(0);
        System.arraycopy()*/
//       标准输出流指向file文件,不指向控制台
       PrintStream printStream=new PrintStream(new FileOutputStream("file"));
//         修改输出方向
        System.setOut(printStream);
        System.out.println("Hello SZ");
    }
}

练习三(记录日志):

package Day2;

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.text.SimpleDateFormat;
import java.util.Date;

public class LogTest1 {
    public static void main(String[] args) {
            logs("成功完成了日志的代码!");
            logs("完成Java的学习!");
            logs("登入系统成功!");
            logs("你向某某支付XXX钱!");
            logs("你收到来着某某的XXXXXXXX人民币!");
    }
public static void logs (String s){
    //        指向日志文件
    PrintStream log =null;
    try {
        log = new PrintStream(new FileOutputStream("log.txt",true));
//             改变输出方向
        System.setOut(log);
//        获取当前时间
        Date nowTime = new Date();
        SimpleDateFormat ss = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
                String times=ss.format(nowTime);
        System.out.println(times+"  "+s);
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
    //不需要关闭

  }
}

11.File类

  • File类和四大家族没有关系,所以不能完成文件的读取。
    File对象代表什么?
  • 文件和目录路径名的抽象表示形式。
    C:\Drivers 这是一个File对象。
    C:\Drevers\reader.txt 也是一个File对象。
  • File只是一个路径名的表现形式。
    FIle的常用方法:
  • 以下通过练习来说明
package Day2;

import java.io.File;
import java.io.IOException;

public class FileTest1 {
    public static void main(String[] args) {
//        创建一个File对象
        File f1 = new File("D:\\tt.txt");
//        判断文件是否存在
        System.out.println(f1.exists());
//        1.如果D:\tt.txt不存在,以文件的形式创建
       /* if(!f1.exists()){
            try {
                f1.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }*/
//       2.如果不存在,以目录的形式创建
        /*if(!f1.exists()){
            f1.mkdir();
        }*/
//        3.以多重目录新建
       /* File f2 =new File("D:\\xi\\c\\z\\a");
            if(!f2.exists()){
                f2.mkdirs();
            }*/
//        4.获取文件的父路径
        File f3 = new File("D:\\WeChat\\locales");
        String parentPath = f3.getParent();
        System.out.println(parentPath);
//        5.获取绝对路径
        File f4 = new File("Team");
        System.out.println(f4.getAbsolutePath());
        
    }
}

练习2:

package Day2;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;

public class FileTest2 {
    public static void main(String[] args) {
        File f1 = new File("D:\\resources");
//      6.获取文件名
        System.out.println(f1.getName());
//        7.判断是否时一个目录
        System.out.println(f1.isDirectory());
//        8.判断是否是一个文件
        System.out.println(f1.isFile());
//        9.获去文件最后一次修改时间
        long haoMiao = f1.lastModified();
//        将总毫秒树转换成日期
        Date time  = new Date(haoMiao);
        SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
        System.out.println(sd.format(time));
//        10.获取文件大小,字节
        System.out.println(f1.length());
//        11.获取当前目录下所有的子文件
        File[] files = f1.listFiles();
        for(File file :files){
            System.out.println(file);
        }
    }
}

12.拷贝目录练习(重点)

package Day2;

import java.io.*;

public class CopyContent1 {
    public static void main(String[] args) {
//        拷贝源
        File srcFile = new File("F:\\QQ");
//        拷贝目标(放哪里)
        File destFile = new File("D:\\");
//        调用方法
        copyDir(srcFile,destFile);
    }

    private static void copyDir(File srcFile, File destFile) {
        if(srcFile.isFile()){//如果是文件递归结束
            /*是文件就拷贝,一边读,一边写*/
            FileInputStream in = null;
            FileOutputStream out = null;
            try {
                //读
                in  = new FileInputStream(srcFile);
                //写
                String path = (destFile.getAbsolutePath().endsWith("\\") ? destFile.getAbsolutePath() : destFile.getAbsolutePath() + "\\" )+srcFile.getAbsolutePath().substring(3);

                out = new FileOutputStream(path);
                byte[] bytes = new byte[1024*1024];
                int readCount =0;
                while((readCount=in.read(bytes))!=-1){
                    out.write(bytes,0,readCount);//读多少写多少
                }
                out.flush();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }finally {
                if(out!=null){
                    try {
                        out.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if(in!=null){
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return;
        }
//        获取源的子目录
       File[] files=  srcFile.listFiles();
        for(File file :files){
            if(file.isDirectory()) {
//            获取所有文件的绝对路径
//            System.out.println(file.getAbsolutePath());
//            新建目标,对应的目录,将源目录除根目录外,其余加到目标目录后面
                String srcDir = file.getAbsolutePath();
                String destDir = (destFile.getAbsolutePath().endsWith("\\") ? destFile.getAbsolutePath() : destFile.getAbsolutePath() + "\\" )+ srcDir.substring(3);
                File newFile = new File(destDir);
                if (!newFile.exists()) {
                    newFile.mkdirs();
                }
            }
//            字目录可能还是目录
            copyDir(file,destFile);
        }
    }
  }


13.ObjectInputStream和ObjectOutputStream对象流

  • 序列化(Serialize):将内存的对象数据信息存到磁盘中。(拆分对象)
  • 反序列化(DeSerialize):将硬盘的数据重新恢复到内存当中,恢复成java对象。
    在这里插入图片描述
  • 参与序列化和反序列化的对象必须实现Serializalbe接口。
  • Serializalbe接口,只是一个标志性接口,起到标志作用,让JVM参考,JVM会为该类创建一个序列化版本号。
  • 序列化版本号的作用:(区分类)
    第一:Java通过类名进行比对,如果类名不一样,肯定不是同一个类。
    第二:如果类名一样,靠序列化版本号区分。
    就算两个人写了同一个类,jvm但不是同一个类,这个时候序列化版本号可以将他们区分。
  • 十年前写的代码放在硬盘,后面改动源代码,重新编译之后,生成了全新的字节码文件。(自动序列化缺点:代码确定后,不能修改,JVM就会认为不是同一个类)
    ==结论:凡是实现Serializable的类,建议收到将序列化版本号写出来,不建议自动生成。
    private static final long serizlVersionUID =456464656546465465L; 建议全球唯一
    ==
  • IDEA自动生成版本号,先到settings->Inspections(搜索Serializable接口),后面鼠标移动到类名,ALt+回车。
package Day2;

import java.io.*;

public class ObjectInputStreamTest1 {
    public static void main(String[] args) {
        Student ss = new Student(1111,"zhangsh");
        ObjectOutputStream oss =null;
        try {
            //      序列化
             oss = new ObjectOutputStream(new FileOutputStream("Students"));
             //序列化对象
            oss.writeObject(ss);
             oss.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            if(oss!=null){
                try {
                    oss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
class Student implements Serializable{//可序列化接口
    private int no;
    private String name;

    public Student() {
    }

    public Student(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
}

反序列化:

package Day2;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class ObjectInputStreamTest2 {
    public static void main(String[] args) {
        ObjectInputStream ois =null;
        try {
            ois = new ObjectInputStream(new FileInputStream("Students"));
            //反序列化
            Object obj = ois.readObject();
            System.out.println(obj);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            if(ois!=null){
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

  • 将对象放到集合当中,序列化集合,要求对象和集合都要实现Serializalbe接口。
package Day2;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class ObjectInputStreamTest1 {
    public static void main(String[] args) {
        ObjectOutputStream oss =null;
        List<Student> list= new ArrayList<>();
        list.add(new Student(33,"adsfsa"));
        list.add(new Student(36,"aa"));
        list.add(new Student(39,"adsa"));
        try {
            //      序列化
             oss = new ObjectOutputStream(new FileOutputStream("Students"));
             //序列化对象
            oss.writeObject(list);
             oss.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            if(oss!=null){
                try {
                    oss.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
class Student implements Serializable{//可序列化接口
    private int no;
    private String name;

    public Student() {
    }

    public Student(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Student{" +
                "no=" + no +
                ", name='" + name + '\'' +
                '}';
    }
}
  • 集合中的对象反序列化,返回的是list类型,需要强转。
    在这里插入图片描述
  • 如果希望某个属性不参与序列化,可以在属性前加 transient关键字,表示游离的。

14.IO +Properties联合使用

IO流:文件的读和写。
Properties:是一个Map集合,key和value都是String类型。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

发布了50 篇原创文章 · 获赞 8 · 访问量 2891

猜你喜欢

转载自blog.csdn.net/jiahuan_/article/details/105310304