JAVA-12-异常、IO流之字节流、编解码

来源:互联网 发布:js confirm 修改为yes 编辑:程序博客网 时间:2024/05/16 17:48

学习IO流之前,先介绍异常,以后我们会经常遇到各种各样的异常,那么遇到异常我们该怎么处理呢?
一:异常
1.1 异常:就是程序出现的不正常的情况。
异常大致分类:
  ①错误(Error):这是非常严重的问题,一般我们处理不了,一般在这里指的是硬件问题。
  ②异常(Exception):
    a.编译时期异常 开始就必须要处理的,如果不处理,后面就走不了。
    b.运行时期异常 开始可以不用处理。这种问题一旦发生,就是我们的程序问题,需要我们修改程序。
1.1.1 异常体系结构图:
这里写图片描述
1.1.2 针对异常,JVM默认的处理方案:
先举一个例子,除数为0的异常:

package com.edu_01;public class ExceptionDemo2 {    public static void main(String[] args) {        System.out.println("start");        int a = 10;        int b = 0;        System.out.println(a/b);        System.out.println("end");    }}//运行结果:/** start    Exception in thread "main" java.lang.ArithmeticException: / by zero            at com.edu_01.ExceptionDemo2.main(ExceptionDemo2.java:23)*/

并没有输出 end,且抛出了java.lang.ArithmeticException: / by zero异常。
  我们发现,一旦遇到程序出现了问题,就会把问题的类名,错误原因,错误的位置等信息打印在控制台,以便我们观察。并且,会自动从当前出问题的地方停止掉。
  然而JVM默认的处理方案虽然可以,但是不够好。
  即便程序出问题,也不应该直接停止,因为我们的程序可能是由多部分组成的,当其中一个部分出问题时,不应该影响其他部分的执行。所以,我们应该想办法让其他的部分能够执行下去。
1.2 如何处理异常,保证各个部分不影响的呢?
  两种方案:
    A:try…catch…finally
    B:throws
方案一: try…catch…finally:
  try{
    可能出现异常的代码
  }catch(异常类名 变量名) {
    针对异常的代码处理
  }finally {
    释放资源的地方
  }
先不看finally,我们简化一下:
  try{
    可能出现异常的代码
  }catch(异常类名 变量名) {
    针对异常的代码处理
  }

package com.edu_01;public class ExceptionDemo3 {    public static void main(String[] args) {        System.out.println("start");        int a= 10;        int b = 0;        try{            //可能出现异常的代码            System.out.println(a/b);//当除数为0的时候会抛出ArithmeticException这个异常                                    //接着程序会拿着这个异常和catch里面的异常类已经对比                           }catch(ArithmeticException e){            //当程序抛出ArithmeticException这个异常之后给出的具体的处理方法            System.out.println("你的除数不能为0");        }        System.out.println("end");    }}/** *  start    你的除数不能为0    end */

1.3多个异常的处理
  A:针对每一个出现问题的地方写一个try…catch语句
  B:针对多个异常,采用一个try,多个catch的情况。try…catch…catch…
遇到try里面的问题,就自动和catch里面进行匹配。一旦匹配就执行catch里面的内容,执行完毕后,接着执行后面的代码。
  注意:如果异常间有子父关系,父必须在最后。

package com.edu_01;public class ExceptionDemo4 {    public static void main(String[] args) {        int[] arr = {1,2,3};        try{            System.out.println(arr[3]);//抛出IndexOutOfBoundsException            System.out.println(10/0);//抛出ArithmeticException            arr = null;            System.out.println(arr[2]);//抛出空指针异常NullpointerException        }catch(IndexOutOfBoundsException e){//Exception e = new IndexOutOfBoundsException();            System.out.println("数组越界了");        }catch(ArithmeticException e){            System.out.println("除数不能为0");        }catch(Exception e){//当前面的异常类都没有匹配到之后,会自动匹配这个异常            System.out.println("出现了其他异常");        }       }}//输出结果://数组越界了

1.4 编译时期异常和运行时期异常的区别:
  编译时期异常:Java程序必须显示处理,否则程序就会发生错误,无法通过编译。
  运行时期异常:无需显示处理,也可以和编译时异常一样处理。

1.5 Throwable中的方法:(演示除数为0异常)
  printStackTrace():打印异常信息,程序从出问题的地方开始就会打印创建一个该异常对应的对象, 该对象直接调用打印方法

package com.edu_01;public class ExceptionDemo6 {    public static void main(String[] args) {        try{            System.out.println(10/0);//当程序出问题了之后会抛出一个ArithmeticException的对象            //new ArithmeticException()        }catch(ArithmeticException e){//ArithmeticException e = new ArithmeticException();            e.printStackTrace();//打印异常信息        }    }}//输出结果:/** * java.lang.ArithmeticException: / by zero    at com.edu_01.ExceptionDemo6.main(ExceptionDemo6.java:12) */

1.6 try…catch 与 throws的区别:
  try…catch 是直接进行了处理。而throws则是把异常处理的事情交给了调用者。
  throws用在方法上,声明方法有异常,交给调用者处理。但是如果是编译时期异常,调用者就必须处理。如果是运行时期异常,调用者可以处理,也可以不处理。

1.7 throws:(演示文件未找到异常,除数为0异常)
  用在方法声明后面,跟的是异常类名
  可以跟多个异常类名,用,号隔开
  表示抛出异常,由该方法的调用者来处理
  throws表示出现异常的一种可能,并不一定会发生这些异常

package com.edu_01;import java.io.FileInputStream;import java.io.FileNotFoundException;public class ExceptionDemo7 {    public static void main(String[] args){        method();        //method2();//将这个异常交给了调用者去进行处理                 //1.可以继续往上throws,将异常继续向上进行抛出了                 //2.自己try..catch...,相当与自己处理了这个异常        try{            method2();        }catch(FileNotFoundException e){            System.out.println("文件未找到");        }        /**         * 我们以后遇到异常是抛还是抓呢?         * 答:原则上能抛就抛,等到已经抛到了程序最底层的时候,最好就不要抛了,自己抓取。         */    }    private static void method2() throws FileNotFoundException {        //此时会抛出一个编译时期的异常,        //我们必须在方法上进行声明,如果不声明的话,会一直编译报错        FileInputStream fis = new FileInputStream("D://a.txt");    }    private static void method() throws ArithmeticException {        //在方法声明上声明这个方法可能出现的异常,不代表这个异常一定会出现        //此时仅仅是告诉我的调用者我的这个方法可能会出现异常,并不做具体的处理,交给        //调用者自己去处理这个异常        //此时抛出的 出数为0的异常,属于运行时期异常        System.out.println(10/5);    }}

1.8 异常处理 try…catch…finally
finally:一般用于释放资源。在数据库操作或者IO流比较常见。
  特点:被finally控制的语句体一定会执行
  特殊情况:在执行到finally之前jvm退出了(比如System.exit(0))

1.9 final,finally的区别?
  final:最终的意思。可以修饰类,方法,变量。
    修饰类,类不能被继承
    修饰方法,方法不能被重写
    修饰变量,变量是常量
  finally:
    异常处理的一部分。被finally控制的代码一定会执行。
    特殊情况:在执行到finally之前,jvm退出了。

1.9.1异常处理时应注意:
  A:子类重写父类方法时,子类的方法必须抛出相同的异常或父类异常的子类。(父亲坏了,儿子不能比父亲更坏)
  B:如果父类抛出了多个异常,子类重写父类时,只能抛出相同的异常或者是他的子集,子类不能抛出父类没有的异常
  C:如果被重写的方法没有异常抛出,那么子类的方法绝对不可以抛出异常,如果子类方法内有异常发生,那么子类只能try,不能throws
  上述仅仅针对编译时期异常,与运行时期异常无关。
1.9.2 throw和throws的区别?
  throws:
    用在方法声明后面,跟的是异常类名
    可以跟多个异常类名,用逗号隔开
    表示抛出异常,由该方法的调用者来处理
    throws表示出现异常的一种可能性,并不一定会发生这些异常
  throw:
    用在方法体内,跟的是异常对象名
    只能抛出一个异常对象名
    表示抛出异常,由方法体内的语句处理
    throw则是抛出了异常,执行throw则一定抛出了某种异常?
注意:如果throw的是编译时期异常,在方法声明上必须用throws进行标记
   如果throw的是运行时期异常,在方法声明上可以用throws进行标记,也可以不用。
二:IO流
学习完异常,现在开始正式学习IO流,IO流部分是java中非常重要的一部分。
(一) I/O流的分类
这里写图片描述
一般我们在讨论IO的分类时,默认是按照数据类型分的。
(二) File
File类的使用和普通类基本一样,着重学习构造方法和成员方法。
File:文件和目录(文件夹)路径名的抽象表示形式。

2.1 File的构造方法:
  File(String pathname):把一个路径名称封装成File对象
  File(String parent, String child):把一个父路径和一个子路径封装成一个File对象
  File(File parent, String child):把一个父路径File对象和一个子路径封装成一个File对象

package com.edu_01;import java.io.File;import java.io.IOException;public class FileDemo {    public static void main(String[] args) throws IOException {        //File(String pathname):把一个路径名称封装成File对象        //File file = new File("D://a.txt");        //System.out.println(file.createNewFile());//当文件不存在的时候,创建文件,如果文件存在,不创建        System.out.println("------------");        //File(String parent, String child):把一个父路径和一个子路径封装成一个File对象        //File file = new File("D://test", "a.txt");        //java.io.IOException: 系统找不到指定的路径,        //创建文件的时候一定要保证路径存在        //System.out.println(file.createNewFile());        System.out.println("-----------------");        //File(File parent, String child):把一个父路径File对象和一个子路径封装成一个File对象        File file = new File("D://test");        File file2 = new File(file, "a.txt");        System.out.println(file2.createNewFile());    }}

2.2 成员方法:
创建功能
  A:创建文件
    public boolean createNewFile():如果文件不存在,就创建。否则,不创建。
  B:创建目录
    public boolean mkdir():如果目录不存在,就创建。否则,不创建。
    public boolean mkdirs():如果目录不存在,就创建。否则,不创建。即时父目录不存在,也可以连父目录一起创建。
  注意事项:需要造什么东西,就应该用对应的方法。
删除功能:
  public boolean delete():既可以删除文件,又可以删除目录。
  路径问题:
    A:绝对路径 就是以盘符开始的路径(d:\test\aaa\b.txt)
    B:相对路径 就是不以盘符开始的路径(a.txt),一般都是相对应当前的项目而言的。
  注意事项:
    A:Java程序的删除不走回收站。
    B:如果目录内还有内容就不能删除。
判断功能
  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():获取名称
(三) 字节流及高效字节流
字节流比较通用,因为现在所有东西都是建立在字节的基础上的。

3.1 字节输出流  顾名思义是往文件里录入东西。
  类OutputStream是一个抽象类,我们所用的是其子类FileOutputStream
有了这个类之后,我们如何往一个文件写数据呢(比如向文件中录入helloworld)?或者说就是字节输出流的操作步骤是什么呢?
  A:创建字节输出流对象
  B:调用写数据的方法
  C:释放资源
具体做法如下:
  A: 构造(两种方法)
    FileOutputStream(File file)
    FileOutputStream(String name)

FileOutputStream fos  = new FileOutputStream("fos.txt");        /**请问上面这个操作做了哪几件事情?          1.创建了一个文件输出流fos,指向文件a.txt          2.创建了a.txt这个文件         */

  B: FileOutputStream写数据的方法
      write(byte[] b)
      write(int b) :一次写一个字节
      write(byte[] b, int off, int len) :一次写一个字节数组的一部分

fos.write("helloworld".getBytes());//FileOutputStream对象fos调用write方法

  C: fos.close(); 关流
    关流之后就不能再往里录入了。

package cokm.edu_01;//向文件中录入helloworldimport java.io.File;import java.io.FileOutputStream;import java.io.IOException;public class OutputStreamDemo {    public static void main(String[] args) throws IOException {        //1.创建文件输出流对象        FileOutputStream fos = new FileOutputStream("a.txt");        //或者 File file = new File("a.txt");        //FileOutputStream fos = new FileOutputStream(file);        //2.调用输出流的写数据的方法给文件中写数据        byte[] byf = "helloworld".getBytes();        fos.write(byf);        //3.释放资源,关流操作        fos.close();    }}

3.2 字节输入流: 读取文件
我们现在会写了,那么怎么读取文件呢?这里用到了字节输入流。
字节输入流操作步骤(类似于字节输出流):
  A: 创建字节输入流对象
    FileInputStream fis = new FileInputStream(“a.txt”);
    构造方法:
      FileOutputStream(File file)
      FileOutputStream(String name)
  B: 调用方法读取数据,read方法,当文件读取到末尾后如果继续读取,将返回,-1
    read(),一次读取一个字节,返回的是int型,是读取字节对应的ASCII码
    read(byte[] b) 一次读取一个字节数组的长度,返回int,是实际读取的长度,并将读取的内容返回到数组中
    read(byte[] b, int off, int len) 一次读取一个字节数组的一部分,返回int,是实际读取的长度,并将读取的内容返回到数组中
  C: 释放资源
    fis.close();

package cokm.edu_02;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;public class InputStreamDmeo {    public static void main(String[] args) throws IOException {        //读取文件a.txt        //1.创建文件输入流对象,并关联文件        FileInputStream fis = new FileInputStream("a.txt");        //2.调用输入流的读取文件的方法,读取文件        int by;        while ((by=fis.read())!=-1) {            System.out.println((char)by);        }        //3.释放资源        fis.close();    }}

现在我们会读取一个文件,又会写文件,那么复制一个文件呢?边读边写就行了 。
练习:A:把a.txt的内容复制到b.txt中
  分析:
  A. 封装数据源和目的地
  B. 利用循环,边读边写
  C. 释放资源,关流

package cokm.edu_03;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;public class CopyFile {    public static void main(String[] args) throws IOException {        //A:把a.txt的内容复制到b.txt中        //封装数据源和目的地        FileInputStream fis = new FileInputStream("a.txt");        FileOutputStream fos = new FileOutputStream("b.txt");        //2.读取数据源中的数据,将读取到的数据写入目的地中        int by;        while ((by=fis.read())!=-1) {            //将读取到的字节写入fos中            fos.write(by);        }        //3.释放资源        fos.close();        fis.close();        }}

3.3 字节缓冲区流(也叫高效流):
  BufferedInputStream(read() 一次读取一个字节, public int read(byte[] b):返回实际读取长度,数据被读取到数组中。)
  BufferedOutputStream(write(byte[] b))
字节流分为:
  低级流: 基本的流,可以直接操作文件。
  高级流:是操作基本流的流。

字节流综合练习:
  字节流复制文件(视频文件,并测试所用时间):
    A:基本字节流一次读写一个字节
    B:基本字节流一次读写一个字节数组(字节流中的最重要部分)
    C:高效字节流一次读写一个字节
    D:高效字节流一次读写一个字节数组

package cokm.edu_06;import java.io.BufferedInputStream;import java.io.BufferedOutputStream;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;public class Test {    public static void main(String[] args) throws IOException {        //复制文件开始之前的时间        long startTime = System.currentTimeMillis();        method1();//15099        method2();//31(这种处理方案使用的是最多的)        method3();//234        method4();//16        long endTime = System.currentTimeMillis();        System.out.println(endTime-startTime);    }    private static void method4() throws IOException {        // TODO Auto-generated method stub        //C:高效字节流一次读写一个字节           BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D://b.mp4"));        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a.mp4"));        //高校字节流一次读写一个字节        byte[] byf = new byte[1024];        int len;        while ((len = bis.read(byf))!=-1) {            bos.write(byf, 0, len);        }        //3.关流        bos.close();        bis.close();    }    private static void method3() throws IOException {        //C:高效字节流一次读写一个字节           BufferedInputStream bis = new BufferedInputStream(new FileInputStream("D://b.mp4"));        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("a.mp4"));        //高校字节流一次读写一个字节        int by;        while ((by = bis.read())!=-1) {            bos.write(by);        }        //3.关流        bos.close();        bis.close();    }    private static void method2() throws IOException {        // B:基本字节流一次读写一个字节数组            //疯转数据剧院和目的地        FileInputStream fis = new FileInputStream("D://b.mp4");        FileOutputStream fos = new FileOutputStream("a.mp4");        //一次读写一个字节        byte[] byf =new byte[1024];        int len;        while ((len=fis.read(byf))!=-1) {            fos.write(byf, 0, len);        }        //3.释放资源        fos.close();        fis.close();    }    private static void method1() throws IOException {        //A:基本字节流一次读写一个字节           //疯转数据剧院和目的地        FileInputStream fis = new FileInputStream("D://b.mp4");        FileOutputStream fos = new FileOutputStream("a.mp4");        //一次读写一个字节        int by;        while ((by=fis.read())!=-1) {            fos.write(by);        }        //3.释放资源        fos.close();        fis.close();    }}

到此,字节流基本上学完了。

=====================以下为知识补充=====================
编码、解码问题
  public byte[] getBytes(String charsetName) 按照给定的编码方式,编码字节数组(gbk,utf-8)
  String(byte[] bytes, String charsetName) 按照给定的编码方式解码字符数组

package cokm.edu_07;//编解码练习import java.io.UnsupportedEncodingException;import java.util.Arrays;public class EncodingDemo {    public static void main(String[] args) throws UnsupportedEncodingException {        //创建一个字符串        String s = "中国好";        byte[] bytes1 = s.getBytes();        //使用gbk去进行编码的时候,我们是将一个汉字编码成两个负数的字节。        //当我们解码的时候,如果字节数组中遇见负数的话,会将前面和后面的负数进行拼接,去gbk码表中找到相应的明文        System.out.println(Arrays.toString(bytes1));//[-42, -48, -71, -6, -70, -61]        System.out.println("----------------");        byte[] bytes2 = s.getBytes("gbk");        System.out.println(Arrays.toString(bytes2));//[-42, -48, -71, -6, -70, -61]        System.out.println("------------");        byte[] bytes3 = s.getBytes("utf-8");        //当使用uft-8进行编码的时候,一个汉字会被编码成3个负数的字节        //解码的时候,一旦遇见负数,三个拼接,去码表中进行解码        System.out.println(Arrays.toString(bytes3));//[-28, -72, -83, -27, -101, -67, -27, -91, -67]        System.out.println("--------------");        System.out.println(new String(bytes3));//涓浗濂?        System.out.println(new String(bytes3, "utf-8"));//中国好        //结论:以后使用哪种编码就使用哪种解码(编解码方式必须一致,不然会出现乱码)        //我们以后编解码方式一般来说不需要指定特定的码表,一般来说使用默认的就可以      }}
0 0