Java基础(11)— IO流

IO流

File类

  • java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的建立、查找和删除等操做

构造方法

  • public File(String pathname),经过将给定的路径名字符串转换为抽象路径名来建立新的 File实例。java

  • public File(String parent, String child),从父子路径字符串建立新的 File实例。程序员

  • public File(File parent, String child),从父级File对象和子路径字符串建立新的 File实例。windows

    public static void main(String[] args) {
        /*
        public File(String pathname)
         */
        String pathName = "E:\\hello.txt"; //路径字符串
        File file1 = new File(pathName);
        /*
        public File(String parent, String child) 父子文件路径字符串
         */
        String parent = "D:\\hello";
        String child = "world.txt";
        /*
        public File(File parent, String child) 父级File对象和子路径字符串
         */
        File file3 = new File("D:\\hello");
        child = "java.txt";
    }
    /*
    tips: 
    	1. 一个File对象表明硬盘中实际存在的一个文件或者目录
        2. 不管该路径下是否存在文件或者目录,都不影响File对象的建立
    */

经常使用方法

  • public String getAbsolutePath() ,返回此File的绝对路径名字符串数组

  • public String getPath()将此File转换为路径名字符串app

  • public String getName(),返回由此File表示的文件或目录的名称ide

  • public long length() ,返回由此File表示的文件的长度测试

    public static void main(String[] args) {
        File file1 = new File("D:/hello/world.txt");
        System.out.println("文件绝对路径:" + file1.getAbsolutePath());
        System.out.println("文件构造路径:" + file1.getPath());
        System.out.println("文件名称:" + file1.getName());
        System.out.println("文件长度:" + file1.length());
        File file2 = new File("D:/hello");
        System.out.println("目录绝对路径:" + file2.getAbsolutePath());
        System.out.println("目录构造路径:" + file2.getPath());
        System.out.println("目录名称:" + file2.getName());
        System.out.println("目录长度:" + file2.length());
    }
    /*
    文件绝对路径:D:\hello\world.txt
    文件构造路径:D:\hello\world.txt
    文件名称:world.txt
    文件长度:288字节
    文件绝对路径:D:\hello
    文件构造路径:D:\hello
    文件名称:hello
    文件长度:
    */
    // API中说明:length(),表示文件的长度。可是File对象表示目录,则返回值未指定。

绝对路径和相对路径

  • 绝对路径:从盘符开始的路径,这是一个完整的路径优化

  • 相对路径:相对于项目目录的路径,这是一个便捷的路径,开发中常用编码

    public static void main(String[] args) { 
        // D盘下的hello.java文件 
        File f = new File("D:\\hello.java"); 
        System.out.println(f.getAbsolutePath()); 
        // 项目下的hello.java文件 
        File f2 = new File("hello.java"); 
        System.out.println(f2.getAbsolutePath()); 
    }
    /*
    D:\hello.java 
    D:\idea_project\hello.java
    */

判断功能的方法

  • public boolean exists(),此File表示的文件或目录是否实际存在idea

  • **public boolean isDirectory() **,此File是否为目录

  • public boolean isFile() ,此File是否为文件

    public static void main(String[] args) { 
        File file1 = new File("d:\\hello\\world.java"); 
        File file2 = new File("d:\\java"); 
        // 判断是否存在 
        System.out.println("d:\\hello\\world.java 是否存在:"+f.exists()); 
        System.out.println("d:\\java 是否存在:"+f2.exists()); 
        // 判断是文件仍是目录 
        System.out.println("d:\\java 文件?:"+f2.isFile());
        System.out.println("d:\\java 目录?:"+f2.isDirectory()); 
    } 
    /*
    输出结果: 
    	d:\hello\world.javaa 是否存在:true 
    	d:\java 是否存在:true 
    	d:\java 文件?:false 
    	d:\java 目录?:true
    */

建立删除功能的方法

  • public boolean createNewFile(),当且仅当具备该名称的文件尚不存在时,建立一个新的空文件

  • public boolean delete(),删除由此File表示的文件或目录。

  • public boolean mkdir(),建立由此File表示的目录

  • public boolean mkdirs(),建立由此File表示的目录,包括任何须需但不存在的父目录

    public static void main(String[] args) throws IOException { 
        // 文件的建立 
        File f = new File("aaa.txt"); 
        System.out.println("是否存在:"+f.exists()); // false 
        System.out.println("是否建立:"+f.createNewFile()); // true 
        System.out.println("是否存在:"+f.exists()); // true 
        // 目录的建立 
        File f2= new File("newDir"); 
        System.out.println("是否存在:"+f2.exists());// false 
        System.out.println("是否建立:"+f2.mkdir()); // true 
        System.out.println("是否存在:"+f2.exists());// true 
        // 建立多级目录 
        File f3= new File("newDira\\newDirb"); 
        System.out.println(f3.mkdir());// false 
        File f4= new File("newDira\\newDirb"); 
        System.out.println(f4.mkdirs());// true 
        // 文件的删除 
        System.out.println(f.delete());// true 
        // 目录的删除 
        System.out.println(f2.delete());// true 
        System.out.println(f4.delete());// false
    } 
    /*
    	API中说明:delete方法,若是此File表示目录,则目录必须为空才能删除。
    */

目录的遍历

  • public String[] list(),返回一个String数组,表示该File目录中的全部子文件或目录

  • public File[] listFiles(),返回一个File数组,表示该File目录中的全部的子文件或目录

    public static void main(String[] args) { 
        File dir = new File("d:\\java_code"); 
        //获取当前目录下的文件以及文件夹的名称。 
        String[] names = dir.list(); 
        for(String name : names){ 
            System.out.println(name);
        }
        //获取当前目录下的文件以及文件夹对象,只要拿到了文件对象,那么就能够获取更多信息 
        File[] files = dir.listFiles(); 
        for (File file : files) { 
            System.out.println(file); 
        } 
    }
    /*
    	tips: 调用listFiles方法的File对象,表示的必须是实际存在的目录,不然返回null,没法进行遍历。
    */

IO概述

什么是IO流

  • 生活中,你确定经历过这样的场景。当你编辑一个文本文件,忘记了 ctrl+s ,可能文件就白白编辑了。当你电脑 上插入一个U盘,能够把一个视频,拷贝到你的电脑硬盘里。那么数据都是在哪些设备上的呢?键盘、内存、硬 盘、外接设备等等。

  • 咱们把这种数据的传输,能够看作是一种数据的流动,按照流动的方向,之内存为基准,分为 输入input 和 输出 output ,即流向内存是输入流,流出内存的输出流。

  • Java中I/O操做主要是指使用 java.io 包下的内容,进行输入、输出操做。输入也叫作读取数据,输出也叫作做写出数据

  • IO的流向说明图解

    截图

IO的分类

  • 根据数据的流向分为:输入流和输出流
    • 输入流 :把数据从 其余设备 上读取到 内存 中的流。
    • 输出流 :把数据从 内存 中写出到 其余设备 上的流。
  • 格局数据的类型分为:字节流和字符流
    • 字节流 :以字节为单位,读写数据的流。
    • 字符流 :以字符为单位,读写数据的流。

IO流的顶级父类

输入流 输出流
字节流 字节输入流
InputStream
字节输出流
OutPutStream
字符流 字符输入流
Reader
字符输出流
Writer

字节流

一切皆为字节

  • 一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一 样如此。因此,字节流能够传输任意文件数据。在操做流的时候,咱们要时刻明确,不管使用什么样的流对象,底 层传输的始终为二进制数据。

字节输出流

  • java.io.OutputStream,抽象类是表示字节输出流的全部类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。

    • public void close()关闭此输出流并释放与此流相关联的全部系统资源
    • public void flush()刷新此输出流并强制任何缓冲的输出字节被写出
    • public void write(byte[] b),将 b.length字节从指定的字节数组写入此输出流
    • public void write(byte[] b, int off, int len)从指定的字节数组写入 len字节,从偏移量 offff开始输 出到此输出流
    • public abstract void write(int b),将指定的字节输出流
    /*
    tips:
    	当完成流操做时,必须调用close(),关闭并释放资源
    */

文件输出流

  • java.io.FileOutputStream 是 OutPutStream 的子类,名为文件输出流,用于将数据写出到文件

  • 构造方法(输出=写 / 输入=读)

    • public FileOutputStream(File file),建立文件输出流以写出由指定的 File对象表示的文件
    • public FileOutputStream(String name), 建立文件输出流以指定的名称写出文件
  • 当你建立一个流对象时,必须传入一个文件路径。该路径下,若是没有这个文件,会建立该文件。若是有这个文 件,会清空这个文件的数据

    public class FileOutputStreamConstructor throws IOException { 
        public static void main(String[] args) { 
            // 使用File对象建立流对象
            File file = new File("a.txt"); 
            FileOutputStream fos = new FileOutputStream(file); 
            // 使用文件名称建立流对象 
            FileOutputStream fos = new FileOutputStream("b.txt"); 
        } 
    }
  • 写出字节数据

    • 写入字节 write(int byte),每次能够写出一个字节数据
    public class FOSWrite {
        public static void main(String[] args) throws IOException { 
            // 使用文件名称建立流对象 
            FileOutputStream fos = new FileOutputStream("fos.txt"); 
            // 写出数据 
            fos.write(97); // 写出第1个字节 
            fos.write(98); // 写出第2个字节 
            fos.write(99); // 写出第3个字节 
            // 关闭资源 
            fos.close(); 
        } 
    }
    /*
    tips:
    	1. 虽然参数为int类型四个字节,可是只会保留一个字节的信息写出
        2. 流操做完毕后,必须释放系统资源,调用close(),千万记得
    */
    • 写出字节数组 write(byte[] byte),每次能够写出数组中的数据
    public class FOSWrite { 
        public static void main(String[] args) throws IOException { 
            // 使用文件名称建立流对象 
            FileOutputStream fos = new FileOutputStream("fos.txt"); 
            // 字符串转换为字节数组 
            byte[] b = "完美世界".getBytes(); 
            // 写出字节数组数据 
            fos.write(b); 
            // 关闭资源 
            fos.close(); 
        } 
    }
    /*
    输出结果:
    	完美世界
    */
    • 写出指定长度字节数组 write(byte[] byte, int off, int len),每次写出从off索引开始,len个字节
    public class FOSWrite { 
        public static void main(String[] args) throws IOException { 
            // 使用文件名称建立流对象 
            FileOutputStream fos = new FileOutputStream("fos.txt"); 
            // 字符串转换为字节数组 
            byte[] b = "abcde".getBytes(); 
            // 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。 
            fos.write(b,2,2);
            // 关闭资源
            fos.close();
        } 
    }
    /*
    输出结果:
    	cd
    */
  • 数组追加续写

    • 通过以上的演示,每次程序运行,建立输出流对象,都会清空目标文件中的数据。如何保留目标文件中数据,还能继续添加新数据呢?

    • public FileOutputStream(File file, boolean append),建立文件输出流以指定的File对象续写或重写文件的数据

    • public FileOutputStream(String name, boolean append),建立文件输出流以指定的名称续写或重写文件的数据

    • 这两个构造方法,参数中都须要传入一个boolean类型的值, true 表示追加数据, false 表示清空原有数据。 这样建立的输出流对象,就能够指定是否追加续写了

    public class FOSWrite { 
        public static void main(String[] args) throws IOException { 
            // 使用文件名称建立流对象
            FileOutputStream fos = new FileOutputStream("fos.txt",true); 
            // 字符串转换为字节数组
            byte[] b = "abcde".getBytes(); 
            // 写出从索引2开始,2个字节。索引2是c,两个字节,也就是cd。 fos.write(b);
            // 关闭资源
            fos.close(); 
        } 
    }
    /*
    	文件操做前:cd 
    	文件操做后:cdabcde
    */
  • 写出换行

    • windows系统里,换行符号是\r\n
    public class FOSWrite {
        public static void main(String[] args) throws IOException {
            // 使用文件名称建立流对象 
            FileOutputStream fos = new FileOutputStream("fos.txt"); 
            // 定义字节数组 
            byte[] words = {97,98,99,100,101};
            // 遍历数组 
            for (int i = 0; i < words.length; i++) { 
                // 写出一个字节 
                fos.write(words[i]);
                // 写出一个换行, 换行符号转成数组写出 
                fos.write("\r\n".getBytes()); 
            }
            // 关闭资源 
            fos.close();
        }
    }
    /*
    输出结果:
    	a
    	b
    	c
    	d
    	e
    */ 
    /*
    回车符 \r 和换行符 \n :
    	回车符:回到一行的开头(return)。 
    	换行符:下一行(newline)。 
    系统中的换行: 
    	Windows系统里,每行结尾是 回车+换行 ,即 \r\n  
    	Unix系统里,每行结尾只有 换行 ,即 \n  
    	Mac系统里每行结尾是 回车 ,即 \r [从 Mac OS X开始与Linux统一]
    */

字节输入流

  • java.io.InputStream抽象类是表示字节输入流的全部类的超类,能够读取字节信息到内存中。它定义了字节输入流的基本共性功能方法

    • public void close(),关闭此输入流并释放与此流相关联的任何系统资源
    • public abstract int read(),从输入流读取数据的下一个字节
    • public int read(byte[] b),从输入流中读取一些字节数,并将它们存储到字节数组 b中
  • tips:流操做完成时,必须调用close()方法关闭,释放系统资源

文件输入流

  • java.io.FileInputputStream 是 InputStream的子类,名为文件输出流,从文件中读取字节

  • 构造方法

    • FileInputStream(File file),经过打开与实际文件的链接来建立一个FileInputStream,该文件由文件系统中的File对象file命名。
    • FileInputStream(String name),经过打开与实际文件的链接来建立一个FileInputStream,该文件由文件系统中的路径名 name命名
  • tips:当建立一个流对象时,必须传入一个文件路径。若是路径下没有该文件会抛出FileNotFoundException异常

    public class FileInputStreamConstructor throws IOException{ 
        public static void main(String[] args) {
            // 使用File对象建立流对象 
            File file = new File("a.txt"); 
            FileInputStream fos = new FileInputStream(file); 
            // 使用文件名称建立流对象 
            FileInputStream fos = new FileInputStream("b.txt"); 
        } 
    }
  • 读取字节数据

    • read(),每次能够读取一个字节的数据,提高为int类型,读取到文件末尾返回-1
    public class FISRead { 
        public static void main(String[] args) throws IOException{ 
            // 使用文件名称建立流对象
            FileInputStream fis = new FileInputStream("read.txt"); 
            // 读取数据,返回一个字节 
            int read = fis.read(); 
            System.out.println((char) read); 
            read = fis.read(); 
            System.out.println((char) read); 
            read = fis.read(); 
            System.out.println((char) read);
            read = fis.read(); 
            System.out.println((char) read);
            read = fis.read(); 
            System.out.println((char) read); 
            // 读取到末尾,返回‐1 
            read = fis.read(); 
            System.out.println( read);
            // 关闭资源 
            fis.close(); 
        } 
    }
    /*
    输出结果:
    	a
    	b
    	c
    	d
    	e
    	-1
    */
    • 循环改进读取方式
    public class FISRead { 
        public static void main(String[] args) throws IOException{ 
            // 使用文件名称建立流对象
            FileInputStream fis = new FileInputStream("read.txt");
            // 定义变量,保存数据 
            int b ;
            // 循环读取 
            while ((b = fis.read()) != -1) {
                System.out.println((char)b);//读取字节会自动提高为int,因此须要转char
            }
            // 关闭资源 
            fis.close(); 
        } 
    }
    /*
    输出结果:
    	a
    	b
    	c
    	d
    	e
    */
  • 使用字节数组读取

    • read(byte[] b) ,每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回 -1
    public class FISRead { 
        public static void main(String[] args) throws IOException{ 
          	// 使用文件名称建立流对象. 
            FileInputStream fis = new FileInputStream("read.txt");// 文件中为abcde
            // 定义变量,做为有效个数 
            int len;
            // 定义字节数组,做为装字节数据的容器 
            byte[] b = new byte[2]; 
            // 循环读取 
            while (( len= fis.read(b))!= -1) { 
                 // 每次读取后,把数组变成字符串打印 
                 System.out.println(new String(b)); 
            }
            // 关闭资源 
            fis.close(); 
        }
    }
    /*
    输出结果:
    	ab
    	cd
    	ed
    */
    • tips:错误数据ed是因为最后一次读取时,只读取一个字节e,数组里上次读取的数据没有被彻底替换,因此要经过len,获取有效的字节
    public class FISRead { 
        public static void main(String[] args) throws IOException{ 
            // 使用文件名称建立流对象
            FileInputStream fis = new FileInputStream("read.txt"); // 文件中为abcde 
            // 定义变量,做为有效个数
            int len;
            // 定义字节数组,做为装字节数据的容器 
            byte[] b = new byte[2];
            // 循环读取 
            while (( len= fis.read(b))!=‐1) { 
                // 每次读取后,把数组的有效字节部分,变成字符串打印 
                System.out.println(new String(b,0,len));// len 每次读取的有效字节个数 
            }
            // 关闭资源 
            fis.close(); 
        }
    }
    /*
    输出结果:
    	ab
    	cd
    	e
    */
  • 字节流练习(图片复制)

    • 复制原理图解

    截图

    • 案例实现
    public class Copy {
        public static void main(String[] args) throws IOException {
            // 1.建立流对象
            // 1.1 指定数据源
            FileInputStream fis = new FileInputStream("D:\\test.jpg");
            // 1.2 指定目的地
            FileOutputStream fos = new FileOutputStream("test_copy.jpg");
            // 2.读写数据
            // 2.1 定义数组
            byte[] b = new byte[1024];
            // 2.2 定义长度
            int len;
            // 2.3 循环读取
            while ((len = fis.read(b)) != -1) {
                // 2.4 写出数据
                fos.write(b, 0 , len);
            }
            // 3.关闭资源
            fos.close(); fis.close();
        }
    }
    • tips:流关闭原则,先开后关,后开先关。

字符流

  • 当使用字节流读取文本文件时,可能会有一个小问题。就是遇到中文字符时,可能不会显示完整的字符,那是由于一个中文字符可能占用多个字节存储。因此Java提供一些字符流类,以字符为单位读写数据,专门用于处理文本文件。

字符输入流

  • java.io.Reader 抽象类是表示用于读取字符流的全部类的超类,能够读取字符信息到内存中。它定义了字符输入 流的基本共性功能方法。

    • public void close(),关闭此流并释放与此流相关联的任何系统资源
    • public int read(),从输入流读取一个字符
    • public int read(char[] cbuf),从输入流中读取一些字符,并将它们存储到字符数组 cbuf中
  • FileReader 类

    • java.io.FileReader 类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区

    ※ tips:

    • 字符编码:字节与字符的对应规则。Windows系统的中文编码默认是GBK编码表。idea中UTF-8

    • 字节缓冲区:一个字节数组,用来临时存储字节数据

  • 构造方法

    • FileReader(File file),建立一个新的 FileReader ,给定要读取的File对象。
    • FileReader(String fileName),建立一个新的 FileReader ,给定要读取的文件的名称‘
    • 当你建立一个流对象时,必须传入一个文件路径。相似于FileInputStream
    public class FileReaderConstructor throws IOException{ 
        public static void main(String[] args) { 
            // 使用File对象建立流对象 
            File file = new File("a.txt"); 
            FileReader fr = new FileReader(file); 
            // 使用文件名称建立流对象 
            FileReader fr = new FileReader("b.txt"); 
        } 
    }
  • 读取字符数据

    • read 方法,每次能够读取一个字符的数据,提高为int类型,读取到文件末尾返回 -1 ,循环读取
    public class FRRead { 
        public static void main(String[] args) throws IOException { 
            // 使用文件名称建立流对象
            FileReader fr = new FileReader("read.txt"); 
            // 定义变量,保存数据 
            int b;
            // 循环读取 
            while ((b = fr.read())!=‐1) {
                System.out.println((char)b); 
            }
            // 关闭资源 
            fr.close(); 
        }
    }
    /*
    输出结果:
    	叶
    	遮
    	天
    */

    ※ tips:虽然读取了一个字符,可是会自动提高为int类型

    • read(char[] cbuf) ,每次读取b的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时返回 -1
    public class FRRead { 
        public static void main(String[] args) throws IOException { 
            // 使用文件名称建立流对象 
            FileReader fr = new FileReader("read.txt"); 
            // 定义变量,保存有效字符个数 
            int len;
            // 定义字符数组,做为装字符数据的容器 
            char[] cbuf = new char[2]; 
            // 循环读取 
            while ((len = fr.read(cbuf))!=‐1) { 
                System.out.println(new String(cbuf)); 
            }
            // 关闭资源 
            fr.close(); 
        } 
    }
    /*
    输出结果:
    	叶遮
    	天遮
    */
    • 获取有效字符改进
    public class FISRead { 
        public static void main(String[] args) throws IOException { 
            // 使用文件名称建立流对象 
            FileReader fr = new FileReader("read.txt"); 
            // 定义变量,保存有效字符个数 
            int len;
            // 定义字符数组,做为装字符数据的容器 
            char[] cbuf = new char[2]; 
            // 循环读取 
            while ((len = fr.read(cbuf))!=‐1) { 
                System.out.println(new String(cbuf,0,len)); 
            }
            // 关闭资源 
            fr.close(); 
        } 
    }
    /*
    输出结果:
    	叶遮
        天
    */

字符输出流

  • java.io.Writer抽象类是表示用于写出字符流的全部类的超类,将指定的字符信息写出到目的地。它定义了字节 输出流的基本共性功能方法。

    • void write(int c) 写入单个字符
    • void write(char[] cbuf) 写入字符数组
    • abstract void write(char[] cbuf, int off, int len) 写入字符数组的某一部分,offff数组的开始索引,len 写的字符个数。
    • void write(String str) 写入字符串。
    • void write(String str, int off, int len) 写入字符串的某一部分,offff字符串的开始索引,len写的字符个 数。
    • void flush() 刷新该流的缓冲。
    • void close() 关闭此流,但要先刷新它。
  • FileWriter类

    • java.io.FileWriter 类是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区
  • 构造方法

    • FileWriter(File file) : 建立一个新的 FileWriter,给定要读取的File对象
    • FileWriter(String fileName) : 建立一个新的 FileWriter,给定要读取的文件的名称
    public class FileWriterConstructor {     
        public static void main(String[] args) throws IOException { 
            // 使用File对象建立流对象               
            File file = new File("a.txt");        
            FileWriter fw = new FileWriter(file);                
            // 使用文件名称建立流对象         
            FileWriter fw = new FileWriter("b.txt");    
        } 
    }
  • 基本输出数据

    • write(int byte),每次能够写出一个字符数据,代码使用演示:
    public class FWWrite {     
        public static void main(String[] args) throws IOException {         
            // 使用文件名称建立流对象         
            FileWriter fw = new FileWriter("fw.txt");             
            // 写出数据           
            fw.write(97); // 写出第1个字符           
            fw.write('b'); // 写出第2个字符           
            fw.write('C'); // 写出第3个字符          
            fw.write(30000); // 写出第4个字符,中文编码表中30000对应一个汉字
            /*            
            【注意】关闭资源时,与FileOutputStream不一样。         
             若是不关闭,数据只是保存到缓冲区,并未保存到文件。            
            */         
            // fw.close();     
        }
    }
    /*
    输出结果:
    	abc田
    */

    ※ tips:

    • 虽然参数为int类型四个字节,可是只会保留一个字符的信息写出
    • 未调用close方法,数据只是保存到了缓冲区,并未写出到文件中
  • 关闭和刷新

    由于内置缓冲区的缘由,若是不关闭流输出,没法写出字符到文件中。可是关闭的流对象,是没法继续写出数据的。若是咱们既想写出数据,又想继续使用流,就须要flush方法了

    • flush,刷新缓冲区,流对象能够继续使用
    • close,先刷新缓冲区,而后通知系统释放资源。流对象不能够再被使用了
    public class FWWrite {     
        public static void main(String[] args) throws IOException {      
            // 使用文件名称建立流对象        
            FileWriter fw = new FileWriter("fw.txt");        
            // 写出数据,经过flush         
            fw.write('刷'); // 写出第1个字符        
            fw.flush();         
            fw.write('新'); // 继续写出第2个字符,写出成功         
            fw.flush();              
            // 写出数据,经过close           
            fw.write('关'); // 写出第1个字符        
            fw.close();         
            fw.write('闭'); // 继续写出第2个字符,【报错】java.io.IOException: Stream closed         f
            w.close();    
        } 
    }

    ※ tips:即使是flush()写出了数据,操做的最后仍是要调用close方法,释放系统资源

  • 写出其余数据

    • 写出字符组,write(char[] cbuf) 和 write(char[] cbuf, int off, int len),每次能够写出字符数组中的数据,用法相似FileOutStream
    public class FWWrite {     
        public static void main(String[] args) throws IOException {        
            // 使用文件名称建立流对象
            FileWriter fw = new FileWriter("fw.txt");             
            // 字符串转换为字节数组           
            char[] chars = "完美世界".toCharArray();                  
            // 写出字符数组          
            fw.write(chars); // 完美世界   
            // 写出从索引2开始,2个字节。索引2是'美',两个字节,也就是'美世'。                  
            fw.write(b,2,2); // 美世     
            // 关闭资源           
            fos.close();    
        }
    }
    • 写出字符串,write(String str) 和 write(String str, int off, int len),每次能够写出字符串中的数据,更为方便
    public class FWWrite {    
        public static void main(String[] args) throws IOException {        
            // 使用文件名称建立流对象        
            FileWriter fw = new FileWriter("fw.txt");        
            // 字符串           
            String msg = "完美世界";                  
            // 写出字符数组           
            fw.write(msg); //完美世界       
            // 写出从索引2开始,2个字节。索引2是'美',两个字节,也就是'美世'。              
            fw.write(msg,2,2); // 美世            
            // 关闭资源         
            fos.close();  
        }
    }
    • 续写和换行,操做相似于FileOutStream
    public class FWWrite {    
        public static void main(String[] args) throws IOException {    
            // 使用文件名称建立流对象,能够续写数据         
            FileWriter fw = new FileWriter("fw.txt",true);        
            // 写出字符串           
            fw.write("遮天");        
            // 写出换行          
            fw.write("\r\n");         
            // 写出字符串      
            fw.write("完美世界");          
            // 关闭资源           
            fw.close();  
        }
    }
    /*
    输出结果:
    	遮天
    	完美世界
    */

    ※ tips:字符流,只能操做文本文件,不能操做图片,视频等非文本文件。

IO异常处理

  • JDK 7 以前的处理

    public class HandleException1 {   
        public static void main(String[] args) {      
            // 声明变量            
            FileWriter fw = null; 
            try {            
                //建立流对象       
                fw = new FileWriter("fw.txt");         
                // 写出数据             
                fw.write("完美世界"); //完美世界       
            } catch (IOException e) {    
                e.printStackTrace();       
            } finally {            
                try {                
                    if (fw != null) {        
                        fw.close();         
                    }            
                } catch (IOException e) {   
                    e.printStackTrace();      
                }        
            }    
        }
    }
  • JDK 7 的处理

    /*还可使用JDK7优化后的 try-with-resource 语句,该语句确保了每一个资源在语句结束时关闭。所谓的资源 (resource)是指在程序完成后,必须关闭的对象*/
    //格式
    try (建立流对象语句,若是多个,使用';'隔开) { 
        // 读写数据      
    } catch (IOException e) {
        e.printStackTrace();    
    }
    public class HandleException2 {    
        public static void main(String[] args) {   
            // 建立流对象            
            try ( FileWriter fw = new FileWriter("fw.txt"); ) {   
                // 写出数据             
                fw.write("黑马程序员"); //黑马程序员        
            } catch (IOException e) {  
                e.printStackTrace();     
            }   
        } 
    }
  • JDK 9的改进

    /*JDK9中 try-with-resource 的改进,对于引入对象的方式,支持的更加简洁。被引入的对象,一样能够自动关闭, 无需手动close,咱们来了解一下格式。*/
    /*改进前*/
    // 被final修饰的对象 
    final Resource resource1 = new Resource("resource1"); 
    // 普通对象 
    Resource resource2 = new Resource("resource2"); 
    // 引入方式:建立新的变量保存 
    try (Resource r1 = resource1;
         Resource r2 = resource2) { 
        // 使用对象 
    }
    /*改进后*/
    // 被final修饰的对象
    final Resource resource1 = new Resource("resource1"); 
    // 普通对象 
    Resource resource2 = new Resource("resource2");   
    // 引入方式:直接引入 
    try (resource1; resource2) {      
        // 使用对象
    }
    public class TryDemo {     
        public static void main(String[] args) throws IOException {    
            // 建立流对象         
            final  FileReader fr  = new FileReader("in.txt");  
            FileWriter fw = new FileWriter("out.txt");
            // 引入到try中 
            try (fr; fw) {      
                // 定义变量         
                int b;            
                // 读取数据           
                while ((b = fr.read())!=‐1) {    
                    // 写出数据                 
                    fw.write(b);            
                }           
            } catch (IOException e) {  
                e.printStackTrace();   
            }    
        }
    }

属性集

  • 概述
    • java.util.Properties 继承于 Hashtable ,来表示一个持久的属性集。它使用键值结构存储数据,每一个键及其 对应值都是一个字符串。该类也被许多Java类使用,好比获取系统属性时, System.getProperties 方法就是返回 一个 Properties 对象。

Properties类

  • 构造方法

    • public Properties() ,建立一个空的属性列表
  • 基本的存储方法

    • public Object setProperty(String key, String value),保存一对属性。
    • public String getProperty(String key),使用此属性列表中指定的键搜索属性值。
    • public Set stringPropertyNames(),全部键的名称的集合。
    public class ProDemo {     
        public static void main(String[] args) throws FileNotFoundException {    
            // 建立属性集对象        
            Properties properties = new Properties();     
            // 添加键值对元素        
            properties.setProperty("filename", "a.txt");       
            properties.setProperty("length", "209385038");     
            properties.setProperty("location", "D:\\a.txt");         
            // 打印属性集对象        
            System.out.println(properties);     
            // 经过键,获取属性值         
            System.out.println(properties.getProperty("filename"));        
            System.out.println(properties.getProperty("length"));      
            System.out.println(properties.getProperty("location"));     
            // 遍历属性集,获取全部键的集合
             Set<String> strings = properties.stringPropertyNames();   
            // 打印键值对         
            for (String key : strings ) {  
                System.out.println(key+" ‐‐ "+properties.getProperty(key));       
            }     
        } 
    } 
    /*
    输出结果: 
    {filename=a.txt, length=209385038, location=D:\a.txt} 
    a.txt 
    209385038 
    D:\a.txt 
    filename ‐‐ a.txt 
    length ‐‐ 209385038 
    location ‐‐ D:\a.txt
    */
  • 与流相关的方法

    • public void load(InputStream inStream),从字节输入流中读取键值对。参数中使用了字节输入流,经过流对象,能够关联到某文件上,这样就可以加载文本中的数据了
    filename=a.txt 
    length=209385038
    location=D:\a.txt
    public class ProDemo2 {    
        public static void main(String[] args) throws FileNotFoundException {   
            // 建立属性集对象        
            Properties pro = new Properties();   
            // 加载文本中信息到属性集        
            pro.load(new FileInputStream("read.txt"));  
            // 遍历集合并打印         
            Set<String> strings = pro.stringPropertyNames();    
            for (String key : strings ) {     
                System.out.println(key+" ‐‐ "+pro.getProperty(key));         
            }      
        }
    } 
    /*
    输出结果:
    	filename ‐‐ a.txt 
    	length ‐‐ 209385038 
    	location ‐‐ D:\a.txt
    */

    ※ tips: 文本中的数据,必须是键值对形式,可使用空格,等号,冒号等符号分隔。

缓冲流

  • 缓冲流,也叫高效流,是对4个基本的 FileXxx 流的加强,因此也是4个流,按照数据类型分类

    • 字节缓冲流: BufferedInputStream , BufferedOutputStream
    • 字符缓冲流: BufferedReader , BufferedWriter
  • 缓冲流基本原理,实在建立流对象时会建立一个内置的默认大小的缓冲区数组,经过缓冲区读写,减小系统IO次数,从而提升读写的效率

字节缓冲流

构造方法

  • public BufferedInputStream(InputStream in),建立一个 新的缓冲输入流

  • public BufferedOutputStream(OutputStream out),建立一个新的缓冲输出流

    // 建立字节缓冲输入流 
    BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bis.txt"));
    // 建立字节缓冲输出流 
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bos.txt"));

效率测试

  • 查询API,缓冲流读写方法与基本的流是一致的,咱们经过复制大文件(375MB),测试它的效率

    • 基本流
    public class BufferedDemo {    
        public static void main(String[] args) throws FileNotFoundException {     
            // 记录开始时间        
            long start = System.currentTimeMillis();    
            // 建立流对象                 
            try (          
                FileInputStream fis = new FileInputStream("jdk9.exe");
                FileOutputStream fos = new FileOutputStream("copy.exe")
            ){  
                // 读写数据                 
                int b;            
                while ((b = fis.read()) != ‐1) {    
                    fos.write(b);            
                }         
            } catch (IOException e) {  
                e.printStackTrace();       
            } // 记录结束时间               
            long end = System.currentTimeMillis();        
            System.out.println("普通流复制时间:"+(end ‐ start)+" 毫秒");  
        } 
    }
    //十几分钟过去了....
    • 缓冲流
    public class BufferedDemo {    
        public static void main(String[] args) throws FileNotFoundException {   
            // 记录开始时间        
            long start = System.currentTimeMillis();    
            // 建立流对象                  
            try (          
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk9.exe"));
                BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.exe"))
            ){
                // 读写数据             
                int b;             
                while ((b = bis.read()) != ‐1) {                 
                    bos.write(b);             
                }         
            } catch (IOException e) {  
                e.printStackTrace();         
            } 
            // 记录结束时间                 
            long end = System.currentTimeMillis();   
            System.out.println("缓冲流复制时间:"+(end ‐ start)+" 毫秒");    
        } 
    }
    //缓冲流复制时间:8016 毫秒
    • 如何更快呢?固然是使用数组的方式
    public class BufferedDemo {   
        public static void main(String[] args) throws FileNotFoundException {  
            // 记录开始时间            
            long start = System.currentTimeMillis(); 
            // 建立流对象        
            try ( BufferedInputStream bis = new BufferedInputStream(new FileInputStream("jdk9.exe"));
                 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy.exe"))
                ){            
                // 读写数据        
                int len;           
                byte[] bytes = new byte[8*1024];    
                while ((len = bis.read(bytes)) != ‐1) {  
                    bos.write(bytes, 0 , len);       
                }         
            } catch (IOException e) {  
                e.printStackTrace();      
            } 
            // 记录结束时间                 
            long end = System.currentTimeMillis();         
            System.out.println("缓冲流使用数组复制时间:"+(end ‐ start)+" 毫秒");    
        } 
    }
    //缓冲流使用数组复制时间 666毫秒

字符缓冲流

构造方法

  • public BufferedReader(Reader in),建立一个 新的缓冲输入流。

  • public BufferedWriter(Writer out),建立一个新的缓冲输出流

    // 建立字符缓冲输入流 
    BufferedReader br = new BufferedReader(new FileReader("br.txt")); 
    // 建立字符缓冲输出流 
    BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt"));

特有方法

  • 字符缓冲流的基本方法与普通字符流调用方式一致,再也不阐述,咱们来看它们具有的特有方法

    • BufferedReader: public String readLine(),读一行文字。
    • BufferedWriter: public void newLine(),写一行行分隔符,由系统属性定义符号
  • readLine

    public class BufferedReaderDemo {     
        public static void main(String[] args) throws IOException {        
            // 建立流对象            
            BufferedReader br = new BufferedReader(new FileReader("in.txt")); 
            // 定义字符串,保存读取的一行文字                  
            String line  = null;        
            // 循环读取,读取到最后返回null      
            while ((line = br.readLine())!=null) {        
                System.out.print(line);             
                System.out.println("‐‐‐‐‐‐");        
            } // 释放资源                 
            br.close();     
        } 
    }
  • newLine

    public class BufferedWriterDemo throws IOException {    
        public static void main(String[] args) throws IOException  {     
            // 建立流对象   
            BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt"));    
            // 写出数据           
            bw.write("完");     
            // 写出换行           
            bw.newLine();         
            bw.write("美");        
            bw.newLine();       
            bw.write("世界");      
            bw.newLine(); 
            // 释放资源        
            bw.close();    
        }
    }
    /*
    输出结果:
    	完
    	美
    	世界
    */
  • 练习,文本排序,请假下列信息恢复顺序

    3..侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚觉得宫中之事,事无大小,悉 以咨之,而后施行,必得裨补阙漏,有所广益。
    8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其 咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。 
    4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚觉得营中之事,悉以咨之, 必能使行阵和气,优劣得所。
    2.宫中府中,俱为一体,陟罚臧否,不宜异同。如有做奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不 宜偏私,使内外异法也。 
    1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外 者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以 塞忠谏之路也。 
    9.今当远离,临表涕零,不知所言。 
    6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨 以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
    7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深刻不毛。 今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣因此报先帝而忠陛 下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
    5.亲贤臣,远小人,此先汉因此兴隆也;亲小人,远贤臣,此后汉因此倾颓也。先帝在时,每与臣论此事,何尝不叹息 痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。
    
    案例分析 
    1. 逐行读取文本信息。 
    2. 解析文本信息到集合中。 
    3. 遍历集合,按顺序,写出文本信息
    public class BufferedTest {    
        public static void main(String[] args) throws IOException {      
            // 建立map集合,保存文本数据,键为序号,值为文字    
            HashMap<String, String> lineMap = new HashMap<>();  
            // 建立流对象         
            BufferedReader br = new BufferedReader(new FileReader("in.txt"));         BufferedWriter bw = new BufferedWriter(new FileWriter("out.txt")); 
            // 读取数据         
            String line  = null;    
            while ((line = br.readLine())!=null) {     
                // 解析文本           
                String[] split = line.split("\\.");       
                // 保存到集合             
                lineMap.put(split[0],split[1]);
            }        
            // 释放资源       
            br.close();          
            // 遍历map集合         
            for (int i = 1; i <= lineMap.size(); i++) {    
                String key = String.valueOf(i);         
                // 获取map中文本            
                String value = lineMap.get(key);    
                // 写出拼接文本               
                bw.write(key+"."+value);     
                // 写出换行              
                bw.newLine();       
            }
            // 释放资源            
            bw.close();    
        } 
    }

转换流

字符编码和字符集

字符编码

  • 计算机中储存的信息都是用二进制数表示的,而咱们在屏幕上看到的数字、英文、标点符号、汉字等字符是二进制 数转换以后的结果。按照某种规则,将字符存储到计算机中,称为编码 。反之,将存储在计算机中的二进制数按照 某种规则解析显示出来,称为解码 。好比说,按照A规则存储,一样按照A规则解析,那么就能显示正确的文本f符 号。反之,按照A规则存储,再按照B规则解析,就会致使乱码现象。
  • 字符编码 Character Encoding : 就是一套天然语言的字符与二进制数之间的对应规则

字符集

  • 字符集 Charset :也叫编码表。是一个系统支持的全部字符的集合,包括各国家文字、标点符号、图形符 号、数字等。

    截图

  • 计算机要准确的存储和识别各类字符集符号,须要进行字符编码,一套字符集必然至少有一套字符编码。常见字符 集有ASCII字符集、GBK字符集、Unicode字符集等

  • 可见,当指定了编码,它所对应的字符集天然就指定了,因此编码才是咱们最终要关心的

  • ASCII字符集

    • ASCII(American Standard Code for Information Interchange,美国信息交换标准代码)是基于拉丁 字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显 示字符(英文大小写字符、阿拉伯数字和西文符号)。
    • 基本的ASCII字符集,使用7位(bits)表示一个字符,共128字符。ASCII的扩展字符集使用8位(bits) 表示一个字符,共256字符,方便支持欧洲经常使用字符。
  • ISO-8859-1字符集

    • 拉丁码表,别名Latin-1,用于显示欧洲使用的语言,包括荷兰、丹麦、德语、意大利语、西班牙语等。
    • ISO-5559-1使用单字节编码,兼容ASCII编码。
  • GBxxx字符集

    • GB就是国标的意思,是为了显示中文而设计的一套字符集。
    • GB2312:简体中文码表。一个小于127的字符的意义与原来相同。但两个大于127的字符连在一块儿时, 就表示一个汉字,这样大约能够组合了包含7000多个简体汉字,此外数学符号、罗马希腊的字母、日文 的假名们都编进去了,连在ASCII里原本就有的数字、标点、字母都通通从新编了两个字节长的编码,这 就是常说的"全角"字符,而原来在127号如下的那些就叫"半角"字符了。
    • GBK:最经常使用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了 21003个汉字,彻底兼容GB2312标准,同时支持繁体汉字以及日韩汉字等。
    • GB18030:最新的中文码表。收录汉字70244个,采用多字节编码,每一个字能够由1个、2个或4个字节 组成。支持中国国内少数民族的文字,同时支持繁体汉字以及日韩汉字等。
  • Unicode字符集 :

    • Unicode编码系统为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国 码。
    • 它最多使用4个字节的数字来表达每一个字母、符号,或者文字。有三种编码方案,UTF-八、UTF-16和UTF32。最为经常使用的UTF-8编码。
    • UTF-8编码,能够用来表示Unicode标准中任何字符,它是电子邮件、网页及其余存储或传送文字的应用 中,优先采用的编码。互联网工程工做小组(IETF)要求全部互联网协议都必须支持UTF-8编码。因此, 咱们开发Web应用,也要使用UTF-8编码。它使用一至四个字节为每一个字符编码,编码规则:
      1. 128个US-ASCII字符,只需一个字节编码。
      2. 拉丁文等字符,须要二个字节编码。
      3. 大部分经常使用字(含中文),使用三个字节编码。
        1. 其余极少使用的Unicode辅助字符,使用四字节编码

编码引出的问题

  • 在IDEA中,使用 FileReader 读取项目中的文本文件。因为IDEA的设置,都是默认的 UTF-8 编码,因此没有任何问题。可是,当读取Windows系统中建立的文本文件时,因为Windows系统的默认是GBK编码,就会出现乱码。

    public class ReaderDemo {     
        public static void main(String[] args) throws IOException { 
            FileReader fileReader = new FileReader("E:\\File_GBK.txt");
            int read;
            while ((read = fileReader.read()) != ‐1) { 
                System.out.print((char)read); 
            }         
            fileReader.close();   
        } 
    } 
    /*
    输出结果: 
    	���
    */

InputStreamReader类

  • 转换流 java.io.InputStreamReader ,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定 的字符集将其解码为字符。它的字符集能够由名称指定,也能够接受平台的默认字符集。

构造方法

  • InputStreamReader(InputStream in),建立一个使用默认字符集的字符流。

  • InputStreamReader(InputStream in, String charsetName),建立一个指定字符集的字符流。

    InputStreamReader isr = new InputStreamReader(new FileInputStream("in.txt")); InputStreamReader isr2 = new InputStreamReader(new FileInputStream("in.txt") , "GBK");
  • 指定编码读取

    public class ReaderDemo2 {    
        public static void main(String[] args) throws IOException {    
            // 定义文件路径,文件为gbk编码           
            String FileName = "E:\\file_gbk.txt";      
            // 建立流对象,默认UTF8编码           
            InputStreamReader isr = new InputStreamReader(new FileInputStream(FileName));    
            // 建立流对象,指定GBK编码 
            InputStreamReader isr2 = new InputStreamReader(new FileInputStream(FileName) , "GBK"); 
            // 定义变量,保存字符       
            int read;        
            // 使用默认编码字符流读取,乱码       
            while ((read = isr.read()) != ‐1) {
                System.out.print((char)read); // ��Һ�
            }     
            isr.close();            
            // 使用指定编码字符流读取,正常解析         
            while ((read = isr2.read()) != ‐1) {     
                System.out.print((char)read);// 你们好    
            }         
            isr2.close();    
        }
    }
  • 转换流图解

    截图

  • 练习,转换文件编码

    • 指定GBK编码的转换流,读取文本文件。
    • 使用UTF-8编码的转换流,写出文本文件。
    public class TransDemo {    
        public static void main(String[] args) {       
            // 1.定义文件路径           
            String srcFile = "file_gbk.txt";      
            String destFile = "file_utf8.txt"; 
            // 2.建立流对象               
            // 2.1 转换输入流,指定GBK编码
            InputStreamReader isr = new InputStreamReader(new FileInputStream(srcFile) , "GBK");    
            // 2.2 转换输出流,默认utf8编码
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(destFile)); 
            // 3.读写数据             
            // 3.1 定义数组           
            char[] cbuf = new char[1024];      
            // 3.2 定义长度             
            int len;     
            // 3.3 循环读取      
            while ((len = isr.read(cbuf))!=‐1) {      
                // 循环写出           
                osw.write(cbuf,0,len);       
            }      
            // 4.释放资源     
            osw.close();        
            isr.close(); 
        }   
    }

序列化

概述

  • Java 提供了一种对象序列化的机制。用一个字节序列能够表示一个对象,该字节序列包含该 对象的数据 、 对象的 类型 和 对象中存储的属性 等信息。字节序列写出到文件以后,至关于文件中持久保存了一个对象的信息。

  • 反之,该字节序列还能够从文件中读取回来,重构对象,对它进行反序列化。 对象的数据 、 对象的类型 和 对象中 存储的数据 信息,均可以用来在内存中建立对象。看图理解序列化

    截图

ObjectOutputStream类

  • java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储

构造方法

  • public ObjectOutputStream(OutputStream out) ,建立一个指定OutputStream的ObjectOutputStream。

    FileOutputStream fileOut = new FileOutputStream("employee.txt"); ObjectOutputStream out = new ObjectOutputStream(fileOut);

序列化操做

  • 一个对象要想序列化,必须知足两个条件:

    • 必须实现 java.io.Serializable 接口, Serializable 是一个标记接口,不实现此接口的类将不会使任 何状态序列化或反序列化,会抛出 NotSerializableException 。
    • 该类的全部属性必须是可序列化的。若是有一个属性不须要可序列化的,则该属性必须注明是瞬态的,使用 transient 关键字修饰。
    public class Employee implements java.io.Serializable {     
        public String name;     
        public String address;    
        public transient int age; // transient瞬态修饰成员,不会被序列化
        public void addressCheck() {       
            System.out.println("Address  check : " + name + " ‐‐ " + address);   
        } 
    }
  • 写出对象方法

    • public final void writeObject (Object obj),将指定的对象写出
    public class SerializeDemo{     
        public static void main(String [] args)   { 
            Employee e = new Employee();          
            e.name = "zhangsan";          
            e.address = "beiqinglu";           
            e.age = 20;           
            try {             
                // 建立序列化流对象   
                ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.txt"));  
                // 写出对象             
                out.writeObject(e);          
                // 释放资源               
                out.close();            
                fileOut.close();         
                System.out.println("Serialized data is saved"); 
                // 姓名,地址被序列化,年龄没有被序列 化。          
            } catch(IOException i)   {         
                i.printStackTrace();       
            }    
        }  
    }
    /*
    输出结果:
    	Serialized data is saved
    */

ObjectInputStream类

  • ObjectInputStream反序列化流,将以前使用ObjectOutputStream序列化的原始数据恢复为对象。

构造方法

  • public ObjectInputStream(InputStream in) ,建立一个指定InputStream的ObjectInputStream

反序列化操做1

  • 若是能找到一个对象的class文件,咱们能够进行反序列化操做,调用 ObjectInputStream 读取对象的方法:

    • public final Object readObject (),读取一个对象
     public static void main(String [] args)   {        
         Employee e = null;         
         try {                   
             // 建立反序列化流             
             FileInputStream fileIn = new FileInputStream("employee.txt"); 
             ObjectInputStream in = new ObjectInputStream(fileIn);        
             // 读取一个对象             
             e = (Employee) in.readObject();            
             // 释放资源          
             in.close();           
             fileIn.close();        
         }catch(IOException i) {        
             // 捕获其余异常            
             i.printStackTrace();      
             return;        
         }catch(ClassNotFoundException c)  {  
             // 捕获类找不到异常              
             System.out.println("Employee class not found");   
             c.printStackTrace();          
             return;        
         }         
         // 无异常,直接打印输出        
         System.out.println("Name: " + e.name); // zhangsan     
         System.out.println("Address: " + e.address); // beiqinglu   
         System.out.println("age: " + e.age); // 0  
     }
    • 对于JVM能够反序列化对象,它必须是可以找到class文件的类。若是找不到该类的class文件,则抛出一个 ClassNotFoundException 异常

反序列化操做2

  • 另外,当JVM反序列化对象时,能找到class文件,可是class文件在序列化对象以后发生了修改,那么反序列化操 做也会失败,抛出一个 InvalidClassException 异常。发生这个异常的缘由以下

    • 该类的序列版本号与从流中读取的类描述符的版本号不匹配
    • 该类包含未知数据类型
    • 该类没有可访问的无参数构造方法
  • Serializable 接口给须要序列化的类,提供了一个序列版本号。 serialVersionUID 该版本号的目的在于验证序 列化的对象和对应类是否版本匹配

    public class Employee implements java.io.Serializable {      
        // 加入序列版本号     
        private static final long serialVersionUID = 1L;     
        public String name;    
        public String address;     
        // 添加新的属性 ,从新编译, 能够反序列化,该属性赋为默认值.  
        public int eid;      
        public void addressCheck() {       
            System.out.println("Address  check : " + name + " ‐‐ " + address);    
        } 
    }

序列化集合

  • 将存有多个自定义对象的集合序列化操做,保存到 list.txt 文件中

  • 反序列化 list.txt ,并遍历集合,打印对象信息

  • 案例分析

    • 把若干学生对象 ,保存到集合中。
    • 把集合序列化。
    • 反序列化读取时,只须要读取一次,转换为集合类型。
    • 遍历集合,能够打印全部的学生信息
    public class SerTest { 
        public static void main(String[] args) throws Exception {   
            // 建立 学生对象        
            Student student = new Student("老王", "laow");      
            Student student2 = new Student("老张", "laoz");        
            Student student3 = new Student("老李", "laol");            
            ArrayList<Student> arrayList = new ArrayList<>();        
            arrayList.add(student);          
            arrayList.add(student2);        
            arrayList.add(student3);        
            // 序列化操做         
            // serializ(arrayList);     
            // 反序列化      
            ObjectInputStream ois  = new ObjectInputStream(new FileInputStream("list.txt"));      
            // 读取对象,强转为ArrayList类型       
            ArrayList<Student> list  = (ArrayList<Student>)ois.readObject();
            for (int i = 0; i < list.size(); i++ ){          
                Student s = list.get(i);          
                System.out.println(s.getName()+"‐‐"+ s.getPwd())
              }    
        }        
        private static void serializ(ArrayList<Student> arrayList) throws Exception {
            // 建立 序列化流          
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.txt"));     
            // 写出对象         
            oos.writeObject(arrayList);    
            // 释放资源         
            oos.close();  
        }
    }

打印流

概述

  • 平时咱们在控制台打印输出,是调用 print 方法和 println 方法完成的,这两个方法都来自于 java.io.PrintStream 类,该类可以方便地打印各类数据类型的值,是一种便捷的输出方式

PrintStream类

构造方法

  • public PrintStream(String fileName),使用指定的文件名建立一个新的打印流

    PrintStream ps = new PrintStream("ps.txt");

改变打印流向

  • System.out 就是 PrintStream 类型的,只不过它的流向是系统规定的,打印在控制台上。不过,既然是流对象, 咱们就能够玩一个"小把戏",改变它的流向。

    public class PrintDemo {     
        public static void main(String[] args) throws IOException { 
            // 调用系统的打印流,控制台直接输出97          
            System.out.println(97);       
            // 建立打印流,指定文件的名称             
            PrintStream ps = new PrintStream("ps.txt");      
            // 设置系统的打印流流向,输出到ps.txt       
            System.setOut(ps);       
            // 调用系统的打印流,ps.txt中输出97       
            System.out.println(97);   
        }
    }
相关文章
相关标签/搜索