【Java】面向对象的三大特征:封装、继承、多态

封装

什么叫封装?

在我们写代码的时候经常会涉及两种角色: 类的实现者 类的调用者。
封装的本质就是让类的调用者不必太多的了解类的实现者是如何实现类的, 只要知道如何使用类就行了,这样就降低了类使用者的学习和使用成本, 从而 降低了复杂程度。
下面我们来介绍一下在我们Java里的几个访问权限修饰符:
  • private: 类内部能访问, 类外部不能访问
  • 默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问
  • protected: 类内部能访问, 子类和同一个包中的类可以访问, 其他类不能访问
  • public : 类内部和类的调用者都能访问

使用private实现封装

  • 被 public 修饰的成员变量或者成员方法, 可以直接被类的调用者使用。
  • 被 private 修饰的成员变量或者成员方法, 不能被类的调用者使用。

封装的本质是:类的使用者根本不需要知道, 也不需要关注一个类都有哪些 private 的成员。从而让类调用者以更低的成本来使用类。

代码示例:

class Student{
    private String name = "张三";
    private int age = 20;

    public void show(){
        System.out.println("姓名:"+name+" ,年龄:"+age);
    }
}
public class Demo1 {
    public static void main(String[] args) {
        Student student = new Student();
        student.show();
    }
}
  • 此时字段已经使用 private 来修饰,类的调用者(main方法中)不能直接使用, 而需要借助 show 方法,此时类的使用者就不必了解 Person 类的实现细节。
  • 同时如果类的实现者修改了字段的名字,类的调用者不需要做出任何修改(类的调用者根本访问不到 name, age这样的字段)

注意事项

  • private 不光能修饰字段, 也能修饰方法。
  • 通常情况下我们会把字段设为 private 属性, 但是方法是否需要设为 public, 就需要视具体情形而定. 一般我们希望一个类只提供 "必要的" public 方法, 而不应该是把所有的方法都无脑设为 public

继承

代码中创建的类, 主要是为了抽象现实中的一些事物(包含属性和方法)

有的时候客观事物之间就存在一些关联关系, 那么在表示成类和对象的时候也会存在一定的关联

这时我们就需要引入父类 , 基类 或 超类子类, 派生类,和现实中的儿子继承父亲的财产类似, 子类也会继承父类的字段和方法, 以达到代码重用的效果,从而降低代码的冗余性。

继承的基本语法:

class 子类 extends 父类 {
}

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

我们来用代码实现一个继承关系:

class Father{
    public String name;
    public int age;
    public char sex;

    public void eat(){
        System.out.println("吃饭");
    }
}

class Son extends Father{
    public void drink(){
        System.out.println("喝水");
    }
}

public class Demo1{
    public static void main(String[] args) {
        Son son = new Son();
        son.name = "张三";
        son.age = 20;
        son.sex = '男';
        son.eat();
        son.drink();
    }
}

运行结果:

 我们通过代码很容易看出来:当 Son 类继承了 Father 类后,就相当于把父类里的一些用 public 修饰过的属性和方法给拷贝过来了。

protected 关键字

在上述代码中我们发现, 如果把字段设为 private, 子类不能访问. 但是设成 public, 又违背了我们 "封装" 的初衷。所以我们就有个两全其美的办法:就是使用 protected 关键字。

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

Final关键字

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

final int a = 10;
a = 20;  // 编译出错

final 关键字也能修饰类, 此时表示被修饰的类就不能被继承

当我们用 final 修饰 Father 类后,再想用 Son 类继承 Father 类就发生报错了

组合

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

代码示例:

class Student {

}
class Teacher {

}
class School {
    public Student[] students;
    public Teacher[] teachers;
}

组合并没有涉及到特殊的语法(诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字段.这是我们设计类的一种常用方式之一.

多态

向上转型

代码示例:

class Father{
    
}
class Son extends Father{
    
}
public class Data {
    public static void main(String[] args) {
        Father f1 = new Father();
    }
}

向上转型发生的时机:

  • 直接赋值
  • 方法传参
  • 方法返回

直接赋值的方式我们已经演示了. 另外两种方式和直接赋值没有本质区别

方法传参

代码示例:

class Father{

}
class Son extends Father{

}
public class Data {
    public static void func(Father f1){
        
    }
    public static void main(String[] args) {
        func(new Son());
    }
}

方法返回

代码示例

class Father{

}
class Son extends Father{

}
public class Data {
    public static Father func(){
        Son son = new Son();
        return son;
    }
    public static void main(String[] args) {
        Father f1 = func();
    }
}

此时方法 func() 返回的是一个 Father 类型的引用, 但是实际上对应到 Son 的实例。

动态绑定

当子类和父类中出现同名方法的时候, 再去调用就会发生动态绑定。

class Father{
    public String name;
    public int age;
    public void eat(){
        System.out.println("正在吃东西");
    }
}
class Son extends Father{
    public void eat(){
        System.out.println("正在吃苹果");
    }
}
public class Data {
    public static void main(String[] args) {
        Father f = new Son();
        f.eat();
    }
}

此时, 我们发现:

  • f 虽然是 Father 类型的引用, 但是 f 指向 Son 类型的实例。
  • 在调用 eat() 方法的时候,本应该是调用 Father 类里面的方法,实际上调用的是 Son 类里面的方法

因此, 在 Java 中, 调用某个类的方法, 究竟执行了哪段代码 (是父类方法的代码还是子类方法的代码) , 要看究竟这个引用指向的是父类对象还是子类对象. 这个过程是程序运行时决定的(而不是编译期), 因此称为 动态绑定。

方法重写

针对刚才的 eat 方法来说:子类实现父类的同名方法, 并且参数的类型和个数完全相同, 这种情况称为 覆写/重写/覆盖。

关于重写的注意事项:

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

重载和重写的区别:

多态

有了面的向上转型, 动态绑定, 方法重写之后, 我们就可以使用 多态的形式来设计程序了.我们可以写一些只关注父类的代码, 就能够同时兼容各种子类的情况。

代码示例:打印各种形状

class Shape{
    public void draw(){
        System.out.println("打印一个图形");
    }
}
class Rectangle extends Shape{
    @Override
    public void draw() {
        System.out.println("矩形");
    }
}
class Circle extends Shape{
    @Override
    public void draw() {
        System.out.println("圆形");
    }
}
class Flower extends Shape{
    @Override
    public void draw() {
        System.out.println("❀");
    }
}

public class Data{
    public static void stamp(Shape shape){
        shape.draw();
    }
    public static void main(String[] args) {
        stamp(new Rectangle());
        stamp(new Circle());
        stamp(new Flower());
    }
}

运行结果:

多态顾名思义, 就是 "一个引用, 能表现出多种不同形态"

使用多态的好处是什么?

1) 类调用者对类的使用成本进一步降低

  • 封装是让类的调用者不需要知道类的实现细节.
  • 多态能让类的调用者连这个类的类型是什么都不必知道, 只需要知道这个对象具有某个方法即可

2) 能够降低代码的 "圈复杂度", 避免使用大量的 if - else

3) 可扩展能力更强

向下转型

向上转型是子类对象转成父类对象, 向下转型就是父类对象转成子类对象. 相比于向上转型来说, 向下转型没那么常见,但是也有一定的用途.

代码示例:

class Son extends Father{
    public void eat(){
        System.out.println(name + "正在吃东西");
    }

    public void haha(){
        System.out.println("haha");
    }
}

public class Data{
    public static void main(String[] args) {
        Father f2 = new Son();
        if(f2 instanceof Son){
            Son f3 = (Son)f2;
            f3.haha();
        }else{
            System.out.println("不能转型");
        }
    }
}

如果我们用普通的向上转型的话,是无法使用除了与父类同名的方法外的任何方法,但是我们使用向下转型就可以使用。只是这样一般不安全,所以我们要使用 instanceof 来判断是否正确的引用了子类。

super关键字

前面的代码中由于使用了重写机制, 调用到的是子类的方法. 如果需要在子类内部调用父类方法怎么办? 可以使用super 关键字。

代码示例:

class Father{
    public String name;
    public int age = 20;
    public void eat(){
        System.out.println("正在吃东西");
    }
}
class Son extends Father{
   public int age = 30;
   public void func(){
       System.out.println(super.age);
   }
}
public class Data {
    public static void main(String[] args) {
        Son son = new Son();
        son.func();
    }
}

我们可以看到在我们的子类和父类里都有属性 age ,当我们使用了 super 关键字后就可以访问父类的该属性。

运行结果:

super 和 this 的区别:

抽象类

语法规则:

abstract class Shape {
abstract public void draw();
}

  • 在 draw 方法前加上 abstract 关键字, 表示这是一个抽象方法. 同时抽象方法没有方法体(没有 { }, 不能执行具体代码).
  • 对于包含抽象方法的类, 必须加上 abstract 关键字表示这是一个抽象类. 

注意事项:

1) 抽象类不能直接实例化

Shape shape = new Shape();
// 编译出错
Error:(30, 23) java: Shape是抽象的; 无法实例化

2) 抽象方法不能是 private 的

abstract class Shape {
abstract private void draw();
}
// 编译出错
Error:(4, 27) java: 非法的修饰符组合: abstract和private 

3) 抽象类中可以包含其他的非抽象方法, 也可以包含字段. 这个非抽象方法和普通方法的规则都是一样的, 可以被重写,也可以被子类直接调用 

abstract class Shape {
abstract public void draw();
void func() {
System.out.println("func");
}
}
class Rect extends Shape {
...
}

抽象类的作用 :

  • 抽象类存在的最大意义就是为了被继承
  • 抽象类本身不能被实例化, 要想使用, 只能创建该抽象类的子类. 然后让子类重写抽象类中的抽象方法
  • 使用抽象类相当于多了一重编译器的校验

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/669219.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

民国漫画杂志《时代漫画》第39期.PDF

时代漫画39.PDF: https://url03.ctfile.com/f/1779803-1248636473-6bd732?p9586 (访问密码: 9586) 《时代漫画》的杂志在1934年诞生了,截止1937年6月战争来临被迫停刊共发行了39期。 ps: 资源来源网络!

问界新M7 Ultra仅售28.98万元起,上市即交付

5月31日,问界新M7 Ultra正式上市。发布会上,鸿蒙智行旗下多款产品交出最新答卷——问界新M5上市1个月大定突破2万台;智界S7位列30万纯电轿车4月交付量NO.3;问界M9上市5个月大定突破9万台。其中,作为中国高端豪华SUV市场…

【Linux】日志管理

一、日志进程 1、处理日志的进程 rsyslogd:系统专职日志程序 观察rsyslogd程序: ps aux | grep rsyslogd 2、常见的日志文件 1、系统主日志文件: /var/log/messages 动态查看日志文件尾部: tail -f /var/log/messages 2、安全…

车载软件架构 - AP AUTOSAR 设计思想及原理

车载软件架构 - AP AUTOSAR 设计思想及原理 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消…

SpringAdminClient如何将Httpbasic账号密码告知SpringAdminServer

场景,因为Config Service开了权限校验,注册到eureka之后,SpringAdmin查看信息会报错401,如果想在SpringAdmin中正确的看到Config Service的actuator信息则需要将账号密码告知给SpringAdmin,磁力用的是Eureka作为发现服…

泄漏libc基地址

拿libc基地址 方法一:格式化字符串 格式化字符串,首先确定输入的 AAAA 在栈上的位置(x)。使用 elf.got[fun] 获得got地址。利用格式化字符串,构造payload泄漏got地址处的值,recv接受到的字符串中&#xf…

力扣之链表专题

1. (LeetCode-21)合并两个有序链表 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。 示例 1: 输入:l1 [1,2,4], l2 [1,3,4] 输出:[1,1,2,3,4,4]示例 2: 输入:l1 …

AIGC绘画设计——midjourney有哪些好用的关键词?

midjourney有哪些高级关键词? 这一期继续分享一些高级的关键词, 我有一些案例也是从其他博主那学习来的, 但为了尽可能不出错,每个案例都是自己尝试了很多次后才拿出来的。 挑选了几个效果比较好,使用场景较高的类型…

牛客NC164 最长上升子序列(二)【困难 贪心+二分 Java/Go/PHP/C++】

题目 题目链接: https://www.nowcoder.com/practice/4af96fa010c44638a7e112abf65f7237 思路 贪心二分 所谓贪心,就是往死里贪,所以对于最大上升子序列,结尾元素越小,越有利于后面接上其他的数,也就可能变…

这里一定有你不知道的VS调试技巧

目录 使用环境:Visual Studio 2022,如无特殊说明,都是在Debug、x64环境下编译 一.什么是BUG 二.调试快捷键 F9:创建断电或取消断点 条件断点:满足这个条件才触发 F5:启动调试,经常⽤来直接跳到下⼀个断…

Windows通过cmd运行快速启动应用

Windows如何通过cmd运行快速启动应用? 在Windows操作系统中,可以通过配置环境变量的方式将文件的路径配置到环境变量的path中,配置完成后可以在cmd中输入对应的应用名称即可启动应用,具体操作如下: 1. 添加应用程序路径…

【机器学习300问】102、什么是混淆矩阵?

一、混淆矩阵的定义 混淆矩阵是一种用于评估分类模型性能的评估指标。当模型对数据进行预测并将数据分配到预定义的类别时,混淆矩阵提供了一种直观的方式来总结这些预测与数据实际类别之间的对应关系。具体来说,它是一个表格。 二、分类模型性能评估一级…

项目启动 | 宏昌电器牵手盘古信息,数字化制造引领企业高质量发展

随着时代的发展,数字化转型已成为实现企业持续增长和塑造竞争优势不可或缺的关键因素。浙江宏昌电器科技股份有限公司(以下简称为“宏昌电器”)围绕企业战略发展需求,积极加速数字化转型升级进程,以数字化力量推动公司…

VS Code 开发小技巧

VS Code的开发小技巧 添加代码片段 平时开发的时候,可以快速创建一个空白的模板。 一个快速生成代码片段的网站:https://snippet-generator.app/ 打开网站,把常用的模板代码复制进去,就会自动生成VS Code可以使用的代码片段了。…

【上海大学计算机组成原理实验报告】六、内存系统实验

一、实验目的 学习内存访问机制。理解代码和数据的分区存放原理和技术。 二、实验原理 根据实验指导书的相关内容,地址寄存器MAR用来存放要进行读或写的存储器EM的地址。其内容经数据总线DBUS写入,因此必须在数据总线上具有数据后,配合MAR允…

element-ui表格全选

项目场景&#xff1a; 根据项目需求&#xff0c;要求在表格外加【全选】复选框&#xff0c;切换分页也需将每一行都勾选上 实现方式&#xff1a; 借用element-ui文档的这几个方法和属性 <el-checkboxv-model"checkAll"change"handleCheckAllChange"&g…

【linux】宝塔,首页挂载磁盘,显示使用情况

挂载前&#xff1a; 挂载后&#xff1a; 数据无价&#xff0c;建议&#xff1a;备份需要挂载的磁盘&#xff0c;或者使用新磁盘来进行操作。 1、下载自动挂载磁盘的脚本&#xff1a; wget -O auto_disk.sh http://download.bt.cn/tools/auto_disk.sh 2、给脚本添加执行权限&a…

深入剖析Java线程池的核心概念与源码解析:从Executors、Executor、execute逐一揭秘

文章目录 文章导图前言Executors、Executor、execute对比剖析Executors生成的线程池&#xff1f;线程池中的 execute 方法execute 方法的作用execute的工作原理拒绝策略 源码分析工作原理基本知识线程的状态线程池的状态线程池状态和线程状态总结线程池的状态信息和线程数量信息…

LeetCode题练习与总结:路径总和Ⅱ--113

一、题目描述 给你二叉树的根节点 root 和一个整数目标和 targetSum &#xff0c;找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。 叶子节点 是指没有子节点的节点。 示例 1&#xff1a; 输入&#xff1a;root [5,4,8,11,null,13,4,7,2,null,null,5,1], target…

基于Springboot驾校预约平台小程序的设计与实现(源码+数据库+文档)

一.项目介绍 系统角色&#xff1a;管理员、教练、学员 小程序(仅限于学员注册、登录)&#xff1a; 查看管理员发布的公告信息 查看管理员发布的驾校信息 查看所有教练信息、预约(需教练审核)、评论、收藏喜欢的教练 查看管理员发布的考试信息、预约考试(需管理…