Java异常处理之数字溢出问题

Posted new-one

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java异常处理之数字溢出问题相关的知识,希望对你有一定的参考价值。

在进行 Java 开发时,经常会遇到数字溢出的问题。这个问题在很多程序中都非常常见,尤其是在进行数值计算的时候。Java 中提供了一种异常机制来处理这种情况,我们可以在代码中使用 try-catch 语句来捕获异常并进行相应的处理。

在 Java 中,整型数据类型 int 的范围是 -2^31 到 2^31-1,也就是 -2147483648 到 2147483647。如果我们尝试将一个超出这个范围的整数赋值给一个 int 变量,就会发生数字溢出的问题。此时,程序会抛出 ArithmeticException 异常。

例如,下面这段代码会出现数字溢出的问题:

int a = 2147483647;
a++;

这段代码会将 a 的值加 1,但是由于 a 已经达到了 int 类型的最大值,所以此时 a 的值将变成了 -2147483648。这个结果显然不是我们所期望的,而且它还会导致程序出现潜在的错误。

为了避免这种情况的发生,我们可以在代码中使用异常处理机制来捕获数字溢出的异常并进行相应的处理。例如,下面是一个简单的使用 try-catch 语句来处理数字溢出异常的示例:

try 
    int a = Integer.MAX_VALUE;
    a++;
 catch (ArithmeticException e) 
    System.out.println("数字溢出异常:" + e.toString());

在这个示例代码中,我们首先定义了一个 int 类型的变量 a,并将其赋值为 Integer.MAX_VALUE。然后,我们尝试将 a 的值加 1,从而导致数字溢出异常的发生。这时候,程序会抛出 ArithmeticException 异常,我们就可以在 catch 语句中捕获这个异常,并在控制台上输出相应的提示信息。

除了上面的方法外,我们还可以通过判断用户输入的值是否在 int 类型的范围内来避免这种异常的发生。例如,下面是一个简单的使用 Scanner 类读取用户输入并判断是否在范围内的示例:

try 
    Scanner scanner = new Scanner(System.in);
    int a = scanner.nextInt();
    if (a < Integer.MIN_VALUE || a > Integer.MAX_VALUE) 
        System.out.println("输入的值超出了 int 类型的范围!");
    
 catch (InputMismatchException e) 
    System.out.println("输入的值不是一个整数!");

在这个示例代码中,我们首先创建了一个 Scanner 对象来获取用户输入的值。然后,我们使用 nextInt() 方法来读取用户输入的整数,并将其赋值给一个 int 类型的变量 a。接着,我们检查这个变量的值是否超出了 int 类型的范围,如果超出了,就在控制台上输出相应的提示信息。如果输入的不是一个整数,则会抛出 InputMismatchException 异常,我们也需要在 catch 语句中进行处理。


 下面是一段代码,想能够准确判断出输入不是数字产生的异常还是输入的值溢出:

    public static void main(String[] args)
        Scanner sc = new Scanner(System.in);
        try
            int a = sc.nextInt();
        if(a<Integer.MAX_VALUE && a>Integer.MIN_VALUE)
            System.out.println("在范围内");
        
    catch (Exception e)
        System.out.println("出现异常,数字溢出!");
        System.out.println(e.getMessage());
    

  

改进结果如下:

public static void main(String[] args) 
    Scanner sc = new Scanner(System.in);
    try 
        int a = Integer.parseInt(sc.nextLine());
        System.out.println("在范围内");
     catch (NumberFormatException e) 
        System.out.println("出现异常,输入非数字!");
     catch (Exception e) 
        System.out.println("出现异常,数字溢出!");
    

主要的改进点如下:

  1. 使用 sc.nextLine() 而不是 sc.nextInt() 获取用户输入的值,因为 nextInt() 会忽略输入流中的空格或其他空白字符,而且无法判断输入是否有效。
  2. 使用 Integer.parseInt() 将字符串转换为整数。如果输入的字符串不是一个有效的整数,则会抛出 NumberFormatException 异常。
  3. 在捕获异常的时候,先捕获 NumberFormatException 异常,以便能够准确地判断用户输入的值是否是数字。如果捕获到这个异常,则直接输出“输入非数字”的提示信息,并退出程序。
  4. 如果没有捕获到 NumberFormatException 异常,再进一步捕获可能出现的其他异常,例如 ArithmeticException、NullPointerException 等等。如果捕获到这些异常,则输出“数字溢出”的提示信息,并显示具体的异常信息。

 

总的来说,Java 在处理数字溢出问题时提供了很多方法,开发者可以根据实际情况选择最合适的方法。除了使用异常处理机制之外,我们还可以通过判断用户输入的值是否在 int 类型的范围内来尽量避免这种问题的产生。无论采用哪种方法,都需要对程序中可能出现的异常进行充分的处理,以确保程序能够保持正确的逻辑和正常的运行。

 
 
 

Java之异常处理,日期处理

Java异常处理

异常:异常就是Java程序在运行过程中出现的错误。

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

异常分类图解:

技术分享

Throwable
  1--Error 严重问题,我们不处理。
  2--Exception
    (1)--RuntimeException 运行期异常,我们需要修正代码
    (2)--非RuntimeException 编译期异常,必须处理的,否则程序编译不通过

Java虚拟机默认处理

public class ExceptionTest1 {
    public static void main(String[] args) {
        int a=10;
        int b=0;
        System.out.println(a/b);
        System.out.println("通过!");
    }
}

技术分享

这是一个除数为0造成的典型的运行异常

这是Java虚拟机默认的处理,将异常的名称,原因,出现的问题输出在控制台

同时也中断程序

自己处理异常

try...catch...finally
  自己编写处理代码,后面的程序可以继续执行
throws
  抛出,把自己处理不了的,在方法上声明,告诉调用者,这里有问题

注意:try里面的代码越少越好

将问题包在try中,程序可以运行,但是catch里必须有代码,不然只能是隐藏问题而不是处理异常

public class ExceptionTest1 {
    public static void main(String[] args) {
        int a=10;
        int b=0;
        try{
            System.out.println(a/b);
        }catch(ArithmeticException Ex){
            System.out.println("异常!除数不能为0");
        }
        System.out.println("通过!");
    }
}

技术分享

//多个异常的情况
public class ExceptionTest1 {
    public static void main(String[] args) {
        int a=10;
        int b=0;
        int i[]={0,1,2};
        try{
            System.out.println(i[3]);
            System.out.println(a/b);
        }catch(ArithmeticException Ex){
            System.out.println("异常!除数不能为0");
        }catch(ArrayIndexOutOfBoundsException Ex){
            System.out.println("异常!访问无效!");
        }
        System.out.println("通过!");
    }
}

技术分享

我们可以看到并没有输出第一个catch中的异常处理语句,这是因为一旦try 里出了问题就会把这个异常抛出去到 catch 中匹配异常,匹配到之后执行 catch 里的语句,然后结束 try...catch 在执行下面的语句

public class ExceptionTest1 {
    public static void main(String[] args) {
        int a=10;
        int b=0;
        int i[]={0,1,2};
        try{
            System.out.println(i[3]);
            System.out.println(a/b);
            System.out.println("未知的异常!");
        }catch(ArithmeticException Ex){
            System.out.println("异常!除数不能为0");
        }catch(ArrayIndexOutOfBoundsException Ex){
            System.out.println("异常!访问无效!");
        }catch(Exception Ex){//Exception可以匹配所有的异常类型
            System.out.println("异常!未知!");
        }
        System.out.println("通过!");
    }
}

在catch()中我们应该写异常类型,能明确的尽量明确以节约资源,不能明确的也可以写 Exception 

注意:Exception 可以匹配所有异常,所有不能写在前面,否则后面的无效,所以,平级关系的异常前后顺序无所谓,父子关系的异常父类必须在后面。

try...catch...finally的格式变形
* A:try...catch...finally
* B:try...catch
* C:try...catch...catch...
* D:try...catch...catch...finally
* E:try...finally 这种做法的目前是为了释放资源。

 JDK7的新特性,可以再catch中将多个异常用  |  隔开

public class ExceptionTest1 {
    public static void main(String[] args) {
        int a=10;
        int b=0;
        int i[]={0,1,2};
        try{
            System.out.println(i[3]);
            System.out.println(a/b);
            System.out.println("未知的异常!");
        }catch(ArithmeticException | ArrayIndexOutOfBoundsException Ex){
            System.out.println("异常!");
        }
        System.out.println("通过!");
    }
}

这种方法的局限性,只能给出一个解决方案,多个异常要是平级关系

编译期异常和运行期异常的区别?
  编译期异常 必须要处理的,否则编译不通过
  运行期异常 可以不处理,也可以处理

Throwable类的常见方法

getMessage

public String getMessage()
返回此 throwable 的详细消息字符串。
返回:
Throwable 实例(可以为 null)的详细消息字符串。

 

toString

public String toString()
返回此 throwable 的简短描述。如果此 Throwable 对象是利用非空详细消息字符串创建的,则结果是三个字符串的串联:
  • 此对象的实际类的名称
  • ": "(冒号和空格)
  • 此对象的 getMessage() 方法的结果
如果此 Throwable 对象利用 null 详细消息字符串创建,则返回此对象的实际类的名称。
覆盖:
Object 中的 toString
返回:
该 throwable 的字符串表示形式。

 

printStackTrace

public void printStackTrace()
将此 throwable 及其追踪输出至标准错误流。此方法将此 Throwable 对象的堆栈跟踪输出至错误输出流,作为字段 System.err 的值。输出的第一行包含此对象的 toString() 方法的结果。剩余行表示以前由方法 fillInStackTrace() 记录的数据。此信息的格式取决于实现,但以下示例是最常见的:
 java.lang.NullPointerException
         at MyClass.mash(MyClass.java:9)
         at MyClass.crunch(MyClass.java:6)
         at MyClass.main(MyClass.java:3)
 
本示例通过运行以下程序生成:
 class MyClass {
     public static void main(String[] args) {
         crunch(null);
     }
     static void crunch(int[] a) {
         mash(a);
     }
     static void mash(int[] b) {
         System.out.println(b[0]);
     }
 }
 
对于带初始化非空 cause 的 throwable 的追踪,通常应该包括 cause 的追踪。此信息的格式取决于实现,但以下示例是最常见的:
 HighLevelException: MidLevelException: LowLevelException
         at Junk.a(Junk.java:13)
         at Junk.main(Junk.java:4)
 Caused by: MidLevelException: LowLevelException
         at Junk.c(Junk.java:23)
         at Junk.b(Junk.java:17)
         at Junk.a(Junk.java:11)
         ... 1 more
 Caused by: LowLevelException
         at Junk.e(Junk.java:30)
         at Junk.d(Junk.java:27)
         at Junk.c(Junk.java:21)
         ... 3 more
 
注意,存在包含字符 "..." 的行。这些行指示此异常的椎栈跟踪的其余部分匹配来自异常(由 "enclosing" 异常引起)的堆栈跟踪底部的指定数量的帧。这种简便方法可以大大缩短通常情况下的输出长度,这里抛出了包装的异常,其方法与捕获“作为 cause 的异常”的方法相同。上述示例通过运行以下程序生成:
 public class Junk {
     public static void main(String args[]) { 
         try {
             a();
         } catch(HighLevelException e) {
             e.printStackTrace();
         }
     }
     static void a() throws HighLevelException {
         try {
             b();
         } catch(MidLevelException e) {
             throw new HighLevelException(e);
         }
     }
     static void b() throws MidLevelException {
         c();
     }   
     static void c() throws MidLevelException {
         try {
             d();
         } catch(LowLevelException e) {
             throw new MidLevelException(e);
         }
     }
     static void d() throws LowLevelException { 
        e();
     }
     static void e() throws LowLevelException {
         throw new LowLevelException();
     }
 }

 class HighLevelException extends Exception {
     HighLevelException(Throwable cause) { super(cause); }
 }

 class MidLevelException extends Exception {
     MidLevelException(Throwable cause)  { super(cause); }
 }
 
 class LowLevelException extends Exception {
 }
public class ExceptionTest1 {
    public static void main(String[] args) {
        int a=10;
        int b=0;
        int i[]={0,1,2};
        try{
            System.out.println(i[3]);
        }catch(Exception Ex){
            System.out.println(Ex.getMessage());  //异常的消息字符串
            System.out.println(Ex.toString());  //异常消息简单描述
            Ex.printStackTrace();    //获取异常类名和异常信息,以及异常出现在程序中的未知,void,把信息输出在控制台
        }
        System.out.println("通过!");
    }
}

 throws

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

    public void show() throws ParseException  {
        String s="2017-07-23";
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d=sdf.parse(s);
        System.out.println(sdf.format(d));
    }
        try {
            d.show();
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

注意:编译期异常抛出将来调用者必须处理

运行期异常抛出,将来调用者可以不处理

throws后也可以跟多个异常

throw

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

        int a=10;
        int b=0;
        if(b==0){
            throw new ArithmeticException();
        }else{
            System.out.println(a/b);
        }

throws和throw的区别
throws
用在方法声明后面,跟的是异常类名
可以跟多个异常类名,用逗号隔开
表示抛出异常,由该方法的调用者来处理
throws表示出现异常的一种可能性,并不一定会发生这些异常
throw
用在方法体内,跟的是异常对象名
只能抛出一个异常对象名
表示抛出异常,由方法体内的语句处理
throw则是抛出了异常,执行throw则一定抛出了某种异常 

总结

如果该功能内部可以将问题处理,用try,如果处理不了,交由调用者处理,这是用throws
区别:
后续程序需要继续运行就try
后续程序不需要继续运行就throws
举例:
感冒了就自己吃点药就好了,try
吃了好几天药都没好结果得了H7N9,那就的得throws到医院
如果医院没有特效药就变成Error了

finally

特点:

被finally控制的语句体一定会执行
特殊情况:在执行到finally之前jvm退出了(比如System.exit(0))

作用:用于释放资源 

        String s="2017-07-23";
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date d=null;
        try{
            d=sdf.parse(s);
        }catch(ParseException ex){
            ex.printStackTrace();
        }finally{
            System.out.println("!这里的语句一定会执行!");
        }
        System.out.println(d);    

常见问题:

final,finally和finalize的区别
final:最终的意思,可以修饰类,成员变量,成员方法
 修饰类,类不能被继承
 修饰变量,变量是常量
 修饰方法,方法不能被重写
 finally:是异常处理的一部分,用于释放资源。
 一般来说,代码肯定会执行,特殊情况:在执行到finally之前jvm退出了
 finalize:是Object类的一个方法,用于垃圾回收

如果catch里面有return语句,finally里面的代码还会执行吗?
如果会,是在return前,还是return后。
会。前。
准确的说,应该是在中间。

    public static int getInt() {
        int a = 10;
        try {
            System.out.println(a / 0);
            a = 20;
        } catch (ArithmeticException e) {
            a = 30;
            return a;
            /*
             * return a在程序执行到这一步的时候,这里不是return a而是return 30;这个返回路径就形成了。
             *它发现后面还有finally,所以继续执行finally的内容,a=40
             * 再次回到以前的返回路径,继续走return 30;
             */
        } finally {
            a = 40;
            return a;//如果这样结果就是40了。
        }
        return a; //这里的语句不会执行

自定义异常

Java中定义了很多常见的异常类,但是在实际运行中也需要我们自己定义异常类。

自定义的异常类需要继承自继承自Exception或者RuntimeException,只需要提供无参构造和一个带参构造即可

//自定义异常类
public class MyException extends Exception {
    public MyException() {
    }

    public MyException(String message) {
        super(message);
    }
}
//老师类
public class Teacher {
    public void check(int score) throws MyException {
        if (score > 100 || score < 0) {
            throw new MyException("分数必须在0-100之间");
        } else {
            System.out.println("分数没有问题");
        }
    }
}
//主方法
import java.util.Scanner;
public class StudentDemo {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入学生成绩:");
        int score = sc.nextInt();

        Teacher t = new Teacher();
        try {
            t.check(score);
        } catch (MyException e) {
            e.printStackTrace();
        }
    }
}

技术分享

 如果继承自RuntimeException

public class Teacher {
    public void check(int score) throws MyException {
    //针对MyException继承自RuntimeException
    public void check(int score) {
    if (score > 100 || score < 0) {
        throw new MyException();
    } else {
        System.out.println("分数没有问题");
    }
    }
}
public class MyException extends RuntimeException {

}

异常的注意
A:父的方法有异常抛出,子的重写方法在抛出异常的时候必须要小于等于父的异常
B:父的方法没有异常抛出,子的重写方法不能有异常抛出
C:父的方法抛出多个异常,子的重写方法必须比父少或者小

 

以上是关于Java异常处理之数字溢出问题的主要内容,如果未能解决你的问题,请参考以下文章

Java基础之异常处理

Java之今天的异常处理了吗

学习Java(10)之异常处理

java内存区域与内存溢出异常

Java内存区域与内存溢出异常

java中异常处理