提示
本文主要讲解面向对象编程的核心三大特性:封装、继承和多态。@ermo
# 面向对象
面向对象编程(Object-Oriented Programming)OOP 要和面向过程编程(Procedure-Oriented Programming)POP 比较才能加强记忆。
早期的高级编程语言都是面向过程设计的,比如 C 语言。面向过程的核心是以事件编程,将一个个步骤编写成函数,然后通过控制代码进行顺序执行。
面向对象的程序则是一种以对象为中心的设计思想。将一个个事物设计成相应的对象,每个对象内含有自己的属性和行为,多个对象组成一个功能,从而解决实际问题。大幅度增加代码的可维护性,降低耦合。
# 面向对象的三大特性
(1)封装
封装(Encapsulation),将类的属性私有化(private 修饰符),通过该类提供的方法来访问私有信息。使用者无需关心该类的内部实现细节。
就像用户想吃饭就去找厨师(类),而不需要关心食材、厨具以及厨艺(隐藏的属性)。
- 隐藏内部实现细节,提高程序安全性
- 降低耦合:降低程序的修改成本,便于对属性的统一管理与测试
- 类内部结构可以自由修改
- 对成员进行精确的控制:通过方法访问成员提高代码灵活度,不只是简单的赋值操作
下文厨师的这个类中,倘若有用户要吃饭,只需要调用 cook()
方法,用户并不需要知道 age
和 sick
的具体细节。
public class Chef {
private String name;
private int age;
private boolean sick;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void cook() {
if (age >= 18 && age <= 65 && !sick) {
System.out.println("cooking quick ...");
} else {
System.out.println("cooking hard ...");
}
}
}
(2)继承
无论是在生活中还是社会中,都会有很多分类。比如电商网站的商品分类,电视机就会分为很多品牌。再比如说手机,也会有很多类型的手机。自然中的动物也是这样,羊、狗、猫等等,每一类动物都会有很多细分的品类。
计算机中经常使用继承(Inheritance)来表示上述生活中的品类。使用继承可以将一类事务的相同属性和行为放到父类中,子类只需要关注一些独有的特点即可。
计算机编程中的父子关系与现实中不同,在编程中,通过 is-a 关系进行判断继承关系是否恰当。
比如:
- 男人是人,正确
- 正方形是图形,正确
- 小狗是动物,正确
- 狗粮是狗,错误
在 Java 中类只支持单继承,不支持多继承。Java 接口支持多重实现。
public interface Children {
}
public interface Human {
}
public class Person {
public void eat() {
}
}
public class Student extends Person implements Human, Children {
@Override
public void eat() {
System.out.println("Student eat.");
}
}
(3)多态
多态(Polymorphism)即字面意思,多种形态。一个行为(方法)具有多种不同的表现形式,就是多态。
多态的好处是:
- 可扩展性,一个方法或接口可以让不同的类调用,如果要新增一种新的类型,无需修改原有代码
- 降低耦合,当前行为无需知道类的具体调用类型,只知道该类有这个方法即可
实现多态的三个条件:继承、重写、向上转型。
看一个例子,比如说一所学校购买了一批篮球供学生在体育课上使用。
那使用代码实现应该是这样的。
public class Ball {
public void play() {
System.out.println("Students play ball.");
}
}
public class Basketball extends Ball {
@Override
public void play() {
System.out.println("Students play basketball.");
}
}
public class Students {
public void playBall(Basketball basketball) {
System.out.println("Physical education class begin.");
System.out.println("Student start run.");
basketball.play();
System.out.println("Physical education class end.");
}
public static void main(String[] args) {
Students students = new Students();
Basketball basketball = new Basketball();
students.playBall(basketball);
}
}
篮球属于球类的一种,所以我们这里使用了继承,覆写了球类的 play
方法,毕竟每种球类的游戏规则不同。
目前学生类中的 playBall
方法也只支持 Basketball
的参数类型。
一段时间后,学校有钱了,为了丰富学生的体育活动,又采购了乒乓球和足球供学生使用。如果用代码表示应该是这样的。
public class Football extends Ball {
@Override
public void play() {
System.out.println("Students play football.");
}
}
public class TableTennis extends Ball {
@Override
public void play() {
System.out.println("Student play table tennis.");
}
}
public class Students {
public void playBall(Basketball basketball) {
System.out.println("Physical education class begin.");
System.out.println("Student start run.");
basketball.play();
System.out.println("Physical education class end.");
}
public void playBall(Football football) {
System.out.println("Physical education class begin.");
System.out.println("Student start run.");
football.play();
System.out.println("Physical education class end.");
}
public void playBall(TableTennis tableTennis) {
System.out.println("Physical education class begin.");
System.out.println("Student start run.");
tableTennis.play();
System.out.println("Physical education class end.");
}
public static void main(String[] args) {
Students students = new Students();
Basketball basketball = new Basketball();
students.playBall(basketball);
TableTennis tableTennis = new TableTennis();
students.playBall(tableTennis);
Football football = new Football();
students.playBall(football);
}
}
如上述代码,学生就拥有了三个 playBall
方法,对于方法名相同,参数列表不同,返回值相同的方法我们称之为 方法的重载。
方法的重载(overload)和方法的覆写(override)的区别是,重载是多个方法,覆写只是屏蔽了父类的行为,子类进行重新定义。
上面的代码显然违背了 DRY(Don't Repeat Yourself)原则,要优化这段代码,就需要使用到 Java 中多态的第二个必要条件:向上转型。
public class Students {
public void playBall(Ball ball) {
System.out.println("Physical education class begin.");
System.out.println("Student start run.");
ball.play();
System.out.println("Physical education class end.");
}
public static void main(String[] args) {
Students students = new Students();
Ball basketball = new Basketball();
students.playBall(basketball);
}
}
首先修改的是学生类的参数列表,对参数定义进行向上转型,playBall
方法并不需要关心入参的具体类型,只需要知道入参具有 play
的行为即可,一定程度上降低了代码的耦合度。
其次要修改的就是调用过程中的变量声明。将 Basketball basketball
修改为 Ball ball
,赋值进行实例化的过程中再创建具体的子类实现 new Basketball()
。
从上面代码可以看出,playBall
方法在声明的过程中并不知道入参的真实类型,这就是多态的特点:方法只有在运行时才能知道入参的真实类型。
使用多态也是对当前方法入参进行了抽象,真实业务场景中使用多态是提高代码质量的一种关键手段。
← jvm/jre/jdk的区别 装箱与拆箱 →