前言
大家好啊,我是汤圆,明天给大家带来的是《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 的默认办法和静态方法
- 等等等
后记
最初,感激大家的观看,谢谢