Java 面对对象(下)

4.1类的继承

4.1.1继承的概念

类的继承是指在一个现有类的基础上构建一个新的类,新类称为子类,现有称为父类,子类继承父类的属性和方法,使得子类对象具有父类的特征和行为。

继承需要用到extends关键字,语法格式:

class  父类{

}
class 子类  extends 父类{

}
package review;

//1.定义Animal类
class Animal{
    private String name;
    private int age;
    //get和set方法
    public String getName(){
        return name;
    }
    public int getAge(){
        return age;
    }
    public void setName(String name){
        this.name = name;
    }
    public void setAge(int age){
        this.age = age;
    }
}
//2.定义Dog类传承Animal类
class Dog extends Animal{
    private String color; //在子类中增加color属性,
    //每增加一个属性就要加get and set
    public String getColor(){ return color;}
    public void setColor(String color){this.color = color;}
}

public class r1 {
    public static void main(String[] args) {
        Dog dog = new Dog();//创建一个子类对象
        dog.setName("牧羊犬"); //子类可以访问父类的方法
        dog.setAge(1);
        dog.setColor("花色");
        System.out.println(dog.getName()+" "+dog.getAge()+" "+dog.getColor());
    }
}

需要注意的几个问题:

(1)在java中,类只支持单继承,不允许多重继承,一个类只能有一个直接父类,只能有一个儿子。

class A{}
class B{}
class C extends A , B{} //错误

(2)多个类可以继承一个父类,

class A{}
class B extends A{}
class C extends A{}

(3)可以多层继承,

class A{}
class B extends A{}
class C extends B{}

(4)子类不能直接访问父类中的私有成员,子类可以调用父类的私有方法,但是不能调用父类的私有成员。

4.1.2方法的重写

子类会自动继承父类中定义的方法,但有时在子类中需要对继承的方法进行修改,因而需要对父类的方法进行重写

在子类中重写的方法需要和父类被重写的方法具有相同的方法名、参数列表和返回值类型,且在子类重写的方法不能使用比父类更加严格的访问权限。

package review;
//1.定义Animal类
class Animal{
    //定义叫方法
    void shout(){
        System.out.println("动物叫声");
    }
}
//2.定义Dog类传承Animal类
class Dog extends Animal{
    //重写父类的shout()方法
    void shout(){
        System.out.println("汪汪汪");
    }
}

public class r1 {
    public static void main(String[] args) {
        Dog dog = new Dog();//创建一个子类对象
        dog.shout(); //dog对象调用的是子类重写的方法
    }
}

子类重写的方法不能使用比父类更加严格的访问权限

//1.定义Animal类
class Animal{
    //定义叫方法
    public void shout(){
        System.out.println("动物叫声");
    }
}
//2.定义Dog类传承Animal类
class Dog extends Animal{
    //重写父类的shout()方法
    private void shout(){               //报错,正在尝试分配更低的访问权限; 以前为public
        System.out.println("汪汪汪");
    }
}

4.1.3super关键字

当子类重谢父类的方法后,子类对象将无法访问父类被重写的方法。

super关键字可以在子类中调用父类的普通属性、方法和构造方法。

(1)使用super关键字访问父类的成员变量和成员方法

super.成员变量

super.成员方法(参数1,参数2…)

package review;
//1.定义Animal类
class Animal{
    String name = "牧羊犬";
    int age = 2;
    void shout(){
        System.out.println("动物叫声");
    }
}
//2.定义Dog类传承Animal类
class Dog extends Animal{
    String name = "小花"; //重写父类name成员变量
    //重写父类的shout()方法
    void shout(){
        super.shout(); //通过super调用父类的方法
        System.out.println("汪汪汪");
    }
    void printName(){
        System.out.println("类别:"+ super.name +"\n名字:"+ name ); //通过super调用父类的成员变量
        System.out.println("年龄:"+super.age);//调用父类没有被重写的成员
    }
}
public class r1 {
    public static void main(String[] args) {
        Dog dog = new Dog();//创建一个子类对象
        dog.shout(); //dog对象调用的是子类重写的方法
        dog.printName();
    }
}
动物叫声
汪汪汪
类别:牧羊犬
名字:小花
年龄:2

(2)使用super关键字访问父类中指定的构造方法

super (参数1,参数2 …)

package review;

//1.定义Animal类
class Animal{
    private String name;
    private int age;
    //1.构造方法
    public Animal(String name , int age){
        this.name = name;
        this.age = age;
    }
    //2.get和set方法
    public String getName(){
        return name;
    }
    public int getAge(){
        return age;
    }
    public void setName(String name){
        this.name = name;
    }
    public void setAge(int age){
        this.age = age;
    }
    //3.输出信息方法info (information)
    public String info(){
        return "名称:"+this.getName()+",年龄:"+this.getAge() ;//还可以这样返回
    }
}
//2.定义Dog类传承Animal类
class Dog extends Animal{
    private String color; //在子类中增加color属性,
    //1.如果在父类中定义了构造方法,那么在子类中必须有构造方法,不然报错
    public Dog(String name, int age , String color) {
        super(name, age); //只能在第一行,并出现一次
        this.color = color;
    }
    //2.每增加一个属性就要加get and set
    public String getColor(){ return color;}
    public void setColor(String color){this.color = color;}
    //3.重写父类的info方法
    public String info(){
        return super.info() +",颜色:"+getColor(); //调用父类方法,扩充
    }
}

public class r1 {
    public static void main(String[] args) {
        Dog dog = new Dog("牧羊犬",3,"黑色");//创建一个子类对象
        Animal animal = new Animal("小花",1); //创建一个父对象
        System.out.println(dog.info());//调用子类信息方法
        System.out.println(animal.info()); //调用父类信息方法
    }
}
名称:牧羊犬,年龄:3,颜色:黑色
名称:小花,年龄:1

通过super( )调用父类构造方法的代码必须位于子类构造方法的第一行,并且只能出现一次。

4.2final关键字

final意为最终,使用final关键字声明类、属性、方法,在声明应注意:

(1)使用final修饰的类不能有子类;

(2)使用final修饰的方法不能被重写;

(3)使用final修饰的变量(成员变量和局部变量)是常量,常量不可修改。

4.2.1final修饰类

类被final修饰后,该类将不可以被继承,即不能派生子类。

final class Animal{

}
class Dog extends Animal{ //报错

}

4.2.2final修饰方法

当一个类的方法被final关键字修饰后,这个类的子类将不能重写该方法。

class Animal{
	public final void shout(){
	
	}
}
class Dog extends Animal{
	public void shout(){ //报错
	
	}
}

4.2.3final修饰变量

被final关键字修饰的变量为常量,常量只能在声明时被赋值一次,后面其值不能被改变。(如const )

public static void main(String[] args){
	final int AGE = 10;
	AGE = 20; //报错,不能修改
}

一般为了区分,用final修饰的变量一般全部为大写字母,表示为常量。

另外,当用public static final 声明变量时,则此变量将成为全局变量。

public static final String Name = "哈士奇";

4.3抽象类和接口

4.3.1抽象类

当在父类中不能准确描述方法的实现,则不需要在父类中具体实现需要继承的方法,这时就需要将该方法定义为抽象方法,从而只需在子类中给出具体实现,在父类中不必给出具体实现,只需声明该方法为抽象方法,并且该类也必须为抽象类。

抽象方法是使用abstract关键字修饰的成员方法,抽象方法在定义时不需要实现方法体

abstract 返回类型 方法名称(参数);

当一个类包含抽象方法,则该类必须是抽象类,抽象类定义格式:

abstract class Animal{
	
	public abstract void shout(); //抽象方法
}

抽象类的定义规则:

(1)包含抽象方法的类必须是抽象类;

(2)抽象方法和抽象类都使用abstract修饰;

(3)抽象方法只声明不实现;(编译器不允许实现,如果实现会报错)

(4)如果一个类继承了抽象类,那么子类必须实现抽象类中的全部抽象方法。

package review;

//1.定义含有抽象方法的抽象Animal类
abstract class Animal{
    //不允许实现抽象方法shout()
    abstract void shout(); //不允许实现,实现会报错
}
class Dog extends Animal{
    //必须实现抽象方法shou()
    void shout(){
        System.out.println("汪汪汪");
    }
}
public class r1 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.shout();
    }
}

使用abstract修饰的抽象方法不能使用private修饰,因为抽象方法必须被子类实现。

4.2.3接口

如果一个抽象类的所有方法都是抽象的,则可以将这个类定义为接口

(1)接口中包括抽象方法外,还可以包括默认方法和静态方法(也叫类方法),默认方法用default修饰,静态方法使用static修饰,且两种方法都必须有方法体

(2)定义接口格式,用interface关键字:

1)定义:

public interface 接口名  extends 接口1,接口2...{
	public static final void Age = 2;//全局常量
	public abstract void shout(); //抽象方法
    static int getAge(){  }//静态方法必须有方法体
    default int getName(){ }//默认方法,必须有函数体
}

2)实际:

public interface 接口名  extends 接口1,接口2...{
    int Age = 2;//全局常量,在接口中不必用public static final修饰
    void display(int s);//抽象方法,在接口中不必用abstract修饰
    static int getAge(){  }//静态方法必须有方法体
    default int getName(){ }//默认方法,必须有函数体
}

(3)使用接口的目的就是克服单继承的限制,因为一个类只能有一个父类,而一个接口可以同时继承多个父接口。

(4)接口中包含三类方法:抽象方法,默认方法,静态方法;

静态方法可以通过“接口名.方法名”调用,抽象方法和默认方法通过接口实现类的对象来调用。

(5)定义接口的实现类:

一个类可以在继承另一个类的同时,也可实现(即继承思想)多个接口

public class Animal implements 接口1 , 接口2,...{

}
package review;

//1.定义接口Animal
interface Animal{
    int ID = 1; //全局常量ID,接口中定义的变量默认为全局常量
    String NAME = "牧羊犬";//全局常量一般全为大写字母
    void shout();//抽象方法,接口中定义的一般方法默认为抽象方法
    static int getID(){ //静态方法和默认方法必须要有函数体
        return Animal.ID;
    }
    public void info();//抽象方法
}
//2.定义Action接口
interface Action{
    public void eat();
}
//3.定义Dog类实现Animal接口和Action接口
class Dog implements Animal , Action{  //在接口实现类中,必须实现接口中的所有方法
    //重写Animal接口中的抽象方法
    public void shout(){
        System.out.println("汪汪汪");
    }
    public void info(){
        System.out.println("名称:"+NAME); //访问全局变量NAME
    }
    //重写Action接口中的抽象方法
    public void eat(){
        System.out.println("吃骨头");
    }
}

public class r1 {
    public static void main(String[] args) {
        System.out.println("ID:"+Animal.getID());//静态方法直接接口调用
        System.out.println("ID:"+Animal.ID +",NAME:"+Animal.NAME); //也可直接访问

        Dog dog = new Dog();
        System.out.println("ID:"+dog.ID +",NAME:"+dog.NAME); //也可通过接口类的对象访问
        dog.info();
        dog.shout();
        dog.eat();
    }
}
ID:1
ID:1,NAME:牧羊犬
ID:1,NAME:牧羊犬
名称:牧羊犬
汪汪汪
吃骨头

可看出,接口类的实例化对象可以访问接口中的常量,实现的接口方法(抽象方法)和本类内部的方法。

接口的实现类,必须实现接口中的所有方法

(6)一个类既要实现接口,也继承抽象类;

public class Dog extends Animal implements 接口1 , 接口2,...{

}
package review;

//1.定义接口Animal
interface Animal{
    String NAME = "牧羊犬";//全局常量一般全为大写字母
    void shout();//抽象方法,接口中定义的一般方法默认为抽象方法
    public void info();//抽象方法
}
//2.定义Action抽象类,内含抽象方法
abstract class Action{
    public abstract void eat();
}
//3.定义Dog类继承Action抽象类和实现Animal接口
class Dog extends Action implements Animal{  //在接口实现类中,必须实现接口中和抽象类中的所有抽象方法
    //重写Action抽象类中的抽象方法
    public void eat(){
        System.out.println("吃骨头");
    }
    //重写Animal接口中的抽象方法
    public void shout(){
        System.out.println("汪汪汪");
    }
    public void info(){
        System.out.println("名称:"+NAME); //访问全局变量NAME
    }

}

public class r1 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.info();
        dog.shout();
        dog.eat();
    }
}
名称:牧羊犬
汪汪汪
吃骨头

接口不允许继承抽象类,但允许一个接口继承多个接口

package review;

//1.定义接口Animal
interface Animal{
    String NAME = "牧羊犬";//全局常量一般全为大写字母
    public void info();//抽象方法
}
//2.定义Color接口
interface Color{
    public void black();
}
//3.定义Action接口继承上面两接口
interface Action extends Animal , Color{
    public void shout();
}
//4.定义Dog类继承Action抽象类和实现Animal接口
class Dog implements Action{  //在接口实现类中,必须实现接口中和抽象类中的所有抽象方法
    //重写Animal接口中的抽象方法
    public void info(){
        System.out.println("名称:"+NAME); //访问全局变量NAME
    }
    //重写Color接口中的抽象方法
    public void black(){
        System.out.println("黑色");
    }
    //重写Action接口中的抽象方法
    public void shout(){
        System.out.println("汪汪汪");
    }
}

public class r1 {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.info();
        dog.shout();
        dog.black();
    }
}
名称:牧羊犬
汪汪汪
黑色

4.4多态

4.4.1多态概述

多态是指不同对象在调用同一个方法时表现出的不同行为

多态主要有两种形式:方法重载和对象的多态性(方法重写)

4.4.2对象类型的转换

对象类型转换分为:

(1)向上转型:子类对象->父类对象

父类类型 父类对象 = 子类实例;

(2)向下转型:父类对象->子类对象

父类类型 父类对象 = 子类实例;
子类类型 子类对象 = (子类)父类对象

向上转型:

package review;

class Animal{
    public void shout(){
        System.out.println("喵喵喵");
    }
}
class Dog extends Animal{
    public void shout(){
        System.out.println("汪汪汪");
    }
    public void eat(){
        System.out.println("吃骨头");
    }
}
public class r1 {
    public static void main(String[] args) {
        Dog dog = new Dog(); //子类对象
        Animal an = dog; //子类->父类
        an.shout(); //调用的是子类的重写的方法
        //an.eat();不能调用,因为在父类中没有
    }
}
汪汪汪
public class r1 {
    public static void main(String[] args) {
        Animal an = new Dog(); //此时发生向上转型,子类->父类
        an.shout();
    }//也可这样写
}

如果对象发生了向上转型后,所调用的方法一定是被子类重写过的方法。

向下转型:

在进行对象向下转型前,必须发生对象向上转型;

在向下转型时,必须指明要转型的子类类型。

汪汪汪package review;

class Animal{
    public void shout(){
        System.out.println("喵喵喵");
    }
}
class Dog extends Animal{
    public void shout(){
        System.out.println("汪汪汪");
    }
    public void eat(){
        System.out.println("吃骨头");
    }
}
public class r1 {
    public static void main(String[] args) {
        Animal an = new Dog(); //此时发生向上转型,子类->父类
        Dog dog = (Dog) an; //向下转型,必须指明要转型的子类类型。
        dog.shout();
        dog.eat();
    }
}
汪汪汪
吃骨头

不能这样写:

Dog dog = (Dog)new Animal(); //报错

4.4.3instanceof关键字

使用instanceof关键字判断一个对象是否是某个类(或接口)的实例,

对象  instanceof  类名(或接口名)   //返回值为true/false
是否是Animal的实例true
是否是Dog的实例true
是否是Animal的实例true
是否是Dog的实例falsepackage review;

class Animal{
    public void shout(){
        System.out.println("喵喵喵");
    }
}
class Dog extends Animal{
    public void shout(){
        System.out.println("汪汪汪");
    }
    public void eat(){
        System.out.println("吃骨头");
    }
}
public class r1 {
    public static void main(String[] args) {
        Animal an = new Dog(); //此时发生向上转型,子类->父类
        System.out.println("是否是Animal的实例"+(an instanceof Animal));
        System.out.println("是否是Dog的实例"+(an instanceof Dog));
        Animal as = new Animal();
        System.out.println("是否是Animal的实例"+(as instanceof Animal));
        System.out.println("是否是Dog的实例"+(as instanceof Dog));
    }
}
是否是Animal的实例true
是否是Dog的实例true
是否是Animal的实例true
是否是Dog的实例false

4.5Object类

Object类,是所有类的父类,每个类都直接或间接继承Object类,称为超类

Object 类的toString( )方法 返回对象的字符串表示形式

package review;

class Animal{
    public void shout(){
        System.out.println("喵喵喵");
    }
}

public class r1 {
    public static void main(String[] args) {
        Animal an = new Animal(); //此时发生向上转型,子类->父类
        System.out.println(an.toString());
    }
}
review.Animal@1b6d3586

重写toString( )方法

package review;

class Animal{
    public void shout(){
        System.out.println("喵喵喵");
    }
    public String toString(){
        return "这是一个动物";
    }
}

public class r1 {
    public static void main(String[] args) {
        Animal an = new Animal(); //此时发生向上转型,子类->父类
        System.out.println(an.toString());
    }
}
这是一个动物

4.6内部类

允许在一个类的内部定义类,这称为内部类,外面的称外部类,

内部类可分为成员内部类、局部内部类、静态内部类、匿名内部类。

4.6.1成员内部类

在一个类中定义一个类,这个类就叫做成员内部类。

成员内部类可以访问外部类的所有成员。

package review;

class Outer{
    int m = 0;
    //1.成员方法test1()
    void test1(){
        System.out.println("外部类test1()");
    }
    //2.定义内部类Inner
    class Inner{
        int n = 1;
        void show1(){
            System.out.println("在内部类中访问外部类成员变量m = "+m);
            test1();
        }
        void show2(){
            System.out.println("内部类成员方法show2()");
        }
    }
    //3.外部类方法test2()
    void test2(){
        //在外部类中要访问内部类变量和方法,必须实例化对象
        Inner inner = new Inner();
        System.out.println("在外部类中通过对象访问内部成员变量n = "+ inner.n);
        inner.show2();
    }
}

public class r1 {
    public static void main(String[] args) {
        Outer outer = new Outer(); //实例化外部类
        Outer.Inner inner = outer.new Inner();//实例化内部类,需要new Inner();
        inner.show1();
        outer.test2();
    }
}
在内部类中访问外部类成员变量m = 0
外部类test1()
在外部类中通过对象访问内部成员变量n = 1
内部类成员方法show2()

在main中需要通过外部类创建内部类对象,来访问内部类的成员

 Outer outer = new Outer(); //实例化外部类  
 Outer.Inner inner = outer.new Inner();//实例化内部类
public class r1 {
    public static void main(String[] args) {
        Outer.Inner inner = new Outer().new Inner(); //直接定义成员内部类
        inner.show1();
    }
}

创建内部类对象的语法格式:(合在一起,先创外部再创内部)

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

4.6.2局部内部类

也称方法内部类,是指定义在某个局部范围中的类,和局部变量一样,在方法中定义

在局部内部类中,局部内部类可以访问外部类的所有成员变量和方法,而局部内部类中变量和方法只能在所属方法中访问。

4.6.3静态内部类

用static关键字修饰的成员内部类。

静态内部类只能访问外部类的静态成员,通过外部类访问静态内部类成员时,可以跳过外部类直接访问静态内部类。

package review;

class Outer{
    static int m = 0;
    static class Inner{
        int n = 1;
        void show1(){
            System.out.println("在内部类中访问外部类成员变量m = "+m);
        }
    }
}

public class r1 {
    public static void main(String[] args) {
        Outer.Inner inner = new Outer.Inner(); //创建静态内部类对象
        inner.show1();
    }
}
在内部类中访问外部类成员变量m = 0

创建态内部类对象的语法格式:(注意与成员内部类的区别)

 Outer.Inner inner = new Outer.Inner(); //创建静态内部类对象

4.6.4匿名内部类

就是没有名称的内部类

当在调用某个方法时,若该方法的参数是接口类型,除了可以传入一个接口实现类外,还可以使用实现接口的匿名内部类作为参数,在匿名内部类中直接完成方法的实现。

创建匿名内部类的基本语法:

new 父接口(){

}
package review;

//1.接口
interface Animal{
    void shout();
}
public class r1 {
    public static void main(String[] args) {
        String name = "小花";
        //2.将匿名内部类作为方法的参数,并直接实现方法,即对接口中shout()方法进行重写
        //animalShout(new Animal(){ } ); 方法头,在调用方法头时,定义Animal接口实现类,
        //在参数中直接声明接口实现类,将该类隐藏,在该类中进行对接口中抽象方法的重写。
        animalShout(new Animal(){
            public void shout(){
                System.out.println(name +"喵喵喵");
            }
        });//方法头
    }
    //3.方法
    public static void animalShout(Animal an){ //在参数表中,实例化Animal接口实现类,并且该类是匿名的内部类
        an.shout();
    }
}
小花喵喵喵

在参数中直接声明接口实现类,将该类隐藏,在该类中进行对接口中抽象方法的重写。

4.7异常

4.7.1什么是异常

例如除数不能为0,否则报错

4.7.2try…catch 和finally

为了解决异常,java提供对异常进行处理的方式,异常捕获。使用try…catch语句实现

try{
	//程序代码块
}catch (Exception e){   //Exception类或其子类
	//对ExceptionType的处理
}

说明:在try中放可能出现异常的语句,在catch中放针对异常进行处理的代码。

package review;

//1.接口
interface Animal{
    void shout();
}
public class r1 {
    public static void main(String[] args) {
        try{
            int re = divide(4,0);
            System.out.println(re);
        } catch(Exception e){ //参数是Exception类
            System.out.println("捕获的异常信息:"+e.getMessage());
        }
        System.out.println("程序继续向下执行");
    }
    public static int divide(int x , int y){
        int r = x / y;
        return r;
    }
}
捕获的异常信息:/ by zero
程序继续向下执行

发生异常语句后面的代码不会被执行。

如果希望有些语句无论程序是否发生异常都要执行,这时需要在try…catch语句后面加一个finally代码块。

package review;

//1.接口
interface Animal{
    void shout();
}
public class r1 {
    public static void main(String[] args) {
        try{
            int re = divide(4,0);
            System.out.println(re);
        } catch(Exception e){ //参数是Exception类
            System.out.println("捕获的异常信息:"+e.getMessage());
            return;//程序结束,后面除过finally代码块中的语句,其他语句都不会被执行
        } finally {
            System.out.println("finally代码块");//finally代码块无论程序提前结束都会执行
        }
        System.out.println("程序继续向下执行");//程序结束不执行
    }
    public static int divide(int x , int y){
        int r = x / y;
        return r;
    }
}
捕获的异常信息:/ by zero
finally代码块

finally代码块无论程序提前结束都会执行,不受return影响。

但受System.exit(0);因为该语句表示退出java虚拟机,任何代码都运行不了。

4.7.3throws关键字

在方法的后面使用throws关键字对外声明该方法有可能发生的异常,从而检查方法中是否存在异常。

throws关键字声明抛出异常的语法格式:

修饰符 返回类型 方法名(参数表) throws 异常类1 , 异常类2...{
	//方法体
} 
 public static void main(String[] args){
        int re = divide(4 , 2);
        System.out.println(re);
    }
	//使用throws声明抛出异常
    public static int divide(int x , int y) throws Exception {
        int r = x / y;
        return r;
    }
//未报告的异常错误java.lang.Exception; 必须对其进行捕获或声明以便抛出

在前面调用divide方法时,就出现了抛出异常,不能除0的异常,所以程序直接不能编译,报错。

说明divide 方法存在错误。

最终导致程序不能正常运行,计算4/2

若要程序还能够继续运行,方法:

(1)用try…catch处理divide方法抛出的异常

package review;

public class r1 {
    public static void main(String[] args) {
        try{
            int re = divide(4,2);
            System.out.println(re);
        } catch(Exception e){
           e.printStackTrace(); //打印捕获的异常信息
        }
    }
    public static int divide(int x , int y) throws Exception {
        int r = x / y;
        return r;
    }
}
2

(2)若不会try…catch将异常抛出,也可以使用throws关键字继续将异常抛出。

给调用方法的方法中加throws,如在main中加

package review;

public class r1 {
    public static void main(String[] args) throws Exception {
            int re = divide(4,2);
            System.out.println(re);

    }
    public static int divide(int x , int y) throws Exception {
        int r = x / y;
        return r;
    }
}
2

4.7.4编译时异常与运行时异常

在程序编译时产生的异常,必须对这些异常进行处理,这种异常称为编译时异常,也称checked异常。

在运行时产生的,不编写异常处理代码依然可以通过编译,称为运行时异常,也称unchecked异常。

(1)编译时异常

在Exception类中,除RuntimeException类及其子类外,Exception的其他子类都是编译时异常。

出现异常后必须对异常进行处理,否则不会编译。

处理方法为:try…catch对异常进行捕获处理;throws关键字声明抛出异常,调用者对异常进行处理。

(2)运行时异常

RuntimeException类及其子类都是运行时异常。

编译器不会对该类异常进行检查,换句话说,就是当程序中出现这类异常时,即使没有使用try…catch语句捕获或使用throws声明抛出异常,程序也能编译通过。

如,数组越界,就会发生运行异常,仍会编译通过。

int[] arr = new int[5];
System.out.println(arr[6]);

4.7.5自定义异常

java允许用户自定义异常,但自定义的异常类必须继承自Exception或其子类。

自定义异常:

//1.自定义异常继承Exception
class Divide extends Exception{
    public Divide(){
        super(); //调用Exception的构造方法
    }
    public Divide(String message){
        super(message);
    }
}

(1)自定义异常中使用throw关键字在方法中声明异常的实例对象:(而非throws)

throw Exception 异常对象

(2)使用throw关键字在方法中向调用者抛出自定义的Divide异常对象:

package review;

//1.自定义异常继承Exception
class Divide extends Exception{
    public Divide(){
        super(); //调用Exception的构造方法
    }
    public Divide(String message){
        super(message);
    }
}
public class r1 {
    public static void main(String[] args) throws Exception {
            int re = divide(4,-2);//不允许被除数是负数,否则出现异常
            System.out.println(re);

    }
    public static int divide(int x , int y) {
        if(y < 0){
            throw new Divide("除数是负数");//标红,报错,编译不通过
        }
        int r = x / y;
        return r;
    }
}

为什么不会编译通过?

是因为在一个方法内使用throw关键字抛出异常对象时,必须要使用try…catch语句对抛出的异常进行处理,或者在divide()方法上,使用throws关键字声明抛出异常,从而,程序正常编译。

package review;

//1.自定义异常继承Exception
class Divide extends Exception{
    public Divide(){
        super(); //调用Exception的构造方法
    }
    public Divide(String message){
        super(message);
    }
}
public class r1 {
    public static void main(String[] args) throws Exception {
        //2.定义try...catch语句用于捕获异常
        try {
            int re = divide(4, -2);//不允许被除数是负数,否则出现异常
            System.out.println(re);
        } catch (Divide e){    //利用自定义的异常,对捕获到的异常进行处理
            System.out.println(e.getMessage()); //打印捕获到的异常信息
        }
    }
    //3.使用throws关键字声明抛出自定义异常
    public static int divide(int x , int y) throws Divide{
        if(y < 0){
            throw new Divide("除数是负数");
        }
        int r = x / y;
        return r;
    }
}

在调用divide()方法时,传入的除数不能为负数,否则程序会抛出一个自定义的Divide异常,该异常最终被catch代码块捕获处理,并打印异常信息。

程序运行顺序:

调用divide(4, -2) --> throw new Divide()抛出一个异常 --> 跳转到catch处理异常 --> 打印异常信息 --> 程序正常结束。

总之:

(1) 当发生异常时,程序会立即终止,不会向下继续执行,为了就是解决异常,使程序能捕获到异常并打印异常信息,在发生异常后,然后程序不会停止,继续执行,

为了这个目标引进了异常捕获和异常处理,即try…catch语句。

(2)使用throws关键字,来抛出方法中所存在的异常,如果该方法存在异常,则在调用方法时标红报错,

要想解决这个问题,不让报错,想让程序能正常编译运行,有两个方法,一是try…catch捕获异常处理异常(方法调用放在try中),二是在方法调用所在的方法中同样用throws Exception抛出异常。

(3)自定义异常,只是继承Exception派生新的异常类,

使用throw关键字声明异常的实例对象,将该对象放在要发生异常的代码块中,用来判断是否发生异常,

其余异常处理,抛出异常类似于前面,注意在catch声明异常类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值