5、IO(1)

一、File类

    public static void test(){
        //File src = new File("C:/Users/yj/Desktop/1.txt");
        File src = new File("1.txt");
        System.out.println(src.getName());
        System.out.println(src.getPath());//如果是绝对路径,否则返回相对路径
        System.out.println(src.getAbsolutePath());//返回绝对路径
        System.out.println(src.getParent());//返回上一级目录,如果是相对路径,没有上一级返回空
        System.out.println("是否是绝对路径: " + src.isAbsolute());
    }
    
    //判断信息
    public static void test1(){
        //File src = new File("1.txt");
        File src = new File("C:/Users/yj/Desktop/1.txt");
        System.out.println("文件是否存在:" + src.exists());
        System.out.println("文件是否可写: " + src.canWrite());
        System.out.println("是不是文件夹: " + src.isDirectory());
        System.out.println("是不是文件: " + src.isFile());
        System.out.println("文件的长度: " + src.length());//只有文件才能读取长度
    }

说明:以上是File类的一些常用方法。下面我们看File中对目录的操作方法:

mkdir()创建目录,必须确保父目录存在,否则创建失败
mkdirs(),创建目录,如果父目录不存在,也一同创建
list()以字符串形式列出所有文件和目录名字(不包含路径)
listFiles()以字符串形式列出所有文件和目录名字(包含完整路径)
static listRoots()根路径

二、流(stream)

2.1 概念

程序与文件、数组、网络连接、数据库都是使用流进行交互,但是都是以程序为中心

2.2 分类

  • 1、根据流向:输入流和输出流
  • 2、根据数据类型:字节流(二进制,可以处理任何数据)和字符流(文本,只能处理纯文本)
  • 3、根据功能:节点流,就是离数据源最近的,包裹源头的;处理流,是增强功能,提高性能的。

2.3 字符流和字节流

2.3.1 字节流

输入流:InputStream(抽象类)(常用实现类FileInputStream

public int read(byte[] b)
public int read(byte[] b, int off, int len)
public void close()

输出流:OutputStream(抽象类)(常用实现类FileOutputStream

public void write(byte[] b)
public void write(byte[] b,int off,int len)
public void flush()
public void close()

下面看使用字节流读取文件的基本步骤:

package cn.itcast.day146.stream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

//文件的读取
public class Demo01 {
    public static void main(String[] args) {
        //1、建立联系
        File src = new File("D:/FQ/parent/FeiqCfg.xml");
        //2、选择流
        InputStream is = null;//提升作用域
        try {
            is = new FileInputStream(src);
            //3、操作,不断读取
            byte[] buffer = new byte[1024];//相当于一个缓冲数组,即每次读取的字节数不超过1024个
            int len = 0;//接收实际读取的大小
            //循环读取,每次读取1024个字节
            while((len = is.read(buffer)) != -1){
                //输出,需要将字节数组转换成字符串
                String info = new String(buffer , 0, len);
                System.out.println(info);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            System.out.println("文件不存在");
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("读取文件失败");
        }finally{
            //4、释放资源
            if(is != null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                    System.out.println("关闭输入流失败");
                }
            }
        }
    }
}

下面我们看使用字节流写入文件的基本步骤:

package cn.itcast.day146.stream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

//写出文件
public class Demo02 {
    public static void main(String[] args) {
        //1、建立联系
        File dest = new File("D:/FQ/parent/test.txt");
        //2、选择流
        OutputStream os = null;
        try {
            os = new FileOutputStream(dest, true);//写出文件,以追加的形式
            //3、操作
            String str = "something is so difficult \r\n";
            //字符串转换成字节数组
            byte[] data = str.getBytes();
            os.write(data, 0, data.length);
            os.flush();//刷新, 如果使用close方法会默认调用此方法
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            System.out.println("文件未找到");
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("文件写出失败");
        }finally{
            try {
                if(os != null){
                    os.close();//释放资源
                }
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("关闭输出流失败");
            }
        }
    }
}

说明:写入文件的基本步骤较为简单,首先将要写入的内容转换成字节数组,然后使用write方法一次性写入即可。同时我们在关闭流之前最好手动刷新缓存,虽然关闭流的时候会自动刷新。我们将读取和写入结合起来就可以实现文件的拷贝了:

    public static void fileCopy02(String srcPath, String destPath){
        // 1、创建源(文件必须存在)+目的地(文件可以不存在)
        File src = new File(srcPath);
        File dest = new File(destPath);
        // 2、选择流
        try (InputStream is = new FileInputStream(src);
                OutputStream os = new FileOutputStream(dest);) {
            // 3、文件拷贝=读取+写出
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = is.read(buffer)) != -1) {// 读取
                // 写出
                os.write(buffer, 0, len);
            }
            os.flush();
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

说明:这里要注意的是文件源必须存在,而目的地可以不存在,同时这里我们使用了1.7的新特性,即try with,内部会帮我们关闭流。

2.3.2 字符流

输入流:Reader(抽象类)(常用实现类FileReader

public int read(char[] cbuf)
public abstract int read(char[] cbuf,int off,int len)
public abstract void close()

输出流:Writer(抽象类)(常用实现类FileWriter

public void write(char[] cbuf)
public abstract void write(char[] cbuf,int off,int len)
public abstract void close()
public void write(String str,int off,int len)

下面我们给出使用字符流读取文件的基本步骤:

package cn.itcast.day146.stream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
//文件的读取,使用字符流
public class Demo04 {
    public static void main(String[] args) {
        //创建源
        File src = new File("D:/FQ/parent/test.txt");
        //选择流
        Reader reader = null;
        try {
            reader = new FileReader(src);
            char[] buffer = new char[1024];
            int len = 0;
            try {
                while((len = reader.read(buffer)) != -1){
                    //字符数组转换成字符串
                    String str = new String(buffer, 0, len);
                    System.out.println(str);
                }
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("文件读取失败");
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            System.out.println("源文件不存在");
        }finally{
            if(reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

说明:这里可以看到和使用字节流读取基本一样,只是使用的是字符数组。下面看使用字符流写入文件:

package cn.itcast.day146.stream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
//使用字符流写出文件
public class Demo05 {
    public static void main(String[] args) {
        //创建源
        File src = new File("F:/FQ/parent/test.txt");
        //选择流
        Writer writer = null; 
        try {
            writer = new FileWriter(src, true);//第二个参数为true表示追加
            //写出
            String msg = "狗蛋打了铁柱一顿";
            writer.write(msg);//这里可以直接写字符串
            writer.append("狗蛋");
            writer.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(writer != null){
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

说明:这里要注意的是在字符流没有关闭的情况下我们可以直接使用append方法进行写入。使用字符流进行文件的拷贝这里就不给出了。

2.4 缓冲流

缓冲流主要用于增强功能,提高性能。处理流一定要在节点流之上。我们推荐使用缓冲流,而不是直接使用节点流。

2.4.1字节缓冲流

针对字节的缓冲流,主要实现类有:

BufferedInputStream:没有新增方法
BufferedOutputStream: 没有新增方法

下面我们看使用的基本步骤:

package cn.itcast.day146.stream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/* 
 * 文件拷贝,使用缓冲字节流,建议使用,提高性能
 * 只是在节点流上面包上一层缓冲流
 * */
public class Demo06 {
    public static void fileCopy(String srcPath, String destPath) throws FileNotFoundException, IOException {
        File src = new File(srcPath);
        File dest = new File(destPath);
        if(!src.isFile()){
            System.out.println("只能拷贝文件");
            throw new IOException("只能拷贝文件");
        }
        
        //如果目的地为已经存在的文件夹,不能建立与文件夹同名的文件或文件夹,当然如果是file则会覆盖
        if(dest.isDirectory()){
            System.out.println("能建立与文件夹同名的文件或文件夹");
            throw new IOException("能建立与文件夹同名的文件或文件夹");
        }
        
        //2、选择流
        InputStream is = new BufferedInputStream(new FileInputStream(src));
        OutputStream os = new BufferedOutputStream(new FileOutputStream(dest));
        //3、文件拷贝=读取+写出
        byte[] buffer = new byte[1024];
        int len = 0;
        while((len = is.read(buffer)) != -1){//读取
            os.write(buffer, 0, len);//写出
        }
        os.flush();
        //关闭:先打开的后关闭
        os.close();
        is.close();
    }
}

说明:可以看到使用步骤和之前的字节流基本是一致的,只是我们使用缓冲流对字节流进行了包装,用以提高读取和写入的性能。

2.4.2 字符缓冲流

针对字符的缓冲流,主要实现类有:

BufferedReader:新增方法:readLine()
BufferedWriter:新增方法:newLine()

下面我们看基本的使用步骤:

package cn.itcast.day146.stream;
//文件拷贝,使用缓冲字符流
//新增方法
public class Demo07 {
    public static void main(String[] args) {
        File src = new File("D:/FQ/parent/test.txt");
        BufferedReader reader = null;//使用新增方法不能有多态,即定义的时候需要使用缓冲字符流
        File dest = new File("D:/FQ/parent/test01.txt");
        BufferedWriter writer = null;//使用新增方法不能有多态,即定义的时候需要使用缓冲字符流
        try {
            //包装
            reader = new BufferedReader(new FileReader(src));
            writer = new BufferedWriter(new FileWriter(dest));
            //使用之前的方式
//          char[] buffer = new char[1024];
//          int len = 0;
//          while((len = reader.read(buffer)) != -1){
//              writer.write(buffer, 0, len);
//          }
            //使用新增方法进行读取,使用新增方法不能有多态,即定义的时候需要使用缓冲字符流
            String line = null;
            while((line = reader.readLine()) != null){//一行一行的读取
                writer.write(line);//写出
                writer.newLine();//加上换行符
            }
            writer.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(writer != null){
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(reader != null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

说明:基本的使用步骤还是和之前一样,当然我们推荐使用新方法,这样比较简单。

2.5 转换流

字节流转换为字符流,用于处理乱码问题。这里我们先看乱码产生原因:

  • 1)编码与解码的字符集不统一
  • 2)字符缺少,长度丢失
    常用实现类:
InputStreamReader
OutputStreamWriter

例如:

    //乱码原因一:解码和编码的字符集不统一
    public static void test1() throws UnsupportedEncodingException {
        String str = "中国";//utf-8
        //编码 char-->二进制
        byte[] data = str.getBytes();
        //解码和编码的字符集统一
        System.out.println(new String(data));//没有乱码
        data = str.getBytes("gbk");//设定编码字符集
        //解码和编码的字符集不统一
        System.out.println(new String(data));//有乱码
        //编码
        byte[] data2 = "中国".getBytes("UTF-8");
        //解码
        str = new String(data2, "UTF-8");
        System.out.println(str);
    }
    
    //乱码原因二:字节数不完整导致乱码
    public static void test2(){
        String str = "中国";
        byte[] data = str.getBytes();
        //字节数不完整导致乱码
        System.out.println(new String(data, 0, 3));
    }

下面我们看转换流的基本使用步骤:

/*转换流:字节-->字符
 * 1、输出流 OutputStreamWriter编码
 * 2、输入流 InputStreamReader解码
 * */
public class Demo09 {
    public static void main(String[] args) throws IOException {
        //可以在转换流中指定解码字符集,注意:这里的字符集就是我们要读取文件的编码字符集
        BufferedReader br = new BufferedReader(
                new InputStreamReader(/*这是一个转换流,将字符流和字节流联系起来*/
                        new FileInputStream(new File("D:/FQ/parent/test.txt")), "UTF-8"));//底层是一个字节流
        //写出文件
        BufferedWriter bw = new BufferedWriter(
                new OutputStreamWriter(
                        new FileOutputStream(new File("D:/FQ/parent/test01.txt")), "UTF-8"));
        char[] buffer = new char[1024];
        int len = 0;
        while((len = br.read(buffer)) != -1){
            bw.write(buffer, 0, len);
        }
        bw.flush();
        
        /*String info = null;
        while(null != (info = br.readLine())){
            System.out.println(info);
        }
        br.close();*/
        br.close();
        bw.close();
    }
}

说明:这里我们知道转换流是将字节流转换成字符流,所以底层包装的是字节流,当然这里我们还使用了缓冲流对字符流进行了包装。这里我们使用字节流读取,在读取的时候进行解码,即将内容转换成了字节,而同时我们在写入的时候也是将字符转换成字节再写入,这样就不会出现乱码的问题。

2.6 其他流

2.6.1 字节流

这里我们看字节数组流,可将此流看作在其他电脑的内存中,所以我们不用关闭,关闭也是无效的。其常用的实现类有:

ByteArrayInputStream
ByteArrayOutputStream

下面给出基本的使用步骤:

    //输入流操作与文件输入流操作一致
    public static void read(byte[] src) throws IOException{
        //String msg = "输入流操作与文件输入流操作一致";
        //byte[] src = msg.getBytes();
        
        //选择流
        InputStream is = new BufferedInputStream(new ByteArrayInputStream(src));
        //操作
        byte[] buffer = new byte[1024];
        int len = 0;
        while(-1 != (len = is.read(buffer))){
            System.out.println(new String(buffer, 0, len));
        }
        is.close();
    }
    //输出流与文件输出流有些不同,因为有新增方法,不能使用多态
    public static byte[] write() throws IOException{
        //目的地
        byte[] dest;
        //选择流,不同点
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        //写出
        String msg = "输入流操作与文件输入流操作一致";
        byte[] info = msg.getBytes();
        bos.write(info, 0, info.length);
        //获取数据
        dest = bos.toByteArray();
        bos.close();
        return dest;
    }

说明:这里我们只是给出了读写的方法,并没有说从哪里读取,写到哪里去,这是因为在使用此类流的时候需要明白,比如在文件拷贝的时候,是按照这样一个步骤进行,首先是将文件内容拷贝到程序中,之后再从程序中拷贝到文件中,下面我们给出例子:

package cn.itcast.day146.stream;
import java.io.*;
/*
 * 使用字节数组流读取写出文件
 * 1、文件-->程序-->字节数组
 * 文件输入流+字节数组输出流
 * 
 * 2、字节数组-->程序-->文件
 * 字节数组输入流+文件输出流
 * */
public class Demo11 {
    public static void main(String[] args) throws IOException {
        byte[] data = getBytesFromFile("D:/FQ/parent/test.txt");
        toFileFromByteArray(data, "D:/FQ/parent/test01.txt");
    }
    
    //文件-->程序
    public static byte[] getBytesFromFile(String stcPath) throws IOException{
        //创建源(文件)和目的地(字节数组)
        File src = new File(stcPath);
        byte[] dest = null;
        //选择流
        //文件输入流
        InputStream is = new BufferedInputStream(new FileInputStream(src));
        //字节数组输出流,不能使用多态
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        //操作:不断读取文件,写出到字节数组流中
        byte[] buffer = new byte[1024];
        int len = 0;
        while(-1 != (len = is.read(buffer))){
            //写出到字节数组流中
            bos.write(buffer, 0, len);
        }
        bos.flush();
        //获取数据
        dest = bos.toByteArray();//将字节数组流中的数据写到一个字节数组中
        bos.close();
        is.close();
        return dest;
    }
    
    public static void toFileFromByteArray(byte[] src, String destPath) throws IOException{
        //创建源,即src
        //创建目的地, 即dest
        File dest = new File(destPath);
        //选择流
        //字节数组输入流
        InputStream is = new BufferedInputStream(new ByteArrayInputStream(src));
        //文件输出流
        OutputStream os = new BufferedOutputStream(new FileOutputStream(dest));
        //不断读取
        byte[] buffer = new byte[1024];
        int len = 0;
        while(-1 != (len = is.read(buffer))){
            os.write(buffer, 0, len);//写出到文件中
        }
        os.flush();
        os.close();
        is.close();
    }
}

说明:这里要注意的是此类流由于数组的大小有限,所以只适合读取少量数据的文件。同时我们不管在读取还是写的时候一定是以程序为中心,这样就不至于分不清到底是使用输入流还是输出流了。

2.6.2 处理流

这里我们先给出DataInputStreamDataOutputStream两个流,这个类型的流可以保留数据和数据类型,比如可以直接读取String类型数据,并保留其类型。但是这个类有个限制就是读取和写入的顺序必须一致。下面给出基本的使用步骤:

    public static void write(String destPath) throws IOException{
        double point = 2.5;
        long num = 100L;
        String str = "数据类型";
        //创建源
        File dest = new File(destPath);
        
        //选择流 DataInputStream
        DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(
                new FileOutputStream(dest)));
        //操作,写出,同时写出的顺序和读取的顺序必须一致
        dos.writeDouble(point);
        dos.writeLong(num);
        dos.writeUTF(str);
        dos.flush();
        dos.close();
    }
    
    //读取数据加类型
    public static void read(String srcPath) throws IOException{
        //创建源
        File src = new File(srcPath);
        //选择流
        DataInputStream dis = new DataInputStream(
                new BufferedInputStream(new FileInputStream(src)));
        //操作,读取的顺序必须和写出的数据一致, 必须存在才能读取
        double num1 = dis.readDouble();
        long num2 = dis.readLong();
        String str = dis.readUTF();
        System.out.println(str);
        
    }

说明:这个类型的流了解即可。这里是对基本数据类型的处理流,下面我们看引用类型的处理流。

反序列化(输入流)ObjectInputStraem : readObject()
序列化(输出流)ObjectOutputStream : writeObject()

首先我们给出一个javabean

package cn.itcast.day159.stream;
import java.io.Serializable;
//序列化的类
public class Employee implements Serializable{
    private transient String name ;//此属性不进行序列化
    private double salary ;
    
    public Employee() {
        super();
    }
    public Employee(String name, double salary) {
        super();
        this.name = name;
        this.salary = salary;
    }
//getters and setters method
}

下面我们看基本的操作步骤:

package cn.itcast.day159.stream;
import java.io.*;
import java.util.Arrays;
public class Demo01 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //write("F:/FQ/parent/data.txt");
        read("F:/FQ/parent/data.txt");  
    }
    
    //序列化
    public static void write(String destPath) throws IOException{
        Employee employee = new Employee("Tom", 10000);
        int[] arr = {1,2,3,4,5};//数组内部实现了序列化接口
        //创建源
        File dest = new File(destPath);
        
        //选择流 DataInputStream
        ObjectOutputStream dos = new ObjectOutputStream(new BufferedOutputStream(
                new FileOutputStream(dest)));
        //操作,写出,同时写出的顺序和读取的顺序必须一致
        dos.writeObject(employee);
        dos.writeObject(arr);
        dos.flush();
        dos.close();
    }
    
    //反序列化
    public static void read(String srcPath) throws IOException, ClassNotFoundException{
        //创建源
        File src = new File(srcPath);
        //选择流
        ObjectInputStream dis = new ObjectInputStream(
                new BufferedInputStream(new FileInputStream(src)));
        //操作,读取的顺序必须和写出的数据一致, 必须存在才能读取
        Object obj = dis.readObject();
        if(obj instanceof Employee){
            Employee e = (Employee) obj;
            //这里名字是取不到的,因为我们没有序列化此属性
            System.out.println("名字: " + e.getName() + ", 薪水: " + e.getSalary());
        }
        int arr[] = (int[]) dis.readObject();
        System.out.println(Arrays.toString(arr));
    }
}

说明:我们在类中使用关键字transient限定那些字段不进行序列化,同时在测试的时候注意写入在进行读取。
注意:

  • 1、反序列化必须和序列化顺序一致,先序列化后反序列化
  • 2、不是所有的对象都可以序列化,必须实现接口java.io.Serializable
  • 3、不是所有的属性都需要序列化,不想序列化的属性使用transient标识

你可能感兴趣的:(5、IO(1))