Java学习笔记 - 高级部分-前半

面向对象的编程-高级部分


Java笔记系列:
Java学习笔记–基础内容
Java学习笔记-类的基本概念
Java学习笔记 - 类的特征



一、类变量和类方法

1.类变量
  • 类变量:类变量也叫静态变量、静态属性,是该类所有对象共享的变量。
  • 定义语法:
    • 访问修饰符 static 数据类型 变量名;[推荐]
    • static 访问修饰符 数据类型 变量名;
  • static变量是同一个类所有对象共享
  • static类变量,在类加载时就生成了,随着类的加载而创建,没有对象实例也可以访问。
  • 访问方式: 类名.类变量名 [推荐] 或 对象名.类变量名

适用范围和细节:

  1. 当需要某个类的所有对象都共享一个变量时,可以考虑使用类变量(静态变量)
  2. 类变量由该类的所有对象共享,而实例变量是每个对象独享的
  3. 加上static称为类变量或静态变量,否则为实例变量/普通变量/非静态变量
  4. 类变量的生命周期随类的加载开始,随类的消亡销毁
2.类方法
  • 类方法也叫静态方法。
  • 定义语法:
    • 访问修饰符 static 数据返回类型 方法名(){} [推荐]
    • static 访问修饰符 数据返回类型 方法名(){}
  • 使用方式: 类方法.类方法名 或者 对象名.类方法名

适用范围和细节:

  1. 当方法中不涉及任何和对象相关的成员,希望不创造实例,也可也调用某个方法时,可以将方法设计成静态方法
  2. 类方法和普通方法都随类的加载而加载,结构信息存储在方法区,类方法中无this参数,普通方法中隐含this参数
  3. 类方法可以通过类名调用,也可以通过对象名调用
  4. 类方法中不允许使用和对象有关的关键字,比如thissuper
  5. 类方法只能访问静态变量或静态方法,但是普通成员方法既可以访问非静态成员,也可也访问静态成员

二、main()方法和代码块

1.main()方法
  • public static void main(String[] args){}
    1. main()方法由虚拟机调用
    2. Java虚拟机需要调用main(),并且执行main()方法时不必创造对象,所以要求方法是publicstatic
    3. 该方法接受String类型的数组,args[]数组中保存的是执行Java命令是传递给所运行的类的参数
    4. main()方法中,可以直接调用main方法所在类的静态方法和属性
    5. 因为main()方法是静态方法,不能直接访问main()方法所在类的非静态成员,必须创建该类的一个实例对象,然后通过对象去访问类中非静态成员

image-20231214225805362

2.代码块
  • 代码块:代码化块又称为初始化块,属于类中的成员,类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。但是只有方法体,而且不用通过对象或类显式调用,而是加载类时或者创建对象时隐式调用。

  • 语法:[修饰符] {代码};

  • 注意事项:

    • 1.修饰符可不写,如果写只能写static
    • 2.有static修饰的叫静态代码块,没有修饰的,叫普通代码块/非静态代码块
    • 3.逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
    • 4.可以写,也可省略
  • 理解:相当于另一种形式的构造器,可以做初始化操作,如果多个构造器中都有重复的语句,可以抽取到初始代码块中提高代码重用性。

  • 例:

    class A{
        {
            System.out.println("调用的构造器为....");      
        };//不管调用哪个构造器创建对象,都会先调用代码块的内容
        public A(){
            System.out.println("无参调用");
        }
        public A(String a){
            System.out.println("String 类型调用")
        }
    }
    

细节:

  1. static代码块的作用就是对类进行初始化,随着类的加载执行,只会执行一次。[类加载时]
  2. 对于普通代码块,每创建一个对象就会执行一次,但调用类的静态成员时,普通代码块不会执行。[创造对象实例时]
  3. 类的加载时机:
    1. 创造对象实例时
    2. 创造子类对象实例时,父类会被加载
    3. 使用类的静态成员时
  4. 创造对象时,在类中的调用顺序:
    1. 静态代码块+静态属性初始化,按定义顺序调用
    2. 普通代码块+普通属性初始化,按定义顺序调用
    3. 构造方法调用
  5. 创造子类对象的调用顺序:
    1. 父类的静态代码块+静态属性初始化,按定义顺序调用
    2. 子类的静态代码块+静态属性初始化,按定义顺序调用
    3. 父类的普通代码块+普通属性初始化,按定义顺序调用
    4. 父类的构造方法调用
    5. 子类的普通代码块+普通属性初始化,按定义顺序调用
    6. 子类的构造方法调用
  6. 静态代码块只能调用静态成员,普通代码块都可以

三、单例设计模式和final关键字

1.单例设计模式
  • 设计模式:设计模式是静态方法和属性是经典使用,是经过优选的代码结构、编程风格、思考问题的解决方式。
  • 单例设计模式:采取一定的方法保证在软件系统中,某个类只能存在一个对象实例,并且该类只提供一个取得对象实例的方法
  • 两种方式:
    • 1.饿汉式:在类的加载时就创建了对象实例,不存在线程安全问题,但是存在资源浪费问题(如果对象实例没有使用到
    • 2.懒汉式:使用时才创建对象实例,存在线程安全问题,但是没有资源浪费
  • 实现方式:
      1. 构造器私有化private=>防止new
      2. 类的内部创建对象
      3. 向外暴露一个静态的公共方法返回对象
//饿汉式
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关键字
  • 概念:用于修饰类、属性、方法和局部变量
  • 使用范围:
      1. 不希望类被继承时,使用final修饰
      2. 不希望父类某个方法被子类覆盖/重写时,使用final修饰
      3. 不希望类的某个属性的值被修改时,使用final修饰
      4. 不希望某个局部变量被修改时,使用final修饰

注意事项和细节:

  1. final修饰的属性叫做常量,一般用XX_XX_XX命名
  2. final修饰的属性在定义时,必须要赋初值,且之后不能再修改[在定义时、构造器中或代码块中赋初值]
  3. 如果final修饰的属性是static的,初始化位置不能是在构造器中,只能定义时和代码块中
  4. final类不能继承,但是可以实例化对象 final class AA{} AA aa = new AA;
  5. 如果类不是final类,但是有final方法,则该方法虽然不能重写,但是可以继承
  6. 对于final类,没必要修饰其中的方法为final方法
  7. final不能修饰构造器
  8. finalstatic搭配使用的效率更高,不会导致类加载

四、抽象类

1.抽象类
  • 抽象类:当父类的一些方法不能确定时,可以用abstract关键字来修饰该方法,该方法就是抽象方法,用abstract来修饰的类就是抽象类。
  • 基本介绍:
    • 1.用abstract关键字来修饰的类叫做抽象类。 访问修饰符 abstract 类名{ }
    • 2.用abstract关键字来修饰的方法叫做抽象方法。 访问修饰符 abstract 返回类型 方法名(参数列表);//没有方法体
    • 3.抽象类的价值更多的在于设计,设计好后,让子类继承并实现抽象类()
  1. 抽象类不能被实例化
  2. 抽象类不一定要包含abstract方法,抽象类可以没有abstract方法
  3. 如果类中包含了abstract方法,则要求类必须声明为abstarct
  4. abstract只能修饰类和方法,不能修饰属性和其他的
  5. 抽象类的本质还是类,抽象方法不能有主体即不能为 abstract void aa(){ };
  6. 如果一个类继承了抽象类,则要求其必须实现抽象类的所有抽象方法,除非其自己也声明为abstract
  7. 抽象方法不能用privatefinalstatic来修饰,这些关键字与重写相违背
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{}
    //接口不能继承类,但是可以继承别的接口
    
  1. 接口不能被实例化
  2. 接口的所有方法都是public方法,接口中的抽象方法可以不用abstract修饰
  3. 一个普通类实现接口必须要实现接口内的所有方法(接口内的方法都是抽象的,所以普通类要实现所有的抽象方法
  4. 抽象类实现接口,可以不用实现接口的方法(抽象类中可以存在抽象方法
  5. 接口中的属性,只能是final的,而且是public static final修饰符
  6. 接口中的属性访问形式:接口名.属性名
  7. 接口的修饰符只能是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 可以省略 理解:接口是从多个相似类中抽象出来的规范,不需要提供具体实现
  • 在 JDK8.0 时,接口中允许声明默认方法和静态方法:
    • (3)公共的默认的方法:其中 public 可以省略,建议保留,但是 default 不能省略
    • (4)公共的静态的方法:其中 public 可以省略,建议保留,但是 static 不能省略
  • 在 JDK9.0 时,接口又增加了:
    • (5)私有方法 除此之外,接口中没有构造器,没有初始化块,因为接口中没有成员变量需要动态初始化
  • 对于接口的静态方法,直接使用**“接口名.”**进行调用即可 – 也只能使用“接口名."进行调用,不能通过实现类的对象进行调用
  • 对于接口的抽象方法、默认方法,只能通过实现类对象才可以调用 – 接口不能直接创建对象,只能创建实现类的对象

六、内部类

  • 内部类分类:
    • 定义在类的局部位置:
        1. 局部内部类(有类名
        2. 匿名内部类(无类名
    • 定义在成员位置:
        1. 非静态内部类(没有static修饰
        2. 静态内部类(有static修饰
  • 基本介绍:一个类的内部又完整的嵌套了另一个类结构,被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类(outer class)。
  • 类的五大成员:属性,方法,构造器,代码块,内部类
  • 特点:内部类可以直接访问私有属性,并且可以体现类与类之间的包含关系
1.局部内部类
  • 概念:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名
  • 特点:
      1. 可以直接访问外部类的所有成员,包含私有的
      2. 不能添加访问修饰符,可以使用final修饰,属于局部变量
      3. 作用域仅在定义其的方法或代码块中
      4. 外部类访问局部类的成员要通过在局部类的作用域中创建对象,通过对象访问
      5. 外部其他类不能访问局部内部类
      6. 如果外部类和局部内部类成员重名,默认遵循就近原则,访问外部类的成员要使用外部类名.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 父类或父接口(参数列表){ 重写方法... };
  • 细节:
      1. 匿名内部类既是一个类的定义,也是一个对象
      2. 可以直接访问外部类的所有成员,包含私有的
      3. 作用域仅在定义其的方法或代码块中,不能添加修饰符,属于是局部变量
      4. 匿名类直接访问外部类成员
      5. 外部其它类不能访问匿名内部类
      6. 如果外部类和匿名内部类成员重名,默认遵循就近原则,访问外部类的成员要使用外部类名.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修饰

  • 特点:

      1. 可以直接访问外部类的所有成员,包含私有的
      2. 可以添加任意访问修饰符,属于是成员
      3. 作用域是整个类体
      4. 成员内部类可以直接访问外部类成员
      5. 外部类要通过创建对象的方式的方式访问内部类
      6. 如果外部类和非静态内部类成员重名,默认遵循就近原则,访问外部类的成员要使用外部类名.this.成员去访问
  • 静态内部类:静态内部类定义在外部类的成员位置,有static修饰

  • 特点:

      1. 可以直接访问外部类的所有静态成员,包括私有的,但不能直接访问非静态成员
      2. 可以添加任意访问修饰符,属于是类中的成员
      3. 作用域是整个类体
      4. 静态内部类可以直接访问所有的静态成员
      5. 外部类要通过创建对象的方式访问内部类
      6. 如果外部类和静态内部类成员重名,默认遵循就近原则,访问外部类的成员要使用外部类名.this.成员去访问
  • 内部类在外部类之外实例化(method为非静态方法:

    • 实例化静态内部类

      • Outer.Inner inner = new Outer.Inner();

        inner.method();

    • 实例化非静态内部类(main方法中

      • Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();
        inner.method();
class Outer{
    //外部类
    class Inner1{
        //非静态内部类
        public void method(){
        }
        
    }
    static class Inner2{
        //静态内部类
        public void method(){
        }
    }
}
class Other{
    //外部其他类
}

七、枚举和注解

1.枚举介绍
  • 理解:枚举类型本质上也是一种类,只不过是这个类的对象是有限的、固定的几个,不能让用户随意创建。
  • 实现方式:
      1. JDK5.0以前,自定义实现枚举类型
      2. JDK5.0以后,Java支持enum关键字来快速定义枚举类型
2.自定义实现枚举
    1. 不需要提供set方法,因为枚举对象值通常为只读,但可以提供get方法
    2. 枚举对象/属性使用 public static final 共同修饰,对外暴露对象
    3. 枚举对象通常用大写命名,表示常量,可以有多个属性
    4. 构造器私有化
  • 例:
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.注解
  • 注解:
      1. 注解(Annotation)也被称为元数据(Metadata),用于修饰解释包、类、放啊、属性、构造器、局部变量等数据信息。
      2. 注解不影响程序逻辑但是可以被编译运行,相当于代码中的补充信息。
      3. 某种程度上:框架 = 注解 + 反射 + 设计模式
  • 三个最基本的注解:
      1. @Override:
        • 如果写了@Override注解,编译器会去检查该方法是否重写了父类,如果没有构成重写,编译错误
        • 只能修饰方法
      1. @Deprecated:
        • 用于表示某个程序元素(类、方法)已过时
        • 可以修饰方法、类、属性、包、局部变量、参数
      1. @SuppressWarnings:
        • 抑制编译器警告
        • 可以用于修饰类、属性、方法、构造、局部变量、参数
        • @SuppressWarnings({})中可以写希望抑制的警告信息
          • – all,抑制所有警告
          • – unchecked,抑制与未检查的作业相关的警告
          • – unused,抑制与未用的程式码及停用的程式码相关的警告
          • – deprecation,抑制与淘汰的相关警告
          • – nls,抑制与非 nls 字串文字相关的警告
          • – null,抑制与空值分析相关的警告
          • – rawtypes,抑制与使用 raw 类型相关的警告
          • – static-access,抑制与静态存取不正确相关的警告
  • 元注解(用于看源码)
      1. Retention //指定注解的作用范围(SOURECE,CLASS,RUNTIME)
        1. RententionPolicy.SOURCE:编译器使用后,直接丢弃这种策略的注释
        2. RententionPolicy.CLASS:编译器把注解记录在class文件中,运行Java程序时,JVM不保留注解,这是默认值
        3. RententionPolicy.RUNTIME:编译器把注解记录在class文件中,运行Java程序时,JVM保留注解,程序可以通过反射获取该注解
      2. Target //指定注解可以使用的地方
      3. Documented //指定该注解是否在Javadoc体现
      4. Inherited //表示子类会继承父类注解
@Documented
@Retention(Retention.Policy.RUNTIME)//作用范围
@Target(value={CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE})//可以修饰的程序元素
public @interface Deprecated {
}

八、异常-Excepetion

  • 基本概念:Java语言中,程序执行中发生的不正常情况称为异常.
  • 执行过程中的异常事件分为两大类:
      1. Error(错误):Java虚拟机无法解决的严重问题,是严重错误,程序会崩溃.如JVM系统内部错误,资源耗尽等,StackOverflowError[栈溢出]
      2. Exception:其它因编程或外在因素导致的一般性错误,可以使用代码处理.如空指针异常,网络中断等
        1. 运行时异常(非受检异常):程序运行时发生的异常,一般指编译时的逻辑错误,可以不做处理
        2. 编译时异常(受检异常):编程时,编译器检查出的异常,要求编译器必须处置
1.常见异常
1.1常见运行时异常
  1. NullPointerException(空指针异常):当应用程序试图在需要对象的地方使用null时,抛出该异常
  2. ArithmeticException(数学运算异常):当出现异常的运算条件时,抛出该异常,例如整数除以零时
  3. ArrayIndexOutOfBoundsException(数组下标越界异常):用非法索引访问数组时抛出的异常,如索引为负或者大于等于数组大小,则索引为非法索引
  4. ClassCastException(类型转化异常):当试图将对象强制转化为不是实例的子类时
  5. NumberFormatException(数字格式不正确异常):当应用程序试图将字符串转换成一种数值类型,但是该字符串不能转换为适当的格式时抛出该异常
1.2常见的编译异常
  1. SQLException:操作数据库时,查询表可能发生异常
  2. IOException:操作文件时,发生的异常
  3. FileNotFoundException: 当操作一个不存在的文件时,发生异常
  4. ClassNotFoundException: 加载类而类不存在时,异常
  5. EOFException: 操作文件,到文件末尾,发生异常
  6. IllegalArguementException: 参数异常
2.异常处理
2.1 try-catch-finally
  • try-catch_finally捕获异常: 程序员在代码中捕获发生的异常,自行处理
  • 说明: Java提供trycatch块来处理异常,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块剩下的语句不再执行,直接执行catchcatch执行完后执行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…{ }
  • 注意:
      1. throws后的异常类型名可以是方法中产生的类型,也可以是它的父类。
      2. 编译异常必须在程序中使用try-catchthrows进行处理。
      3. 针对运行异常,如果程序中没有处理,默认是throws方法进行处理。
      4. 子类重写的父类方法,所抛出的异常要么与父类一致,要么为父类抛出的异常类型的子类型。
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 自定义异常
  • 作用:自定义异常是在开发者遇见核心库中未定义的异常情况下,根据自己的业务需要的异常情况来定义异常类,如年龄负数。
  • 定义步骤:
    1. 定义类:自定义异常类名继承ExceptionRuntimeException
    2. 如果继承Exception,属于编译异常
    3. 如果继承RuntimeException,属于运行异常(一般来说继承RuntimeException)
    4. 建议提供至少两个构造器,一个是无参构造,一个是(String message)构造器。
  • 细节:
    1. 自定义的异常只能通过 throw 抛出。
    2. 自定义异常最重要的是异常类的名字和message属性。当异常出现时,可以根据名字判断异常类型。比如:TeamException(“成员已满,无法添加”);、 TeamException(“该员工已是某团队成员”);
    3. 自定义异常对象只能手动抛出。抛出后由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();
    }
}
  • 25
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值