• Java 面向对象的【三大特征】,你都掌握了吗



    前言

    📕各位读者好, 我是小陈, 这是我的个人主页
    📗小陈还在持续努力学习编程, 努力通过博客输出所学知识
    📘如果本篇对你有帮助, 烦请点赞关注支持一波, 感激不尽
    📙 希望我的专栏能够帮助到你:
    JavaSE基础: 基础语法, 类和对象, 封装继承多态, 接口, 综合小练习图书管理系统等
    Java数据结构: 顺序表, 链表, 堆, 二叉树, 二叉搜索树, 哈希表等
    JavaEE初阶: 多线程, 网络编程, TCP/IP协议, HTTP协议, Tomcat, Servlet, Linux, JVM等(正在持续更新)

    上篇文章介绍了Java里【类和对象】的相关知识,了解了什么是面向对象,什么是对象,如何创建对象等等

    在面向对象程序中,有 三大特点:封装,继承, 多态

    本篇将介绍这三大特点的相关知识


    提示:是正在努力进步的小菜鸟一只,如有大佬发现文章欠佳之处欢迎评论区指出讨论~ 废话不多说,发车~

    一、封装

    1.封装的概念

    简单来说:就是套壳屏蔽细节

    比如:对于电脑这样一个复杂的设备,提供给用户的就只是:开关机、通过键盘输入,显示器,USB插孔等,让用户来和计算机进行交互,完成日常事务

    但实际上:电脑真正工作的却是CPU、显卡、内存等一些硬件元件

    在这里插入图片描述

    计算机厂商在出厂时,在外部套上壳子,将内部实现细节隐藏起来,仅仅对外提供开关机、鼠标以及键盘插孔等,让用户可以与计算机进行交互即可

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

    那么如何进行隐藏呢?下面来认识一下“ 访问修饰限定符 ”


    2.访问修饰限定符

    Java中主要通过类和访问权限来实现封装:类可以将数据以及封装数据的方法结合在一起,更符合人类对事物的认知,而访问权限用来控制方法或者字段能否直接在类外使用

    Java中提供了四种访问限定符:
    述

    protected 主要是用在继承中( 继承部分详细介绍 )
    default 权限指:什么都不写时的 默认权限
    访问权限除了可以限定类中成员的可见性,也可以控制的可见性


    我们看这段代码:在这里插入图片描述
    因为在 PetDog 这个类当中,定义“ 名字 ”这个成员变量时用 private 修饰,所以在 Test 这个类中就不能通过对象名 + 点号 直接访问这个变量了

    那怎么办呢?
    不能直接访问,那就 间 接 访 问

    活人还能让尿憋死?

    定义 getter 和 setter 这两个成员方法

        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    快捷键:Alt + Insert —— 点击 Getter and Setter 即可

    然后就可以通过 get 和 set 这两个成员方法进行访问:

        PetDog dog1 = new PetDog();
        dog1.setName("坦克");
        System.out.println(dog1.getName());
    
    • 1
    • 2
    • 3

    我们把 name 这个成员变量隐藏起来,不对外公开,只提供 setter 和 getter 这两个方法可以访问 name ,而用户也不需要关心 setter 和 getter 是如何实现的,这就是封装

    一般情况下成员变量设置为 private,成员方法设置为 public,具体还得看业务需求


    3.初识 “ 包 ”

    在面向对象体系中,提出了一个软件包的概念即:为了更好的管理类,把多个类收集在一起成为一组,称为软件包

    有点类似于文件夹。比如:为了更好的管理电脑中的歌曲,一种好的方式就是将相同属性的歌曲放在相同文件下,也可以对某个文件夹下的音乐进行更详细的分类
    在这里插入图片描述

    在Java中也引入了,包是对类、接口等的封装机制的体现,是一种对类或者接口等的很好的组织方式,比如:一个包中的类不想被其他包中的类使用。

    包还有一个重要的作用:在同一个工程中允许存在相同名称的类,只要处在不同的包中即可。

    所以同一个包中不允许出现相同名称的类


    4. static 成员

    我们拿学生这个类来举例:

    class Student{
        // 成员属性
        public String name;
        public String classroom;
        
    	//构造方法
        public Student(String name, String classroom) {
            this.name = name;
            this.classroom = classroom;
        }
        
        // 成员方法
        public void doClass(){
            System.out.println(this.name + "在" + this.classroom + "上课");
        }
    
    }
    public class Test {
        public static void main(String[] args) {
            Student student1 = new Student("张三", "高三一班");
            Student student2 = new Student("李四", "高三一班");
            Student student3 = new Student("王五", "高三一班");
            
            student1.doClass();
            student2.doClass();
            student3.doClass();
        }
    }
    //输出结果:
    //张三在高三一班上课
    //李四在高三一班上课
    //王五在高三一班上课
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    这样的代码合适吗?

    这三个同学是同一个班的,那么他们上课肯定是在同一个教室,那既然在同一个教室,那 能否给类中再加一个成员变量 classroom,来保存同学上课时的教室呢

    在 Student 类中定义的成员变量( 例如name ),每个对象中都会包含一份( 称之为实例变量 ),因为需要使用这些信息来描述具体的某个学生

    而现在要表示学生上课的教室,这个 classroom 并不需要每个学生对象中都存储一份,而是需要让所有的学生来 共享

    在Java中,被 static 修饰的成员,称之为静态成员,也可以称为类成员,其不属于某个具体的对象,是所有对象所 共享

    所以这里的 classroom 应该用 static 修饰:

    	public static String classroom;
    
    • 1

    static修饰成员变量

    static 修饰的成员变量,称为静态成员变量,静态成员变量最大的特性:不属于某个具体的对象,是所有对象所共享的

    静态成员变量特性:
    不属于某个具体的对象,是类的属性,所有对象共享的,不存储在某个对象的空间( 堆 )中
    既可以通过 对象 访问,也可以通过 类名 + 静态变量名 访问,但一般更推荐使用类名访问
    存储在方法区当中
    生命周期伴随类的一生( 随 类的加载而创建,随 类的卸载而销毁 )

    既然 static 修饰的成员属性不属于对象了,那么刚才的代码中:" this.classroom "就是错误的写法

    那么 doClass 的方法体就要变成:

    public void doClass(){
    	System.out.println(this.name + "在" + classroom + "上课");
    }
    
    • 1
    • 2
    • 3

    static修饰成员方法

    一般类中的数据成员都设置为 private ,而成员方法设置为 public ,那设置之后,Student 类中 classRoom 属性如何在类外访问呢?

    	private static String classroom;
    
    • 1

    静态成员一般是通过静态方法来访问的

    Java中,被 static 修饰的成员方法称为静态成员方法,是类的方法,不是某个对象所特有的

    	...
    	public static String getClassRoom(){
    		return classRoom;
    	}	
    
    public static void main(String[] args) {
        System.out.println(Student.getClassRoom());
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    不需要实例化对象,只需要用 类名 + 点号 就可以访问到 classroom 了

    静态方法特性:
    不属于某个具体的对象,是类方法
    可以通过对象调用,也可以通过 类名 + 点号静态方法名 的方式调用,更推荐使用后者
    不能在静态方法中直接访问任何非静态成员变量
    静态方法中不能调用任何非静态方法,因为非静态方法有this参数,在静态方法中调用时候无法传递this引用


    static成员初始化

    注意:静态成员变量一般不会放在构造方法中来初始化,构造方法中初始化的是与对象相关的实例属性

    静态成员变量的初始化分为两种:就地初始化 和 静态代码块初始化

    就地初始化:

    	private static String classroom = "高三一班";
    
    • 1

    静态代码块初始化:
    使用 static 定义的代码块称为静态代码块。一般用于初始化静态成员变量

    	...
    	static {
    		classRoom = "高三一班";
    	}
    	...
    
    • 1
    • 2
    • 3
    • 4
    • 5

    二、继承

    1.为什么要继承

    Java中使用类对现实世界中实体来进行描述,类经过实例化之后的产物对象,则可以用来表示现实中的实体,但是现实世界错综复杂,事物之间可能会存在一些关联,那在设计程序是就需要考虑。

    比如:狗和猫,它们都是一个动物。使用Java语言来进行描述,就会设计出:

    // Dog 类
    public class Dog {
    	string name;
    	int age;
    	float weight;
    	public void eat() {
    		System.out.println(name + "正在吃饭");
    	}
    	public void sleep() {
    		System.out.println(name + "正在睡觉");
    	}
    	void Bark() {
    		System.out.println(name + "汪汪汪~~~");
    	}
    }
    
    // Cat 类
    public class Cat {
    	string name;
    	int age;
    	float weight;
    	public void eat() {
    		System.out.println(name + "正在吃饭");
    	}
    	public void sleep() {
    		System.out.println(name + "正在睡觉");
    	}
    	void mew() {
    		System.out.println(name + "喵喵喵~~~");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31

    通过观察上述代码会发现,猫和狗的类中存在大量重复
    比如猫和狗这两个类都定义了 name 、 age 、 weight 这些成员属性,也都定义了 eat 和 sleep 这两个成员方法
    有一处不同的是:狗是汪汪叫,猫是喵喵叫

    那能否将这些共性抽取呢?
    面向对象思想中提出了 继承 的概念,专门用来进行共性抽取,实现代码 复用


    2.继承的概念

    继承( inheritance )机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展增加新功能,这样产生新的类,称派生类
    继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用

    在这里插入图片描述
    上述图示中,Dog 和 Cat 都继承了 Animal 类
    其中:Animal 类称为 父类 / 基类 / 超类
    Dog 和 Cat 可以称为 Animal 的 子类 / 派生类
    继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可。

    从继承概念中可以看出继承最大的作用就是:实现代码复用,还有就是来实现多态( 后序讲 )


    3.语法

    在 Java 中如果要表示类之间的继承关系,需要借助** extends 关键字**,具体如下:

    修饰符 class 子类 extends 父类 {
    	// ... 
    }
    
    • 1
    • 2
    • 3

    我们把刚刚的代码用 继承 体现一下:

    // Animal 类
    public class Animal {
    	String name;
    	int age;
    	public void eat() {
    		System.out.println(name + "正在吃饭");
    	}
    	public void sleep() {
    		System.out.println(name + "正在睡觉");
    	}
    }
    
    // Dog 类
    public class Dog extends Animal {
    	void bark() {
    		System.out.println(name + "汪汪汪~~~");
    	}
    }
    
    // Cat 类
    public class Cat extends Animal {
    	void mew() {
    		System.out.println(name + "喵喵喵~~~");
    	}
    }
    
    // TestExtend 类
    public class TestExtend {
    	public static void main(String[] args) {
    		Dog dog = new Dog();
    		// dog 类中并没有定义任何成员变量
    		// name 和 age 属性肯定是从父类 Animal 中继承下来的
    		System.out.println(dog.name);
    		System.out.println(dog.age);
    		// dog 访问的 eat() 和 sleep() 方法也是从 Animal 中继承下来的
    		dog.eat();
    		dog.sleep();
    		// bark()方法是自己特有的
    		dog.bark();
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    注意:
    子类会将父类中的成员变量或者成员方法继承到子类中了
    子类继承父类之后,必须要新添加自己特有的成员,体现出与父类的不同,否则就没有必要继承了


    4.父类成员访问

    在继承体系中,子类将父类中的方法和字段( 属性 )继承下来了,那在子类中能否直接访问父类中继承下来的成员呢?

    1.子类中访问父类的成员变量

    当子类和父类中的成员变量不重名时:

    class A {
        int a = 10;
        int b = 20;
    }
    class B extends A {
        int c = 30;
        public void method() {
            System.out.println(this.a);// 访问从父类中继承下来的a
            System.out.println(this.b);// 访问从父类中继承下来的b
            System.out.println(this.c);// 访问子类自己特有的c
    
        }
    }
    public class Test {
        public static void main(String[] args) {
            B b = new B();
            b.method();
        }
    }
    // 输出结果是:
    // 10
    // 20
    // 30
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    当子类和父类中的成员变量重名时:

    class A {
        int a = 10;
        int b = 20;
        int c = 30;
    }
    class B extends A {
        int c = 40;
        public void method() {
            System.out.println(this.a);// 访问从父类中继承下来的 a
            System.out.println(this.b);// 访问从父类中继承下来的 b
            System.out.println(this.c);// 访问从父类中继承下来的 c ?还是子类自己的 c ?
    
        }
    }
    public class Test {
        public static void main(String[] args) {
            B b = new B();
            b.method();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    此时 输出的 c 的结果是 30 还是 40 呢?----答案是 40

    总结
    在子类方法中 或者 通过子类对象访问成员时
    如果访问的成员变量子类中有,优先访问自己的成员变量。
    如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
    如果访问的成员变量与父类中成员变量同名,则优先访问自己的。

    变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找


    2.子类中访问父类的成员方法

    原则和上文所总结的一致

    但我们有了一个问题:
    如果子类中存在与父类中相同的成员时,那如何在子类中访问父类相同名称的成员呢?

    下面我们来认识一下 super 关键字


    5. super 关键字

    由于设计不好,或者因场景需要,子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成员时,该如何操作?

    直接访问是无法做到的,Java提供了 super 关键字

    该关键字主要作用:在子类方法中访问父类的成员

    class A {
        int a = 10;
        int b = 20;
        int c = 30;
        public void func() {
            System.out.println(this.c);
        }
    }
    class B extends A {
        int c = 40;
        public void func() {
            System.out.println(this.c);
        }
        public void method() {
            super.func();// 访问父类的 func 方法
    
            System.out.println(super.c);// 访问从父类中继承下来的 c
        }
    }
    public class Test {
        public static void main(String[] args) {
            B b = new B();
            b.method();
        }
    }
    // 运行结果:
    // 30
    // 30
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    在子类方法中,如果想要明确访问父类中成员时,借助super关键字即可

    注意事项:
    只能在非静态方法中使用
    在子类方法中,访问父类的成员变量和方法


    6. 子类构造方法

    子类是类,所以在子类中同样也可以有构造方法
    父子父子,先有父再有子
    即:子类对象构造时,需要先调用父类构造方法,然后执行子类的构造方法

    在这里插入图片描述

    子类对象中成员是有两部分组成的, 基类继承下来的 以及 子类新增加的 两部分

    在构造子类对象时候 ,先要调用基类的构造方法,将从基类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整

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


    7. super 和 this

    super 和 this 都可以在成员方法中用来访问:成员变量和调用其他的成员函数,都可以作为构造方法的第一条语句,那他们之间有什么区别呢?

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

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


    8. protected 访问限定符

    在类和对象章节中,为了实现封装特性, Java 中引入了访问限定符,主要限定: 或者 类中成员能否在类外 或者 其他包中被访问
    述

    // 为了掩饰基类中不同访问权限在子类中的可见性,为了简单类B中就不设置成员方法了
    
    // extend01 包中
    public class B {
    	private int a;
    	protected int b;
    	public int c;
    	int d;
    }
    
    // extend01 包中
    // 同一个包中的子类
    public class D extends B {
    	public void method() {
    	// super.a = 10; // 编译报错,父类private成员在相同包子类中不可见
    	super.b = 20; // 父类中protected成员在相同包子类中可以直接访问
    	super.c = 30; // 父类中public成员在相同包子类中可以直接访问
    	super.d = 40; // 父类中默认访问权限修饰的成员在相同包子类中可以直接访问
    	}
    }
    
    // extend02包中
    // 不同包中的子类
    public class C extends B  {
    	public void method() {
    	// super.a = 10; // 编译报错,父类中private成员在不同包子类中不可见
    	super.b = 20; // 父类中protected修饰的成员在不同包子类中可以直接访问
    	super.c = 30; // 父类中public修饰的成员在不同包子类中可以直接访问
    	//super.d = 40; // 父类中默认访问权限修饰的成员在不同包子类中不能直接访问
    	}
    }
    
    // extend02包中
    // 不同包中的类
    public class TestC {
    	public static void main(String[] args) {
    	C c = new C();
    	c.method();
    	// System.out.println(c.a); // 编译报错,父类中private成员在不同包其他类中不可见
    	// System.out.println(c.b); // 父类中protected成员在不同包其他类中不能直接访问
    	System.out.println(c.c); // 父类中public成员在不同包其他类中可以直接访问
    	// System.out.println(c.d); // 父类中默认访问权限修饰的成员在不同包其他类中不能直接访问
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    注意:父类中private成员变量虽然在子类中不能直接访问,但是也继承到子类中了

    什么时候下用哪一种呢?
    我们希望类要尽量做到 “封装”,即隐藏内部实现细节,只暴露出必要的信息给类的调用者
    因此我们在使用的时候应该尽可能的使用比较严格的访问权限,例如如果一个方法能用 private,就尽量不要用 public


    三、多态

    1.什么是多态

    多态的概念:通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态

    听不懂,说人话:
    举例1:打印机,都是具备打印功能,但是可以打印出黑白图片,和彩色图片
    举例2:吃,都是吃这个动作,人吃饭,狗吃 shit,猫吃 fish

    耐心往下看,看完之后,回过头再看多态的概念,你会恍然大悟的


    2.多态实现的条件

    在java中要实现多态,必须要满足如下几个条件,缺一不可:
    必须在继承体系下
    子类必须要对父类中方法进行重写
    通过向上转型调用重写的方法

    重写是啥?向上转型是啥?往下看!


    3.重写

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

    比如你和你老婆吵架了,老婆让你写一份检讨书,你写完了给老婆大人过目
    老婆大人说:“ 不合格,重写!”
    那你重写的当然还是这份检讨书,开头还是“ 亲爱的老婆大人 ”,格式还是检讨书的格式,结尾还是“ 老婆别生气了~ ”,但内容重写了
    难不成你要重写成一篇《逍遥游》交给你老婆吗?

    在程序中,重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。

    class A {
        public void func() {
            System.out.println("在父类中打印");
        }
    }
    class B extends A {
        public void func() {
            System.out.println("在子类中打印");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    方法重写的规则:
    子类在重写父类的方法时,一般必须与父类方法原型一致: 返回值类型 方法名和参数列表要完全一致
    被重写的方法返回值类型可以不同,但是必须是具有父子关系的
    访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被 public 修饰,则子类中重写该方法就不能声明为 protected;父类被 static、private 修饰的方法、构造方法都不能被重写。
    重写的方法, 可以使用 @Override 注解来显式指定,有了这个注解能帮我们进行一些合法性校验,例如不小心将方法名字拼写错了 ( 比如写成 aet ),那么此时编译器就会发现父类中没有 aet 方法,就会编译报错,提示无法构成重写

    在这里插入图片描述

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

    例如:若干年前的手机,只能打电话,发短信,来电显示只能显示号码,而今天的手机在来电显示的时候,不仅仅可以显示号码,还可以显示头像,地区等

    在这个过程当中,我们不应该在原来的类上进行修改,因为原来的类,可能还在有用户使用,正确做法是:新建一个新手机的类,对来电显示这个方法重写就好了,这样就达到了我们当今的需求了。

    我们来看一段体现多态的代码:

    public class Animal {
    	String name;
    	int age;
    	public Animal(String name, int age) {
    		this.name = name;
    		this.age = age;
    	}
    	public void eat() {
    		System.out.println(name + "吃饭");
    	}
    }
    
    public class Cat extends Animal { 
    	public Cat(String name, int age) {
    		super(name, age);
    	}
    	@Override // 重写
    	public void eat() {
    		System.out.println(name+"吃鱼~~~");
    	}
    }
    
    public class Dog extends Animal {
    	public Dog(String name, int age) {
    		super(name, age);
    	}
    	@Override // 重写
    	public void eat() {
    		System.out.println(name+"吃骨头~~~");
    	}
    }
    
    // ---------------------分割线--------------------------------
    
    public class TestAnimal {
    	// 编译器在编译代码时,并不知道要调用Dog 还是 Cat 中eat的方法
    	// 等程序运行起来后,形参a引用的具体对象确定后,才知道调用那个方法
    	// 注意:此处的形参类型必须时父类类型才可以
    	public static void eat(Animal a) { // 这里发生了向上转型
    		a.eat();
    	}
    	public static void main(String[] args) {
    		Cat cat = new Cat("元宝",2);
    		Dog dog = new Dog("小七", 1);
    		eat(cat);
    		eat(dog);
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

    在上述代码中, 分割线上方的代码是 类的实现者 编写的, 分割线下方的代码是 类的调用者 编写的
    当类的调用者在编写 eat 这个方法的时候,参数类型为 Animal (父类), 此时在该方法内部并不知道,也不关注当前的 a 引用指向的是哪个类型( 哪个子类 )的实例,此时 a 这个引用调用 eat 方法可能会有多种不同的表现( 和 a 引用的实例
    相关 ),这种行为就称为 多态

    	public static void eat(Animal a) { // 这里发生了向上转型
    		a.eat();
    	}
    
    • 1
    • 2
    • 3

    这里可能有人就迷惑了,什么玩意,看不懂
    这就是接下来要讲的:向上转型


    4.向上转型

    实际就是创建一个子类对象,将其当成父类对象来使用

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

    	Animal animal = new Cat();
    
    • 1

    Animal 是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换

    上面这行代码可以理解为,我 new 了一只猫,这个猫当然是动物,或者我 new 一条狗,这个狗当然也是动物,这样就是向上转型

    当然,这是在实例化对象的时候发生的向上转型,也可以在方法传参以及返回值接收的时候发生向上转型
    意思就是,我实参是猫,形参是动物 这里也发生了向上转型(见上述代价),或者我方法的返回值返回的是 new Cat() ,接收返回值用Animal animal = 方法名 这里也是向上转型

    小伙伴们可以自己试一下~


    总结

    以上就是今天要讲的关于Java 面向对象的【三大特征】的内容,封装,继承,多态,相关的知识尤为重要,与君共勉

    如果本篇对你有帮助,请点赞收藏支持一下,小手一抖就是对作者莫大的鼓励啦🤪🤪🤪


    上山总比下山辛苦
    下篇文章见

  • 相关阅读:
    Python QT 之PySide6简单入门
    【分治算法】Hanoi塔问题Python实现
    Rust 构建 TCP/UDP 网络服务
    MySQL:日志系统介绍 | 错误日志 | 查询日志 | 二进制日志:bin-log数据恢复实践 | 慢日志查询
    04 # 手写 apply
    深度学习【fastText原理解析】
    mySQL—索引
    MobaXtem通过SSH远程登录ubuntu系统
    JVM后端编译与优化——编译器优化技术
    面试必问的8个CSS响应式单位,你知道几个?
  • 原文地址:https://blog.csdn.net/yzhcjl_/article/details/127883055