JavaSE基础总结
目录
- 初始Java
- 数据类型和变量
- 运算符
- 逻辑控制
- 方法
- 数组
- 类和对象
- 继承和多态
- 抽象类和接口
- String
- 异常
1、初始java
Java之父——詹姆斯.高斯林
Java语言的命名(爪哇岛。盛产咖啡)和咖啡图标都反映了詹姆斯。高斯林热爱咖啡。
1、“一次编译,到处运行”
Java是一门半编译型、半解释型语言。先通过javac编译程序把源文件进行编译,编译后生成的.class文件是由字节码组成的平台无关、面向JVM的文件。最后启动java虚拟机来运行.class文件,此时JVM会将字节码转换成平台能够理解的形式来运行。正是因为生成的字节码文件面向JVM,与平台无关,Java才具有如此强大的跨平台性。
2、JDK、JRE、JVM之间的关系
- JDK(Java Development Kit):Java开发工具包,提供给Java程序员使用,包含了JRE,同时还包含了编译器javac与自带的调试工具Jconsole、jstack等。
- JRE(Java Runtime Environment):Java运行时环境,包含了JVM,Java基础类库。是使用Java语言编写程序运行的所需环境。
- JVM:Java虚拟机,运行Java代码
3、Java注释
Java中的注释主要分为以下三种
- 单行注释:// 注释内容(用的最多)
- 多行注释:/* 注释内容*/(不推荐)
- 文档注释: /** 文档注释 */(常见于方法和类之上描述方法和类的作用),可以被javadoc工具成一套以网页文件形式体现的程序说明文档。
注意:
- 多行注释不能嵌套使用
- 不论是单行还是多行注释,都不参与编译,即编译之后生成的.class文件中不包含注释信息
4、标识符
在程序中用户给类名、方法名或者变量所取的名字叫做标识符。
硬性规则:
标识符中可以包含:字母、数字以及 下划线和 $ 符号等等。
注意:标识符不能以数字开头,也不能是关键字,且严格区分大小写。
软性规则:
- 类名:类名:每个单词的首字母大写(大驼峰)
- 方法名:首字母小写,后面每个单词的首字母大写(小驼峰)
- 变量名:与方法名规则相同
2、数据类型和变量
在Java中数据类型主要分为两类:基本数据类型和引用数据类型。
注意:
- 不论是在16位系统还是32位系统,int都占用4个字节,long都占8个字节
- 整型和浮点型都是带有符号的
- 整型默认为int型,浮点型默认为double
1、变量的定义与使用
变量在使用之前必须要赋初值,否则编译报错,推荐没有合适初始值设为0,(boolean类型变量设置为false)
整型变量(int):不论在哪个系统下都占四个字节;
长整型变量(long):初始值后加L或者l,推荐加L; 长整型不论在哪个系统下都占8个字节
短整型变量(short):在任何系统下都占2个字节
字节型变量(byte):在任何系统下都占1个字节,范围是:-128 ~ 127
单精度浮点型变量(float):在 Java 中占四个字节
双精度浮点型变量(double):在任何系统下都占8个字节,double 类型的内存布局遵守 IEEE 754 标准(和C语言一样), 尝试使用有限的内存空间表示可能无限的小数, 势必会存在一定的精度误差,因此浮点数是个近似值,并不是精确值。
字符型变量(char):
1、Java 中使用 单引号 + 单个字母 的形式表示字符字面值。
2. 计算机中的字符本质上是个整数. 在 C 语言中使用 ASCII 表示字符, 而 Java 中使用 Unicode 表示字符. 因此一个字符占用两个字节, 表示的字符种类更多, 包括中文
布尔型变量:
1、boolean 类型的变量只有两种取值, true 表示真, false 表示假。
3. Java 的 boolean 类型和 int 不能相互转换, 不存在 1 表示 true, 0 表示 false 这样的用法。
2、类型转换及提升
自动类型转换(隐式):当小范围和大范围运算时,编译器自动将小范围的变量转换为大范围的变量(比如int和long相加,就会将int类型转换为long类型)反之,编译器则会报错,因为大范围转换成小范围(把long类型变量的值赋给int类型变量)可能会有数据丢失。
强制类型转换(显式):
int a = 10;
long b = 100L;
b = a; // int-->long,数据范围由小到大,隐式转换
a = (int)b; // long-->int, 数据范围由大到小,需要强转,否则编译失败
byte与byte的运算:
byte a = 10;
byte b = 20;
byte c = a + b;
System.out.println(c);
// 编译报错
Test.java:5: 错误: 不兼容的类型: 从int转换到byte可能会有损失
byte c = a + b;
^
这是因为计算机的 CPU 通常是按照 4 个字节为单位从内存中读写数据。 为了硬件上实现方便, 诸如 byte 和 short这种低于 4 个字节的类型, 会先提升成 int, 再参与计算。byte类型提升为int类型进行运算,但又不能将int类型的变量自动赋值给byte类型的变量,编译报错。正确的写法:
byte a = 10;
byte b = 20;
byte c = (byte)(a + b);
System.out.println(c);
3、运算符
1、新的右移符号
Java种引入了一个新的运算符‘>>>’,无符号右移(逻辑右移),左空位补0
2、高效的移位符
由于计算机计算移位效率高于计算乘除, 当某个代码正好乘除 2 的N次方的时候可以用移位运算代替。但是,移动负数位或者移位位数过大都没有意义。
3、逻辑短路原则
Java中仍然保存了逻辑短路原则,保证了运行时的效率、避免了异常的发生。
逻辑短路原则:
在逻辑与中,如果出现false不再向后判断条件。
在逻辑或中,如果出现true后不再向后判断条件。
下面是一个例子:
if(x!=0&&y/x==1)//如果x为0,判断直接结束,不会执行后续的判断,避免了不能除以0的数学错误
4、逻辑控制
在Java中,仍然遵循顺序、分支、循环三种结构的控制体系。在语法中与C语言的逻辑控制语句差别不大。唯一值得注意的是:
1、判断条件
判断的条件必须是一个bool表达式(boolean类型变量),也就意味着下图所示的代码会出现编译错误。
int size = 10;
while(size--) {
//code
}
正确的写法:
int size = 10;
while(size!=0) {
//code
size--;
}
其实,这或许是出于安全考虑,如果我们将一个不可自减的字符型变量放入其中,后果将不堪设想。
2、循环输入
while(scanner.hasNextInt()) {
//code
}
5、方法
1、方法的概念
方法就是一个代码片段, 类似于 C 语言中的 “函数”。方法存在的意义:
- 是能够模块化的组织代码(当代码规模比较复杂的时候).
- 做到代码被重复使用, 一份代码可以在多个位置使用.
- 让代码更好理解更简单.
- 直接调用现有方法开发, 不必重复造轮子
注意:
5. 在java当中,方法必须写在类当中
6. 在java当中,方法不能嵌套定义
7. 在java当中,没有方法声明一说
2、方法形参和实参的关系
Java中方法的形参只是方法在定义时需要借助的一个变量,用来保存方法在调用时传递过来的值。
注意:对于基础类型来说, 形参相当于实参的拷贝. 即传值调用。
3、方法重载
在Java中,如果多个方法的名字相同,参数列表不同,返回值类型可相同可不同,则称该几种方法被重载了。
6、数组
1、数组的创建及初始化
数组创建:
T[] 数组名 = new T[N];
//T:表示数组中存放元素的类型
//T[]:表示数组的类型
//N:表示数组的长度
数组初始化:分为静态初始化和动态初始化。
动态初始化在创建数组时,直接指定数组中元素的个数。
int[] array = new int[10];
静态初始化在创建数组时,不直接指定数据元素个数,而直接将具体的数据内容进行指定。
int[] array1 = new int[]{0,1,2,3,4,5,6,7,8,9};
2、JVM内存分布
由于程序对内存中存储数据的要求,JVM也对所使用的内存按照功能的不同进行了划分:
- 程序计数器 (PC Register): 只是一个很小的空间, 保存下一条执行的指令的地址
- 虚拟机栈(JVM Stack): 与方法调用相关的一些信息,每个方法在执行时,都会先创建一个栈帧,栈帧中包含有:局部变量表、操作数栈、动态链接、返回地址以及其他的一些信息,保存的都是与方法执行时相关的一些信息。比如:局部变量。当方法运行结束后,栈帧就被销毁了,即栈帧中保存的数据也被销毁了。
- 本地方法栈(Native Method Stack): 本地方法栈与虚拟机栈的作用类似. 只不过保存的内容是Native方法的局部变量. 在有些版本的 JVM 实现中(例如HotSpot), 本地方法栈和虚拟机栈是一起的
- 堆(Heap): JVM所管理的最大内存区域. 使用 new 创建的对象都是在堆上保存 (例如前面的 new int[]{1, 2,3} ),堆是随着程序开始运行时而创建,随着程序的退出而销毁,堆中的数据只要还有在使用,就不会被销
毁。
方法区(Method Area): 用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据. 方法编译出的的字节码就是保存在这个区域
3、数组是一种引用类型
引用变量并不直接存储对象本身,可以简单理解成存储的是对象在堆中空间的起始地址。通过该地址,引用变量便可以去操作对象。有点类似C语言中的指针,但是Java中引用要比指针的操作更简单。
null 在 Java 中表示 “空引用” , 也就是一个不指向对象的引用(通常可以作为赋给引用变量的初值)注意: Java 中并没有约定 null 和 0 号地址的内存有任何关联
7、类和对象
类和对象的详细讲解请看之前发布的博客,在此仅仅只总结最为重要的内容。
类和对象(上)
类和对象(下)
1、类中方法的调用
如果是普通的成员方法,需要通过对象的引用来访问,如果是静态的成员方法,需要类名来访问。
2、this引用
当前对象的引用可以通过关键字this:
- 访问自己的成员变量
- 访问自己的成员方法
- 调用自己的构造方法
谁调用某个方法,谁就是this
3、构造方法
构造方法:方法名和类名一样,必须用public修饰,没有返回值。可以有多个,意味着可以被重载。
- 调用时机:在实例化对象时,构造方法被调用。
- 默认提供:如果一个类没有提供任何构造方法,默认会有一个不带参数的构造方法。
- 方法重载:如果如果一个类提供了构造方法,不会执行默认不带参数的构造方法。
- 默认初始化:没有初始化时,有一个默认的值,引用类型默认为null,boolean类型默认为false。
4、封装
封装是隐藏了类内部的实现细节,通过公开的方法来操作这些数据。在实现上,采用了private修饰成员变量或成员方法,提高了类的安全性。
实现封装后,我们可以通过get和set方法来操作被封装的数据或者方法。
限定修饰符:共有public、protected、default(什么都不写)、private。
- public修饰的类可以被不同包中的非子类、不同包中的子类、同一包中的不同类、同一包中的同一类访问
- protected修饰的类可以被不同包中的子类、同一包中的不同类、同一包中的同一类访问
- default修饰的类可以被同一包中的不同类、同一包中的同一类访问
- private修饰的类只能被同一包中的同一类访问
5、包
包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。
注意:建议显式的指定要导入的类名, 否则还是容易出现冲突的情况
自定义包:
- 在文件的最上方加上一个 package 语句指定该代码在哪个包中.
- 包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如 com.bit.demo1 ).
- 包名要和代码路径相匹配. 例如创建 com.bit.demo1 的包, 那么会存在一个对应的路径 com/bit/demo1 来存储代码.
- 如果一个类没有 package 语句, 则该类被放到一个默认包中
6、static关键字
static修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的
静态成员特性:
- 不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间中
- 既可以通过对象访问,也可以通过类名访问,但一般更推荐使用类名访问
- 类变量存储在方法区当中
- 生命周期伴随类的一生(即:随类的加载而创建,随类的卸载而销毁
static修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的。静态成员一般是通过静态方法来访问的
静态方法特性:
5. 不属于某个具体的对象,是类方法
6. 可以通过对象调用,也可以通过类名.静态方法名(…)方式调用,更推荐使用后者
7. 不能在静态方法中访问任何非静态成员变量
7、代码块
使用 {} 定义的一段代码称为代码块。根据代码块定义的位置以及关键字,又可分为以下四种:
- 普通代码块
- 构造代码块
- 静态代码块
- 同步代码块
1、普通代码块
定义在方法中的代码块,这种用法比较少见。
2、构造代码块
也称作实例代码块,一般用于初始化实例成员变量。如下图所示:
public class Student{
//实例成员变量
private String name;
private String gender;
private int age;
private double score;
public Student() {
System.out.println("I am Student init()!");
}
//实例代码块
{
this.name = "bit";
this.age = 12;
this.sex = "man";
System.out.println("I am instance init()!");
}
}
3、静态代码块
使用static定义的代码块称为静态代码块。一般用于初始化静态成员变量。
public class Student{
private String name;
private String gender;
private int age;
private double score;
private static String classRoom;
// 静态代码块
static {
classRoom = "bit306";
System.out.println("I am static init()!");
}
}
注意:
- 静态代码块不管生成多少个对象,其只会执行一次
- 静态成员变量是类的属性,因此是在JVM加载类时开辟空间并初始化的
- 如果一个类中包含多个静态代码块,在编译代码时,编译器会按照定义的先后次序依次执行(合并)
- 实例代码块只有在创建对象时才会执行
8、内部类
在 Java 中,可以将一个类定义在另一个类或者一个方法的内部,前者称为内部类,后者称为外部类。内部类也是封装的一种体现。
注意:
- 定义在class 类名{}花括号外部的,即使是在一个文件里,都不能称为内部类
- 内部类和外部类共用同一个java源文件,但是经过编译之后,内部类会形成单独的字节码文件
内部类的分类:
3. 成员内部类
- 实例内部类(未被static修饰)
- 静态内部类(被static修饰)
- 局部内部类
- 匿名内部类
对于实例内部类,注意:
- 外部类中的任何成员都可以在实例内部类方法中直接访问
- 实例内部类所处的位置与外部类成员位置相同,因此也受public、private等访问限定符的约束
- 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名称.this.同名成员 来访问
- 实例内部类对象必须在先有外部类对象前提下才能创建
- 实例内部类的非静态方法中包含了一个指向外部类对象的引用
- 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象
8、继承和多态
详细请看
继承和组合
多态
1、继承
1、super关键字
- 访问父类的成员变量
- 访问父类的成员方法
- 访问父类的构造方法
2、子类的责任
子类继承父类之后,要帮助父类成员进行初始化。子类当中显示调用父类的构造方法。
3、继承关系上的执行顺序
父类的静态,子类的静态,父类的实例,子类的实例,父类的构造,子类的构造。
4、final关键字
- 被final修饰的变量不可更改(相当于C语言中的const关键字)
- 被final修饰的类不可被继承
- 被final修饰的方法不能被重写
2、多态
多态发生条件:
- 必须在继承体系下
- 重写父类方法
- 发生动态绑定
1、向上转型
向上转型:父类引用子类的对象
优点:使代码简单灵活
缺点:不能调用到子类特有的方法
Animal animal = new Dog();//狗是动物
2、重写
重写:
- 方法名相同
- 形式参数列表相同
- 返回值也要相同
注意:子类重写父类方法的时候,子类的访问权限大于等于父类的访问权限。
3、动态绑定
动态绑定:通过父类来调用被重写的方法
4、向下转型
向下转型:子类引用父类的对象,向下转型不安全,因为不是所有的动物都是狗。
例如:
Dog dog = new Animal();
向下转型要配合instanceof关键字使用,并且需要强制类型转换
例如以下代码:
public class TestAnimal {
public static void main(String[] args) {
Cat cat = new Cat("元宝",2);
Dog dog = new Dog("小七", 1);
// 向上转型
Animal animal = cat;
animal.eat();
animal = dog;
animal.eat();
if(animal instanceof Cat){
cat = (Cat)animal;
cat.mew();
}
if(animal instanceof Dog){
dog = (Dog)animal;
dog.bark();
}
}
}
5、静态绑定
静态绑定:指的是在编译的时候进行的绑定,比如:方法的重载。
9、抽象类和接口
1、抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类;对于没有实际工作的方法, 我们可以把它设计成一个抽象方法(abstract method)。
语法如下:
// 抽象类:被abstract修饰的类
public abstract class Shape {
// 抽象方法:被abstract修饰的方法,没有方法体
abstract public void draw();
abstract void calcArea();
// 抽象类也是类,也可以增加普通方法和属性
public double getArea(){
return area;
}
protected double area; // 面积
}
注意:抽象类也是类,内部可以包含普通方法和属性,甚至构造方法
抽象类的特性:
- 不能直接实例化对象
- 不能被private修饰
- 不能被final和static修饰(抽象方法要被重写)
- 必须被继承,并且继承后子类要重写父类的抽象方法,否则子类也是抽象类,要用abstract修饰。
- 含有抽象方法的一定是抽象类
- 可以有构造方法,供子类创建对象时,初始化父类的成员变量。
2、接口
在Java中,接口可以看成是:多个类的公共规范,是一种引用数据类型。
接口的定义需要使用关键字interface,接口中的方法默认被public abstract修饰,接口中的变量默认被public static final修饰。
定义接口的语法如下:
public interface 接口名称 {
void method();
}
接口命名:一般以大写字母I开头,使用形容词性的单词。
接口使用:接口不能直接使用,必须要有一个实现类来实现这个接口,实现接口中所有的抽象方法。实现类实现接口需要使用关键字implements。
语法如下:
public class 类名称 implements 接口名称{
// code
}
接口的特性:
- 是一种引用类型,不能直接new对象
- 每一个方法都是public的抽象方法,每一个变量都被public static final修饰
- 接口中的方法只能通过实现类来实现
- 重写接口中的方法不能使用默认的访问权限
- 接口中不能有静态代码块和构造方法
多继承问题:Java的类不支持多继承,但是可以通过一个类实现多个接口来解决这个问题。接口可以使用extends关键字继承一个或多个接口,达到复用的效果。
3、抽象类和接口的区别
4、Object类及其方法重写
Object是Java默认提供的一个类。Java里面除了Object类,所有的类都是存在继承关系的,默认会继承Object父类。即所有类的对象都可以使用Object的引用进行接收
1、获取对象信息
重写toString()方法,使得调用println时按照我们想要的格式打印。
例如在学生类中重写该方法:
public String toString() {
return student.name+"获得了:"+student.score;
}
//打印出 张三获得了:59分
2、对象比较equals方法
在Java中,使用==进行比较时:
- 两侧都是基本类型变量,比较值是否相同
- 两侧都是引用类型变量,比较引用变量的地址是否相同
这告诉我们如果两个引用类型变量都引用相同的字符串,用==比较的结果也可能是false,因为值相同的字符串可能放在内存的不同地方,得到的地址不一样,这样比较存在潜在的风险。因此,我们需要调用equals方法进行重写。
如下代码(学生对象的比较)举例:
class Person{
...
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if(this == obj) {
return true;
}
// 不是Person类对象
if (!(obj instanceof Person)) {
return false;
}
Person person = (Person) obj ; // 向下转型,比较属性值
return this.name.equals(person.name) && this.age==person.age;
}
}
3、地址比较hashcode方法
刚才提到的比较引用类型的地址,实际上就是在调用hashcode方法,hashcode方法返回一个hash值标志在内存中的地址,我们直接对两个值相同的字符串调用hashcode方法得到的两个hash值可能不同。但是我们仍然可以通过重写hashcode方法来实现比较。
class Person {
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
public class TestDemo4 {
public static void main(String[] args) {
Person per1 = new Person("zhangsan", 20) ;
Person per2 = new Person("zhangsan", 20) ;
System.out.println(per1.hashCode());
System.out.println(per2.hashCode());
}
}
//执行结果
460141958
460141958
10、String
1、String类
在C语言中已经涉及到字符串了,但是在C语言中要表示字符串只能使用字符数组或者字符指针,可以使用标准库提供的字符串系列函数完成大部分操作,但是这种将数据和操作数据方法分离开的方式不符合面相对象的思想,而字符串应用又非常广泛,因此Java语言专门提供了String类
字符串构造:
public static void main(String[] args) {
// 使用常量串构造
String s1 = "hello bit";
System.out.println(s1);
// 直接newString对象
String s2 = new String("hello bit");
System.out.println(s1);
// 使用字符数组进行构造
char[] array = {'h','e','l','l','o','b','i','t'};
String s3 = new String(array);
System.out.println(s1);
}
注意:String是引用类型。内部并不存储字符串本身。
2、字符串的不可变性
所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象
【纠正】网上有些人说:字符串不可变是因为其内部保存字符的数组被final修饰了,因此不能改变。
这种说法是错误的,不是因为String类自身,或者其内部value被final修饰而不能被修改。
final修饰类表明该类不想被继承,final修饰引用类型表明该引用变量不能引用其他对象,但是其引用对象中的内容是可以修改的。
3、StringBuilder和StringBuffer
由于String的不可更改特性,为了方便字符串的修改,Java中又提供StringBuilder和StringBuffer类。这两个类大部分功能是相同的。
String和StringBuilder最大的区别在于String的内容无法修改,而StringBuilder的内容可以修改。频繁修改字符串的情况考虑使用StringBuilder
注意:String和StringBuilder类不能直接转换。如果要想互相转换,可以采用如下原则:
- String变为StringBuilder: 利用StringBuilder的构造方法或append()方法
- StringBuilder变为String: 调用toString()方
StringBuilder和StringBuffer区别:
- StringBuilder线程不安全
- StringBuffer线程安全,但是不涉及线程安全问题时使用可能会产生效率问题,频繁地加锁和释放锁也耗费系统的资源。
11、异常
在Java中,将程序执行过程中发生的不正常行为称为异常。
异常种类繁多,为了对不同异常或者错误进行很好的分类管理,Java内部维护了一个异常的体系结构:
- Throwable:是异常体系的顶层类,其派生出两个重要的子类, Error 和 Exception
- Error:指的是Java虚拟机无法解决的严重问题,比如:JVM的内部错误、资源耗尽等,典型代表:
StackOverflowError和OutOfMemoryError,一旦发生回力乏术。 - Exception:异常产生后程序员可以通过代码进行处理,使程序继续执行。比如:感冒、发烧。我们平时所说
的异常就是Exception。
1、异常的分类
异常的分类:
- 编译时异常(受查异常)
- 运行时异常(非受查异常)RunTimeException以及其子类对应的异常,都称为运行时异常。比如:NullPointerException、ArrayIndexOutOfBoundsException、ArithmeticException。
注意:
编译时出现的语法性错误,不能称之为异常。例如将 System.out.println 拼写错了, 写成了system.out.println. 此时编译过程中就会出错, 这是 “编译期” 出错。而运行时指的是程序已经编译通过得到class 文件了, 再由 JVM 执行过程中出现的错误
2、异常的处理
1、LBYL: Look Before You Leap. 在操作之前就做充分的检查. 即:事前防御型
缺陷:正常流程和错误处理流程代码混在一起, 代码整体显的比较混乱。
2、EAFP: It’s Easier to Ask Forgiveness than Permission. “事后获取原谅比事前获取许可更容易”. 也就是先操作, 遇到问题再处理. 即:事后认错型
优势:正常流程和错误流程是分离开的, 程序员更关注正常流程,代码更清晰,容易理解代码,异常处理的核心思想就是 EAFP
在Java中,异常处理主要的5个关键字:throw、try、catch、final、throws
3、异常的抛出
在Java中,可以借助throw关键字,抛出一个指定的异常对象,将错误信息告知给调用者。具体语法如下:
throw new XXXException("异常产生的原因");
注意:
- throw必须写在方法体内部
- 抛出的对象必须是Exception 或者 Exception 的子类对象
- 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理
- 如果抛出的是编译时异常,用户必须处理,否则无法通过编译
- 异常一旦抛出,其后的代码就不会执行
4、异常的捕获
异常的捕获,也就是异常的具体处理方式,主要有两种:异常声明throws 以及 try-catch捕获处理
异常声明throws:
借助throws将异常抛给方法的调用者来处理。即当前方法不处理异常,提醒方法的调用者处理异常
修饰符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2...{
}
注意:
- throws必须跟在方法的参数列表之后
- 声明的异常必须是 Exception 或者 Exception 的子类
- 方法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型具有父子关系,直接声明父类即可。
- 调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出
try-catch捕获处理:
throws对异常并没有真正处理,而是将异常报告给抛出异常方法的调用者,由调用者处理。如果真正要对异常进行处理,就需要try-catch。
语法如下:
try{
// 将可能出现异常的代码放在这里
}catch(要捕获的异常类型 e){
// 如果try中的代码抛出异常了,此处catch捕获时异常类型与try中抛出的异常类型一致时,或者是try中抛出异常的基类时,就会被捕获到
// 对异常就可以正常处理,处理完成后,跳出try-catch结构,继续执行后序代码
}[catch(异常类型 e){
// 对异常进行处理
}finally{
// 此处代码一定会被执行到
}]
// 后序代码
// 当异常被捕获到时,异常就被处理了,这里的后序代码一定会执行
// 如果捕获了,由于捕获时类型不对,那就没有捕获到,这里的代码就不会被执行
注意:
- try块内抛出异常位置之后的代码将不会被执行
- 如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到
JVM收到后中断程序----异常是按照类型来捕获的 - try中可能会抛出多个不同的异常对象,则必须用多个catch来捕获----即多种异常,多次捕获
5、finally语句
finally中的代码一定会执行的,一般finally中进行一些资源清理的扫尾工作。
finally 执行的时机是在方法返回之前(try 或者 catch 中如果有 return 会在这个 return 之前执行 finally). 但是如果finally 中也存在 return 语句, 那么就会执行 finally 中的return, 从而不会执行到 try 中原有的 return
6、自定义异常类
具体方式:
- 自定义异常类,然后继承自Exception 或者 RunTimeException
- 实现一个带有String类型参数的构造方法,参数含义:出现异常的原因
例如:
class UserNameException extends Exception {
public UserNameException(String message) {
super(message);
}
}
class PasswordException extends Exception {
public PasswordException(String message) {
super(message);
}
}
注意:
- 自定义异常通常会继承自 Exception 或者 RuntimeException
- 继承自 Exception 的异常默认是受查异常
- 继承自 RuntimeException 的异常默认是非受查异常