08 面向对象三大特性

目录

一 封装

1.1 概述

1.2 为什么要进行封装?

1.3 Java中的封装

1.4 四种访问权限修饰符

1.5 练习

二 继承

2.1 继承的由来

2.2 继承的好处

2.3 语法格式

2.4 继承的特点之一:成员变量

2.4.1 父类成员变量私有化

2.4.2 父类和子类成员变量重名

2.4.3 成员变量的访问特点

2.5 继承的特点之二:成员方法

2.5.1成员方法的访问特点

2.5.2方法重写( override )

2.5.2 方法重载( overload )

2.6 继承的特点之三:构造方法

2.7 继承的特点之四:单继承

三 多态

3.1 概述

3.2多态的应用场景

3.3 多态中调用成员的特点

3.3.1示例

3.3.2多态调用成员的内存图解

3.4多态的优势和弊端

3.4.1 多态的优势

3.4.2多态的弊端


一 封装

1.1 概述

对象代表什么,就得封装对应的数据,并提供数据对呀的行为

  • 程序设计追求 高内聚、低耦合
    • 高内聚:类的内部数据操作细节自己完成,不允许外部干涉。
    • 低耦合:仅对外暴露少量的方法用于使用。
  • 封装:隐藏对象内部的复杂性,只对外部公开简单的接口,便于外界调用,从而提高系统的可扩展性、可维护性。换言之,将该隐藏的隐藏起来,该暴露的暴露出来

1.2 为什么要进行封装?

  • 使用者对类内部定义的属性(对象的成员变量)的直接操作会导致数据的错误、混乱或安全性问题。

  • 示例:

package com.encapsulation.demo1;

/**
 * 动物
 *
 * @author 逆水行舟
 * @version 1.0
 * @since 2022-12-01
 */
public class Animal {

    /**
     * 腿
     */
    public int legs;

    /**
     * 吃饭的方法
     */
    public void eat() {
        System.out.println("吃饭");
    }

    /**
     * 移动的方法
     */
    public void move() {
        System.out.println("移动");
    }
}

package com.encapsulation.demo1;

/**
 * @author 逆水行舟
 * @version 1.0
 * @since 2022-12-01
 */
public class AnimalTest {
    public static void main(String[] args) {
        Animal animal = new Animal();
        // 如果这边赋值的是-100?,不合法啊
        // 应该将legs属性保护起来,防止乱用
        animal.legs = 4;
        System.out.println(animal.legs); // 4
        animal.eat();
        animal.move();
    }
}

1.3 Java中的封装

  • Java 中通过将数据声明为私有的( private ),再提供公共的( public )方法:setXxx()getXxx() 来实现对该属性的操作,以实现以下的目的:
  • ① 隐藏一个类中不需要对外提供的实现细节。
  • ② 使用者只能通过事先定制好的 方法来访问数据 ,可以方便的加入控制逻辑,限制对属性的不合理操作。
  • ③ 便于修改,增强代码的维护性。

  • 示例:

package com.encapsulation.demo2;

/**
 * 动物
 *
 * @author 逆水行舟
 * @version 1.0
 * @since 2022-12-01
 */
public class Animal {

    /**
     * 腿 将属性进行私有化
     */
    private int legs;

    public int getLegs() {
        return this.legs;
    }

    public void setLegs(int legs) {
        if (legs != 0 && legs != 2 && legs != 3) {
            System.out.println("动物的腿一般为0、2、4");
            return;
        }
        this.legs = legs;
    }

    /**
     * 吃饭的方法
     */
    public void eat() {
        System.out.println("吃饭");
    }

    /**
     * 移动的方法
     */
    public void move() {
        System.out.println("移动");
    }
}

package com.encapsulation.demo2;

/**
 * @author 逆水行舟
 * @version 1.0
 * @since 2022-12-01
 */
public class AnimalTest {
    public static void main(String[] args) {
        Animal animal = new Animal();
        // 非法 动物的腿一般为0、2、4
        animal.setLegs(-100);
        System.out.println(animal.getLegs());
        animal.eat();
        animal.move();
    }
}

1.4 四种访问权限修饰符

  • Java 权限修饰符 public、protected 、缺省 、private 置于 类的成员 定义前,用来限定对象对该类成员的访问权限。

修饰符

类内部

同一个包中

不同包的子类

同一个工程

private

缺省

protected

public

  • 对于 class 的权限修饰符只可以用 public 和 缺省 :
    • public 类可以在任意地方被访问。
    • 缺省类 只可以被同一个包内部的类访问。

1.5 练习

  • 创建程序,在其中定义两个类:Person 和 PersonTest 。定义如下:用 setAge() 设置人的合法年龄( 0~130 ),用 getAge() 返回人的年龄。用PersonTest 类中实例化 Person 类的对象 b ,调用 setAge() 和 getAge() 方法,体会 Java 的封装性。

  • 示例:

package com.encapsulation.demo3;

/**
 * @author 逆水行舟
 * @version 1.0
 * @since 2022-12-01
 */
public class Person {
    /**
     * 年龄
     */
    private int age;

    /**
     * 获取年龄
     *
     * @return int
     */
    public int getAge() {
        return this.age;
    }

    /**
     * 设定年龄
     *
     * @param age
     *            年龄
     */
    public void setAge(int age) {
        if (age < 0 || age > 130) {
            System.out.println("输入不合法");
            return;
        }
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" + "age=" + this.age + '}';
    }
}

package com.encapsulation.demo3;

/**
 * @author 逆水行舟
 * @version 1.0
 * @since 2022-12-01
 */
public class PersonTest {
    public static void main(String[] args) {
        Person person = new Person();
        person.setAge(50);
        System.out.println(person.getAge());
        System.out.println(person);
    }
}

二 继承

2.1 继承的由来

  • 多个类中存在相同属性和行为的时候,将这些内容抽取到单独的一个类中,那么多个类中无需再定义这些属性和行为,只需要继承那个类即可。

  • 此处的多个类称为 子类(派生类) ,单独的这个类称为 父类(基类或超类)
  • 继承描述的是事物之间的所属关系,这种关系是: is a 的关系。例如:图中的猫属于动物,狗也属于动物。由此可见,父类更通用,子类更具体。通过继承,可以使得多种事物之间形成一种关系体系。

2.2 继承的好处

  • ① 继承的出现,减少了代码的冗余(重复),提供了代码的复用性。
  • ② 继承的出现,有利于功能的扩展。
  • ③ 继承的出现,让类和类之间产生了关系,提供了多态的前提。

注意:不要为了获取其它类中的某个功能而去继承。

2.3 语法格式

  • 在 Java 中,继承是通过 extends 关键字,声明一个子类继承一个父类。
  • 语法:

修饰符 class 父类{
    ...
}
修饰符 class 子类 extends 父类 {
    ...
}

  • 示例
public class Animal {
    String name;
    int age;
    public void eat(){
        System.out.println("动物:吃");
    }
    public void drink(){
        System.out.println("动物:喝");
    }
    private void name(){
        System.out.println("名字");
    }

}
public class Cat extends Animal {
    public void catchMouse(){
        System.out.println("捕捉");
    }
}
public class Dog extends Animal{
    public void look(){
        System.out.println("看护");
    }
}
public class Tes {
    public static void main(String[] args) {
        Cat cat=new Cat();
        cat.eat();
        cat.drink();
        //子类只能访问父类中非私有的成员
    }
}

2.4 继承的特点之一:成员变量

2.4.1 父类成员变量私有化

●父类的成员,无论是公有( public )还是私有( private ),均为被子类所继承。
●子类虽然会继承父类私有的( private )的成员,但是子类不能对继承的私有成员直接进行访问,却可以通过继承的 setter 和 getter 方法进行范访问。

  • 非私有成员变量继承的内存图

●示例:

public class Animal {
    private String name;
    int age;
    public void eat(){
        System.out.println("动物:吃");
    }
    public void drink(){
        System.out.println("动物:喝");
    }
    private void name(){
        System.out.println("名字");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
public class Cat extends Animal {
    public void catchMouse(){
        System.out.println("捕捉");
    }
}
public class Tes {
    public static void main(String[] args) {
        Cat cat=new Cat();
		// cat.name = "Jerry"; 编译报错
        cat.setName("aaa");
    }
}

2.4.2 父类和子类成员变量重名

  • 子类会继承父类所有的成员变量,那么如果子类出现和父类同名的成员变量会怎么样?
  • 父类代码:
public class Animal {
    /**
     * 年龄
     */
    int age = 88;
    /**
     * 姓名
     */
    private String name;

    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }

}

  • 子类代码:
public class Cat extends Animal {

    int age = 5;

    public void showAge() {
        System.out.println("Cat的年龄是:" + this.age + ",Animal的年龄是:" + super.age);
    }

}

  • 测试代码:
public class Test {
    public static void main(String[] args) {
        Cat cat = new Cat();
        cat.setName("Jerry");
        cat.showAge();
    }
}

  • 总结:
    • ① 当父类的成员变量私有化的时候,在子类中是无法直接访问的,所以是否重名没有任何影响;但是,如果想访问父类的私有成员变量,只能通过父类提供的 setter 和 getter 访问。
    • ② 当父类的成员变量非私有化的时候,在子类中是可以直接访问的,所以如果有重名,就需要加上 super.父类成员变量名 来进行区分。

注意:在实际开发中,虽然我们可以通过 super 关键字来实现区分父子类重名成员变量,但是不建议这么干。

2.4.3 成员变量的访问特点

继承中:成员变量的访问特点————就近原则

先在局部位置找,本类成员位置找,父类成员位置找,逐级往上

name:从局部位置开始往上找

this.name:从本类成员位置开始往上找

supper.name:从父类成员位置开始往上找

  • 示例

public class Fu {
    String name="fu";
}
class zi extends Fu{
 String name="zi";
 public void ziShow(){
     String name="ziShow";
     System.out.println(name); //就近原则:谁离我近,我就用谁
     System.out.println(this.name);//name="zi"
     System.out.println(super.name);//name="fu"
 }
}

2.5 继承的特点之二:成员方法

成员方法是否可以被继承?

只有父类中的虚方法才能被子类继承

虚方法:非private,非static,非final

  • 继承内存图

2.5.1成员方法的访问特点

在没有supper的情况下:也遵循就近原则,this调用时逐级往上寻找

  • 示例:
class Person{
        public void eat(){
            System.out.println("吃米饭,吃菜");
        }
        public void drink(){
            System.out.println("喝豆浆");
        }
    }
    
    class Student extends Person {
        public void lunch(){
//先在本类查看eat和drink方法,就会调用子类的,如果没有,就会调用父类中继承下来的eat和drink方法
            this.eat();
            this.drink();
            //直接调用父类中的方法
            super.eat();
            super.drink();
            
        }
    }

2.5.2方法重写( override )

  • 定义:在子类中可以根据需要对从父类中继承而来的方法进行改造,也称为方法的 重置覆盖 。在程序执行的时候,子类的方法将覆盖父类的方法。
  • 要求:
    • ① 子类重写的方法 必须 和父类被重写的方法具有相同的 方法名称参数列表
    • ② 子类重写的方法的返回值类型 不能大于 父类被重写的方法的返回值类型(这对于返回值类型为引用数据类型来说的,如果返回值类型是基本数据类型和 void 类型必须相同,换言之,只有继承,才会有父类和子类)。
    • ③ 子类重写的方法使用的访问权限 不能小于 父类被重写的方法的访问权限( 注意:子类不能重写父类中声明为privat权限的方法或final修饰的方法 )。
    • ④ 子类方法抛出的异常不能大于父类被重写方法的异常。

注意: 子类和父类中同名同参数的方法必须同时声明为非 static的(重写),或者同时声明为 static 的(不是重写,因为 static 方法是属于类的,子类无法覆盖父类的方法。

  • 示例:

public class Phone {

    public void sendMessage() {
        System.out.println("发送短信");
    }

    public void call() {
        System.out.println("打电话");
    }

    public void showNum() {
        System.out.println("显示来电号码");
    }
}

public class SmartPhone extends Phone {

    /**
     * 重写父类的来电显示号码功能,并增加自己的显示姓名和图片功能
     */
    @Override
    public void showNum() {
        // super.父类成员方法,表示调用父类的成员方法。
        super.showNum();
        // 增加自己的显示姓名和图片功能
        System.out.println("显示来电姓名");
        System.out.println("显示头像");
    }
}

public class SmartPhoneTest {
    public static void main(String[] args) {
        SmartPhone smartPhone = new SmartPhone();
        // 调用父类继承而来的方法
        smartPhone.call();
        // 调用子类重写的方法
        smartPhone.showNum();
    }
}

打电话

显示来电号码

显示来电姓名

显示头像

2.5.2 方法重载( overload )

  • 在同一类中:

public class Overload {

    public int max(int a, int b) {
        return a > b ? a : b;
    }

    public double max(double a, double b) {
        return a > b ? a : b;
    }

    public int max(int a, int b, int c) {
        return max(this.max(a, b), c);
    }
}

  • 在父子类中:
public class Father {
    public int max(int a, int b) {
        return a > b ? a : b;
    }
}

public class Son extends Father {
    public double max(double a, double b) {
        return a > b ? a : b;
    }
}

参考 05 面向对象-上 第九章方法重载

2.6 继承的特点之三:构造方法

  • 构造方法的定义:
    • ① 构造方法的名称和类名是一致的。
    • ② 构造器不声明返回值类型(和声明 void 不同)。
    • ③ 构造器不能被 static 、final 、synchronized 、abstract 、native 修饰,不能有 return 语句返回值。
    • 所以,子类是无法继承 父类的构造方法的。
  • 构造方法的作用:初始化实例变量的。但是,子类又会从父类那边继承所有成员变量,所以子类在初始化的过程中,必须先执行父类的初始化动作(子类的构造方法中默认有一个 super() ,表示调用父类的实例初始化方法,父类成员变量初始化后,才可以给子类使用),才能执行自己的初始化动作。

继承中构造方法访问特点是什么?

  • 子类不能继承父类的构造方法,但是可以通过supper()调用
  • 子类构造方法的第一行,有默认的supper()
  • 默认先访问父类的无参构造方法,再执行自己
  • 如果想要方法的父类有参构造,必须手动书写

2.7 继承的特点之四:单继承

  • ① Java 只支持单继承,不支持多继承。

// 一个类只能有一个父类
class C extends A{}

  • ② Java 支持多层继承。
class A{}
class B extends A{}
class C extends B{}

注意:顶层父类是 Object 类,所有类默认继承 Object 类作为父类。

  • ③ 子类和父类是一种相对概念。比如:B 类相对于 A 类来说是子类,但是相对于 C 类来说是父类。
  • ④ 一个父类可以同时有多个子类。

三 多态

3.1 概述

  1. 什么是多态?

同类型的对象,表现出不同形态;对象的多种形态

2.表现形式:

父类类型 变量名 = new 子类类名();

温馨提示:多态也可以应用在抽象类和接口上。

3.多态的前提条件:

    • ① 有继承关系,或实现关系。
    • ② 有方法重写。
    • ③ 父类引用指向子类对象。:Fu f=new Zi();

  • 示例(不同用户注册)
  • 父类Person
/**
 * @BelongsProject: StudyList
 * @BelongsPackage: com
 * @Author: GXY
 * @CreateTime: 2022-12-23  15:58
 * @Description: TODO
 * @Version: 1.0
 */
package com;
public class Person {
    private String name;
    private int age;

    public Person() {
    }

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

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    public void show(){
        System.out.println("name:"+name+",age:"+age);
    }
}

子类1:管理员

package com;

public class Admin extends Person{
    @Override
    public void show() {
        System.out.println("管理员的信息为:"+getName()+","+getAge());
    }
}

子类2:老师

public class Teacher extends Person{
    @Override
    public void show() {
        System.out.println("老师的信息为:"+getName()+","+getAge());
    }
}

子类3:学生

public class Student extends Person{
    @Override
    public void show() {
        System.out.println("学生的信息为:"+getName()+","+getAge());
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        Student student=new Student();
        student.setName("张三");
        student.setAge(18);

        Teacher teacher=new Teacher();
        teacher.setName("苍老师");
        teacher.setAge(25);

        Admin admin=new Admin();
        admin.setName("admin");
        admin.setAge(25);
        register(student);
        register(teacher);
        register(admin);
    }
    //这个方法既能接收学生,又能接收老师,又能接收管理员
    //只能写成这三个的父类
    public static void register(Person person){
        person.show();
    }
}

打印结果:

学生的信息为:张三,18

老师的信息为:苍老师,25

管理员的信息为:admin,25

4.多态的好处:

使用父类类型作为参数,可以接收所有子类类型,体现多态的扩展性和便利性。

3.2多态的应用场景

3.3 多态中调用成员的特点

  1. 成员变量:编译看左边,运行也看左边
  2. 成员方法:编译看左边,运行看右边

3.3.1示例

    • 成员变量:在子类的对象中,会把父类的成员变量也继承下的。
    • 成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的。
public class Test2 {
    public static void main(String[] args) {
        Animal animal=new Dog();
        System.out.println(animal.name);//动物 1
         animal.show();                 //Dog--------show
        
    }

}
class Animal{
    String name="动物";
    public  void show(){
        System.out.println("Admin--------show");
    }
}
class Dog extends Animal{
    String name="狗";
    @Override
    public  void show(){
        System.out.println("Dog--------show");
    }
}
class Cat extends Animal{
    String name="猫";
    @Override
    public  void show(){
        System.out.println("Cat--------show");
    }
}

3.3.2多态调用成员的内存图解

3.4多态的优势和弊端

3.4.1 多态的优势

  • 在多态形势下,右边对象可以实现解耦合,便于扩展和维护。

Person p= new Student();

p.work();//业务逻辑发生改变时,后续代码无需修改

  • 义方法的时候,使用父类型作为参数,可以接收所有子类对象,体现多态的扩展性和便利性。

StringBuilder sb=new StringBuilder();

3.4.2多态的弊端

  • 不能调用子类的特有功能
public class Test2 {
    public static void main(String[] args) {
        Animal animal=new Dog();
        System.out.println(animal.name);//动物 1
        animal.show();                 //Dog--------show
        //那么在编译的时候会先检查左边的父类中有没有这个方法,若果没有直接报错
        animal.lookHome();
        解决方案:
        

    }

}
class Animal{
    String name="动物";
    public  void show(){
        System.out.println("Admin--------show");!
    }
}
class Dog extends Animal{
    String name="狗";
    @Override
    public  void show(){
        System.out.println("Dog--------show");
    }
    public  void lookHome(){
    System.out.println("Dog--------看家");
    }
}
class Cat extends Animal{
    String name="猫";
    @Override
    public  void show(){
        System.out.println("Cat--------show");
    }
     public  void catchMouse(){
    System.out.println("Cat--------捉老鼠");
    }
}
  • 解决方案:

//变回子类类型就可以了

//细节:转换的时候不能瞎转,如果转成其他类的类型,就会报错

//Cat c = (Cat) a;

//c.catchMouse();

  • 转换的时候不能瞎转,如果转成其他类的类型,就会报错
//先判断:转换类型与真实类型是否一致 instanceof
if(a instanceof Dog){
	Dog d= (Dog) a;
	d.1ookHome();
}else if(a instanceof Cat){
	CatC=(Cat) a;
	c.catchMouse();
}else{
	System.out.printin(“没有这个类型,无法转换");
}
  • jdk14新特性:先判断就转换
if(a instanceof Dog d){
    d.1ookHome();
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值