面向对象程序设计的三大特性

本文详细介绍了面向对象编程中的三个核心特性:封装、继承和多态。封装通过访问权限控制数据隐藏;继承支持代码复用和派生;多态包括重写、重载和动态绑定,展示了Java中的向上转型和向下转型示例。
摘要由CSDN通过智能技术生成

面向对象程序设计(OOP)语言有三个重要特征,分别是:封装、继承和多态

下面将详细讲述这三大特性。

封装

封装,就是将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。

在Java中,主要通过类和访问权限来实现封装

访问权限 \ 访问修饰限定符privateprotectedpublic
同一包中的同一类

同一包中的不同类

不同包中的子类
不同包中的非子类

访问权限除了可以限定类中成员的可见性,还可以控制类的可见性。

一般情况下,成员变量设置为private,成员方法设置为public

继承

继承,是面向对象程序设计中是代码可以复用的最重要的手段,它允许程序员在保持原有特性的基础上进行扩展,增加新功能,从而产生新的类,称为派生类,或者被称为子类。被继承的类被称为父类或基类。

【注意】:

  • 子类会将父类中的成员变量或者成员方法继承到子类中
  • 子类继承父类之后,要添加自己特有的成员,体现出与父类的不同,否则就没有必要继承了
  • 访问修饰限定符只能决定其访问权限,而不能决定其是否能被继承
  • Java不支持多继承,可以使用接口来解决多继承的问题

父类成员访问

子类访问父类的成员变量

 子类和父类不存在同名的成员变量
class Base {
    int a;
    int b;
}
class Derived extends Base{
    int c;
    public void method(){
        a = 10; // 访问从父类中继承下来的a
        b = 20; // 访问从父类中继承下来的b
        c = 30; // 访问子类自己的c
    }
}
子类中有和父类同名的成员变量

优先访问子类中的同名成员变量

在子类方法中或者通过子类对象访问成员时:

  • 如果访问的成员变量子类中有,优先访问子类的成员变量
  • 如果访问的成员变量子类中无,则访问父类继承下来的。如果父类中也没有定义,则编译失败
  • 如果访问的成员变量与父类中成员变量同名,则优先访问自己的

总的来说:成员变量的访问遵循就近原则。

 子类访问父类的成员方法

与访问父类中的成员变量的方法类似:

  • 通过子类对象访问父类与子类中不同名的方法时,优先在子类中找,找到则访问;否则在父类中找,找到则访问,否则编译报错。
  • 通过派生类对象访问父类与子类同名方法时,如果父类与子类的同名方法的参数列表不同,根据调用方法传递的参数选择合适的方法访问,没有则报错。

如果要在子类方法中访问父类的成员,可以使用super关键字

//super 的三个用法
super.成员变量
super.成员方法
super() // 调用父类的构造方法

【注意】:只能在非静态方法中使用

 super和this的异同点

this.成员变量
this.成员方法
this()  //构造方法


super.成员变量
super.成员方法
super() // 调用父类的构造方法

【相同点】

  1. 都是Java中的关键字
  2. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段
  3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在

【不同点】

  1. this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用
  2. 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问从父类继承下来的方法和属性
  3. 在构造方法中:this()用于调用本类构造方法,super()用于调用父类构造方法,两种调用不能同时在构造方法中出现
  4. 构造方法中一定会存在super()的调用,即使用户没有写编译器也会增加,但是this()用户不写则没有

子类构造方法

子类对象构造时,需要先调用父类构造方法,然后执行子类的构造方法(即子类在构造完成之前,要先帮助父类进行初始化,然后再将自己新增加的成员初始化)

【注意】:

  1. 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用父类构造方法
  2. 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败
  3. 在子类构造方法中,super()调用父类构造时,必须是子类构造函数中第一条语句
  4. super()只能在子类构造方法中出现一次,并且不能和this同时出现

代码块

代码块的执行顺序:

  1.  父类静态代码块优先于子类静态代码块执行,且是最早执行
  2. 父类实例代码块和父类构造方法紧接着执行
  3. 子类的实例代码块和子类构造方法紧接着再执行
  4. 第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行

final关键字

 final关键字可以用来修饰成员变量、成员方法以及类。

  • 修饰变量或字段,表示常量(即不能修改)
  • 修饰类:表示此类不能被继承
  • 修饰方法:表示该方法不能被重写

多态

 多态,通俗来说,即多种形态。展开来说就是当完成某个行为时,不同的对象会展现不同的状态

重写

重写,也成为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写, 返回值和形参都不能改变。

方法重写的规则

  • 子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名 (参数列表) 要完全一致
  • 被重写的方法返回值类型可以不同,但是必须是具有父子关系的
  • 访问权限不能比父类中被重写的方法的访问权限更低。
  • 父类中被static、private修饰的方法、构造方法都不能被重写。
  • 重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验

重载与重写的区别

区别点

重写重载
参数列表一定不能修改必须修改
返回类型一定不能修改(除非有父子关系)可以修改
访问限定符不能比被重写方法的访问权限更低可以修改

 方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现

重写的设计原则

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

动态绑定与静态绑定

静态绑定

静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用那个方法。典型代表函数重载。

动态绑定

动态绑定:也称为后期绑定(晚绑定),即在编译时,不能确定方法的行为,需要等到程序运行时,才能够确定具体调用那个类的方法。

在java中要实现多态,必须要满足如下几个条件,缺一不可:

  1. 必须在继承体系下
  2. 子类必须要对父类中方法进行重写(子类和父类必须有同名的覆盖/重写方法) 
  3. 通过父类的引用调用重写的方法

完成以上三部分,会发生动态绑定。动态绑定是多态的基础

向上转型与向下转型

向上转型

向上转型:实际就是创建一个子类对象,将其当成父类对象来使用。

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

常见的3个可以发生向上转型的时机:

  1. 直接赋值

  2. 方法的参数,传参的时候进行向上转型

  3. 返回值 向上转型

优点:

让代码实现更简单灵活。

缺陷:

不能调用到子类特有的方法

向下转型

将父类引用再还原为子类对象即可,即向下转换。

向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入了 instanceof ,如果该表达式为true,则可以安全转换。


class Animal {
    public String name;
    public int age;
    public Animal(String name,int age) {
        this.name = name;
        this.age = age;
    }
    public void eat() {
        System.out.println(this.name+"正在吃饭");
    }

}
class Dog extends Animal {
    public Dog(String name,int age) {
        super(name,age);
    }
    public void bark() {
        System.out.println(this.name+"汪汪");
    }

    /**
     * 如果在继承关系上,满足:
     *   1.方法的返回值一样
     *   2.方法名一样
     *   3.方法的参数列表一样
     * 那么就说这两个方法之间的关系是重写
     */
    @Override
    public void eat() {  //重写
        System.out.println(this.name+"正在吃狗粮");
    }


}
class Cat extends Animal {
    public Cat(String name,int age) {
        super(name,age);
    }
    public void bark() {
        System.out.println(this.name+"喵喵");
    }
    @Override
    public void eat() {  //重写
        System.out.println(this.name+"正在吃猫粮");
    }
}

/**
 * 常见的可以发生向上转型的3个时机:
 *   1.直接赋值
 *   2.方法的参数,传参的时候进行向上赋值
 *   3.返回值,向上转型
 */
public class Test {
    public static void main(String[] args) {
        Animal animal = new Dog("yuanyuan",2);
        //animal.bark();//向上转型的缺点:不能调用到子类特有的方法。
        Dog dog = (Dog) animal;  //向下转型
        dog.bark();//可以成功但不安全

        //如果animal引用的对象是Cat对象的实例 ,如果安全则是true
        if (animal instanceof Cat) {
            Cat cat = (Cat) animal;
            cat.bark();
        } else {
            System.out.println("123123");
        }

    }
    public static void eatFun(Animal animal) {
        animal.eat();
        //当父类引用的子类对象不一样时,调用这个重写的方法所表现出来的行为是不一样的
        //这种思想就叫做多态
    }

    public static void main5(String[] args) {
        Dog dog = new Dog("远远",2);
        eatFun(dog);
        Cat cat = new Cat("咪咪",2);
        eatFun(cat);
    }
    public static void main4(String[] args) {
        Animal animal = new Dog("远远",2);
        animal.eat();

        Animal animal1 = new Cat("咪咪",2);
        animal.eat();
    }

    public static void func1(Animal animal) {
/* 2.方法的参数,传参的时候进行向上赋值*/
    }
    public static Animal func2() {
        /*3.返回值,向上转型*/
        Dog dog = new Dog("huahua",10);
        return dog;
    }
    public static void main3(String[] args) {
        Dog dog = new Dog("huahua",10);
        func1(dog);  //这也是向上转型

        func2();  //这也是向上转型
    }
    public static void main2(String[] args) {
        /*1.直接赋值*/
        /*Dog dog = new Dog("Tom",10);
        //animal这个引用指向了dog这个引用所指向的对象
        Animal animal = dog;*/
        //合并为
        Animal animal = new Dog("Tom",10);  //向上转型
        animal.eat();  //重写后,调用子类的eat
        //animal.bark(); //不能调用
    }
    public static void main1(String[] args) {
        Dog dog = new Dog("abc",10);
        dog.eat();
        dog.bark();
        System.out.println();
        /*Animal animal = new Animal("huahua",10);
        animal.eat();
        //通过父类引用,只能调用父类自己特有的成员方法或者成员变量
        */
    }
}

多态的优缺点

【优点】

  1. 能够降低代码的“圈复杂度”,避免使用大量的if-else
  2. 可扩展能力更强

【缺点】

  • 代码的运行效率降低
  • 42
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值