Java——IO流

发布时间 2023-11-15 17:22:09作者: 在这么冷的天

一、异常

概述:

  就是Java程序在运行的过程中出现的错误

  由来:问题也是现实生活中的一个具体事务,也可以通过java的类的形式进行描述,并封装成对象。其实就是Java对不正常情况进行描述后的对象体现

Throwable:

  Error:表示很严重的问题,一般情况下不解决

  Exception: 

    编译时期异常:写代码的过程中,编译错误时是不会产生class文件,一般情况下是我们自己的原因导致代码本身抛出了一个变异异常

    RuntimeException:在java运行过程中出现的错误,一般也是因为我们自己的原因导致的

JVM默认的处理异常的方式,先抛出整个异常,然后将整个程序停止。

异常处理方案

  try...catch...finally...

  throws

第一种方式:try...catch...finally...

    try{

      放出可能会出错的代码  

         }catch(Exception e){                  

      处理的方式逻辑

      }finally{        //可写可不写

      无论如何都会执行的代码

        }

编译时异常和运行时异常的区别

  RuntimeException 类及其子类的实例被称为运行时异常,其他的异常就是编译时异常

  编译时异常

    java程序必须显示处理,否则程序就会发生错误,无法通过编译

  运行时异常

    无需显示处理,也可以和编译时异常一样处理

finally的特点和作用

  特点

    被finally控制的语句体一定会执行

    特殊情况:在执行到finally之前jvm退出了(比如System.exit(0))

  作用:

    用于释放资源,在IO流操作和数据库操作中经常会看到

  面试题

    1.final,finally,finalize的区别

    final:final关键字可以用来修饰类、方法和变量。当用final修饰类时,表示该类不能被继承;当用final修饰方法时,表示该方法不能被子类重写;

       当用final修饰变量时,表示该变量的值不能被修改,即为常量。

    finally:finally是Java中的异常处理关键字,它通常与try-catch结构一起使用,用于定义无论是否发生异常都会执行的代码块。

        无论try块中是否抛出异常,finally块中的代码都会被执行。

    finalize:finalize是Object类中的一个方法,用于在对象被垃圾回收之前执行一些清理操作。

        一般来说,不建议直接使用finalize方法,因为它的执行时机不确定且不可靠。  

        在Java中,推荐使用try-with-resources或者手动关闭资源的方式来进行资源的释放和清理。

    因此,final用于定义最终的类、方法或变量,finally用于异常处理中的必定执行的代码块,而finalize是Object类中的方法,用于对象的垃圾回收前的清理操作。

  

    2.如果catch里边有return语句,请问finally的代码还会执行吗?如果会,请问是在return前还是在return后

    当一个catch块中包含return语句时,finally块中的代码仍然会执行。finally块中的代码会在return语句执行之后、但在实际返回之前执行。

    无论在try块中的代码是否抛出异常,finally块中的代码都会被执行,以便进行一些清理工作或收尾操作

Throwable中的方法  

    getMessage()
      获取异常信息,返回字符串。
    toString()
      获取异常类名和异常信息,返回字符串。
    printStackTrace()
      获取异常类名和异常信息,以及异常出现在程序中的位置。返回值void。

    System.out.println(e.getMessage());   // 打印错误的信息 6
    System.out.println(e.toString());   // 打印异常信息的同时打印异常类

throws

  定义功能方法时,需要把出现的问题暴露出来让调用者去处理。就通过throws在方法上标识。

throw

  在功能方法内部出现某种情况,程序不能继续运行, 需要进行跳转时,就用throw把异常对象抛出。

throws和throw的区别

  throws

    用在方法声明后边,跟的是异常类名

    可以跟多个异常类名,用逗号隔开

    表示抛出异常,由该方法的调用者来处理

    throws表示出现异常的一种可能性,并不一定会发生这些异常

  throw

    用在方法体内,跟的是异常对象名

    只能抛出一个异常对象名

    表示抛出异常,有方法体内的语句处理

    throw则是抛出了异常,执行throw则一定抛出了某种异常

异常如何处理

  原则:如果该功能内部能将问题处理,就用try,如果处理不了,交给调用者处理,需要用throws

  区别:后续程序需要继续运行就用try

      后续程序不需要继续运行就用throws

二、File类

概述

  文件和目录路径名的抽象表现形式

构造方法 

  public File(String pathname)
  public File(String parent,String child)
  public File(File parent,String child)

成员方法  

  创建功能
    public boolean createNewFile()    将文件创建出来,不是文件夹
    public boolean mkdir()
    public boolean mkdirs()
  删除功能
    public boolean delete()    可以删除文件和文件夹,但删除文件夹的话,要求文件夹是空的
  重命名功能
    public boolean renameTo(File dest)

  判断功能
    public boolean isDirectory()    是否是一个文件夹
    public boolean isFile()    是否是一个文件
    public boolean exists()    是否存在
    public boolean canRead()    是否可读
    public boolean canWrite()    是否可写

    public boolean isHidden()    是否隐藏

  基本获取功能:
    public String getAbsolutePath()    获取文件的绝对路径
    public String getPath()    获取文件的相对路径
    public String getName()    获取文件名
    public long length()    获取字节数
    public long lastModified()    获取最后一次修改的时间,返回的是毫秒级别的时间戳

  高级获取功能:
    public String[] list(): 将文件夹中的所有文件以及文件夹的名字获取出来,并封装成一个数组
    public File[] listFiles(): 将文件夹中的所有文件以及文件夹都封装成File对象后,再封装成一个数组

文件名称过滤器:FilenameFilter
判断E盘目录下是否有后缀名为.jpg的文件,如果有,就输出此文件名称
public File[] listFiles(FilenameFilter filter)

  

三、递归

  方法定义中调用方法本身的现象

注意事项

  要有开始条件

  要有结束条件(出口),如果没有结束条件,那么就是一个死递归,如果是死递归,就容易造成栈内存溢出。

  递归要有返回值

  构造方法不能递归使用

解决方法

  找出出口

  找出规律

需求:求5的阶乘

是使用递归将E盘下所有.jpg后缀的文件拿出来

四、IO流

概述

  用来处理设备之间的数据传输(上传文件和下载文件)

  java对数据的操作是通过流的方式

  java用于操作流的对象都在IO包中

分类

  根据流向

    输入流:input  外部的数据-->java

    输出流:output    java-->外部文件中

  根据数据类型的划分

    字节流(万能流,计算机中都是采用字节的方式存储的)

      字节输入流:InputStream

      字节输出流:OutputStream

    字符流:当你使用记事本打开一个文件能够看懂的时候

      字符输入流:Reader

      字符输出流:Writer

字节输出流:将java中的数据写出去

  实现子类:FilOutputStream

  构造方法: (如果目标文件不存在,将自动创建)

      FileOutputStream(File file)
      FileOutputStream(String name)

  写数据方式:   

      public void write(int b)    写一个int类型的数,转成ASCII码写到文件中
      public void write(byte[] b)    写一个字节数组
      public void write(byte[] b,int off,int len)    将自己数组的一部分写到文件中

  如何实现追加写?  

    public FileOutputStream(String name, boolean append)

    传参的时候,可以加一个true,表示追加写入

  如何实现换行?

    换行符也是数据的一部分   

    fos.write("\r\n".getBytes())

  加入异常的IO流操作

字节流读取数据

    InputStream(抽象类) -- FileInputStream(实现子类)

  构造方法(读取的文件必须要提前存在,不然会报错)  

    FileInputStream(File file)
    FileInputStream(String name)

  成员方法  

    public int read()    一次读取一个字节,每调用一次read()方法,光标会向后移动一次
    public int read(byte[] b)    一次读取一个字节数组

    用whlie循环改进

    

复制数据练习:将E盘下的xxx.jpg复制到D盘下

 

五、字节缓冲流

  字节流一次读取一个数组的速度明显比一次读取一个字节的速度快很多,这也是加入了数组这样的缓冲区的效果。所以java本身也提供了字节缓冲流区 

字节缓冲输出流

  BufferedOutputStream

字节缓冲输入流

  BufferedInputStream

缓冲输出流方法

  void write(byte[] b, int off, int len)     从指定的字节数组写入 len个字节,从偏移 off开始到缓冲的输出流。

  bos.flush();     // 如果只是想要刷新缓冲区中的数据,而不是关闭资源,建议使用flush(),每写一次,刷一次

  void write(int b)  // 将指定的字节写入缓冲的输出流。

  bos.close();   // 底层调用flush方法刷新缓冲区、

缓冲输入流方法 

  int read()  //见 read法 InputStream的一般合同。

  int read(byte[] b)  //从该输入流读取最多 byte.length个字节的数据到字节数组。

练习:四种方式比较复制效率   略

六、字符流

  字符流=字节流+编码表

  注意:如果要保证读取的数据和写的数据一致的话,就要保证读时候的编码和写时候的编码一直就可以了

字符输出流方法:

  public void write(int c)   一次写一个字符
  public void write(char[] cbuf)  一次写一个字符数组
  public void write(char[] cbuf,int off,int len)  写字符数组的一部分
  public void write(String str)  写一个字符串
  public void write(String str,int off,int len)  写字符串的一部分

 

  public OutputStreamWriter(OutputStream out,String charsetName) // 字符流=字节流+编码表

  注意:字符流写数据都要刷新一下

字符输入流:(Reader-->InputStreamReader)

  构造方法  

    public InputStreamReader(InputStream in)
    public InputStreamReader(InputStream in,String charsetName)

  方法  

    public int read() 一次读取一个字符
    public int read(char[] cbuf) 一次读取一个字符数组  (也可以用while循环改善)

  使用场景   

    字节流适用于任意格式的文件(图片,视频,文本文件,其他的)
    字符流只适用于我们使用记事本打开能看懂的文件

练习:使用字符流复制一个文本文件 数据源:D:\\三国.txt  目的地: E:\\三国演义.txt

转换流的简单写法 

  输入流:
    Reader -- InputStreamReader -- FileReader
  输出流
    Writer -- OutputStreamWriter -- FileWriter

 

  创建字符输出流对象
    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("xxx/d.txt"));
    FileWriter fw = new FileWriter("xxx/d.txt");

  创建字符输入流对象
    InputStreamReader isr = new InputStreamReader(new FileInputStream("xxx/d.txt"));
    FileReader fr = new FileReader("xxx/d.txt");

七、字符缓冲流

    字符缓冲输入流:BufferedReader
    字符缓冲输出流:BufferedWriter

字符输出流

    构造方法
      BufferedWriter(Writer out)
    创建使用默认大小的输出缓冲区的缓冲字符输出流。
       BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("xxx/e.txt")));
    使用简化写法创建
      BufferedWriter bw = new BufferedWriter(new FileWriter("xxx/e.txt"));

 

    BufferedWriter中有一个特有的方法:public void newLine() 自动生成对应操作系统的换行符

字符输入流 

  创建字符输入流对象
    BufferedReader(Reader in)
  创建使用默认大小的输入缓冲区的缓冲字符输入流。
     BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("xxx/e.txt")));
  使用简化写法
    BufferedReader br = new BufferedReader(new FileReader("E:\\三国演义.txt"));

 

 

  BufferedReader有一个特殊的读取数据的方式:一次读取一行public String readLine()
  注意:readLine()方法不会读取换行符

 

读取方式: 

 

练习1:把ArrayList集合中的字符串数据存储到文本文件

    字符串我们是能看懂的,优先考虑字符流,而且是字符输出流,要快的话,最终使用字符缓冲输出流BufferedWriter

练习二:从文本文件中读取数据(每一行为一个字符串数据)到集合中,并遍历集合

    因为文件使用记事本打开能够看懂,所以优先考虑使用字符流,又因为是读取数据,所以采用字符输入流,要快的话,最终使用字符缓冲输入流BufferedReader

练习三:键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低存入文本文件

先建一个学生类

然后

练习4:

已知s.txt文件中有这样的一个字符串:“hcexfgijkamdnoqrzstuvwybpl”
请编写程序读取数据内容,把数据排序后写入ss.txt中

练习5:登录注册IO版

IO流使用方式

 

八、序列化流 

  序列化:将对象像数据流一样在网络中传输的过程
  反序列化:将网络中传输的数据流转变成一个对象的过程

  序列化流:
    反序列化(对象输入流):ObjectInputStream
    序列化(对象输出流):ObjectOutputStream

  注意:
    只有实现了Serializable接口的类创建出的对象才可以进行序列化,而Serializable接口是一个标记接口

 

  在实现序列化和反序列化了的时候,改变了一下类的内容,结果报错了

 

  原因:因为改动后的class中serialVersionUID与实际存储在文件中的对象serialVersionUID编号不一致
  解决方案:只要保证无论怎么该class,它的serialVersionUID不变就可以了。自动生成即可

  java提供了一个关键字:transient 可以修饰成员,被修饰的成员无法进行序列化

九、配置文件

  创建配置文件对象
    Properties()
  创建一个没有默认值的空属性列表。
    Properties prop = new Properties();
  告诉它你要读取的文件
    prop.load(new BufferedReader(new FileReader("xxx/users.properties")));

  通过键获取配置文件中的值
    String username = prop.getProperty("username");
    String password = prop.getProperty("password");