java基础之IO流

63 篇文章 0 订阅

一、 File类

1、File类简介

File 类 就是当前系统中 文件或者文件夹的抽象表示
    
通俗的讲  就是 使用File对象 才操作我们电脑系统中的文件或者文件夹

学习File类 其实就是学习 如果通过file对象 对系统中的文件/文件夹进行增删改查

2、file类的使用

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

常用构造器

public File(String pathname)
以pathname为路径创建File对象,可以是 绝对路径或者相对路径,如果pathname是相对路径,则默认的当前路径在系统属性user.dir中存储。

  • 绝对路径:是一个固定的路径,从盘符开始
  • 相对路径:是相对于某个位置开始

路径分隔符

路径中的每级目录之间用一个路径分隔符隔开。
路径分隔符和系统有关:

  • windows和DOS系统默认使用“\”来表示
  • UNIX和URL使用“/”来表示

Java程序支持跨平台运行,因此路径分隔符要慎用。
为了解决这个隐患,File类提供了一个常量:
public static final String separator。根据操作系统,动态的提供分隔符。

举例:

File file1 = new File("d:\\a\\a.txt");
File file2 = new File("d:" + File.separator + "a" + File.separator + "a.txt");
File file3 = new File("d:/a");

 常用方法

File类的获取功能

  •     public String getAbsolutePath():获取绝对路径
  •     public String getPath() :获取路径
  •     public String getName() :获取名称
  •     public String getParent():获取上层文件目录路径。若无,返回null
  •     public long length() :获取文件长度(即:字节数)。不能获取目录的长度。
  •     public long lastModified() :获取最后一次的修改时间,毫秒值
  •     public String[] list() :获取指定目录下的所有文件或者文件目录的名称数组
  •     public File[] listFiles() :获取指定目录下的所有文件或者文件目录的File数组

File类的重命名功能

  •     public boolean renameTo(File dest):把文件重命名为指定的文件路径

File类的判断功能

  •     public boolean isDirectory():判断是否是文件目录
  •     public boolean isFile() :判断是否是文件
  •     public boolean exists() :判断是否存在
  •     public boolean canRead() :判断是否可读
  •     public boolean canWrite() :判断是否可写
  •     public boolean isHidden() :判断是否隐藏

File类的创建功能

  •     public boolean createNewFile() :创建文件。若文件存在,则不创建,返回false
  •     public boolean mkdir() :创建文件目录。如果此文件目录存在,就不创建了。如果此文件目录的上层目录不存在,也不创建。
  •     public boolean mkdirs() :创建文件目录。如果上层文件目录不存在,一并创建注意事项:如果你创建文件或者文件目录没有写盘符路径,那么,默认在项目路径下。
  • File类的删除功能
  • public boolean delete():删除文件或者文件夹
     

删除注意事项:
Java中的删除不走回收站。
要删除一个文件目录,请注意该文件目录内不能包含文件或者文件目录

二、IO流

 Java IO原理

I/O是Input/Output的缩写, I/O技术是非常实用的技术,用于处理设备之间的数据传输。如读/写文件,网络通讯等。
Java程序中,对于数据的输入/输出操作以“流(stream)” 的方式进行。
java.io包下提供了各种“流”类和接口,用以获取不同种类的数据,并通过标准的方法输入或输出数据。
输入input:读取外部数据(磁盘、光盘等存储设备的数据)到程序(内存)中。
输出output:将程序(内存)数据输出到磁盘、光盘等存储设备中。

流的分类

按操作数据单位不同分为:字节流(8 bit),字符流(16 bit)
按数据流的流向不同分为:输入流,输出流
按流的角色的不同分为:节点流,处理流

(抽象基类)字节流字符流
输入流InputStreamReader
输出流OutputStreamWriter
  1. Java的IO流共涉及40多个类,实际上非常规则,都是从如下4个抽象基类派生的。
  2. 由这四个类派生出来的子类名称都是以其父类名作为子类名后缀。

 字节输出流

OutputStream & Writer

OutputStream 和 Writer 也非常相似:

 void write(int b/int c);
 void write(byte[] b/char[] cbuf);
 void write(byte[] b/char[] buff, int off, int len);
 void flush();
 void close(); 需要先刷新,再关闭此流

因为字符流直接以字符作为操作单位,所以 Writer 可以用字符串来替换字符数组,即以 String 对象作为参数

  •     void write(String str);
  •     void write(String str, int off, int len);
  •     FileOutputStream 从文件系统中的某个文件中获得输出字节。FileOutputStream 用于写出非文本数据之类的原始字节流。要写出字符流,需要使用 FileWriter
     

OutputStream

  •     void write(int b)

    将指定的字节写入此输出流。write 的常规协定是:向输出流写入一个字节。要写入的字节是参数 b 的八个低位。b 的 24 个高位将被忽略。 即写入0~255范围的。

  •     void write(byte[] b)

    将 b.length 个字节从指定的 byte 数组写入此输出流。write(b) 的常规协定是:应该与调用 write(b, 0, b.length) 的效果完全相同。

  •     void write(byte[] b,int off,int len)

    将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此输出流。

  •     public void flush()throws IOException

    刷新此输出流并强制写出所有缓冲的输出字节,调用此方法指示应将这些字节立即写入它们预期的目标。

  •     public void close() throws IOException

    关闭此输出流并释放与该流关联的所有系统资源。

Writer

  •     void write(int c)

    写入单个字符。要写入的字符包含在给定整数值的 16 个低位中,16 高位被忽略。 即
    写入0 到 65535 之间的Unicode码。

  •     void write(char[] cbuf)

    写入字符数组。

  •     void write(char[] cbuf,int off,int len)

    写入字符数组的某一部分。从off开始,写入len个字符

  •     void write(String str)

    写入字符串。

  •     void write(String str,int off,int len)

    写入字符串的某一部分。

  •     void flush()

    刷新该流的缓冲,则立即将它们写入预期目标。

  •     public void close() throws IOException

    关闭此输出流并释放与该流关联的所有系统资源。

第一种方式
    @Test
    public void test5() throws Exception{

        /*1 创建流*/
        FileOutputStream fileOutputStream = new FileOutputStream("D:/aaa/666.txt");

        //2 输出操作
        fileOutputStream.write(100);
        fileOutputStream.write(101);
        fileOutputStream.flush();
        /*3 关闭流*/
        fileOutputStream.close();

    }

调用write函数 参数是int类型   代表将int类型对应的char字符写出

第二种方式    byte数组形式
     @Test
    public void test5() throws Exception{

        /*1 创建流*/
        FileOutputStream fileOutputStream = new FileOutputStream("D:/aaa/666.txt");

        //2 输出操作

        byte[] data = "asdlkfjalksdjflksadjf;ljaksdf  world".getBytes();
        fileOutputStream.write(data);

        fileOutputStream.flush();
        /*3 关闭流*/
        fileOutputStream.close();

    }

第三种方式   局部byte数组
     @Test
    public void test5() throws Exception{

        /*1 创建流*/
        FileOutputStream fileOutputStream = new FileOutputStream("D:/aaa/666.txt");

        //2 输出操作

        byte[] data = "abcdefghijklmnopqrstuvwxyz".getBytes();
        fileOutputStream.write(data,3,6);

        fileOutputStream.flush();
        /*3 关闭流*/
        fileOutputStream.close();

    }

追加操作
     @Test
    public void test5() throws Exception{

        /*1 创建流*/
        FileOutputStream fileOutputStream = new FileOutputStream("D:/aaa/666.txt",true);

        //2 输出操作

        byte[] data = "abcdefghijklmnopqrstuvwxyz".getBytes();
        fileOutputStream.write(data,4,4);

        fileOutputStream.flush();
        /*3 关闭流*/
        fileOutputStream.close();

    }

字节输入流  

InputStream 和 Reader 是所有输入流的基类。
InputStream(典型实现:FileInputStream)

  • int read()
  • int read(byte[] b)
  • int read(byte[] b, int off, int len)

Reader(典型实现:FileReader)

  • int read()
  • int read(char [] c)
  • int read(char [] c, int off, int len)

程序中打开的文件 IO 资源不属于内存里的资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件 IO 资源。

FileInputStream 从文件系统中的某个文件中获得输入字节。FileInputStream 用于读取非文本数据之类的原始字节流。要读取字符流,需要使用 FileReader


InputStream

  • int read()

从输入流中读取数据的下一个字节。返回 0 到 255 范围内的 int 字节值。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。

  • int read(byte[] b)

从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。如果因为已经到达流末尾而没有可用的字节,则返回值 -1。否则以整数形式返回实际读取的字节数。

  • int read(byte[] b, int off,int len)

将输入流中最多 len 个数据字节读入 byte 数组。尝试读取 len 个字节,但读取的字节也可能小于该值。以整数形式返回实际读取的字节数。如果因为流位于文件末尾而没有可用的字节,则返回值 -1。

  • public void close() throws IOException

关闭此输入流并释放与该流关联的所有系统资源。

Reader

  •     int read()

    读取单个字符。作为整数读取的字符,范围在 0 到 65535 之间 (0x00-0xffff)(2个字节的Unicode码),如果已到达流的末尾,则返回 -1

  •     int read(char[] cbuf)

    将字符读入数组。如果已到达流的末尾,则返回 -1。否则返回本次读取的字符数。

  •     int read(char[] cbuf,int off,int len)

    将字符读入数组的某一部分。存到数组cbuf中,从off处开始存储,最多读len个字符。如果已到达流的末尾,则返回 -1。否则返回本次读取的字符数。

  •     public void close() throws IOException

    关闭此输入流并释放与该流关联的所有系统资源。

方式一  直接调用  read()  函数  返回为读取的一个字节信息
    @Test
    public void test6() throws Exception {

        // 1 创建流
        InputStream in = new FileInputStream("D:/aaa/666.txt");


        // 2 操作
        int read = in.read();
        int read1 = in.read();

        System.out.println(read  +  "----"  +read1);

        // 3 关闭流
        in.close();
    }


方式二   直接调用 read(byte[]) 函数
    
 @Test
    public void test6() throws Exception {

        // 1 创建流
        InputStream in = new FileInputStream("D:/aaa/666.txt");


        // 2 操作
        byte[]  data = new byte[16];
        int read = in.read(data);

        System.out.println(read);
        System.out.println(Arrays.toString(data));


        // 3 关闭流
        in.close();

    }

26
[65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

此时 会将本次读取的数据 存储到 byte数组中  并且返回当前读取的个数
问题: 数组定义多少容量合适?

方式三  
@Test
    public void test6() throws Exception {

        // 1 创建流
        InputStream in = new FileInputStream("D:/aaa/666.txt");


        // 2 操作
        byte[] data = new byte[50];
        int read = in.read(data, 5, 8);

        System.out.println(read);
        System.out.println(Arrays.toString(data));

        // 3 关闭流
        in.close();

    }

8
[0, 0, 0, 0, 0, 65, 66, 67, 68, 69, 70, 71, 72, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
注意  第二个参数代表初始索引  第三个参数代表 长度   整体代表 数组中 哪些位置可以用来存储数据

    
 将数组中的内容转换成原始内容
 @Test
    public void test6() throws Exception {

        // 1 创建流
        InputStream in = new FileInputStream("D:/aaa/666.txt");


        // 2 操作
        byte[] data = new byte[70];
        int read = in.read(data );

        // 将byte数组转换成字符串
        String s = new String(data ,  0 ,  read);
        System.out.println(s);

        // 3 关闭流
        in.close();

    }    
 
循环读取大文件数据
    @Test
    public void test6() throws Exception {

        // 1 创建流
        InputStream in = new FileInputStream("D:/aaa/666.txt");


        // 2 操作   26
        byte[] data = new byte[4];

        /*用来记录每次读取的长度*/
        int  len = -1;

        StringBuilder  sb = new StringBuilder();

        //  1  in.read(data) 读取数据到数组中  2  本次读取的长度 赋值给 len  3 判断len是否大于0
        while(      (len =  in.read(data)  )         >  0       ){

            String s = new String(data, 0, len);
            sb.append(s);

        }

        System.out.println(sb);

        // 3 关闭流
        in.close();

    }  

异常处理

1 将异常转移的代码干掉 换成异常捕获
   /**
     * FileNotFoundException  IOException
     */
    @Test
    public void test7() {

        try{
            FileInputStream in = new FileInputStream("D:/datasource/jdk_8.0.1310.11_64.exe");
            FileOutputStream out = new FileOutputStream("D:/jdk8.exe");


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

            while(    (len = in.read(data))  >  0  ){
                out.write(data,0,len);
            }

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

    }
此时代码虽然不报错 但是有问题,关闭资源的代码 在try中,如果中途出现异常 会出现 资源打开但是没有关闭的情况
我们的资源无论有没有问题 都需要关闭。
    
2  将关闭资源的代码写到 finally 中 
 @Test
    public void test7() {

        try{
            FileInputStream in = new FileInputStream("D:/datasource/jdk_8.0.1310.11_64.exe");
            FileOutputStream out = new FileOutputStream("D:/jdk8.exe");


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

            while(    (len = in.read(data))  >  0  ){
                out.write(data,0,len);
            }


        }catch (Exception  e){
            e.printStackTrace();
        }finally {
            in.close();
            out.close();
        }

    }
此时  in和out报错 错误信息为: Cannot resolve symbol 'in'   使用了未定义的变量
in 和  out的定义在 try中 ,局部变量的作用域 此时从定义开始 到 所在结束大括号
    
3 所以我们需要 in和out的定义在try外面 赋值在里面 保证了 作用域能达到 并且 异常在try中处理
 @Test
    public void test7() {
        FileInputStream in =  null;
        FileOutputStream out = null;

        try{

            in =  new FileInputStream("D:/datasource/jdk_8.0.1310.11_64.exe");
            out = new FileOutputStream("D:/jdk8.exe");

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

            while(    (len = in.read(data))  >  0  ){
                out.write(data,0,len);
            }

        }catch (Exception  e){
            e.printStackTrace();
        }finally {

            in.close();
            out.close();

        }

    }
此时 close() 代码报错 : Unhandled exception: java.io.IOException  
 也就是说 close中也有 非运行时异常 也需要处理
    
 4 在finally 中 通过 try-catch 处理 close的异常
public void test7() {
        FileInputStream in =  null;
        FileOutputStream out = null;
        try{

            in =  new FileInputStream("D:/datasource/jdk_8.0.1310.11_64.exe");
            out = new FileOutputStream("D:/jdk8.exe");

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

            while(    (len = in.read(data))  >  0  ){
                out.write(data,0,len);
            }
        }catch (Exception  e){
            e.printStackTrace();
        }finally {

            try {
                in.close();
                out.close();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
此时注意 虽然看着不出问题了 但是有隐藏问题  如果 in和out在没有赋值之前就崩了 此时会导致 catch进行捕获 最终到达fianlly 执行关闭代码 但是 in和out还是个 null  就会出现空指针异常
    
5 所以我们需要在关闭之前添加 非空判断
    @Test
    public void test7() {
        FileInputStream in =  null;
        FileOutputStream out = null;
        try{
            int i = 1/0;
            in =  new FileInputStream("D:/datasource/jdk_8.0.1310.11_64.exe");
            out = new FileOutputStream("D:/jdk8.exe");

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

            while(    (len = in.read(data))  >  0  ){
                out.write(data,0,len);
            }
        }catch (Exception  e){
            e.printStackTrace();
        }finally {

            try {
                if(in != null) {
                    in.close();
                }
                if(out != null){
                    out.close();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }   
    
此时看上去没有问题 但是依然有隐藏问题: 如果 in在关闭的时候 出现异常 此时会导致 out关闭无法正常执行 
    
6 在in 的 try之后的finally中再去关闭 out
@Test
    public void test7() {
        FileInputStream in =  null;
        FileOutputStream out = null;
        try{
            in =  new FileInputStream("D:/datasource/jdk_8.0.1310.11_64.exe");
            out = new FileOutputStream("D:/jdk8.exe");
            byte[] data = new byte[1024];
            int len = -1;
            while(    (len = in.read(data))  >  0  ){
                out.write(data,0,len);
            }
        }catch (Exception  e){
            e.printStackTrace();
        }finally {
            try {
                if(in != null) {
                    in.close();
                }
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                try {
                    if(out != null) {
                        out.close();
                    }
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }
    }
所以 JDK1.7开始 推出了新的 try-catch 解决关闭资源麻烦的问题

注意

  • 定义文件路径时,注意:可以用“/”或者“\”。
  • 在写入一个文件时,如果使用构造器FileOutputStream(file),则目录下有同名文件将被盖。
  • 如果使用构造器FileOutputStream(file,true),则目录下的同名文件不会被覆盖,在文件内容末尾追加内容。
  • 在读取文件时,必须保证该文件已存在,否则报异常。
  • 字节流操作字节,比如:.mp3,.avi,.rmvb,mp4,.jpg,.doc,.ppt
  • 字符流操作字符,只能操作普通文本文件。最常见的文本文件:.txt,.java,.c,.cpp 等语言的源代码。尤其注意.doc,excel,ppt这些不是文本文件。

三、缓冲

为了提高数据读写的速度,Java API提供了带缓冲功能的流类,在使用这些流类时,会创建一个内部缓冲区数组,缺省使用8192个字节(8Kb)的缓冲区。

缓冲流要“套接”在相应的节点流之上,根据数据操作单位可以把缓冲流分为:

  

 BufferedInputStream 和 BufferedOutputStream
    BufferedReader 和 BufferedWriter

当读取数据时,数据按块读入缓冲区,其后的读操作则直接访问缓冲区

当使用BufferedInputStream读取字节文件时,BufferedInputStream会一次性从文件中读取8192个(8Kb),存在缓冲区中,直到缓冲区装满了,才重新从文件中读取下一个8192个字节数组。

向流中写入字节时,不会直接写到文件,先写到缓冲区中直到缓冲区写满,BufferedOutputStream才会把缓冲区中的数据一次性写到文件里。使用方法flush()可以强制将缓冲区的内容全部写入输出流

关闭流的顺序和打开流的顺序相反。只要关闭最外层流即可,关闭最外层流也会相应关闭内层节点流
flush()方法的使用:手动将buffer中内容写入文件
如果是带缓冲区的流对象的close()方法,不但会关闭流,还会在关闭流之前刷新缓冲区,关闭后不能再写出

 

 

 BufferedReader br = null;
        BufferedWriter bw = null;
        try {
		// 创建缓冲流对象:它是处理流,是对节点流的包装
            br = new BufferedReader(new FileReader("d:\\IOTest\\source.txt"));
            bw = new BufferedWriter(new FileWriter("d:\\IOTest\\dest.txt"));
            String str;
            while ((str = br.readLine()) != null) { // 一次读取字符文本文件的一行字符
                bw.write(str); // 一次写入一行字符串
                bw.newLine(); // 写入行分隔符
            }
            bw.flush(); // 刷新缓冲区
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
		// 关闭IO流对象
            try {
                if (bw != null) {
                    bw.close(); // 关闭过滤流时,会自动关闭它所包装的底层节点流
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (br != null) {
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值