四 面向对象三大特征之继承的相关概念

5 篇文章 0 订阅

 

继承(Inheritance)是面向对象编程的三大特征之一,它让我们更加容易实现对于已有类的扩展、更加容易实现对于现实世界的建模。

1 .1 继承及其作用

继承让我们更加容易实现类的扩展。 比如,我们定义了人类,再定义Boy类就只需要扩展人类即可。实现了代码的重用,不用再重新发明轮子(don’t  reinvent  wheels)。

从英文字面意思理解,extends的意思是“扩展”。子类是父类的扩展。现实世界中的继承无处不在。比如:

 

                                                            

上图中,哺乳动物继承了动物。意味着,动物的特性,哺乳动物都有;在我们编程中,如果新定义一个Student类,发现已经有Person类包含了我们需要的属性和方法,那么Student类只需要继承Person类即可拥有Person类的属性和方法。

public class Animal  {//extends Object
    private String color;//颜色
    private int age;//年龄
    public Animal() {
        super();
    }
    public Animal(String color, int age) { //alt+insert
        this.color = color;
        this.age = age;
    }
    public void eat(){
        System.out.println("eating ..........");
    }
    public void sleep(){
        System.out.println("sleeping............");
    }
    public void introduce(){
        System.out.println(color+"  "+age);
    }
}

public class Dog extends Animal{
    private String nickName;//昵称
    public Dog() {
    }
    public Dog(String color,int age){
    }
    public Dog(String color,int age,String nickName){
//        this.color = color;
//        this.age = age;
        super(color,age);
        this.nickName = nickName;
    }
    public void guard(){
        System.out.println("Dog guarding......");
    }
}
public class Cat extends  Animal {
    private int eysSight;//视力
    public Cat() {
        super();//默认调用父类的无参数构造方法
    }
    public Cat(String color, int age, int eysSight) {
        super(color,age);
        this.eysSight = eysSight;
    }
    public  void grabMouse(){
        System.out.println("Cat grab mouse..........");
    }
}

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog("黑色",3,"旺财");
        dog.eat();//从父类继承的方法
        dog.sleep();//从父类继承的方法
        dog.introduce();//从父类继承的方法
        dog.guard();//子类特有的方法
        
        Cat cat = new Cat("花色",3,5);//alt+enter
        cat.eat();
        cat.sleep();
        cat.introduce();
        cat.grabMouse();
    }
}

 

继承使用要点

  1. 父类也称作超类、基类。子类:派生类等。
  2. Java中只有单继承,没有像C++那样的多继承。多继承会引起混乱,使得继承链过于复杂,系统难于维护。
  3. 子类继承父类,可以得到父类的全部属性和方法 (除了父类的构造方法),但不见得可以直接访问(比如,父类私有的属性和方法)。
  4. 如果定义一个类时,没有调用extends,则它的父类是:java.lang.Object。

1.2方法重写

父类的方法introduce()已经无法满足子类的需求,怎么办?同理,Object类的toString()已经无法满足Animal类、Dog类的需求,怎么办?可通过方法重写(override)解决,或者称为方法覆盖。

方法重写示例

public class Animal {//extends Object
    protected String color;//颜色
    private int age;//年龄
    public Animal() {
        super();
    }
    public Animal(String color, int age) { 
        this.color = color;
        this.age = age;
    }
   public void introduce(){
        System.out.println(color+"  "+age);
    }
    @Override
    public String toString() {
        //return super.toString();
        return "Animal[color="+color+",age="+age+"]";
    }
}
public class Dog extends Animal{
    private String nickName;//昵称
    public Dog() {
    }
    public Dog(String color, int age){
    }
public Dog(String color, int age, String nickName){
//        this.color = color;
//        this.age = age;
        super(color,age);
        this.nickName = nickName;
    }
    public  void introduce(){
        //this.introduce();
        //super.introduce();
        System.out.println(color+"  "+this.getAge()+"  "+nickName);
    }
    public void guard(){
        //this.guard();
        //super.guard();
        System.out.println("Dog guarding......");
    }
    @Override
    public String toString() {
        //return super.toString()+" "+nickName;
        return "Dog[name="+color+",age="+getAge()+
",nickName="+this.nickName+"]";
    }
}

* 方法重写的规则:
*       1.方法名称和参数列表必须相同。
*           可以使用@override检测是否重写成功
*       2.子类返回值范围必须【小于等于】父类返回值范围
*       3.子类方法的权限必须【大于等于】父类的权限的修饰符
*       修饰符权限:public>protected>(default)什么都不写,默认>private.

重写注意:
     1. 方法的返回值类型是【基本数据类型】 则必须完全一致
        方法的返回值类型是【引用数据类型】 子类可以是父类返回值的孩子 <=父类
     2. 权限修饰符 不能比父类更加严格
     3.方法名字必须一致
     4.方法的参数 也要保持一致

 

易混点:方法重载和方法重写:

方法重载和方法重写(覆盖)是面向对象中两个重要概念,其实这两个概念之间没有什么关系,但是毕竟都是关于方法的,毕竟容易引起混淆。对此我也做了一些归纳,感觉能够把这两个概念很好的区分开。我打算从总体区别、细节区别两个方面来说明。

总体的区别:最主要的区别,是解决的问题不同,即作用不同。

 

英文

位置不同

作用不同

重载

overload

同一个类中

在一个类里面为一种行为提供多种实现方式并提高可读性

重写

override

子类和父类间

父类方法无法满足子类的要求,子类通过方法重写满足要求

细节的区别:一个方法的声明自左向右包括权限修饰符、方法返回值、方法名、参数列表、抛出的异常类型等。下面从这几方面说明区别

 

修饰符

返回值

方法名

参数

抛出异常

重载

无关

无关

相同

不同

无关

重写

大于等于

小于等于

相同

相同

小于等于

 

重载实例:构造方法重载、println()方法重载

重写实例:Object类的toString()、equals()、hashCode()等都可以被子类重写

可选

  1. 某些方法使用final修饰,将无法被重写。比如Object类的wait()、notify()等
  2. 静态方法无法进行方法重写。在Java中,如果父类中含有一个静态方法,且在子类中也含有一个返回类型、方法名、参数列表均与之相同的静态方法,那么该子类实际上只是将父类中的该同名方法进行了隐藏,而非重写。添加@override注解将会报错。

2.1 Object类

Object类是所有Java类的根基类,也就意味着所有的Java对象都拥有Object类的属性和方法。如果在类的声明中未使用extends关键字指明其父类,则默认继承Object类。

方法摘要

 boolean

equals(Object obj)  指示其他某个对象是否与此对象“相等”。

 Class<?>

getClass() 返回此 Object 的运行时类。

 int

hashCode() 返回该对象的哈希码值。

 void

notify()  唤醒在此对象监视器上等待的单个线程。

 void

notifyAll()    唤醒在此对象监视器上等待的所有线程。

 String

toString() 返回该对象的字符串表示。

 void

wait()  在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。

 void

wait(long timeout)    在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者超过指定的时间量前,导致当前线程等待。

 void

wait(long timeout, int nanos)     在其他线程调用此对象的 notify() 方法或 notifyAll() 方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。

     以上方法是Object类所有的public方法。

 

方法摘要

protected  Object

clone()      创建并返回此对象的一个副本。

protected  void

finalize()  当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。

Object类的Protected方法。

 

 

扩展:native关键字

  1. 一个native方法就是一个Java调用非Java代码的接口。一个native方法是指该方法的实现由非Java语言实现,比如用C或C++实现。
  2. 在定义一个native方法时,并不提供实现体,因为其实现体是由非Java语言在外面实现的。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。
  3. JNI是Java本机接口(Java Native Interface),是一个本机编程接口,它是Java软件开发工具箱(Java Software Development Kit,SDK)的一部分。JNI允许Java代码使用以其他语言编写的代码和代码库。Invocation API(JNI的一部分)可以用来将Java虚拟机(JVM)嵌入到本机应用程序中,从而允许程序员从本机代码内部调用Java代码。

 

2.2 成员变量的隐藏

如果父类和子类中有同名的成员变量,不存在变量的重写,分别占有自己的空间。子类的成员变量优先,称为成员变量的隐藏。

示例   成员变量的隐藏

public class Animal {
    String color="Animal的color";
    int age;
    public String getColor(){
        return color;
    }
}
public class Dog extends Animal {
    String color ="Dog的color";
    String nickName;
    public String getColor(){
        return color;
    }
    public String getSuperColor(){
        return super.getColor();
    }
    public void show(){
        String color = "方法的color";
        System.out.println(color);
        System.out.println(this.color);
        System.out.println(super.color);
    }
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.show();
        System.out.println(dog.getColor());
        System.out.println(dog.getSuperColor());
    }
}

2.3 继承情况下构造方法的调用过程

继承条件下构造方法的执行顺序

  1. 构造方法的第一条语句默认是super(),含义是调用父类无参数的构造方法
  2. 构造方法的第一条语句可以显式的指定为父类的有参数构造方法:super(.....)
  3. 构造方法的第一条语句可以显式的指定为当前类的构造方法:this(.....)

 

注意事项

  1. 每个类最好要提供无参数的构造方法
  2. 构造方法的第一条语句可以是通过super或者this调用构造方法,须是第一条语句
  3. 构造方法中不能同时使用super和this调用构造方法,并不是说不能同时出现this和super

 示例   继承情况下构造方法的调用过程

public class Animal {
    String color;
    private int age;
    public Animal(){
        super();
    }
    public Animal(String color,int age){
        this.color = color;
        this.age = age;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
public class Dog extends  Animal {
    private String nickName;
    private String type; 
    public Dog(){
        super();
    }
    public Dog(String color,int age,String nickName){
        //super();
        super(color,age);
        //this(color,age,nickName,"aaa");
        this.nickName = nickName;
    }
    public Dog(String color,int age,String nickName,String type){
        //super();
        //super(color,age);
        this(color,age,nickName);
        //this.nickName = nickName;
        this.type = type;
    }
    public String toString() {
        return this.color+" "+ this.getAge()+" "+this.nickName+"  "+this.type;
    }
    public static void main(String[] args) {
        Dog dog = new Dog("黑色",3,"旺财","泰迪");
        System.out.println(dog.toString());
    }
}

重写toString():
 *   直接输出属性的信息 不再 去额外定义一个方法 进行信息展示

3.1 super关键字

前面的示例中已经多次使用到了super关键字。

super“可以看做”是直接父类对象的引用。每一个子类对象都会有一个super引用指向其直接父类对象。

使用super可以

  1. 调用成员变量  super.color
  2. 调用成员方法  super.introduce();
  3. 调用构造方法  super(color,age);

注意

  1. 使用super调用普通方法,语句没有位置限制,可以在子类中随便调用。
  2. 在一个类中,若是构造方法的第一行代码没有显式的调用super(...)或者this(...);那么Java默认都会调用super(),含义是调用父类的无参数构造方法。这里的super()可以省略。

3.2 ==和equals方法

“==”代表比较双方是否相同。如果是基本类型则表示值相等,如果是引用类型则表示地址相等即是同一个对象。

Object类中定义有:public boolean equals(Object obj)方法,提供定义“对象内容相等”的逻辑。比如,判断两个Dog是否是一个Dog,要求color、age、nickName等所有属性都相同。

Object 的 equals 方法默认就是比较两个对象的hashcode,是同一个对象的引用时返回 true 否则返回 false。显然,这无法满足子类的要求,可根据要求重写equals方法。


/*

问题: 当 属性值完全相同时 认为是同一个对象
 */
public class TestPerson3 {
    public static void main(String[] args) {

        Person  p1 = new Person("杜甫",18);
        Person  p2 = new Person("杜甫",18);

        System.out.println(p1==p2);//false
        System.out.println(p1.equals(p2));//true false
        Person p3 = p1;
        System.out.println(p1.equals(p3));//true
        Person p4 = null;
        System.out.println(p1.equals(p4));//false


    }
}

 

public class Person {

   String name;
    int age;
    public Person(){}

    public Person(String name,int age){
        this.age = age;
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {
        //1.当两者的地址相同时肯定相等
        if(this==obj){
            return true;
        }

        //2.当obj==null时 则肯定不相等
        if(obj==null){
            return false;
        }
       /* double d = 3.14;
        int i = (int)d;*/
        //比较 属性值
        Person p = (Person)obj;
        //比较 属性值
        if(this.age==p.age && this.name.equals(p.name)){
            return true;
        }
        return false;
    }
    @Override
    public String toString() {
public class Man extends Person {
    String sex;
    int score;


    public Man() {
    }

    public Man(String sex, int score) {
        this.sex = sex;
        this.score = score;
    }

    public Man(String sex, int score, String name, int age) {
        super(name, age);
        this.sex = sex;
        this.score = score;
    }

    @Override
    public boolean equals(Object obj) {//Object obj = man2; Man
        //比较父亲的属性
        boolean flag = super.equals(obj);
        if (flag) {//如果父亲属性值比较成功 再进行 子类元素的比较
            Man man = (Man) obj;
            if (this.score == man.score && this.sex.equals(man.sex)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public String toString() {
        return super.toString() + "sex = " + sex + ",score =" + score;
    }
}
public class TestMan {
    public static void main(String[] args) {


        Man man = new Man("男", 88);

        System.out.println(man.toString());


        Man man1 = new Man("男", 99, "白居易", 99);
        Man man2 = new Man("男", 99, "白居易", 99);

        boolean flag = man1.equals(man2);

        System.out.println(flag);

        System.out.println(man2);
    }
}

2.3 组合

继承和组合是复用代码的两种方式;

继承  is-a   Dog is-a Animal  Cat is-a Animal

组合    has-a   Computer has-a cpu memery mainBoard。

  

扩展: 面向对象的设计原则之一:组合聚合复用原则(优先使用组合,而不是继承)

  1. 除非两个类之间是“is-a”的关系,否则不要轻易的使用继承,不要单纯的为了实现代码的重用而使用继承,因为过多的使用继承会破坏代码的可维护性,当父类被修改时,会影响到所有继承自它的子类,从而增加程序的维护难度和成本。
  2. 不要仅仅为了实现多态而使用继承,如果类之间没有“is-a”的关系,可以通过实现接口与组合的方式来达到相同的目的。设计模式中的策略模式可以很好的说明这一点,采用接口与组合的方式比采用继承的方式具有更好的可扩展性

示例:组装一台电脑 ,电脑有CPU 主板  GPU 屏幕 等等  用 has a的关系组一台电脑

CPU:

public class CPU {
    String name;


    public CPU(String name){
        this.name = name;
    }

    public void start(){
        System.out.println(name+"  CPU开始运行");
    }
}

GPU: 

public class GPU {
    String name;

    public GPU(String name){
        this.name = name;
    }

    public void start(){

        System.out.println(name+" GPU 开始工作ing");
    }
}

 主板:

public class MainBoard {
    String name;

    public MainBoard(String name){
        this.name = name;
    }

    public void start(){
        System.out.println(name +" 主板开始执行。。。。。。");
    }
}

 屏幕:

public class Screen {
    private String name;

    public Screen(String name){
        this.name = name;
    }
    public void start(){

        System.out.println(name+ "  Screen 开始执行。。。。。。");
    }
}

电脑类:

public class Computer {
 

  CPU cpu;
  MainBoard mainBoard;
  Screen screen;
  GPU gpu;

  public Computer(){}

  public Computer(CPU cpu,MainBoard mainBoard,Screen screen,GPU gpu){
      this.cpu = cpu;
      this.gpu = gpu;
      this.mainBoard = mainBoard;
      this.screen = screen;
  }

  public void start(){
      cpu.start();
      mainBoard.start();
      gpu.start();
      screen.start();
  }

}

组合

/*
组合: has a的关系
      把一个类作为另一个类的属性。

电脑 has :
        CPU  AMD  intel
        mainBoard
        memory
        GPU
        Screen

 */
public class Test {

    public static void main(String[] args) {

        //买配件
        CPU cpu = new CPU("AMD");
        Screen screen = new Screen("京东方");
        GPU gpu = new GPU("雷蛇");
        MainBoard mainBoard = new MainBoard("华硕");


        //装电脑
        Computer computer = new Computer(cpu,mainBoard,screen,gpu);
        //启动


        computer.start();
    }
}

 

 


 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值