想学好一门开发语言,掌握了它的语言特性,学习起来往往能达到事半功倍的效果,自从接触到Java这个词,想必被灌输最多的就是Java是面向对象的编程的,要记住一点万物皆可对象,所有的特性都是基于对象来展开的,坐稳了,直接开车。
一. 抽象
生活中的任何事,物都能抽象成对象,把具有相同特征的事物抽象成一个对象。举个例子,玩过LOL,王者荣耀的都知道进入游戏要选英雄,每个人英雄有自己的血量,蓝量以及技能,这个属性(血量,蓝量)和特性(释放技能)就可以抽象成一个对象。你只需要把他们共同特征罗列出来,这样你就完成了对象类,此时你并不需要知道这个对象的具体细节,当我们要使用这个英雄对象的时候再从更高的层面去实现它的细节如下。
abstract class Hero {
int redValue;//血量
int blueValue;//蓝量
abstract void releasekills();//技能
}
把这个对象,赋予不同的血量,蓝量以及技能他就是代表不同的英雄,这样一说是不是很好理解了呀,记住万物皆可对象。在实际的开发工作中,如何将某一个业务抽象化还是挺重要的,这样便于我们更好的理解业务和开发。
二. 封装
顾名思义就是将具体的实现细节隐藏起来,使用此对象的用户并不清楚具体实现内容。还是上面例子,我们打游戏的时候,按下QWER释放了技能,此时我们就相当于调用了释放技能这个方法。但是这个技能怎么放出来,怎么实现的,我们并不知晓,只看到了释放技能的结果。那为什么用封装,封装的好处呢?
- 安全性,对用户隐藏具体细节;
- 降低程序的复杂程度,只需要调用封装的方法,不需要知道具体实现;
- 对于封装方法,我们只关心结果,不关心过程,这样更好的调试我们的程序。
-
三. 继承
抽象的时候,我们已经抽象出了一个英雄类,此时我们如果需要使用具体英雄,这时候就用到了继承了。子类继承父类,使用关键字 extends** 来实现,子类继承父类的方法和属性(PS:private修饰的方法和属性不能被继承),我们使用上面的抽象类Hero**来写个坦克英雄父类的列子,子类对于父类来说其实是个增量的关系,在父类的基础上还能定义其他的属性和方法,这样一来我们就能够很好重用父类代码。 ```java public class TankHero extends Hero{//坦克英雄 int redValue = 500;//血量 int blueValue = 300;//蓝量
@Override public void releasekills(){
System.out.println("每秒回复20生命值");
} public void increaseArmor(){
System.out.println("护甲值增加100");
} }
public class ApHero extends Hero{//法师英雄 int redValue = 400;//血量 int blueValue = 300;//蓝量 @Override public void releasekills(){ System.out.println(“法术强度增加100”); } }
> Java中为什么是单继承,多实现?
> 以我们上面的内容举例子。定义两个类ApHero(法师父类),TankHero(坦克父类),此时有个具体的英雄诺克萨斯统领(俗称乌鸦)即使法师,又是坦克,如果这个时候他同时继承这两个父类,而这两个父类又继承于抽象类Hero,他们都有释放技能的方法releasekills(),这时**乌鸦的方法releasekills()到底是继承ApHero中的,还是TankHero中的,所以这就是个问题**,Java才只能单继承,为了安全。当单继承无法满足我们的需求时,可以实现接口来满足,因为接口是可以多实现的,实现接口需要对方法进行重写,调用的时候还是调用实现类中的方法,所以重复没有关系。
<a name="rdGJY"></a>
# 四. 多态
多态就是父类引用指向子类对象,同一个操作作用于不同的子类对象产生不同的效果,比如每个英雄都可以释放技能,但是每个技能的形态又各不相同,这也是一种多态的表象形式。
<a name="ePD44"></a>
#### 1. 多态表现形式
```java
Hero hero = new TankHero();
Hero hero = new ApHero();
多态存在的三个必要条件:类继承或者接口实现、子类要重写父类的方法、父类的引用指向子类的对象
2. 编译时多态和运行时多态
- 编译时多态(静态绑定)
方法重载都是编译时多态。根据实际参数的数据类型、个数和顺序,Java在编译时能够确定执行重载方法中的哪一个。
方法重写的静态绑定,当前对象的引用指向当前对象时为编译时多态。
public static void main(String[] args){
ApHero apHero = new ApHero();
TankHero tankHero = new TankHero();
//方法重载的静态绑定
gainEffect(apHero);
gainEffect(tankHero);
System.out.println("-----------");
//方法重写的静态绑定(重写了Hero的releasekills方法)
apHero.releasekills();
tankHero.releasekills();
}
public static void gainEffect(ApHero apHero){
System.out.println("法师获得蓝BUFF");
}
public static void gainEffect(TankHero tankHero){
System.out.println("坦克获得红BUFF");
}
/**运行结果:
* 法师获得蓝BUFF
* 坦克获得红BUFF
* -----------
* 法术强度增加100
* 每秒回复20生命值
*/
- 运行时多态(动态绑定)
方法重写,父类引用指向子类对象时,此时是运行时多态。
public class test {
public static void main(String[] args){
ApHero apHero = new Syndra();
apHero.releasekills();
System.out.println("辛德拉的蓝量:"+apHero.blueValue);
}
/**运行结果:
* 释放E技能弱者退散
* 辛德拉的蓝量:300
*/
}
public class ApHero extends Hero{//法师英雄
int redValue = 400;//血量
int blueValue = 300;//蓝量
@Override
public void releasekills(){
System.out.println("法术强度增加100");
}
}
public class Syndra extends ApHero{//辛德拉
int redValue = 400;//血量
int blueValue = 500;//蓝量
@Override
public void releasekills(){
System.out.println("释放E技能弱者退散");
}
public void releaseFlash(){
System.out.println("大意了啊,没有闪");
}
}
释放技能是调用重写后的方法,释放辛德拉的技能,但是有没有注意到,输出的辛德拉蓝量居然是300,是父类的蓝量。为什么呢?原来属性的值取父类还是子类并不取决于我们创建对象的类型,而是取决于我们定义的变量的类型**。**还要注意父类引用是无法调用子类对象独有的方法,上面的例子中,apHero引用无法调用辛德拉的releaseFlash()方法,这是子类独有的,父类引用无法调用。
3. 使用多态的优缺点
- 优点:
易替换,扩展性高,灵活性高,降低类型的耦合度
- 缺点:
不能使用子类的特有功能
**