异常和错误的理解与解决
1、理解
异常是程序在运行期发生的****不正常的事件****,它会打断指令的正常执行流程。
设计良好的程序应该在异常发生时提供处理这些不正常事件的方法,使程序不会因为异常的发生而阻断或产生不可预见的结果。
Java语言使用异常处理机制为程序提供了****异常处理的能力****
2、分类
1、错误(Error)
JVM系统内部错误或资源耗尽等严重情况-****属于JVM需要负担的责任****这一类异常事件无法恢复或不可能捕获,将导致应用程序中断。
1、栈内存溢出的错误
package com.xx.exception01;
public class Test01 {
/**
* Error出现的情况
*
* 错误(Error):JVM系统内部错误或资源耗尽等严重情况-属于JVM需要负担的责
* 任这一类异常事件无法恢复或不可能捕获,将导致应用程序中断。
*/
public static void main(String[] args) {
//StackOverflowError - 栈内存溢出的错误
method();
}
public static void method(){
method();
}
}
2、内存溢出的错误
package com.xx.exception01;
import java.util.ArrayList;
public class Test02 {
/**
* Error出现的情况
*
* 错误(Error):JVM系统内部错误或资源耗尽等严重情况-属于JVM需要负担的责
* 任这一类异常事件无法恢复或不可能捕获,将导致应用程序中断。
*/
public static void main(String[] args) {
//OutOfMemoryError - 内存溢出的错误
//出现原因:创建出的数组对象的地址被集合存储,对象的空间就不会被释放,最终导致内存溢出
ArrayList<byte[]> list = new ArrayList<>();
while(true){
byte[] bs = new byte[1024];
list.add(bs);
}
}
}
2、异常(Exception)
其它因编程错误或偶然的外在因素导致的一般性问题。这类异常得到恰当的处理时,程序有机会恢复至正常运行状况。
1、类型转换异常:java.lang.ClassCastException
package com.xx.exception01;
public class Test05 {
/**
* Exception出现的情况
*
* 异常(Exception):其它因编程错误或偶然的外在因素导致的一般性问题。
* 这类异常得到恰当的处理时,程序有机会恢复至正常运行状况。
*
* RunntimeException - 运行时异常/非受检性异常:那些程序员在编写程序的时应该避免的异常(逻辑异常)
*/
public static void main(String[] args) {
//ClassCastException - 类型转换异常
Object obj = new Integer(100);
Double d = (Double) obj;
System.out.println(d);
}
}
2、数组下标越界异常:java.lang.ArrayIndexOutOfBoundsException
1、理解
- 错误的索引值:当你使用一个超出数组长度(索引从0开始到数组长度减1)的整数作为索引来访问数组元素时,就会抛出这个异常。
- 循环中的错误:在循环中访问数组时,如果循环条件或索引更新逻辑不正确,就可能导致索引越界。
- 负索引:虽然这在正常的编程实践中很少见,但如果你不小心使用了负数作为数组索引,也会抛出异常。
- 动态计算索引时的错误:有时索引可能是通过某种计算动态得出的,如果计算逻辑有误,也可能导致索引越界。
2、解决方案
- 检查并修正所有对数组索引的访问,确保索引值在有效范围内(0 到
array.length - 1
)。- 在循环中,确保循环条件正确地限制了索引的范围。
- 如果索引是动态计算的,请确保计算逻辑是正确的,并且结果始终在有效范围内。
- 使用异常处理(try-catch 块)来捕获并处理可能的
ArrayIndexOutOfBoundsException
,但这通常不是首选方法,因为更好的做法是避免异常的发生。
package com.qf.exception01;
public class Test06 {
/**
* 知识点:Exception出现的情况
*
* 异常(Exception):其它因编程错误或偶然的外在因素导致的一般性问题。
* 这类异常得到恰当的处理时,程序有机会恢复至正常运行状况。
*
* RunntimeException - 运行时异常/非受检性异常:那些程序员在编写程序的时应该避免的异常(逻辑异常)
*/
public static void main(String[] args) {
//ArrayIndexOutOfBoundsException - 数组下标越界异常
int[] arr = {1,2,3};
System.out.println(arr[10]);
}
}
3、空指针访问异常:java.lang.NullPointerException
1、理解
- 变量未初始化:当你声明了一个对象引用变量,但没有给它分配一个实际的对象时,它的默认值是
null
。如果此时你尝试使用这个变量去调用方法或访问字段,就会抛出空指针异常。- 方法返回
null
:如果一个方法应该返回一个对象,但由于某种原因返回了null
,而调用这个方法的代码没有检查这个返回值是否为null
就直接使用,也会导致空指针异常。- 数组或集合为空:当你尝试访问一个空数组或集合的元素时,虽然不会直接抛出
NullPointerException
,但如果你使用了错误的索引或键,可能会间接导致空指针异常,特别是当你使用索引或键来从一个对象(比如映射的值)中获取另一个对象时。- 静态初始化中的错误:在静态初始化块或静态字段初始化时,如果引用了尚未初始化的对象,也可能导致空指针异常。
2、解决方案
- 检查变量是否为
null
:在使用任何对象之前,确保它不是null
。- 对方法进行适当的错误处理:如果方法可能返回
null
,确保调用它的代码能够正确处理这种情况。- 避免空集合和数组:确保在访问集合或数组的元素之前,它们不是空的,或者使用了有效的索引或键。
- 使用Java 8的Optional类:这是一个可以帮助你更好地处理可能为
null
的值的类。- 阅读并理解堆栈跟踪:当空指针异常发生时,Java会打印出一个堆栈跟踪,显示异常发生的位置。阅读并理解这个堆栈跟踪可以帮助你找到问题的根源。
package com.xx.exception01;
public class Test04 {
public static void main(String[] args) {
//NullPointerException - 空指针异常
method(null);
}
public static void method(String str){
System.out.println(str.length());
}
}
4、算术异常(除0溢出):java.lang.ArithmeticException
package com.qf.exception01;
public class Test03 {
/**
* RunntimeException - 运行时异常/非受检性异常:那些程序员在编写程序的时应该避免的异常(逻辑异常)
*/
public static void main(String[] args) {
//ArithmeticException - 算数异常
System.out.println(10/0);
}
}
5、没有找到指定名称的类:java.lang.ClassNotFoundException
1、理解
- 类路径(Classpath)不正确:如果 JVM 没有在类路径中找到指定的类,则会抛出此异常。类路径是 JVM 用来查找类和资源的路径。
- 拼写或包名错误:类名或包名可能拼写错误,或者大小写不正确(Java 是大小写敏感的)。
- 编译时与运行时环境不一致:你可能在编译时使用了某个类,但在运行时环境中该类不存在。
- JAR 文件未包含:如果你的类在一个 JAR 文件中,但该 JAR 文件没有被添加到类路径中,也会导致这个异常。
- 动态加载类时出错:如果你使用
Class.forName()
或类加载器动态加载类,并且类不存在或路径错误,则会抛出此异常。
2、解决方法
- 检查类路径:确保你的类在 JVM 的类路径中。你可以通过
-cp
或-classpath
参数来指定类路径,或者在 IDE(如 IntelliJ IDEA、Eclipse 等)中设置项目的类路径。- 检查拼写和包名:确保类名和包名都是正确的,并且大小写也匹配。
- 清理和重建项目:如果你在使用 IDE,尝试清理并重建你的项目,以确保所有的类都正确编译和放置在正确的位置。
- 检查 JAR 文件:如果你使用了 JAR 文件,确保它们被正确地添加到类路径中。
- 检查动态加载代码:如果你在使用
Class.forName()
或类加载器动态加载类,请确保你传递的类名是完全限定的(包括包名),并且该类在类路径中可用。- 查看日志和异常堆栈跟踪:通常,当你遇到
ClassNotFoundException
时,JVM 会输出一个堆栈跟踪,显示是哪个类加载器在尝试加载类,以及是在哪里尝试加载的。这些信息可以帮助你定位问题。
6、访问不存在的文件:java.io.FileNotFoundException
1、理解
- 文件确实不存在:你尝试访问的文件在指定的路径下不存在。
- 文件路径错误:你提供的文件路径不正确或包含拼写错误。
- 文件路径指向目录:你尝试将目录作为文件来打开。
- 权限问题:虽然文件存在,但你的程序没有足够的权限去访问它。
- 磁盘空间不足:在某些情况下,如果磁盘空间不足,尝试创建新文件可能会失败,并抛出
FileNotFoundException
(尽管这更常见的是抛出IOException
或其他相关异常)。- 文件被另一个进程锁定:在某些操作系统中,如果文件被另一个进程锁定或独占,则可能导致无法访问。
2、解决方案
- 检查文件路径是否正确,并确保文件确实存在。
- 确保你的程序有足够的权限去访问文件。
- 捕获异常并优雅地处理它,例如通过向用户显示错误消息或尝试使用不同的文件路径。
7、操作文件时发送的异常:java.io.IOException
1、理解
- 文件不存在:尝试打开或读取一个不存在的文件。
- 文件路径错误:指定的文件路径不正确或无法访问。
- 权限问题:没有足够的权限来读取、写入或执行指定的文件。
- 磁盘空间不足:磁盘空间不足以完成操作。
- 磁盘错误:磁盘本身有问题,无法正确读写。
- 文件正在被其他程序使用:尝试打开或修改一个已被其他程序锁定或使用的文件。
- 文件系统错误:文件系统损坏或存在其他问题,导致无法正确访问文件。****
2、解决方案
- 捕获异常:在你的代码中,使用
try-catch
块来捕获可能抛出的IOException
。- 查看异常信息:通过
e.getMessage()
或e.printStackTrace()
获取异常的详细信息,这有助于确定问题的原因。- 修复问题:根据异常信息,确定问题所在并修复它。例如,如果文件不存在,你可能需要检查文件路径是否正确,或者创建一个新文件。
- 测试:在修复问题后,重新运行你的代码以确保问题已解决。
8、操作数据库时发生的异常:java.sql.SQLException
1、理解
这个异常通常表示在尝试连接到数据库、执行查询、更新或关闭连接时发生了问题。
2、处理步骤
- 捕获异常:在你的代码中,使用
try-catch
块来捕获可能抛出的SQLException
- 分析异常:在
catch
块中,你可以使用e.getMessage()
来获取异常的详细信息,这有助于你确定问题的根源。- 处理异常:根据异常的具体内容,你可以采取不同的修复措施。例如,如果是因为连接字符串不正确导致的异常,你需要检查并更新连接字符串。如果是因为表不存在或列名错误,你需要修正 SQL 语句。如果是因为权限问题,你需要检查数据库用户的权限设置。
- 资源清理:确保在
finally
块中关闭所有打开的数据库资源,如Connection
,Statement
,ResultSet
等。这有助于避免资源泄漏和潜在的内存问题。- 使用 try-with-resources:从 Java 7 开始,你可以使用 try-with-resources 语句来自动管理资源,它会在 try 代码块执行完毕后自动关闭资源。
- 查看文档和日志:如果问题仍然无法解决,查看数据库的文档和日志可能会提供有关问题的更多信息。
- 考虑使用数据库连接池:在生产环境中,使用数据库连接池(如 HikariCP, C3P0, Apache DBCP 等)可以提高性能和稳定性,并减少因频繁创建和关闭连接而导致的异常。