第4单元 面向对象(下)
4.1 类的继承
在程序中,继承描述的是事物之间的所属关系,通过继承可以使多种事物之间形成一种关系体系。
4.1.1 继承的概念
定义:在现有类(父类、基类)的基础上去构建一个新的类,构建出来的新类(子类,派生类)
作用:(1)自动继承父类(所有)成员变量和成员方法
(2)扩展(定义)子类本身的成员变量和成员方法
类的继承语法:extends关键字
class 父类{
.......
}
class 子类 extends 父类{
......
}
例子4-1: 子类自动继承父类成员变量和成员方法
class Animal { // 定义Animal类
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class Dog extends Animal { // 定义Dog类继承Animal类
//此处不写任何代码
}
public class Example01 { // 定义测试类
public static void main(String[] args) {
Dog dog = new Dog(); // 创建一个Dog类的实例对象
dog.setName("牧羊犬"); // 此时访问的方法是父类中的,子类中并没有定义
dog.setAge(3); // 此时访问的方法是父类中的,子类中并没有定义
System.out.println("名称:"+dog.getName()+",年龄:"+dog.getAge());
}
}
名称:牧羊犬,年龄:3
分析:父类:2个属性,4个方法
子类: 2个属性,4个方法
-----------------------------------------------------
例子4-2: 子类扩展(定义)本身的成员变量和成员方法
class Animal { // 定义Animal类
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
class Dog extends Animal { // 定义Dog类继承Animal类
private String color; //定义本身的color属性
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
} //此处不写任何代码
}
public class Example01 { // 定义测试类
public static void main(String[] args) {
Dog dog = new Dog(); // 创建一个Dog类的实例对象
dog.setName("牧羊犬"); // 此时访问的方法是父类中的,子类中并没有定义
dog.setAge(3); // 此时访问的方法是父类中的,子类中并没有定义
dog.setColor("黑色"); //此时访问的方法是子类本身中的
System.out.println("名称:"+dog.getName()+",年龄:"+dog.getAge()+"颜色:"+dog.getColor());
}
}
名称:牧羊犬,年龄:3,颜色:黑色
分析:父类:2个属性,4个方法
子类: 3个属性,6个方法
继承的注意:
(1)Java只支持单继承,不允许多继承,即一个类只有一个直接父类
class A{}
class B{}
class C extends A,B{} // C类不可以同时继承A类和B类
(2)多了个类可以继承自同一个父类
class A{}
class B extends A{}
class C extends A{} // 类B和类C都可以继承类A
(3)在Java中,多继承通过传递继承,即一个类的父类可以再继承另外的父类
class A{}
class B extends A{} // 类B继承类A,类B是类A的子类
class C extends B{} // 类C继承类B,类C是类B的子类,同时也是类A的子类
(4)子类继承父类所有成员变量和方法;
子类可以直接访问和调用父类的非私有成员(成员变量和成员方法);
子类不能直接访问父类的私有成员变量,只能通过公有的成员方法间接访问。
4.1.2 方法的重写
定义:子类对继承自父类的方法进行修改
方法重写要求:(1)相同的返回值、方法名和参数列表
(2)子类重写方法访问权限,不能比父类方法严格(访问权限扩大:子类>=父类),
否则编译器报错
结果:当子类重写父类方法后,子类对象调用本身重写的方法,而不能调用父类同名方法
例子4-3: 子类对父类方法重写
class Animal { // 定义Animal类
void shout() { //定义动物叫的方法: default
System.out.println("动物发出叫声");
}
}
class Dog extends Animal { // 定义Dog类继承动物类
void shout() { //重写父类Animal中的shout()方法: 相同的返回类型,方法名和参数列表
System.out.println("汪汪汪……"); 访问权限:子类=父类
}
}
public class Example03 { // 定义测试类
public static void main(String[] args) {
Dog dog = new Dog(); // 创建Dog类的实例对象
dog.shout(); // 调用dog重写的shout()方法
}
}
汪汪汪.........
4.1.3 super关键字
问题:当子类重写父类方法后,子类对象无法访问父类被重写的方法
解决方法:子类中,使用super关键字调用父类的普通(非私有)属性、(非私有)方法和构造方法
作用:(1)子类使用super关键词,访问父类(非私有)成员变量与成员方法
(2)子类使用super关键字,访问父类指定的构造方法(public)
(super()调用父类构造方法必须位于子类构造法方法第一行,且只能出现一次)
例子4-5:子类使用super关键词,访问父类(非私有)成员变量与成员方法
class Animal { // 定义Animal类
String name = "牧羊犬";
void shout() { //定义动物叫的方法
System.out.println("动物发出叫声");
}
}
class Dog extends Animal { // 定义Dog类继承动物类
public void shout() { //重写父类Animal中的shout()方法,扩大了访问权限
super.shout(); //子类使用super关键字,调用父类中的shout()方法
System.out.println("汪汪汪……");
}
public void printName(){
System.out.println("名字:"+super.name); //子类使用super关键字,调用父类中的name属性
}
}
public class Example05 { // 定义测试类
public static void main(String[] args) {
Dog dog = new Dog(); // 创建Dog类的实例对象
dog.shout(); // 调用dog重写的shout()方法
dog.printName(); // 调用Dog类中的printName()方法
}
}
动物发出叫声
汪汪汪……
名字:牧羊犬
例子4-6:子类使用super关键字,访问父类指定的构造方法
class Animal { // 定义Animal类
private String name;
private int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String info() {
return "名称:"+this.getName()+",年龄:"+this.getAge(); //此处this可以缺省
}
}
class Dog extends Animal { // 定义Dog类继承Animal类
private String color;
public Dog(String name, int age, String color) {
super(name, age); //子类使用super()调用父类构造方法,且必须放在子类构造方法第一行
this.setColor(color);
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String info() { //重写父类的info()方法
return super.info()+",颜色:"+this.getColor(); //使用super关键字调用父类的方法
}
}
// 定义测试类
public class Example06 {
public static void main(String[] args) {
Dog dog = new Dog("牧羊犬",3,"黑色"); // 创建Dog类的实例对象
System.out.println(dog.info());
}
}
名称:牧羊犬,年龄:3,颜色:黑色
注意:
(1)this、super关键字,都可以调用构造方法、普通方法和属性
(2)this,super调用构造方法是,不可能同时出现(调用构造方法是,都必须放在第一行。)
4.2 final关键字
final:“最终”。
在Java中,可以使用final关键字声明类、方法和变量(成员变量或局部变量),在声明时需要注意以下几点:
(1)使用final修饰的类(最终类)不能有子类。
(2)使用final修饰的方法(最终方法)不能被子类重写。
(3)使用final修饰的变量(成员变量或局部变量)(最终变量)是常量,不可修改。
4.2.1 final关键字修饰类:final类不能被继承
例子4-7: final关键字修饰类
final class Animal { // 使用final关键字修饰Animal类
// 方法体为空
}
class Dog extends Animal { // Dog类继承Animal类
// 方法体为空
}
// 定义测试类
public class Example07 {
public static void main(String[] args) {
Dog dog = new Dog(); // 创建Dog类的实例对象
}
}
编译器报错
4.2.2 final关键字修方法:子类不能重写父类final方法
例子4-8: final关键字修饰方法
class Animal { // 定义Animal类
public final void shout() { // 使用final关键字修饰shout()方法
}
}
class Dog extends Animal { // 定义Dog类继承Animal类
public void shout() { // 重写Animal类的shout()方法
}
}
public class Example08 { // 定义测试类
public static void main(String[] args) {
Dog dog=new Dog(); // 创建Dog类的实例对象
}
}
编译器报错
4.2.3 final关键字修变量:常量
在Java中,常量是指被final修饰的变量(字母全部大写),常量只能在声明时赋值一次。
例子4-9 final关键字修变量:final修饰的变量(字母全部大写), 即常量
public class Example09 {
public static void main(String[] args) {
final int AGE = 18; // 第一次可以赋值
AGE = 20; // 再次赋值会报错
}
}
注意:
全局常量:public static final 变量类型 变量名;
public static final String NAME="哈士奇";
4.3 抽象类和接口
方法定义
[访问控制符] 返回值类型 方法名(参数列表){
方法体; //方法实现的具体内容
}
问题:类中成员方法,具有方法体。但有时候方法体不确定,比如
在Animal类中,定义shout()方法描述动物叫声,但不同动物叫声不同,
因此,shout()方法体无法确定。
解决:抽象方法解决方法体无法确定的方法。
4.3.1 抽象类
1、抽象方法:
定义:使用abstract关键字修饰的成员方法,且定义时不需要实现方法体。
语法格式:
[访问控制符] abstract 返回值类型 方法名(参数列表); //抽象方法声明,方法体无实现
2、抽象类
定义:包含至少一个抽象方法的类,且该类用abstract关键字修饰
语法格式:
abstract class 抽象类名{
成员变量;
[访问控制符] 返回值类型 方法名(参数列表){ //成员方法;
方法体;
}
[访问控制符] abstract 返回值类型 方法名(参数列表); //抽象方法
}
定义规则:
(1)包含一个及以上抽象方法的类是抽象类
(2)抽象类和抽象方法,都必须使用abstract关键字修饰
(3)抽象方法只需声明, 而不需要实现
(4)一个非抽象类继承抽象类,子类必须实现抽象类的全部抽象方法
(即子类重写父类抽象方法,使其具有方法体)
(5)abstract修饰的方法(抽象方法)不能同时使用private或final修饰
例子4-10 抽象类使用
abstract class Animal { // 定义抽象类Animal
abstract void shout(); // 定义抽象方法shout()
}
class Dog extends Animal { // 定义Dog类继承抽象类Animal
void shout() {
System.out.println("汪汪..."); // 子类实现父类抽象方法shout()
}
}
public class Example10 { // 定义测试类
public static void main(String[] args) {
Dog dog = new Dog(); // 创建Dog类的实例对象
dog.shout(); // 调用dog对象的shout()方法
}
}
汪汪.......
4.3.2 接口
1、接口:interface接口关键词
基本定义:所有方法都是抽象方法,成员变量都是全局常量,该抽抽象类是接口
基本语法格式:(推荐)
public interface 接口名 extends 接口1,接口2,.....接口n{
public static final 数据类型 变量名=常量值; //成员变量为全局常量
public abstract 返回值类型 方法名(参数列表); //所有方法为抽象方法
}
JDK8定义:抽象类中不仅有抽象方法、还有静态方法和默认方法,成员变量都是全局常量,该抽抽象类是接口
public interface 接口名 extends 接口1,接口2,.....接口n{
public static final 数据类型 变量名=常量值; //成员变量为全局常量
public static 返回值类型 方法名(参数列表){ //静态方法
方法体;
}
default 返回值类型 方法名(参数列表){ //默认方法
方法体;
}
public abstract 返回值类型 方法名(参数列表); //所有方法为抽象方法
}
接口说明:
(1)接口使用interface关键字
(2)extends关键字表示接口继承,接口允许多继承,不同接口键使用逗号分隔
(3)接口中的抽象方法修饰(即使无修饰符或缺省,也默认):public abstract;
成员变量是全局常量(即使无修饰符或缺省,也默认),public static final
(4)接口定义时,常常缺省public。不管是否写访问权限: 接口方法,public修饰;接口变量:public static final修饰
2、接口实现类
定义:类使用implements关键字实现接口,并实现接口中所有的抽象方法。
语法格式:
[修饰符] class 类名 implements 接口1,接口2,.....接口n{
}
说明:类可以继承一个类的同时实现多个接口
例子4-11 接口使用
interface Animal { // 定义接口Animal(默认:public修饰)
int ID = 1; // 定义全局常量(默认:public static final修饰)
String NAME = "牧羊犬";
void shout(); // 定义抽象方法shout()
static int getID(){ //定义静态方法
return Animal.ID; //静态成员变量,接口名(类名)直接访问
}
public void info(); // 定义抽象方法info()
}
interface Action { //定义接口Action(默认:public修饰)
public void eat(); // 定义抽象方法eat()
}
class Dog implements Animal,Action{ // 定义Dog类实现Animal接口和Action接口
public void eat() { // 重写Action接口中的抽象方法eat()
System.out.println("喜欢吃骨头");
}
public void shout() { // 重写Animal接口中的抽象方法shout()
System.out.println("汪汪...");
}
public void info() { // 重写Animal接口中的抽象方法info()
System.out.println("名称:"+NAME);
}
}
class Example11 { // 定义测试类
public static void main(String[] args) {
System.out.println("编号"+Animal.getID());
Dog dog = new Dog(); // 创建Dog类的实例对象
dog.info();
dog.shout(); // 调用Dog类中重写的shout()方法
dog.eat(); // 调用Dog类中重写的eat()方法
}
}
编号1
名称:牧羊犬
汪汪...
喜欢吃骨头
说明:
(1)类必须实现接口中,所有抽象方法
(2)静态成员,可以通过接口名直接访问,也可以通过创建的对象访问
如果某个类既要继承抽象类又要实现接口
语法形式:
[修饰符] class 类名 extends 抽象类 implements 接口名1,接口名2,....{
........
}
例子4-12 某类继承抽象类又要实现接口
interface Action { // 定义接口Animal(默认:public修饰)
public String NAME = "牧羊犬"; // 定义全局常量(默认:public static final修饰)
public [abstract] void shout(); // 定义抽象方法shout()
public [abstract] void info(); // 定义抽象方法info()
}
abstract class Animal{ //定义抽象类Action(默认:public修饰)
public abstract void eat(); // 定义抽象方法eat()
}
class Dog extends Animal implements Action{ // 定义Dog类继承Animal类,并实现Action接口
public void eat() { // 重写Action接口中的抽象方法eat()
System.out.println("喜欢吃骨头");
}
public void shout() { // 重写Animal接口中的抽象方法shout()
System.out.println("汪汪...");
}
public void info() { // 重写Animal接口中的抽象方法info()
System.out.println("名称:"+NAME);
}
}
class Example12{ // 定义测试类
public static void main(String[] args) {
Dog dog = new Dog(); // 创建Dog类的实例对象
dog.info();
dog.shout(); // 调用Dog类中重写的shout()方法
dog.eat(); // 调用Dog类中重写的eat()方法
}
}
接口不允许继承抽象类,但允许一个接口继承多个接口
例子4-13 接口继承多个接口
interface Animal { // 定义接口Animal(默认:public修饰)
public String NAME = "牧羊犬"; // 定义全局常量(默认:public static final修饰)
public [abstract] void info(); // 定义抽象方法info()
}
interface Color{ //定义抽象类Action(默认:public修饰)
public [abstract] void black(); // 定义抽象方法black()
}
interface Action extends Animal,Color{ //接口多继承
public [abstract] void shout(); // 定义抽象方法shout()
}
class Dog implements Action{ // 定义Dog类实现Action接口
public void info() { // 重写Animal接口中的抽象方法info()
System.out.println("名称:"+NAME);
}
public void black() { // 重写Action接口中的抽象方法black()
System.out.println("黑色");
}
public void shout() { // 重写Animal接口中的抽象方法shout()
System.out.println("汪汪...");
}
}
class Example13{ // 定义测试类
public static void main(String[] args) {
Dog dog = new Dog(); // 创建Dog类的实例对象
dog.info();
dog.shout(); // 调用Dog类中重写的shout()方法
dog.black(); // 调用Dog类中重写的eat()方法
}
}
名称:牧羊犬
汪汪...
黑色
4.4 多态
4.4.1 多态概述
多态:不同对象调用同一方法,表现不同行为。
实现方式:2种
(1)方法重载:同一个类中,参数不同(类型或个数)
(2)方法重写:子类重写父类方法
例子4-14 方法重写
abstract class Animal { // 定义抽象类Anmal
abstract void shout(); // 定义抽象shout()方法
}
class Cat extends Animal { // 定义Cat类继承Animal抽象类
public void shout() { // 实现shout()方法
System.out.println("喵喵……");
}
}
class Dog extends Animal { // 定义Dog类继承Animal抽象类
public void shout() { // 实现shout()方法
System.out.println("汪汪……");
}
}
public class Example14 { // 定义测试类
public static void main(String[] args) {
Cat an1 = new Cat(); // 创建Cat对象,使用Animal类型的变量an1引用
Dog an2 = new Dog(); // 创建Dog对象,使用Animal类型的变量an2引用
an1.shout();
an2.shout();
}
}
4.4.2 对象类型转换
定义:父类与子类对象的转换
类型:2种:
1、向上转型:子类对象----->父类对象
语法:
父类类名 父类对象名=子类实例;
说明:
(1)向上转型,系统自动完成
(2)向上转型,父类对象调用重写方法时,调用子类重写方法。
父类对象无法调用子类特有成员方法。
2、向下转型:父类对象----->子类对象
父类类名 父类对象名=子类实例;
子类类名 子类对象名=(子类类名)父类对象
说明:
(1)向下转型,必须先向上转型,再向下转型(直接向下转型,会报错)
例子4-15 向上转型
class Animal { // 定义类Anmal
public void shout(){
System.out.println("喵喵……");
}
}
class Dog extends Animal { // Dog类
public void shout() { // 重写shout()方法
System.out.println("汪汪……");
}
public void eat() {
System.out.println("吃骨头……");
}
}
public class Example15 { // 定义测试类
public static void main(String[] args) {
Dog dog = new Dog(); // 创建Dog对象
Animal an = dog; //子类实例向上转型
an.shout();
}
}
汪汪……
例子4-16 向下转型
class Animal { // 定义类Anmal
public void shout(){
System.out.println("喵喵……");
}
}
class Dog extends Animal { // Dog类
public void shout() { // 重写shout()方法
System.out.println("汪汪……");
}
public void eat() {
System.out.println("吃骨头……");
}
}
public class Example16 { // 定义测试类
public static void main(String[] args) {
Animal an = new Dog(); // 先向上转型,子类→父类
Dog dog = (Dog)an; // 再了向下转型
dog.shout();
dog.eat();
}
}
汪汪……
吃骨头
4.4.3 instanceof关键字
作用:判定对象是否是某个类(或接口)的实例
语法:
对象 instanceof 类(接口);
结果:是返回true,否返回false
例子4-17 instanceof关键字使用
class Animal { // 定义类Anmal
public void shout(){
System.out.println("动物叫……");
}
}
class Dog extends Animal { // Dog类
public void shout() { // 重写shout()方法
System.out.println("汪汪……");
}
public void eat() {
System.out.println("吃骨头……");
}
}
public class Example17 { // 定义测试类
public static void main(String[] args) {
Animal a1 = new Dog(); // 通过向上转型实例化Animal对象
System.out.println("Animal a1 = new Dog():"+(a1 instanceof Animal));
System.out.println("Animal a1 = new Dog():"+(a1 instanceof Dog));
Animal a2 = new Animal(); // 实例化Animal对象
System.out.println("Animal a2 = new Animal():"+(a2 instanceof Animal));
System.out.println("Animal a2 = new Animal():"+(a2 instanceof Dog));
}
}
true
true (无法理解)
true
false
4.5 Object类
定义:所有类的父类(又称超类),每个类都直接或间接继承Object类。
当一个类没有显示指定父类,该类默认继承Object类
常用方法:
boolean equals(): 判定两个对象是否相等
int hashCode(): 返回对象的散列码值
String toString(): 返回对象的字符串表示形式
例子4-18 Object类使用
class Animal { // 定义Animal类
void shout() { // 定义动物叫的方法
System.out.println("动物叫!");
}
}
public class Example18 {
public static void main(String[] args) {
Animal animal = new Animal(); // 创建Animal类对象
System.out.println(animal.toString()); // 继承自Object类的toString()方法并打印
}
}
例子4-19 子类重写Object类方法
class Animal { // 定义Animal类
public String toString(){ //重写Object类的toString()方法
return "这是一个动物。";
}
}
public class Example19 {
public static void main(String[] args) {
Animal animal = new Animal(); // 创建Animal类对象
System.out.println(animal.toString()); // 调用toString()方法并打印
}
}
这是一个动物。
4.6 内部类
定义:在一个类(外部类)的内部定义的类。
种类:根据内部的定义、位置和修饰符,内部类可以分为4种:
(1)成员内部类
(2)局部内部类
(3)静态内部类
(4)匿名内部类
4.6.1 成员内部类
定义:在外部类中定义的成员(内部)类,与外部类中的成员变量与成员方法并列。
功能:成员内部类可以访问外部类的所有成员(成员变量、成员方法)。
创建成员内部类对象的语法:
外部类名.内部类名 变量名=new 外部类名(). new 内部类名();
例子4-20: 成员内部类
class Outer {
int m = 0; // 定义类的成员变量
void test1() { // 下面的代码定义了一个成员方法,方法中访问内部类
System.out.println("外部类成员方法");
}
// 下面的代码定义了一个成员内部类
class Inner {
int n = 1;
void show1() {
System.out.println("外部成员变量m = " + m); // 在成员内部类的方法中访问外部类的成员变量
}
void show2() {
System.out.println("内部成员方法");
}
}
void test2() {
Inner inner = new Inner();
System.out.println("内部成员变量n = " + inner.n);
inner.show2();
}
}
public class Example20 {
public static void main(String[] args) {
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(); //创建内部类对象
inner.show1();
outer.test2();
}
}
外部成员变量m =0
内部成员变量n =1
内部成员方法
4.6.2 局部内部类(方法内部类)
定义:在方法中定义的类,仅在方法内部有效。
功能:局部内部类可以访问外部类的所有成员(成员变量、成员方法)
局部内部类中的变量和方法,只能在所属方法中访问
例子4-21: 局部内部类
class Outer {
int m = 0; // 定义类的成员变量
// 下面的代码定义了一个成员方法,方法中访问内部类
void test1() {
System.out.println("外部类成员方法");
}
void test2() {
// 下面的代码定义了一个成员内部类
class Inner {
int n = 1;
void show() {
// 在成员内部类的方法中访问外部类的成员变量
System.out.println("外部成员变量m = " + m);
test1();
}
}
Inner inner = new Inner();
System.out.println("局部内部类变量n = " + inner.n);
inner.show();
}
}
public class Example21 {
public static void main(String[] args) {
Outer outer = new Outer();
outer.test2();
}
}
局部内部类变量n =1
外部成员变量m =0
外部类成员方法
4.6.3 静态内部类
定义:使用static关键词修饰的成员内部。
功能:静态内部类仅可以访问外部类的静态成员(成员变量、成员方法)。
创建静态内部类对象的语法:
外部类名.静态内部类名 变量名=new 外部类名().静态内部类名();
例子4-22: 静态内部类
class Outer {
static int m = 0; // 定义类的成员变量
// 下面的代码定义了一个静态内部类
static class Inner {
int n = 1;
void show() {
// 在静态内部类的方法中访问外部类的成员变量
System.out.println("外部静态变量m = " + m);
}
}
}
public class Example22 {
public static void main(String[] args) {
Outer.Inner inner = new Outer.Inner();
inner.show();
}
}
外部静态变量m =0
4.6.4 匿名内部类
问题:在Java中,调用某个方法,该方法的参数是接口类型。
解决方法:(1)传入接口实现类
(2)实现接口匿名内部类,在匿名内部类中,实现接口抽象方法的重写
定义:没有名称的内部类
创建匿名内部类对象(子类对象)的基本语法:
new 父接口(){
父接口抽象方法重写;
}
说明:(1)匿名:隐藏的是接口实现类的名字
(2)new 父接口():创建的是实现类的对象,非父接口的对象。 (可以看作:new 父接口[实现类名]())
(3){}:抽象方法的具体实现
例子4-23: 匿名内部类
interface Animal{
void shout();
}
public class Example23{
public static void main(String[] args){
String name = "小花";
animalShout(new Animal(){ //匿名内部类
public void shout() { //接口方法重写
System.out.println(name+"喵喵..."); //在匿名内部类中,访问局部变量
}
});
}
public static void animalShout(Animal an){
an.shout();
}
}
说明:
可以在局部内部类和匿名内部类中,访问非final修饰的局部变量
4.7 异常
4.7.1 什么是异常
1、定义:在程序运行过程中,出现的各种非正常情况。 比如:硬盘空间不足,网络中断,类不存在,数组越界等
2、产生异常后果:异常方生后,若不对其进行处理,程序会立即结束,无法继续向下执行。
3、解决异常:以异常类的形式对非正常情况(异常)进行封装,
使用异常处理机制,处理程序运行的各种情况
例子4-24 异常的实例
public class Example24 {
public static void main(String[] args) {
int result = divide(4, 0); // 调用divide()方法
System.out.println(result);
}
//下面的方法实现了两个整数相除
public static int divide(int x, int y) {
int result = x / y; // 定义一个变量result记录两个数相除的结果
return result; // 将结果返回
}
}
报错:
Exception in thread "main" java.lang
.ArithmeticException: /by zero
at cn.itcast.Example.divide(Example08.java:9)
at cn.itcast.Example.divide(Example08.java:4)
算术异常(ArithmeticException),参数传入时,分母为0了
异常方生后,若不对其进行处理,程序会立即结束,无法继续向下执行。
3、Java异常类
异常类:Java提供了大量的异常类,所有异常类都继承自Throwable类。(在java.lang包: lang包是常用包,自动导入,不需要人为显示导入)
异常类继承体系:
Throwable
Error Exception
IOError 其他子类(编译时异常) RuntimeException(运行时异常)
AWTError ArithmeticException
其他子类 ClassCaseException
其他子类
说明:
(1)Throwable类有两个直接子类Error和Exception
(2)Error类(错误类):程序本身无法处理的错误,程序运行时产生的系统内部错误或资源耗尽错误。
(3)Exception类(异常类):程序本身可以处理的错误。在Java中处理的异常类,都是针对Exception类及其子类
(4)Exception类:有两个直接子类。 其他子类(编译时异常) , RuntimeException(运行时异常)
Throwable类中,获取异常信息的通用方法。(Throwable子类会自动继承)
方法声明 功能描述
String getMessage() 返回异常的消息字符串
String toString() 返回异常的简单信息描述
void printStackTrace() 获取异常类名和异常信息,以及异常出现在程序中的位置
4、Java异常处理的两种方式
(1)使用try...catch....[finally.....]语句,处理异常
(2)方法声明处,使用throws关键字声明抛出异常;方法调用处,对异常进行处理
4.7.2 try......catch和finally
1、目标:为使异常发生后,能解决异常,并能使程序正常执行下去,需解决异常
2、方法:Java提供了异常处理机制:异常捕获
3、异常捕获语法:try.....catch语句
try{
可能会发生异常的语句块;
}catch(ExceptionType(Exception类及其子类) e){
异常处理的语句块;
}
4、说明:
(1)try{}中,编写可能会发生异常的Java语句块
(2)catch(){}:() 参数:指明异常类类型(必须是Exception类或其子类)
{} 编写针对异常的处理语句块
(3)实现过程:try代码块中的程序发生了异常,
系统会将异常的信息封装成一个异常对象,
并将这个对象传递给catch代码块进行处理
例子4-25 try…catch语句对异常进行捕获
public class Example25 {
public static void main(String[] args) { //下面的代码定义了一个try…catch语句用于捕获异常
try {
int result = divide(4, 0); //调用divide()方法
System.out.println(result);
} catch (Exception e) { //对异常进行处理
System.out.println("捕获的异常信息为:" + e.getMessage());
}
System.out.println("程序继续向下执行...");
}
public static int divide(int x, int y) { //下面的方法实现了两个整数相除
int result = x / y; //定义一个变量result记录两个数相除的结果
return result; //将结果返回
}
}
捕获的异常信息为:/by zero
程序继续向下执行
注意:
(1)try{}代码块中,发生异常语句,该语句后面的语句不会被执行。
(2)catch{}代码块对异常进行处理,处理完成后,程序继续向下执行,而不会终止程序。
5、无论异常是否发生,有些语句始终要执行。
关键词:try.....catch.....finally....
语法格式:
try{
可能会发生异常的语句块;
}catch(ExceptionType(Exception类及其子类) e){
异常处理的语句块;
}finally{
无论异常是否发生,必须要执行的语句块;
}
例子4-26 try…catch....fianlly语句对异常进行捕获
public class Example26 {
public static void main(String[] args) { //下面的代码定义了一个try…catch…finally语句用于捕获异常
try {
int result = divide(4, 0); //调用divide()方法
System.out.println(result);
} catch (Exception e) { //对捕获到的异常进行处理
System.out.println("捕获的异常信息为:" + e.getMessage());
return; //用于结束当前main()方法
} finally {
System.out.println("进入finally代码块");
}
System.out.println("程序继续向下执行…");
}
public static int divide(int x, int y) { //下面的方法实现了两个整数相除
int result = x / y; //定义一个变量result记录两个数相除的结果
return result; //将结果返回
}
}
捕获的异常信息为:/by zero
进入finally代码块
程序说明:
(1)return语句,用于结束当前方法(main()方法),使得return之后代码块不会执行。
(2)finally语句却执行了,不受return语句影响。
(换句话,不论程序是发生异常还是使用return语句结束,finally中的语句都会执行)
(3)finally{}作用:处理必须完成的事情。比如释放系统资源
(4)System.exit(0):退出当前Java虚拟机,任何代码都不执行
4.7.3 throws关键字:
1、问题:编写程序时,大部分是调用别人编写的程序,无法确定该方法是否发生异常
2、处理机制:在方法的声明(定义)的后面,使用throws关键字,声明该方法可能会发生异常(方法声明处,抛出异常)
在程序中,必须对该方法声明的异常进行处理,否则编译无法通过。(方法调用处,处理抛出异常)
3、throws关键字声明抛出异常语法:
[修饰符] 返回值类型 方法名(参数1,参数2,...参数n) throws 异常类1,异常2,....{
方法体;
}
说明:
(1)throws关键词,写在方法声明的后面(方法声明处,throw声明抛出异常)
(2) 在方法调用处,处理抛出异常
例子4-27 方法声明处,throws关键字声明抛出异常
public class Example27 {
public static void main(String[] args) {
int result = divide(4, 2); //调用divide()方法
System.out.println(result);
}
public static int divide(int x, int y) throws Exception { //下面的方法实现了两个整数相除,并使用throws关键字声明抛出异常
int result = x / y; //定义一个变量result记录两个数相除的结果
return result; //将结果返回
}
}
编译器报错.....
(说明:虽然divide()方法调用时,不会发生除0异常,
在divide()方法在声明时抛出异常,调用该方法时,
必须对抛出的异常进行处理,否则编译报错)
例子4-28 方法声明处,throws抛出异常;方法调用处,try...catch语句处理抛出的异常
public class Example28 {
public static void main(String[] args) {
try { //下面的代码定义了一个try…catch语句用于捕获异常
int result = divide(4, 2); //调用divide()方法
System.out.println(result);
} catch (Exception e) { //对捕获到的异常进行处理
e.printStackTrace(); //打印捕获的异常信息
}
}
public static int divide(int x, int y) throws Exception { //下面的方法实现了两个整数相除,并使用throws关键字声明抛出异常
int result = x / y; //定义一个变量result记录两个数相除的结果
return result; //将结果返回
}
}
2
4、当不知道如何处理抛出的异常,可以继续使用throws关键字继续抛出。
(编译能通过,但程序一旦发生异常并且异常没有被处理,程序会非正常终止)
例子4-29 throws关键字继续抛出,且不做异常处理
public class Example29 {
public static void main(String[] args) throws Exception{
int result = divide(4, 0); //调用divide()方法
System.out.println(result);
}
public static int divide(int x, int y) throws Exception { //下面的方法实现了两个整数相除,并使用throws关键字声明抛出异常
int result = x / y; //定义一个变量result记录两个数相除的结果
return result; //将结果返回
}
}
报错
(说明:在方式声明时,使用throws抛出异常,再main()方法中继续使用throws抛出,且未对异常处理
编译能通过,但一旦发生异常,程序会终止运行。)
4.7.4 编译时异常与运行时异常
(Exception)异常类有两个直接子类:编译异常和运行时异常
1、编译时异常(checked异常)
(1)定义:
Java编译器会对该异常进行检查,必须对该异常进行处理,否则编译无法通过
(2)编译异常处理方法:2种
1)使用try...catch....[finally.....]语句,处理异常
2)方法声明处,使用throws关键字声明抛出异常;方法调用处,对异常进行处理
2、运行时异常(unchecked异常,RuntimeException异常)
(1)定义:
Java编译器不会对该异常进行检查。
(2)错误原因:程序逻辑错误
例子: 运行时异常
int[] arr=new int[5];
System.out.println(arr[6]);
4.7.5 自定义异常
1、定义:允许用户自定义异常,处理程序中特有的异常情况
2、要求:
(1)自定义异常类,必须继承自Exception类或其子类
(2)在自定义异常类构造方法中,使用super()语句调用Exception类的构造方法
例子:自定义异常类声明
public class DivideByMinusException extends Exception{
public DivideByMinusException(){
super();
}
public DivideByMinusException(String message){
super(message);
}
}
3、自定义异常类对象实例化:
(1)位置:在方法声明中,声明自定义异常类
(2)语法格式:
throws new 自定义异常类名(参数列表);
例子4-30 改进例4-29的divide()方法,在divide()方法声明中判定除数是否为负数
如果为负数,在方法声明中使用throws关键字,向方法的调用者抛出自定义类的
DivideBy MinusException异常对象
public class Example30 {
public static void main(String[] args) {
int result = divide(4, -2);
System.out.println(result);
}
//下面的方法实现了两个整数相除
public static int divide(int x, int y) {
if(y<0){
throw new DivideByMinusException("除数是负数"); //在方法声明处,向方法调用者抛出自定义类实例对象
}
int result = x / y; // 定义一个变量result记录两个数相除的结果
return result; // 将结果返回
}
}
程序报错。。。。。
(原因:在方法声明内,使用throws关键字抛出自定义异常类对象,必须使用对异常进行处理
异常处理方法:(1)try....catch语句捕获并处理;
(2)在方法声明处,使用throws关键字声明抛出,由调用者负责处理)
例子4-31 完整的自定义异常类处理
public class DivideByMinusException extends Exception{
public DivideByMinusException(){
super();
}
public DivideByMinusException(String message){
super(message);
}
}
public class Example31 {
public static void main(String[] args) {
try { // 下面的代码定义了一个try…catch语句用于捕获异常
int result = divide(4, -2);
System.out.println(result);
} catch (DivideByMinusException e) { // 对捕获到的异常进行处理
System.out.println(e.getMessage()); // 打印捕获的异常信息
}
}
public static int divide(int x, int y) throws DivideByMinusException { // 下面的方法实现了两个整数相除,并使用throws关键字声明抛出自定义异常
if (y < 0) {
throw new DivideByMinusException("除数是负数");
}
int result = x / y; // 定义一个变量result记录两个数相除的结果
return result; // 将结果返回
}
}
DivideByMinusException e= new DivideByMinusException("除数是负数")
菜单
本页目录