方法的重写和重载
要求:方法的名称一致。
方法的重写(覆盖)
要求:方法的名称一致
方法的重写(覆盖)一定发生在父子类之间
package com.yan6;
//有继承,父类定义方法,子类重写方法
public class Test2 {
public static void main(String[] args) {
// 父类的引用指向子类的对象
A2 aa = new B2();// new谁就是谁
aa.pp();
// aa对象有编译器类型和运行时类型2种类型
/*
* 编译器类型就是编译器所识别的类型,识别aa对象为A2类型,因为A2中并没有cc方法,所以aa.cc();编译失败
*
*
* 在代码中需要进行强制性转换
*/
// aa.cc();
((B2) aa).cc();
}
}
class A2 {
public void pp() {
System.out.println("A2...pp");
}
}
class B2 extends A2 {
public void pp() {
System.out.println("B2...pp");
}
public void cc() {
System.out.println("B2...cc");
}
}
执行规则:new谁运行谁的方法,和声明的类型无关,还有就是属性和static方法除外
方法的覆盖定义要求方法名称一致
@Override 注解可以使 IDE 工具在编译源代码时进行检查,如果有手写错误则 IDE 工具报错
方法的参数一致(个数、类型、顺序),和参数名称无关
顺序一致的问题, 系统识别方法依靠是方法名称和参数类型列表,和方法参数的名称无关。例如这里系统识别的方法为ear(String,String) 。要识别顺序还得依靠类型的区别,例如 eat(int,double) 和 eat(double,int)
返回数据类型一致(因为如果返回类型不一致,则无法进行语法检查,例如父类返回Double,而子类返回Integer,调用处语法检查是按照Double进行检查还是按Integer检查?允许父类中返回的是父类型,而子类返回子类型,例如父类中返回的是Number类型,而子类中返回的是Integer)
抛出异常一致,注意实际上允许子类抛出比父类更少的异常
package com.yan6;
import java.io.IOException;
public class Test3 {
public static void main(String[] args) throws Exception {
A3 aa = new B3();
aa.pp(21);
}
}
//方法覆盖要求方法的签名一致
//方法名称一致
//范围限定词要求子类中定义的方法范围需要大于等于父类中方法的范围
//返回值类型应该一致,也可以是子类返回类型是父类类型的子类
//参数必须一致,父子类型也不允许
//如果方法抛出异常,子类方法抛出的异常应该等于父类异常或父类异常的子类型
class A3 {
public Number pp(Integer kk) throws Exception {// 鲁棒性强
System.out.println("A3...pp");
return 0;
}
}
class B3 extends A3 {
@Override // 注解
public Integer pp(Integer kk) throws IOException {
System.out.println("B3...pp");
return 0;
}
}
静态方法的覆盖调用,用谁声明调用谁的静态方法,还可以用new的方法进行强转
package com.yan6;
//super
public class Test5 {
public static void main(String[] args) {
A5 aa = new B5();// 属性的覆盖会导致二义性,所以一般不用属性的覆盖
System.out.println(aa.getId());
System.out.println(aa.getId());
System.out.println(((B5) aa).getId1());
aa.pp();
}
}
class A5 {
public int id = 99;
public int getId() {
return this.id;
}
public void pp() {
System.out.println("A5:" + this.id);
}
}
class B5 extends A5 {
public String id = "zhangsan";
public String getId1() {
return this.id;
}
public void pp() {
System.out.println("B5:" + this.id);
System.out.println("B5:" + super.id);// super用于指代父类对象
System.out.println("================");
super.pp();
}
}
方法的重载
1、方法的名称相同,参数不同,和返回值类型无关。可以在一个类内或者父子类之间调用规则:类型最佳匹配原则2、要求:方法名称相同并且参数不同。参数不同有 3 种情况:参数个数不同、参数类型不同、参数顺序不同和参数名称无关3、和返回类型无关为什么返回类型不同判断不同的方法?例如调用方法时不需要处理返回值pp(10), 容易造成混淆public double pp(int k){}public int pp(int k){}一个方法有返回值,调用处实际上可以不接收。假设系统可以按照返回类型将这两个方法识别为不同方法,则调用pp(10) 就没办法判断到底执行哪个4、和范围无关5、和方法抛出的异常无关6、方法的重载可以出现父子类之间,也可以是在一个类内。但是方法的覆盖一定是父子类之间,不能在一个类内实现方法的覆盖
多态的优点
消除类型之间的耦合关系可替换性、可扩充性接口性、灵活性、简化性
抽象类
面向对象编程的核心是面向抽象编程,一般依赖抽象不依赖具体
包含了抽象方法的类叫作 “ 抽象类 ” ,所谓的抽象方法是指没有函数体的方法,抽象方法必须在子类中给出具体实现,而抽象类本身不能创建对象。 public abstract class A{}如果一个类中有抽象方法则这个类一定是抽象类抽象类中可以定义的内容与普通类一样,只是可以定义抽象方法抽象类中可以没有抽象方法普通类继承抽象类必须实现抽象类中所有的抽象方法抽象类一般是用来定义规范的,该规范的实现是通过子类来实现的
抽象类的特点
1、方法只有声明没有实现时,该方法就是抽象方法【不是空实现方法体 , 没有 {} 】,需要被 abstract 修饰,否则语法报错。抽象方法必须定义在抽象类中,该类必须也被abstract 修饰①抽象方法只能被public 、 protected 或不加域修饰符修饰,抽象方法一定不能是 private 的②public void pp(){}不是抽象方法,只是方法的实现为空 , 有方法体③public void pp(); 没有 {} 才是没有方法体,才是抽象方法,当然需要添加关键字 abstract④不能定义抽象构造函数抽象类中可以定义构造器,也可以不定义构造器,使用系统默认提供的无参构造器,但是自定义构造器不能private抽象类不能是final class2、不能定义抽象静态方法抽象类中可以有静态方法,但是必须有方法体,不能是抽象方法允许抽象类直接调用静态方法3、抽象类不能直接创建对象,只能通过继承的方式由子类实现对应的抽象方法;① 一般的使用方法为【动物 x=new 人 (); 】② 所有抽象类的子类必须实现抽象父类中的所有抽象方法或者自己也声明成抽象类[ 没有实现所有的抽象方法]③ 抽象类除了可以有抽象函数,也可以有非抽象函数没有任何限制,允许属性、方法,也允许抽象方法
抽象类不可以被实例化
是因为调用抽象方法没有意义?抽象类必须有其子类覆盖了所有的抽象方法后,该子类才可以实例化,否则这个子类还是抽象类 强调:注意抽象类中可以包含构造器、析构器、抽象方法和方法以及静态方等,也可以没有抽象方法
抽象类和普通类的区别
抽象类不能直接实例化,并且对抽象类使用 new 运算符是编译时错误抽象类允许(但不要求)抽象类包含抽象成员抽象类不能被密封简单说就是被final 修饰的类,密封类不能被继承,防止了恶意的派生
模板模式
定义抽象类的目的是提供可由其子类共享的一般形式,子类可以根据自身需要扩展抽象类
什么是模板模式
在模板模式 Template Pattern 中一个抽象类公开定义了总体的算法【算法骨架】,把没有办法在父类中实现的方法延迟到子类中具体实现。这种类型的设计模式属于行为型模式
何时使用
有多个子类共有的方法,且逻辑相同重要的、复杂的方法,可以考虑作为模板方法
为防止恶意操作,一般模板方法都加上final关键字
优缺点
优点:封装不变部分,扩展可变部分提取公共代码,便于维护行为由父类控制,子类实现。缺点:每一个不同的实现都需要一个子类来实现,导致类的个数增加,使得系统更加庞大。
抽象类的作用
在面向对象方法中,抽象类主要用来进行类型隐藏。构造出一个固定的一组行为的抽象描述,但是这组行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类,而这一组任意个可能的具体实现则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,因此它可以是不允许修改的通过从这个抽象体派生,也可扩展此模块的行为功能。为了能够实现面向对象设计的一个最核心的原则开闭原则OCP ,抽象类是其中的关键所在抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象OOP 的核心是面向抽象编程在父类中固化算法骨架,在子类中实现特殊的方法依赖抽象
常见问题
1、抽象类中有构造器吗 ?有,用于给子类对象进行初始化new 子类时抽象父类的构造器一定会被执行2、抽象类可以不定义抽象方法吗 ?可以的,但是很少见目的就是不让该类创建对象3、抽象类一定是个父类吗 ?是的因为需要子类覆盖其方法后才可以对子类实例化4、创建对象时,加载对象的执行顺序先加载父类的静态变量和静态初始化块加载子类的静态变量和静态初始化块加载父类的成员变量、初始化块加载父类的构造方法加载子类的成员变量、初始化块加载子类的构造方法
类与类之间的耦合问题
OOP 要求类内高内聚、类间弱耦合 --- 客户需求变动//如何实现类和类之间松耦合 --- 使用抽象不用具体public class A {private BubbleSorter bs ; //PigSorter}