Java中的三大特性 - 超详细篇
前言
大家好啊,我是汤圆,今天给大家带来的是《Java中的三大特性 - 超详细篇》,希望对大家有帮助,谢谢
这一节的内容可能有点多,大家可以选择性的来看
简介
Java的三大特性:封装、继承、多态
乍一听,好像很高大上,其实当你真正用的时候,会发现高大上的还在后面呢。。。
热身
在正式讲解三大特性之前,先普及几个知识
1. 访问权限修饰符
Java中关于访问权限的四个修饰符,表格如下
private | friendly(默认) | protected | public | |
---|---|---|---|---|
当前类访问权限 | √ | √ | √ | √ |
包访问权限 | × | √ | √ | √ |
子类访问权限 | × | × | √ | √ |
其他类访问权限 | × | × | × | √ |
其中比较尴尬的是protected修饰符,有点卡在中间,不上不下的感觉
因为它不适合用来修饰属性
假设用它修饰属性,那么任何一个人都可以通过继承这个类,来直接访问到这个类的属性,从而破坏"封装性"
2. 抽象类(abstract)
什么是抽象类?
抽象类就是用abstract修饰,且不能被直接初始化的类,但是可以通过子类来初始化
比如:Father father = new Son()
对应的,抽象方法就是用abstract修饰的方法
抽象方法是一种很特殊的方法,它没有方法体,即方法实现代码为空,比如abstract public void fun();
抽象方法一般在子类中进行实现,它就好像是在说:我不写代码,我只是声明一个方法名,剩下的交给我的子孙后代(继承类)去做
抽象类有一个很重要的特点:抽象类可以没有抽象方法,但是如果一个类有抽象方法,那么这个类肯定是抽象类
为什么会有抽象类
解耦,使代码结构更加清晰
因为抽象类不能被直接创建为对象,它只是作为一个通用接口来供别人实现和调用,所以这样就使得抽象的代码更加清晰(它只声明方法,不实现方法)
就好比,老板和员工,老板负责分发任务,员工负责去具体的实现任务
好了,关于抽象类,先介绍到这里,更详细的后面的章节再深入
3. 重载(overloading)和覆写(overwriting)
重载和覆写是两个很容易混淆的概念
重载:同一个类中,一个方法的多种表现形式(参数类型不同,参数个数不同)
覆写:继承设计中,子类覆盖父类的方法(也可以叫做重写,不过这样跟重载有点混淆,所以个人喜欢叫做覆写)
他们之间的区别如下
重载 | 覆写 | |
---|---|---|
访问权限 | 可以不同 | 可以不同(但是子类的可见性不能比父类的低) |
方法返回值 | 可以不同 | 相同 |
参数类型 | 不同(充分条件) | 相同 |
参数个数 | 不同(充分条件) | 相同 |
这里要注意几点
- 覆写时,子类的方法访问权限不能低于父类,比如父类方法为public,那么子类也只能为public
- 重载时,访问权限和方法返回值,不能作为用来判断一个方法是否为重载的依据;只能说重载允许不同的访问权限和返回值
覆写示范
代码示范如下,
// 覆写一:正确示范
@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这个是干嘛的?之前没见过啊
这个修饰符用来说明这个方法是覆写方法,不写也可以,系统会自己识别方法是不是覆写的
那为啥还要多此一举呢?用系统默认的识别机制不好吗?
要多此一举;不好;
因为加了注解,代码可读性更高,代码更加规范,别人看了代码后,立马就知道这个方法是覆写方法
重载示范
重载用图展示可能会更加清晰
图示解释:
参数类型和参数个数,只要满足其一,就可以说这个方法被重载了
访问权限和方法返回值用虚线框,是为了说明他们两个只是重载的一个附加表现形式(可有可无),不能作为重载的判断依据
下面用代码演示下
// 基础方法
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 defined
return 0;
}
下面进入正文,开始顺序介绍这三大特性
正文
1. 封装(Encapsulation)
就是把类的属性私有化(private修饰),再通过公有方法(public)进行访问和修改
为什么要封装呢?
追踪变化:可以在set方法中,编写代码来追踪属性的改变记录
public void setName(String name) {
System.out.println("名字即将被修改");
System.out.println("旧名字:" + this.name);
System.out.println("新名字:" + name);
this.name = name;
}修改底层实现:在修改属性名时,不会影响外部接口对属性的访问
比如: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;
}
校验数据:可以在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中的三大特性 - 超详细篇的更多相关文章
- Java 中的三大特性
我们都知道 Java 中有三大特性,那就是继承 ,封装和多态 .那我今天我就来说说这几个特性 . 老样子 ,先问问自己为什么会存在这些特性 .首先说封装 ,封装就是使用权限修饰符来实现对属性的隐藏 , ...
- java中的三大特性
java的三大特性是封装.继承.多态: 继承是OOD(面向对象设计)为了更好的建模,编程的时候是OOP(面向对象编程)提高代码的复用性.这里有个注意点:一个类只有一个直接的父类,但不是只有一个父类. ...
- Java中面向对象三大特性之——多态
多态的概述: 多态是继封装.继承之后,面向对象的第三大特性. 生活中,比如跑的动作,小猫.小狗和大象,跑起来是不一样的.再比如飞的动作,昆虫.鸟类和飞机,飞起来也是不一样的.可见,同一行为,通过不同 ...
- Java中面向对象三大特性之继承
1. 继承的概述 继承就是子类继承父类的变量和方法,下面用代码解释一下: class Student {// 定义学生类 String name; int age; void study() { Sy ...
- Java中的三大特性:封装、继承、多态
封装: 概念:封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问,适当的封装可以让代码更容易理解与维护,也加强了代码的安全性. 原则:将属性隐藏起来,若需要访问某个属性,提供公共方法对 ...
- Java中面向对象三大特性之——继承
继承的概述 多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可. 现实生活中继承:子承父业,用来描述事物之间的关系 代码中继承:就是用 ...
- Java中面向对象三大特性之——封装
概述 面向对象编程语言是对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界无法直接操作和修改. 封装可以被认为是一个保护屏障,防止该类的代码和数据被其他类随意访问.要访问该类的数据,必须通 ...
- Java中的equals()和hashCode() - 超详细篇
前言 大家好啊,我是汤圆,今天给大家带来的是<Java中的equals()和hashCode() - 详细篇>,希望对大家有帮助,谢谢 文章纯属原创,个人总结难免有差错,如果有,麻烦在评论 ...
- 夯实Java基础系列1:Java面向对象三大特性(基础篇)
本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 [https://github.com/h2pl/Java-Tutorial](https: ...
随机推荐
- ESLint error level
ESLint error level https://eslint.org/docs/user-guide/getting-started#configuration .eslintrc { &quo ...
- 牛市下SPC新空投糖果来了
2021年元旦刚过没几天,比特币就开启了牛市的旅程,比特币涨至三万四千美元,率领众多币种暴涨,一股浓浓的牛市味道来了. 虽然从昨天开始,比特币带领着主流币进行了一波调整,但是只涨不跌的市场是 大家说比 ...
- BGV暴涨千倍,未来或将超越YFI领跑DeFi全场!
毫无疑问,YFI在2020年上半年以一己之力掀翻了DeFi市场的热潮.迄今为止,YFI的新鲜资讯从不缺席,最近也是频频登上各大知名媒体热搜.其币价远远超过比特币价格,也让资本市场注意到DeFi市场原来 ...
- [Python学习笔记]调试
编码占了编程工作量的90%,调试占了另外90%,这是一个流传着的笑话.调试在编程中占有很大的分量,即使专业的程序员也一直在制造缺陷. 抛出异常 抛出异常相当于是说:"停止运行这个函数中的代码 ...
- 使用hive增量更新
目录 1.增量更新 2.对第一种情况 2.1.准备工作 2.2.更新数据 3.对第二种情况 3.1.准备工作 3.2.方法1 3.3.方法2 参考文末文章,加上自己的理解. 1.增量更新 有一个 ba ...
- java中this和super的用法及区别
this 用法1:代表当前对象本身 用法2:方法形参和类成员变量重名,用this进行区别 class demo{ private int age = 10; public int getAge(int ...
- JS中indexOf的用法
String.IndexOf(Char, [startIndex], [count]):返回指定字符在原字符串中的第一个匹配项的索引.可指定字符开始检索位置和指定长度的字符,若没有找到该字符,则返回 ...
- CentOS7安装Maven3.6.3及Git2.8.3
安装Maven3.6.3 点击进入官网 1:下载 wget https://mirror.bit.edu.cn/apache/maven/maven-3/3.6.3/binaries/apache-m ...
- 箭头函数this的指向
箭头函数的this 什么是箭头函数,箭头函数是es6的新特性,其出现就是为了更好的表示(代替)回调函数 // 箭头函数 (arg1, arg2) => {} // 当箭头函数只有一个参数 arg ...
- 解决appium点击软键盘上的搜索按钮
在执行appium自动化测试的时候,需要点击软件盘上的搜索按钮. 具体操作步骤如下: 前提:需要事先安装搜狗输入法 1.唤醒软件盘,可以封装到一个类里,用到的时候随时调用. import os#调起s ...