前言

大家好啊,我是汤圆,今天给大家带来的是《Java中的三大特性 - 超详细篇》,希望对大家有帮助,谢谢

这一节的内容可能有点多,大家可以选择性的来看

简介

Java的三大特性:封装、继承、多态

乍一听,好像很高大上,其实当你真正用的时候,会发现高大上的还在后面呢。。。

热身

在正式讲解三大特性之前,先普及几个知识

1. 访问权限修饰符

Java中关于访问权限的四个修饰符,表格如下

private friendly(默认) protected public
当前类访问权限
包访问权限 ×
子类访问权限 × ×
其他类访问权限 × × ×

其中比较尴尬的是protected修饰符,有点卡在中间,不上不下的感觉

因为它不适合用来修饰属性

假设用它修饰属性,那么任何一个人都可以通过继承这个类,来直接访问到这个类的属性,从而破坏"封装性"

2. 抽象类(abstract)

什么是抽象类?

抽象类就是用abstract修饰,且不能被直接初始化的类,但是可以通过子类来初始化
比如:Father father = new Son()

对应的,抽象方法就是用abstract修饰的方法

抽象方法是一种很特殊的方法,它没有方法体,即方法实现代码为空,比如abstract public void fun();

抽象方法一般在子类中进行实现,它就好像是在说:我不写代码,我只是声明一个方法名,剩下的交给我的子孙后代(继承类)去做

抽象类有一个很重要的特点:抽象类可以没有抽象方法,但是如果一个类有抽象方法,那么这个类肯定是抽象类

为什么会有抽象类

解耦,使代码结构更加清晰

因为抽象类不能被直接创建为对象,它只是作为一个通用接口来供别人实现和调用,所以这样就使得抽象的代码更加清晰(它只声明方法,不实现方法)
就好比,老板和员工,老板负责分发任务,员工负责去具体的实现任务

好了,关于抽象类,先介绍到这里,更详细的后面的章节再深入

3. 重载(overloading)和覆写(overwriting)

重载和覆写是两个很容易混淆的概念

重载:同一个类中,一个方法的多种表现形式(参数类型不同,参数个数不同)

覆写:继承设计中,子类覆盖父类的方法(也可以叫做重写,不过这样跟重载有点混淆,所以个人喜欢叫做覆写)

他们之间的区别如下

重载 覆写
访问权限 可以不同 可以不同(但是子类的可见性不能比父类的低)
方法返回值 可以不同 相同
参数类型 不同(充分条件) 相同
参数个数 不同(充分条件) 相同

这里要注意几点

  1. 覆写时,子类的方法访问权限不能低于父类,比如父类方法为public,那么子类也只能为public
  2. 重载时,访问权限和方法返回值,不能作为用来判断一个方法是否为重载的依据;只能说重载允许不同的访问权限和返回值

覆写示范

代码示范如下,

// 覆写一:正确示范
@Override
public void fun(){System.out.println("son fun");
}
// 覆写二:错误示范,访问权限低了
@Override
private void fun(){// 报错:'fun()' in 'SonDemo' clashes with 'fun()' in 'Father'; attempting to assign weaker access privileges ('private'); was 'public'System.out.println("son fun");
}

@Override这个是干嘛的?之前没见过啊

这个修饰符用来说明这个方法是覆写方法,不写也可以,系统会自己识别方法是不是覆写的

那为啥还要多此一举呢?用系统默认的识别机制不好吗?

要多此一举;不好;

因为加了注解,代码可读性更高,代码更加规范,别人看了代码后,立马就知道这个方法是覆写方法

重载示范

重载用图展示可能会更加清晰

图示解释:

  1. 参数类型和参数个数,只要满足其一,就可以说这个方法被重载了

  2. 访问权限和方法返回值用虚线框,是为了说明他们两个只是重载的一个附加表现形式(可有可无),不能作为重载的判断依据

下面用代码演示下

// 基础方法
public void fun1(int a){}
// 重载一:参数个数不同
public void fun1(){}
// 重载二:参数类型不同
public void fun1(float a){}
// 重载三:错误示范,仅仅用访问权限的不同来重载
private void fun1(int a){// 编译报错:'fun1(int)' is already defined
}
// 重载四:错误示范,仅仅用返回值的不同来重载
public int fun1(int a){// 编译报错:'fun1(int)' is already definedreturn 0;
}

下面进入正文,开始顺序介绍这三大特性

正文

1. 封装(Encapsulation)

就是把类的属性私有化(private修饰),再通过公有方法(public)进行访问和修改

为什么要封装呢?

  1. 追踪变化:可以在set方法中,编写代码来追踪属性的改变记录

    public void setName(String name) {System.out.println("名字即将被修改");System.out.println("旧名字:" + this.name);System.out.println("新名字:" + name);this.name = name;
    }
  2. 修改底层实现:在修改属性名时,不会影响外部接口对属性的访问

    比如:name属性改为firstName和lastName,name就可以在get方法中修改返回值为firstName+lastName,对外接口没变化

    // 修改前private String name;public String getName() {return name;}// 修改后private String firstName;private String lastName;// 方法名不用变,只是方法内容作了修改public String getName() {return firstName + lastName;}
    

  3. 校验数据:可以在set方法中,校验传来的数据是否符合属性值的设定范围,防止无效数据的乱入

    public void setAge(int age) throws Exception {if(age>1000 || age<0){throw new Exception("年龄不符合规范,0~1000");}this.age = age;
    }
    

2. 继承(Inheritance)

如果子类继承了父类,那么子类就可以复用父类的方法和属性,并且可以在此基础上新增方法和属性

这里要注意的一点是:Java是单继承语言,即每个类只能有一个父类

这里还要普及一个常识:如果一个类没有指定父类(即没有继承任何类),那么这个类默认继承Object类

为什么要用继承呢?

为了代码复用,减少重复工作

单继承不会太局限吗?为啥不用多继承?

因为多继承会导致"致命方块"问题(因为像扑克牌的方块符号)

  • 比如A同时继承B和C,然后B和C各自继承D
  • B和C各自覆写了D的fun方法
  • 那这时A该调用哪个类的fun方法呢

下面用图来说话

那为什么叫致命方块,而不是致命三角形呢?那个D类好像是多余的

不多余

这个D类其实就是上面讲到的抽象类的作用:将共有的部分fun()抽象出来(或者提供一个基础的实现),然后子类分别去实现各自的,这也是多态的一种体现(下面会将多态)

如果没有D类,那么B和C的fun()就会存在重复代码,这时你可能就想要搞一个父类出来了,这个父类就是D类

那要怎么判断继承类设计得好不好呢?

通过is-a关系来判断

is-a关系指的是一个是另一个的关系,男人是人(说得通),人是男人(一半说得通)

用is-a关系可以很好地体现你的继承类设计的好还是坏

  • 如果子类都可以说是一个父类,那么这个继承关系设计的就很好(男人是人,is-a关系)
  • 如果子类和父类只是包含或者引用的关系,那么这个继承关系就很糟糕(猫是猫笼,包含关系)

有没有什么办法可以阻止类的继承?就像private修饰符用来封装属性,其他人访问不到一样

有啊,final修饰符可以阻止类的继承

这里重点讲一下final修饰符

final可以用来修饰属性、方法、类,表示他们是常量,不可被修改的

final修饰属性:属性是常量,必须在定义时初始化,或者构造函数中初始化

final修饰方法:方法不能被覆写

final修饰类:类不能被继承

说到final,有必要提一下内联

内联指的是,如果一个方法内容很短,且没有被其他类覆写时,方法名会被直接替换为方法内容

比如:final getName()这个方法可以内联为name属性

再比如:getSum(){return a+b},会直接被内联为a+b

为什么会有内联这个东西呢?

因为这样可以提高效率(细节:CPU在处理方法调用的指令时,使用的分支转移会扰乱预取指令的策略,这个比较底层,这里先简单介绍,后面章节再深入)

那它有没有什么缺点呢?

有,如果一个方法内容过长,又误被当做内联处理,那么就会影响性能

比如你的代码多个地方都调用这个方法,那么你的代码就会膨胀变得很大,从而影响性能

那有没有办法可以解决呢?

有,虚拟机的即时编译技术

即时编译会进行判断,如果一个方法内容很长,且被多次调用,那么它会自动关闭内联机制,防止代码膨胀

3. 多态(Polymorphism)

字面理解,就是多种形态,在Java中,多态指的是,一个类可以有多种表现形态

多态主要是 用来创建可扩展的程序

像我们上面提到的继承就是属于多态的一种

还有一种就是接口(interface)

接口类一种是比抽象类更加抽象的类

因为抽象类起码还可以实现方法,但是接口类没得选,就只能定义方法,不能实现

不过从Java8开始,接口支持定义默认方法和静态方法

接口的默认方法(default修饰符)和静态方法(static修饰符),会包含方法内容,这样别人可以直接调用接口类的方法(后面章节再细讲)

这样你会发现接口变得很像抽象类了,不过接口支持多实现(即一个类可以同时实现多个类,但是一个类同时只能继承一个类)

这样一来,Java相当于间接地实现了多继承

下图说明继承和实现的区别:单继承,多实现

多态一般用在哪些场景呢?

场景很多,这里说两个最常用的

  • 场景一:方法的参数,即方法定义时,父类作为方法的形参,然后调用时传入子类作为方法的实参
  • 场景二:方法的返回值,即方法定义时,父类作为方法的返回值,然后在方法内部实际返回子类

代码示范如下:

public class PolyphorismDemo {public static void main(String[] args) {PolyphorismDemo demo = new PolyphorismDemo();//场景一:形参,将猫(子类)赋值给动物(父类)demo.fun(new Cat());//场景二:返回值,将猫赋值给动物Animal animal = demo.fun2();}public void fun(Animal animal){}public Animal fun2(){return new Cat();}
}class Animal{}class Cat extends Animal{}

总结

其中还有很多知识点没总结,太多了,看起来会不方便,所以其他的内容会陆续放到后面章节来讲

这里先简单列出来,比如:

  • equals和hashcode的关系
  • instanceof和getClass()的区别
  • 静态绑定和动态绑定
  • Java8的默认方法和静态方法
  • 等等等

后记

最后,感谢大家的观看,谢谢

Java中的三大特性 - 超详细篇相关推荐

  1. 夯实Java基础系列1:Java面向对象三大特性(基础篇)

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  2. Java并发知识总结,超详细!(上)

    Java并发知识总结,超详细! 首先给大家分享一个github仓库,上面放了 200多本经典的计算机书籍 ,包括C语言.C++.Java.Python.前端.数据库.操作系统.计算机网络.数据结构和算 ...

  3. java面试题答案大全超详细(持续更新)

    java面试题答案大全超详细 第01章 java语言面试题 项目经理(作者):张明星 JVM 运行时数据区是什么? 程序计数器是什么? 程序计数器(线程私有) Java 虚拟机栈的作用? 本地方法栈的 ...

  4. 转 :2019年Java大厂面试(吐血超详细总结)

    2019年Java大厂面试(吐血超详细总结) 本文来自于慕课网手记:Java大厂面试(吐血超详细总结),转载请保留链接 ;) 转载自:https://www.imooc.com/article/286 ...

  5. Java学习笔记二十五:Java面向对象的三大特性之多态

    Java面向对象的三大特性之多态 一:什么是多态: 多态是同一个行为具有多个不同表现形式或形态的能力. 多态就是同一个接口,使用不同的实例而执行不同操作. 多态性是对象多种表现形式的体现. 现实中,比 ...

  6. Java语言的三大特性

    Java语言的三大特性即是:封装.继承.多态 封装: 首先,属性可用来描述同一类事物的特征,方法可描述一类事物可做的操作.封装就是把属于同一类事物的共性(包括属性与方法)归到一个类中,以方便使用. 1 ...

  7. Java中的继承性特性

    继承性是java中的第二特性之一.而继承性最为关键的地方为:代码重用性的问题,利用继承性可以从已有的类中继续派生出新的子类,也可以利用子类扩展出更多的操作功能. 继承性的实现代码为:class 子类 ...

  8. JAVA中JVM的重排序详细介绍(写得很明白)

    刚刚在研究volatile变量的时候,涉及到重排序的概念,于是发现了这篇很好的文章,写得很简短很明白.所以转载一下. 原文地址:JAVA中JVM的重排序详细介绍 原文贴出来: 重排序通常是编译器或运行 ...

  9. Day55-每日一道Java面试题-Java 面向对象编程三大特性: 封装 继承 多态

    Java 面向对象编程三大特性: 封装 继承 多态 封装 封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问.但是如果一个类没有 ...

  10. win10环境下下载安装openpose(only cpu)并在pycharm中运行代码(超详细)

    win10环境下下载安装openpose(only cpu)并在pycharm中运行代码(超详细) (一)前言 (二)准备工作 (三)友情提醒 (四)详细安装步骤 1.新建文件夹 2.下载OpenPo ...

最新文章

  1. 递归的妙用—遍历子控件
  2. 从零开始一起学习SLAM | 理解图优化,一步步带你看懂g2o代码
  3. 技术面试,高效求职准则
  4. 大家有没有推荐不错开源的小程序商城?这几个不要错过
  5. coco数据集大小分类_MicroSoft COCO数据集
  6. 自定义键盘组件_一文读懂!iOS系统组件的设计规范全解
  7. 浪潮服务器5112面板灯_SA5112M4 – IPMI设置
  8. 陈玉林是哪里人_玉林仿古砖公司在哪里
  9. Python3爬虫入门之Urllib库的用法
  10. AngularJs ui-router 路由的简单介绍
  11. 生产环境nginx平滑升级
  12. postgresql9.5.9相关的日志文件介绍
  13. 虚幻浏览器插件 播放视频
  14. Android7工程模式,安卓手机的“工程模式”是什么?
  15. 服务器只识别2t硬盘,网吧用2008R2服务器系统不认2T以上单个硬盘?
  16. python链家数据分析统计服_Python数据分析实战-链家北京二手房价分析
  17. 基线_读心术基础(四) 什么是基线
  18. cad能整体比例缩小吗_cad怎么把原尺寸图缩小几倍
  19. random.seed(0)
  20. 蓝底寸照该如何在手机上修改照片底色

热门文章

  1. 基于SSM的小区物业管理系统(附源码+论文)
  2. Ext3.4.0中EditorGridPanel可以复制文本
  3. 创客匠人打造在线课堂,助力内容变现
  4. 基于java 网页的宠物店管理系统
  5. 最新传奇游戏公司网站模板源码+带手机端/易优CMS内核
  6. 张果老能是鸿蒙时期一蝙蝠,他是八仙之一,吹一口气纸驴就能活过来,真实身份却是一只蝙蝠精...
  7. echarts使用之坑 隐藏显示echarts变形 echarts官网访问不了
  8. 荣耀手机动态修改imei信息
  9. Linux将鼠标解放,DwellClick:让鼠标下岗 解放你的手指
  10. threejs给gltf模型贴图出现纹理错乱,贴图歪曲解决办法