java IO流

java IO流

主要内容

  • java.io.File类的使用
  • IO原理及流的分类
  • 文件流
    • FileInputStream/FileOutputStream
    • FileReader/FileWriter
  • 缓冲流
    • BufferedInputStream/BufferedOutputStream
    • BufferedReader/BufferWriter
  • 转换流
    • InputStreamReader/OutputStreamWriter
  • 标准输入/输出流
  • 打印流(了解)
    • PrintStream/PrintWriter
  • 数据流(了解)
    • DataInputStream/DataOutputStream
  • 对象流 —— 涉及序列化、反序列化
    • ObjectInputStream/ObjectOutputStream
  • 随机存取文件流
    • RandomAccessFile

(一)File

java.io.File类
1.凡是与输入、输出相关的类、接口等相关的都定义在java.io包下
2.File是一个类,可以有构造器创建其对象。此对象对应着一个文件(.txt .avi .doc ...)或文件夹
3.File类对象是与平台无关的
4.File中的方法,仅涉及到如何创建、删除、重命名等,只要涉及到文件内容的,File是无能为力的,必须有io流来完成
5.File类的对象常作为io流的具体类的构造器的形参

路径:

  • 绝对路径:包含盘符在内的完整的文件路径
  • 相对路径:在当前文件目录下的文件的路径

访问文件名

  • getName()
  • getPath()
  • getAbsoluteFile()
  • getAbsolutePath()
  • getParent()
  • renameTo(File newName)
    @Test
    public void test1() {
        File file1 = new File("E:/io/helloworld.txt");
        File file2 = new File("E:/io");
        File file3 = new File("hello.txt");//存在
        File file4 = new File("hello1.txt");//不存在

        System.out.println("获取名称:" + file1.getName());
        System.out.println("获取路径:" + file1.getPath());
        System.out.println("获取绝对名称:" + file1.getAbsoluteFile());
        System.out.println("获取父目录:" + file1.getParent());
        System.out.println("获取绝对路径:" + file1.getAbsolutePath());

        System.out.println("---------------------");

        System.out.println("获取名称:" + file2.getName());
        System.out.println("获取路径:" + file2.getPath());
        System.out.println("获取绝对名称:" + file2.getAbsoluteFile());
        System.out.println("获取父目录:" + file2.getParent());
        System.out.println("获取绝对路径:" + file2.getAbsolutePath());

        System.out.println("---------------------");
        
        //renameTo(File newName): 重命名
        //file1.renameTo(file2):要求:file1必须存在,file2必须不存在
        boolean b = file3.renameTo(file1);
        System.out.println(b);
        boolean c = file3.renameTo(file4);
        System.out.println(c);
    }


------------- 运行结果 --------------
获取名称:helloworld.txt
获取路径:E:\io\helloworld.txt
获取绝对名称:E:\io\helloworld.txt
获取父目录:E:\io
获取绝对路径:E:\io\helloworld.txt
---------------------
获取名称:io
获取路径:E:\io
获取绝对名称:E:\io
获取父目录:E:\
获取绝对路径:E:\io
---------------------
false
true

文件检测

  • exists()
  • canWriter()
  • canRead()
  • isFile()
  • isDirectory()

获取常规文件信息

  • lastModified():返回最后一次修改后事件的long值
  • length()
    @Test
    public void test2(){
        File file1 = new File("E:/io/helloworld.txt");
        File file2 = new File("E:/io/io1");

        System.out.println("是否存在:" + file1.exists());
        System.out.println("是否可写:" + file1.canWrite());
        System.out.println("是否可读:" + file1.canRead());
        System.out.println("是不是文件:" + file1.isFile());
        System.out.println("是不是文件夹:" + file1.isDirectory());
        System.out.println("最后修改时间:" + file1.lastModified());
        System.out.println("最后修改时间(本地):" + new Date(file1.lastModified()).toLocaleString());
        System.out.println("内容大小:" + file1.length());

        System.out.println("------------------------");

        System.out.println("是否存在:" + file2.exists());
        System.out.println("是否可写:" + file2.canWrite());
        System.out.println("是否可读:" + file2.canRead());
        System.out.println("是不是文件:" + file2.isFile());
        System.out.println("是不是文件夹:" + file2.isDirectory());
        System.out.println("最后修改时间:" + file2.lastModified());
        System.out.println("最后修改时间(本地):" + new Date(file2.lastModified()).toLocaleString());
        System.out.println("内容大小:" + file2.length());
    }


------------- 运行结果 --------------
是否存在:true
是否可写:true
是否可读:true
是不是文件:true
是不是文件夹:false
最后修改时间:1534142862439
最后修改时间(本地):2018-8-13 14:47:42
内容大小:10
------------------------
是否存在:true
是否可写:true
是否可读:true
是不是文件:false
是不是文件夹:true
最后修改时间:1534146413112
最后修改时间(本地):2018-8-13 15:46:53
内容大小:0

文件操作

  • createNewFile()
  • delete()

目录操作

  • mkDir()
  • mkDirs()
  • list()
  • listFiles()
    /**
     * mkdir():创建一个文件目录、只有在上层文件目录存在的情况下,才能返回true
     * mkdirs():创建一个文件目录、若上层文件文件目录不存在,一并创建
     *
     * @throws IOException
     */
    @Test
    public void test3() throws IOException {
        File file1 = new File("E:/io/helloworld.txt"); //存在
        File file2 = new File("E:/io/io2"); //不存在
        File file3 = new File("E:/io1/io2"); //io1、io2都不存在
        File file4 = new File("D:/"); //io1、io2都不存在

        System.out.println("删除文件file1:" + file1.delete());

        if (!file1.exists()) {
            boolean b = file1.createNewFile();
            System.out.println("文件是否创建成功:" + b);
        }

        System.out.println("------------------------");
        System.out.println("删除目录file2:" + file2.delete());

        if (!file2.exists()) {
            boolean b = file2.mkdir();
            System.out.println("目录是否创建成功:" + b);
        }

        System.out.println("------------------------");
        if (!file3.exists()) {
            boolean b = file3.mkdirs();
            System.out.println("多层目录是否创建成功:" + b);
        }

        System.out.println("------------------------");
        String[] list = file4.list();
        System.out.println("使用list()方法:");
        if (list != null)
            for (String s : list)
                System.out.println("--- " + s);


        System.out.println("------------------------");
        File[] files = file4.listFiles();
        System.out.println("使用listFiles()方法:");
        if (files != null)
            for (File file : files)
                System.out.println("--- " + file.getName());
    }


------------- 运行结果 --------------
删除文件file1:true
文件是否创建成功:true
------------------------
删除目录file2:false
目录是否创建成功:true
------------------------
多层目录是否创建成功:true
------------------------
使用list()方法:
--- $RECYCLE.BIN
--- Config.Msi
--- del_data.sql
--- game
--- hr_cre.sql
--- hr_popul.sql
--- JetBrains
--- Program
--- Program Files
--- soft
--- System Volume Information
--- Tencent
--- Virtual Machines
--- zlex.cer
--- zlex.keystore
------------------------
使用listFiles()方法:
--- $RECYCLE.BIN
--- Config.Msi
--- del_data.sql
--- game
--- hr_cre.sql
--- hr_popul.sql
--- JetBrains
--- Program
--- Program Files
--- soft
--- System Volume Information
--- Tencent
--- Virtual Machines
--- zlex.cer
--- zlex.keystore

遍历目录

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

/**
 * 遍历目录
 */
public class DirErgodic {

    public static int depth = 1;
    public static int fileCount = 0; //文件个数
    public static int folderCount = 0; //目录个数

    public static void traverseFolder(String pathName, int depth) throws IOException {

        //获取fileName的File对象
        File dirFile = new File(pathName);

        //判断该文件或目录是否存在,不存在时控制台输出
        if (!dirFile.exists()) {
            System.out.println("this dir not exit");
            return;
        }
        //判断如果不是一个目录,就判断是不是一个文件,时文件则输出文件路径
        if (!dirFile.isDirectory()) {
            if (dirFile.isFile()) {
                System.out.println(dirFile.getCanonicalFile());//获取标准的绝对路径
                fileCount++;
                System.out.println(fileCount);
            }
            return;
        }

        for (int i = 0; i < depth - 1; i++) {
            System.out.print("   ");
        }

        System.out.print("|--");
        System.out.println(dirFile.getName());

        //获取此目录下的所有文件名与目录名
        String[] fileList = dirFile.list();
        int currentDepth = depth + 1;

        /**
         * assert后边跟一个布尔表达式,同时再跟一个返回值为基本类型的表达式。
         * 当表达式为true时,则继续运行剩余业务代码,不会执行‘:’后边的表达式。
         * 当表达式为false时,则会执行‘:’后边的表达式,并将结果放置在AssertionError异常中,并抛出。
         */
        assert fileList != null;
        for (String str : fileList) {//遍历文件目录
            File file = new File(dirFile.getPath(), str);
            String name = file.getName();

            //如果是一个目录,搜索深度depth++,输出目录名后,进行递归
            if (file.isDirectory()) {
                //进行递归
                folderCount++;
                traverseFolder(file.getCanonicalPath(), currentDepth);
            } else {
                //如果是文件,则输出文件名
                for (int i = 0; i < currentDepth - 1; i++) {
                    System.out.print("   ");
                }
                fileCount++;
                System.out.print("|--");
                System.out.println(name);
            }
        }
    }

    public static void main(String[] args) throws IOException {
        DirErgodic.traverseFolder("E:\\U盘文件", DirErgodic.depth);
        System.out.println("文件个数:" + DirErgodic.fileCount);
        System.out.println("目录个数:" + DirErgodic.folderCount);
    }
}

(二)IO流概述

Java IO原理

  • IO流用来处理设备之间的数据传输。
  • Java程序中,对于数据的输入/输出操作以”流(stream)” 的方式进行。
  • java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。

java IO原理

  • 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中
  • 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中

流的分类

流的分类结构图

流的分类结构图

  • 按操作数据单位不同分为:字节流(8bit),字符流(16bit)
  • 按数据流的流向不同分为:输入流,输出流
  • 按流的角色的不同分为:节点流,处理流
(抽象基类) 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer
  1. Java的IO流共涉及40多个类,实际上非常规则,都是从这4个抽象基类派生的
  2. 由这4个类派生出来的子类名称都是以其父名作为子类名后缀

IO流体系

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

InputStream&Reader

  • InputStream 和 Reader 是所有输入流的基类。
  • InputStream(典型实现:FileInputStream
    • int read()
    • int read(byte[] b)
    • int read(byte[] b, int off, int len)
  • Reader(典型实现:FileReader
    • int read()
    • int read(char [] c)
    • int read(char [] c, int off, int len)
  • 程序中打开的文件 IO 资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件 IO 资源。

OutputStream&Writer

  • OutputStream 和 Writer 也非常相似:
    • void write(int b/int c);
    • void write(byte[] b/char[] cbuf);
    • void write(byte[] b/char[] buff, int off, int len);
    • void flush();void close(); 需要先刷新,再关闭此流
  • 因为字符流直接以字符作为操作单位,所以 Writer 可以用字符串来替换字符数组,即以 String 对象作为参数
    • void write(String str);
    • void write(String str, int off, int len);

(三)文件流

FileInputStream

	//hello.txt中的内容是:abcdefg速度	

	//从硬盘存在的一个文件中,将其内容读取到程序中
    //要读取的文件一定要存在,否则抛FileNotFoundException
    @Test
    public void testFileInputStream1() throws IOException {
        //1.常见一个File对象
        File file = new File("hello.txt");

        //2.创建一个FileInputStream对象
        FileInputStream fileInputStream = new FileInputStream(file);

        //3.调用FileInputStream的方法,实现实现file文件的读取
        //read():读取文件的一个字节,当执行到文件末尾时返回-1;目前不能输出汉字
        int b;

        while ((b = fileInputStream.read()) != -1) {
            System.out.print((char) b);
        }
        //4.关闭相应的流
        fileInputStream.close();
    }

------------运行结果--------------
    abcdefg速度

改进之后的程序

	//使用try-catch处理以下异常更合理,保证流的关闭操作一定存在
    //将一次读取一个字节改为一次读取多个字节
    //可以读取汉字(读取的时候不能将汉字的字节分别读取)
    @Test
    public void testFileInputStream2() {
        FileInputStream fileInputStream = null;
        try {
            File file = new File("hello.txt");
            fileInputStream = new FileInputStream(file);
            byte[] b = new byte[10];//读取到的数据要写入的数组
            int len;//每次读入到byte中的字节的长度
            while ((len = fileInputStream.read(b)) != -1) {
                /*for (int i = 0; i < len; i++) {
                    System.out.print((char) b[i]);
                }*/
                String str = new String(b, 0, len);
                System.out.print(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

------------运行结果--------------
    abcdefg速度

FileOutputStream

 	@Test
    public void testFileOutputStream() {
        //1.创建一个File对象,表明要写入的文件位置
        //输出的物理文件可以不存在,在执行过程中,若不存在,会自动的创建
        //若存在,会将原有的覆盖
        File file = new File("Hello2.txt");
        //2.创建一个FileOutputStream对象
        FileOutputStream fileOutputStream = null;
        try {
            fileOutputStream = new FileOutputStream(file);
            String s = "I love China I love World! 我爱中国";
            //3.写入操作
            fileOutputStream.write(s.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭输出流
            if (fileOutputStream != null){
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

运行结果

FileOutputStream写入操作

文件复制(FileInputStream/FileOutputStream)

    /**
     * 实现文件复制的方法
     * @param src 文件来源的路径
     * @param dest 复制到的文件路径
     * @param speed 传输速度
     */
    public void copyFile(String src, String dest, int speed) {
        //1.提供读入、写出的文件
        File file1 = new File(src);
        File file2 = new File(dest);

        //2.提供相应的流
        FileInputStream fileInputStream = null;
        FileOutputStream fileOutputStream = null;

        try {
            fileInputStream = new FileInputStream(file1);
            fileOutputStream = new FileOutputStream(file2);
            //3.实现文件的复制
            byte[] bytes = new byte[speed];
            int len;
            while ((len = fileInputStream.read(bytes)) != -1) {
                //错误的写法
                //fileOutputStream.write(bytes, 0, bytes.length);
                //fileOutputStream.write(bytes);
                fileOutputStream.write(bytes, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //4.关闭流
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //文件复制
    @Test
    public void testInputOutputStream() {
        String src = "hello2.txt";
        String dest = "hello3.txt";
        copyFile(src, dest, 20);
    }

FileReader

将文件内容读到控制台

@Test
    public void testFileReader() {

        FileReader fileReader = null;
        try {
            File file = new File("dbcp.txt");
            fileReader = new FileReader(file);
            char[] c = new char[24];
            int len;
            while ((len = fileReader.read(c)) != -1) {
                String str = new String(c, 0, len);
                System.out.print(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileReader != null){
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

FileWriter

将内容写进文件

	@Test
    public void testFileWriter() {
        FileWriter fileWriter = null;
        try {
            File file = new File("hello4.txt");
            fileWriter = new FileWriter(file);
            String str = "I love China I love World! 我爱中国";
            fileWriter.write(str);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fileWriter != null){
                try {
                    fileWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

文件复制(FileReader/FileWriter)

    /**
     * 使用FileReader、FileWriter可以实现文本文件的复制
     * 对于非文本文件(视频、音频、图片),只能使用字节流
     * @param src 源路径
     * @param dest 目标路径
     */
    public void copyFile(String src, String dest) {
        FileReader reader = null;
        FileWriter writer = null;

        try {
            File file1 = new File(src);
            File file2 = new File(dest);

            reader = new FileReader(file1);
            writer = new FileWriter(file2);

            char[] c = new char[24];
            int len;
            while ((len = reader.read(c)) != -1) {
                writer.write(c, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Test
    public void testFileReaderWriter() {
        String src = "dbp.txt";
        String dest = "dbcp1.txt";
        copyFile(src, dest);
    }

(四)缓冲流

  • 为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组
  • 根据数据操作单位可以把缓冲流分为:
    • BufferedInputStream 和 BufferedOutputStream
    • BufferedReader 和 BufferedWriter
  • 缓冲流要“套接”在相应的节点流之上,对读写的数据提供了缓冲的功能,提高了读写的效率,同时增加了一些新的方法
  • 对于输出的缓冲流,写出的数据会先在内存中缓存,使用flush()将会使内存中的数据立刻写出

文件复制(缓冲流)

BufferedInputStream/BufferedOutputStream

    //使用缓冲流进行文件复制
    @Test
    public void testBuffered() {
        String src = "1.jpg";
        String dest = "2.jpg";
        copyFileByBuffered(src,dest);
    }

    private void copyFileByBuffered(String src, String dest) {
        BufferedInputStream bufferedInputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        try {
            File file1 = new File(src);
            File file2 = new File(dest);

            bufferedInputStream = new BufferedInputStream(new FileInputStream(file1));
            bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(file2));

            byte[] bytes = new byte[1024];
            int len;
            while ((len = bufferedInputStream.read(bytes)) != -1) {
                bufferedOutputStream.write(bytes, 0, len);
                bufferedOutputStream.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bufferedOutputStream != null){
                try {
                    bufferedOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedInputStream != null){
                try {
                    bufferedInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

BufferedReader/BufferedWriter

bufferedReader特别之处,可以使用readLine()方法一行一行地读取

bufferedWriter可以使用newLine()方法换行

	@Test
    public void testBufferedReader() {
        String src = "dbcp.txt";
        String dest = "dbcp2.txt";
        copyFile(src, dest);
    }

    private void copyFile(String src, String dest) {
        BufferedReader bufferedReader = null;
        BufferedWriter bufferedWriter = null;
        try {
            File file1 = new File("dbcp.txt");
            File file2 = new File("dbcp2.txt");
            
            bufferedReader = new BufferedReader(new FileReader(file1));
            bufferedWriter = new BufferedWriter(new FileWriter(file2));

            String str;
            while ((str = bufferedReader.readLine()) != null) {
                bufferedWriter.write(str);
                bufferedWriter.newLine();
                bufferedWriter.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bufferedWriter != null) {
                try {
                    bufferedWriter.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

(五)转换流

  • 转换流提供了在字节流和字符流之间的转换J
  • ava API提供了两个转换流:
    • InputStreamReader和OutputStreamWriter
  • 字节流中的数据都是字符时,转成字符流操作更高效。

转换流之间的关系

InputStreamReader

  • 用于将字节流中读取到的字节按指定字符集解码成字符。需要和InputStream“套接”。
  • 构造方法
    • public InputStreamReader(InputStream in)
    • public InputSreamReader(InputStream in,String charsetName)
    • 如: Reader isr = new InputStreamReader(System.in,”ISO5334_1”);

OutputStreamWriter

  • 用于将要写入到字节流中的字符按指定字符集编码成字节。需要和OutputStream“套接”。
  • 构造方法
    • public OutputStreamWriter(OutputStream out)
    • public OutputSreamWriter(OutputStream out, String charsetName)

使用InputStreamReader和OutputStreamWriter实现字节流和字符流之间的转换

    //实现字节流和字符流之间的转化
    @Test
    public void test1() {
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            //解码
            File file1 = new File("dbcp.txt");
            FileInputStream fis = new FileInputStream(file1);
            InputStreamReader isr = new InputStreamReader(fis, "GBK");
            br = new BufferedReader(isr);

            //编码
            File file2 = new File("dbcp3.txt");
            FileOutputStream fos = new FileOutputStream(file2);
            OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");
            bw = new BufferedWriter(osw);
            String str;
            while ((str = br.readLine()) != null) {
                bw.write(str);
                bw.newLine();
                bw.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bw != null){
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

补充:字符编码

  • 编码表的由来

    计算机只能识别二进制数据,早期由来是电信号。为了方便应用计算机,让它可以识别各个国家的文字。就将各个国家的文字用数字来表示,并一一对应,形成一张表。这就是编码表。

  • 常见的编码表

    • ASCII:美国标准信息交换码。
      • 用一个字节的7位可以表示。
    • ISO8859-1:拉丁码表。
      • 欧洲码表用一个字节的8位表示。
    • GB2312:中国的中文编码表。
    • GBK:中国的中文编码表升级,融合了更多的中文文字符号。
    • Unicode:国际标准码,融合了多种文字。
      • 所有文字都用两个字节来表示,Java语言使用的就是unicode
    • UTF-8:最多用三个字节来表示一个字符。

(六)标准输入输出流

  • System.in和System.out分别代表了系统标准的输入和输出设备
  • 默认输入设备是键盘,输出设备是显示器
  • System.in的类型是InputStream
  • System.out的类型是PrintStream,其是OutputStream的子类FilterOutputStream 的子类
  • 通过System类的setIn,setOut方法对默认设备进行改变。
    • public static void setIn(InputStream in)
    • public static void setOut(PrintStream out)

从键盘输入字符串,要求将读取到的整行字符串转成大写输出。然后继续进行输入操作,直至当输入“e”或者“exit”时,退出程序。

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class Standard {
    public static void main(String[] args) {
        transform();
    }
    public static void transform() {
        BufferedReader br = null;
        try {
            InputStream in = System.in;
            InputStreamReader isr = new InputStreamReader(in);
            br = new BufferedReader(isr);
            String str;
            while (true) {
                System.out.println("请输入字符串:");
                str = br.readLine();
                if (str.equalsIgnoreCase("e") || str.equalsIgnoreCase("exit"))
                    break;

                String str1 = str.toUpperCase();
                System.out.println("转成大写为:" + str1);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } 
    }
}

运行结果为:

运行结果

(七)打印流(了解)

  • 在整个IO包中,打印流是输出信息最方便的类。
  • PrintStream(字节打印流)和PrintWriter(字符打印流)
    • 提供了一系列重载的print和println方法,用于多种数据类型的输出
    • PrintStream和PrintWriter的输出不会抛出异常
    • PrintStream和PrintWriter有自动flush功能
    • System.out返回的是PrintStream的实例

打印ASCII表到print.txt 中

    @Test
    public void printStreamWriter() {
        try {
            FileOutputStream fos = new FileOutputStream(new File("print.txt"));
            //创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
            PrintStream ps = new PrintStream(fos, true);
            System.setOut(ps);// 把标准输出流(控制台输出)改成文件

            for (int i = 0; i < 255; i++) {
                System.out.print((char) i);
                if (i % 50 == 0)
                    System.out.println();
            }
            ps.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

(八)数据流(了解)

  • 为了方便地操作Java语言的基本数据类型的数据,可以使用数据流。
  • 数据流有两个类:(用于读取和写出基本数据类型的数据)
    • DataInputStream 和 DataOutputStream
    • 分别“套接”在 InputStream 和 OutputStream 节点流上
  • DataInputStream中的方法
    • boolean readBoolean() byte readByte()
      char readChar() float readFloat()
      double readDouble() short readShort()
      long readLong() int readInt()
    • String readUTF() void readFully(byte[] b)
  • DataOutputStream中的方法将上述的方法的read改为相应的write即可。

DataOutputStream

将数据写入文件中,文件中的数据是乱码

	//数据流,用来处理基本数据类型、String、字节数组的数据
    @Test
    public void testData() {
        DataOutputStream dos = null;
        try {
            dos = new DataOutputStream(new FileOutputStream(new File("data.txt")));
            dos.writeUTF("我喜欢你,你却不知道");
            dos.writeBytes("我喜欢你,你却不知道");
            dos.writeBoolean(true);
            dos.writeLong(1432525223);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (dos != null) {
                try {
                    dos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

DataInputStream

将文件中的数据打印在控制台

    @Test
    public void testData1() {
        DataInputStream dis = null;

        try {
            dis = new DataInputStream(new FileInputStream(new File("data.txt")));

            System.out.println(dis.readUTF());
            System.out.println(dis.readByte());
            System.out.println(dis.readBoolean());
            System.out.println(dis.readLong());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (dis != null){
                try {
                    dis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

(九)对象流

  • ObjectInputStream和OjbectOutputSteam
    • 用于存储和读取对象的处理流。它的强大之处就是可以把Java中的对象写入到数据源中,也能把对象从数据源中还原回来。
  • 序列化(Serialize):用ObjectOutputStream类将一个Java对象写入IO流中
  • 反序列化(Deserialize):用ObjectInputStream类从IO流中恢复该Java对象
    • ObjectOutputStream和ObjectInputStream不能序列化statictransient修饰的成员变量
  • 对象序列化机制允许把内存中的Java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其它程序获取了这种二进制流,就可以恢复成原来的Java对象
  • 序列化的好处在于可将任何实现了Serializable接口的对象转化为字节数据,使其在保存和传输时可被还原
  • 序列化是 RMI(Remote Method Invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 RMI 是 JavaEE 的基础。因此序列化机制是 JavaEE 平台的基础
  • 如果需要让某个对象支持序列化机制,则必须让其类是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一:
    • Serializable
    • Externalizable
  • 凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量:
    • private static final long serialVersionUID;
    • serialVersionUID用来表明类的不同版本间的兼容性
    • 如果类没有显示定义这个静态变量,它的值是Java运行时环境根据类的内部细节自动生成的。若类的源代码作了修改,serialVersionUID 可能发生变化。故建议,显示声明
  • 显示定义serialVersionUID的用途
    • 希望类的不同版本对序列化兼容,因此需确保类的不同版本具有相同的serialVersionUID
    • 不希望类的不同版本对序列化兼容,因此需确保类的不同版本具有不同的serialVersionUID
  • 若某个类实现了 Serializable 接口,该类的对象就是可序列化的:
    • 创建一个 ObjectOutputStream
    • 调用 ObjectOutputStream 对象的 writeObject(对象) 方法输出可序列化对象。注意写出一次,操作flush()
  • 反序列化
    • 创建一个 ObjectInputStream
    • 调用 readObject() 方法读取流中的对象
  • 强调:如果某个类的字段不是基本数据类型或 String 类型,而是另一个引用类型,那么这个引用类型必须是可序列化的,否则拥有该类型的 Field 的类也不能序列化

ObjectOutputStream

对象类

/**
 * 要实现序列化的类:
 * 1.要求此类是可序列化的:实现Serializable接口
 * 2.要求类的属性同样的要实现Serializable接口
 * 3.提供一个版本号:private static final long serialVersionUID
 * 4.使用static或transient修饰的属性,不可实现序列化
 */
class Person implements Serializable {
    private static final long serialVersionUID = -605361270135132132L;
    String name;
    Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" + "name='" + name  + ", age=" + age + ", pet=" + pet + '}';
    }
}
class Pet implements Serializable{
    String name;

    public Pet(String name) {
        this.name = name;
    }
    
    @Override
    public String toString() {
        return "Pet{" + "name='" + name + '}';
    }
}

使用ObjectOutputStream进行序列化

	//对象的序列化过程:将内存中的对象通过ObjectOutputStream转换成二进制流,存储在硬盘文件中
    @Test
    public void testObjectOutputStream() {
        ObjectOutputStream oos = null;
        try {
            Person person1 = new Person("小米", 23, new Pet("花花"));
            Person person2 = new Person("红米", 21, new Pet("欢欢"));
            oos = new ObjectOutputStream(new FileOutputStream(new File("person.txt")));

            oos.writeObject(person1);
            oos.flush();
            oos.writeObject(person2);
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (oos != null) {
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

--------------person.txt内容(乱码)--------------
�� sr com.rzhwff.io.object.Person��S!��� L aget Ljava/lang/Integer;L namet Ljava/lang/String;L pett Lcom/rzhwff/io/object/Pet;xpsr java.lang.Integer⠤���8 I valuexr java.lang.Number������  xp   t 小米sr com.rzhwff.io.object.Petj�oz�= L nameq ~ xpt 花花sq ~  sq ~    t 红米sq ~ 	t 欢欢

ObjectOutputStream

使用ObjectOutputStream进行序列化

	//对象的反序列化过程:将硬件中的文件通过ObjectInputStream转换成相应的对象
    @Test
    public void testObjectInputStream() {
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream(new File("person.txt")));
            Person person1 = (Person) ois.readObject();
            Person person2 = (Person) ois.readObject();
            System.out.println(person1);
            System.out.println(person2);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (ois != null){
                try {
                    ois.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

--------------运行结果-------------
    Person{name='小米, age=23, pet=Pet{name='花花}}
    Person{name='红米, age=21, pet=Pet{name='欢欢}}

(十)RandomAccessFile类

  • RandomAccessFile 类支持 “随机访问” 的方式,程序可以直接跳到文件的任意地方来读、写文件
    • 支持只访问文件的部分内容
    • 可以向已存在的文件后追加内容
  • RandomAccessFile 对象包含一个记录指针,用以标示当前读写处的位置。RandomAccessFile 类对象可以自由移动记录指针:
    • long getFilePointer():获取文件记录指针的当前位置
    • void seek(long pos):将文件记录指针定位到 pos 位置
  • 构造器
    • public RandomAccessFile(File file, String mode)
    • public RandomAccessFile(String name, String mode)
  • 创建 RandomAccessFile 类实例需要指定一个 mode 参数,该参数指定 RandomAccessFile 的访问模式:
    • r:以只读方式打开
    • rw:打开以便读取和写入
    • rwd:打开以便读取和写入;同步文件内容的更新
    • rws:打开以便读取和写入;同步文件内容和元数据的更新

使用RandomAccessFile进行读写、覆盖、插入

import org.junit.Test;

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

/**
 * RandomAccessFile:支持随机访问
 * 1.既可以充当一个输出流,又可以充当一个输出流
 * 2.支持从文件的开头、写入
 * 3.支持从任意位置的读取、写入
 */
public class TestRandomAccessFile {
    //在hello.txt中第四个位置后插入xy(多行文件) 实现插入效果、更通用
    @Test
    public void test4() {
        RandomAccessFile raf = null;
        try {
            raf = new RandomAccessFile(new File("hello1.txt"), "rw");

            raf.seek(4);
            byte[] buf = new byte[50];
            int len;
            StringBuffer sb = new StringBuffer();
            while ((len = raf.read(buf)) != -1) {
                sb.append(new String(buf, 0, len));
            }
            raf.seek(4);
            raf.write("xy".getBytes());
            raf.write(sb.toString().getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (raf != null) {
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //在hello.txt中第四个位置后插入xy(单行文件) 实现插入效果
    @Test
    public void test3() {
        RandomAccessFile raf = null;
        try {
            raf = new RandomAccessFile(new File("hello1.txt"), "rw");

            raf.seek(4);
            String str = raf.readLine();
            //System.out.println(str);//efg123456
            //long filePointer = raf.getFilePointer();
            //System.out.println(filePointer);
            raf.seek(4);
            raf.write("xy".getBytes());
            raf.write(str.getBytes());

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

    //在hello.txt中第四个位置后插入xy 实现的覆盖的效果
    @Test
    public void test2() {
        RandomAccessFile raf = null;
        try {
            raf = new RandomAccessFile(new File("hello.txt"), "rw");
            raf.seek(3);
            raf.write("qwe".getBytes());

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

    //进行文件的读写
    @Test
    public void test1() {
        RandomAccessFile raf1 = null;
        RandomAccessFile raf2 = null;
        try {
            raf1 = new RandomAccessFile(new File("hello.txt"), "r");
            raf2 = new RandomAccessFile(new File("hello1.txt"), "rw");

            byte[] buf = new byte[20];
            int len;
            while ((len = raf1.read(buf)) != -1) {
                raf2.write(buf, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (raf1 != null) {
                try {
                    raf1.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (raf2 != null) {
                try {
                    raf2.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

文件的分割与合并

FileUtil.java

import java.io.Closeable;

public class FileUtil {
	/**
	 * 工具类关闭流
	 * 可变参数: ...  只能形参最后一个位置,处理方式与数组一致
	 */
	public static void close(Closeable ... io){
		for(Closeable temp:io){
			try {
				if (null != temp) {
					temp.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}

SplitMergeFile.java

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

import com.rzhwff.io.util.FileUtil;

public class SplitMergeFile {
    //文件的路径
    private String filePath;
    //文件名
    private String fileName;
    //文件大小
    private long length;
    //块数
    private int size;
    //每块的大小
    private long blockSize;
    //分割后的存放目录
    private String destBlockPath;
    //每块的名称
    private List<String> blockPath;

    public SplitMergeFile() {
        blockPath = new ArrayList<>();
    }

    public SplitMergeFile(String filePath, String destBlockPath) {
        this(filePath, destBlockPath, 1024);
    }

    public SplitMergeFile(String filePath, String destBlockPath, long blockSize) {
        this();
        this.filePath = filePath;
        this.destBlockPath = destBlockPath;
        this.blockSize = blockSize;
        init();
    }

    /**
     * 初始化操作 计算 块数、确定文件名
     */
    public void init() {
        File src = null;
        //健壮性
        if (null == filePath || !(((src = new File(filePath)).exists()))) {
            return;
        }
        if (src.isDirectory()) {
            return;
        }
        //文件名
        this.fileName = src.getName();

        //计算块数 实际大小 与每块大小
        this.length = src.length();
        //修正 每块大小
        if (this.blockSize > length) {
            this.blockSize = length;
        }
        //确定块数
        size = (int) (Math.ceil(length * 1.0 / this.blockSize));
        //确定文件的路径
        initPathName();
    }

    private void initPathName() {
        for (int i = 0; i < size; i++) {
            this.blockPath.add(destBlockPath + "/" + this.fileName + ".part" + i);
        }
    }

    /**
     * 文件的分割
     * 0、第几块
     * 1、起始位置
     * 2、实际大小
     */
    public void split() {
        long beginPos = 0;  //起始点
        long actualBlockSize = blockSize; //实际大小
        //计算所有块的大小、位置、索引
        for (int i = 0; i < size; i++) {
            if (i == size - 1) { //最后一块
                actualBlockSize = this.length - beginPos;
            }
            spiltDetail(i, beginPos, actualBlockSize);
            beginPos += actualBlockSize; //本次的终点,下一次的起点
        }
    }

    /**
     * 文件的分割 输入 输出
     * 文件拷贝
     *
     * @param idx             第几块
     * @param beginPos        起始点
     * @param actualBlockSize 实际大小
     */
    private void spiltDetail(int idx, long beginPos, long actualBlockSize) {
        //1、创建源
        File src = new File(this.filePath);  //源文件
        File dest = new File(this.blockPath.get(idx)); //目标文件

        //创建分隔文件的父级目录(如果不存在)
        File parent = new File(dest.getParent());
        if (!parent.exists()){
            parent.mkdirs();
        }
        //2、选择流
        RandomAccessFile raf = null;  //输入流
        BufferedOutputStream bos = null; //输出流
        try {
            raf = new RandomAccessFile(src, "r");
            bos = new BufferedOutputStream(new FileOutputStream(dest));

            //读取文件
            raf.seek(beginPos);
            //缓冲区
            byte[] flush = new byte[1024];
            //接收长度
            int len;
            while (-1 != (len = raf.read(flush))) {
                if (actualBlockSize - len >= 0) { //查看是否足够
                    //写出
                    bos.write(flush, 0, len);
                    actualBlockSize -= len; //剩余量
                } else { //写出最后一次的剩余量
                    bos.write(flush, 0, (int) actualBlockSize);
                    break;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            FileUtil.close(bos, raf);
        }
    }

    /**
     * 文件的合并
     */
    public void merge(String destPath) {
        //创建源
        File dest = new File(destPath);
        //选择流
        BufferedOutputStream bos = null; //输出流
        SequenceInputStream sis = null;//输入流
        //创建一个容器
        Vector<InputStream> vi = new Vector<>();
        try {
            for (String aBlockPath : this.blockPath) {
                vi.add(new BufferedInputStream(new FileInputStream(new File(aBlockPath))));
            }
            bos = new BufferedOutputStream(new FileOutputStream(dest, true)); //追加
            sis = new SequenceInputStream(vi.elements());

            //缓冲区
            byte[] flush = new byte[1024];
            //接收长度
            int len;
            while (-1 != (len = sis.read(flush))) {
                bos.write(flush, 0, len);
            }
            bos.flush();
            FileUtil.close(sis);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            FileUtil.close(bos);
            FileUtil.close(sis);
        }
    }

    /**
     * 文件的合并
     */
    public void merge1(String destPath) {
        //创建源
        File dest = new File(destPath);
        //选择流
        BufferedOutputStream bos = null; //输出流
        try {
            bos = new BufferedOutputStream(new FileOutputStream(dest, true)); //追加
            BufferedInputStream bis = null;
            for (String aBlockPath : this.blockPath) {
                bis = new BufferedInputStream(new FileInputStream(new File(aBlockPath)));

                //缓冲区
                byte[] flush = new byte[1024];
                //接收长度
                int len = 0;
                while (-1 != (len = bis.read(flush))) {
                    bos.write(flush, 0, len);
                }
                bos.flush();
                FileUtil.close(bis);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            FileUtil.close(bos);
        }
    }

    public static void main(String[] args) {
        SplitMergeFile split = new SplitMergeFile("D:/TestFile.java", "D:/Test/", 1000);
        System.out.println(split.size);
        split.split();
        split.merge("D:/test.java");
    }
}

(十一)总结

  • 流是用来处理数据的。
  • 处理数据时,一定要先明确数据源,与数据目的地数
    • 据源可以是文件,可以是键盘。
    • 数据目的地可以是文件、显示器或者其他设备。而流
  • 只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理、转换处理等。
  • 字节流-缓冲流(重点)
    • 输入流InputStream-FileInputStream-BufferedInputStream
    • 输出流OutputStream-FileOutputStream-BufferedOutputStream
  • 字符流-缓冲流(重点)
    • 输入流Reader-FileReader-BufferedReader
    • 输出流Writer-FileWriter-BufferedWriter
  • 转换流
    • InputSteamReader和OutputStreamWriter
  • 对象流ObjectInputStream和ObjectOutputStream(难点)
  • 序列化
  • 反序列化
  • 随机存取流RandomAccessFile(掌握读取、写入)
posted @ 2018-08-15 07:41  秋风de领域  阅读(265)  评论(0编辑  收藏  举报