【Java IO流知识总结】

Java IO(Input&Output)

java.io包

核心组成:一个类(File)、一个接口(Serializable)、四个抽象类(InputStream/OutputStream、Reader/Writer)

【Java IO流知识总结】_第1张图片

InputStream

  • 此抽象类是表示字节输入流的所有类的父类。InputSteam是一个抽象类,它不可以实例化。数据的读取需要由它的子类来实现。根据节点的不同,它派生了不同的节点流子类。
  • 继承自InputSteam的流都是用于向程序中输入数据,且数据的单位为字节(8 bit)。

常用方法:

  • int read():读取一个字节的数据,并将字节的值作为int类型返回(0-255之间的一个值)。如果未读出字节则返回-1(返回值为-1表示读取结束)。
  • void close():关闭输入流对象,释放相关系统资源。

OutputStream

  • 此抽象类是表示字节输出流的所有类的父类。输出流接收输出字节并将这些字节发送到某个目的地。

常用方法:

  • void write(int n):向目的地中写入一个字节。
  • void close():关闭输出流对象,释放相关系统资源。

**Reader **

  • Reader 用于读取的字符流抽象类,数据单位为字符。
  • int read():读取一个字符的数据,并将字符的值作为int类型返回(0-65535 之间的一个值,即Unicode值)。如果未读出字符则返回-1(返回值为-1表示读取结束)。
  • void close():关闭流对象,释放相关系统资源。

**Writer **

  • Writer用于输出的字符流抽象类,数据单位为字符。
  • void write(int n):向输出流中写入一个字符。
  • void close():关闭输出流对象,释放相关系统资源。

1 File类

(1)File类的基本概念

File类:表示文件和目录路径名的抽象表示形式。
File类可以实现文件的创建、删除、重命名、得到路径、创建时间等等,是唯一与文件本身有关的操作类。

操作方法:

变量和类型 方法 描述
boolean canExecute() 测试应用程序是否可以执行此抽象路径名表示的文件。
boolean canRead() 测试应用程序是否可以读取此抽象路径名表示的文件。
boolean canWrite() 测试应用程序是否可以修改此抽象路径名表示的文件。
int compareTo(File pathname) 按字典顺序比较两个抽象路径名。
boolean createNewFile() 当且仅当具有此名称的文件尚不存在时,以原子方式创建由此抽象路径名命名的新空文件。
static File createTempFile(String prefix, String suffix) 在默认临时文件目录中创建一个空文件,使用给定的前缀和后缀生成其名称。
static File createTempFile(String prefix, String suffix, File directory) 在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。
boolean delete() 删除此抽象路径名表示的文件或目录。
void deleteOnExit() 请求在虚拟机终止时删除此抽象路径名表示的文件或目录。
boolean equals(Object obj) 测试此抽象路径名与给定对象的相等性。
boolean exists() 测试此抽象路径名表示的文件或目录是否存在。
File getAbsoluteFile() 返回此抽象路径名的绝对形式。
String getAbsolutePath() 返回此抽象路径名的绝对路径名字符串。
File getCanonicalFile() 返回此抽象路径名的规范形式。
String getCanonicalPath() 返回此抽象路径名的规范路径名字符串。
long getFreeSpace() 通过此抽象路径名返回分区 named中未分配的字节数。
String getName() 返回此抽象路径名表示的文件或目录的名称。
String getParent() 返回此抽象路径名父项的路径名字符串,如果此路径名未指定父目录,则返回 null
File getParentFile() 返回此抽象路径名父项的抽象路径名,如果此路径名未指定父目录,则返回 null
String getPath() 将此抽象路径名转换为路径名字符串。
long getTotalSpace() 通过此抽象路径名返回分区 named的大小。
long getUsableSpace() 通过此抽象路径名返回分区 named上此虚拟机可用的字节数。
int hashCode() 计算此抽象路径名的哈希码。
boolean isAbsolute() 测试此抽象路径名是否为绝对路径。
boolean isDirectory() 测试此抽象路径名表示的文件是否为目录。
boolean isFile() 测试此抽象路径名表示的文件是否为普通文件。
boolean isHidden() 测试此抽象路径名指定的文件是否为隐藏文件。
long lastModified() 返回上次修改此抽象路径名表示的文件的时间。
long length() 返回此抽象路径名表示的文件的长度。
String[] list() 返回一个字符串数组,用于命名此抽象路径名表示的目录中的文件和目录。
String[] list(FilenameFilter filter) 返回一个字符串数组,用于命名由此抽象路径名表示的目录中的文件和目录,以满足指定的过滤器。
File[] listFiles() 返回一个抽象路径名数组,表示此抽象路径名表示的目录中的文件。
File[] listFiles(FileFilter filter) 返回一个抽象路径名数组,表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。
File[] listFiles(FilenameFilter filter) 返回一个抽象路径名数组,表示此抽象路径名表示的目录中满足指定过滤器的文件和目录。
static File[] listRoots() 列出可用的文件系统根目录。
boolean mkdir() 创建此抽象路径名指定的目录。
boolean mkdirs() 创建此抽象路径名指定的目录,包括任何必需但不存在的父目录。
boolean renameTo(File dest) 重命名此抽象路径名表示的文件。
boolean setExecutable(boolean executable) 一种方便的方法,用于设置此抽象路径名的所有者执行权限。
boolean setExecutable(boolean executable, boolean ownerOnly) 设置此抽象路径名的所有者或每个人的执行权限。
boolean setLastModified(long time) 设置此抽象路径名指定的文件或目录的上次修改时间。
boolean setReadable(boolean readable) 一种方便的方法,用于设置此抽象路径名的所有者读取权限。

(2)实例操作:

package com.huang.TestIO.Demo01;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;

/**
 * File类的使用
 *
 */
public class Demo01 {
    public static void main(String[] args) {
        //File类表示一个文件或目录
        //"H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt"
        //"H:/JAVA/2.Java常用类、集合、IO/IO/word1.txt"
        /*"H:"+File.separator+"JAVA"+File.separator+"2.Java常用类、集合、IO"
                       + File.separator+"IO"+File.separator+"word2.txt"*/

        File f1 = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
        File f2 = new File("H:/JAVA/2.Java常用类、集合、IO/IO/my");
        File f3 = new File("H:"+File.separator+"JAVA"+File.separator+"2.Java常用类、集合、IO"
                + File.separator+"IO"+File.separator+"word2.txt");
        if(!f1.exists()){//判断文件是否存在,异常抛出 -->IOException
            try {
                f1.createNewFile();
                System.out.println("文件创建成功!");
            } catch (IOException e) {
                e.printStackTrace();

            }
        }

        System.out.println("是否为文件夹:"+f1.isDirectory());
        System.out.println("是否为文件:"+f1.isFile());

        boolean b = f2.delete();//当为空文件夹时才能删除,否则不删除
        System.out.println("是否删除:"+b);

        String[] names = f2.list();//列出当前目录下的所有文件
        System.out.println(Arrays.toString(names));

        File[] fs = f2.listFiles();//列出当前目录下所有文件,以File对象返回
        for(File f:fs){
            System.out.println("length = "+f.length());
            System.out.println("name = "+f.getName());
            System.out.println("相对路径 = "+f.getPath());
            System.out.println("绝对路径 = "+f.getAbsolutePath());
            System.out.println("是否为隐藏文件 = "+f.isHidden());

            Date date = new Date(f.lastModified());
            DateFormat df = new SimpleDateFormat("YYYY/MM HH:mm:ss");
            System.out.println("文件最后修改的时间 = "+df.format(date));
            System.out.println("文件最后修改的时间 = "+f.lastModified());


            System.out.println("------------------");
        }
        File f4 = new File("test.txt");
        System.out.println(f4.getPath());//相对路径
        System.out.println(f4.getAbsolutePath());//绝对路径

        //创建文件夹
        File f5 = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my1");
        f5.mkdir();//单级目录
        //f5.mkdirs();//若创建的目录,多级目录不存在时使用
        //重命名与移动文件
        //f5.renameTo(new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my2"));
        //f5.renameTo(new File("H:\\JAVA\\2.Java常用类、集合、IO\\my1"));

        System.out.println("-----------------");
        //过滤文件
        File f6 = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my");
        //匿名函数形式
/*        File[] files = f6.listFiles(new FileFilter() {
            @Override
            public boolean accept(File pathname) {
                return pathname.getName().endsWith(".txt");
            }
        });*/
        //Lambda表达式形式
        File[] files = f6.listFiles((pathname)->pathname.getName().endsWith(".txt"));

        for(File f:files){
            System.out.println(f.getName());
        }

    }
}
package com.huang.TestIO.Demo01;

import java.io.File;

/**
 * 指定目录中查找文件
 */
public class Demo02 {
    public static void main(String[] args) {
        findFile(new File("H:\\JAVA\\"),".txt");
    }
    //查找文件方法
    private static void findFile(File target,String ext){
        if(target == null)return;
        if(target.isDirectory()){//如果文件是目录
            File[] files = target.listFiles();
            if(files!=null){
                for(File f:files){
                    findFile(f,ext);//递归调用
                }
            }
        } else{//此处表示File时一个文件
            String name = target.getName().toLowerCase();
//            System.out.println(name);
            if(name.endsWith(ext)){
                System.out.println(target.getAbsolutePath());
            }
        }
    }
}

2 字节输出输入流

IO流:输入输出流(Input/Output)

流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直接的进行数据操作。

IO流分类:

  • 根据数据处理类型:字符流和字节流
  • 根据数据流向:输入流和输出流,InputStream&OutputStream

字节是数据传输的基本单位,文件内容也是以字节为单位存储的;

从文件中把数据读到程序使用:输入流,从程序把数据写到文件使用:输出流

InputStream&OutputStream

字节输出流:OutputStream抽象类是表示输出字节流的所有类的超类。输出流接受输出字节并将这些字节发送到InputStream类某个接收器要向文件中输出,使用FileOutputStream类

字节输入流:InputStream抽象类是表示输入字节流的所有类的超类。FileInputStream从文件系统中的某个文件中获得输入字节。

package com.huang.TestIO.Demo02;

import java.io.*;

/**
 * 字节输出输入流
 * 输出流:超类OutputStream,对文件的输出流使用子类FileOutputStream
 * 输入流:超类InputStream,对文件输入流使用子类FileInputStream
 */
public class Demo01 {
    public static void main(String[] args) {
        out();
        in();
    }
    private static void out(){
        //0、确定目标文件
        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
        //1、构建一个文件输出流对象
        try {
            OutputStream out = new FileOutputStream(file,true);//append 为true 表示追加内容
            //2、输出的内容
//            String line = System.getProperty("line.separator");//获取换行符
//            String str = "温柔的故乡"+line;
            String str = "温柔的故乡\r\n";//\r\n表示换行

            //3、把内容写入到文件
            out.write(str.getBytes());
            //4、关闭流
            out.close();
            System.out.println("write success!");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void in(){
        //0、确定目标文件
        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
        //1、构建一个文件输入流对象
        try {
            InputStream in = new FileInputStream(file);
            byte[] bytes = new byte[1024];
            StringBuilder buf = new StringBuilder();
            int len = -1;//表示每次读取的字节长度
            //把数据读入到数组当中,并返回读取的字节数,当不等于-1时,表示读取到数据;等于-1,表示文件已经读取完毕。
            while((len = in.read(bytes)) != -1){
                buf.append(new String(bytes));//根据读取到的字节数组,转换为字符串内容,添加到StringBilder中

            }
            //打印内容
            System.out.println(buf);
            //关闭输入流
            in.close();


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

输入输出字节流操作原理:每次只会操作一个字节(从文件中读取或写入)

3 字符流

用于解决多次提取字节,而取到一半字节译不出来的问题。

Writer

写入字符流的抽象类。子类必须实现的方法仅有write(char[],int,int)、f lush()和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
与OutputStream一样,对文件的操作使用:FileWriter类完成。

Reader

用于读取字符流的抽象类。子类必须实现的方法只有read(char[],int,int)和 close()。但是,多数子类将重写此处定义的一些方法,以提供更高的效率和/或其他功能。
使用FileReader类进行实例化操作。

文件字符操作流会自带缓存,默认大小为1024字节,在缓存满后、或手动刷新缓存、或关闭流时会把数据写入文件;

字节操作流,默认每次执行写入操作会直接把数据写入文件。

字节流与字符流的区别:(字符流的内部实现还是字节流)

  • 在所有的流操作里。字节永远是最基础的。任何基于字节的操作都是正确的。
  • 无论你是文本文件还是二进制的文件。如果确认流里面只有可打印的字符,包括英文的和各种国家的文字,也包括中文,那么可以考虑用字符流。由于编码不同,多字节的字符可能占用多个字节。比如GBK的汉字就占用2个字节,而UTF-8的汉字就占用3个字节。所以,字符添是根据指定的编码,将1个或多个字节转化为java里面的unicode的字符,然后进行操作。
  • 字符操作一般使用Writer,Reader等,
  • 字节操作一般都是InputStream,OutputStream以及各种包装类,比如BufferedlnputStream和BufferedOutputStream等。
  • 总结:如果你确认你要处理的流是可打印的字符,那么使用字符流会看上去简单一点。如果不确认,那么用字节流总是不会错的。

如何选择字节流/字符流:

一般操作非文本文件时,使用字节流;操作文本文件时,建议使用字符流。

package com.huang.TestIO.Demo03;

import java.io.*;

/**
 * 字符流
 * 字符输出流:Writer,对文件的操作使用类:FileWriter
 * 字符输入流:Reader,对文件的操作使用类:FileReader
 * 每次操作的单位时一个字符
 */
public class Demo01 {
    public static void main(String[] args) {
//        out();
        in();
    }
    private static void out(){
        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
        try {
            Writer out = new FileWriter(file,true);
            out.write("良辰美景奈何天");

            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    private static void in(){
        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
        try {
            Reader in = new FileReader(file);
            char[] cs = new char[1];
            int len = -1;
            StringBuilder buf = new StringBuilder();
            while((len = in.read(cs)) != -1){
                buf.append(new String(cs,0,len));
            }
            in.close();
            System.out.println(buf);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

实例:文件的复制

package com.huang.TestIO.Demo03;

import java.io.*;

/**
 * 文件复制
 * 从一个输入流中读取数据,然后通过输出流写入目标文件
 * 一边读一边写
 */
public class Demo02 {
    public static void main(String[] args) {
        System.out.println("start cpoy...");
        copy("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt","H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\word.txt");
        System.out.println("copy success!");
    }
    private static void copy(String src,String target){
        File srcFile = new File(src);
        File targetFile = new File(target);
        InputStream in = null;
        OutputStream out = null;

        try {
            in = new FileInputStream(srcFile);
            out = new FileOutputStream(targetFile);

            byte[] bytes = new byte[1024];
            int len = -1;

            while((len=in.read(bytes)) != -1){
                out.write(bytes,0,len);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(in!=null) {
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(out!=null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

4 字节字符转换流

转换流,可以将一个字节流转换为字符流,也可以将一个字符流转换为字节流。

  • OutputStreamWriter:可以将输出的字符流转换为字节流的输出形式
  • InputStreamReader:将输入的字节流转换为字符流输入形式
package com.huang.TestIO.Demo04;

import java.io.*;
import java.nio.charset.Charset;

/**
 * 转换流
 * - OutputStreamWriter:可以将输出的字符流转换为字节流的输出形式
 * - InputStreamReader:将输入的字节流转换为字符流输入形式
 */
public class demo01 {
    public static void main(String[] args) throws IOException {
//        InputStream in = new FileInputStream("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
//        read(in);
        OutputStream out = new FileOutputStream("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt",true);
        write(out);
    }

    public static void read(InputStream in) throws IOException {
        Reader reader = new InputStreamReader(in, Charset.defaultCharset());
        char[] cs = new char[1024];
        int len = -1;
        while((len=reader.read(cs)) != -1){
            System.out.println(new String(cs,0,len));
        }
        reader.close();
    }

    public static void write(OutputStream out) throws IOException {
        Writer writer = new OutputStreamWriter(out,Charset.defaultCharset());
        writer.write("赏心悦目谁家院\r\n");
        writer.close();
    }
}

5 缓冲流

对文件或其它目标频繁的读写操作,效率低,性能差。
使用缓冲流的好处:能够更高效的读写信息]原理是将数据先缓冲起来,然后一起写入或者读取出来。

(1)字节缓冲流

  • BufferedInputStream:为另一个输入流添加一些功能,在创建BufferedInputStream时,会创建一个内部缓冲区数组,用于缓冲数据。
  • BufferedOutputStream:通过设置这种输出流,应用程序就可以将各个字节写入底层输出流中,而不必针对每次字节写入调用底层系统。
package com.huang.TestIO.Demo05;

import java.io.*;

/**
 * 缓存流
 * 缓存目的:解决在写入文件操作时,频繁操作文件所带来的性能降低的问题
 * BufferedOutputStream 内部缓存大小时8KB,每次写入时存储到缓存中的byte数组中,
 * 当数组存满时,会把数组中的数据写入到文件中,并且缓存下标归零。
 */
public class Demo01 {
    public static void main(String[] args) throws IOException {
//        byteWriter();
        byteReader2();
    }
    private static void byteWriter() throws IOException {
        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
        OutputStream out = new FileOutputStream(file,true);
        //构造一个字节缓冲流
        BufferedOutputStream bos = new BufferedOutputStream(out);
        String info = "无可奈何花落去,似曾相识燕归来。\r\n";
        bos.write(info.getBytes());
        bos.close();
//        out.close();
    }
    private static void byteReader() throws IOException {
        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
        InputStream in = new FileInputStream(file);
        //构造一个字节缓冲流
        BufferedInputStream bis = new BufferedInputStream(in);
        byte[] bytes = new byte[1024];
        int len = -1;
        while((len = bis.read(bytes)) != -1){
            System.out.println(new String(bytes,0,len));
        }
        bis.close();
    }
    private static void byteReader2(){
        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
        InputStream in = null;
        try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file))){
            byte[] bytes = new byte[1024];
            int len = -1;
            while((len = bis.read(bytes)) != -1){
                System.out.println(new String(bytes,0,len));
            }
            bis.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

(2)字符缓冲流

  • BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
  • BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
package com.huang.TestIO.Demo05;

import java.io.*;

/**
 * 字符缓存流
 *- BufferedReader:从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取。
 *- BufferedWriter:将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
 *
 * 字符流
 * 1、加入字符缓存流,增强读取功能(readLine)
 * 2、更高效的读取数据
 * FileReader:内部使用InputStreamReader(sun.nio.cs.StreamDecoder),解码过程,byte-->char,默认缓存大小时8K
 * BufferedReader:默认缓存大小时8K,但是可以手动指定缓存大小,把数据直接读取到缓存中,减少每次转换过程,效率更高
 * BufferedWriter 同上
 */
public class Demo02 {
    public static void main(String[] args) {
        
    }
    private static void charReader() throws IOException {
        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
        Reader reader = new FileReader(file);
        //为字符流提供缓冲,已达高效读取的目的
        BufferedReader br = new BufferedReader(reader);
        char[] cs = new char[1024];
        int len = -1;
        while((len = br.read(cs)) != -1){
            System.out.println(new String(cs,0,len));
        }
        br.close();//同时关闭reader

    }

    private static void charWriter() throws IOException {
        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
        Writer writer = new FileWriter(file);
        //为字符流提供缓冲,已达高效读取的目的
        BufferedWriter bw = new BufferedWriter(writer);
        bw.write("天生我才必有用!");
        bw.flush();//刷新缓存
        bw.close();//同时关闭reader

    }
}

6 打印流

打印流的主要功能是用于输出,在整个10包中打印流分为两种类型:

  • 字节打印流:PrintStream
  • 字符打印流:PrintWriter

打印流可以很方便的进行输出

package com.huang.TestIO.Demo06;

import java.io.*;

/**
 * 打印流
 * - 字节打印流:PrintStream
 * 在字节输出时,可以增强输出功能
 * - 字符打印流:PrintWriter
 */
public class Demo01 {
    public static void main(String[] args) throws IOException {
//        bytePrint();
        charPrint();
    }
    private static void bytePrint() throws FileNotFoundException {
        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
        OutputStream out = new FileOutputStream(file);
        //加缓存
        BufferedOutputStream bos = new BufferedOutputStream(out);
        //增强打印功能
        PrintStream ps = new PrintStream(bos);
        ps.println("天若有情天亦老");
        ps.close();
    }
    private static void charPrint() throws IOException {
        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.txt");
        Writer  out = new FileWriter(file);
        //加缓存
        BufferedWriter bos = new BufferedWriter(out);
        //增强打印功能
        PrintWriter pw = new PrintWriter(bos);
        pw.println("天若有情天亦老");
        pw.close();
    }
}

7 对象流与序列化

对象流的两个类:

  • ObjectOutputStream 将 Java对象的基本数据类型和图形写入OutputStream
  • ObjectlnputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。

序列化一组对象:
在序列化操作中,同时序列化多个对象时,反序列化也必须按顺序操作,如果想要序列化一组对象该如何操作呢?
序列化一组对象可采用:对象数组的形式,因为对象数组可以向Object进行转型操作。

transient关健:
如果用transient声明一个实例变量,当对象存储时,它的值不需要维持。

package com.huang.TestIO.Demo07.Demo01;

import java.io.Serializable;

//java.io.NotSerializableException,没有序列化
//如果一个类创建对象,需要被序列化,那么该类必须实现Serializable接口,
//Serializable 是一个标记接口,没有任何定义,为了告诉JVM该类对象可以被序列化

//什么时候对象需要被序列化呢?
//1、把对象保存到文件中(存储到物理介质)
//2、对象需要在网络上传输

//如果对象没有实现Serializable接口,会报错java.io.NotSerializableException
//java.io.NotSerializableException,没有序列化

public class Dog implements Serializable {
    private String name;
    private int age;
    private String sex;
    private transient int id;//在序列化中被忽略
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public Dog(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public Dog() {
        super();
    }

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}

package com.huang.TestIO.Demo07.Demo01;
/**
 * 对象流
 * - ObjectOutputStream 将 Java对象的基本数据类型和图形写入OutputStream
 * - ObjectlnputStream 对以前使用 ObjectOutputStream 写入的基本数据和对象进行反序列化。
 *
 *
 */

import java.io.*;

public class Test {

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        writerObjects();
        readObject();
    }

    /**
     *反序列化的过程
     * 从文件中把对象的内容读取出来,还原成对象
     */
    private static void readObject() throws IOException, ClassNotFoundException {
        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\dog.txt");
        InputStream in = new FileInputStream(file);
        ObjectInputStream ois = new ObjectInputStream(in);
        Dog[] dog = (Dog[])ois.readObject();
        ois.close();
        System.out.println(dog[0]);
        System.out.println(dog[1]);
    }

    /**
     * 对象序列化
     * 把对象写入文件:实际写入的是类名、属性名、属性类型、属性的值等
     */
    private static void writerObject() throws IOException {
        Dog dog = new Dog("豆豆",1,"母");
        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\dog.txt");
        OutputStream out = new FileOutputStream(file);
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(dog);
        oos.close();
    }
    /**
     * 对象序列化
     * 把对象写入文件:实际写入的是类名、属性名、属性类型、属性的值等
     */
    private static void writerObjects() throws IOException {
        Dog dog = new Dog("豆豆",1,"母");
        Dog dog1 = new Dog("二哈",2,"公");
        Dog[] dogs = {dog,dog1};
        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\dog.txt");
        OutputStream out = new FileOutputStream(file);
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(dogs);
        oos.close();
    }
}

8 字节数组流

ByteArraylnputStream
包含一个内部缓冲区,该缓冲区包含从流中读取的字节。内部计数器跟踪read方法要提供的下一个字节。
关闭ByteArraylnputStream无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException。

ByteArrayOutputStream
此类实现了一个输出流,其中的数据被写入一个byte数组。缓冲区会随着数据的不断写入而自动增长。
可使用toByteArray()和 toString()获取数据。
关ByteArrayOutputStream无效。此类中的方法在关闭此流后仍可被调用,而不会产生任何IOException

ByteArraylnputStream 可以将字节数组转化为输入流;
ByteArrayOutputStream可以捕获内存缓冲区的数据,转换成字节数组。

package com.huang.TestIO.Demo08;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

/**
 * 字节数组流:
 *基于内存操作,内部维护着宇哥字节数组,可以利用流的读取机制来处理字符串
 * 无需关闭
 */
public class Demo01 {
    public static void main(String[] args) {
        byteArray();
    }
    private static void byteArray(){
        String str = "234csdfwt5efHDr4&^&*(90c";
        ByteArrayInputStream bais = new ByteArrayInputStream(str.getBytes());
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int current = -1;//每次读取字节
        while((current = bais.read()) != -1){
            if((current>=65 && current<=90) ||(current>=97 && current<=122)){
                baos.write(current);
            }
        }
        //此时无需关闭,原因:字节数组流是基于内存的操作流
        System.out.println(baos.toString());
    }
}

9 数据流

DatalnputStream:

  • 数据输入流允许应用程序以与机器无关方式从底层输入流中读取基本Java数据类型。
  • 应用程序可以使用数据输出流害入稍后由数据输入流读取的数据。
  • DatalnputStream对于多线程访问不一定是安全的。线程安全是可选的,它由此类方法的使用者负责。

DataOutputStream:

  • 数据输出流允许应用程序以适当方式将基本Java数据类型写入输出流中。
  • 然后,应用程序可以使用数据输入流将数据读入。
package com.huang.TestIO.Demo09;

import java.io.*;

/**
 * 数据流
 * 与机器无关的操作Java的基本数据类型
 */
public class Demo01 {

    public static void main(String[] args) throws IOException {
//        Write();
        Read();
    }
    private static void Write() throws IOException {
        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.dat");
        OutputStream out = new FileOutputStream(file);
        BufferedOutputStream bos = new BufferedOutputStream(out);
        DataOutputStream dos = new DataOutputStream(bos);
        dos.writeInt(10);//写入10个字节
        dos.writeByte(1);
        dos.writeUTF("中");

        dos.close();
    }

    private static void Read() throws IOException {
        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\word.dat");
        InputStream in = new FileInputStream(file);
        BufferedInputStream bis = new BufferedInputStream(in);
        DataInputStream dis = new DataInputStream(bis);
        int num = dis.readInt();
        byte b = dis.readByte();
        String s = dis.readUTF();
        System.out.println(num+","+b+","+s);

        dis.close();
    }


}
  • 案例:实现文件分割合并。
package com.huang.TestIO.Demo09;

import java.io.*;
import java.util.Enumeration;
import java.util.Vector;

/**
 * 文件分割与合并
 */
public class Demo02 {
    public static void main(String[] args) throws IOException {
//        File file = new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\图片.png");
//        disvision(file,1024*200);
        
        InputStream in1 = new FileInputStream(new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\1-temp-图片.png"));
        InputStream in2 = new FileInputStream(new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\2-temp-图片.png"));
        InputStream in3 = new FileInputStream(new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\3-temp-图片.png"));
        InputStream in4 = new FileInputStream(new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\4-temp-图片.png"));
        InputStream in5 = new FileInputStream(new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\5-temp-图片.png"));

        //集合工具类,内部实现使用了数组
        Vector<InputStream> v = new Vector<>();
        v.add(in1);
        v.add(in2);
        v.add(in3);
        v.add(in4);
        v.add(in5);

        Enumeration<InputStream> es = v.elements();
        merge(es);
    }
    /**
     * 文件分割
     * targetFile:要分割的目标文件
     * cutSize:每个文件大小
     */
    private static void disvision(File targetFile, long cutSize) throws IOException {
        if(targetFile == null)return;
        //计算总分割的文件数
        int num = targetFile.length()%cutSize==0?
                (int)(targetFile.length()/cutSize):(int)(targetFile.length()/cutSize + 1);

        //构造一个文件输入流
        BufferedInputStream in = new BufferedInputStream(new FileInputStream(targetFile));
        BufferedOutputStream out = null;//每次要读取的字节数
        byte[] bytes = null;//每次要读取的字节数
        int len = -1;//每次实际读取的长度
        int count = 0;//每个文件要读取的次数

        //循环次数为生成文件的个数
        for(int i =0;i<num;i++){
            out = new BufferedOutputStream(
                    new FileOutputStream(new File("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\"+(i+1)+"-temp-"+targetFile.getName()))
            );
            if(cutSize<=1024){
                bytes = new byte[(int)cutSize];
                count = 1;
            }else{
                bytes = new byte[1024];
                count = (int)cutSize/1024;
            }
            while(count>0 && (len =in.read(bytes))!=-1){
                out.write(bytes,0,len);
                out.flush();
                count--;
            }
            //计算每个文件大小除以 1024Byte = 1KB 的余数,决定是否在读取一次
            if(cutSize%1024!=0){
                bytes = new byte[(int)cutSize%1024];
                len = in.read(bytes);
                out.write(bytes,0,len);
                out.flush();
            }
            out.close();
        }
        in.close();
    }

    /**
     * 文件合并
     */
    private static void merge(Enumeration<InputStream> es) throws IOException {
        //构造一个合并流
        SequenceInputStream sis = new SequenceInputStream(es);
        BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\-合并-图片.png")
        );
        byte[] bytes = new byte[1024];
        int len = -1;
        while((len = sis.read(bytes)) != -1){
            bos.write(bytes,0,len);
            bos.flush();
        }
        bos.close();
        sis.close();
        System.out.println("合并完成!");
    }
}

10 合并流、字符串流、管道流

  1. 合并流
    **SequencelnputStream **表示其他输入流的逻辑串联。

    它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。

  2. 字符串流

    • StringReader其源为一个字符串的字符流。
    • StringWriter一个字符流,可以用其回收在字符串缓冲区中的输出来构造字符串。关闭StringWriter无效。此类中的方法在关闭该流后仍可被调用,而不会产生任何IOException。
  3. 管道流
    管道输入流应该连接到管道输出流,管道输入流提供要写入管道输出流的所有数据字节。

    通常,数据由某个线程从PipedlnputStream对象读取,并由其他线程将其写入到相应的PipedOutputStream。

    一个线程写入,一个线程读取,作用:用于线程之间的数据通讯!

    不建议对这两个对象尝试使用单个线程,因为这样可能死锁线程。管道输入流包含一个缓冲区,可在缓冲区限定的范围内将读操作和写操作分离开。如果向连接管道输出流提供数据字节的线程不再存在,则认为该管道已损坏。

其中StringReader和StringWriter未来在操作XML或JSON时可能用到!

11 RandomAccessFile类

RandomAccessFile功能虽然强大,但在读写效率上的缺陷问题,可以使用nio的内存映射文件来替代!

  • RandomAccessFile是IO包的类,从Object直接继承而来。只可以对文件进行操作,可以对文件进行读取和写入
  • 当模式为r是,当文件不存在时会报异常;
  • 当模式为rw时,当文件不存在时,会自己动创建文件,当文件已经存在时不会对原有文件进行覆盖。
  • RandomAccessFile有强大的文件读写功能,其内部是大型byte[],可以通过seek(),getFilePointer() 等方法操作的指针,方便对数据进行写入与读取。还可以对基本数据类型进行直接的读和写操作。
  • RandomAccessFile的绝大多数功能,已经被JDK 1.4的nio的"内存映射文件(memory-mapped files)"给取代了,你该考虑一下是不是用“内存映射文件”来代替RandomAccessFile了。
package com.huang.TestIO.Demo11;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;

/**
 * RandomAccessFile类
 */
public class Demo01 {
    public static void main(String[] args) {
        try {
            //读取文件
            RandomAccessFile r = new RandomAccessFile("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\dog.txt","r");
            //写入文件
            RandomAccessFile w = new RandomAccessFile("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my\\dog.txt","rw");

            byte[] bytes = new byte[1024];
            int len = -1;
            while((len = r.read(bytes)) != -1){
                w.write(bytes,0,len);

            }
            w.close();
            r.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        System.out.println("copy success!");
    }
}

12 Properties文件操作

Properties(Java.util.Properties),主要用于读取Java的配置文件,各种语言都有自己所支持的配置文件,配置文件中很多变量是经常改变的,这样做也是为了方便用户,让用户能够脱离程序本身去修改相关的变量设置。(在JavaWeb和Java EE中经常用到)

它提供了几个主要的方法:

  1. getProperty(String key),用指定的键在此属性列表中搜索属性。也就是通过参数key,得到key所对应的value。
  2. load(InputStream inStream),从输入流中读取属性列表(键和元素对)。通过对指定的文件(比如说上面的test.properties文件)进行装载来获取该文件中的所有键-值对。以供getProperty(String key)来搜索。
  3. setProperty(String key,String value),调用Hashtable 的方法 put。他通过调用基类的put方法来设置键-值对。
  4. store(OutputStream out,String comments),以适合使用 load方法加载到Properties表中的格式,将此Properties 表中的属性列表(键和元素对)写入输出流。与load方法相反,该方法将键-值对写入到指定的文件中去。
  5. clear(),清除所有装载的键-值对。该方法在基类中提供。

软件发布流程:编写源代码–>打包项目–>发布–>服务器

package com.huang.TestIO.Demo12;

import java.io.*;
import java.util.Properties;

/**
 * Properties文件操作
 * properties可以用来做配置文件
 * javaweb javaee开发中通常会用到
 *
 * ResourcceBundle只读
 * Properties可读,可写
 */
public class Demo01 {
    public static String version = "";
    public static String username = "";
    public static String password = "";

    //静态代码块,只会执行一次
    static {
//        readConfig();//读取一次结束
    }
    /**
     * 读取properties配置文件
     */
    private static void readConfig(){
        Properties p = new Properties();
        try {
            //线程-类加载器-资源,通过当前线程的类加载器,来加载指定包下的配置文件
            InputStream inStream = Thread.currentThread().getContextClassLoader().
                    getResourceAsStream("com/res/config.properties");
//            InputStream inStream = new FileInputStream("H:\\JAVA\\1.Java零基础\\JavaCode\\Java基础语法" +
//                    "\\src\\com\\huang\\TestIO\\Demo12\\config.properties");
            p.load(inStream);//加载文件

            //从properties中获取数据
            version = p.getProperty("app.version");
            username = p.getProperty("db.username");
            password = p.getProperty("db.password");

            inStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    /**
     *对属性文件的写操作
     */
    private static void writeConfig(String version,String username,String password){
        Properties p = new Properties();
        p.put("app.version",version);
        p.put("db.username",username);
        p.put("db.password",password);
        //写文件
        try {
            OutputStream out = new FileOutputStream("H:\\JAVA\\1.Java零基础\\JavaCode\\Java基础语法" +
                    "\\src\\com\\huang\\TestIO\\Demo12\\config.properties");
            p.store(out,"update confige");
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }
    public static void main(String[] args) {
        //writeConfig("2","admin","987654");
        readConfig();
        System.out.println(Demo01.version);
        System.out.println(Demo01.username);
        System.out.println(Demo01.password);
    }

}

13 文件压缩与解压缩

java中实现zip的压缩与解压缩

  • ZipOutputStream实现文件的压缩
    ZipOutputStream(OutputStream out),创建新的ZIP输出流。
    void putNextEntry(ZipEntry e),开始写入新的ZIP文件条目并将流定位到条目数据的开始处。
    ZipEntry(String name),使用指定名称创建新的ZIP条目。
  • ZiplnputStream实现文件的解压
    ZiplnputStream(InputStream in),创建新的ZIP输入流。
    ZipEntry getNextEntry(),读取下一个zIP文件条目并将流定位到该条目数据的开始处。

【Java IO流知识总结】_第2张图片

14 装饰者模式

装饰者模式的应用场景是,不改变原有类结构基础上,在新增或者限制或者改造功能时候

意图:
动态地给一个对象添加一些额外的职责。就增加功能来说,Decorator模式相比生成子类更为灵活。该模式以对客户端透明的方式扩展对象的功能。

适用环境:
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
处理那些可以撤消的职责。
当不能采用生成子类的方法进行扩充时。一种情况是,**可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长。**另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。

类图:

  • Component(被装饰对象基类)
    定义对象的接口,可以给这些对象动态增加职责;(OutputStream)
  • ConcreteComponent(具体被装饰对象)
    定义具体的对象,Decorator可以给它增加额外的职责;(FileOutputStream)
  • Decorator(装饰者抽象类)
    维护指向Component实例的引用,定义与Component一致的接口;(File)
  • ConcreteDecorator(具体装饰者)
    具体的装饰对象,给内部持有的具体被装饰对象增加具体的职责;(BufferedOutputStream)

涉及角色:
抽象组件:定义一个抽象接口,来规范准备附加功能的类。
具体组件:将要被附加功能的类,实现抽象构件角色接口。
抽象装饰者:持有对具体构件角色的引用并定义与抽象构件角色一致的接口。
具体装饰:实现抽象装饰者角色,负责为具体构件添加额外功能。

代码实现:
Drink.java 被装饰者对象的接口
SoyaBeanMilk.java 具体的被装饰者对象
EggDecorator.java 具体装饰者对象
SugarDecorator.java 具体装饰者对象
BlackBeanDecorator.java 具体装饰者对象
Decorator.java 装饰者基类
Test.java 测试

OutputStream out = new FileOutputStream("xxx"); 
BufferedOutputStream bos = new BufferedOutputStream(out);
PrintStream ps = new PrintStream(bos); 
ps.print(..);

装饰者模式小结:
OO原则:动态地将责任附加到对象上。想要扩展功能,装饰者提供有别于继承的另一种选择。
要点:
1、继承属于扩展形式之一,但不见得是达到弹性设计的最佳方案。
2、在我们的设计中,应该允许行为可以被扩展,而不须修改现有的代码。
3、组合和委托可用于在运行时动态地加上新的行为。
4、除了继承,装饰者模式也可以让我们扩展行为。
5、装饰者模式意味着一群装饰者类,这些类用来包装具体组件。
6、装饰者类反映出被装饰的组件类型(实际上,他们具有相同的类型,都经过接口或继承实现)。
7、装饰者可以在被装饰者的行为前面与/或后面加上自己的行为,甚至将被装饰者的行为整个取代掉,而达到特定的目的。
8、你可以有无数个装饰者包装一个组件。
9、装饰者一般对组建的客户是透明的,除非客户程序依赖于组件的具体类型。

15 常见字符编码

了解编码问题可以帮助我们在开发中避免乱码,尤其在学习后续的web开发中

在计算机世界里,任何的文字都是以指定的编码方式存在的。
常见编码有:ISO8859-1、GBK/GB2312、unicode、UTF

iso8859-1:
编码属于单字节编码,最多只能表示0–255的字符范围,主要在英文上应用。
GBK/GB2312:
中文的国际编码,专门用来表示汉字,是双字节编码
unicode:
java中就是使用此编码方式,也是最标准的一种编码,是使用16进制表示的编码。但此编码不兼容iso8859-1编码。
UTF:
由于unicode不支持iso8859-1编码,而且容易占用更多的空间,而且对于英文母也需要使用两个字节编码,这样使用unicode不便于传输和储存,因此产生了utf编码,utf编码兼容了iso8859-1编码,也可以用来表示所有语言字符,不过utf是不定长编码,每个字符的长度从1-6个字节不等,一般在中文网页中使用此编码,因为这样可以节省空间。

造成乱码的根本原因:

  1. 程序使用的编码与本机的编码不统一
  2. 在网络中,客户端与服务端编码不统(在web开发中出现的乱码情况)
package com.huang.TestIO.Demo15;

import java.io.UnsupportedEncodingException;

/**
 * 字符编码
 */
public class CodeDemo {
    public static void main(String[] args) {
        //通常产生乱码的情况是,两个不兼容的编码相互转换
        String info = "西北望,射天狼。";//GB2312
        try {
            String newInfo = new String(info.getBytes("gb2312"),"iso8859-1");
            System.out.println(newInfo);

            String newInfo2 = new String(newInfo.getBytes("iso8859-1"),"gb2312");
            System.out.println(newInfo2);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

    }
}

16 New IO

NIO模式是新的主流IO操作模式,最重要的提升就是在高效读取数据上,未来学习的Netty网络通信框架就是基于NIO实现

NIO可以提高数据的读取效率的根本原因是利用了操作系统(如Linux)底层的10多路复用机制实现

为什么要使用NIO?
NIO是JDK1.4加入的新包,NIO的创建目的是为了让Java程序员可以实现高速I/O而无需编写自定义的本机代码。
NIO将最耗时的I/O操作(即填充和提取缓冲区)转移回操作系统,因而可以极大地提高速度。

流与块的比较

  • 原来的I/O库(在java.jo.*中)与NIO最重要的区别是数据打包和传输的方式,原来的I/O以流的方式处理数据,而NIO以块的方式处理数据。
  • 面向流的I/0系统一次一个字节地处理数据。一个输入流产生一个字节的数据,一个输出流消费一个字节的数据。不利的一面是,面向流的I/O通常相当慢。
  • 一个面向块的I/O系统以块的形式处理数据。每一个操作都在一步中产生或者消费一个数据块。按块处理数据比按(流式的)字节处理数据要快得多。但是面向块的I/O缺少一些面向流的I/O所具有的优雅性和简单性。

缓冲区
在NIO库中,断有数据都是用缓冲区处理的。在读取数据时,它是直接读到缓冲区中的。在写入数据时,它是写入到缓冲区中的。任何时候访问NIO中的数据,您都是将它放到缓冲区中。
缓冲区实质上是一个数组。通常它是一个字节数组,但是也可以使用其他种类的数组。但是一个缓冲区不仅仅是一个数组。缓冲区提供了对数据的结构化访问,而且还可以跟踪系统的读写进程。

缓冲区类型
最常用的缓冲区类型是ByteBuffer。一个ByteBuffer可以在其底层字节数组上进行get/set操作(即字节的获取和设置)。ByteBuffer不是NIO中唯一的缓冲区类型。事实上,对于每一种基本Java类型都有一种缓冲区类型:
ByteBuffer
CharBuffer
ShortBuffer
IntBuffer
LongBuffer
FloatBuffer
DoubleBuffer

缓冲区内部细节:
状态变量:(可以用三个值指定缓冲区在任意时刻的状态)
position limit capacity

package com.huang.TestIO.Demo16;

import java.nio.ByteBuffer;

/**
 * New IO
 */
public class Demo01 {
    public static void main(String[] args) {
        //创建一个字节缓冲区,申请内存空间为8字节
        ByteBuffer buf = ByteBuffer.allocate(8);
        System.out.println("position=" + buf.position());
        System.out.println("limit=" + buf.limit());
        System.out.println("capacity=" + buf.capacity());
        System.out.println("----------------------");
        //向缓冲区中写入数据
        buf.put((byte) 10);
        buf.put((byte) 20);
        buf.put((byte) 30);
        buf.put((byte) 40);
        System.out.println("position=" + buf.position());
        System.out.println("limit=" + buf.limit());
        System.out.println("capacity=" + buf.capacity());
        System.out.println("----------------------");

        //缓冲区反转
        buf.flip();//flip()-->limit=position;position=0;
        System.out.println("position=" + buf.position());
        System.out.println("limit=" + buf.limit());
        System.out.println("capacity=" + buf.capacity());
        System.out.println("----------------------");

        //告知在当前位置和限制之间是否有元素
        if(buf.hasRemaining()){
            //返回当前位置与限制之间的元素数
            for(int i=0; i<buf.remaining();i++){
                byte b = buf.get(i);
                System.out.println(b);
            }
        }
        //取值不会对状态变量有影响
        System.out.println("position=" + buf.position());
        System.out.println("limit=" + buf.limit());
        System.out.println("capacity=" + buf.capacity());
        System.out.println("----------------------");
    }
}

**通道:**Channel
Channel 是一个对象,可以通过审读取和写入数据。拿NIO与原来的I/O做个比较,通道就像是流。
程序–>缓冲区–>通道–>文件
正如前面提到的,所有数据都通过Buffer对象来处理。
永远不会将字节直接写入通道中,相反,是将数据写入包含一个或者多个字节的缓冲区。
同样,不会直接从通道中读取字节,而是将数据从通道读入缓冲区,再从缓冲区获取这个字节。

使用通道读写文件示例

package com.huang.TestIO.Demo16;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

/**
 * 使用通道复制文件
 * FileChannel类
 * 比较IO操作的性能比较
 * 1、内存映射最快
 * 2、NIO读写文件
 * 3、使用了缓存的IO流
 * 4、无缓存的IO流
 */
public class Demo02 {
    public static void main(String[] args) throws IOException {
        copyFile();
        randomAccessFileCopy();
    }

    private static void copyFile() throws IOException {
        //创建一个输入文件的通道
        FileChannel fcIn = new FileInputStream
                ("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\picture.png").getChannel();
        //创建一个输出文件的通道
        FileChannel fcOut = new FileOutputStream("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my1\\picture.png").getChannel();

        ByteBuffer buf = ByteBuffer.allocate(1024);
        while(fcIn.read(buf) != -1){
            buf.flip();
            fcOut.write(buf);
            buf.clear();
        }
        fcIn.close();
        fcOut.close();
        System.out.println("copy success!");
    }
    //使用内存映射实现文件复制
    private static void randomAccessFileCopy() throws IOException {
        RandomAccessFile in = new RandomAccessFile("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\picture.png","r");
        RandomAccessFile out = new RandomAccessFile("H:\\JAVA\\2.Java常用类、集合、IO\\IO\\my1\\picture1.png","rw");

        FileChannel fcIn = in.getChannel();
        FileChannel fcOut = out.getChannel();

        long size = fcIn.size();//输入流的大小
        //输入流的缓冲区
        MappedByteBuffer inBuff = fcIn.map(FileChannel.MapMode.READ_ONLY,0,size);
        //输出流的缓冲区
        MappedByteBuffer outBuff = fcOut.map(FileChannel.MapMode.READ_WRITE,0,size);

        for(int i=0;i<size;i++){
            outBuff.put(inBuff.get());
        }
        //关闭(关闭通道时会写入数据块)
        fcIn.close();
        fcOut.close();
        in.close();
        out.close();
        System.out.println("copy succsee!");
    }
}

JDK1.7引人了新的IO操作类,java.nio.file包下,Java NIO Path接口、Paths类和Files类

Path接口

  1. Path表示的是一个目录名序列,其后还可以跟着一个文件名,路径中第一个部件是根部件时就是绝对路径,例如 / 或 C:\ ,而允许访问的根部件取决于文件系统;
  2. 以根部件开始的路径是绝对路径,否则就是相对路径;
  3. 静态的Paths.get方法接受一个或多个字符串,字符串之间自动使用默认文件系统的路径分隔符连接起来(Unix是/,Windows是 \ ,这就解决了跨平台的问题,接着解析连接起来的结果,如果不是合法路径就抛出InvalidPathException异常,否则就返回一个Path对象。

Files工具类

  1. 读写文件
    static path write(Path path,byte[] bytes,OpenOption… options)写入文件

    static byte[] readAllBytes(Path path)读取文件中的所有字节。

  2. 复制、剪切、删除
    static path copy(Path source,Path target,CopyOption… options)

    static path move(Path source,Path target,CopyOption… options)

    static void delete(Path path)//如果path不存在文件将抛出异常,此时调用下面的比较好

    static boolean deletelfExists(Path path)

  3. 创建文件和目录
    //创建新目录,除了最后一个部件,其他必须是已存在的
    Files.createDirectory(path);

    //创建路径中的中间目录,能创建不存在的中间部件
    Files.createDirectories(path);

    //创建一个空文件,检查文件存在,如果已存在则抛出异常而检查文件存在是原子性的,
    //因此在此过程中无法执行文件创建操作
    Files.create File(path);

    //添加前/后缀创建临时文件或临时目录
    Path newPath = Files.createTempFile(dir,prefix,suffix);

    Path newPath = Files.createTempDirectory(dir,prefix);

资料整理于:千峰Java

视频连接:点击进入

你可能感兴趣的:(Java基础,java,开发语言,经验分享)