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声明异常类。