欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JavaIO流总结

程序员文章站 2022-05-31 19:13:40
1. File类的使用 java.io.File类:文件和文件目录路径的抽象表示形式,与平台无关; File能新建、删除、重命名文件和目录,但File不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流; 想要在Java程序中表示一个真实存在的文件或目录,那么必须有一个File对象 ......

1. file类的使用

  • java.io.file类:文件和文件目录路径的抽象表示形式,与平台无关;
  • file能新建、删除、重命名文件和目录,但file不能访问文件内容本身。如果需要访问文件内容本身,则需要使用输入/输出流;
  • 想要在java程序中表示一个真实存在的文件或目录,那么必须有一个file对象,但是java程序中的一个file对象,可能没有一个真实存在的文件或目录;
  • file对象可以作为参数传递给流的构造器。

1.1 file类的构造器

  • public file(string pathname)
    以pathname为路径创建file对象,可以是 绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。
  • public file(string parent,string child)
    以parent为父路径,child为子路径创建file对象;
  • public file(file parent,string child)
    根据一个父file对象和子文件路径创建file对象 。

1.2 file类的常用方法

  • file类的获取功能

    • public string getabsolutepath():获取绝对路径
    • public string getpath() :获取路径
    • public string getname() :获取名称
    • public string getparent():获取上层文件目录路径。若无,返回null
    • public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
    • public long lastmodified() :获取最后一次的修改时间,毫秒值
    • public string[] list() :获取指定目录下的所有文件或者文件目录的名称数组
    • public file[] listfiles() :获取指定目录下的所有文件或者文件目录的file数组
  • file类的重命名功能

    • public boolean renameto(file dest):把文件重命名为指定的文件路径;

      比如:file1.renameto(file2)为例:
      要想保证返回true,需要file1在硬盘中是存在的,且file2不能在硬盘中存在。

  • file 类的判断功能

    • public boolean isdirectory():判断是否是文件目录
    • public boolean isfile() :判断是否是文件
    • public boolean exists() :判断是否存在
    • public boolean canread() :判断是否可读
    • public boolean canwrite() :判断是否可写
    • public boolean ishidden() :判断是否隐藏
  • file类的创建功能

    • public boolean createnewfile() :创建文件。若文件存在,则不创建,返回false

    • public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。

    • public boolean mkdirs() :创建文件目录。如果上层文件目录不存在,一并创建

      注意事项:如果你创建文件或者 文件 目录没有 写 盘符路径 , 那么 , 默认在项目
      路径下 。

  • file 类的删除功能

    • public boolean delete():删除文件或者文件夹
      删除注意事项:
      java中的删除不走回收站。
      要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录。

1.3 应用举例:文件过滤

判断指定目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称

import org.junit.test;
import java.io.file;
import java.io.filenamefilter;
/**
 * 判断指定目录下是否有后缀名为.jpg的文件,如果有,就输出该文件名称
 */
public class findjpgfiletest {
    @test
    public void test1(){
        file srcfile = new file("d:\\code");
        
        string[] filenames = srcfile.list();
        for(string filename : filenames){
            if(filename.endswith(".jpg")){
                system.out.println(filename);
            }
        }
    }
    @test
    public void test2(){
        file srcfile = new file("d:\\code");
        
        file[] listfiles = srcfile.listfiles();
        for(file file : listfiles){
            if(file.getname().endswith(".jpg")){
                system.out.println(file.getabsolutepath());
            }
        }
    }
    /*
     * file类提供了两个文件过滤器方法
     * public string[] list(filenamefilter filter)
     * public file[] listfiles(filefilter filter)
     */
    @test
    public void test3(){
        file srcfile = new file("d:\\code");
        
        file[] subfiles = srcfile.listfiles(new filenamefilter() {
            
            @override
            public boolean accept(file dir, string name) {
                return name.endswith(".jpg");
            }
        });
        
        for(file file : subfiles){
            system.out.println(file.getabsolutepath());
        }
    }
}

1.4 应用举例:遍历指定目录

import java.io.file;
/**
 * 遍历指定目录所有文件名称,包括子文件目录中的文件。
    拓展1:并计算指定目录占用空间的大小
    拓展2:删除指定文件目录及其下的所有文件
 */
public class listfilestest {

    public static void main(string[] args) {
        // 递归:文件目录
        /** 打印出指定目录所有文件名称,包括子文件目录中的文件 */

        // 1.创建目录对象
        file dir = new file("e:\\teach\\01_javase\\java编程语言\\3_软件");

        // 2.打印目录的子文件
        printsubfile(dir);
    }

    public static void printsubfile(file dir) {
        // 打印目录的子文件
        file[] subfiles = dir.listfiles();

        for (file f : subfiles) {
            if (f.isdirectory()) {// 文件目录
                printsubfile(f);
            } else {// 文件
                system.out.println(f.getabsolutepath());
            }
        }
    }

    // 方式二:循环实现
    // 列出file目录的下级内容,仅列出一级的话
    // 使用file类的string[] list()比较简单
    public void listsubfiles(file file) {
        if (file.isdirectory()) {
            string[] all = file.list();
            for (string s : all) {
                system.out.println(s);
            }
        } else {
            system.out.println(file + "是文件!");
        }
    }

    // 列出file目录的下级,如果它的下级还是目录,接着列出下级的下级,依次类推
    // 建议使用file类的file[] listfiles()
    public void listallsubfiles(file file) {
        if (file.isfile()) {
            system.out.println(file);
        } else {
            file[] all = file.listfiles();
            // 如果all[i]是文件,直接打印
            // 如果all[i]是目录,接着再获取它的下一级
            for (file f : all) {
                listallsubfiles(f);// 递归调用:自己调用自己就叫递归
            }
        }
    }

    // 拓展1:求指定目录所在空间的大小
    // 求任意一个目录的总大小
    public long getdirectorysize(file file) {
        // file是文件,那么直接返回file.length()
        // file是目录,把它的下一级的所有大小加起来就是它的总大小
        long size = 0;
        if (file.isfile()) {
            size += file.length();
        } else {
            file[] all = file.listfiles();// 获取file的下一级
            // 累加all[i]的大小
            for (file f : all) {
                size += getdirectorysize(f);// f的大小;
            }
        }
        return size;
    }

    // 拓展2:删除指定的目录
    public void deletedirectory(file file) {
        // 如果file是文件,直接delete
        // 如果file是目录,先把它的下一级干掉,然后删除自己
        if (file.isdirectory()) {
            file[] all = file.listfiles();
            // 循环删除的是file的下一级
            for (file f : all) {// f代表file的每一个下级
                deletedirectory(f);
            }
        }
        // 删除自己
        file.delete();
    }
}

2. io流的原理及分类

2.1 java io原理

  • i/o是input/output的缩写,i/o技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等;
  • java程序中,对于数据的输入/输出操以“流(stream)”的方式进行;
  • java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输出如或输出数据。

2.2 流的分类

  • 按操作数据单元不同分为:字节流(8bit),字符流(16bit);

  • 按数据流的流向不同分为:输入流、输出流;

  • 按流的角色的不同分为:节点流,处理流;

  • java的io流共涉及40多个类,实际上非常规则,都是从如下4个基类派生的。由这四个类派生出来的子类都是以其父类名作为后缀;

    (抽象基类) 字节流 字符流
    输入流 inputstream reader
    输出流 outputstream writer

JavaIO流总结

JavaIO流总结

2.3 节点流和处理流

  • 节点流:直接从数据源或者目的地读写数据;
  • 处理流:不直接连接到数据源或者目的地,而是“连接”在已存在的流(节点流或处理流)之上,通过对数据的处理为程序提供更为强大的读写功能。

3. 节点流(或文件流)filereader和filewriter

package com.atguigu.java;

import org.junit.test;

import java.io.file;
import java.io.filereader;
import java.io.ioexception;

public class filereaderwritertest {
    public static void main(string[] args) {
        file file = new file("hello.txt");// 相较于当前功能
    }

    /*
    将day09下的hello.txt文件内容读入程序中,并输出到控制台
    说明点:
    1. read()的理解:返回读入的一个字符。如果达到文件末尾,返回-1
    2. 异常的处理:为了保证流资源一定可以执行关闭操作。需要使用try-catch-finally处理
    3. 读入的文件一定要存在,否则就会报filenotfoundexception。
    */
    @test
    public void testfilereader() {
        filereader fr = null;
        try {
            // 1. 实例化file类对象,指明要操作的文件
            file file = new file("hello.txt"); // 相较于当前module
            // 2. 提供具体的流
            fr = new filereader(file);

            // 3. 数据的读入
            //read():返回读入的一个字符。如果达到文件末尾,返回-1
            //方式一:
//        int data = fr.read();
//        while(data != -1){
//            system.out.println((char)data);
//            data = fr.read();
//        }

            // 方式二:语法上针对方式一的修改
            int data;
            while((data = fr.read()) != -1){
                system.out.println((char)data);
            }
        } catch (ioexception e) {
            e.printstacktrace();
        } finally {
            try {
                // 4. 关闭流
                if(fr != null){
                    fr.close();
                }
            } catch (ioexception e) {
                e.printstacktrace();
            }

        }
    }

    //对read()操作升级:使用read的重载方法
    @test
    public void testfilereader1() {
        filereader fr = null;
        try {
            // 1. file类的实例化
            file file = new file("hello.txt");
            // 2. filereader流的实例化
            fr = new filereader(file);

            // 3. 读入的操作
            //read(char[] cbuf):返回每次读入cbuf数组中的字符的个数。如果达到文件末尾,返回-1
            char[] cbuf = new char[5];
            int len;
            while((len = fr.read(cbuf)) != -1){
                // 方式一:
                // 错误的写法
//                for(int i = 0; i < cbuf.length; i++){
//                    system.out.print(cbuf[i]);
//                }
                // 正确的写法:
//                for(int i = 0; i < len; i++){
//                    system.out.print(cbuf[i]);
//                }
                // 方式二:
                // 错误的写法:对应方式一的错误写法
//                string str = new string(cbuf);
//                system.out.print(str);
                // 正确的写法二:
                string str= new string(cbuf, 0, len);
                system.out.print(str);
            }
        } catch (ioexception e) {
            e.printstacktrace();
        } finally {
            if(fr != null){
                // 4. 资源的关闭
                try {
                    fr.close();
                } catch (ioexception e) {
                    e.printstacktrace();
                }
            }
        }
    }
}
/*
    从内存中写出数据到硬盘的文件里。

    说明:
    1. 输出操作,对应的file可以不存在的。并不会报异常
    2.
         file对应的硬盘中的文件如果不存在,在输出的过程中,会自动创建此文件。
         file对应的硬盘中的文件如果存在:
           如果流使用的构造器是:filewriter(file,false) / filewriter(file):对原有文件的覆盖
           如果流使用的构造器是:filewriter(file,true):不会对原有文件覆盖,而是在原有文件基础上追加内容

     */
    @test
    public void testfilewriter() {
        filewriter fw = null;
        try {
            //1.提供file类的对象,指明写出到的文件
            file file = new file("hello1.txt");

            //2.提供filewriter的对象,用于数据的写出
            fw = new filewriter(file,false);

            //3.写出的操作
            fw.write("i have a dream!\n");
            fw.write("you need to have a dream!");
        } catch (ioexception e) {
            e.printstacktrace();
        } finally {
            //4.流资源的关闭
            if(fw != null){
                try {
                    fw.close();
                } catch (ioexception e) {
                    e.printstacktrace();
                }
            }
        }
    }
  • 读写
@test
public void testfilereaderfilewriter() {
    filereader fr = null;
    filewriter fw = null;
    try {
        //1.创建file类的对象,指明读入和写出的文件
        file srcfile = new file("hello.txt");
        file destfile = new file("hello2.txt");

        //不能使用字符流来处理图片等字节数据
        //            file srcfile = new file("爱情与友情.jpg");
        //            file destfile = new file("爱情与友情1.jpg");

        //2.创建输入流和输出流的对象
        fr = new filereader(srcfile);
        fw = new filewriter(destfile);


        //3.数据的读入和写出操作
        char[] cbuf = new char[5];
        int len;//记录每次读入到cbuf数组中的字符的个数
        while((len = fr.read(cbuf)) != -1){
            //每次写出len个字符
            fw.write(cbuf,0,len);
        }
    } catch (ioexception e) {
        e.printstacktrace();
    } finally {
        //4.关闭流资源
        //方式一:
        //            try {
        //                if(fw != null)
        //                    fw.close();
        //            } catch (ioexception e) {
        //                e.printstacktrace();
        //            }finally{
        //                try {
        //                    if(fr != null)
        //                        fr.close();
        //                } catch (ioexception e) {
        //                    e.printstacktrace();
        //                }
        //            }
        //方式二:
        try {
            if(fw != null)
                fw.close();
        } catch (ioexception e) {
            e.printstacktrace();
        }

        try {
            if(fr != null)
                fr.close();
        } catch (ioexception e) {
            e.printstacktrace();
        }
    }
}

4. 节点流(或文件流)fileinputstream和fileoutputstream

  • 对于文本文件(.txt,.java,.c,.cpp),使用字符流处理
  • 对于非文本文件(.jpg,.mp3,.mp4,.avi,.doc,.ppt,...),使用字节流处理
//使用字节流fileinputstream处理文本文件,可能出现乱码。
@test
public void testfileinputstream() {
    fileinputstream fis = null;
    try {
        //1. 造文件
        file file = new file("hello.txt");

        //2.造流
        fis = new fileinputstream(file);

        //3.读数据
        byte[] buffer = new byte[5];
        int len;//记录每次读取的字节的个数
        while((len = fis.read(buffer)) != -1){
            string str = new string(buffer,0,len);
            system.out.print(str);
        }
    } catch (ioexception e) {
        e.printstacktrace();
    } finally {
        if(fis != null){
            //4.关闭资源
            try {
                fis.close();
            } catch (ioexception e) {
                e.printstacktrace();
            }
        }
    }
}

/*
    实现对图片的复制操作
     */
@test
public void testfileinputoutputstream()  {
    fileinputstream fis = null;
    fileoutputstream fos = null;
    try {
        file srcfile = new file("爱情与友情.jpg");
        file destfile = new file("爱情与友情2.jpg");
        
     
        fis = new fileinputstream(srcfile);
        fos = new fileoutputstream(destfile);

        //复制的过程
        byte[] buffer = new byte[5];
        int len;
        while((len = fis.read(buffer)) != -1){
            fos.write(buffer,0,len);
        }

    } catch (ioexception e) {
        e.printstacktrace();
    } finally {
        if(fos != null){
            try {
                fos.close();
            } catch (ioexception e) {
                e.printstacktrace();
            }
        }
        if(fis != null){
            try {
                fis.close();
            } catch (ioexception e) {
                e.printstacktrace();
            }
        }
    }
}

5. 处理流之缓冲流

  • 缓冲流:
    • bufferedinputstream
    • bufferedoutputstream
    • bufferedreader
    • bufferedwriter
  • 作用;
    • 提高流的读取、写入的速度
    • 提高读写速度的原因:内部提供了一个缓存区;
  • 处理流,就是“套接”在已有的流的基础上。

5. 1 字节型缓冲流实现非文本文件的复制操作

/*
实现非文本文件的复制
*/
@test
public void bufferedstreamtest() throws filenotfoundexception {
    bufferedinputstream bis = null;
    bufferedoutputstream bos = null;

    try {
        //1.造文件
        file srcfile = new file("爱情与友情.jpg");
        file destfile = new file("爱情与友情3.jpg");
        //2.造流
        //2.1 造节点流
        fileinputstream fis = new fileinputstream((srcfile));
        fileoutputstream fos = new fileoutputstream(destfile);
        //2.2 造缓冲流
        bis = new bufferedinputstream(fis);
        bos = new bufferedoutputstream(fos);

        //3.复制的细节:读取、写入
        byte[] buffer = new byte[10];
        int len;
        while((len = bis.read(buffer)) != -1){
            bos.write(buffer,0,len);

            //                bos.flush();//刷新缓冲区

        }
    } catch (ioexception e) {
        e.printstacktrace();
    } finally {
        //4.资源关闭
        //要求:先关闭外层的流,再关闭内层的流
        if(bos != null){
            try {
                bos.close();
            } catch (ioexception e) {
                e.printstacktrace();
            }

        }
        if(bis != null){
            try {
                bis.close();
            } catch (ioexception e) {
                e.printstacktrace();
            }

        }
        //说明:关闭外层流的同时,内层流也会自动的进行关闭。关于内层流的关闭,我们可以省略.
        //        fos.close();
        //        fis.close();
    }

5. 2 字符型缓冲流实现文本文件的复制操作

使用bufferedreader和bufferedwriter实现文本文件的复制

     */
    @test
    public void testbufferedreaderbufferedwriter(){
        bufferedreader br = null;
        bufferedwriter bw = null;
        try {
            //创建文件和相应的流
            br = new bufferedreader(new filereader(new file("dbcp.txt")));
            bw = new bufferedwriter(new filewriter(new file("dbcp1.txt")));

            //读写操作
            //方式一:使用char[]数组
//            char[] cbuf = new char[1024];
//            int len;
//            while((len = br.read(cbuf)) != -1){
//                bw.write(cbuf,0,len);
//    //            bw.flush();
//            }

            //方式二:使用string
            string data;
            while((data = br.readline()) != null){
                //方法一:
//                bw.write(data + "\n");//data中不包含换行符
                //方法二:
                bw.write(data);//data中不包含换行符
                bw.newline();//提供换行的操作

            }


        } catch (ioexception e) {
            e.printstacktrace();
        } finally {
            //关闭资源
            if(bw != null){

                try {
                    bw.close();
                } catch (ioexception e) {
                    e.printstacktrace();
                }
            }
            if(br != null){
                try {
                    br.close();
                } catch (ioexception e) {
                    e.printstacktrace();
                }

            }
        }

    }

6. 处理流之转换流

  • 转换流提供了在字节流和字符流之间的转换;
  • java api提供了两个转换流
    • inputstreamreder:将inputstream转换为reader;
    • outputstreamwriter:将writer转换为outputstream;
  • 字节流中数据都是字符时,转换成字符流操作更高效;
  • 很多时候我们使用转换流来处理文件乱码问题,实现编码和解码的功能;

JavaIO流总结

  • 转换流实现文件的读入和写出
import org.junit.test;

import java.io.*;

/**
 * 处理流之二:转换流的使用
 * 1.转换流:属于字符流
 *   inputstreamreader:将一个字节的输入流转换为字符的输入流
 *   outputstreamwriter:将一个字符的输出流转换为字节的输出流
 *
 * 2.作用:提供字节流与字符流之间的转换
 *
 * 3. 解码:字节、字节数组  --->字符数组、字符串
 *    编码:字符数组、字符串 ---> 字节、字节数组
 *
 *
 * 4.字符集
 *ascii:美国标准信息交换码。
    用一个字节的7位可以表示。
 iso8859-1:拉丁码表。欧洲码表
    用一个字节的8位表示。
 gb2312:中国的中文编码表。最多两个字节编码所有字符
 gbk:中国的中文编码表升级,融合了更多的中文文字符号。最多两个字节编码
 unicode:国际标准码,融合了目前人类使用的所有字符。为每个字符分配唯一的字符码。所有的文字都用两个字节来表示。
 utf-8:变长的编码方式,可用1-4个字节来表示一个字符。

 */
public class inputstreamreadertest {

    /*
    此时处理异常的话,仍然应该使用try-catch-finally
    inputstreamreader的使用,实现字节的输入流到字符的输入流的转换
     */
    @test
    public void test1() throws ioexception {

        fileinputstream fis = new fileinputstream("dbcp.txt");
//        inputstreamreader isr = new inputstreamreader(fis);//使用系统默认的字符集
        //参数2指明了字符集,具体使用哪个字符集,取决于文件dbcp.txt保存时使用的字符集
        inputstreamreader isr = new inputstreamreader(fis,"utf-8");//使用系统默认的字符集

        char[] cbuf = new char[20];
        int len;
        while((len = isr.read(cbuf)) != -1){
            string str = new string(cbuf,0,len);
            system.out.print(str);
        }

        isr.close();

    }

    /*
    此时处理异常的话,仍然应该使用try-catch-finally

    综合使用inputstreamreader和outputstreamwriter
     */
    @test
    public void test2() throws exception {
        //1.造文件、造流
        file file1 = new file("dbcp.txt");
        file file2 = new file("dbcp_gbk.txt");

        fileinputstream fis = new fileinputstream(file1);
        fileoutputstream fos = new fileoutputstream(file2);

        inputstreamreader isr = new inputstreamreader(fis,"utf-8");
        outputstreamwriter osw = new outputstreamwriter(fos,"gbk");

        //2.读写过程
        char[] cbuf = new char[20];
        int len;
        while((len = isr.read(cbuf)) != -1){
            osw.write(cbuf,0,len);
        }

        //3.关闭资源
        isr.close();
        osw.close();
    }
}

7. 标准流、打印流、数据流

7.1 标准输入流和标准输出流

  • system.in和system.out分别代表了系统标准的输入和输出设备
  • 默认输入设备是:键盘,输出设备是:显示器
  • system.in的类型是inputstream
  • system.out的类型是printstream,其是outputstream的子类,filteroutputstream 的子类
  • 重定向:通过system类的setin,setout方法对默认设备进行改变。
    • public static void setin(inputstream in)
    • public static void setout(printstream out)
/*
    1.标准的输入、输出流
    1.1
    system.in:标准的输入流,默认从键盘输入
    system.out:标准的输出流,默认从控制台输出
    1.2
    system类的setin(inputstream is) / setout(printstream ps)方式重新指定输入和输出的流。

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

    方法一:使用scanner实现,调用next()返回一个字符串
    方法二:使用system.in实现。system.in  --->  转换流 ---> bufferedreader的readline()

     */
    public static void main(string[] args) {
        bufferedreader br = null;
        try {
            inputstreamreader isr = new inputstreamreader(system.in);
            br = new bufferedreader(isr);

            while (true) {
                system.out.println("请输入字符串:");
                string data = br.readline();
                if ("e".equalsignorecase(data) || "exit".equalsignorecase(data)) {
                    system.out.println("程序结束");
                    break;
                }

                string uppercase = data.touppercase();
                system.out.println(uppercase);

            }
        } catch (ioexception e) {
            e.printstacktrace();
        } finally {
            if (br != null) {
                try {
                    br.close();
                } catch (ioexception e) {
                    e.printstacktrace();
                }

            }
        }
    }

7.2 打印流

  • 实现将 基本数据类型的数据格式转化为 字符串输出
  • 打印流:printstream和printwriter
    • 提供了一系列重载的print()和println()方法,用于多种数据类型的输出
    • printstream和printwriter的输出不会抛出ioexception异常
    • printstream和printwriter有自动flush功能
    • printstream 打印的所有字符都使用平台的默认字符编码转换为字节。在需要写入字符而不是写入字节的情况下,应该使用 printwriter 类。
    • system.out返回的是printstream的实例
/*
    2. 打印流:printstream 和printwriter

    2.1 提供了一系列重载的print() 和 println()
    2.2 练习:



     */

    @test
    public void test2() {
        printstream ps = null;
        try {
            fileoutputstream fos = new fileoutputstream(new file("d:\\io\\text.txt"));
            // 创建打印输出流,设置为自动刷新模式(写入换行符或字节 '\n' 时都会刷新输出缓冲区)
            ps = new printstream(fos, true);
            if (ps != null) {// 把标准输出流(控制台输出)改成文件
                system.setout(ps);
            }


            for (int i = 0; i <= 255; i++) { // 输出ascii字符
                system.out.print((char) i);
                if (i % 50 == 0) { // 每50个数据一行
                    system.out.println(); // 换行
                }
            }


        } catch (filenotfoundexception e) {
            e.printstacktrace();
        } finally {
            if (ps != null) {
                ps.close();
            }
        }

    }

7.3 数据流

  • 为了方便地操作java语言的基本数据类型和string的数据,可以使用数据流。
  • 数据流有两个类:(用于读取和写出基本数据类型、string类的数据)
    • datainputstream 和 dataoutputstream
    • 在 分别“套接”在 inputstream 和 和 outputstream子类的流 上
  • datainputstream 中的方法
    • boolean readboolean() byte readbyte()
    • char readchar() float readfloat()
    • dou ble readdouble() short readshort()
    • long readlong() int readint()
    • string readutf() void readfully(byte[] b)
  • dataoutputstream 中的方法
    • 将上述的方法的read改为相应的write即可。
    /*
    3. 数据流
    3.1 datainputstream 和 dataoutputstream
    3.2 作用:用于读取或写出基本数据类型的变量或字符串
    练习:将内存中的字符串、基本数据类型的变量写出到文件中。
    注意:处理异常的话,仍然应该使用try-catch-finally.
     */
    @test
    public void test3() throws ioexception {
        dataoutputstream dos = new dataoutputstream(new fileoutputstream("data.txt"));
        dos.writeutf("刘建辰");
        dos.flush();//刷新操作,将内存中的数据写入文件
        dos.writeint(23);
        dos.flush();
        dos.writeboolean(true);
        dos.flush();
        dos.close();
    }
    /*
    将文件中存储的基本数据类型变量和字符串读取到内存中,保存在变量中。

    注意点:读取不同类型的数据的顺序要与当初写入文件时,保存的数据的顺序一致!

     */
    @test
    public void test4() throws ioexception {
        //1.
        datainputstream dis = new datainputstream(new fileinputstream("data.txt"));
        //2.
        string name = dis.readutf();
        int age = dis.readint();
        boolean ismale = dis.readboolean();

        system.out.println("name = " + name);
        system.out.println("age = " + age);
        system.out.println("ismale = " + ismale);

        //3.
        dis.close();

    }

8. 对象流

8.1 对象流

  • objectinputstream 和ojbectoutputsteam
    • 用于存储和读取 基本数据类型数据或 对象的处理流。它的强大之处就是可以把java中的对象写入到数据源中,也能把对象从数据源中还原回来;
  • 序列化:用objectoutputstream类 保存基本类型数据或对象的机制;
  • 反序列化:用objectinputstream类 读取基本类型数据或对象的机制;
  • objectoutputstream和objectinputstream不能序列化static和transient修饰的成员变量。
import java.io.*;
/**
 * 对象流的使用
 * 1.objectinputstream 和 objectoutputstream
 * 2.作用:用于存储和读取基本数据类型数据或对象的处理流。它的强大之处就是可以把java中的对象写入到数据源中,也能把对象从数据源中还原回来。

 */
public class objectinputoutputstreamtest {

    /*
    序列化过程:将内存中的java对象保存到磁盘中或通过网络传输出去
    使用objectoutputstream实现
     */
    @test
    public void testobjectoutputstream(){
        objectoutputstream oos = null;

        try {
            oos = new objectoutputstream(new fileoutputstream("object.dat"));
            oos.writeobject(new string("我爱北京*"));
            oos.flush();//刷新操作
            oos.writeobject(new person("王铭",23));
            oos.flush();
            oos.writeobject(new person("*",23,1001,new account(5000)));
            oos.flush();

        } catch (ioexception e) {
            e.printstacktrace();
        } finally {
            if(oos != null){
                try {
                    oos.close();
                } catch (ioexception e) {
                    e.printstacktrace();
                }
            }
        }
    }

    /*
    反序列化:将磁盘文件中的对象还原为内存中的一个java对象
    使用objectinputstream来实现
     */
    @test
    public void testobjectinputstream(){
        objectinputstream ois = null;
        try {
            ois = new objectinputstream(new fileinputstream("object.dat"));

            object obj = ois.readobject();
            string str = (string) obj;

            person p = (person) ois.readobject();
            person p1 = (person) ois.readobject();

            system.out.println(str);
            system.out.println(p);
            system.out.println(p1);

        } catch (ioexception e) {
            e.printstacktrace();
        } catch (classnotfoundexception e) {
            e.printstacktrace();
        } finally {
            if(ois != null){
                try {
                    ois.close();
                } catch (ioexception e) {
                    e.printstacktrace();
                }
            }
        }
    }
}

person需要满足如下的要求,方可序列化:

1.需要实现接口:serializable

2.当前类提供一个全局常量:serialversionuid

3.除了当前person类需要实现serializable接口之外,还必须保证其内部所有属性也必须是可序列化的。(默认情况下,基本数据类型可序列化)。

8.2 对象的序列化

  • 对象序列化机制允许把内存中的java对象转换成平台无关的二进制流,从而允许把这种二进制流持久地保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。//当其它程序获取了这种二进制流,就可以恢复成原
    来的java对象;
  • 序列化的好处在于可将任何实现了serializable接口的对象转化为 字节数据,使其在保存和传输时可被还原;
  • 序列化是 rmi(remote method invoke – 远程方法调用)过程的参数和返回值都必须实现的机制,而 rmi 是 javaee 的基础。因此序列化机制是javaee 平台的基础;
  • 如果需要让某个对象支持序列化机制,则必须让对象所属的类及其属性是可序列化的,为了让某个类是可序列化的,该类必须实现如下两个接口之一。否则,会抛出notserializableexception异常
    • serializable
    • externalizable
  • 凡是实现serializable接口的类都有一个表示序列化版本标识符的静态变量:
    • private static final long serialversionuid;
    • serialversionuid用来表明类的不同版本间的兼容性。 简言之,其目的是以序列化对象进行版本控制,有关各版本反序列化时是否兼容。
    • 如果类没有显示定义这个静态常量,它的值是java运行时环境根据类的内部细节自动生成的。若类的实例变量做了修改,serialversionuid 可能发生变化。故建议,显式声明。
  • 简单来说,java的序列化机制是通过在运行时判断类的serialversionuid来验证版本一致性的。在进行反序列化时,jvm会把传来的字节流中的serialversionuid与本地相应实体类的serialversionuid进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。(invalidcastex ception)

8.3 面试题

谈谈你对java.io.serializable 接口的理解,我们知道它用于序列化,是空方法接口,还有其它认识吗?

实现了serializable 接口的对象,可将它们转换成一系列字节,并可在以后完全恢复回原来的样子。 这一过程亦可通过网络进行。这意味着序列化机制能自动补偿操作系统间的差异。在 换句话说,可以先在windows 机器上创建一个对象,对其序列化,然后通过网络发给一台unix 机器,然后在那里准确无误地重新“装配”。不必关心数据在不同机器上如何表示,也不必关心字节的顺序或者其他任何细节。
由于大部分作为参数的类如string 、integer 等都实现了java.io.serializable 的接口,也可以利用多态的性质,作为参数使接口更灵活。


9. 随机存取文件流randomaccessfile 类

  • randomaccessfile 声明在java.io包下,但直接继承于java.lang.object类。并且它实现了datainput、dataoutput这两个接口,也就意味着这个类既可以读也可以写。
  • randomaccessfile 类支持 “随机访问” 的方式,程序可以直接跳到文件的任意地方来 读、写文件
    • 支持只访问文件的部分内容
    • 可以向已存在的文件后追加内容
  • randomaccessfile 对象包含一个记录指针,用以标示当前读写处的位置。randomaccessfile 类对象可以*移动记录指针:
    • long getfilepointer():获取文件记录指针的当前位置
    • void seek(long pos):将文件记录指针定位到 pos 位置
  • 构造器
    • public randomaccessfile(file file, string mode)
    • public randomaccessfile(string name, string mode)
  • 创建 randomaccessfile 类实例需要指定一个 mode 参数,该参数指定 randomaccessfile 的访问模式:
    • r: 以只读方式打开
    • rw :打开以便读取和写入
    • rwd: 打开以便读取和 写入;同步文件内容的更新
    • rws: 打开以便读取和 写入; 同步文件内容和元数据 的 更新
  • 如果模式为只读r。则不会创建文件,而是会去读取一个已经存在的文件,如果读取的文件不存在则会出现异常。 如果模式为rw读写。如果文件不存在则会去创建文件,如果存在则不会创建。
import org.junit.test;
import java.io.file;
import java.io.ioexception;
import java.io.randomaccessfile;

/**
 * randomaccessfile的使用
 * 1.randomaccessfile直接继承于java.lang.object类,实现了datainput和dataoutput接口
 * 2.randomaccessfile既可以作为一个输入流,又可以作为一个输出流
 *
 * 3.如果randomaccessfile作为输出流时,写出到的文件如果不存在,则在执行过程中自动创建。
 *   如果写出到的文件存在,则会对原有文件内容进行覆盖。(默认情况下,从头覆盖)
 *
 * 4. 可以通过相关的操作,实现randomaccessfile“插入”数据的效果
 */
public class randomaccessfiletest {

    @test
    public void test1() {

        randomaccessfile raf1 = null;
        randomaccessfile raf2 = null;
        try {
            //1.
            raf1 = new randomaccessfile(new file("爱情与友情.jpg"),"r");
            raf2 = new randomaccessfile(new file("爱情与友情1.jpg"),"rw");
            //2.
            byte[] buffer = new byte[1024];
            int len;
            while((len = raf1.read(buffer)) != -1){
                raf2.write(buffer,0,len);
            }
        } catch (ioexception e) {
            e.printstacktrace();
        } finally {
            //3.
            if(raf1 != null){
                try {
                    raf1.close();
                } catch (ioexception e) {
                    e.printstacktrace();
                }

            }
            if(raf2 != null){
                try {
                    raf2.close();
                } catch (ioexception e) {
                    e.printstacktrace();
                }
            }
        }
    }

    @test
    public void test2() throws ioexception {
        randomaccessfile raf1 = new randomaccessfile("hello.txt","rw");

        raf1.seek(3);//将指针调到角标为3的位置
        raf1.write("xyz".getbytes());//

        raf1.close();

    }
    /*
    使用randomaccessfile实现数据的插入效果
     */
    @test
    public void test3() throws ioexception {

        randomaccessfile raf1 = new randomaccessfile("hello.txt","rw");

        raf1.seek(3);//将指针调到角标为3的位置
        //保存指针3后面的所有数据到stringbuilder中
        stringbuilder builder = new stringbuilder((int) new file("hello.txt").length());
        byte[] buffer = new byte[20];
        int len;
        while((len = raf1.read(buffer)) != -1){
            builder.append(new string(buffer,0,len)) ;
        }
        //调回指针,写入“xyz”
        raf1.seek(3);
        raf1.write("xyz".getbytes());

        //将stringbuilder中的数据写入到文件中
        raf1.write(builder.tostring().getbytes());

        raf1.close();
    }
}