1 多态
1.1 概述
在编程语言和类型论中,多态(英语:polymorphism)指为不同数据类型的实体提供统一的接口。 多态类型(英语:polymorphic type)可以将自身所支持的操作套用到其它类型的值上。
多态是面向对象程序设计(OOP)的一个重要特征,指同一个实体同时具有多种形式,即同一个对象,在不同时刻,代表的对象不一样,指的是对象的多种形态。
可以把不同的子类对象都当作父类来看,进而屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,统一调用标准。
1.2 特点
- 多态的前提1:是继承
- 多态的前提2:要有方法的重写
- 父类引用指向子类对象,如:Animal a = new Cat();
- 多态中,编译看左边,运行看右边
1.2.1 多态入门案例
public class TestDemo {
public static void main(String[] args) {
Animal a = new Animal();
Cat c = new Cat();
Dog d = new Dog();
a.eat();
c.eat();
d.eat();
/** 2、父类不能调用子类的特有功能 */
//a.play();
c.play();
d.play();
/** 3、口诀:格式:父类引用指向子类对象
* 解释:创建出来的子类对象地址值交给父类类型的引用类型变量来保存
*/
Animal a2 = new Cat();
Animal a3 = new Dog();
/**
* 4、口诀:编译看左边运行看右边
* 解释:必须要父类定义这个方法,才能通过编译,编译时,把多态对象看做是父类类型
* 必须要子类重写这个方法,才能满足多态,运行时实际干活的是子类
*/
a2.eat();
//a2.play();// 多态对象把自己看做父类类型,不能使用子类的特有方法
}
}
/**
* 1、多态的前提:继承 + 重写
*/
class Animal {
public void eat() {
System.out.println("小动物Animal吃啥都行~");
}
}
class Cat extends Animal {
@Override
public void eat() {
System.out.println("小猫爱吃小鱼干~");
}
public void play() {
System.out.println("小猫Cat跳得老高啦~");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("小狗爱吃肉骨头~");
}
public void play() {
System.out.println("小狗Dog跑得老快啦~");
}
}
1.3 多态的好处
- 多态可以让我们不用关心某个对象到底具体是什么类型,就可以使用该对象的某些方法
- 提高了程序的可扩展性和可维护性
1.3.1 多态的使用
前提:多态对象把自己看做是父类类型
- 成员变量: 使用的是父类的
- 成员方法: 由于存在重写现象,所以使用的是子类的
- 静态成员: 随着类的加载而加载,谁调用就返回谁的
1.3.2 案例练习1
public class TestDemo2 {
public static void main(String[] args) {
Animal2 a = new Animal2();
Dog2 d = new Dog2();
/**
* 口诀:父类引用指向子类对象
*/
Animal2 a2 = new Dog2();
d.eat();
d.play();
/**
* 口诀2:编译看左边,运行看右边
* 2、多态中成员变量使用的都是父类的
*/
System.out.println(a2.sum);
/**
* 3、多态中,成员方法的定义使用的是父类的,实现(方法体)使用的是子类的
*/
a2.eat();
/**
* 4、多态中,如果父子类都有静态重名的方法,这个不是重写的现象,所以静态方法调用的还是父类的实现(方法体)
*/
a2.play();
}
}
class Animal2 {
int sum = 10;
public void eat() {
System.out.println("吃啥都行");
}
public static void play() {
System.out.println("玩啥都行");
}
}
/**
* 1、多态前提:继承 + 重写
*/
class Dog2 extends Animal2 {
int sum = 20;
@Override
public void eat() {
System.out.println("小狗爱吃肉包子!");
}
/**
* 3、静态方法不存在重写的现象,在哪个类里写,就属于哪个类的资源,可以被类名直接调用
*/
public static void play() {
System.out.println("小狗爱打滚儿~");
}
}
1.3.3 案例练习2
public class TestExec {
public static void main(String[] args) {
Car c = new Car();
System.out.println(c.getColor());
c.start();
c.stop();
BMW b = new BMW();
System.out.println(b.color);
b.start();
b.stop();
Car c2 = new BMW();
/**
* 多态对象调用方法,如果方法没有重写,使用的是父类的功能,因为他把自己看做父类型
* 如果方法重写了,那么方法的定义看父类,方法体看子类
*/
c2.start();
c2.stop();
}
}
class Car {
private String brand;
private String color;
private double price;
private double size;
public void start() {
System.out.println("启动我心爱的小车车~");
}
public void stop() {
System.out.println("赶紧刹车~");
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
public double getSize() {
return size;
}
public void setSize(double size) {
this.size = size;
}
}
class BMW extends Car {
String color = "五彩斑斓的黑";
@Override
public void start() {
System.out.println("咻咻咻~发射成功");
}
}
class TSL extends Car {
String color = "黑不溜秋的白";
public void fly() {
System.out.println("哎呀!我的小车车飞起来啦了~");
}
}
2 异常
2.1 概述
Java Exception 异常处理机制其实起源很早,所以它也不是个什么新鲜的东西,如果您对Exception机制一点都不了解,没关系,只是国内通常接触新事物的时间都相对的要晚老美几年,但了解Java Exception机制对Java开发人员来说是必要的,不过Exception 异常处理机制也并没有固定,在Anders 的眼里它也是一个试验性的东西。 理论上异常处理划分为两个模型(中止模型与继续模型),但实际使用方面我们对中止模型用的比较多,这个模型比较实用,而继续模型则不是那么的应用普遍,多少是耦合的过于紧密。
异常是一些用来封装错误信息的对象
它由异常的类型、提示信息、报错的行号提示三部分组成
2.2 异常的继承结构
2.3 异常的处理方式
当程序中遇到了异常,通常有两种处理方式:捕获或者向上抛出
当一个方法抛出异常,调用位置可以不做处理继续向上抛出,也可以捕获处理异常
大家可以结合生活中的例子:如果工作中遇到了问题,我们可以选择自己处理(捕获),或者交给上级处理(抛出)
- 捕获方式
- 抛出方式
对于不想现在处理或者处理不了的异常可以选择向上抛出
方式:在方法上设置异常的抛出管道,即:
在可能会会发生异常的方法上添加代码:
throws 异常类型
2.3.1 异常案例练习
public class ExceptionDemo2 {
//public static void main(String[] args) throws Exception {//因为没人解决了,不推荐
public static void main(String[] args) {
//method1();//测试暴露异常
//method2();//测试异常解决方案一
f3();//在method3()被调用之前处理它的异常
//method3();//测试异常解决方案二
}
private static void f3() {//本方法用于解决method3()抛出的异常
try{
method3();//业务是f3处理调用method3()时抛出的异常
}catch (Exception e){
System.out.println("请输入正确的数据!");
}
}
/*如果一个方法抛出了异常,那么谁调用这个抛出异常的方法,谁就需要处理这个异常
* 这里的处理也有两种解决方案:捕获自己解决/继续向上抛出
* 但是注意:我们一般会在main()调用之前处理掉异常
* 而不是把问题直接抛给main(),因为没人解决了*/
/*异常处理方案二:异常抛出--自己不处理,交给别人处理
* 异常抛出的格式:在方法定义的小括号与大括号之间写:throws 异常类型
* 如果有多个异常,可以使用逗号进行分隔*/
private static void method3()
throws InputMismatchException,ArithmeticException,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(要匹配的异常类型 异常名){
匹配到异常提供的解决方案
}
* */
private static void method2() {
try{
//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);
}catch(InputMismatchException e){//注意导包
System.out.println("请输入正确的数据类型!");
e.printStackTrace();//打印错误信息到控制台
}catch(ArithmeticException e){
System.out.println("除数不能为0!");
}catch(Exception e){/*提供的是通用解决方案,多态最为经典的一种实现*/
System.out.println("输入的数据不正确!请重新输入!");
}
/*关于多态,我们并不关心子类具体的异常类型
* 而是统一将所有的异常类型看作是父类类型来处理
* 提供的是通用的解决方案
* 所以异常处理可以嵌套,也可以不嵌套,只提供一种通用解决方案*/
/*注意:运行时异常可以不做编码处理,只不过有时运行时会报错
* Java是不强制要求代码做运行时异常的处理的,只不过处理后程序更加完美健壮*/
}
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.打印接收到两个整数的除法结果
System.out.println(a/b);
//输入任意一个小数:InputMismatchException--输入不匹配异常
//除数为0:ArithmeticException--算数异常
/*1.不要害怕BUG,真正的勇士敢于直面自己写的BUG*/
/*2.学会看报错的信息提示,确定自己错误的方向*/
/*3.学会看报错的行号信息,确定自己报错的位置,哪里不对点哪里
* 注意!!!源码不会错,要看自己写的代码*/
}
}
2.4 catch 和 throws
异常处理只有两种方式: catch 和 throws,所以必须二选一
由于Java语法本身的特点,需要开发者事先考虑异常如何处理,也就是我们常说的:“未雨绸缪”
对于初级开发者来说,我们可能会捕获,但不处理异常
try {
…
} catch(Exception e) {
}
底层异常,应该向前抛到前面处理
经验少时,不知道该在什么位置捕获处理,应该选择 throws
但是大家需要注意,在异常抛出时,有些异常比如运行时异常,可能并不会强制要求抛出此异常,调用时也没有报错显示需要额外处理,这个时候就需要大家平时多积累,掌握良好的编码习惯了,手动添加代码进行预处理,增强程序的健壮性了。
2.5 异常类型
程序错误分为三种:
编译错误(checked异常);
运行时错误(unchecked异常);
逻辑错误;
- 编译错误是因为程序没有遵循语法规则,编译程序能够自己发现并且提示我们错误的原因和位置,这个也是大家在刚接触编程语言最常遇到的问题。
- 运行时错误是因为程序在执行时,运行环境发现了不能执行的操作。
- 逻辑错误是因为程序没有按照预期的逻辑顺序执行。异常也就是指程序运行时发生错误,而异常处理就是对这些错误进行处理和控制。
2.6 throws 与 throw 的区别
- throws
用在方法声明处,其后跟着的是异常类的名字
表示此方法会抛出异常,需要由本方法的调用者来处理这些异常
但是注意:这只是一种可能性,异常不一定会发生 - throw
用在方法的内部,其后跟着的是异常对象的名字
表示此处抛出异常,由方法体内的语句处理
注意:执行throw一定抛出了某种异常
3 抽象类
3.1 概述
Java中可以定义被abstract关键字修饰的方法,这种方法只有声明,没有方法体,叫做抽象方法.
Java中可以定义被abstract关键字修饰的类,被abstract关键字修饰的类叫做抽象类
- 如果一个类含有抽象方法,那么它一定是抽象类
- 抽象类中的方法实现交给子类来完成
3.2 抽象方法格式
3.3 特点
- abstract 可以修饰方法或者类
- 被abstarct修饰的类叫做抽象类,被abstract修饰的方法叫做抽象方法
- 抽象类中可以没有抽象方法
- 如果类中有抽象方法,那么该类必须定义为一个抽象类
- 子类继承了抽象类以后,要么还是一个抽象类,要么就把父类的所有抽象方法都重写
- 多用于多态中
- 抽象类不可以被实例化
3.3.1 抽象类入门案例
public class AbstractDemo {
public static void main(String[] args) {
/**
* 3、抽象类不可实例化--创建对象
*/
//new Phone();
Phone p = new XM();
p.call();
p.message();
p.money1();
p.money2();
}
}
/**
* 1、被abstract修饰的类时抽象类
* 如果一个类中包含了一个抽象方法,那么这个类必须被声明一个抽象类
*/
abstract class Phone {
public void call() {
System.out.println("正在打电话~");
}
public void message() {
System.out.println("正在发短信~");
}
public void message2() {
System.out.println("正在发短信~");
}
/**
* 2、被abstract修饰的方法是抽象方法,抽象方法没有方法体
*/
abstract public void money1();
abstract public void money2();
}
/**
* 4、当一个类继承了一个抽象父类,有两种解决方案:
* 方案一:变成一个抽象子类
* 方案二:变成抽象子类,实现抽象父类中的所有抽象方法
*/
class XM extends Phone {
@Override
public void money1() {
System.out.println("实现父类未实现的方法");
}
@Override
public void money2() {
System.out.println("实现父类未实现的方法2");
}
}
3.3.2 总结
abstract注意事项:
抽象方法要求子类继承后必须重写。
那么,abstract关键字不可以和哪些关键字一起使用呢?以下关键字,在抽象类中。用是可以用的,只是没有意义了。
- private:被私有化后,子类无法重写,与abstract相违背。
- static:静态优先于对象存在,存在加载顺序问题。
- final:被final修饰后,无法重写,与abstract相违背。