面向对象核心技术
一、抽象类与接口
1.抽象类
假设我们并不希望新建的类的对象会被创建出来,那么我们就把该新建的类定义为抽象的。我们用abstract关键字来定义抽象类。抽象类的作用仅仅是表达接口,而不是具体的实现细节。抽象类中可以有抽象方法且该方法也由关键字abstract关键字进行修饰。抽象的方法是不完全的,它只是一个方法签名而完全没有方法体。
注意:
- 如果一个类有了一个抽象的方法,这个类就必须声明为抽象类。
- 如果父类是抽象类,那么子类必须覆盖所有在父类中的抽象方法,否则子类也成为一个抽象类。
- 一个抽象类可以没有任何一个抽象方法,所有的方法都有方法体,但是整个类是抽象的。设计这样的抽象类主要是为了防止制造它的对象出来。
2.接口
接口其实就是完全的抽象类,因此接口和类的地位是一样的。接口所有的方法都是没有方法体的且都是public abstract,即使不声明。而接口中的所有成员变量都是public static final的变量,并且必须有定义初始化,因为所有变量都必须在编译的时候有确定的值。Java用一个动词:实现(implements)关键字进行修饰接口。实现接口的类必须覆盖接口的所有方法,否则这个类就会成为一个抽象类。Java=不允许多继承,但允许一个类实现多个接口得到继承,但是不允许接口从类继承。
3.抽象类与接口的对比
参数 | 抽象类 | 接口 |
---|---|---|
默认的方法实现 | 它可以有默认的方法实现 | 接口完全是抽象的。它根本不存在方法的实现 |
实现 | 子类使用extends关键字来继承抽象类。如果子类不是抽象类的话,它需要提供抽象类中所有声明的方法的实现。 | 子类使用关键字implements来实现接口。它需要提供接口中所有声明的方法的实现 |
访问修饰符 | 抽象方法可以有public、protected和default这些修饰符 | 接口方法默认修饰符是public。你不可以使用其它修饰符。 |
4.github练习题
实例1:使用抽象类模拟"去商场买衣服"场景
abstract class Market{//定义一个抽象类实现具体是什么商场、买什么东西
public String name;//商场名称
public String goods;//商品名称
public abstract void shop();//抽象方法,用来输出信息
}
//定义一个TaobaoMarket继承自Market抽象类,实现其中的shop()抽象方法
class TaobaoMarket extends Market{
public void shop(){
System.out.println(name+"网购"+goods);
}
}
//定义一个WallMarket类,继承自Market抽象类,实现其中的shop()抽象方法
class WallMarket extends Market{
public void shop(){
System.out.println(name+"实体店购买"+goods);
}
}
//创建WallMarket类与TaobaoMarket类创建抽象类的对象,并分别给抽象类中的成员变量赋不同的值
public class GoShopping {
public static void main(String[] args){
Market market=new WallMarket();//使用派生类对象创建抽象类对象
market.name="沃尔玛";
market.goods="七匹狼西服";
market.shop();
market=new TaobaoMarket();//使用派生类对象穿件抽象类对象
market.name="淘宝";
market.goods="韩度衣舍花裙";
market.shop();
}
}
代码运行结果:
练习1:创建 Shape(图形)类,该类中有一个计算面积的方法。圆形和矩形都继承自图形类,输出圆形和矩形的面积。
abstract class Shape{//定义一个形状的抽象类
public double r;//圆的半径
public double a;//长方形的长
public double b;//长方形的宽
public abstract void area();//抽象方法用来输出信息
}
//定义一个CfxArea类继承Shape抽象类,实现其中的area方法
class CfxArea extends Shape{
public void area(){
System.out.println("长为"+a+"宽为"+b+"的长方形的面积是:"+(a*b));
}
}
//定义一个CircleArea类继承Shape抽象类,实现其中的area方法
class CircleArea extends Shape{
public void area(){
System.out.println("半径为"+r+"的圆的面积为:"+(r*r*3.14));
}
}
public class Area {
public static void main(String args[]){
Shape area=new CfxArea();//使用派生类对象创建抽象类对象
area.a=7.2;
area.b=10;
area.area();
area=new CircleArea();//使用派生类对象创建抽象类对象
area.r=20;
area.area();
}
}
运行结果截图:
练习2:创建工厂类,工厂类中有一个抽象的生产方法,创建汽车厂和鞋厂类,重写工厂类中的抽象生产方法,输出汽车厂生产的是汽车,鞋厂生产的是鞋。
abstract class BigFactory{//创建抽象大工程类
public abstract void bigfactory();//构造抽象方法用来输出信息
}
//构造Car类继承BigFactory类
class Car extends BigFactory{
public void bigfactory(){
System.out.println("汽车工厂生产汽车");
}
}
//构造Shose类继承BigFactor类
class Shose extends BigFactory{
public void bigfactory(){
System.out.println("制鞋工厂生产鞋");
}
}
public class Factory {
public static void main(String args[]){
BigFactory factory=new Car();
factory.bigfactory();
factory=new Shose();
factory.bigfactory();
}
}
代码运行截图:
实例2:通过实现接口 绘制出特殊的平行四边形
interface DrawImage{//定义“画图形”接口
public void draw();//定义抽象方法“画”
}
class Rectangle implements DrawImage{//矩形实现了drawTest接口
public void draw(){
System.out.println("画矩形");
}
}
class Square implements DrawImage{
public void draw(){
System.out.println("画正方形");
}
}
class Diamond implements DrawImage{
public void draw(){
System.out.println("画菱形");
}
}
public class SpecialParallelogram {//定义特殊的平行四边形类
public static void main(String args[]){
//接口也可以进行行傻瓜转型操作
DrawImage[] images={new Rectangle(),new Square(),new Diamond()};
//遍历“画图形”接口类型的数组
for (int i=0;i<images.length;i++){
images[i].draw();//调用draw()方法
}
}
}
运行代码截图:
实例3: 爸爸喜欢做的事有抽烟和钓鱼,妈妈喜欢做的事有看电视和做饭,儿子完全继承了爸爸妈妈的爱好,使用多重继承输出儿子喜欢做的事。
interface DadLikeDo{//定义“爸爸喜欢做的事”接口
void smoke();//抽烟的方法
void fish();//钓鱼的方法
}
interface MomLikeDo{
void watchTV();//看电视的方法
void cook();//做饭的方法
}
//继承DadLikeDo接口和MomLikeDo接口
public class SonLikeDo implements DadLikeDo,MomLikeDo {
public void watchTV(){
System.out.println("看电视");
}
public void cook(){
System.out.println("做饭");
}
public void smoke(){
System.out.println("抽烟");
}
public void fish(){
System.out.println("钓鱼");
}
public static void main(String args[]){
SonLikeDo son=new SonLikeDo();//通过子类创建接口对象
System.out.println("儿子喜欢做的事情有:");
//子类队形调用DadLikeDo和MomLikeDo两个接口被实现的所有方法
son.watchTV();
son.cook();
son.smoke();
son.fish();
}
}
运行代码截图:
二、异常处理
1.什么是异常
概念:程序在运行过程中出现的不正常现象。如:数组越界、范围越界等不正常现象,当出现这些现象不处理时,程序将终止运行。
异常处理的必要性:任何程序都可能存在大量的未知问题、错误;如果不对这些问题进行性正确处理,则可能导致程序的中断,造成不必要的损失。
异常处理机制:Java编程语言使用异常处理机制为程序提供异常处理的能力。
2.异常的分类
Throwable:可抛出的,一切错误或异常的父类,位于java.lang包中。
- Error:JVM、硬件、执行逻辑错误,不能手动处理。(StackOverflowError&OutOfMemoryError)
- Exception:程序在运行和配置中产生的问题,可处理。(RuntimeException:运行时异常,可处理,可不处理&CheckedException:检查时异常,必须处理)
3.常见运行时异常
类型 | 说明 |
---|---|
NullPointerException | 空指针异常 |
ArrayIndexOutOfBoundsException | 数组越界异常 |
ClassCastException | 类型转换异常 |
NumberFormatException | 数字格式化异常 |
ArithmeticException | 算数异常 |
异常的产生:当程序在运行时遇到不符合规范的代码或结果时,会产生异常或程序员使用throw关键字手动抛出异常。
异常的传递:按照方法的调用链反向传递,如始终没有处理异常,最终会由JVM进行默认异常处理(打印堆栈跟踪信息)并中断运行。
4.异常处理
Java的异常处理通过5个关键字来实现:
- try:执行可能产生异常的代码
- catch:捕获异常 ,并处理
- finally:无论是否发生异常,代码总能执行
- throw:手动抛出异常
- throws:声明方法可能要抛出的各种异常
1.try…catch
try{
可能出现异常的代码
}catch(Exception e){
异常处理的关键代码,如:getMessage()、 printStackTrace()
}
会出现三种情况:
- 正常请求
- 出现异常并处理
- 异常类型不匹配
演示try…catch…语句的使用:输入两个数进行除法运算,注意当除数为零时需要抛出一个异常进行处理
import java.util.Scanner;
public class Demo1 {
public static void main(String args[]){
Scanner in=new Scanner(System.in);
int result=0;//不能在try中声明这个result变量,因为如果在try中声明则该变量只能在try中使用,不能在try以外使用
try {//将可能发生异常的代码放入try中
System.out.println("请输入第一个数字:");
int num1=in.nextInt();//输入的类型可能有问题
System.out.println("请输入第二个数字:");
int num2=in.nextInt();//输入的字符类型或输入零会出现问题
result=num1/num2;
}catch (Exception e){//捕获异常,是所有异常的父类
//处理(异常处理后可以执行下面的程序)
e.printStackTrace();//将出现的异常情况打印出来后执行下面的程序
}
System.out.println("运行结果是:"+result);
System.out.println("程序结束了....");
}
}
运行结果截图:
注意:不活捕获到异常才执行了try…catch…外的两个输出语句。若没有异常处理或该异常处理失败则不会出现后面的两个输出语句。
2.try…catch…finally
try{
可能出现异常的代码
}catch(Exception e){
异常处理的相关代码,如:getMessage()、printStackTrace()
}finally{
是否发生异常都会执行,可以释放资源等...
}
(1)finally块是否发生异常都执行,可以释放资源
(2)finally块不执行的唯一情况,退出Java虚拟机
演示try…catch…finally…使用
import java.util.Scanner;
public class Demo1 {
public static void main(String args[]){
Scanner in=new Scanner(System.in);
int result=0;//不能在try中声明这个result变量,因为如果在try中声明则该变量只能在try中使用,不能在try以外使用
try {//将可能发生异常的代码放入try中
System.out.println("请输入第一个数字:");
int num1=in.nextInt();//输入的类型可能有问题
System.out.println("请输入第二个数字:");
int num2=in.nextInt();//输入的字符类型或输入零会出现问题
result=num1/num2;
}catch (Exception e){//捕获异常,是所有异常的父类
//处理(异常处理后可以执行下面的程序)
e.printStackTrace();//将出现的异常情况打印出来后执行下面的程序
}finally {
System.out.println("释放资源....");
}
System.out.println("运行结果是:"+result);
System.out.println("程序结束了....");
}
}
当没有异常时的运行结果截图:
有异常时的运行结果截图:
3.多重catch
try{
可能出现异常的代码
}catch(异常类型1){
满足异常类型1执行的相关代码
}catch(异常类型2){
满足异常类型2执行的相关代码
}catch(异常类型3){
满足异常类型3执行的相关代码
}
(1)子类异常在前,父类异常在后
(2)发生异常时按顺序逐个匹配
(3)执行第一个与异常类型匹配的catch语句
(4)finally根据需要可写或不写
演示多重catch语句
import java.util.InputMismatchException;
import java.util.Scanner;
public class Demo2 {
public static void main(String args[]){
Scanner in=new Scanner(System.in);
int result=0;//不能在try中声明这个result变量,因为如果在try中声明则该变量只能在try中使用,不能在try以外使用
try {//将可能发生异常的代码放入try中
System.out.println("请输入第一个数字:");
int num1=in.nextInt();//输入的类型可能有问题
System.out.println("请输入第二个数字:");
int num2=in.nextInt();//输入的字符类型或输入零会出现问题
result=num1/num2;
}catch (ArithmeticException e){//捕获异常,是所有异常的父类
//处理(异常处理后可以执行下面的程序)
System.out.println("算数异常");//将出现的异常情况打印出来后执行下面的程序
}catch (InputMismatchException e){
System.out.println("输入不匹配异常");
}catch (Exception e){
System.out.println("未知异常");
}
System.out.println("运行结果是:"+result);
System.out.println("程序结束了....");
}
}
输入不匹配异常运行结果截图:
算数异常运行结果:
多加几个catch可以使程序更加精确,且子类异常在前,父类异常在后
4.try…finally
- try…finally不能捕获异常,仅仅用来当发生异常时,用来释放资源。
- 一般用在底层代码,只释放资源不做异常处理,把异常向上抛出。
try{
可能出现异常的代码
}finally{
是否发生异常都会执行,可以释放资源等...
}
演示try…finally…使用
import java.util.InputMismatchException;
import java.util.Scanner;
public class Demo3 {
public static void main(String args[]){//JVM
try {
divide();
}catch (Exception e){
System.out.println("出现异常"+e.getMessage());
}
}
public static void divide(){
Scanner in=new Scanner(System.in);
int result=0;//不能在try中声明这个result变量,因为如果在try中声明则该变量只能在try中使用,不能在try以外使用
try {//将可能发生异常的代码放入try中
System.out.println("请输入第一个数字:");
int num1=in.nextInt();//输入的类型可能有问题
System.out.println("请输入第二个数字:");
int num2=in.nextInt();//输入的字符类型或输入零会出现问题
result=num1/num2;
}finally {
System.out.println("释放资源");
}
System.out.println("运行结果是:"+result);
System.out.println("程序结束了....");
}
}
运行结果截图:
如果输入有异常发生不会出现finally外的输出结果
参考
【1】开源学习
【2】Java编程基础(第2版)清华大学出版社
【3】Java编程基础从入门到精通(小白速通篇)视频教程