JAVA基础 面向对象(三) 继承性 方法的重写 super 多态性 instanceof

11 篇文章 0 订阅
5 篇文章 0 订阅

一、继承性

子类可以继承父类的所有属性和方法。子类功能更加强大(一代更比一代强)

(一)继承的优势:

1、减少代码冗余,提高代码的复用性

2、便于功能扩展。如果子类都想加入某个功能或属性,直接在父类中加入就可以,非常方便。

3、为之后多态性的使用,提供了前提

(二)继承语法:  extends

权限修饰符 class 子类名 extends 父类名 {

子类属性;       //声明子类自己特有的属性

子类方法;       //声明子类自己特有的方法

}

说明:

1、子类,也叫派生类(C++),subclass;

2、父类,也叫基类(C++),超类,superclass

3、子类继承了父类以后,子类就获取了父类中声明的结构:所有的属性和方法

特别说明:父类里面private的属性和方法也获取到了,但是由于封装性的影响,没法直接调用。但这些属性和方法确实已经存在于子类的堆内存里面啦。

4、子类继承父类以后,可以声明自己特有的属性或方法,实现功能扩展

(三)Java中继承的规定

1、Java支持单继承,多层继承,不允许多重继承(C++可多继承)

一个子类只能有一个父类。(理解成父子关系,父亲可以有多个孩子,但是孩子只能有一个父亲)

2、子父类是相对的概念,子类直接继承的父类称为直接父类。间接继承的父类称为间接父类。子类继承父类以后,就获取了直接父类以及间接父类中声明的所有属性和方法。

3、Java中所有的类(包括自己写的类),都直接或间接继承于java.lang.object类。因此所有类都包含了object类的属性和方法(object类的属性和方法很通用)

注意:继承了很多层的子类,特别是网上找的一些大神写的类,根本不知道他们继承了哪些类的属性和方法。Idea中,可以用ctrl + H 查看类的继承结构。右键 Jump to source 就可以去看相关类的原码。

(四)子类方法(函数)重写   (注意不是重载)

    子类可以根据需要对从父类继承来的方法进行改造,也称为方法的重置,覆盖。在执行程序时,子类的方法将"覆盖"父类的方法。

注意:重写方法,必须方法名和形参列表都一样。这样才能精确定位是覆盖哪一个方法(父类可能有多个同名方法),只有非static才有重写。(static是类方法,每个类都可以有多个子类,子类继承父类方法的时候,先从静态开始,没法覆盖父类方法的,所以不能重写)

    方法重写以后,Idea会在代码行那里标记小三角。

特别注意:覆盖不是指让父类的方法消失,只是提供方便而已,父类的方法还是存在。只是需要用super显示调用父类方法了

重写语法:

权限修饰符 返回值类型 方法名(形参列表) 【throws 异常类型】 // 方法名(形参列表)必须和父类被重写方法一样,其他分情况讨论

{

方法体;         //自然需要和父类不一样

}

重写规定: (重写方法和被重写方法格式完全一样最好,就没后面那么多事了)

1、权限修饰符

子类重写方法的权限修饰符不小于父类被重新方法的权限修饰符。 (想象要覆盖一个东西,至少要一样大才能覆盖撒) 

特别:如果子类方法的权限修饰符是private,这种就没法重写。(想象都封装了,子类根本看不到这个方法,就没法重写)

2、返回值类型,

(1)如果父类被重写方法返回值类型为 void,则子类重写方法的返回值必须也为void (同为空void)

(2)如果父类被重写方法的返回值类型为基本数据类型,如float,则子类重写方法的返回值类型也必须是float。(同一基本数据类型)

(3)如果父类被重写方法返回值类型为引用数据类型,如某个类(比如String),则子类重写方法的返回值类型也必须是String类或String类的子类。(同一类或该类的子类)

3、异常类型

重写方法跑出的异常类型不大于父类被重写方法抛出的异常类型(必须是父类异常类型或其子类)和上面的(3)相似

总结:实际操作很简单,直接把父类复制过来就对了(相当于完全一样)。即使想搞点不同,而且IDEA也会帮你找问题的。同时,IDEA可以帮你补全重写提示" ctrl+ 空格"

(五)super关键字

      仿照this的理解,指向直接父类的指针,可以就将其看成父类。

使用场景:

1、调用父类构造器,子类之所以继承了父类的所有属性和方法,就是通过用super调用父类构造器获取的这些属性和方法。同时,也将父类构造器中的代码弄到子类来了,可以减少子类构造器中的代码量。

2、遇到子父类属性或方法同名的时候。//这种情况少,写代码的时候注意别让子父类同名就是了

语法: super.属性;  //调用父类属性

            super.方法;  //调用父类方法

           super(形参列表);  //调用父类构造器

特别说明:和this一样,super也只能用于代码块首行。换句话说,this 和 super 只能二选一。如果在类的构造器中没有显式声明“this(形参列表)”或“super(形参列表)”,系统会默认调用父类中的空参构造器,即super() 来继承父类的所有属性和方法。所以注意,类一定要提供空参构造器(里面没代码也行),否则子类在由于粗心没写super或this的时候,默认用super(),而父类又没有,就会报错。

使用场景解释:

1、子类继承了父类的所有属性和方法,子类可以添加自己特有的属性和方法。由于子类在新的一个作用域,因此子类自己的属性可以和父类的属性名字一样(编译器不会报错,但是编译器会晕)。属性没有重写。此时为了不让编译器晕,就需要用super关键字来区别子父类的属性。

2、如果子类重写了父类中的方法,编译器就会调用子类的同名方法。但此时如果还是需要使用父类的方法的话,就需要使用super关键字

3、子类的构造器,往往都是父类构造器+子类特有的属性赋值。由于父类构造器部分都已经写过了,自然子类中,就不想再写这一段了,因此可以用super(形参列表),来copy父类的代码。当然最重要的是,通过super继承父类的所有资源。

exp: public class Person{

int id=20;

public Person(int id){    //父类构造器,给人的id赋值,假设是身份证

  this.id = id;    

  }

......

}

public class Student extends Person {

int id=30;                //假设学生id代表学号

public Student(int id,int stuId){

  super(id);         //调用父类构造器public Person(int id) 给 Person id 身份证赋值

  this.id = stuId;   //  给子类学生id 学号 赋值

  }

public void show(){

   System.out.println("id="+id);    //输出30  在子类中,默认调用的是this.id 即子类的id

   System.out.println("id="+super.id);   //输出20  如果用了super关键字,就会调用父类的id

   }

}

总结:一般实际工作中,为了提高代码可读性,就不要搞啥子同名的了。

(六)子类的实例化过程

1、从结果上来看(继承性)

子类继承父类以后,就获取了父类中声明的属性或方法。

创建子类的对象,在堆空间中,就会加载所有父类中声明的属性。

2、从过程上来看

   当我们通过子类构造器构建子类对象时,我们一定会直接或间接调用其父类的构造器,进而调用父类的父类的构造器,一直到调用了最顶层 java.lang.object类中的空参构造器为止。正是由于加载过所有的父类的结构,所以才可以看到内存中有父类中的结构,子类对象才可以进行调用。

如图:通过 super 一级一级继承父类,直到 java.lang.object

二、多态性(polymorphism)   父类使用子类中重写的方法(虚拟方法调用)

    父类的引用指向子类的对象(子类对象赋给父类的引用),可以直接用于抽象类和接口上。

    有了对象多态性以后,我们在编译期,只能调用父类中声明的方法,因为有多个子类,编译器也不晓得要用哪个子类的方法(比如右边是通过随机数的值随机生成子类,编译时就完全没法知道子类是啥了),所以就编译左边父类的方法。但在运行期,右边的子类已经确定了,我们实际执行子类重写父类的方法(把子类方法虚拟成父类方法,即虚拟方法调用)。因此多态性是一个运行时的行为。

总结:编译,看左边;运行,看右边

语法: 父类 父对象名 = new 子类() ;  

说明:在内存中,由于new了子类(),子类确实已经完整的创建了。由于引用数据类型是父类(显然父类的内存空间比子类少),所以子类自身的属性和特有的方法,没法传给父类对象。如果需要把子类所有属性展示出来,可以用强制转换。即:

子类 对象名 =(子类)父对象名。 此时,提升后的对象就解锁了子类的所有属性和方法。但是如果提升的不对,可能出现ClassCastException异常。因此做变量提升前,可以先判断该对象是否为该类的实例。(instanceof运算符)

多态性使用前提:

1、类的继承关系

2、子类有方法的重写

注意:多态性只适用于方法,属性没有多态性。属性的编译和运行都看左边,即只管父类。

特点:

1、当调用子类父类同名,同参数的方法时,实际执行的是子类方法。

2、由于编译时编译父类,同时子类的方法没内存展示(被屏蔽)自然没法调用父类没有的方法。(即没法调用子类特有的方法)

应用场景举例:数据库有多种,mysql,db,oracle等等。虽然数据库的操作方法不一样(函数体不一样),但是操作步骤和方法名可以一样。因此可以写一个数据库的父类 connection,父类中定义好每种方法名。每种数据库一个子类,在子类中各自重写调用方法。这样调用数据库时,就可以写一个函数,形参是connection,方法connection.方法名。然后实参传子类(mysql,db等),就实现了多态。函数只写了一次代码,实际可以调用所有子类的方法。

应用场景举例2: 欢迎光临不同国家的人。

instanceof运算符是一个比较运算符,类似 == ,< ,返回一个boolean类型。

instanceof运算符用于判断对象a是否是类A的实例,如果是,返回true

使用场景:instanceof一般用于使用了检查多态的父类对象(天晓得这个对象引用的是哪个子类)。通过强制转换提升该对象以后,可以解锁子类的全部属性和方法,可是为了避免出错,就用instanceof 来检验以下。

exp: if ( p instanceof Woman ){  //判读对象p是否属于Woman类

Woman w = (Woman) p;

}

注意:一旦p instanceof  Woman为true,那p instanceof Woman的父类也是true,一直传递到object对象,都是true。就是因为多态性,父类 父对象 = new 子类();  所以子类对象都可以给父类,只是由于内存,不能显示那么多内容而已。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值