Java异常基础


Error和Exception有什么区别?

  • Throwable是java.lang包中一个专门用来处理异常的类。在Java中只有Throwable 类型的实例才能被抛出或者捕获。它有两个子类,即ErrorException,它们分别用来处理两组异常。
    在这里插入图片描述

  • Error(错误)是系统中的错误,程序员是不能改变的和处理的,是在程序编译时出现的错误,只能通过修改程序才能修正。一般是用来处理程序运行环境方面的异常,指与虚拟机相关的问题,如虚拟机错误、装载错误和连接错误,这类异常主要是和硬件有关的,而不是由程序本身抛出的。对于这类错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。Error是非检查异常,OutOfMemoryError 这些也是编译期无法检查出来的。

  • Exception(异常)表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。

Exception分类

Exception 可以分为检查异常(CheckedException)和非检查异常(UnCheckedException)。

  • 检查异常在我们的源代码中必须显示地进行捕获处理,且没有继承 RuntimeException,这类问题在编译期就可以确定的问题,比如 FileNotFoundExceptionIOException。

编译器对检查异常的处理要求:
1.使用 try…catch 捕获。
2. 不断向上抛出,交由 JVM 来处理。

对于检查异常,需要尽快处理,如果任由异常不断上抛,每次上抛 JVM 都会获取抛出异常方法相应的堆栈信息,对性能会有一定的影响。

  • 非检查异常继承了RuntimeException,也叫运行时异常,这类问题大部分属于代码逻辑问题,只有在运行时才能知道是否有问题,异常在编译时不会检查。

编译器对非检查异常的处理要求:
1.使用 try…catch 捕获。
2. 不断向上抛出
3.不处理。

在实际开发中,对于可能存在问题的点最好是用 try…catch 根据 Exception 的类型进行分类处理,减少因为可预见的异常导致的系统故障。

常见运行时异常

表 1 Java中常见运行时异常

异常类型说明
ArithmeticException算术错误异常,如以零做除数
ArraylndexOutOfBoundException数组索引越界
ArrayStoreException向类型不兼容的数组元素赋值
ClassCastException类型转换异常
IllegalArgumentException使用非法实参调用方法
lIIegalStateException环境或应用程序处于不正确的状态
lIIegalThreadStateException被请求的操作与当前线程状态不兼容
IndexOutOfBoundsException某种类型的索引越界
NullPointerException尝试访问 null 对象成员,空指针异常
NegativeArraySizeException再负数范围内创建的数组
NumberFormatException数字转化格式异常,比如字符串到 float 型数字的转换无效
TypeNotPresentException类型未找到

表 2 Java常见非运行时异常

异常类型说明
ClassNotFoundException没有找到类
IllegalAccessException访问类被拒绝
InstantiationException试图创建抽象类或接口的对象
InterruptedException线程被另一个线程中断
NoSuchFieldException请求的域不存在
NoSuchMethodException请求的方法不存在
ReflectiveOperationException与反射有关的异常的超类

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

A:运行时期异常:
是不需要throws或try…catch处理的,只需要修改代码
B:编译时期异常:
需要处理的两种方式throws或try…catch 在开发中异常如果能够捕获最好要捕获处理。

Java异常关键字

  • try – 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
  • catch – 用于捕获异常。catch用来捕获try语句块中发生的异常。
  • finally – finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。只有finally块,执行完成之后,才会回来执行try或者catch块中的return或者throw语句,如果finally中使用了return或者throw等终止方法的语句,则就不会跳回执行,直接停止。
  • throw – 用于抛出异常。
  • throws – 用在方法签名中,用于声明该方法可能抛出的异常。

throw和throws的用法和区别?

A:throw
用法:用在方法内部,后面跟的是异常对象名称,抛出异常 区别:
用throw抛出了编译时期异常,方法上面必须用throws抛出。 用throw抛出了运行时期异常,方法上面可以不用throws抛出。

B:throws 用法:用在方法声明上,后面跟的是异常类名,声明异常
区别:用throws在方法上声明了异常,内部可以没有throw

  • throws:用来声明一个方法可能产生的所有异常,不做任何处理而是将异常往上传,谁调用我,我就抛给谁。
    • 用在方法声明后面,跟的是异常类名
    • 可以跟多个异常类名,用逗号隔开
    • 表示抛出异常,由该方法的调用者来处理
    • throws表示出现异常的一种可能性,并不一定会发生这些异常
  • throw:则是用来抛出一个具体的异常类型。
    • 用在方法体内,跟的是异常对象名
    • 只能抛出一个异常对象名
    • 表示抛出异常,由方法体内的语句处理
    • throw则是抛出了异常,执行throw则一定抛出了某种异常
package 异常;

import java.io.IOException;

/**
 * 区别一:throw 是语句抛出一个异常;throws 是方法抛出一个异常;
 *      throw语法:throw <异常对象>
 *      在方法声明中,添加throws子句表示该方法将抛出异常。
 *      throws语法:[<修饰符>]<返回值类型><方法名>([<参数列表>])[throws<异常类>]
 *          其中:异常类可以声明多个,用逗号分割。
 * 区别二:throws可以单独使用,但throw不能;
 * 区别三:throw要么和try-catch-finally语句配套使用,要么与throws配套使用。但throws可以单独使用,然后再由处理异常的方法捕获。
 */

public class TestThrowsThrow {

    public static void main(String[] args) {
        testThrows();

        Integer i = null;
        testThrow(i);

        String filePath = null;
        try {
            testThrow(filePath);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 测试 throws 关键字
     * @throws NullPointerException
     */
    public static void testThrows() throws NullPointerException {
        Integer i = null;
        System.out.println(i + 1);
    }

    /**
     * 测试 throw 关键字抛出 运行时异常
     * @param i
     */
    public static void testThrow(Integer i) {
        if (i == null) {
            throw new NullPointerException();//运行时异常不需要在方法上申明
        }
    }

    /**
     * 测试 throw 关键字抛出 非运行时异常,需要方法体需要加 throws 异常抛出申明
     * @param i
     */
    public static void testThrow(String filePath) throws IOException {
        if (filePath == null) {
            throw new IOException();//运行时异常不需要在方法上申明
        }
    }
}

Exception和RuntimeException的区别?

A:Exception编译时期异常,必须处理的。

  • 如果在方法上,throws了该类型的异常,将来调用者必须处理。
  • 如果在方法内部,throw了该类型的异常,必须在方法上throws该异常。

B:RuntimeException运行时期异常,是不需要处理的。要改代码的。

  • 如果在方法上,throws了该类型的异常,不需要处理。
  • 如果在方法内部,throw了该类型的异常,方法上可以throws该异常,也可以不throws该异常。

final,finally,finalize的区别

  • final:是个关键字

它修饰的类不能被继承;
它修饰的方法不能不重写;
它修饰的变量就变成了常量。

package 异常;
import java.util.Random;

public class FinalTest {
    /**
     *  Random r=new Random() :每次运行程序时seedValue不一样,得到的随机数序列不一样,一般会这么用
     *
     * Random r=new Random(seedValue):  :每次运行程序得到的随机数序列都是一样的。
     * 例如第一次运行程序得到的随机数是 2, 4, 1, 5, 7。那么重启程序,再次得到的随机数还是2, 4, 1, 5, 7
     *
     * public int nextInt(int n)
     *
     * 该方法的作用是生成一个随机的int值,该值介于[0,n)的区间,也就是0到n之间的随机int值,包含0而不包含n。
     */
    private static Random rand = new Random(30);
    //static修饰变量时,该变量将只被初始化一次,此后不再重新初始化。
    private static final int var = rand.nextInt(15);//不变
    private static int var4=rand.nextInt(10);//不变
    //运行时被初始化值
    //final修饰变量时,该变量在类加载时就会被初始化,会因为对象的创建而创建加载。
    private final int var2 = rand.nextInt(18);//变
    private final int var3;//空白final 必须在构造器内赋值
    public FinalTest() {
        // TODO Auto-generated constructor stub
        var3 = 9;//不变
    }
    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        FinalTest f = new FinalTest();
        FinalTest f2 =new FinalTest();
        System.out.println(f);
        System.out.println(f2);
    }

    public String toString() {
        return "var的值:" +var + ";var2的值:--" + var2 + ";var3的值:--" + var3+
                 ";var4的值:--" + var4;
    }
}
  • finally:是一个关键字。是异常处理的一部分。
  • 一般用在捕获异常时,它里面的代码永远会被执行,而不管无异常发生。(特殊情况在执行到finally之前就退出了虚拟机)。不管有没有异常被抛出、捕获,finally块会被执行。
  • try块中的内容是在无异常时执行到结束。catch块中的内容,是在try块内容发生catch所声明的异常时,跳转到catch块中执行。finally块则是无论异常是否发生,都会执行finally块的内容,所以在代码逻辑中有需要无论发生什么都必须执行的代码,就可以放在finally块中。
  • finalize():是Object类中垃圾回收的方法。
  • java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。
  • 这个方法是由垃圾收集器在确定这个对象没有被引用时对这个对象调用的。
  • 它是在object类中定义的,因此所有的类都继承了它。子类覆盖finalize()方法以整理系统资源或者被执行其他清理工作。
  • finalize()方法是在垃圾收集器删除对象之前对这个对象调用的。

try-catch

try-catch-finally

  • 当方法中发生异常,异常处之后的代码不会再执行,如果之前获取了一些本地资源需要释放,则需要在方法正常结束时和 catch 语句中都调用释放本地资源的代码,显得代码比较繁琐,finally 语句可以解决这个问题。
  • 调用该方法时,读取文件时若发生异常,代码会进入 catch 代码块,之后进入 finally 代码块;若读取文件时未发生异常,则会跳过 catch 代码块直接进入 finally 代码块。所以无论代码中是否发生异常,fianlly 中的代码都会执行。
  • 若 catch 代码块中包含 return 语句,finally 中的代码还会执行吗?即使 catch 中包含了 return 语句,finally 子句依然会执行。若 finally 中也包含 return 语句,finally 中的 return 会覆盖前面的 return.

try-with-resource

• finally 中的 close 方法也可能抛出 IOException, 从而覆盖了原始异常。JAVA 7 提供了更优雅的方式来实现资源的自动释放,自动释放的资源需要是实现了 AutoCloseable 接口的类。

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

会执行。在return前执行。

public class Demo {
	public static void main(String[] args) {
		//测试c的值
		int c = method(10, 0);
		System.out.println("c = " + c);	//20
	}
 
	public static int method(int a, int b) {
		int c = 10;
		try {
          //可能出现异常
			System.out.println(a / b);
		} catch (ArithmeticException e) {
          //打印异常信息
			e.printStackTrace();
			return 20;
		} finally {
			c = 30;
		}
		return c;
	}
}
1.  当执行【e.printStackTrace();】完后,控制台出现异常提示,
2.  然后没有接着执行【return 20;】,而是跳转到finally语句,执行了【c = 30;】,
3.  然后才回去执行【return 20;】,
4.  所以最后打印c = 20

在finally中,修改return 的返回值变量,最后返回值究竟有没有发生改变?

  • finally里的 return语句会把 try/catch块里的 return语句效果给覆盖掉
  • 若返回值类型是:基本数据类型,finally 中修改返回值变量,返回值不会改变【值传递】
  • 若返回值类型是:引用数据类型,finally 中修改返回值变量,返回值会改变【引用传递】

JVM 是如何处理异常的?

• 在一个方法中如果发生异常,这个方法会创建一个异常对象,并转交给 JVM,该异常对象包含异常名称,异常描述以及异常发生时应用程序的状态。创建异常对象并转交给 JVM 的过程称为抛出异常。可能有一系列的方法调用,最终才进入抛出异常的方法,这一系列方法调用的有序列表叫做调用栈
• JVM 会顺着调用栈去查找看是否有可以处理异常的代码,如果有,则调用异常处理代码。当 JVM 发现可以处理异常的代码时,会把发生的异常传递给它。如果 JVM 没有找到可以处理该异常的代码块,JVM 就会将该异常转交给默认的异常处理器(默认处理器为 JVM 的一部分),默认异常处理器打印出异常信息并终止应用程序。

常见异常处理方式

直接抛出异常

  • 通常,应该捕获那些知道如何处理的异常,将不知道如何处理的异常继续传递下去。传递异常可以在方法签名处使用 throws 关键字声明可能会抛出的异常。

封装异常再抛出

  • 有时我们会从 catch 中抛出一个异常,目的是为了改变异常的类型。多用于在多系统集成时,当某个子系统故障,异常类型可能有多种,可以用统一的异常类型向外暴露,不需暴露太多内部异常细节。

捕获异常

  • 在一个 try-catch 语句块中可以捕获多个异常类型,并对不同类型的异常做出不同的处理
  • 同一个 catch 也可以捕获多种类型异常,用 | 隔开

自定义异常

  • 习惯上,定义一个异常类应包含两个构造函数,一个无参构造函数和一个带有详细描述信息的构造函数(Throwable 的 toString 方法会打印这些详细信息,调试时很有用)

NoClassDefFoundErrorClassNotFoundException 区别?

  • NoClassDefFoundError 是一个 Error 类型的异常,是由 JVM 引起的,不应该尝试捕获这个异常。
    • 引起该异常的原因是 JVMClassLoader 尝试加载某类时在内存中找不到该类的定义,该动作发生在运行期间,即编译时该类存在,但是在运行时却找不到了,可能是变异后被删除了等原因导致;
  • ClassNotFoundException 是一个受查异常,需要显式地使用 try-catch 对其进行捕获和处理,或在方法签名中用 throws 关键字进行声明。
    • 当使用 Class.forName, ClassLoader.loadClassClassLoader.findSystemClass 动态加载类到内存的时候,通过传入的类路径参数没有找到该类,就会抛出该异常;另一种抛出该异常的可能原因是某个类已经由一个类加载器加载至内存中,另一个加载器又尝试去加载它。

Java异常处理最佳实践

最佳实践,主要参考这位博主的:https://juejin.cn/post/6844904128959741965#heading-46

在 finally 块中清理资源或者使用 try-with-resource 语句

  • 当使用类似InputStream这种需要使用后关闭的资源时,一个常见的错误就是在try块的最后关闭资源。
  • 问题就是,只有没有异常抛出的时候,这段代码才可以正常工作。try 代码块内代码会正常执行,并且资源可以正常关闭。但是,使用 try 代码块是有原因的,一般调用一个或多个可能抛出异常的方法,而且,你自己也可能会抛出一个异常,这意味着代码可能不会执行到 try 代码块的最后部分。结果就是,你并没有关闭资源。
  • 所以,你应该把清理工作的代码放到 finally 里去,或者使用 try-with-resource 特性。

使用 finally 代码块

  • 与前面几行 try 代码块不同,finally 代码块总是会被执行。不管 try 代码块成功执行之后还是你在 catch 代码块中处理完异常后都会执行。因此,你可以确保你清理了所有打开的资源。
public void closeResourceInFinally() {
    FileInputStream inputStream = null;
    try {
        File file = new File("./tmp.txt");
        inputStream = new FileInputStream(file);
        // use the inputStream to read a file
    } catch (FileNotFoundException e) {
        log.error(e);
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close();
            } catch (IOException e) {
                log.error(e);
            }
        }
    }}

Java 7 的 try-with-resource 语法

  • 如果你的资源实现了 AutoCloseable 接口,你可以使用这个语法。大多数的 Java 标准资源都继承了这个接口。当你在 try 子句中打开资源,资源会在 try 代码块执行后或异常处理后自动关闭。
public void automaticallyCloseResource() {
    File file = new File("./tmp.txt");
    try (FileInputStream inputStream = new FileInputStream(file);) {
        // use the inputStream to read a file
    } catch (FileNotFoundException e) {
        log.error(e);
    } catch (IOException e) {
        log.error(e);
    }
}

优先明确的异常

  • 你抛出的异常越明确越好,永远记住,你的同事或者几个月之后的你,将会调用你的方法并且处理异常。
  • 因此需要保证提供给他们尽可能多的信息。这样你的 API 更容易被理解。你的方法的调用者能够更好的处理异常并且避免额外的检查。
  • 因此,总是尝试寻找最适合你的异常事件的类,例如,抛出一个 NumberFormatException 来替换一个 IllegalArgumentException 。避免抛出一个不明确的异常。

对异常进行文档说明

  • 当在方法上声明抛出异常时,也需要进行文档说明。目的是为了给调用者提供尽可能多的信息,从而可以更好地避免或处理异常。
    在 Javadoc 添加 @throws 声明,并且描述抛出异常的场景。

使用描述性消息抛出异常

  • 在抛出异常时,需要尽可能精确地描述问题和相关信息,这样无论是打印到日志中还是在监控工具中,都能够更容易被人阅读,从而可以更好地定位具体错误信息、错误的严重程度等。
  • 但这里并不是说要对错误信息长篇大论,因为本来 Exception 的类名就能够反映错误的原因,因此只需要用一到两句话描述即可。
  • 如果抛出一个特定的异常,它的类名很可能已经描述了这种错误。所以,你不需要提供很多额外的信息。一个很好的例子是 NumberFormatException 。当你以错误的格式提供 String 时,它将被 java.lang.Long 类的构造函数抛出。

优先捕获最具体的异常

  • 大多数 IDE 都可以帮助你实现这个最佳实践。当你尝试首先捕获较不具体的异常时,它们会报告无法访问的代码块。
  • 但问题在于,只有匹配异常的第一个 catch 块会被执行。 因此,如果首先捕获 IllegalArgumentException ,则永远不会到达应该处理更具体的 NumberFormatException 的 catch 块,因为它是 IllegalArgumentException 的子类。
  • 总是优先捕获最具体的异常类,并将不太具体的 catch 块添加到列表的末尾。

不要捕获 Throwable 类

  • Throwable 是所有异常和错误的超类。你可以在 catch 子句中使用它,但是你永远不应该这样做!
  • 如果在 catch 子句中使用 Throwable ,它不仅会捕获所有异常,也将捕获所有的错误。JVM 抛出错误,指出不应该由应用程序处理的严重问题。 典型的例子是 OutOfMemoryError 或者 StackOverflowError 。两者都是由应用程序控制之外的情况引起的,无法处理。
  • 所以,最好不要捕获 Throwable ,除非你确定自己处于一种特殊的情况下能够处理错误。

不要忽略异常

  • 很多时候,开发者很有自信不会抛出异常,因此写了一个catch块,但是没有做任何处理或者记录日志。
  • 但现实是经常会出现无法预料的异常,或者无法确定这里的代码未来是不是会改动(删除了阻止异常抛出的代码),而此时由于异常被捕获,使得无法拿到足够的错误信息来定位问题。
  • 合理的做法是至少要记录异常的信息。

不要记录并抛出异常

  • 这可能是本文中最常被忽略的最佳实践。可以发现很多代码甚至类库中都会有捕获异常、记录日志并再次抛出的逻辑。仅仅当想要处理异常时才去捕获,否则只需要在方法签名中声明让调用者去处理。

包装异常时不要抛弃原始的异常

  • 捕获标准异常并包装为自定义异常是一个很常见的做法。这样可以添加更为具体的异常信息并能够做针对的异常处理。在你这样做时,请确保将原始异常设置为原因(注:参考下方代码 NumberFormatException e 中的原始异常 e )。Exception 类提供了特殊的构造函数方法,它接受一个 Throwable 作为参数。否则,你将会丢失堆栈跟踪和原始异常的消息,这将会使分析导致异常的异常事件变得困难。

不要使用异常控制程序的流程

  • 不应该使用异常控制应用的执行流程,例如,本应该使用if语句进行条件判断的情况下,你却使用异常处理,这是非常不好的习惯,会严重影响应用的性能。

使用标准异常

  • 如果使用内建的异常可以解决问题,就不要定义自己的异常。Java API 提供了上百种针对不同情况的异常类型,在开发中首先尽可能使用 Java API 提供的异常,如果标准的异常不能满足你的要求,这时候创建自己的定制异常。尽可能得使用标准异常有利于新加入的开发者看懂项目代码。

异常会影响性能

  • 异常处理的性能成本非常高,每个 Java 程序员在开发时都应牢记这句话。创建一个异常非常慢,抛出一个异常又会消耗1~5ms,当一个异常在应用的多个层级之间传递时,会拖累整个应用的性能。
    • 仅在异常情况下使用异常;
    • 在可恢复的异常情况下使用异常;
  • 尽管使用异常有利于 Java 开发,但是在应用中最好不要捕获太多的调用栈,因为在很多情况下都不需要打印调用栈就知道哪里出错了。因此,异常消息应该提供恰到好处的信息。

• 综上所述,当你抛出或捕获异常的时候,有很多不同的情况需要考虑,而且大部分事情都是为了改善代码的可读性或者 API 的可用性。
• 异常不仅仅是一个错误控制机制,也是一个通信媒介。因此,为了和同事更好的合作,一个团队必须要制定出一个最佳实践和规则,只有这样,团队成员才能理解这些通用概念,同时在工作中使用它。

异常练习

货船载重问题

題目內容:
模拟向货船上装载集装箱,每个集装箱有一定重量,该重量(整数)大于100小于1000,
货船总重为1000,装载若干集装箱后,如果货船超重,那么货船认为这是一个异常,将拒绝装载集装箱,
但无论是否发生异常,货船都需要正点启航。

package 异常;

import java.util.Scanner;

/**
 * 題目內容:
 * 模拟向货船上装载集装箱,每个集装箱有一定重量,该重量(整数)大于100小于1000,
 * 货船总重为1000,装载若干集装箱后,如果货船超重,那么货船认为这是一个异常,将拒绝装载集装箱,
 * 但无论是否发生异常,货船都需要正点启航。
 */
class DangerException extends Exception {
    //starts
    public void showError() {
        System.out.println("超重");
    }
    //end
}
class CargoBoat {
    int realContent;  //装载的重量
    int maxContent;   //最大装载量
    public void setMaxContent(int c) {
        maxContent = c;
    }
    public void loading(int m) throws DangerException {
        //starts
        realContent+=m;
        if(realContent>maxContent)throw new DangerException();
        System.out.printf("目前装载了%d吨货物\n",realContent);
        //end
    }
}
public class CargoBoatTest {
    public static void main(String args[]) {
        CargoBoat ship = new CargoBoat();
        Scanner s=new Scanner(System.in);
        ship.setMaxContent(1000);
        int m=0;
        try{
            //starts
            while(true) {
                m=s.nextInt();
                ship.loading(m);
            }
            //end
        } catch(DangerException e) {
            //starts
            e.showError();
            System.out.printf("无法再装载重量是%d吨的集装箱\n",m);
            //end
        } finally {
            System.out.printf("货船将正点启航");
        }
    }
}


写一个方法void triangle(int a,int b,int c),判断三个参数是否能构成一个三角形。如果不能则抛出异常

package 异常;

import java.util.InputMismatchException;

import java.util.Arrays;
import java.util.InputMismatchException;
import java.util.Scanner;

/**
 * 題目內容:
 * 写一个方法void triangle(int a,int b,int c),判断三个参数是否能构成一个三角形。如果不能则抛出异常IllegalArgumentException,显示异常信息:“a,b,c不能构成三角形”;如果可以构成则显示三角形三个边长。在主方法中得到命令行输入的三个整数,调用此方法,并捕获异常。
 * 输入不匹配异常抛出InputMismatchException,非法数据异常抛出IllegalArgumentException。
 */
class TestTriangle {
    static void triangle(int a, int b,int c) throws IllegalArgumentException,InputMismatchException{
        //starts
        if(a<0||b<0||c<0||a+b<=c||a+c<=b||b+c<=a) throw new IllegalArgumentException();
        System.out.printf("三角形的三边长为:%d,%d,%d\n",a,b,c);
        //end
    }
}
public class TriangleTest {
    public static void main(String[] args) {
        int a=0, b=0, c=0;
        Scanner in = new Scanner(System.in);
        try{
            //starts
            a = in.nextInt();
            b = in.nextInt();
            c = in.nextInt();
            TestTriangle.triangle(a,b,c);
            //end
        }
        catch(InputMismatchException e) {
            System.err.println("请输入整数作为三角形的边长!");
        }
        catch(IllegalArgumentException e) {
            System.err.println("a,b,c不能构成三角形");
        }
    }
}

==与equals的区别

== 在基本数据类型:值内容, 引用类型时:地址
equals 重写:值内容 , equals不重写:地址

  • 对象类型不同
    equals():是超类Object中的方法。
    ==:是操作符。
  • 比较的对象不同
    equals():重写:值内容,equals不重写:地址
    ==:用于比较引用和比较基本数据类型时具有不同的功能,具体如下:
    (1)基础数据类型:比较的是他们的值是否相等,比如两个int类型的变量,比较的是变量的值是否一样。
    (2)引用数据类型:比较的是引用的地址是否相同,比如说新建了两个User对象,比较的是两个User的地址是否一样。
  • 运行速度不同
    equals():没有 == 运行速度快。
    ==:运行速度比equals()快,因为==只是比较引用。

自动装箱与拆箱

• 装箱:
自动将基本数据类型转换为包装器类型
调用方法:Integer 的 valueOf (int) 方法
• 拆箱:
自动将包装器类型转换为基本数据类型
调用方法:Integer 的 intValue 方法
==什么时候会自动拆箱?
包装类 == 运算在不遇到算术运算的情况下不会自动拆箱。即只有遇到运算符才会自动拆箱。
boxing装箱:将基本类型—转换成—对应的包装类;
unboxing拆箱:将包装类型—转换成—基本数据类型;
Java使用自动装箱和拆箱机制,节省了常用数值的内存开销和创建对象的开销,提高了效率,由编译器来完成,编译器会在编译期根据语法决定是否进行装箱和拆箱动作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值