多态
概述
可以理解为事物存在的多种体现形态。同样的引用调用同样的方法却做了不同的事情
多态的本质是:一个程序中同名的不同方法。
多态的体现
父类的引用指向子类的对象,父类的引用接收子类的对象。
多态可以表现在很多方面,例如可以通过子类对父类方法的覆盖实现多态,也可以通过一个类中方法的重载实现多态,还可以将子类的对象作为父类的对象实现多态。
多态的前提
类与类之间必须是继承或者实现的关系。通常还有一个前提 就是覆盖
多态的利与弊
大大的提高了程序的扩展性,但是只能使用父类的引用访问父类中的成员。
多态中成员的特点
非静态成员函数:在编译时期,参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。在运行时期,参阅对象所属的类中是否有调用的方法。
总结,成员函数在多态调用的时候,编译看左边父类,运行看右边子类。
成员变量:无论编译和运行,都参考左边父类。
静态成员函数:无论编译和运行都参考左边父类。
如何使用子类特有方法:多态转型
Animal a=new Cat();
//类型提升,向上转型
a.eat(); //父类的共有方法
多态事例与扩展事例
学生例
/*
* 基础班学生:
* 学习 、睡觉
* 高级班学生:
* 学习、睡觉
*/
abstract class Student {
public abstract void study();
public static void sleeps() {
System.out.print("睡觉");
}
}
class jichu extends Student {
public void study() {
System.out.print("学习基础");
}
public static void sleeps() {
System.out.print("站着");
}
}
class gaoji extends Student {
public void study() {
System.out.print("学习高级");
}
public static void sleeps() {
System.out.print("趴着");
}
}
class DoStudent {
public void dosome(Student stu) {
stu.study();
stu.sleeps();
}
}
public class duotai {
public static void main(String[] args) {
DoStudent ds=new DoStudent();
ds.dosome(new jichu());//传的是子类对象
ds.dosome(new gaoji());
}
}
电脑运行实例
/*
* 需求;主板事例
* 电脑运行实例,电脑运行基于主板
*/
interface PCI {
public void open();
public void close();
}
class MainBoard {
public void run() {
System.out.println("主板运行");
}
public void usePCI(PCI p) {
if(p!=null) {
p.open();
p.close();
}
}
}
class netcard implements PCI {
public void open() {
System.out.println("网卡运行");
}
public void close() {
System.out.print("网卡关闭");
}
}
public class duotai {
public static void main(String[] args) {
MainBoard mb=new MainBoard();
mb.run();
mb.usePCI(new netcard());
}
}
实现
方法重写:不同的子类对父类的同一方法给出不同的实现版本
对象造型:用父类型的引用引用子类型的对象
方法的重载
方法的重载就是在同一个类中允许同时存在一个以上的同名方法,只要它们的参数个数或类型不同即可,在这种情况下,该方法就叫被重载了,这个过程称为方法的重载。与方法的覆盖不同,重载不是子类对父类同名方法的重新定义,而是类对自身已有的同名方法的重新定义。
Object类
在Object类中定义了toString()方法,在该方法中输出了对象的基本信息
匿名内部类
new 外部类名或者接口名() {
覆盖类或者接口中的代码(也可以自定义内容)
};
是内部类的简化格式
首先它是内部类,它最大的特点就是没有名字,而且我们不能显示的通过代码为它添加构造方法。
前提:内部类必须继承或者实现一个外部类或者接口
就是建立一个带有内容的外部类或者接口的子类匿名对象
匿名内部类中定义的方法最好不要超过3个
异常
什么是异常
异常属于程序在运行时出现的状况,编译器无法检查
异常是低耦合的处理运行时状况的方式,不打乱原有的业务逻辑
异常也称为例外,是在程序运行中发生的、会打断程序正常执行的非正常事件
Error类称为错误类,它表示Java运行时产生的系统内部错误或资源耗尽的错误,是比较严重的,仅靠修改程序本身是不能恢复执行的。
Exception类称为异常类,它表示程序本身可以处理的错误,在开发Java程序中进行的异常处理,都是针对Excption类及其子类。在Exception类的众多子类中有一个特殊的RuntimeException类,该类及其子类用于表示运行时异常,除了此类,Exception类下所有其它的子类都用于表示编译时异常。
异常的分类
Throwable
Error
IOError
AssertionError
VirtualMachineError
OutOfMemoryError
*Error
AWTError
Exception
受检异常(编译器会检查该类异常并且强制性的要求处理)
ClassNotFoundException
IOException
CloneNotSupportedException
InterruptedException
IllegalAccessException
NoSuchFieldException
NoSuchMethodException
SQLException
RuntimeException
NullPointerException空指针异常
IndexOutOfBoundsException
ArrayIndexOutOfBoundsException数组下标越界
ArithmeticException算数异常
ClassCastException
NegativeArraySizeException
SecurityException
IllegalArgumentException
NumberFormatException字符串转换为数字异常
ConcurrentModificationException
运行时异常
运行时异常大多数是由于程序设计不当而引发的错误,但是这种错误要在运行时才会发生和被发现,如零作除数、数组下标越界、访问空对象等。这类错误可以通过改进程序加以克服,不需对其进行捕获。如果发生了这类异常,系统可以自行处理。
检查型异常
也是在运行时发生的,但是编译器会在编译时进行检查,一旦发现某些语句使得此类异常有产生的“可能”,就会强制要求用户进行处理,否则不能通过编译。
try...catch和finally
try调用声明受检异常的方法必须使用try块进行保护,否则调用者也要进行异常声明
自行编写程序代码来捕捉异常的最大好处是:可以灵活操控程序的流程,且可以做出最适当的处理。
catch
一个try后面可以跟0个或多个catch
并列的catch最多只有一个有机会执行
异常的捕获遵循里氏替换原则
并列的catch应当先捕获子类型异常再捕获父类异常
没有父子关系的异常类型对catch的顺序没有要求
finally定义总是执行代码块,最适合释放外部资源
需要注意的是,finally中的代码块有一种情况下是不会执行的,那就是在try...catch中执行了System.exit(0)语句。System.exit(0)表示退出当前的Java虚拟机,Java虚拟机停止了,任何代码都不能再执行了。
throw
在程序出现状况的地方可以创建异常对象并用throw抛出
catch捕获到异常之后也可以用throw将异常对象再次抛出
再次抛出时可以将受检异常包装成运行时异常
注意:抛出异常时,throw关键字所抛出的是异常类的实例对象,因此我们要先用new关键字来产生一个对象。
throws关键字
子类重写的方法不能比父类被重写方法声明更多的异常
可以用throws为方法声明一个或多个异常,多个异常用逗号隔开
方法声明异常时需要遵循重写三原则
运行时异常和编译时异常
经常会在程序编译时期产生一些异常,而这些异常必须要进行处理,这种异常被称为编译时期异常,也称为checked异常。
编译时异常的特点是Java编译器会对其进行检查,如果出现异常就必须对异常进行处理,否则程序无法通过编译。
处理编译时期的异常有两种方式,具体如下:
使用try…catch语句对异常进行捕获
使用throws关键字声明抛出异常,调用者对其处理。
另外还有一种异常是在程序运行时期产生的,这种异常即使不编写异常处理代码,依然可以通过编译,因此我们称之为运行时异常,也称为unchecked异常。
运行时异常的特点是Java编译器不会对其进行检查
运行时异常一般是由于程序中的逻辑错误引起的,在程序运行时无法恢复。比如通过数组的角标访问数组的元素时,如果超过了数组的最大角标,就会发生运行时异常
自定义异常
在实际开发中,如果没有特殊的要求,自定义的异常类只需继承Exception类,在构造方法中使用super()语句调用Exception的构造方法即可。
class 异常名称 extend Exception { 类主体}
异常设计原则
不要将异常处理用于正常的控制流
对可以恢复的情况使用受检异常,对编程错误使用运行时异常
避免不必要的对受检异常的使用
优先使用标准异常
每个声明(抛出)异常的方法都要有文档
不要在catch中忽略掉捕获到的异常
保持异常的原子性
异常发生后对象能够恢复到异常发生之前的状态
避免过于庞大的try块
不要写一个catch(Exception e) {}捕获所有的异常,异常的捕获要有针对性