Java编程基础知识之异常

18 篇文章 0 订阅
11 篇文章 0 订阅

前言: 

        异常就是程序在执行过程中遇到了不符合设定的不正常情况。使用异常有助于保证程序的健壮性,屹立不倒。除此之外还浅谈了一下断言和日志的知识

目录

前言: 

一、异常      

1.异常分类

2.异常处理

1)throws(抛出异常)

2)try...catch(捕获异常)

3)补充说明

3.抛出or捕获

4. 深入try....catch

5.异常的两重要方法

6.创建异常类

7.finally关键字

1)finally 说明

2)特殊情况

3)try with resource语句

三、断言

1.断言概念

2.断言语法

3.断言的启用和禁用

5.使用断言完成参数检查

四、日志

1.修改日志配置文件

2.基本日志

3.高级日志

1)自定义日志记录器


一、异常      

1.异常分类

        所以异常都是一个类,并且派生于一个Throwable类,由这个类衍生处理两个分支:Error和Exception。

图片来自网上

Error类:

        层次结构描述了Java运行时系统的内部错误和资源耗尽错误。

Exception类:        

        这类异常比较常见,是应该被重视的。由它分出两个层次,一个为运行时异常,一个为非运行时异常,也称为编译时异常

        对于编译时异常,需要我们在编写程序阶段必须预先对这些异常进行处理,如果不处理,编译器报错。这个可以在编译阶段就能被发现

public class RuntimeExceptionDemo {
    public static void main(String[] args) {
        //test方法会有异常抛出,没有进行预处理,编译器报错,属于编译时异常
        test();
    }
    public static void test() throws Exception{
        ......
    }
}

        对于运行时异常,在编写程序阶段可以预先处理。运行时异常,是只有在运行时才能发现的异常。以下是例子

public class RuntimeExceptionDemo {
    public static void main(String[] args) {
        int i = 0;
        int j = 1;
        //0不能作为除数,报异常ArithmeticException
        System.out.println(j/i);
    }    
}
ArithmeticException 继承 RuntimeException,属于  运行时异常

二者的区别:

  • 编译时异常一般发生的概率 比较高
  • 运行时异常一般发生的概率 比较低
  • 编译时异常发生概率较高,需要在运行之前对其进行预处理
  • 运行时异常发生概率较低,没必要提前进行预处理。  

说明: 

public class RuntimeExceptionDemo {
    public static void main(String[] args) {
        int i = 0;
        int j = 1;
        //0不能作为除数,报异常ArithmeticException
        int temp = j/i;
        System.out.println("Hello world");
    }    
}

        在该程序中,是main方法中进行的, 由于出现了一个异常,这个异常将会被抛给main方法,由于main方法没有处理它,将抛给JVM,JVM就就将它给终止掉了,后面System.out.println("Hello world");是不会执行的

2.异常处理

1)throws(抛出异常)

抛出异常语法:方法名 throws 异常

public FileInputStream(String name) throws FileNotFoundException 

        这种异常的处理方式就是上报(推卸责任),将异常上报给调用它的方法,异常发生之后的代码不会执行

2)try...catch(捕获异常)

        在这里就是自己将异常处理,而不会再次往上抛,在处理完异常后,会继续执行代码。特别注意,try块内的代码,在异常之后是无法执行的,而块外的代码在处理完异常后会正常执行

public class RuntimeExceptionDemo {
    public static void main(String[] args) {
        int i = 0;
        int j = 1;
        try {
            int temp = j / i;
        //catch内部为异常类型,e就是运行时JVM创建异常的对象引用
        }catch (Exception e){
            //此处为处理异常,选择将异常抛出作为处理
            e.printStackTrace();
        }
        System.out.println("Hello world");
    }
}

        注意:

  • 如果try语句块中的代码没有抛出任何异常,那么程序将跳过catch语句。
  • 如果方法中的任何代码抛出的了catch语句中没有声明的一个异常类型,那么这个方法会立即退出,原因很简单,没有捕获到就被往上抛,抛着抛着就到JVM上去了。

3)补充说明

  1. 异常发生之后,如果我选择了上抛,抛给了我的调用者,调用者需要对这个异常继续处理,那么调用者处理这个异常同样有两种处理方式。
  2. 一般不建议在main方法上使用throws,因为这个异常如果真正的发生了,一定会抛给JVM。JVM有终止。
  3. 一般main方法中的异常建议使用try…catch进行捕捉。

3.抛出or捕获

  1. 对于希望调用者来进行处理,那么就向上抛出
  2. 其他情况就原地捕获解决

4. 深入try....catch

  1. catch内部的括号中填写的类型可以是具体的异常的类型,也可以是父类(本层或者更高层)
  2. try....catch 结构中可以有多个catch 语句
  3. 使用多个catch结构时,必须遵循异常范围是逐渐变大的
    public static void main(String[] args) {
        try{
            FileInputStream f = new FileInputStream("D:\\aa\\pz.txt");
        } catch (FileNotFoundException e){
            e.printStackTrace();
        } catch (Exception e){
            e.printStackTrace();
        }
    }

5.异常的两重要方法

方法名作用
String getMessage()返回异常的详细消息字符串
void printStackTrace()追踪堆栈异常信息(采用异步线程)

6.创建异常类

        在遇到无法描述的异常时,需要你自己根据实际情况进行创建异常类。创建异常类需要继承Exception类或者它的一个子类。一般异常都包含两个构造器,一个为无参构造器,一个有描述的构造器(通过super调用超类Throwable的toString())。

        创建操作

//自定义异常类
class TestException extends Exception{
    public TestException(){
        
    }
    public TestException(String message){
        super(message);
    }
}

        使用实例 

public class RuntimeExceptionDemo {
    public static void main(String[] args) {
        Test test = new Test();
        try {
            test.isOverZero(-1);
        } catch (TestException e) {
            e.printStackTrace();
        }
    }
}
class Test{
    public void isOverZero(int i) throws TestException{
        if(i<0){
            //注意是throw ,不是 throws
            //这两个东西不一样
            throw new TestException();
        }
    }
}
class TestException extends Exception{
    public TestException(){

    }
    public TestException(String message){
        super(message);
    }
}

7.finally关键字

1)finally 说明

        finally 与try...catch 语句连用,不能单独使用

        finally用于在异常发生后回收资源,因为try中发生异常,后面的代码就无法执行,如果要完成资源的清理就比较麻烦,例如:流的关闭。finally里面的代码是一定会执行的,无论是否出现异常。

try{
    ......
}catch{
    ......
}finally{
    //关闭资源等回收资源的操作
    xxx.close();
}

2)特殊情况

        就如我们所知,return表示的是一个程序将结束,而finally又是try后必须执行的代码块,假如执行了try里面的return语句,就不符合规则了,所以在try语句return前,会跑到finally块里面去执行代码,而finally代码是执行return,就直接停止方法了,这种结果就会造成finally的结果将try的结果进行一个覆盖

    public static int num(){
        try {
            return 5;
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            return 0;
        }
    }

3)try with resource语句

        和上面的try不一样的是try()自己来了资源,

try(Scanner in = new Scanner()){
    
}

        允许多个资源在括号中,注意分号

try(Scanner in = new Scanner();var file = new FileInputStream("D://aa//pz.txt");)
{
    
}

        使用这个语句块,可以携带catch和finally,但是与上面的try不一样的是,这个try会自动关闭资源 ,自动执行in.close() 、file.close(); 

三、断言

1.断言概念

        断言就是断定一条语句是否如希望的那样,为true就正常进行,为false就报异常AssertionError.

2.断言语法

        第一种;

assert condition;

        第二种:

assert condition: expression;

“表达式”(expression)部分的唯一目的是产生一个消息字符串。

AssertionError对象并不存储具体的表达式值(条件并不会自动地生成为错误报告中的一部分),因此以后无法得到这个表达式值。(如果使用表达式的值,就会鼓励程序员尝试从断言失败中恢复程序的运行,这不符合断言机制的初衷。)

        断言机制允许在测试期间向代码插入一些检查,而在生产代码中会自动删除这些检查。 

3.断言的启用和禁用

        在默认情况下,断言是禁用的。可以在运行程序是用 -enableassertions 或 -ea 选项启用断言:

java -enableassertions MyApp

        需要注意的是,不必从新编译程序来启用或禁用断言。启用或禁用断言是**类加载器(class loader)**的功能。禁用断言时,类加载器会除去断言代码,因此,不会降低程序的运行速度。

        可以在某个类或整个包中启用断言,例如:

java -ea:MyClass -ea:com.mycompany.mylib MyApp

        这条命令将为 MyClass类以及 com.mycompany.mylib包和它的子包中的所有类打开断言。选项 -ea 将打开无名包中所有类的断言。可以使用 disableassertions -da 在特定类和包中禁用断言:

java -ea:... -da:MyClass MyApp

有些类不是由类加载器加载,而是直接由虚拟机加载的。可以使用这些开关有选择地启用或禁用那些类中的断言。

        不过,启用和禁用所有断言的 -ea 和 -da 开关不能应用到那些没有类加载器的“系统类”上。对于这些系统类,需要使用-enablesystemassertions/-esa 开关启用断言。也可以通过编程控制类加载器的断言状态。

5.使用断言完成参数检查

在Java语言中,给出了3中处理系统错误的机制:

  • 抛出一个异常。
  • 日志。
  • 使用断言。

什么时候应该使用断言呢?请记住下面几点:

  • 断言失败是致命的、不可恢复的错误。
  • 断言检查只是在开发和测试阶段打开(这种做法有时候被戏称为“在靠近海岸时穿上救生衣,但在海里就把救生衣抛掉”)。

        因此,不应该使用断言向程序的其他部分通知发生了可恢复性的错误,或者,不应该利用断言与程序用户沟通问题。断言只应该用于测试阶段确定内部错误的位置。

        在方法的开头使用断言判断方法的参数是否合法。计算机科学家将这种约定称为前置条件。如果调用者在调用这个方法时没有满足这个前置条件,断言会失败。

四、日志

        每个 Java 程序员都很熟悉在有问题的代码中插人一些 System , out . println 方法调用来帮助规寥程序的行为。当然,一旦发现问题的根源,就要将这些 print 语句从代码中删去。如果下来又出现了问题,就需要再捕人儿个调用 System , out . println 方法的语句。日志 API 就是为了解决这个问题而设计的。下面先讨论这个 API 的主要优点。

  1. 可以很容易地取消全部日志记录,或者仅仅取消某个级别以下的日志,而且可以很容
  2. 易地再次打开日志开关。
  3. 可以很简单地禁止日志记录,因此、将这些日志代码留在程序中的开销很小。日志记录可以被定向到不同的处理器,如在控制台显示、写至文件
  4. 日志记录器和处理器都可以对记录进行过滤。过滤器可以根据过滤器实现器指定的标准丢弃那些无用的记录项
  5. 日志记录可以采用不同的方式格式化,例如,纯文本或 XMI 
  6. 应用程序可以使用多个日志记录器, 他们使用与包名类似的有层次结构的名字如, com . mycompany . myapp 。
  7. 日志系统的配置由配置文件控制。

1.修改日志配置文件

        JDK默认的logging配置文件为:$JAVA_HOME/jre/lib/logging.properties,可以使用系统属性java.util.logging.config.file指定相应的配置文件对默认的配置文件进行覆盖,比如, java -Djava.util.logging.config.file=myfile

2.基本日志

执行代码:

public static void main(String[] args) {
        Logger.getGlobal().info("record");
    }
}

结果信息: 

         在这里是调用了全局记录器(global logger)并调用了info方法,将信息写入到记录中

3.高级日志

1)自定义日志记录器

        使用getLogger静态方法。参数是日志记录器的名字,是一个层次结构:包名 + 程序名(例子中是:com.company + myapp)

private static final Logger myLogger = Logger.getLogger("com.mycompany.myapp");

        日志等级(由高到低)

        默认日志配置会记录INFO更高级别的所有日志,因此应该使用CONFIG、FINE、FINER和FINEST、级别来记录那些有助于诊断但对用户意义不大的调试信息         

        也就是说,高级别的会被记录下来,低级别会被丢掉(高低是相对的说法)

    private static Logger logger = Logger.getLogger("record");
    public static void main(String[] args) {
        logger.setLevel(Level.INFO);
        logger.finest("finest");
        logger.finer("finer");
        logger.fine("fine");
        logger.config("config");
        logger.info("info");
        logger.warning("warning");
        logger.severe("server");

    }

运行结果:

 可以使用log方法指定级别,例如:

logger.log(Level。FiNE, message);

笔记自用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

摸鱼儿hzj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值