day-1-4-1

多态性

多态性是一种允许使用一个界面来访问一类动作的特性,特定的动作可以由不同的具体情况而定(传入不同的参数)。多态性是发送消息给某个对象,让该对象自行决定响应何种行为。

多态形成的三个必要条件:

  • 有继承,父类定义方法,子类重写方法

  • 父类的引用指向子类的对象

  • 可以使用参数传递时多态,也可以直接创建对象时多态

多态可以用三个定义和两个方法来总结。三个定义分别是父类定义子类构建、接口定义实现类构建和抽 象类定义实体类构建,而两个方法分别是方法重载和方法重写。

多态分为两种:

  • 编译时多态:方法的重载

  • 运行时多态:JAVA运行时系统根据调用该方法的实例的类型来决定选择调用哪个方法则被称为运行时多态

对象的多态性

继承表示的是is a的语义

class 动物{}
classextends 动物{}
猫 x=new();
动物 x=new(); 正确的,因为所有的猫都是动物,因为定义classextends 动物{}
猫 x=new 动物(); 错误,不是所有的动物都是猫,因为动物是猫的父类

对象多态性可以使程序有良好的扩展,并可以对所有类的对象进行通用处理

public class Test1 {
	public static void main(String[] args) {
		需要使用动物的类 tt=new 需要使用动物的类();
		tt.pp(new 动物());
		tt.pp(new());
	}
}
class 需要使用动物的类 {
	public void pp(动物 dongwu){ //假设定义方法时参数使用pp(猫 dongwu),当前程
序就和猫类耦合了,如果需要切换到狗则必须修改源代码。但是参数类型为动物,则不管是猫还是
狗都能接收
		dongwu.play();
	}
}

多态引用时,构造子类对象时的构造方法的调用顺序

  • 先调用超类的构造方法,多重超类首先调用最远超类的方法
  • 然后再执行当前子类的构造方法
this和super的用法

this用于指代当前对象,super用于指代父类的内存空间的标识

this关键字

this代表其所在函数所属对象的引用。换言之,this代本类的对象的引用

  • 当成员变量和局部变量重名,可以用关键字this来区分,this就是所在函数所属对象的引用

  • 对象调用了this所在的函数,this就代表哪个对象。一般方法调用默认加this

  • 通过this在构造函数中调用其他构造函数的时候,只能定义在构造函数的第一行,因为初始化动作要先执行,否则就会报错

super关键字
  • 当本类的成员和局部变量同名用this区分,当子父类中的成员变量同名用super区分父类
  • 当子父类中出现成员函数一模一样的情况,会运行子类的函数。这种现象,称为覆盖操作,这是函数在子父类中的特性。在子类覆盖方法中,继续使用被覆盖的方法可以通过super.函数名获取
  • this()表示调用当前类中的另外一个构造器,()中可以带有参数;super()表示调用父类中的某个构造器,()中可以带有参数
this()和super()
  • this()表示调用当前类中的另外一个构造器,()中可以带有参数

  • super()表示调用父类中的某个构造器,()中可以带有参数

类间多态和类内多态

多态是同一个行为具有多个不同表现形式或形态的能力。多态就是同一个接口,使用不同的实例而执行 不同操作

类间多态性

父子类之间的一种多态型,例如:动物 x = new 猫()

public class Test1 {
	public static void main(String[] args) {
		Animal a1=new Cat();
		A a=new A();
		a.eee(a1);
	}
}
class A{
	public void eee(Animal animal){
		animal.eat();
	}
}
class Animal{
	public void eat(){
		System.out.println("Animal.eat()");
	}
}
class Cat extends Animal{
	@Override
	public void eat() {
		System.out.println("就是爱老鼠");
	}
}
class Dog extends Animal{
	@Override
	public void eat() {
		System.out.println("就是爱肉骨头");
	}
}
类内多态性

在一个类对象上调用相同名称的方法,但是当参数不同时执行不同的动作

public class Test2 {
	public static void main(String[] args) {
		A2 a=new A2();
// 		a.pp();
// 		a.pp(12);
		a.pp("shitou");
	}
}
class A2 {
	public void pp() {
		System.out.println("A2.pp()");
	}
	public void pp(Integer k) {
		System.out.println("A2.pp(int)");
	}
	public void pp(Object kk) {
		System.out.println("A2.pp(String)");
	}
	public void pp(int k1,String k2){}
	public void pp(String k1,int k2){}
}

方法名称相同,参数不同,和返回值类型无关

  • 参数个数不同

  • 参数类型不同

  • 参数顺序不同。注意不同参数名称顺序不同

public class Test1 {
	public static void main(String[] args) {
		Fa ff=new Fa();
		ff.pp(new Integer(123)); //原则:最佳匹配原则
		ff.pp(1.234);
	}
}
class Fa {
	public void pp() {
		System.out.println("Fa.pp()");
}
	public void pp(Number kk){
		System.out.println("Fa.pp(Number)");
	}
	public void pp(Integer kk){
		System.err.println("Fa.pp(Integer)");
	}
}

不确定个数的参数

public class Test1 {
	public static void main(String[] args) {
		Fa ff = new Fa();
		ff.pp(); //最佳匹配原则
	}
}
class Fa {
	public void pp() {
		System.out.println("Fa.pp()");
	}
	public void pp(int... pages) {// 不确定个数的参数
	System.out.println("Fa.pp(int...)");
	}
}
方法的重写和重载

要求:方法的名称一致。

方法的重写(覆盖)

方法的重写(覆盖)一定发生在父子类之间

public class Test4 {
	public static void main(String[] args) {
		F4 f=new F4(); f.pp(10);
		S4 s=new S4(); s.pp(10);
		F4 fs=new S4(); fs.pp(10);
	}
}
class F4 {
	public void pp(int k) {
		System.out.println("F4.pp(int)");
	}
}
class S4 extends F4 {
	public void pp(int k) { //子类中定义的同名同参数的方法覆盖了父类中的方法定义,如果需要调用父类中的方法则需要使用super.pp(k)进行调用
		System.out.println("S4.pp(int)");
	}
}
  • 执行规则:new谁运行谁的方法,和声明的类型无关
  • 方法的覆盖定义要求方法名称一致
@Override注解可以使IDE工具在编译源代码时进行检查,如果有手写错误则IDE工具报错
  • 方法的参数一致(个数、类型、顺序),和参数名称无关
方法的重载

方法的名称相同,参数不同,和返回值类型无关。可以在一个类内或者父子类之间

调用规则:类型最佳匹配原则

class A5 {
	public void pp(){
		System.out.println("A5.pp()");
	}
	public int pp(){} 语法错误,因为在系统中识别一个方法是用【方法名称+参数类型列表】进行,系统会将这两个pp方法识别为同一个方法。注意:在一个类中不允许方法相同
	public void pp(int k){
		System.out.println("A5.pp(int)");
	}
}
class B5 extends A5 {
	public void pp(String k){
		System.out.println("B5.pp(String)");
	}
	public void pp(String k,int k1){}
	public void pp(int k,String k1){}
}
  • 要求:方法名称相同并且参数不同。参数不同有3种情况:参数个数不同、参数类型不同、参数顺序不同

    • 和参数名称无关
  • 和返回类型无关

    • 为什么返回类型不同判断不同的方法?【面试】

      例如调用方法时不需要处理返回值pp(10),容易造成混淆

      public double pp(int k){}
      public int pp(int k){}
      一个方法有返回值,调用处实际上可以不接收。假设系统可以按照返回类型将这两个方法识别
      为不同方法,则调用pp(10)就没办法判断到底执行哪个
      
  • 和范围无关

public void pp(){}
protected void pp(){}语法报错
  • 和方法抛出的异常无关
  • 方法的重载可以出现父子类之间,也可以是在一个类内。但是方法的覆盖一定是父子类之间,不能 在一个类内实现方法的覆盖
多态的优点
  • 消除类型之间的耦合关系
  • 可替换性、可扩充性
  • 接口性、灵活性、简化性

抽象类

面向对象编程的核心是面向抽象编程,一般依赖抽象不依赖具体

public class A{
	public void pp(Pig pig){} 如果进行切换类型,则必须进行修改源代码
}
-------------
public class A{
	public void pp(Animal animal){} 这里可以任意更换Animal的子类
}
class Pig extends Animal{}
class Cat extends Animal{}

包含了抽象方法的类叫作“抽象类”,所谓的抽象方法是指没有函数体的方法,抽象方法必须在子类中给出具体实现,而抽象类本身不能创建对象。 public abstract class A{}

  • 如果一个类中有抽象方法则这个类一定是抽象类
  • 抽象类中可以定义的内容与普通类一样,只是可以定义抽象方法
  • 抽象类中可以没有抽象方法
  • 普通类继承抽象类必须实现抽象类中所有的抽象方法
  • 抽象类一般是用来定义规范的,该规范的实现是通过子类来实现的
抽象类的特点
  • 方法只有声明没有实现时,该方法就是抽象方法【不是空实现方法体,没有{}】,需要被abstract修 饰,否则语法报错。抽象方法必须定义在抽象类中,该类必须也被abstract修饰

    • 抽象方法只能被public、protected或不加域修饰符修饰,抽象方法一定不能是private的
    • public void pp(){}不是抽象方法,只是方法的实现为空,有方法体
    • public void pp(); 没有{}才是没有方法体,才是抽象方法,当然需要添加关键字abstract
    • 不能定义抽象构造函数
      • 抽象类中可以定义构造器,也可以不定义构造器,使用系统默认提供的无参构造器,但 是自定义构造器不能private
      • 抽象类不能是final class
  • 不能定义抽象静态方法

    • 抽象类中可以有静态方法,但是必须有方法体,不能是抽象方法
    • 允许抽象类直接调用静态方法
  • 抽象类不能直接创建对象,只能通过继承的方式由子类实现对应的抽象方法;

    • 一般的使用方法为【动物 x=new 人();】
  • 所有抽象类的子类必须实现抽象父类中的所有抽象方法或者自己也声明成抽象类[没有实现所有的抽象方法

  • 抽象类除了可以有抽象函数,也可以有非抽象函数

    • 没有任何限制,允许属性、方法,也允许抽象方法
抽象类不可以被实例化

是因为调用抽象方法没有意义?

  • 抽象类必须有其子类覆盖了所有的抽象方法后,该子类才可以实例化,否则这个子类还是抽象类强调:注意抽象类中可以包含构造器、析构器、抽象方法和方法以及静态方法等,也可以没有抽象方法
什么时候使用抽象类
  • 当一个类的一个或多个方法为抽象方法时

  • 当该类为一个抽象类的子类,并且没有为所有抽象方法提供实现细节或方法主体时

  • 当一个类实现一个接口,并且没有为所有抽象方法提供实现细节或方法主体时

抽象类和普通类的区别
  • 抽象类不能直接实例化,并且对抽象类使用 new 运算符是编译时错误
  • 抽象类允许(但不要求)抽象类包含抽象成员
  • 抽象类不能被密封
    • 简单说就是被final修饰的类,密封类不能被继承,防止了恶意的派生
抽象类的作用

在面向对象方法中,抽象类主要用来进行类型隐藏。构造出一个固定的一组行为的抽象描述,但是这组 行为却能够有任意个可能的具体实现方式。这个抽象描述就是抽象类,而这一组任意个可能的具体实现 则表现为所有可能的派生类。模块可以操作一个抽象体。由于模块依赖于一个固定的抽象体,因此它可 以是不允许修改的

通过从这个抽象体派生,也可扩展此模块的行为功能。为了能够实现面向对象设计的一个最核心的原则 开闭原则OCP,抽象类是其中的关键所在

抽象类往往用来表征对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质 上相同的具体概念的抽象

OOP的核心是面向抽象编程

  • 在父类中固化算法骨架,在子类中实现特殊的方法
  • 依赖抽象
常见问题
抽象类中有构造器吗?
  • 有,用于给子类对象进行初始化
  • new 子类时抽象父类的构造器一定会被执行
抽象类可以不定义抽象方法吗?
  • 可以的,但是很少见
  • 目的就是不让该类创建对象
抽象类一定是个父类吗?
  • 是的
  • 因为需要子类覆盖其方法后才可以对子类实例化
创建对象时,加载对象的执行顺序
  • 先加载父类的静态变量和静态初始化块
  • 加载子类的静态变量和静态初始化块
  • 加载父类的成员变量、初始化块
  • 加载父类的构造方法
  • 加载子类的成员变量、初始化块
  • 加载子类的构造方法
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值