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();
                }
            }
        }
    }
}
相关文章
|
23天前
|
搜索推荐 Java 数据库连接
Java|在 IDEA 里自动生成 MyBatis 模板代码
基于 MyBatis 开发的项目,新增数据库表以后,总是需要编写对应的 Entity、Mapper 和 Service 等等 Class 的代码,这些都是重复的工作,我们可以想一些办法来自动生成这些代码。
30 6
|
1月前
|
Java 应用服务中间件 Maven
【终极解决方案】IDEA maven 项目修改代码不生效。
【终极解决方案】IDEA maven 项目修改代码不生效。
323 1
|
1月前
|
Java Linux 开发工具
IDEA中git提交前如何关闭code analysis以及开启格式化代码
【10月更文挑战第12天】本文介绍了在 IntelliJ IDEA 中关闭代码分析和开启代码格式化的步骤。关闭代码分析可通过取消默认启用检查或针对特定规则进行调整实现,同时可通过设置 VCS 静默模式在提交时跳过检查。开启代码格式化则需在 `Settings` 中配置 `Code Style` 规则,并通过创建 Git 钩子实现提交前自动格式化。
209 3
|
1月前
|
Java 数据处理 开发者
揭秘Java IO流:字节流与字符流的神秘面纱!
揭秘Java IO流:字节流与字符流的神秘面纱!
35 1
|
30天前
|
Java
Java 中 IO 流的分类详解
【10月更文挑战第10天】不同类型的 IO 流具有不同的特点和适用场景,我们可以根据具体的需求选择合适的流来进行数据的输入和输出操作。在实际应用中,还可以通过组合使用多种流来实现更复杂的功能。
46 0
|
1月前
|
存储 Java 程序员
【Java】文件IO
【Java】文件IO
35 0
|
9天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
18天前
|
安全 Java
java 中 i++ 到底是否线程安全?
本文通过实例探讨了 `i++` 在多线程环境下的线程安全性问题。首先,使用 100 个线程分别执行 10000 次 `i++` 操作,发现最终结果小于预期的 1000000,证明 `i++` 是线程不安全的。接着,介绍了两种解决方法:使用 `synchronized` 关键字加锁和使用 `AtomicInteger` 类。其中,`AtomicInteger` 通过 `CAS` 操作实现了高效的线程安全。最后,通过分析字节码和源码,解释了 `i++` 为何线程不安全以及 `AtomicInteger` 如何保证线程安全。
java 中 i++ 到底是否线程安全?
|
6天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
25 9
|
9天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####