1 抽象类
1.1 概念
Java中可以定义被abstract关键字修饰的方法,这种方法只有声明,没有方法体,叫做抽象方法.
Java中可以定义被abstract关键字修饰的类,被abstract关键字修饰的类叫做抽象类
注意:1. 如果一个类含有抽象方法,那么它一定是抽象类
2.抽象类中的方法实现交给子类来完成
1.2 格式
权限修饰符 abstract 返回值类型 方法名 (参数列表);
1.3 特点
- abstract 可以修饰方法或者类
- 被abstarct修饰的类叫做抽象类,被abstract修饰的方法叫做抽象方法
- 抽象类中可以没有抽象方法
- 如果类中有抽象方法,那么该类必须定义为一个抽象类
- 子类继承了抽象类以后,要么还是一个抽象类,要么就把父类的所有抽象方法都重写
- 多用于多态中
- 抽象类不可以被实例化
1.4 抽象入门案例
package cn.oop2;
public class AbstractDemo {
public static void main(String[] args) {
//5.创建对象进行测试
/* 抽象类不可以被创建对象
* 可以创建多态对象进行测试*/
// Animal a = new Animal(); // 会报错
Animal a = new Pig();
a.eat(); //调用的是父类的普通方法
a.fly(); //调用的是抽象父类的声明,普通子类的具体实现
a.fly2(); //调用的是抽象父类的声明,普通子类的具体实现
}
}
//1.创建父类
/* 1.如果一个类中包含了抽象方法,那么这个类必须被声明成抽象类
* 抽象类:被abstract关键字修饰的类*/
abstract class Animal {
//2.创建抽象方法
/* 2.被abstract修饰的方法是抽象方法,并且抽象方法没有方法体*/
public abstract void fly();
public abstract void fly2();
//3.创建抽象类里的普通方法
/* 3.抽象类里可以有:全普 / 全抽 / 半普 / 半抽 ,方法不做限制*/
public void eat() {
System.out.println("吃啥都行");
}
public void play() {
System.out.println("玩啥都行");
}
}
//4.创建Animal的子类Pig
/* 4.当一个子类继承了一个抽象父类之后,有俩种解决方案:
* 方案一:变成抽象类,“躺平”,我也不实现,继续抽象
* 方案二:实现抽象父类中所有的抽象方法,“父债子偿”*/
//4.1 使用方案一
// abstract class Pig extends Animal{ //方案一
//报错:Class 'Pig' must either be declared abstract or implement abstract method 'fly()' in 'Animal'}
//4.2 使用方案二
class Pig extends Animal {
@Override
public void fly() {
System.out.println("我爸的债我终于还清了,我家的猪终于飞起来了~");
}
@Override
public void fly2() {
System.out.println("抽象父类的所有抽象方法都需要实现,子类才能是普通子类");
}
}
1.5 练习:抽象类构造函数测试
抽象类中的构造函数通常在子类对象实例化时使用
package cn.oop2;
/* 本类用于测试抽象类中构造函数的使用
* 抽象类中是否有构造方法?
* 既然抽象类不能实例化,为什么要有构造方法?
* 不是为了自己使用,是为了子类创建对象时使用super*/
public class AbstractDemo2 {
public static void main(String[] args) {
//5.测试抽象类能否实例化、
// Animal2 a = new Animal2(); //抽象类不可以被实例化
//6.创建子类对象进行测试
Pig2 p = new Pig2();
}
}
//1.创建抽象父类
abstract class Animal2{
//2.创建构造方法
public Animal2(){
System.out.println("我是Animal2抽象类的无参构造");
}
}
//3.创建子类
class Pig2 extends Animal2{
public Pig2(){
System.out.println("我是pig2的无参构造");
}
}
- 抽象类中可以有构造方法
- 其构造方法是为了给子类创建对象时使用的
1.6 练习:抽象类成员测试
package cn.oop2;
/* 本类用作抽象类中的成员测试*/
public class AbstractDemo3 {
}
// 创建抽象父类
abstract class Animal3{
/* 1.抽象类中可以定义成员变量吗? 可以*/
int sum = 100;
/* 2.抽象类中可以定义成员常量吗? 可以*/
final String name = "peiqi";
/* 3.抽象类中可以有普通方法吗? 可以*/
public void eat(){
System.out.println("干饭人");
}
/* 4.抽象类中可以都是普通方法吗? 可以*/
/* 5.抽象类中可以有抽象方法吗? 可以*/
/* 6.抽象类中可以都是抽象方法吗? 可以*/
public abstract void eat2();
public abstract void eat3();
public int get() {
return "乔治";
}
public String get2() {
return "佩奇";
}
}
//2.创建子类
//abstract class eat extends Animal3{ } //抽象子类创建方法
class eat extends Animal3{ //普通子类创建方法
@Override
public void eat2() {
System.out.println("哎呦我去");
}
@Override
public void eat3() {
System.out.println("哎呀妈呀");
}
}
abstract注意事项:
抽象方法要求子类继承后必须重写。
那么,abstract关键字不可以和哪些关键字一起使用呢?以下关键字,在抽象类中。用是可以用的,只是没有意义了。
- private:被私有化后,子类无法重写,与abstract相违背。
- static:静态优先于对象存在,存在加载顺序问题。
- final:被final修饰后,无法重写,与abstract相违背。
2 异常
2.1 概述
异常是一些用来封装错误信息的对象
它由异常的类型、提示信息、报错的行号提示三部分组成
2.2 异常的继承结构
2.3 异常的处理方式
当程序中遇到了异常,通常有两种处理方式:捕获或者向上抛出
当一个方法抛出异常,调用位置可以不做处理继续向上抛出,也可以捕获处理异常
大家可以结合生活中的例子:如果工作中遇到了问题,我们可以选择自己处理(捕获),或者交给上级处理(抛出)
捕获方式:
抛出方式:
对于不想现在处理或者处理不了的异常可以选择向上抛出
方式:在方法上设置异常的抛出管道,即:
在可能会会发生异常的方法上添加代码:
throws 异常类型
例如:void method1 throws Exception1,Exception2,Exception3{ }
TIPS:方法上有默认的异常管道:RuntimeException
2.4 练习:异常测试
package cn.tedu.oop;
import java.util.InputMismatchException;
import java.util.Scanner;
/*本类用于异常的入门案例*/
public class ExceptionDemo {
//public static void main(String[] args) throws Exception {//问题实际未处理,还报错
public static void main(String[] args) {
//method1();//调用暴露异常的方法
//method2();//调用解决异常的方法--异常解决方案1--捕获处理--自己解决
/*main()不直接调用会抛出异常的method3()
* 而是调用f(),f()解决了method3()可能会抛出的异常*/
f();
//method3();//调用解决异常的方法--异常解决方案2--向上抛出--交给调用者来解决
}
//相当于在main()调用method3()之前解决了method3()可能会抛出的异常
private static void f() {
try {
method3();
}catch (Exception e){
System.out.println("您输入的数据不对~请重新输入!");
}
}
/*如果一个方法抛出了异常,那么谁来调用这个方法,谁就需要处理这个异常
* 这里的处理也有两种方案:捕获解决 或者 继续向上抛出
* 但注意:我们一般会在main()调用之前将异常解决掉
* 而不是将问题抛给main(),因为没人解决了,该报错还报错*/
/*异常抛出的格式:在方法的小括号与大括号之间,写:throws 异常类型
* 如果有多个异常,使用逗号分隔即可*/
//0.定义一个解决异常的方法-方案2
//private static void method3() throws ArithmeticException,InputMismatchException{
private static void method3() throws Exception{
//1.复写一下刚刚的代码
System.out.println("请您输入要计算的第一个整数:");
int a = new Scanner(System.in).nextInt();
System.out.println("请您输入要计算的第二个整数:");
int b = new Scanner(System.in).nextInt();
System.out.println(a/b);
}
/*异常捕获处理的格式:
* try{
* 可能会抛出异常的代码
* }catch(异常的类型 异常的名字){
* 万一捕获到了异常,进行处理的解决方案
* }
* try-catch结构可以嵌套,如果有多种异常类型需要特殊处理的话
* */
//0.定义一个解决异常的方法-方案1
private static void method2() {
//1.按照捕获处理的格式完成结构
try{
//2.复写一下刚刚的代码
System.out.println("请您输入要计算的第一个整数:");
int a = new Scanner(System.in).nextInt();
System.out.println("请您输入要计算的第二个整数:");
int b = new Scanner(System.in).nextInt();
System.out.println(a/b);
}catch(ArithmeticException e){//异常类型 异常名
System.out.println("除数不能为0!");
}catch (InputMismatchException e){
System.out.println("请输入规定的整数类型!");
/*使用多态的思想,不论是什么子异常,统一看作父类型Exception
* 做出更加通用的解决方案,甚至可以只写这一个,上面2个不写了*/
}catch (Exception e){
System.out.println("您输入的数据不对~请重新输入!");
}
}
//0.定义一个用来暴露异常的方法
private static void method1() {
//1.提示并接收用户输入的两个整数
System.out.println("请您输入要计算的第一个整数:");
int a = new Scanner(System.in).nextInt();
System.out.println("请您输入要计算的第二个整数:");
int b = new Scanner(System.in).nextInt();
//2.输出两个数除法的结果
//输入11和0,报错:ArithmeticException--算术异常,除数不能为0,数学规定
//输入11和3.4,报错:InputMismatchException--输入不匹配异常
System.out.println(a/b);
/*1.不要害怕BUG,真正的勇士敢于直面自己写的BUG*/
/*2.学会看报错的信息提示,确定自己错误的方法*/
/*3.学会看报错的行号提示,确定自己报错的位置,哪里不对点哪里
* 注意:源码不会错,要看的是自己写的代码*/
}
}
2.5 catch 和 throws
异常处理只有两种方式: catch 和 throws,所以必须二选一
由于Java语法本身的特点,需要开发者事先考虑异常如何处理,也就是我们常说的:“未雨绸缪”
对于初级开发者来说,我们可能会捕获,但不处理异常
try {undefined
…
} catch(Exception e) {undefined
}
底层异常,应该向前抛到前面处理
经验少时,不知道该在什么位置捕获处理,应该选择 throws
但是大家需要注意,在异常抛出时,有些异常比如运行时异常,可能并不会强制要求抛出此异常,调用时也没有报错显示需要额外处理,这个时候就需要大家平时多积累,掌握良好的编码习惯了,手动添加代码进行预处理,增强程序的健壮性了。
2.6 程序错误的类型
程序错误分为三种:
编译错误(checked异常);
运行时错误(unchecked异常);
逻辑错误;
- 编译错误是因为程序没有遵循语法规则,编译程序能够自己发现并且提示我们错误的原因和位置,这个也是大家在刚接触编程语言最常遇到的问题。
- 运行时错误是因为程序在执行时,运行环境发现了不能执行的操作。
- 逻辑错误是因为程序没有按照预期的逻辑顺序执行。异常也就是指程序运行时发生错误,而异常处理就是对这些错误进行处理和控制。
其实我们还可以手动针对逻辑错误执行异常的抛出动作,大家可以理解成方法return,只不过此处我们返回的是异常,格式:
if(逻辑错误有异常){undefined
AException e = new AException(“提示消息”);
throw e;
}
package cn.tedu;
import java.util.Scanner;
public class TestThrow {
public static void main(String[] args) {
method4();
}
public static void method4(){
//1.复写刚刚可能会发生异常的代码
System.out.println("请输入您要计算的第一个数据:");
int a = new Scanner(System.in).nextInt();
System.out.println("请输入您要计算的第二个数据:");
int b = new Scanner(System.in).nextInt();
try{
double result = divide(a,b);
System.out.println(result);
//System.out.println(a/b);
}catch (ArithmeticException e){
System.out.println("不能除0是我们的错,请鞭笞我们吧!");
}
}
private static double divide(int a,int b) {
if(b == 0){
ArithmeticException e = new ArithmeticException("/ by zero");
throw e;//类似于return e;
}
return a/b;
}
}
2.7 throws 与 throw 的区别
throws:
- 用在方法声明处,其后跟着的是异常类的名字
- 表示此方法会抛出异常,需要由本方法的调用者来处理这些异常
- 但是注意:这只是一种可能性,异常不一定会发生
throw:
- 用在方法的内部,其后跟着的是异常对象的名字
- 表示此处抛出异常,由方法体内的语句处理
- 注意:执行throw一定抛出了某种异常