14Java的IO流

File类

File类常用方法

 例子

import java.io.File;
import java.io.IOException;

/**
 * 
 * @author leak File类只能操作文件本身,但是不能操作文件内容 例如:你可以把一个txt文档增删改查,但是不能对txt里面的内容增删改查
 */
public class Test {
    public static void main(String[] args) {
    // 1访问文件名
        // 这个时候对象f就是abc.txt文件
        // 文件路径的反斜杆和斜杆/,如果是那么需要\两个,一个做转义符作用D:\abc
        // 如果是斜杆D:/ABC/ABC.TXT,一个就够了
        // File f = new File("文件路径");把文件转为对象操作
        File f = new File("D:/abc/abc.txt");
        // 创建文件对象有两种方式,一种上面的直接加路径
        // 一种就是File f1 = new File("父路径","子路径");
//        File f1 = new File("D:/abc","abc.txt");

        // 使用相对路径来创建file对象
        File f5 = new File("/Test1/src/org/chen/day12/Test.java");
        // 获取当前的文件名,如果是路径,那么就是获取最后一级目录名
        System.out.println(f.getName());
        // 如果new File对象的路径是相对路径,那么getPath就是获取相对路径地址
        // 如果是绝对路径,那么getPath就是获取绝对的地址
        System.out.println(f.getPath());
        // 获取当前文件/夹的路径,是完整的路径(绝对路径)
        System.out.println(f.getAbsolutePath());

        System.out.println(f5);// 打印文件的相对路径
        // 返回一个用当前的文件的绝对路径构建的file对象
        System.out.println(f5.getAbsoluteFile());

        // 返回当前文件/夹的父级路径,也就是文件的前面的完整路径,比如:D:abcabc.txt就是获取D:abc父级路径
        System.out.println(f.getParent());

        // 文件重命名
        f.renameTo(new File("D:/abc/rename.txt"));
        System.out.println("====================");
        
        
    // 2文件检测
        File f6 = new File("D:/abc/favicon.ico");
        // exists()判断文件/夹是否存在,返回布尔值
        System.out.println(f6.exists());

        // canWrite()判断文件/夹是否可写,canRead()判断文件/夹是否可读
        System.out.println(f6.canWrite());
        System.out.println(f6.canRead());

        // 判断当前的file对象是不是文件
        System.out.println(f6.isFile());
        // 判断当前的filed对象是不是文件夹/目录
        System.out.println(f6.isDirectory());

        // 获取文件最后修改的时间,返回的是一个毫秒数
        System.out.println(f6.lastModified());
        // 返回文件的长度,单位是字节数
        System.out.println(f6.length());
        System.out.println("====================");
        
        
    // 3文件/夹操作
        File f3 = new File("D:/abc/abc.txt");
        System.out.println(f3.exists());
        // 如果文件不存在,则创建
        if (!f3.exists()) {
            try {
                f3.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        // 删除文件
        f3.delete();

        File f2 = new File("D:/abc/cc");
        System.out.println(f2.exists());
        if (!f2.exists()) {
            // 创建单层目录,如果使用mkdir方法创建多层目录,只能一层层的执行mkdir方法
            f2.mkdir();
        }

        File f4 = new File("D:/abc/dd/aa/ee");
        if (!f4.exists()) {
            // 创建多层目录,注意是mkdirs(),上面的mkdir少了s
            f2.mkdirs();
        }

        File f7 = new File("D:/abc");
        // list()返回当前文件夹的子集的名称,包括目录和文件名
        String[] flist = f7.list();
        for (String name : flist) {
            System.out.println(name);
        }
        
        // listFiles()返回当前文件夹的子集的file对象,包括目录和文件名
        //就是返回子集里面所有的文件名/目录的绝对路径,例如: D:abccc, D:abcfavicon.ico
        File[] files = f7.listFiles();
        for(File file : files) {
            System.out.println(file);
        }
    }
}
View Code

递归遍历文件夹/文件

通常把某个文件夹里面的文件和目录全部遍历出来,不管层级有多深,但是不知道有多少层目录,就会导致遍历文件的代码会重复,因为每进入一个目录,就要遍历里面的文件和目录,下面举一个例子说明遍历文件夹所有文件/目录时,陷于重复代码。

import java.io.File;

public class Test2 {
    public static void main(String[] args) {
        
    }
    //递归的引出
    public void findAllFiles(File file) {
        if(file.isFile()) {
            System.out.println(file.getAbsoluteFile()+"是一个文件");
        }else {
            //是文件夹则继续遍历里面的文件和目录
            System.out.println(file.getAbsolutePath()+"是文件夹");
            //获取文件夹中的所有目录/文件,使用file数组接收
            File[] files = file.listFiles();
            //遍历数组里面的文件/目录
            for(File f : files) {
        //如果是文件则打印文件,      下面开始第一层重复
                if(file.isFile()) {
                    System.out.println(file.getAbsoluteFile()+"是一个文件");
                }else {
                    //如果是文件夹则继续遍历里面的文件和目录
                    System.out.println(file.getAbsolutePath()+"是文件夹");
                    //获取文件夹中的子集,包含文件/目录
                    File[] files2 = file.listFiles();
                    //遍历数组里面的文件/目录
                    for(File f2 : files) {
        //如果是文件则打印文件,   这里的下面是第二层重复
                        if(file.isFile()) {
                            System.out.println(file.getAbsoluteFile()+"是一个文件");
                        }else {
                            //如果是文件夹则继续遍历里面的文件和目录
                            System.out.println(file.getAbsolutePath()+"是文件夹");
                            //获取文件夹中的子集,包含文件/目录
                            File[] files3 = file.listFiles();
                            //然后继续遍历files3数组里面的文件和目录,如果是目录那么继续遍历
                            //这里就不重复写代码了,for循环后面都是一样的代码,这样一直遍历,不知道要写多少层,是死循环代码
        //下面是第三层重复代码,就不写了,如果是重复的代码,而且不知道写到几层,就应该使用递归方法。递归就是方法调用本身,方法调用自己
                        }
                    }
                }
            }
        }
    }
}
View Code

从上面的例子,可以发现for下面的代码就开始重复了,但是不知道要重复多少次,因为目录不知道有多少层,现在用递归来遍历。

import java.io.File;

public class Test3 {
    public static void main(String[] args) {
        //创建一个目录对象
        File file = new File("D:/abc");
        //然后把目录对象,传给findAllFiles()方法遍历该目录底下的所有层级的目录/文件
        new Test3().findAllFiles(file);
    }
    //递归遍历所有的目录/文件,每一层
        public void findAllFiles(File file) {
            if(file.isFile()) {
                System.out.println(file.getAbsoluteFile()+" 是一个file");
            }else {
                //是文件夹则继续遍历里面的文件和目录
                System.out.println(file.getAbsolutePath()+" 是一个文件夹");
                //获取文件夹中的所有目录/文件,使用file数组接收
                File[] files = file.listFiles();
                //遍历数组里面的文件/目录
                for(File f : files) {
                    //重点在这里,递归:自己调用自己,直到遍历完所有文件/目录,否则重复调用findAllFiles(f);
                    findAllFiles(f);
                }
            }
        }
    }
View Code

注意:递归需要有结束条件,如果没有结束条件会一直执行递归,直到内存溢出报异常。上面的例子的结束条件是目录数,目录是有限的,所以遍历完目录/文件就会结束递归。

 IO原理

iO流用来处理设备之间的数据传输。

Java程序中,对于数据的输入/输出操作以”流(stream)” 的方式进行。

java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。

 IO流的分类

 IO流体系

FileInputStream 文件字节输入流

文件需要读取内容时,可以使用FileInputStream类,下面举一个例子说明FileInputStream类如何读取文件。

import java.io.FileInputStream;

public class Test4 {
    public static void main(String[] args) {
        Test4.readFile("D:/abc/rename.txt");
    }
    //读取文件的方法
    public static void readFile(String path) {
        try {
            //把文件转为输入流
            FileInputStream in = new FileInputStream(path);
            
            //设置byte数组接收读取的文件的内容
            byte[] b = new byte[10];
            
            //设置临时变量存储每次读取的长度,也就是in.read(10)返回的长度
            //肯定有人说为什么不直接使用in.read(10)的返回值,而是要新创建变量len去接收返回值再使用
            //因为每次执行in.read(b)方法时,都会读取一次文件,每次返回的值都不一样
            //比如下面的len = in.read(b)第一次返回10,但是你在循环体中输出in.read(b)的长度就不是10了,因为又执行了一遍读操作
            int len = 0;
            
            //因为byte数组固定了长度为10,所以需要循环读取文件,每次读取10个字符
            //补充:read()方法是读取一个字节,返回字节如果是字母就返回ASCII码,比如文件内容第一个字节是a就返回97
            //read(byte[] b)方法是返回每次读取字节数的个数,个数根据byte数组长度决定,所以下面使用read(byte[] b)方法,而不使用上面的read()方法
            while((len = in.read(b))!=-1) {
                System.out.println(len);
//                System.out.println(in.read(b));//这里注释掉了,如果想了解len变量为什么要存在,可以取消注释,测试一下
                //new String(缓冲的数组,数组里面的字符串转换起始坐标,每次转换的长度),这里是把每次读取的数据转为字符串输出
                System.out.println(new String(b,0,len));    
            }
            //每次读取数据都要关闭输入流,因为流操作的时候凡是跨出虚拟机边界的资源都要求程序员自己关闭,不要指望垃圾回收。
            in.close();//你读一个文件,忘记关闭了流,你在操作系统里对这个文件的写,删除等操作就会报错,告诉你这个文件被某个进程占用。
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
View Code

 FileOutputStream 文件字节输出流

文件需要写进数据时,可以使用FileOutputStream类,下面举一个例子说明FileOutputStream类如何对文件写入内容。

import java.io.FileOutputStream;

public class Test5 {
    public static void main(String[] args) {
        //传一个文件过去,写进数据
        Test5.writeFile("D:/abc/abc.txt");
    }
    public static void writeFile(String absoluteFile) {
        //创建输出流
        try {
            FileOutputStream out = new FileOutputStream(absoluteFile);
            //创建要写入的数据
            String str = "abcdefghijklmnopkrstuvwxyz";
            //把数据写入到输出流,因为OutputStream是二级制流,所以str需要转换为Byte类型才能写入到输出流
            out.write(str.getBytes());//把数据写入到内存
            out.flush();//把数据从内存刷进硬盘
            //关闭输出流
            out.close();
            
            
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}
View Code

练习

把一个文件复制到另外一个目录去,利用FileInputStream和FileOutputStream来操作。

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Test6 {
    public static void main(String[] args) {
        //sAbsoluteFile是要获取的文件路径,前面变量名的s是start
        String sAbsoluteFile = "D:/abc/a.jpg";
        //eAbsoluteFile是要输出的文件路径,前面变量首字母e是end
        String eAbsoluteFile = "D:/abc/cc/sui.jpg";
        //把sAbsoluteFile文件复制到eAbsoluteFile中去
        Test6.copyFile(sAbsoluteFile, eAbsoluteFile);
    }
    /**
     * 复制文件到指定位置
     * @param sAbsoluteFile 源文件路径
     * @param eAbsoluteFile    复制到文件夹位置
     */
    public static void copyFile(String sAbsoluteFile,String eAbsoluteFile) {
        try {
            //创建输入流获取要复制的文件内容,也就是要读取的源文件
            FileInputStream in = new FileInputStream(sAbsoluteFile);
            //创建输出流,输出到指定位置,也就是要复制到哪里
            FileOutputStream out = new FileOutputStream(eAbsoluteFile);
            //创建接收的数组
            byte[] b = new byte[100];
            //创建临时变量接收返回每次读取文件的长度
            int len = 0;
            //循环读取数据,注意read()方法和read(byte[] b)的区别,一个是返回字节,一个是返回每次读取字节的个数
            while((len = in.read(b)) != -1) {
                //把读取到的数据通过输出流写入
                out.write(b, 0, len);//把数据写入到内存
            }
            //把内存中的数据刷入硬盘
            out.flush();
            //关闭输出/输入流
            out.close();
            in.close();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}
View Code

注意:文件字节流非常通用,可以用来操作字符的文档,还可以操作任何的其他类型文件(图片,压缩包等等),引用字节流直接使用二进制。字节流是通过二进制方式传输数据的。

FileReader文件字符输入流

字符输入流和字节输入流用法差不多,区别在于,一个是用于字节传输的方式,一个是字符传输的方式。唯一改变的就是临时数组字节流就采用byte[]数组,字符流就采用char[]数组。

最重要的区别:字节流可以传输类型比较多的文件,但是字符流只能传输文本文档,word文档虽然是文本文档,但是文档存在格式,虽然可以传输成功,但是文件会打不开。也就是只能支持txt类型文本。

import java.io.FileReader;

public class Test7 {
    public static void main(String[] args) {
        String inPath = "D:/abc/abc.txt";
        Test7.testFileReader(inPath);
    }
    
    /**
     *     文件字符输入流FileReader
     * @param inPath 源文件路径
     */
    public static void testFileReader(String inPath) {
        try {
            //创建文件字符输入流的对象
            FileReader fr = new FileReader(inPath);
            
            //创建临时存数据的字符数组
            char[] ch = new char[10];
            
            //定义临时变量存储每次读取数据的长度
            int len = 0;
            
            //循环遍历字符输入流
            while((len = fr.read(ch)) != -1) {
                System.out.println(new String(ch,0,len));
            }
            //关闭输入流
            fr.close();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
View Code

FileWriter文件字符输出流

字符输出流和字节输出流用法差不多,区别在于,一个是用于字节传输的方式,一个是字符传输的方式。

import java.io.FileWriter;

public class Test8 {
    
    public static void main(String[] args) {
        //传一个文件过去,写进数据
        Test8.TestFileWriter("D:/abc/abc.txt");
    }
    public static void TestFileWriter(String absoluteFile) {
        //创建输出流
        try {
            FileWriter out = new FileWriter(absoluteFile);
            //创建要写入的数据
            String str = "abcdefghijklmnopkrstuvwxyz";
            //把数据写入到输出流
            out.write(str);//把数据写入到内存
            out.flush();//把数据从内存刷进硬盘
            //关闭输出流
            out.close();
            
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
View Code

练习

把一个文件复制到另外一个目录去,利用FileReader和FileWriter来操作。

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

public class Test9 {
    public static void main(String[] args) {
        String inPath = "D:/abc/abc.txt";
        String outPath = "D:/abc/cc/ss.txt";
        Test9.copyFile(inPath, outPath);
    }

    /**
     * 
     * @param inPath  源文件路径
     * @param outPath 传输到指定目录
     */
    public static void copyFile(String inPath, String outPath) {
        FileReader fr = null;
        FileWriter fw = null;
        
        try {
            // 创建字符输入流
            fr = new FileReader(inPath);
            // 创建字符输出流
            fw = new FileWriter(outPath);
            
            //创建临时数组接收数据
            char[] ch = new char[10];//因为是字符流所以使用char数组
            int len = 0;
            
            //循环遍历读取文件,然后写入到内存中
            while((len = fr.read(ch)) != -1) {
                fw.write(ch,0,len);
            }
            //把内存的数据刷入硬盘
            fw.flush();
        
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //关闭流
            try {
                fw.close();
                fr.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
View Code

注意:

1、定义文件路径时,注意:可以用“/”或者“\”。

2、在写入一个文件时,如果目录下有同名文件将被覆盖。

3、在读取文件时,必须保证该文件已存在,否则出异常。

 缓冲流

 没有缓冲流的时候,IO流的操作是“ 读一点文件,然后写入文件,再读一点文件,然后再写入文件 “。

举个例子说明缓冲流的作用:比如送快递,寄快递不可能是有人寄出了,就马上送,而是在一个时间点内,把当天的全部快递统一装车运输,这里面运输快递的就是相当于缓冲流

  IO流都是计算机与硬盘之间发送的io操作,基于硬盘的读写相对式比较慢,这个操作的速度受到硬盘的读写速度的约束,为了能够提高读写速度,一定程度上绕过硬盘的限制,Java提供一种缓冲流来实现。

  缓冲流就是先把数据缓冲到内存里,在内存中去做io操作,基于内存的io操作大概能比基于硬盘的io操作快75000多倍.

缓冲流不同的类对应不同的IO流类,不可混合使用缓冲流类。

BufferedInputStream——FileInputStream

BufferedInputStream——FileOutputStream

BufferedReader——FileReader

BufferedWriter——FileWriter

BufferedInputStream

BufferedInputStream是字节缓冲输入流。

例子

import java.io.BufferedInputStream;
import java.io.FileInputStream;

public class Test {
    public static void main(String[] args) {
        Test.testBufferedInputStream();
    }
    
    /**
     * 缓冲字节输入流
     *     BufferedInputStream
     */
    public static void testBufferedInputStream() {
        try {
            //文件字节输入流
            FileInputStream in = new FileInputStream("E:\workspace-sts\Test1\src\org\chen\day13\tt.txt");
            //把文件字节输入流放到缓冲字节输入流,BufferedInputStream(InputStream类/子类)
            BufferedInputStream br = new BufferedInputStream(in);
            
            //创建临时数组接收数据,下面的注释就不写了,操作都差不多
            byte[] b = new byte[10];
            int len = 0;
            while((len = br.read(b)) != -1) {
                System.out.println(new String(b,0,len));
            }
            //关闭流的时候,本着一个最晚开的最早关,依次关闭
            br.close();//关闭缓冲流
            in.close();//关闭输入流
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
View Code

BufferedOutputStream

BufferedOutputStream是字节缓冲输出流。

例子

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;

public class Test2 {
    public static void main(String[] args) {
        Test2.testBufferedOutputStream();
    }
    
    /**
     * 缓冲字节输出流
     * BufferedOutputStream
     */
    public static void testBufferedOutputStream() {
        try {
        //创建输出流
        FileOutputStream out = new FileOutputStream("E:\workspace-sts\Test1\src\org\chen\day13\tt.txt");
        //缓冲字节输出流
        BufferedOutputStream bos = new BufferedOutputStream(out);
        
        //把内容写入到文件中,注意写入的文件如果名字一样,内容会被覆盖
        String str = "需要被写入的内容。。。。。谢谢谢谢谢谢";
        //写入到内存中,write方法只能写入字节,所以str转换byte写入
        bos.write(str.getBytes());
        
        bos.flush();//刷到硬盘上
        //先开后关原则。
        bos.close();
        out.close();
        
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}
View Code

练习

把一个文件复制到另外一个目录去,利用BufferedInputSream和BufferedOutputStream来操作。

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Test4 {
    public static void main(String[] args) {
        String inPath = "D:/abc/a.jpg";
        String outPath = "D:/abc/cc/tou.jpg";
        Test4.copyFile(inPath, outPath);
    }
    /**
     * 缓冲流实现文件的复制
     * @param inPath 源文件
     * @param outPath 复制到指定路径
     */
    public static void copyFile(String inPath,String outPath) {
        try {
        //创建输入流
        FileInputStream fs = new FileInputStream(inPath);
        //缓冲输入流
        BufferedInputStream bis = new BufferedInputStream(fs);
        
        //创建输出流
        FileOutputStream fo = new FileOutputStream(outPath);
        //缓冲输出流
        BufferedOutputStream bos = new BufferedOutputStream(fo);
        
        //临时数组
        byte[] b = new byte[10];
        int len = 0 ;
        //循环读取缓冲输入流
        while((len = bis.read(b)) != -1) {
            //读取的数据写入到缓冲输出流
            bos.write(b,0,len);
        }
        //刷到硬盘中
        bos.flush();
        //先开后关原则
        bos.close();
        fo.close();
        bis.close();
        fs.close();    
        
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}
View Code

BufferedReader

BufferedReader是字符缓冲输入流。

例子

import java.io.BufferedReader;
import java.io.FileReader;
/**
 * 缓冲字符流
 * BufferedReader
 * @author leak
 *
 */
public class Test5 {
    public static void main(String[] args) {
        String file = "D:/abc/abc.txt";
        Test5.testBufferedReader(file);
    }
    /**
     * 
     * @param file 源文件 
     * 
     */
    public static void testBufferedReader(String file) {
        try {
            //创建字符输入流
            FileReader fr = new FileReader(file);
            //创建字符缓冲流
            BufferedReader br = new BufferedReader(fr);
            //创建临时数组
            char[] ch = new char[10];
            int len = 0;
            while((len = br.read(ch)) != -1) {
                System.out.println(new String(ch,0,len));
            }
            //关闭流
            br.close();
            fr.close();
            
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}
View Code

BufferedWriter

BufferedWriter是字符缓冲输出流。

例子

import java.io.BufferedWriter;
import java.io.FileWriter;

/**
 * 字符缓冲输出流
 * BufferedWriter
 * @author leak
 *
 */
public class Test6 {
    public static void main(String[] args) {
        String file = "D:/abc/abc.txt";
        Test6.testBufferedWriter(file);
    }
    
    public static void testBufferedWriter(String file) {
        try {
            //创建字符输出流
            FileWriter fw = new FileWriter(file);
            //字符缓冲流
            BufferedWriter bw = new BufferedWriter(fw);
            String str = "需要录入的 字符。。。";
            bw.write(str);//把内容写进内存
            bw.flush();//把数据刷入硬盘
            //先开后关原则,先开的字符输出流,最后才关字符输出流
            bw.close();
            fw.close();
            
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}
View Code

练习

把一个文件复制到另外一个目录去,利用BufferedReader和BufferedWriter来操作。

注意:字符流只能操作文本文档txt,其他类型文件虽然可以传输成功,但是会打不开使用字符流传输后的文件。

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;

public class Test7 {
    public static void main(String[] args) {
        String inPath = "D:/abc/abc.txt";
        String outPath = "D:/abc/cc/add.txt";
        Test7.copyFile(inPath, outPath);
    }

    /**
     * 文件复制
     * 
     * @param inPath  源文件
     * @param outPath 复制到指定的路径
     */
    public static void copyFile(String inPath, String outPath) {
        try {
            // 创建字符输入流
            FileReader fr = new FileReader(inPath);
            // 字符缓冲流
            BufferedReader br = new BufferedReader(fr);
            // 创建字符输出流
            FileWriter fw = new FileWriter(outPath);
            //字符缓冲流
            BufferedWriter bw = new BufferedWriter(fw);
            
            //临时数组
            char[] ch = new char[100];
            int len = 0;
            
            //循环读取数据
            while((len = br.read(ch)) != -1) {
                bw.write(ch,0,len);//把数据写入内存
            }
            bw.flush();//刷入硬盘
            //关闭流
            bw.close();
            fw.close();
            br.close();
            fr.close();    
            
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}
View Code

转换流

转换流提供了在字节流和字符流之间的转换
Java API提供了两个转换流:
InputStreamReader和OutputStreamWriter
字节流中的数据都是字符时,转成字符流操作更高效。


InputStreamReader  

用于将字节流中读取到的字节按指定字符集解码成字符。需要和 InputStream“套接”。
构造方法
InputStreamReader(InputStream in)
public InputSreamReader(InputStream in,String charsetName)
如: Reader isr = new InputStreamReader(System.in,”ISO5334_1”);

例子

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.InputStreamReader;

public class Test8 {
    public static void main(String[] args) {
        /**
         * 所有文件都有编码格式
         * 对我们来说,txt和Java文件一般都有三种编码
         * ISO8859-1,西欧编码,是纯英文编码,不适应汉字
         * GBK和UTF-8,这两编码适用于中文和英文
         * 一般使用UTF-8编码
         */
        String inPath = "D:/abc/abc.txt";
        String charsetName = "UTF-8";
        Test8.testInputStreamReader(inPath, charsetName);
    }
    /**
     * 字节输入流转换字符输入流
     * @param inPath 源文件
     * @param charsetName 编码格式
     * 注意,在转换字符流的时候,设置的字符集编码要与读取文件的数据的编码一致
     * 不然会出现乱码,比如,你要读取的文件编码是GBK,但是你转为字符流设置了UTF-8,那么读取的内容就会乱码 显示
     */
    public static void testInputStreamReader(String inPath, String charsetName) {
        try {
            // 字节输入流
            FileInputStream fs = new FileInputStream(inPath);
            // 字节流转换字符流 
            InputStreamReader isr = new InputStreamReader(fs, charsetName);//参数1是字节流,参数2编码
            // 缓冲流
            BufferedReader br = new BufferedReader(isr);
            // 临时数组
            char[] ch = new char[100];
            int len = 0;

            while ((len = br.read(ch)) != -1) {
                System.out.println(new String(ch, 0, len));
            }
            //关闭流
            br.close();
            isr.close();
            fs.close();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
View Code

OutputStreamWriter

用于将要写入到字节流中的字符按指定字符集编码成字节。 需要和OutputStream“套接”。
构造方法
public OutputStreamWriter(OutputStream out)
public OutputSreamWriter(OutputStream out,String charsetName)
例子

import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;

public class Test9 {
    public static void main(String[] args) {
        String inPath = "D:/abc/abc.txt";
        String charsetName = "UTF-8";
        Test9.testOutputStreamWirter(inPath, charsetName);
        
    }
    
    /**
     * 字节输出流转字符输出流
     * @param inPath 源文件
     * @param charsetName 编码格式
     */
    public static void testOutputStreamWirter(String inPath,String charsetName) {
        try {
        //创建字节流
        FileOutputStream fs = new FileOutputStream(inPath);
        //转换流
        OutputStreamWriter os = new OutputStreamWriter(fs,charsetName);
        //缓冲流
        BufferedWriter bw = new BufferedWriter(os);
        String str = "需要写入的数据。嘻嘻嘻嘻嘻嘻嘻嘻";
        bw.write(str);//写入内存
        //刷入硬盘
        bw.flush();
        //关闭流
        bw.close();
        os.close();
        fs.close();
        
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}
View Code

标准输入输出流

System.in和System.out分别代表了系统标准的输入和输出设备。

默认输入设备是键盘,输出设备是显示器

System.in的类型是InputStream

System.out的类型是PrintStream,其是OutputStream的子类FileOutputStream的子类

System.in输入流的例子, System.out就不演示了,就输出语句,经常使用。

import java.io.BufferedReader;
import java.io.InputStreamReader;

/**
 * 
 * @author leak
 *    标准输入和输出流
 */
public class Test10 {
    public static void main(String[] args) {
        Test10.testSystemIn();
    }
    /**
     * 标准的输入流
     */
    public static void testSystemIn() {
        try {
            //创建一个接收键盘输入数据的输入流
            InputStreamReader ir = new InputStreamReader(System.in);
            //缓冲流
            BufferedReader br = new BufferedReader(ir);
            //创建临时接收数据的变量,因为是字符缓冲流,所以使用String
            String str = "";
            //循环读取键盘输入的数据,每读取一次就放到str变量中,知道读取完数据,就会返回null
            //注意readLine()是读取一行的数据,以回车符结束当前读取的数据
            while((str = br.readLine()) != null) {
                System.out.println(str);
            }
            
            br.close();
            ir.close();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}
View Code

补充:例子为什么使用转换流接收System.in呢,因为System.in是输入二进制数据就是字节,而输入的是纯文字,所以使用转换流处理文字,当然也可以使用InputStream接收System.in,但是不能使用字符输入流接收System.in,因为字符流是不能直接接收System.in的二进制数据,要使用InputStreamReader转换流。

练习

1、把控制台输入的内容写到指定的TXT文件中,当接收到字符串over,就结束程序的运行。

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.InputStreamReader;

//把控制台输入的内容写到指定的TXT文件中,当接收到字符串over,就结束程序的运行。
public class Test11 {
    public static void main(String[] args) {
        String fileName = "D:/abc/abc.txt";
        Test11.inWrite(fileName);
    }
    
    public static void inWrite(String fileName) {
        try {
            // 创建输入流
            System.out.println("请输入要写入文件的内容,如果结束输入,请回车后输入 over,再回车结束程序:");
            InputStreamReader fr = new InputStreamReader(System.in);
            //缓冲流
            BufferedReader br = new BufferedReader(fr);
            
            //创建输出流
            FileWriter ow = new FileWriter(fileName);
            //缓冲流
            BufferedWriter bw = new BufferedWriter(ow);
            //创建临时变量接收每次读取一行的数据。
            String str = "";
            
            
            //循环读取键盘输入
            //readLine读取一行数据,遇到回车符返回一行数据
            while((str = br.readLine()) != null) {
                //读取到over结束写入的数据
                if("over".equals(str)) {
                    break;
                }
                else {
                    bw.write(str);//写入内存
                }
            }
            bw.flush();//刷入硬盘
            //关闭流
            bw.close();
            ow.close();
            br.close();
            fr.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
View Code

2、在一个TXT文件中,写一组用户名和密码,通过控制台输入用户名和密码,与TXT文件中的用户名密码做对比,如果一样就在打印登录成功,如果不一致,就打印用户名密码错误。

 注意:我这里的方法仅限于txt文档里面的数据格式是{key:value},{key:value} 形式,最后一个键值对后面不能有逗号,存储方式和json二维数组差不多,只是没了双引号把key和value括起来。

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Test12 {
    /**
     * 
     * 在一个TXT文件中,写一组用户名和密码,通过控制台输入用户名和密码,
     * 与TXT文件中的用户名密码做对比,如果一样就在打印登录成功,
     * 如果不一致,就打印用户名密码错误。
     */
    public static void main(String[] args) {
        String fileName = "D:/abc/rename.txt";
        Test12.login(fileName);
        
    }
    
    /**
     *  该方法获取数据的前提:
     *  txt内容格式必须是{key:value},{key:value}形式存放,
     *  最后一个键值对,后面不能留逗号,类似json二维数组格式,但是没有""双引号
     *    核心是字符串截取,这里把下面输出语句都注释了,如果看不懂字符串截取代码,可以取消注释看输出
     */
    public static void login(String fileName) {
            //创建标准输入流
            Scanner in = new Scanner(System.in);
            //之所以放在try外面,是为了在finally后面关闭流
            FileReader fw = null;
            BufferedReader bw = null;
        try {
            
            //创建输入流接收文件数据
            fw = new FileReader(fileName);
            
            //缓冲流
            bw = new BufferedReader(fw);
            //存放账号密码的键值对
            Map<String,String> data = new HashMap<String,String>();
            
            //读取文件一行数据,每循环一次,就读一行
            String str = "";
            //循环读取文件
            while((str = bw.readLine()) != null) {
                //文件每行的键值对
                System.out.println(str);
                //分隔的索引位置
                int index = -1;
                
                //遍历判断是否到最后的键值对,如果到最后返回-1
                while((index = str.indexOf("}")) != -1){
                    //str的前一部分,比如{account:123,pw:aaa
                    //从1开始就截取不到{号了,index是}号,但是substring不包含结束,所以只截取{account:123,pw:aaa
                    String leadStr = str.substring(1,index);//这里每次截取,只截取了{}里面的数据
//                    System.out.println(leadStr+"截取到");
                    
                    //split(",|:")多字符同时切割,首先根据逗号切割成 两个键值对,比如account:789  pw:ccc
                    //然后再根据冒号切割成键值对,变成了4个单独的字符串,比如   account,789,pw,ccc  account是0,依次类推
                    String[] keyValue = leadStr.split(",|:");
//                    System.out.println("账号的value:"+keyValue[1]+",密码的value:"+keyValue[3]);
                    
                    //最后把账号和密码的value放进map集合
                    data.put(keyValue[1], keyValue[3]);
                    
                    //截取str的后一部分,就是剩下的字符串
                    //不过要进行判断,如果到了最后一个键值对,就不截取后面的,因为没有字符串可截取,再强行截取会越界
                    if(str.contains(",{")) {//这里判断的是是否到了最后一个键值对
                        
                        //截取str的后一部分,就是剩下的字符串
                        //index+2是因为index是}号,所以+2就到了{号,为什么不+3呢,直接取括号里面的数据,因为上面str截取前一部分从1开始,所以要保留{号
                        str = str.substring(index+2,str.length());
                        //假设这里已经是最后一个键值对,比如{account:789,pw:ccc},
                        //那么再次循环,就已经不包含,{ 所以就不截取,跳到else停止截取
//                        System.out.println("截取完后的str"+str);
                    }
                    else {
                        break;
                    }
                }
            }
            
            
            while(true) {
                //接收用户输入的账号,密码
                System.out.println("请输入账号:");
                String account = in.nextLine();
                
                System.out.println("请输入密码:");
                String pw = in.nextLine();
                
                //根据get(account)根据key获取value,然后拿value比较输入的pw,然后正确登录成功
                //如果get()获取不到账号,会返回字符串null,对比pw也会不正确
                if(pw.equals(data.get(account))) {
                    System.out.println("登录成功");
                    break;
                }
                else {
                    System.out.println("账号或密码错误");
                }
            }
            
        }catch(Exception e) {
            e.printStackTrace();
        }finally {
            //关闭流
            try {
                in.close();
                bw.close();
                fw.close();
            }catch(IOException e) {
                e.printStackTrace();
            }
        }
    }
}
View Code

打印流

PrintStream参数列表

PrintStream是FilterOutputStream的子类,顶层父类是OutputStream字节输出流。

复习:输入流和输出流的区别,因为程序是运行在内存中,以内存角度来理解输入输出概念。一般以程序作为中心,所以从程序写入数据到其他位置,则是输出流,将数据读入程序中则是输入流

平常我们常用的System.out语句返回的是PrintStream输出类型,也就是把信息输出。

System.out的print有多种重载方法,接收多种类型数据,然后把数据输出到控制台显示。

 数据流

 例子

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Test13 {
    public static void main(String[] args) {
        Test13.testDataOutputStream();
        Test13.testDataInputStream();
    }
    /**
     * 数据输出流
     * DataOutputStream
     * DataOutputStream()需要一个OutputStream或者其子类。
     *     用数据输出流写到文件中的基本数据类型的数据,是乱码的,不能直接辨认出来,需要数据的输入流读取。
     */
    public static void testDataOutputStream() {
        try {
            DataOutputStream out = new DataOutputStream(new FileOutputStream("D:/abc/abc.txt"));
//            out.writeBoolean(true);//把true写入文件,以字节流的方式,但是文件内容会显示乱码。
            out.writeDouble(1.35d);//也是乱码
//            out.writeInt(100);//文件内容也是乱码
            
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();

        }
    }
    
    /** 数据输入流
     * 用数据输出流写到文件中的基本数据类型的数据,是乱码的,不能直接辨认出来,需要数据的输入流读取。
     * 用数据输入流读取数据输出流写到文件中的数据时,要保证使用和当前写入的数据类型一致的类型来读取,否则读取也会乱码,如果读取的数据类型不存在,会报EOFException异常
     * 也就是说,如果写的时候是writeDouble,那么读的时候就的是readDouble
     * DataInputStream
     */
    public static void testDataInputStream() {
        try {
            DataInputStream in = new DataInputStream(new FileInputStream("D:/abc/abc.txt"));
            
//            System.out.println(in.readInt());
            System.out.println(in.readDouble());
            in.close();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}
View Code

对象流

 

 比如一个person对象

1. 把这个对象存到电脑的硬盘上,硬盘是使用二进制存储的,那么就需要把对象转化为二进制的字节流,把这个流保存到电脑上。要使用这个对象,就需要把流转换为对象再使用。

2. 把这个对象通过网络传到另一个机器上,网络通信的基础也是二进制,也需要把对象转为二进制的数据流,把这个流通过网络进行传输,接收者如果要使用该对象,要先把对象的流转换为对象。

序列化:就是把对象通过数据流的方式存储到硬盘上,这个过程叫做对象的序列化。

反序列化:就是把硬盘上的对象通过数据流转换为对象使用,这个过程就是反序列化,序列化和反序列化是相反概念。

持久化:就是把对象永久的存储到硬盘上,持久化和序列化有点相似,但是还是不同的,一个注重过程,一个注重结果

正是因为保存对象到硬盘(对象的持久化)和对象的网络传输,需要做的两件事,就产生了对象的输入与输出流。

注意:序列化也反序列化针对的是对象的各种属性,不包括类的属性,也就是static那些。

对象的序列化和反序列化

反序列化没什么可讲的,就是把硬盘存储的对象通过数据流转换为对象使用。

例子

//需要序列化的类
import java.io.Serializable;

/**
 *     可以序列化与反序列化的对象
 * @author leak
 *
 */
public class Person implements Serializable{

    /**
     * 一个表示序列化版本标识符的静态变量
     * 表明类的不同版本间的兼容性
     */
    private static final long serialVersionUID = 1L;
    String name;
    int age;
}

//测试类的对象序列化和反序列化
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * 序列化和反序列化
 * 注意:对象的序列化和反序列化使用的类要严格一致,包名,类名,类结构等待所有都要一致
 * 比如,你序列化了一个day13包下的Person类,但是你反序列化时,使用的是day12包下的Person类,会报类型转换异常,哪怕类结构一致。
 * 所以,只要序列化和反序列化的对象前后的任何东西发生改变,都会报异常。
 * 也就是你序列化的对象,反序列化时,必须保证这个反序列化的对象是之前序列化的对象,而且这个对象不能有任何改动
 * @author leak
 *
 */
public class Test14 {

    public static void main(String[] args) {
        // 序列化
        Test14.testSerialize();
        // 反序列化
        Test14.testDeserialize();
    }

    /**
     * 序列化方法
     */
    public static void testSerialize() {
        try {
            // 定义对象的输出流,把对象序列化之后的流指定的文件中
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("D:/abc/a.txt"));

            Person p = new Person();
            p.name = "猪头";
            p.age = 22;

            // 把对象通过数据流存储到文件中,注意只是做存储的作用,是以二进制存储的,所以查看内容是乱码
            out.writeObject(p);
            out.flush();
            out.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 对象的反序列化
     */
    public static void testDeserialize() {
        try {
            // 创建对象输入流对象,从指定的文件中把对象反序列化读取出来
            ObjectInputStream in = new ObjectInputStream(new FileInputStream("D:/abc/a.txt"));

            Object obj = in.readObject();
            Person p = (Person) obj;
            System.out.println(p.name + p.age);

            in.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
View Code

 RandomAccessFile类

 例子

import java.io.RandomAccessFile;

/**
 * 文件的随机读取 程序可以直接跳到文件的任意地方来读,写文件
 * @author leak
 *
 */
public class Test15 {
    public static void main(String[] args) {
        Test15.testRandomAccessFileRead();
        Test15.testRandomAccessFileWrite();
    }

    /**
     * 随机读文件
     */
    public static void testRandomAccessFileRead() {
        // RandomAccessFile的构造有两个参数,参数1是读写文件的路径
        /** 参数2是指定RandomAccessFile的访问模式
         *     r:以只读方式打开 
         *     rw:打开以便读取和写入
         *  rwd:打开以便读取和写入;同步文件内容的更新 
         *  rws:打开以便读取和写入;同步文件内容和元数据的更新
         */
        try {
            //RandomAccessFile是字节流读取
            RandomAccessFile ra = new RandomAccessFile("D:/abc/abc.txt", "r");
            
            //每一行后面还有两个隐藏的符,/t/n,来达到从文件的任意位置读取
            ra.seek(0);//设置读取文件内容的起始点
            
            //因为是字节流,所以使用byte数组
            byte[] b = new byte[1024];
            
            int len = -1;
            //循环读取文件
            while((len = ra.read(b)) != -1) {
                //把字节流通过new String方法把内容打印
                System.out.println(new String(b,0,len));
            }
            ra.close();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 文件随机写
     */
    public static void testRandomAccessFileWrite() {
        try {
            
            RandomAccessFile ra = new RandomAccessFile("D:/abc/abc.txt", "rw");
            
            //注意:如果是在文件开头或中间的某个位置开始写的话,就会用写的内容覆盖等长度的原内容
            ra.seek(0);//设置写的起始点,0代表从开头写
//            ra.seek(ra.length());//设置写的起始点,ra.length()代表从文件的结尾写,也就是文件的追加
            
            ra.write("你好".getBytes());//把字符转换byte写进去
            
            ra.close();
        }catch(Exception e) {
            e.printStackTrace();
        }
    }
}
View Code

流的基本应用小节

流是用来处理数据的。

处理数据时,一定要先明确数据源,与数据目的地
数据源可以是文件,可以是键盘。
数据目的地可以是文件、显示器或者其他设备。

而流只是在帮助数据进行传输,并对传输的数据进行处理,比如过滤处理、转换处理等。

IO流概括

原文地址:https://www.cnblogs.com/unlasting/p/12716644.html