面向对象的编程-高级部分
Java笔记系列:
Java学习笔记–基础内容
Java学习笔记-类的基本概念
Java学习笔记 - 类的特征
文章目录
一、类变量和类方法
1.类变量
- 类变量:类变量也叫静态变量、静态属性,是该类所有对象共享的变量。
- 定义语法:
- 访问修饰符
static
数据类型 变量名;[推荐] static
访问修饰符 数据类型 变量名;
- 访问修饰符
static
变量是同一个类所有对象共享static
类变量,在类加载时就生成了,随着类的加载而创建,没有对象实例也可以访问。- 访问方式: 类名.类变量名 [推荐] 或 对象名.类变量名
适用范围和细节:
- 当需要某个类的所有对象都共享一个变量时,可以考虑使用类变量(静态变量)
- 类变量由该类的所有对象共享,而实例变量是每个对象独享的
- 加上
static
称为类变量或静态变量,否则为实例变量/普通变量/非静态变量- 类变量的生命周期随类的加载开始,随类的消亡销毁
2.类方法
- 类方法也叫静态方法。
- 定义语法:
- 访问修饰符
static
数据返回类型 方法名(){} [推荐] static
访问修饰符 数据返回类型 方法名(){}
- 访问修饰符
- 使用方式: 类方法.类方法名 或者 对象名.类方法名
适用范围和细节:
- 当方法中不涉及任何和对象相关的成员,希望不创造实例,也可也调用某个方法时,可以将方法设计成静态方法
- 类方法和普通方法都随类的加载而加载,结构信息存储在方法区,类方法中无
this
参数,普通方法中隐含this
参数- 类方法可以通过类名调用,也可以通过对象名调用
- 类方法中不允许使用和对象有关的关键字,比如
this
和super
- 类方法只能访问静态变量或静态方法,但是普通成员方法既可以访问非静态成员,也可也访问静态成员
二、main()方法和代码块
1.main()方法
public static void main(String[] args){}
-
- main()方法由虚拟机调用
- Java虚拟机需要调用main(),并且执行main()方法时不必创造对象,所以要求方法是
public
和static
的 - 该方法接受String类型的数组,
args[]
数组中保存的是执行Java命令是传递给所运行的类的参数 - 在
main()
方法中,可以直接调用main方法所在类的静态方法和属性 - 因为
main()
方法是静态方法,不能直接访问main()方法所在类的非静态成员,必须创建该类的一个实例对象,然后通过对象去访问类中非静态成员
2.代码块
-
代码块:代码化块又称为初始化块,属于类中的成员,类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。但是只有方法体,而且不用通过对象或类显式调用,而是加载类时或者创建对象时隐式调用。
-
语法:[修饰符] {代码};
-
注意事项:
- 1.修饰符可不写,如果写只能写
static
- 2.有
static
修饰的叫静态代码块,没有修饰的,叫普通代码块/非静态代码块 - 3.逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
- 4.
;
可以写,也可省略
- 1.修饰符可不写,如果写只能写
-
理解:相当于另一种形式的构造器,可以做初始化操作,如果多个构造器中都有重复的语句,可以抽取到初始代码块中提高代码重用性。
-
例:
class A{ { System.out.println("调用的构造器为...."); };//不管调用哪个构造器创建对象,都会先调用代码块的内容 public A(){ System.out.println("无参调用"); } public A(String a){ System.out.println("String 类型调用") } }
细节:
static
代码块的作用就是对类进行初始化,随着类的加载执行,只会执行一次。[类加载时]- 对于普通代码块,每创建一个对象就会执行一次,但调用类的静态成员时,普通代码块不会执行。[创造对象实例时]
- 类的加载时机:
- 创造对象实例时
- 创造子类对象实例时,父类会被加载
- 使用类的静态成员时
- 创造对象时,在类中的调用顺序:
- 静态代码块+静态属性初始化,按定义顺序调用
- 普通代码块+普通属性初始化,按定义顺序调用
- 构造方法调用
- 创造子类对象的调用顺序:
- 父类的静态代码块+静态属性初始化,按定义顺序调用
- 子类的静态代码块+静态属性初始化,按定义顺序调用
- 父类的普通代码块+普通属性初始化,按定义顺序调用
- 父类的构造方法调用
- 子类的普通代码块+普通属性初始化,按定义顺序调用
- 子类的构造方法调用
- 静态代码块只能调用静态成员,普通代码块都可以
三、单例设计模式和final关键字
1.单例设计模式
- 设计模式:设计模式是静态方法和属性是经典使用,是经过优选的代码结构、编程风格、思考问题的解决方式。
- 单例设计模式:采取一定的方法保证在软件系统中,某个类只能存在一个对象实例,并且该类只提供一个取得对象实例的方法
- 两种方式:
- 1.饿汉式:在类的加载时就创建了对象实例,不存在线程安全问题,但是存在资源浪费问题(如果对象实例没有使用到
- 2.懒汉式:使用时才创建对象实例,存在线程安全问题,但是没有资源浪费
- 实现方式:
-
- 构造器私有化
private
=>防止new - 类的内部创建对象
- 向外暴露一个静态的公共方法返回对象
- 构造器私有化
-
//饿汉式
class Singleton {
// 1.私有化构造器
private Singleton() {
}
// 2.内部提供一个当前类的实例
// 4.此实例也必须静态化
private static Singleton single = new Singleton();
// 3.提供公共的静态的方法,返回当前类的对象
public static Singleton getInstance() {
return single;
}
}
//懒汉式
class Singleton {
// 1.私有化构造器
private Singleton() {
}
// 2.内部提供一个当前类的实例
// 4.此实例也必须静态化
private static Singleton single;
// 3.提供公共的静态的方法,返回当前类的对象
public static Singleton getInstance() {
if(single == null) {
single = new Singleton();
}
return single;
}
}
2.final关键字
- 概念:用于修饰类、属性、方法和局部变量
- 使用范围:
-
- 不希望类被继承时,使用
final
修饰 - 不希望父类某个方法被子类覆盖/重写时,使用
final
修饰 - 不希望类的某个属性的值被修改时,使用
final
修饰 - 不希望某个局部变量被修改时,使用
final
修饰
- 不希望类被继承时,使用
-
注意事项和细节:
final
修饰的属性叫做常量,一般用XX_XX_XX命名final
修饰的属性在定义时,必须要赋初值,且之后不能再修改[在定义时、构造器中或代码块中赋初值]- 如果
final
修饰的属性是static
的,初始化位置不能是在构造器中,只能定义时和代码块中final
类不能继承,但是可以实例化对象 final class AA{} AA aa = new AA;- 如果类不是
final
类,但是有final
方法,则该方法虽然不能重写,但是可以继承- 对于
final
类,没必要修饰其中的方法为final
方法final
不能修饰构造器final
和static
搭配使用的效率更高,不会导致类加载
四、抽象类
1.抽象类
- 抽象类:当父类的一些方法不能确定时,可以用
abstract
关键字来修饰该方法,该方法就是抽象方法,用abstract
来修饰的类就是抽象类。 - 基本介绍:
- 1.用
abstract
关键字来修饰的类叫做抽象类。 访问修饰符 abstract 类名{ } - 2.用
abstract
关键字来修饰的方法叫做抽象方法。 访问修饰符 abstract 返回类型 方法名(参数列表);//没有方法体 - 3.抽象类的价值更多的在于设计,设计好后,让子类继承并实现抽象类()
- 1.用
- 抽象类不能被实例化
- 抽象类不一定要包含
abstract
方法,抽象类可以没有abstract
方法- 如果类中包含了
abstract
方法,则要求类必须声明为abstarct
abstract
只能修饰类和方法,不能修饰属性和其他的- 抽象类的本质还是类,抽象方法不能有主体即不能为
abstract void aa(){ };
- 如果一个类继承了抽象类,则要求其必须实现抽象类的所有抽象方法,除非其自己也声明为
abstract
类- 抽象方法不能用
private
、final
和static
来修饰,这些关键字与重写相违背
2.模板设计模式
- 概念:抽象类体现的是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但是子类总体上会保留抽象类的行为方式。
- 作用:
- 当功能内部的一部分实现时确定的,另一部分的实现是不确定的,这是可以把不确定的部分暴露出去,让子类去实现。
- 编写一个抽象父类,父类提供了多个子类的通用方法,并把一个或多个方法留给其子类实现,就是一种模板模式。
例:
//抽象类的应用:模板方法的设计模式
public class TemplateMethodTest {
public static void main(String[] args) {
BankTemplateMethod btm = new DrawMoney();
btm.process();//取款
BankTemplateMethod btm2 = new ManageMoney();
btm2.process();//理财
}
}
abstract class BankTemplateMethod {
// 具体方法
public void takeNumber() {
System.out.println("取号排队");
}
public abstract void transact(); // 办理具体的业务 //钩子方法
public void evaluate() {
System.out.println("反馈评分");
}
// 模板方法,把基本操作组合到一起,子类一般不能重写
public final void process() {
this.takeNumber();
this.transact();// 像个钩子,具体执行时,挂哪个子类,就执行哪个子类的实现代码
this.evaluate();
}
}
class DrawMoney extends BankTemplateMethod {
public void transact() {
System.out.println("我要取款!!!");
}
}
class ManageMoney extends BankTemplateMethod {
public void transact() {
System.out.println("我要理财!我这里有 2000 万美元!!");
}
}
五、接口
1.接口
-
接口(interface):接口就是给出一些没有实现的方法,封装到一起,当某个类要使用的时候再根据具体情况把这些方法写出来。
-
语法:
-
interface 接口名{ //属性 //抽象方法 } class 类名 implements 接口{ 自己属性; 自己方法; 必须实现的接口的抽象方法; } //jdk7.0中接口是更加抽象的抽象的类,抽象类里的方法可以有方法体(抽象内的非抽象方法),接口里的所有方法都没有方法体。 //接口体现了程序设计的多态和高内聚低耦合设计 //jdk8.0后接口类中可以有静态方法,默认方法,可以有方法的具体实现 interface A{} interface B{} class C implements A,B{} //一个类可以实现多个接口 interface D extends A,B{} //接口不能继承类,但是可以继承别的接口
- 接口不能被实例化
- 接口的所有方法都是
public
方法,接口中的抽象方法可以不用abstract
修饰- 一个普通类实现接口必须要实现接口内的所有方法(接口内的方法都是抽象的,所以普通类要实现所有的抽象方法
- 抽象类实现接口,可以不用实现接口的方法(抽象类中可以存在抽象方法
- 接口中的属性,只能是
final
的,而且是public static final
修饰符- 接口中的属性访问形式:接口名.属性名
- 接口的修饰符只能是
public
和默认,类也是
2.接口的多态特性
-
多态参数:接口引用可以指向实现了接口类的对象
-
多态数组:可以实现接口型的数组,通过接口型的数组指向实现了接口的对象
-
多态传递:
interface A{} interface E extends A{} class B implements A{ } class C implements A{ } class D implements E{ //实现了E接口 } //接口A类型的变量a可以指向实现了A接口的类的对象实例 A a = new B(); a = new C(); //多态数组 A[] a = new A[2]; a[0] = new B(); a[1] = new C(); //多态传递,E继承了A接口,D实现了E接口,相当于D也实现了A接口 E e = new D(); A a = new D();
3.接口的静态方法和非静态方法实现
- 在 JDK8.0 之前,接口中只允许出现:
- (1)公共的静态的常量:其中
public static final
可以省略 - (2)公共的抽象的方法:其中
public abstract
可以省略 理解:接口是从多个相似类中抽象出来的规范,不需要提供具体实现
- (1)公共的静态的常量:其中
- 在 JDK8.0 时,接口中允许声明默认方法和静态方法:
- (3)公共的默认的方法:其中
public
可以省略,建议保留,但是default
不能省略 - (4)公共的静态的方法:其中
public
可以省略,建议保留,但是static
不能省略
- (3)公共的默认的方法:其中
- 在 JDK9.0 时,接口又增加了:
- (5)私有方法 除此之外,接口中没有构造器,没有初始化块,因为接口中没有成员变量需要动态初始化
- 对于接口的静态方法,直接使用**“接口名.”**进行调用即可 – 也只能使用“接口名."进行调用,不能通过实现类的对象进行调用
- 对于接口的抽象方法、默认方法,只能通过实现类对象才可以调用 – 接口不能直接创建对象,只能创建实现类的对象
六、内部类
- 内部类分类:
- 定义在类的局部位置:
-
- 局部内部类(有类名
- 匿名内部类(无类名
-
- 定义在成员位置:
-
- 非静态内部类(没有
static
修饰 - 静态内部类(有
static
修饰
- 非静态内部类(没有
-
- 定义在类的局部位置:
- 基本介绍:一个类的内部又完整的嵌套了另一个类结构,被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。
- 类的五大成员:属性,方法,构造器,代码块,内部类
- 特点:内部类可以直接访问私有属性,并且可以体现类与类之间的包含关系
1.局部内部类
- 概念:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名
- 特点:
-
- 可以直接访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,可以使用
final
修饰,属于局部变量 - 作用域仅在定义其的方法或代码块中
- 外部类访问局部类的成员要通过在局部类的作用域中创建对象,通过对象访问
- 外部其他类不能访问局部内部类
- 如果外部类和局部内部类成员重名,默认遵循就近原则,访问外部类的成员要使用
外部类名.this.成员
去访问
-
class Outer{
private int n = 100;
public void m(){
final class inner{
perivate int n = 200;
public void f(){
System.out.println("inner n = "+n); //就近调用n=200
System.out.println("outer n = "+Outer.this.n); //指定Outer的n
}
}
inner in = new inner();//外部类要在内部类的作用域内创建对象调用
inner.f();
}
}
main()中
Outer out = new Outer();
out.m();
//inner n = 200 outer n = 100
2.匿名内部类
- 概念:匿名内部类定义在外部类的局部位置,没有类名。用于一次性使用的类,避免命名的繁琐
- 特点:本质是类,是内部类,没有类名,同时还是一个对象
- 基本语法:
new 父类或父接口(参数列表){ 重写方法... };
- 细节:
-
- 匿名内部类既是一个类的定义,也是一个对象
- 可以直接访问外部类的所有成员,包含私有的
- 作用域仅在定义其的方法或代码块中,不能添加修饰符,属于是局部变量
- 匿名类直接访问外部类成员
- 外部其它类不能访问匿名内部类
- 如果外部类和匿名内部类成员重名,默认遵循就近原则,访问外部类的成员要使用
外部类名.this.成员
去访问
-
1.匿名内部类直接调用
interface A{
void a();
}
public class Test{
public static void main(String[] args){
new A(){
@Override
public void a() {
System.out.println("aaaa");
}
}.a(); //创建实现了A接口的匿名内部类对象,直接调用a()方法
}
}
2.通过父类或父接口的变量多态引用匿名内部类的对象
interface A{
void a();
}
public class Test{
public static void main(String[] args){
A obj = new A(){
@Override
public void a() {
System.out.println("aaaa");
}
};//创建一个实现了A接口的匿名内部类对象,对象名为obj,通过对象名调用a()方法
obj.a();
}
}
3.匿名内部类对象作为实参调用
interface A{
void method();
}
public class Test{
public static void test(A a){
a.method();
}
public static void main(String[] args){
test(new A(){
@Override
public void method() {
System.out.println("aaaa");
}//实现了A接口,作为实参,传入Test()方法
});
}
}
3.成员内部类
-
非静态内部类:成员内部类是定义在外部类的成员位置,没有
static
修饰 -
特点:
-
- 可以直接访问外部类的所有成员,包含私有的
- 可以添加任意访问修饰符,属于是成员
- 作用域是整个类体
- 成员内部类可以直接访问外部类成员
- 外部类要通过创建对象的方式的方式访问内部类
- 如果外部类和非静态内部类成员重名,默认遵循就近原则,访问外部类的成员要使用
外部类名.this.成员
去访问
-
-
静态内部类:静态内部类定义在外部类的成员位置,有
static
修饰 -
特点:
-
- 可以直接访问外部类的所有静态成员,包括私有的,但不能直接访问非静态成员
- 可以添加任意访问修饰符,属于是类中的成员
- 作用域是整个类体
- 静态内部类可以直接访问所有的静态成员
- 外部类要通过创建对象的方式访问内部类
- 如果外部类和静态内部类成员重名,默认遵循就近原则,访问外部类的成员要使用
外部类名.this.成员
去访问
-
-
内部类在外部类之外实例化(method为非静态方法:
-
实例化静态内部类
-
Outer.Inner inner = new Outer.Inner();
inner.method();
-
-
实例化非静态内部类(main方法中
- Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.method();
- Outer outer = new Outer();
-
class Outer{
//外部类
class Inner1{
//非静态内部类
public void method(){
}
}
static class Inner2{
//静态内部类
public void method(){
}
}
}
class Other{
//外部其他类
}
七、枚举和注解
1.枚举介绍
- 理解:枚举类型本质上也是一种类,只不过是这个类的对象是有限的、固定的几个,不能让用户随意创建。
- 实现方式:
-
- JDK5.0以前,自定义实现枚举类型
- JDK5.0以后,Java支持
enum
关键字来快速定义枚举类型
-
2.自定义实现枚举
-
- 不需要提供set方法,因为枚举对象值通常为只读,但可以提供get方法
- 枚举对象/属性使用
public static final
共同修饰,对外暴露对象 - 枚举对象通常用大写命名,表示常量,可以有多个属性
- 构造器私有化
- 例:
class Season{
private final String SEASONNAME;//季节的名称
private final String SEASONDESC;//季节的描述
private Season(String seasonName,String seasonDesc){
this.SEASONNAME = seasonName;
this.SEASONDESC = seasonDesc;
}
public static final Season SPRING = new Season("春天", "春暖花开");
public static final Season SUMMER = new Season("夏天", "夏日炎炎");
public static final Season AUTUMN = new Season("秋天", "秋高气爽");
public static final Season WINTER = new Season("冬天", "白雪皑皑");
@Override
public String toString() {
return "Season{" +
"SEASONNAME='" + SEASONNAME + '\'' +
", SEASONDESC='" + SEASONDESC + '\'' +
'}';
}
}
class SeasonTest{
public static void main(String[] args) {
System.out.println(Season.AUTUMN);
}
}
3.emum关键字快速定义
-
语法:【修饰符】 enum 枚举类名{ 常量对象列表; 对象的实例变量列表; }
-
枚举方法定义要求:
- 枚举类的常量对象列表必须在首行,大写
- 列出的实例系统会自动添加
public static final
修饰 - 枚举默认继承Enum类,不能再继承其他对象
- 如果使用无参构造器创建枚举对象,实参列表和小括号可以忽略
- 有多个枚举对象时,使用==,间隔,最后用;==结尾
- JDK5.0后
switch
,提供支持枚举类型,case
后面可以写枚举常量名,无需添加枚举类作为限定。
-
例:
public enum Week {
MONDAY("星期一"),
TUESDAY("星期二"),
WEDNESDAY("星期三"),
THURSDAY("星期四"),
FRIDAY("星期五"),
SATURDAY("星期六"),
SUNDAY("星期日");
private final String description;
private Week(String description){
this.description = description;
}
@Override
public String toString() {
return super.toString() +":"+ description;
}
}
public class TestWeek {
public static void main(String[] args) {
Week week = Week.MONDAY;
System.out.println(week);
switch (week){
case MONDAY:
System.out.println("怀念周末,困意很浓");break;
case TUESDAY:
System.out.println("进入学习状态");break;
case WEDNESDAY:
System.out.println("死撑");break;
case THURSDAY:
System.out.println("小放松");break;
case FRIDAY:
System.out.println("又信心满满");break;
case SATURDAY:
System.out.println("开始盼周末,无心学习");break;
case SUNDAY:
System.out.println("一觉到下午");break;
}
}
}
4.enmu中常用方法和接口实现
1.枚举类中的常用方法
方法名 | 详细 |
---|---|
String toString() | 默认返回的是常量名(对象名),可以继续手动重写该方法! |
static 枚举类型[] values() | 返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值,是一个静态方法 |
static 枚举类型 valueOf(String name) | 可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArg umentException。 |
String name() | 得到当前枚举常量的名称。建议优先使用 toString()。 |
int ordinal() | 返回当前枚举常量的次序号,默认从 0 开始 |
compareTo() | 比较两个枚举常量,比较的是编号。 |
2.枚举类接口实现
- 使用enum关键字后,不能继承别的类但是可以实现接口
- 形式:
enum 类名 implements 接口1,接口2{ }
5.注解
- 注解:
-
- 注解(Annotation)也被称为元数据(Metadata),用于修饰解释包、类、放啊、属性、构造器、局部变量等数据信息。
- 注解不影响程序逻辑但是可以被编译运行,相当于代码中的补充信息。
- 某种程度上:框架 = 注解 + 反射 + 设计模式
-
- 三个最基本的注解:
-
- @Override:
- 如果写了@Override注解,编译器会去检查该方法是否重写了父类,如果没有构成重写,编译错误
- 只能修饰方法
- @Override:
-
- @Deprecated:
- 用于表示某个程序元素(类、方法)已过时
- 可以修饰方法、类、属性、包、局部变量、参数
- @Deprecated:
-
- @SuppressWarnings:
- 抑制编译器警告
- 可以用于修饰类、属性、方法、构造、局部变量、参数
- @SuppressWarnings({})中可以写希望抑制的警告信息
- – all,抑制所有警告
- – unchecked,抑制与未检查的作业相关的警告
- – unused,抑制与未用的程式码及停用的程式码相关的警告
- – deprecation,抑制与淘汰的相关警告
- – nls,抑制与非 nls 字串文字相关的警告
- – null,抑制与空值分析相关的警告
- – rawtypes,抑制与使用 raw 类型相关的警告
- – static-access,抑制与静态存取不正确相关的警告
- @SuppressWarnings:
-
- 元注解(用于看源码)
-
- Retention //指定注解的作用范围(SOURECE,CLASS,RUNTIME)
- RententionPolicy.SOURCE:编译器使用后,直接丢弃这种策略的注释
- RententionPolicy.CLASS:编译器把注解记录在
class
文件中,运行Java程序时,JVM不保留注解,这是默认值 - RententionPolicy.RUNTIME:编译器把注解记录在
class
文件中,运行Java程序时,JVM保留注解,程序可以通过反射获取该注解
- Target //指定注解可以使用的地方
- Documented //指定该注解是否在Javadoc体现
- Inherited //表示子类会继承父类注解
- Retention //指定注解的作用范围(SOURECE,CLASS,RUNTIME)
-
@Documented
@Retention(Retention.Policy.RUNTIME)//作用范围
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})//可以修饰的程序元素
public @interface Deprecated {
}
八、异常-Excepetion
- 基本概念:Java语言中,程序执行中发生的不正常情况称为异常.
- 执行过程中的异常事件分为两大类:
-
- Error(错误):Java虚拟机无法解决的严重问题,是严重错误,程序会崩溃.如JVM系统内部错误,资源耗尽等,StackOverflowError[栈溢出]
- Exception:其它因编程或外在因素导致的一般性错误,可以使用代码处理.如空指针异常,网络中断等
- 运行时异常(非受检异常):程序运行时发生的异常,一般指编译时的逻辑错误,可以不做处理
- 编译时异常(受检异常):编程时,编译器检查出的异常,要求编译器必须处置
-
1.常见异常
1.1常见运行时异常
- NullPointerException(空指针异常):当应用程序试图在需要对象的地方使用null时,抛出该异常
- ArithmeticException(数学运算异常):当出现异常的运算条件时,抛出该异常,例如整数除以零时
- ArrayIndexOutOfBoundsException(数组下标越界异常):用非法索引访问数组时抛出的异常,如索引为负或者大于等于数组大小,则索引为非法索引
- ClassCastException(类型转化异常):当试图将对象强制转化为不是实例的子类时
- NumberFormatException(数字格式不正确异常):当应用程序试图将字符串转换成一种数值类型,但是该字符串不能转换为适当的格式时抛出该异常
1.2常见的编译异常
- SQLException:操作数据库时,查询表可能发生异常
- IOException:操作文件时,发生的异常
- FileNotFoundException: 当操作一个不存在的文件时,发生异常
- ClassNotFoundException: 加载类而类不存在时,异常
- EOFException: 操作文件,到文件末尾,发生异常
- IllegalArguementException: 参数异常
2.异常处理
2.1 try-catch-finally
- try-catch_finally捕获异常: 程序员在代码中捕获发生的异常,自行处理
- 说明: Java提供
try
和catch
块来处理异常,try
块用于包含可能出错的代码,catch
块用于处理try
块中发生的异常.可以根据需要有多个try...catch
块。 - 基本语法:
try{
可能有异常的代码
}catch(NullPointerException e){
catch按顺序执行,子类在前,父类在后
}catch(Exception e){
捕获到异常
1.异常发生
2.系统将异常封装成Exception对象e,传递给catch
3.得到异常,程序员自行处理
4.如果没有异常,catch代码块不执行
5.可以有多个catch语句,捕获不同异常进行处理,要求父类异常在后,子类在前
6.try中发生异常后,try块剩下的语句不再执行,直接执行catch,catch执行完后执行finally
}finally{
1.不管try代码块是否发生异常,都要执行finally
2.通常将释放资源的代码放在finally块中
可以使用try-finally(没有catch块)配合,相当于未捕获异常,适用于不管是都发生异常都必须执行某个业务逻辑
}
----------------------------------------
try {
Person person = new Person();
//person = null;
System.out.println(person.getName());//NullPointerException
int n1 = 10;
int n2 = 0;
int res = n1 / n2;//ArithmeticException
} catch (NullPointerException e) {
System.out.println("空指针异常=" + e.getMessage());
} catch (ArithmeticException e) {
System.out.println("算术异常=" + e.getMessage());
} catch (Exception e) {
System.out.println(e.getMessage());
} finally {
}
2.2 throws
- throws声明抛出:如果一个方法中可能生成异常,但是并不能确定处理异常的方法,则此方法显示地声明抛出异常,表明该方法将不对这些异常处理,而由调用者负责处理。
- 声明格式:
修饰符 返回值类型 方法名(参数) throws 异常类名 1,异常类名 2…{ }
- 注意:
-
throws
后的异常类型名可以是方法中产生的类型,也可以是它的父类。- 编译异常必须在程序中使用
try-catch
或throws
进行处理。 - 针对运行异常,如果程序中没有处理,默认是
throws
方法进行处理。 - 子类重写的父类方法,所抛出的异常要么与父类一致,要么为父类抛出的异常类型的子类型。
-
public class TestThrowsCheckedException {
public static void main(String[] args) {
System.out.println("上课.....");
try {
afterClass();//换到这里处理异常
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("准备提前上课");
}
System.out.println("上课.....");
}
public static void afterClass() throws InterruptedException {
for(int i=10; i>=1; i--){
Thread.sleep(1000);//本来应该在这里处理异常
System.out.println("距离上课还有:" + i + "分钟");
}
}
}
2.3 throw
- 异常对象的生成方式:
- 虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,那么针对当前代码,就会在后台自动创建一个对应异常类的实例对象并抛出。
- 由开发人员手动创建:
new 异常类型([实参列表]);
创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样,但是一旦throw
抛出,就会对程序运行产生影响了。
- 格式:
throw new 异常类名(参数);
- 说明:
throw
是手动生成异常对象的关键字,后面跟着异常对象,位于方法体中。throw
语句会导致程序执行流程被改变,throw
语句是明确抛出一个异常对象,因此它下面的代码将不会执行。如果当前方法没有try...catch
处理这个异常对象,throw
语句就会代替return
语句提前终止当前方法的执行,并返回一个异常对象给调用者。
public class TestThrow {
public static void main(String[] args) {
try {
System.out.println(max(4,2,31,1));
} catch (Exception e) {
e.printStackTrace();
}
try {
System.out.println(max(4));
} catch (Exception e) {
e.printStackTrace();
}
try {
System.out.println(max());
} catch (Exception e) {
e.printStackTrace();
}
}
public static int max(int... nums){
if(nums == null || nums.length==0){
throw new IllegalArgumentException("没有传入任何整数,无法获取最大值");//制造一个异常好被catch捕捉
}
int max = nums[0];
for (int i = 1; i < nums.length; i++) {
if(nums[i] > max){
max = nums[i];
}
}
return max;
}
}
2.4 自定义异常
- 作用:自定义异常是在开发者遇见核心库中未定义的异常情况下,根据自己的业务需要的异常情况来定义异常类,如年龄负数。
- 定义步骤:
- 定义类:自定义异常类名继承
Exception
或RuntimeException
- 如果继承
Exception
,属于编译异常 - 如果继承
RuntimeException
,属于运行异常(一般来说继承RuntimeException
) - 建议提供至少两个构造器,一个是无参构造,一个是(String message)构造器。
- 定义类:自定义异常类名继承
- 细节:
- 自定义的异常只能通过
throw
抛出。 - 自定义异常最重要的是异常类的名字和
message
属性。当异常出现时,可以根据名字判断异常类型。比如:TeamException(“成员已满,无法添加”);、 TeamException(“该员工已是某团队成员”); - 自定义异常对象只能手动抛出。抛出后由
try..catch
处理,也可以甩锅throws
给调用 者处理。
- 自定义的异常只能通过
class MyException extends Exception {//自定义异常
private int idnumber;
public MyException(String message, int id) {
super(message);
this.idnumber = id;
}
public int getId() {
return idnumber;
}
}
public class MyExpTest {
public void regist(int num) throws MyException {
if (num < 0)
throw new MyException("人数为负值,不合理", 3);
else
System.out.println("登记人数" + num);
}
public void manager() {
try {
regist(100);
} catch (MyException e) {
System.out.print("登记失败,出错" );
}
System.out.print("本次登记操作结束");
}
public static void main(String args[]) {
MyExpTest t = new MyExpTest();
t.manager();
}
}