Java中的继承, 封装, 多态

对于OOP语言而言, 有继承, 封装, 多态等多重特性, 但是最主要的就是继承, 封装和多态, 接下来我会依次进行介绍

一. 继承, 封装

“继承” 这个词对于我们来说并不陌生, 我们常常会听见继承家产, 继承家业之类的. 继承之后, 这些家产, 家业就都归继承者所有.

1. 什么是继承?

继承就是子类继承父类的一种行为.
继承的意义: 为了达到代码的复用效果

2. 怎样使用继承

(1). 继承的关键字: extends

class 子类 extends 父类 {
   // 属性
   // 方法
}

extends关键字的左右关系如下:
在这里插入图片描述
例如:

class Animal {
    public String name;
    public void eat() {
        System.out.println(this.name + "Animal :: eat()");
    }
}

class Cat extends Animal {
    // 继承了Animal的属性和方法(name, eat() )
}

class Bird extends Animal {
    // 继承了Animal的属性和方法(name, eat() )
    public void fly() {
        System.out.println(this.name + "Bird :: fly()");
    }
}

由以上可知, Animal 是父类, 而 Cat 和 Bird 是子类, 它们都继承了 Animal 的特性.

(2). 继承使用时的注意事项:

  1. 子类继承父类除构造方法以外的所有
  2. 在 Java 中, 一个子类只能继承一个父类
  3. 子类在构造的时候, 要先帮父类进行构造(重点)
class Animal {
    public String name;
    // 构造方法
    public Animal(String name) {  
        this.name = name;
        System.out.println("Animal(String)");
    }
    public void eat() {
        System.out.println(this.name + "Animal :: eat()");
    }
}

class Cat extends Animal {
    public Cat(String name) {
        // 帮父类进行构造
        super(name);
        System.out.println("Cat(String)");
    }
}

我们知道, 一个对象的创建分为两步: 1. 给对象分配内存; 2. 调用合适的构造方法.
Cat 在创建对象的时候, 需要调用自己的构造方法. 在创建自己的构造方法时, 需要先使用关键字 super 调用父类 Animal 的构造方法帮助Animal先构造, 然后再进行自己的构造.

这里引入了关键字 super , 这也很容易让我们联想到 this 关键字.
this 和 super 的区别是什么呢?

thissuper
当前对象的引用父类对象的引用
this( ): 调用当前类中其他的构造方法(必须放在第一行)super( ); 调用父类的构造方法(必须放在第一行)
this.data; 调用当前类中的属性super.data: 访问父类的属性
this.func: 调用当前类中的成员方法super.func: 调用父类的成员方法

3. 继承中的访问修饰限定符

访问修饰限定符: private, default(默认), protected, public

我们知道, private是用于封装的访问修饰限定符, 一个属性或者方法被private修饰后, 只能在当前类内访问.
如果父类中的属性或者方法被private修饰之后, 子类就无法访问到父类中的这些方法了.

那么, 一个代码, 既要体现出封装性, 又要体现出继承性, 该怎么办? protected就很好的解决了这个问题
protected 关键字主要就体现在继承上

Java中的四种访问修饰限定符
访问修饰限定符的访问权限: private < default < protected < public
在这里插入图片描述
值得注意的是: 不同包中的子类可以访问父类的 protected 属性或方法. 但是应该使用 super 去访问.

public class TestMain {
    protected int b;
}
public class TestDemo extends TestMain {
    public void func() {
        TestMain testMain = new TestMain();
        System.out.println(super.b); // 使用super来访问父类中protected的属性
    }
    public static void main(String[] args) {
        TestMain testMain = new TestMain();
//        System.out.println(testMain.b); // error  访问方式不对
    }
}

4. 多层继承

一个父类可以派生出许多子类, 子类还可以继续派生子类, 这就是多层继承
一般情况下, 继承最多为三层继承

(1). final 关键字

如果一个类不想再被继承的话,直接在类前加上 final 关键字即可
在这里插入图片描述
final关键字:

  1. final 修饰一个变量: 表示的是一个常量, 只能被初始化一次, 接下来就不能再修改了
  2. final 修饰一个类: 密封类, 一旦这个类被 final 修饰, 那他必然不能再被继承
  3. final 修饰一个方法: 密封方法, 一旦这个方法被 final 修饰, 那他就不能再修改

二. 组合

组合在程序中就是 has-a 的关系

class Student  {
    public String name;
    public String id;
}
class Teacher {
    public String name;
}
public class School {
    public Student student;
    public Teacher teacher;
}

因为学校是有老师和学生的, 所以 School 和 Student, Teacher 就是组合的关系.

三. 多态

1. 向上转型

父类引用 引用子类对象 (把子类赋值给父类)

// 发生了向上转型
Animal animal = new Cat();
Animal animal = new Bird();

发生向上转型的机遇:

  1. 直接赋值
  2. 传参
  3. 返回值
class Animal {
    public String name;
    public Animal(String name) {
        this.name = name;
        System.out.println("Animal(String)");
    }
    public void eat() {
        System.out.println(this.name + "Animal :: eat");
    }
}
class Cat extends Animal {
    public Cat(String name) {
        super(name);
        System.out.println("Cat(String)");
    }
}
public class TestMain {
    public static Animal func() {
        Cat cat = new Cat("mimi");
        return cat;
    }
    public static void main(String[] args) {
        // 3. 返回值
        Animal animal = func();
        animal.eat();
    }

    public static void func(Animal animal) {
        animal.eat();
    }
    public static void main2(String[] args) {
        // 2. 传参
        Cat cat = new Cat("mimi");
        func(cat);
    }

    public static void main1(String[] args) {
        // 1. 直接赋值
        Animal animal = new Cat("mimi");
        animal.eat();
    }
}

得到的结果:

Animal(String)
Cat(String)
mimiAnimal :: eat

通过上面的代码, 可以看出, animal 调用的 eat( ) 方法均为 Animal 自身的方法

2. 运行时绑定 / 动态绑定 / 多态

class Animal {
    public String name;
    public Animal(String name) {
        this.name = name;
        System.out.println("Animal(String)");
    }
    public void eat() {
        System.out.println(this.name + "Animal :: eat");
    }
}
class Cat extends Animal {
    public Cat(String name) {
        super(name);
        System.out.println("Cat(String)");
    }
    @Override // 重写
    public void eat() {
        System.out.println(this.name + "Cat :: eat()");
    }
}

public class TestMain {
    public static void main(String[] args) {
        Animal animal = new Cat("mimi");
        animal.eat();
    }
}

运行结果:

Animal(String)
Cat(String)
mimiCat :: eat()

同样是使用 animal 调用 eat( ) 方法, 这是却调用的是 Cat 的 eat( ) 方法, 这是因为在 Cat 的类中, 重写了 eat( ) 方法, 使得程序发生了运行时绑定.
编译时还访问的是 Animal 里的 eat( ) 方法, 运行时却调用的是 Cat 里的 eat( ) 方法.

3. 重写 / 覆盖 / 覆写(override)

注意事项:

  1. 需要重写的方法, 一定不能是被 final 修饰的. 如果是 final 修饰的方法, 就是密封方法,不能进行修改
  2. 被重写的方法, 访问限定修饰符一定不能是private
  3. 被重写的方法, 子类的访问限定权限一定要大于等于父类的访问限定权限
  4. 被 static 修饰的方法是不能进行重写的

重写 和 重载 的区别

重载重写
方法名相同方法名相同
参数列表不同(参数的个数, 类型)参数列表相同
返回值不做要求返回值相同
前提: 在同一个类中前提: 在不同的类中, 且具有继承的关系

4.向下转型(极其不安全)

class Bird extends Animal {
    public Bird(String name) {
        super(name);
        System.out.println("Bird(String)");
    }
    public void fly() {
        System.out.println(this.name + "Bird :: fly()");
    }
}

public class TestMain {
    public static void main(String[] args) {
        Animal animal = new Bird("八哥");
//        animal.fly(); // error   fly不是animal的方法
        // 发生了向下转型
        Bird bird = (Bird)animal;
        bird.fly();
    }

由于向下转型是极其不安全的, 因此, 我们会较少的使用向下转型.
若要使用向下转型, 最好加上 instanceof 关键字
A instanceof B : 判断 A 是不是 B 的一个实例

public static void main(String[] args) {
       Animal animal = new Cat("mimi");
       if(animal instanceof Bird) {
           Bird bird = (Bird)animal;
           bird.fly();
       } else {
           return;
       }
   }

显然, 这里一定返回空, 因为 animal 不是 Bird 的实例.

5. 在构造方法中调用重写的方法, 一定会发生运行时绑定

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值