java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)

简介: 这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。

前言

学习的过程,遇到了几个问题,而在几个问题,却是java的基础,同时也是经常用的地方。无所是优化代码还是看源码时,基本都能遇到,那就是**静态代理、reflect、IO流**

1. 字符与字节

学习IO流之前,我觉得应该要先学字符字节 ,因为中文、英文、各种符号 在本地以及计算机存储 都是以 字符或者字节 的形式存在的。IO流主要是对这两种形式的文件进行操作。

位 组成的是字节(记住就可以,一般都会说字节),字符与字节是两个独立的概念

  • :数据存储的最小单位。每个二进制数字0或者1就是1个位;也可以说 1位 只能存0或者1。

  • 字节:8个位构成一个字节;即:1 byte (字节)= 8 bit(位);计算机中存储数据的单元,一个8位的二进制数,是一个很具体的存储空间。

  • 字符:人们使用的记号,抽象意义上的一个符号。 a、A、中、+、*、の…均表示一个字符;

  • 字节和字符的转换,又会根据不同的编码形式不同而不同。编码形式在下面说。

2. 编码形式

2.1 编码表由来

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

计算机刚开始只支持英语,其他语言不能够再计算机上存储和显示。这就是ASCII 编码形式。

再后来就国际化了,支持了多种语言的编码。

不同的国家和地区制定了不同的标准,由此产生了 GB2312, BIG5, JIS 等各自的编码标准。这些使用 2 个字节来代表一个字符的各种汉字延伸编码方式,称为 ANSI 编码

  1. ASCII:美国标准信息交换码。
  2. utf-8 字符集:一个汉字 字符 占用 3 个 字节;
  3. gb2312字符集:所有汉字字符在计算机内部采用2个字节来表示,每个字节的最高位规定为1【正好与标准ASCii字符(最高位是0)不重叠,并兼容】,不支持繁体字;
  4. gbk 字符集:gb2312的扩充。一个汉字 字符 占用 2 个 字节;
  5. Unicode:国际标准码,融合了多种文字。所有文字都用两个字节来表示。Java语言使用的就是unicode
  6. UTF-8:最多用三个字节来表示一个字符。
  • 编码:字符串字节数组
  • 解码:字节数组字符串
  • 转换流的编码应用
  • 可以将字符按指定编码格式存储。
  • 可以对文本数据按指定编码格式来解读。
  • 指定编码表的动作由构造器完成。

2.1 IDEA查看并设置项目编码格式

  1. File->Settings->Editor->File Encodings
  2. File->Other Settings->Default Settings ->Editor->File Encodings
  3. 将项目中的.idea文件夹中的encodings.xml文件中的编码格式改为uft-8
  4. File->Settings->Build,Execution,Deployment -> Compiler -> Java Compiler
    设置 Additional command line parameters选项为 -encoding utf-8
  5. 1)打开Run/Debug Configuration,选择你的tomcat

  6. 然后在 Server > VM options 设置为 -Dfile.encoding=UTF-8 ,重启tomcat

  • 清空浏览器缓存再试一次。

3. File类(学IO之前先学这个)

Java是面向对象的,在java的世界里,一切皆可对象。对对象进行操作。IO流操作的是文件,那文件就是一个对象,这个对象就是File。他的属性就是文件的大小、类型、路径等等。

  • java.io.File类:文件和目录路径名的抽象表示形式,与平台无关
  • File 能新建、删除、重命名文件和目录,但 File 不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流。
  • File对象可以作为参数传递给流的构造函数
  • File类的常见构造方法:
public File(String pathname)
//以pathname为路径创建File对象,可以是绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。
public File(String parent,String child)
//以parent为父路径,child为子路径创建File对象。
//File的静态属性String separator存储了当前系统的路径分隔符。
//在UNIX中,此字段为‘/’,在Windows中,为‘\\’
  • File类操作文件的方法。
    访问文件名:
  1. getName()
  2. getPath()
  3. getAbsoluteFile()
  4. getAbsolutePath()
  5. getParent()
  6. renameTo(File newName)
  • 文件检测
  1. exists()
  2. canWrite()
  3. canRead()
  4. isFile()
  5. isDirectory()
  • 文件操作相关
  1. createNewFile()
  2. delete()
  3. 目录操作相关
  4. mkDir()
  5. mkDirs()
  6. list()
  7. listFiles()
  • 获取常规文件信息
  1. lastModified()
  2. length()

Java代码示例:


package com.feng.io;

import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.util.Date;

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

    /**
     * 文件、目录 操作
     * createNewFile() : 创建一个新文件
     * delete() : 删除一个文件
     * mkDir()  : 创建一个文件目录。只有在上层文件目录存在的情况下,才能返回true
     * mkDirs() : 穿件一个文件目录。若上层文件目录不存在,一并创建。
     * list()   :获取所有的文件名称遍历
     * listFiles():获取所有的 文件对象 File
     */
    @Test
    public void test03() throws IOException {
        File file1 = new File("D:/io/helloworld.txt");
        System.out.println("删除文件:"+file1.delete());
        if (!file1.exists()) {
            boolean newFile = file1.createNewFile();
            System.out.println("创建文件:"+newFile);
        }

        File file2 = new File("D:\\io\\io2");
        if (!file2.exists()){
            boolean mkdir = file2.mkdir();
            System.out.println("创建目录"+mkdir);
            // file2.mkdirs();
        }

        System.out.println();
        System.out.println("list()方法:");
        File file3 = new File("D:\\apache-maven-3.6.1");
        String[] strs = file3.list();
        for (String s : strs){
            System.out.println(s);
        }

        System.out.println();
        System.out.println("listFiles()方法:");
        File[] files = file3.listFiles();
        for (int i = 0; i< files.length; i++){
            System.out.println(files[i].getName()+","+files[i].getPath());
        }
    }

    /**
     * 文件检测
     * exists()    :是否存在
     * canWrite():能否写
     * canRead():能否读
     * isFile():是否是文件
     * isDirectory():是否是目录
     * lastModified():最后一次修改时间
     * length():文件长度
     */
    @Test
    public void test02() {
        File file1 = new File("D:/io/helloworld.txt");
        File file2 = new File("D:\\io\\io1");

        System.out.println("文件:");
        System.out.println(file1.exists());
        System.out.println(file1.canRead());
        System.out.println(file1.canWrite());
        System.out.println(file1.isFile());
        System.out.println(file1.isDirectory());
        System.out.println(new Date(file1.lastModified()));
        System.out.println(file1.length());
        System.out.println("目录:");
        System.out.println(file2.exists());
        System.out.println(file2.canRead());
        System.out.println(file2.canWrite());
        System.out.println(file2.isFile());
        System.out.println(file2.isDirectory());
        System.out.println(new Date(file2.lastModified()));
        System.out.println(file2.length());

    }

    /**
     * 路径:
     * 绝对路径:包括盘符在内的完整的文件路径
     * 相对路径:在当前文件目录下的文件的路径
     * <p>
     *     访问文件名
     * getName()    : 获取文件名
     * getPath()    : 获取文件路径
     * getAbsoluteFile() :获取绝对文件名
     * getAbsolutePath() :获取绝对路径
     * getParent()         :获取父级目录
     * renameTo(File newName) :重命名(有坑)
     */
    @Test
    public void test01() {
        File file1 = new File("D:/io/helloworld.txt"); // 绝对目录
        File file2 = new File("hello.txt"); // 相对目录

        File file3 = new File("d:\\io\\io1");// 目录

        System.out.println("文件:");
        System.out.println(file1.getName());
        System.out.println(file1.getPath());
        System.out.println(file1.getAbsoluteFile());
        System.out.println(file1.getAbsolutePath());
        System.out.println(file1.getParent());
        System.out.println("目录:");
        System.out.println(file3.getName());
        System.out.println(file3.getPath());
        System.out.println(file3.getAbsoluteFile());
        System.out.println(file3.getAbsolutePath());
        System.out.println(file3.getParent());

        //renameTo(File newName)
        // file1.renameTo(file2):file1重命名为file2,
        // 要求:file1 一定存在,file2一定不存在, file 可以是文件,也可是目录
        boolean b = file1.renameTo(file2);
        System.out.println(b);
    }
}

输出:
test01:
在这里插入图片描述
test02:
在这里插入图片描述
test03:
在这里插入图片描述

4. 什么是IO流

4.1 IO流详解

  • IO 流的目的就是操作就是 字符和字节(文件的存储都是以字节或者字符的形式)。

  • 对于IO操作,不管是磁盘还是网络,最终都是对字节的操作,而我们平时写的程序都是字符形式的,所以在传输的过程中需要进行转换。在字符到字节的转换过程中,我们需要用到一个类:InputStreamReader。

  • WriterReader 操作的目的就是操作字符,和InputStream和OutputStream配合增加IO效果。通过InputStreamReaderOutputStreamReader可以进行字节和字符的转换

  • 设计Writer和Reader的目的是国际化,使IO操作支持16位的Unicode。

4.2 原理

  • IO流用来处理设备之间的数据传输。
  • Java程序中,对于数据的输入/输出操作以”流(stream)” 的方式进行。
  • java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。
  1. 输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
  2. 输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中
    在这里插入图片描述
    在这里插入图片描述

5. IO流分类

IO流有不少分类方式:可按数据单位、流向、角色。

  • 按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
  • 按数据流的流向不同分为:输入流,输出流
  • 流的角色的不同分为:节点流,处理流

抽象基类 字节流 字符流
输入流 InputStream Reader
输出流 OutputStream Writer
  1. Java的IO流共涉及40多个类,实际上非常规则,都是从以上4个抽象基类派生的。
  2. 由这四个类派生出来的子类名称都是 以其父类名作为子类名后缀。

IO流的分类结构图:
在这里插入图片描述
IO流的分类表:
在这里插入图片描述
重点要掌握的流:

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

6. 节点流、处理流

  • 节点流可以从一个特定的数据源读写数据,直接对文件执行操作(进行输入、输出)。
    (输入) FileInputStream、FileReader
    (输出) FileOutputStream、FileWriter
    在这里插入图片描述
  • 处理流是**“连接”在已存在的流(节点流或处理流)之上**,通过对数据的处理为程序提供更为强大的读写功能。增强了数据的传输能力。
    在这里插入图片描述
    节点流组网图:
    在这里插入图片描述
    代码示例:

6.1 FileInputStream、FileOutputStream 代码

package com.feng.io;

import org.junit.Test;

import java.io.*;

/**
 * 1、流的分类
 * 按照数据流向的不同:输入流 输出流
 * 按照处理数据的单位的不同:字节流 字符流(处理的文本文件)
 * 按照角色的不同:节点流(直接作用于文件的) 处理流
 *
 * 2、 IO 的体系
 * 抽象基类         节点流(文件流)            缓冲流(处理流的一种:加速节点流的操作,速率快,可以说是对节点流的加工)
 * InputStream      FileInputStream(字节)       BufferedInputStream
 * OutputStream     FileOutputStream(字节)      BufferedOurputStream
 * Reader           FileReader(字符)            BufferedReader
 * Writer           FileWriter(字符)            BufferedWriter
 */
public class Test_02_FileInputOutputStream {

    @Test
    public void testCopyFile() {
        long start = System.currentTimeMillis();
//        String src = "C:\\Users\\fw8842\\Desktop\\fengfanli\\rlw.mp4";
//        String dest = "C:\\Users\\fw8842\\Desktop\\fengfanli\\rlw01.mp4";
        String src = "C:\\Users\\fw8842\\Desktop\\fengfanli\\111.zip";
        String dest = "C:\\Users\\fw8842\\Desktop\\fengfanli\\333.zip";
        copyFile(src, dest);
        long end = System.currentTimeMillis();
        System.out.println("花费的时间:" + (end - start)); // 2838
    }

    /**
     * 实现文件复制的方法
     *
     * @param src
     * @param dest
     */
    public void copyFile(String src, String dest) {
        // 1、提供读入、写出的文件
        File file1 = new File(src);
        File file2 = new File(dest);
        // 2、提供相应的流
        FileInputStream fls = null;
        FileOutputStream fos = null;
        try {
            fls = new FileInputStream(file1);
            fos = new FileOutputStream(file2);
            // 3、 实现文件的复制
            byte[] b = new byte[1024];
            int len;
            while ((len = fls.read(b)) != -1) {
                // 错误的两种写法:
                // fos.write(b);
                //fos.write(b, 0 ,b.length);
                fos.write(b, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4、 关闭输出、输入流
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fls != null) {
                try {
                    fls.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 从硬盘读取一个文件,并写入到另一个位置。(相当于复制)
     */
    @Test
    public void testFileInputOutputStream() {
        // 1、提供读入、写出的文件
        File file1 = new File("C:\\Users\\fw8842\\Desktop\\fengfanli\\PersonalPhoto.jpg");
        File file2 = new File("C:\\Users\\fw8842\\Desktop\\fengfanli\\PersonalPhoto01.jpg");
        // 2、提供相应的流
        FileInputStream fls = null;
        FileOutputStream fos = null;
        try {
            fls = new FileInputStream(file1);
            fos = new FileOutputStream(file2);
            // 3、 实现文件的复制
            byte[] b = new byte[20];
            int len;
            while ((len = fls.read(b)) != -1) {
                // 错误的两种写法:
                // fos.write(b);
                //fos.write(b, 0 ,b.length);
                fos.write(b, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4、 关闭输出、输入流
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fls != null) {
                try {
                    fls.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * FileOutputStream
     */
    @Test
    public void testFileOutpurStream() {
        // 1、创建一个File对象,表明要写入的文件位置
        // 输出的物理文件可以不存在,在执行的过程中,若不存在,会自动的创建。若存在,会将原有的文件覆盖
        File file = new File("hello1.txt");
        // 2、创建一个FileOutputStream 的对象,将file 的对象作为形参传递给FileOutputStream 的构造器中
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            // 3、写入的操作
            fos.write(new String(" I Love China I Love The World").getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 4、关闭输出流
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
    * 使用String 字符串 对字节数组 优化。
    *
    **/
    @Test
    public void testFileInputStream3() {
        FileInputStream fls = null;
        try {
            File file = new File("hello.txt");
            fls = new FileInputStream(file);
            byte[] b = new byte[5];    // 读取到的数据要写入的数组
            int len;                    // 每次读入到 byte 中的字节的长度
            while ((len = fls.read(b)) != -1) {
//                for (int i = 0; i< len; i++){  //b.length 则输出 abcdefgcde
//                    System.out.print((char) b[i]);
//                }
                String str = new String(b, 0, len);
                System.out.print(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fls != null) {
                try {
                    fls.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * 使用 try-catch 的方式处理如下的异常更合理:保证流的关闭操作一定可以执行
     */
    @Test
    public void testFileInputStream2() {
        FileInputStream fis = null;
        try {
            // 1、创建一个 File类的对象。
            File file = new File("hello.txt");
            // 2、创建一个FileInputStream 类的对象
            fis = new FileInputStream(file);
            // 3、调用FileInputoutStream 的方法,实现file文件的读取。
            int b;
            while ((b = fis.read()) != -1) {
                System.out.println(b);
                System.out.println((char) b);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                // 4、关闭相应的流
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    /**
     * 从硬盘存的一个文件中,读取其内容到程序中,使用FileInputStream
     * 要读取的文件一定要存在,否则抛异常:FileNotFoundException
     *
     * @throws FileNotFoundException
     */
    @Test
    public void testFileInputStream1() throws IOException {
        // 1、创建一个 File类的对象。
        File file = new File("hello.txt");
        // 2、创建一个FileInputStream 类的对象
        FileInputStream fis = new FileInputStream(file);
        // 3、调用FileInputoutStream 的方法,实现file文件的读取。
        /**
         * read(): 读取文件的一个字节( 1 byte )。当执行到文件结尾时,返回-1
         */
//        int b = fis.read();
//        while (b != -1){
//            System.out.print(b); //  这是把char型转成int型了, 下面是还原出来
//            System.out.print((char) b);
//            b = fis.read();
//        }
        int b;
        while ((b = fis.read()) != -1) {
            System.out.print(b);
            System.out.print((char) b);
        }
        // 4、关闭相应的流   流的资源,不是java虚拟机内存的资源(内存的资源,会自动关闭),需要显示的关,
        fis.close();

        /**
         * hello.txt 算是一个文本, 文本对于英文字符来讲,可用byte来读取。
         *  因为英文字符一个 256 就可放的下,  1byte = 8bit((位)  即256  英文大小:a-z:97-122 A-Z:65-90
         *  如果加中文:中国,一个中文就是两个char, 用byte来读 就会出错。中文是字符来读。
         */
    }
}

倒着来:
testFileInputStream1():从硬盘存的一个文件中,读取其内容到程序中,使用FileInputStream,要读取的文件一定要存在,否则抛异常:FileNotFoundException
testFileInputStream2():对 testFileInputStream1() 进行try-catch 包裹
testFileInputStream3(): 对上面两个方法进行优化。使用String 字符串 读取 字节数组

testFileOutpurStream() :将字符串输出到物理磁盘。
testFileInputOutputStream(): 从硬盘读取一个文件,并写入到另一个位置。(相当于复制)
copyFile():对testFileInputOutputStream() 这个方法的复用。实现文件复制的方法。
testCopyFile() : 调用 copyFile()这个封装的方法。

6.2 FileReader、FileWriter代码

package com.feng.io;

import org.junit.Test;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
 * 使用 FileReader FileWriter 可以实现 文本文件 的复制。
 * 对于非文本文件(视频文件、音频文件、图片),只能使用字节流!!!
 */
public class Test_03_FileReaderWriter {

    /**
     * 复制文件
     */
    @Test
    public void testFileReaderWriter(){
        // 1、输入流对应的文件src一定要存在,否则抛异常。输出流对应的文件dest可以不存在,执行过程中会自动创建
        FileReader fr = null;
        FileWriter fw = null;

        try {
            File src = new File("java.txt");
            File dest = new File("java1.txt");
            fr = new FileReader(src);
            fw = new FileWriter(dest);
            char[] c = new char[24];
            int len;
            while ((len= fr.read(c)) != -1){
                String str = new String(c, 0, len);
//                System.out.print(str);
                fw.write(str);
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            if (fw != null){
                try {
                    fw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fr != null){
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     *  读取文件到控制台
     */
    @Test
    public void testFileReader(){
        File file = new File("java.txt");
        FileReader fr = null;
        try {
            fr = new FileReader(file);
            char[] c = new char[24];
            int len;
            while ((len = fr.read(c)) != -1){
                String str = new String(c, 0, len);
                System.out.print(str);
            }
        }catch (IOException e){
            e.printStackTrace();
        }finally {
            if (fr != null){
                try {
                    fr.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

7. 缓存流

package com.feng.io;

import org.junit.Test;

import java.io.*;

/**
 * * 抽象基类         节点流(文件流)            缓冲流(处理流的一种:加速节点流的操作,速率快,可以说是对节点流的加工)
 * * InputStream      FileInputStream(字节)       BufferedInputStream
 * * OutputStream     FileOutputStream(字节)      BufferedOurputStream (flush());
 * * Reader           FileReader(字符)            BufferedReader  (readLine())  读取一行
 * * Writer           FileWriter(字符)            BufferedWriter (flush());
 */
public class Test_04_Buffered {

    /**
     * 缓冲字节流 :处理文本文件   BufferedReader、BufferedWriter
     */
    @Test
    public void testBufferedReader() {
        BufferedReader br = null;
        BufferedWriter bw = null;
        try {
            File file = new File("java.txt");
            File file1 = new File("java2.txt");
            FileReader fr = new FileReader(file);
            FileWriter fw = new FileWriter(file1);
            br = new BufferedReader(fr);
            bw = new BufferedWriter(fw);

//            char[] c = new char[1024];
//            int len;
//            while ((len = br.read(c)) != -1){
//                String str = new String(c, 0, len);
//                System.out.print(str);
//            }
            String str;
            while ((str = br.readLine()) != null) {          // 这个会更块:阅读一行,返回也是字符串
                System.out.println(str);
                bw.write(str);    // 不会自动换行,都在一行
                bw.newLine();    //  或者 直接一句话 bw.write(str + "\n");  这样就可以换行啦
                bw.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bw != null) {
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    @Test
    public void testCopyFile() {
        long start = System.currentTimeMillis();
//        String src = "C:\\Users\\fw8842\\Desktop\\fengfanli\\rlw.mp4";
//        String dest = "C:\\Users\\fw8842\\Desktop\\fengfanli\\rlw02.mp4";
        String src = "C:\\Users\\fw8842\\Desktop\\fengfanli\\111.zip";
        String dest = "E:\\111.zip";
        copyFile(src, dest);
        long end = System.currentTimeMillis();
        System.out.println("花费的时间为:" + (end - start)); // 4 比使用节点流 快太多了,节点流为  2838
    }

    /**
     * 实现文件复制的方法
     *
     * @param src
     * @param dest
     */
    public void copyFile(String src, String dest) {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            // 1. 提供读入、写出的文件
            File file1 = new File("PersonalPhoto.jpg");
            File file2 = new File("PersonalPhoto01.jpg");
            // 2、想创建相应的节点流 :FileInputStream FileOutputStream
            FileInputStream fis = new FileInputStream(file1);
            FileOutputStream fos = new FileOutputStream(file2);
            // 3、将创建的节点流对象作为形参传递给缓冲流的构造器中
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);
            //4、具体的实现文件复制的操作
            byte[] b = new byte[1024];
            int len;
            while ((len = bis.read(b)) != -1) {
                bos.write(b, 0, len);
                bos.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 5、关闭相应的流
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 缓冲字节流: 使用BufferedInputStream 和 BufferedOutputStream 实现非 文本文件 的复制
     */
    @Test
    public void testBufferedInputStream() {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        try {
            // 1. 提供读入、写出的文件
            File file1 = new File("PersonalPhoto.jpg");
            File file2 = new File("PersonalPhoto01.jpg");
            // 2、想创建相应的节点流 :FileInputStream FileOutputStream
            FileInputStream fis = new FileInputStream(file1);
            FileOutputStream fos = new FileOutputStream(file2);
            // 3、将创建的节点流对象作为形参传递给缓冲流的构造器中
            bis = new BufferedInputStream(fis);
            bos = new BufferedOutputStream(fos);
            //4、具体的实现文件复制的操作
            byte[] b = new byte[256];
            int len;
            while ((len = bis.read(b)) != -1) {
                bos.write(b, 0, len);
                bos.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 5、关闭相应的流
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bis != null) {
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

8. 转换流、标准输入输出流

package com.feng.io;

import org.junit.Test;

import java.io.*;

/**
 * 1、转换流
 * 2、标准的输入输出流
 */
public class Test_05_OtherStream {

    /**                             有问题
     * 标准的输入输出流:
     * 标准的输出流: System.Out 从控制台输出
     * 输出的输入流: System.In  从控制台输入
     *
     * 从键盘输入字符串,要求将读取到的整行字符串转成大写输出。
     * 然后继续进行输入操作,直至当输入“e”或者“exit”时,退出程序。
     */
    @Test
    public void standardInputOutputStream() {
        BufferedReader br = null;
        try {
            InputStream is = System.in;
            InputStreamReader isr = new InputStreamReader(is);
            br = new BufferedReader(isr);
            String str;
            while (true){
                System.out.println("请输入字符串:");
                str = br.readLine();
                if (str.equalsIgnoreCase("e") || str.equalsIgnoreCase("exit")){
                    System.out.println("已经退出!!!");
                    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();
                }
            }
        }
    }

    /**
     * 转换流: InputStreamReader 和 OutputStreamWriter
     * 编码:字符串 --》 字节数组
     * 解码:字节数组--》字符串
     *
     * 题目:
     */
    @Test
    public void testInputStreamReader(){
        BufferedReader br = null;
        BufferedWriter bw = null;

        try {
            // 解码
            File file = new File("java.txt");
            FileInputStream fis = new FileInputStream(file);
            InputStreamReader isr = new InputStreamReader(fis, "UTF-8");
            br = new BufferedReader(isr);

            // 上面和下面的类型相同,同时和编译器类型相同,我这里是 UTF-8。
            // 我原来测试用的GBK ,但是编译器是utf-8,所以打开为乱码

            // 编码
            File file1 = new File("java3.txt");
            FileOutputStream fos = new FileOutputStream(file1);
            OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
            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 (br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bw != null){
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

9. 数据流

package com.feng.io;

import org.junit.Test;

import java.io.*;

public class Test_07_OtherStream {

    // 使用数据流 读数据,也就是写入数据  在控制台打印
    @Test
    public void testData01(){
        DataInputStream dis = null;
        try {
            dis = new DataInputStream(new FileInputStream(new File("data.txt")));
//            byte[] b = new byte[20];
//            int len;
//            while ((len = dis.read(b))!= -1){
//                System.out.println(new String(b, 0, len));
//            }
            /**
             * 上面运行失败,乱码
             * 下面运行正常
             */
            String str = dis.readUTF();
            System.out.println(str);
            boolean b1 = dis.readBoolean();
            System.out.println(b1);
            long l = dis.readLong();
            System.out.println(l);

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

    }

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

    /**
     * 打印流: PrintStream    字符流:PrintWriter
     */
    @Test
    public void printStream(){
        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(new File("print.txt"));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        //  创建打印输出流,设置为自动刷新模式(写入换行符或者字节 '\n' 时都会刷新输出缓冲区)
        PrintStream ps = new PrintStream(fos, true);
        if (null != ps){   // 把标准输出流(控制台输出)改成文件
            System.setOut(ps);
        }
        for (int i = 0; i<= 255; i++){      // 输出 ASCII 字符
            System.out.print((char)i);
            if (i%50 == 0){            // 每50个数据一行
                System.out.println();  // 换行
            }
        }
    }
}

10. 对象流:Object Input Output Stream

10.1 序列化问题

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

  • 序列化:将对象写入到磁盘或者进行网络传输。

  • 要求对象必须实现序列化

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test3.txt"));
Person p = new Person("韩梅梅",18,"中华大街",new Pet());
oos.writeObject(p);
oos.flush();
oos.close();
//反序列化:将磁盘中的对象数据源读出。
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test3.txt"));
Person p1 = (Person)ois.readObject();
System.out.println(p1.toString());
ois.close();

10.2 代码示例

package com.feng.io;

import org.junit.Test;

import java.io.*;

public class Test_08_ObjectInputOutputStream  {

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

    // 对象的序列化过程,将内存中的对象通过 ObjectOutputStream 转换为 二进制流,存储再硬盘文件中
    @Test
    public void testObjectOutputStream(){
        People p1 = new People("张三", 12, new Pet("花花"));
        People p2 = new People("李四", 12, new Pet("小花"));
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("People.txt"));
            oos.writeObject(p1);
            oos.flush();
            oos.writeObject(p2);
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null !=oos){
                try {
                    oos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

/**
 *  对象如果没有序列化,会报错::java.io.NotSerializableException: com.feng.io.People
 *  要实现序列化的类:
 *  1、要求此类是可序列化的;实现 Serializable 接口
 *  2、要求类的属性同样的要实现Serializable接口
 *  3、提供一个版本号:private static final long serialVersionUID
 *  4、使用static 或者 transient 修饰的属性,不可实现序列化   不报错,但是无法
 */
class People implements Serializable{
    private static final long serialVersionUID = 123546;
    private static String name;
    private transient Integer age;
    private Pet pet;

    public People(String name, Integer age, Pet pet) {
        this.name = name;
        this.age = age;
        this.pet = pet;
    }

    @Override
    public String toString() {
        return "People{" +
                "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 + '\'' +
                '}';
    }
}

11.随机访问

package com.feng.io;

import org.junit.Test;

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

/**
 * RandomAccessFile : 支持随机访问
 * 1、既可以充当一个输入流,有可以充当一个输出流
 * 2、支持从文件的开头读取、写入
 * 3、支持从任意位置的读取、写入(插入)
 */
public class Test_09_RandomAccessFile {
    // 对于文件更复杂的话,如果插入
    // 相比较于test03 ,更通用
    @Test
    public void test04(){
        RandomAccessFile raf  = null;
        try {
            raf = new RandomAccessFile(new File("access1.txt"),"rw");
            raf.seek(4); // 改变指针的位置
            byte[] b = new byte[10];
            int len;
            StringBuffer sb = new StringBuffer();
            while ((len = raf.read(b))!= -1){
                sb.append(new String(b, 0, len));
            }
            raf.seek(4);
            raf.write("xy".getBytes());
            raf.write(sb.toString().getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null!=raf){
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // 实现插入的效果:再d 字符后面插入 "xy"
    @Test
    public void test03(){
        RandomAccessFile raf  = null;
        try {
            raf = new RandomAccessFile(new File("access1.txt"),"rw");
            raf.seek(4); // 改变指针的位置
            String str = raf.readLine(); //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 (null!=raf){
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    // 往文件里面写  插入两个字节:xy,再第四个位置加入
    // 实现的实际上是覆盖的效果
    @Test
    public void test02(){
        RandomAccessFile raf  = null;
        try {
            raf = new RandomAccessFile(new File("access1.txt"),"rw");
            raf.seek(4); // 改变指针的位置
            raf.write("xy".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null!=raf){
                try {
                    raf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    @Test
    public void test01(){
        RandomAccessFile raf1 = null;
        RandomAccessFile raf2 = null;
        try {
            raf1 = new RandomAccessFile(new File("access.txt"), "r");
            raf2 = new RandomAccessFile(new File("access1.txt"), "rw");  // r: java.io.IOException: 拒绝访问。
            byte[] b =new byte[20];
            int len;
            while ((len = raf1.read(b))!=-1){
                raf2.write(b, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null!= raf1){
                try {
                    raf1.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null!=raf2){
                try {
                    raf2.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

12. 练习

package com.feng.io;

import org.junit.Test;

import java.io.*;

public class Test_06_Exer {

    /**
     * 字符流 复制 test.txt 为 test1.txt
     */
    @Test
    public void test05(){
        BufferedReader br = null;
        BufferedWriter bw = null;

        try {
            br = new BufferedReader(new FileReader(new File("test.txt")));
            bw = new BufferedWriter(new FileWriter(new File("test1.txt")));
            char[] c = new char[1024];
            int len;
            while ((len = br.read(c)) != -1){
                bw.write(c, 0 ,len);
                bw.flush();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (br != null){
                try {
                    br.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bw != null){
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 使用字节流 实现内容的输入(读取)
     */
    @Test
    public void test04(){
        BufferedInputStream bis = null;
        try {
            bis = new BufferedInputStream(new FileInputStream(new File("text.txt")));
            byte[] b = new byte[1024];
            int len;
            while ((len = bis.read(b))!=-1){
                String str = new String(b, 0, len);
                System.out.print(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bis != null){
                try {
                    bis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 使用字符流实现内容的输入(读取)
     */
    @Test
    public void test03(){
        BufferedReader bf = null;
        try {
            bf = new BufferedReader(new FileReader(new File("text.txt")));
            String str;
            while ((str = bf.readLine())!=null){
                System.out.println(str);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bf != null){
                try {
                    bf.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 使用字符流实现内容的输出
     */
    @Test
    public void test02() {
        BufferedWriter bw = null;
        try {
            bw = new BufferedWriter(new FileWriter(new File("text1.txt")));
            String str = "Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点, \n" +
                    "还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和 \n" +
                    "简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面\n" +
                    "向对象理论,允许程序员以优雅的思维方式进行复杂的编程。";
            bw.write(str);
            bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bw != null){
                try {
                    bw.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 使用字节流实现内容的输出
     */
    @Test
    public void test01() {
//        FileOutputStream fos = new FileOutputStream(new File("test.txt"));
//        BufferedOutputStream bos = new BufferedOutputStream(fos);
        BufferedOutputStream bos = null;
        try {
            bos = new BufferedOutputStream(new FileOutputStream(new File("text.txt")));
            String str = "Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点, \n" +
                    "还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和 \n" +
                    "简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面\n" +
                    "向对象理论,允许程序员以优雅的思维方式进行复杂的编程。";

            bos.write(str.getBytes());
            bos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (bos != null) {
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
相关文章
|
1月前
|
Java
轻松上手Java字节码编辑:IDEA插件VisualClassBytes全方位解析
本插件VisualClassBytes可修改class字节码,包括class信息、字段信息、内部类,常量池和方法等。
89 6
|
2月前
|
搜索推荐 Java 数据库连接
Java|在 IDEA 里自动生成 MyBatis 模板代码
基于 MyBatis 开发的项目,新增数据库表以后,总是需要编写对应的 Entity、Mapper 和 Service 等等 Class 的代码,这些都是重复的工作,我们可以想一些办法来自动生成这些代码。
37 6
|
2月前
|
Java Linux iOS开发
如何设置 Java 的环境变量
设置Java环境变量是使用Java开发工具和运行Java程序的前提。主要步骤包括:安装JDK,配置系统环境变量中的JAVA_HOME、PATH和CLASSPATH,确保命令行可直接调用javac和java命令。
54 6
|
2月前
|
安全 Java Linux
java程序设置开机自启
java程序设置开机自启
142 1
|
4月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
5月前
|
Java 大数据
解析Java中的NIO与传统IO的区别与应用
解析Java中的NIO与传统IO的区别与应用
|
3月前
|
Java 大数据 API
Java 流(Stream)、文件(File)和IO的区别
Java中的流(Stream)、文件(File)和输入/输出(I/O)是处理数据的关键概念。`File`类用于基本文件操作,如创建、删除和检查文件;流则提供了数据读写的抽象机制,适用于文件、内存和网络等多种数据源;I/O涵盖更广泛的输入输出操作,包括文件I/O、网络通信等,并支持异常处理和缓冲等功能。实际开发中,这三者常结合使用,以实现高效的数据处理。例如,`File`用于管理文件路径,`Stream`用于读写数据,I/O则处理复杂的输入输出需求。
232 12
|
4月前
|
Java 数据处理
Java IO 接口(Input)究竟隐藏着怎样的神秘用法?快来一探究竟,解锁高效编程新境界!
【8月更文挑战第22天】Java的输入输出(IO)操作至关重要,它支持从多种来源读取数据,如文件、网络等。常用输入流包括`FileInputStream`,适用于按字节读取文件;结合`BufferedInputStream`可提升读取效率。此外,通过`Socket`和相关输入流,还能实现网络数据读取。合理选用这些流能有效支持程序的数据处理需求。
51 2
|
4月前
|
XML 存储 JSON
【IO面试题 六】、 除了Java自带的序列化之外,你还了解哪些序列化工具?
除了Java自带的序列化,常见的序列化工具还包括JSON(如jackson、gson、fastjson)、Protobuf、Thrift和Avro,各具特点,适用于不同的应用场景和性能需求。
|
4月前
|
缓存 Java
【IO面试题 一】、介绍一下Java中的IO流
Java中的IO流是对数据输入输出操作的抽象,分为输入流和输出流,字节流和字符流,节点流和处理流,提供了多种类支持不同数据源和操作,如文件流、数组流、管道流、字符串流、缓冲流、转换流、对象流、打印流、推回输入流和数据流等。
【IO面试题 一】、介绍一下Java中的IO流