面对对象编程

本文深入探讨了面向对象编程中的核心概念,包括继承(is-a关系)、多态(向上转型、动态绑定、方法重写)、抽象类与接口。通过实例解释了如何创建和使用继承体系,以及如何利用多态提高代码的灵活性和可扩展性。同时,介绍了抽象类和接口在限制继承和规范实现方面的作用,强调了它们在设计模式和系统架构中的重要性。
摘要由CSDN通过智能技术生成

继承

继承表示的语意:is - a

继承的目的就是为了让代码更够很好的被重复使用

继承机制:是面对对象程序设计使用代码可以复用的最重要的手段。

继承主要解决的问题:共性的抽取

构造子类实例的时候,会在子类实例的内部自动构造一个父类的实例(子类实例中包含了父类的实例,父类的实例里就包含了父类的属性)

不写构造方法,就相当与生成了一个无参数的空的构造方法

父类只有一个带参数的构造方法,子类必须显式调用

创建子类实例的时候,先构造父类对象(执行父类构造方法的逻辑),在构造子类对象(再执行子类构造方法逻辑)
如果是显式调用父类的构造方法,那么必须把父类的构造方法放到第一行代码去执行,不能放到下面

为了以访继承层数过多而导致忘记,一般来说继承的层数最多为三层

语法规则

基本语法:

class 子类 extends 父类{

}
  • 使用 extends 指定父类
  • Java中一个子类只能继承一个父类
  • 子类会继承父类所有 public 的字段和方法
  • 对于父类的 private 的字段和方法,子类中数无法访问的
  • 子类的实例中,也包含着父类的实例,可以通过 super 关键字得到父类实例的引用

extends 英文原意指 “扩展”. 而我们所写的类的继承,也可以理解成基于符立进行代码上的 “扩展”

例:创建一个 Animal 类,在创建一个 Cat 类和一个 Bird 类,Cat 类和 Bird 类都为 Animal 类的子类

class Animal {
     public String name;
     public Animal(String name) {
        this.name = name;
    }
}

public void eat(String food) {
      System.out.println(this.name + "正在吃" + food);
      }
}
class Cat extends Animal {
     public Cat(String name) {
         // 使用 super 调用父类的构造方法.
         super(name);
         }
}

class Bird extends Animal {
      public Bird(String name) {
      super(name);
}

public void fly() {
     System.out.println(this.name + "正在飞");
     }
}

public class Test {
      public static void main(String[] args) {
      Cat cat = new Cat("小黑");
      cat.eat("猫粮");
      Bird bird = new Bird("大白");
      bird.fly();
      }
}

protected 关键字

如果把字段设为 private, 子类不能访问. 但是设成 public, 又违背了 “封装” 的初衷.两全其美的办法就是 protected 关键字

  • 对于类的调用者来说,protected 修饰的字段和方法是不能访问的
  • 对于类的 子类同一个包的其他类 来说,protected 修饰的字段和方法是可以访问的

封装(访问权限控制,能被访问的范围越小就认为越高):

  • private:只能再类内部访问 (权限最高)
  • default(不写,默认的):包级权限,可以被同包的其他类访问 (第二)
  • protected:可以被子类访问,也可以被同包的其他类访问 (第三)
  • 可以再类外部访问 (权限最低)

final 关键字

final 关键字,在修饰一个变量或者字段的时候,表示常量(不能修改)

final 关键字也能修饰类,此时表示被修饰的类不能被继承
使用 final 显式的禁止继承,防止继承被滥用

final public class Animal {

}

class Dog extends Animal{

}

在这里插入图片描述

组合

组合表示的语意:has - a

组合和继承类似,组合也是一种表示类之间关系的方式,也是能够达到代码重用的效果

例:一个学校

public class School{
    public SchlloMaster schlloMaster = new SchlloMasterpublic Student student1 = new Studentpublic Student student2 = new Studentpublic Student student3 = new Studentpublic ClassRoom classRoom = new ClassRoom}

class SchlloMaster{
}

class Student{
}

class ClassRoom{
}

组合并没有设计特殊的语法(如 ectends 关键字),仅仅是将一个类的实例作为里一个类的字段

多态

向上转型

向上转型这样的写法可以结合 is - a 语意来理解

向上转型:在创建子类实例的时候,会先构造父类的实例

在向上转型中,父类的引用只能访问到父类的属性和方法,访问不到子类独有的属性和方法

方法一:

public class Main{
    public static void main(String[] args) {
        Cat cat = new Cat();
        Animal animal = null;
        animal = cat;
        }
}

class Animal{
}

class Cat extends Animal{
}

方法二:

public class Main{
    public static void main(String[] args) {
        Animal animal = new Cat();
        }
}

class Animal{
}

class Cat extends Animal{
}

方法三:

public class Main{
    public static void main(String[] args) {
    fun(new Cat());
    }
    
    public static void func(Animal animal){
    }
}

class Animal{
}

class Cat extends Animal{
}

方法四:

public class Main{
    public static void main(String[] args) {
    Animal animal = func();
    }
    
    public static Animal func(){
    return new Cat();
    }
}

class Animal{
}

class Cat extends Animal{
}

动态绑定

规则:
父类 和 子类 中有同名的方法,并且参数也相同。
如果父类中包含的方法在子类中有队形的同名同参数的方法,就会进行 动态绑定.

例:

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

class Animal{
    public String name;
     
     public void eat(String food){
     System.out.println("Animal 正在吃" + food);
     }
}

class Cat{
}

如果 eat 方法只在父类中存在,此时调用此方法不涉及动态绑定.

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

class Animal{
    public String name;     
}

class Cat{
     public void eat(String food){
     System.out.println("Animal 正在吃" + food);
     }
}

在这里插入图片描述
如果 eat 方法只在子类中存在,此时调用此方法就会编译报错,不涉及动态绑定.

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

class Animal{
    public String name;
     
     public void eat(String food){
     System.out.println("Animal 正在吃" + food);
     }
}

class Cat{
     public void eat(String food){
     System.out.println("Animal 正在吃" + food);
     }
}

如果 eat 方法在父类和子类中都存在,并且参数相同,此时调用此方法就会涉及 动态绑定.
在程序运行时,看 animal 究竟是指向一个父类的实例还是子类的实例,指向父类实例就调用父类版本的 eat ,指向子类实例就调用子类版本的 eat .

如果父类和子类中存在同名但不同参数的方法,此时执行的一定是父类中的方法

方法重写

子类实现父类同名的方法,并且参数的类型和个数完全相同,这种情况称为 覆写/重写/覆盖(Override)

注:

  1. 重写和重载完全不一样
  2. 普通方法可以重写,static 修饰的静态方法不能重写
  3. 重写中子类的返回的访问权限不能低于父类方法的访问权限
  4. 重写的方法的返回值类型不一定和父类的方法相同(但是建议最好写成相同的,特殊情况除外)

推荐在代码中重写方法时显式加上 @Override 注解
有了这个注解能帮助我们进行一些些合法性校验,例如不小心将名字拼写错了时,那么编译器就会发现父类中没有此方法,就会编译报错,提示无法后成重写.

重写的规则

对于已经投入使用的类,尽量不要进行修改。最好的方式是:重新定一个新的类,来重复利用其中共性的内容,并且添加或者改动新的内容

重载重写
概念方法名称相同,参数的类型及个数不同方法名称、返回值类型、参数的类型及个数完全相同
范围一个类继承关系
限制没有权限要求被重写的方法不能拥有比父类更严格的访问权限

理解多态

多态:是一种程序设计的思想方法
具体的语法体现:向上转型、方法重写、动态绑定
多态直观的理解:一个引用,对应到多种形态(不同类型的实例)

在 Java 的体系中,必须搭配继承才能使用多态

多态的优势:

  1. 多态是封装的更进一步,让类的使用者不需要关注具体对象的类型也能正确使用
  2. 方便扩展.未来如果需要新增新的子类,对于类的使用者来说影象很小
  3. 消灭一些分支语句(减少if / else swich / case),降低程序的圈复杂度
public class Main {
    public static void main(String[] args) {
        Shape shape1 = new Circle();
        Shape shape2 = new Rect();
        Shape shape3 = new Flower();
        draw(shape1);
        draw(shape2);
        draw(shape3);
    }

    public static void draw(Shape shape){
        shape.draw();
    }
}

class Shape {
    public void draw(){

    }
}

class Circle extends Shape{
    @Override
    public void draw(){
        System.out.println("○");
    }
}

class Rect extends Shape{
    @Override
    public void draw(){
        System.out.println("□");
    }
}

class Flower extends Shape{
    @Override
    public void draw(){
        System.out.println("❀");
    }
}

在这里插入图片描述

向下转型

应用场景:
有些方法只是在子类中存在,但在父类中不存在,此时使用多态的方式就无法执行到对应的子类的方法,就必须把父类引用先转成子类的引用,然后在调用对应的方法

向下转型是存在限制的,不是随便就能转的
其实向下转型相当于向上转型的逆过程
通过向上转型得到的父类的引用,可以借助向下转型还原回原来的类型

public class Test {
    public static void main(String[] args) {
        Animal animal = new Cat();
        Cat cat = (Cat)animal;
    }
}

class Animal {
}

class Cat extends Animal{
}

class Bird extends Animal{
}

这样的转换就是正确

public class Test {
    public static void main(String[] args) {
        Animal animal2 = new Bird();
        Cat cat2 = (Cat)animal2;
    }
}

class Animal {
}

class Cat extends Animal{
}

class Bird extends Animal{
}

这样转换系统就会抛出一个异常
在这里插入图片描述
为了规避这样的异常,可以使用 instencesof 关键字,用于判定此时的 animal2 引用是否只想 Cat 类

public class Test {
    public static void main(String[] args) {
        if(animal2 instenceod Cat){
        Animal animal2 = new Bird();
        Cat cat2 = (Cat)animal2;
        } 
    }
}

class Animal {
}

class Cat extends Animal{
}

class Bird extends Animal{
}

super 关键字

super 表示获取到父类实例的引用

使用 super 调用父类的构造器

public class Animal {
    public String name = "小何";

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

class Cat extends Animal{
    public Cat(String name){
        super(name);
    }
}

使用 super 来调用父类的普通方法

public class Animal {
    public String name;

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

    public void eat(String food){
        System.out.println("小动物正在吃" + food);
    }
}
///分割线//
public class Bird extends Animal{
    public Bird(String name){
        super(name);
    }

    public void eat(String food){
        super.eat("谷子");
    }
}

super 和 this 的区别

thissuper
概念访问本类中的属性和方法由子类访问父类中的属性、方法
查找范围先查找本类,如果本类没有就调用父类不查查找本类而直接调用父类定义
特殊表示当前对象

在构造方法中调用重写(坑)

class A {
    public A() {
      func();
      }
    public void func() {
      System.out.println("A.func()");
      }
}

class B extends A {
    private int num = 1;
    @Override
    public void func() {
      System.out.println("B.func() " + num);
    }
}

public class Test {
    public static void main(String[] args) {
    B b = new B();
    }
}
  1. A 是 B 的父类. 构造 B 的时候, 就需要先构造 A 的实例
  2. 构造 A 的实例, 就会调用 A 的构造方法
  3. 调用 A 的构造方法的时候, 此时就会调用到 this.func(). 此时的 this 是指向子类的实例, 触发方法的动态绑定
  4. 如果在 B.func 打印 B 的 num 属性的值, 就会发现, 结果是 0. 在执行父类的构造方法的时候就触发了打印 num 值的操作,而此时此刻, B 的初始化代码(包括就地初始化和代码块以及构造方法都没有执行到)

抽象类

给一个类前面加上 abstract,此时这就是一个抽象类
如果尝试创建抽象类的实例,就会编译报错
抽象类除了不能实例化之外,其他的语法规则都和普通类一样

  1. 抽象类可以有普通的属性和方法
  2. 抽象类可以有静态的属性和方法
  3. 抽象类继承其他的类,也可以被其他类继承

给方法前面加上 abstract ,此时这个方法就是一个抽象方法

  1. 抽象方法不需要方法体
  2. 抽象方法只能在抽象类中存在(也可以在接口中存在),不能在普通类中存在的
  3. 抽象方法存在的意义就是为了让子类进行重写

基本格式:

abstract public class 类名 {
    abstract public 类型 方法名();
}

接口

接口在命名的时候一般用 I(大写) 作为前缀,且一般使用形容词词性的单词进行命名

语意:一个类具有 xxx 的特性

接口相当于一种约束,要求了实现该接口的类,必须重写所用的接口中的抽象方法

接口中可以放抽象方法,这里的抽象方法不必写 abstract 关键字(写或不写都是抽象方法)

接口中不能放普通的方法和属性,只能放 public static final 修饰的属性(写或者不写都是有 public static final 修饰)

接口不能继承自其他的类,但可以继承自其他的接口

接口不能被类继承,而是被其他的类 “实现” ,或者说某个类实现了接口

抽象类和接口的对比:

  1. 抽象类和普通类差不多,只是不能实例化,而接口和普通类之间相去甚远(包含属性、方法、和其他类的关系)
  2. 一个类纸鞥继承自一个抽象类,但一个类可以同时实现多个接口

为什么要有接口这样的语法呢??
为了解决 Java 中不能多继承的问题
Java 的继承是单继承,但有些场景下多继承是有用的,Java 中可以通过 继承一个类,实现多个接口 的方式来完成乐死于多继承的效果

接口存在意义:
技能实现类似于多继承的效果,同时又能规避多继承带来的问题

基本格式:

public interface 类名{
    public static final 类型 属性名;
    
    public static final 类型 方法名();
}

\\\\\\\\\\\\\\\\\分割线\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

public class 方法名 implements 接口方法名{
}

例:

public interface shape{
    public static final int = 10public static final void draw();
}

\\\\\\\\\\\\\\\\\分割线\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

public class Cercle implements shape{
}

接口之间可以继承的(说是“继承”,表述成“组合”)
例:

public interface IFlying {
    public static final void fly();
}

public interface ISwiming {
    public static final void swim();
}

\\\\\\\\\\\\\\\\\分割线\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\

public class Cercle implements IFlying, ISwiming {
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值