面向对象三大特性
面向对象的三大特性,封装,继承,多态
朋友问我面向对象的三个特性,我脱口而出这三个名词,但是等到朋友继续问我具体什么是封装,什么是继承,什么是多态,我感觉自己的理解很含糊,不能用精确的语言去描述。希望自己可以用专业的语言去交流,用通俗的方式去理解。
封装
封装,就是隐藏对象类的属性和实现细节,构建一个不可分的整体,对外只保留必须的接口,控制外界对于类内部的操作权限。
我们每个人都是一个天然的封装对象,对外靠语言或者肢体动作进行交流,至于你脑海内部发生的无数次的思想变更,除了你个人之外没有其他人了解。
封装的优点是什么?
-
良好的封装能够减少耦合。
-
类内部的结构可以自由修改。
-
可以对成员变量进行更精确的控制。
-
隐藏实现细节。
简单来说就是类的内部只有类自己知道,类的外部只能通过提供的接口进行访问,这些优点和设计原则中的开闭原则和迪米特法则又有千丝万缕的关联。
封装如何实现?
一个类如何封装其实并没有必然的规定,主要依靠每个人对于这个类的理解进行封装。类的属性一般情况下用private修饰,以隐藏内部的实现细节。
public class Person {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name,Integer age){
this.name = name;
this.age = age;
}
public boolean isYoung() {
return this.age < 30;
}
}
public class Test {
public static void main(String[] args) {
Person zhangSan = new Person("zhangSan",18);
Person liSi = new Person("liSi",32);
System.out.println(zhangSan.getName());
System.out.println(zhangSan.isYoung());
System.out.println(liSi.getName());
System.out.println(liSi.isYoung());
}
}
### 输出结果
zhangSan
true
liSi
false
继承
继承,就是子继承了父类的一些特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
继承是设计中一个很重要的思路,抽象出相同的特征和方法可以有效减少代码的冗余,提高代码质量。
继承的特点是什么?
-
子类拥有父类public修饰的属性、方法(protected修饰需要一个包下)。
-
子类可以拥有自己的属性和方法,即子类可以对父类进行扩展,子类独有属性和方法父类不可用。
-
子类可以用自己的方式实现父类的方法,也就是可以重写。
-
Java是单继承,即一个类只能继承一个父类。
但是我们常说,多用组合,少用继承,那么继承存在什么问题呢?
继承提高了类之间的耦合性,使得类之间的耦合度加深,代码之间的联系变得紧密,代码独立性变差,破坏了封装性。当然,这个缺点在实际开发中不应该被过分顾虑。
继承如何实现?
继承用extends关键字实现。
public class Person {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public boolean isYoung() {
return this.age < 30;
}
public void eat() {
System.out.println(this.name + "在吃东西");
}
}
public class Man extends Person {
private String sex;
public Man(String name, Integer age, String sex) {
super(name, age);
this.sex = sex;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public void eat() {
System.out.println(this.getName() + "的性别是" + this.getSex() + ",正在吃东西");
}
}
public class Test {
public static void main(String[] args) {
Person zhangSan = new Person("zhangSan", 30);
System.out.println(zhangSan.getName() + " " + zhangSan.isYoung());
zhangSan.eat();
Man wangWu = new Man("wangWu", 25, "男");
System.out.println(wangWu.getName() + " " + wangWu.isYoung() + " " + wangWu.getSex());
wangWu.eat();
Person zhaoLiu = new Man("zhaoLiu", 3, "男"); //父类引用指向子类对象
System.out.println(zhaoLiu.getName() + " " + zhaoLiu.isYoung() );
Man zhaoLiuMan = (Man) zhaoLiu; //父类转换为子类对象
System.out.println(zhaoLiuMan.getName() + " " + zhaoLiuMan.isYoung() + " " + zhaoLiuMan.getSex());
zhaoLiuMan.eat();
}
}
###输出结果
zhangSan false
zhangSan在吃东西
wangWu true 男
wangWu的性别是男,正在吃东西
zhaoLiu true
zhaoLiu true 男
zhaoLiu的性别是男,正在吃东西
从例子看出,子类继承了父类的时候,需要重新实现构造函数,以加上新的属性。在新建对象的时候,可以将父类引用指向子类对象,而用子类构建的父类对象,可以强行转换为子类对象,如例子所示。这个特性在我们设计代码结构的时候经常用到,可以用父类作为入参。
在写例子的时候,发现这个例子的封装很差,正好可以作为一个反面教材。 性别明显是属于人的属性,而非男人的属性,因为如果再定义一个Woman的类,我们也需要一个性别属性,这无疑是失败的。而Man这个类中,sex的值永远都是"男"。 如果要定义Man这个类,应该找到Man这个类独有的属性。
接口继承
java中还有一个接口继承用来类似实现类的多继承,继承接口实现,采用关键字implements实现。
public interface Study {
public void readBook();
default public void writePaper() {
System.out.println("写文章");
}
}
public interface Work {
public void work();
public void makeMoney();
}
public class Women implements Study,Work {
@Override
public void work() {
}
@Override
public void makeMoney() {
}
@Override
public void readBook() {
}
}
继承的接口方法,如果接口没有default(JDK8之后接口可以有默认实现)方法,则必须实现其接口。
可以同时使用extends和implements,这样可以在实现类中定义用户的属性和行为。
public class Women extends Person implements Study,Work {
public Women(String name, Integer age) {
super(name, age);
}
@Override
public void work() {
}
@Override
public void makeMoney() {
}
@Override
public void readBook() {
}
}
多态
多态,简单来说就是一个行为具有多种表现形式,一个接口被多个对象以不同的方式实现。
举个例子来说,就是一个父亲生了三个孩子,但是在学习这件事上,表现各不相同,一个考100,一个考80,一个考60,这就是一种多态。
多态的三个必要条件
-
存在继承关系,有父类和子类
-
子类对父类的方法进行重写
-
父类的引用指向子类对象,也就是例子中的(Person zhaoLiu = new Man(“zhaoLiu”, 3, “男”);)
为什么需要这三个必要条件,我们简单聊下多态的目的。在一个存在继承关系的结构中,我们定义一个指向子类对象的父类引用,这个时候我们就可以同时处理父类对象和子类对象。当存在多个不同的继承子类时,在实现的过程中,可以以父类作为参数,根据需要选择不同子类的实现方式。因此这三个条件是必要条件,缺一不可。
面向对象只是一种编程的思路,并不是说面向过程的方式就无法进行开发,只是面向对象的结构更加清晰,面向对象的特性就是为设计结构更加优秀的代码。
多态的实现
public class Animal {
public void eat() {
System.out.println("吃东西");
}
}
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("吃鱼");
}
}
public class Dog extends Animal {
@Override
public void eat(){
System.out.println("吃骨头");
}
}
public class Test {
public static void main(String[] args) {
Animal animal = new Dog();
eat(animal);
}
private static void eat(Animal animal) {
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.eat();
} else if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.eat();
} else {
throw new RuntimeException();
}
}
}
###输出结果
吃骨头
通过简单的例子可以看出多态的妙用,将具有相同行为但是不同实现方式的对象统一用父类表示。在新增一个新的具有相同行为的对象时,可以非常容易,且对旧的结构不产生很大的影响。
多态降低了类之间的耦合性,且使得扩展性大大提供,用专业的语言描述其优点如下:
- 消除类型之间的耦合关系
- 可替换性
- 可扩充性
- 接口性
- 灵活性
- 简化性
总结
面向对象的三大特性可以说是从现实生活中对象这个概念中抽象而来,这三大特性对世界是一种让人惊艳的抽象,最早提出这个思想的大佬让人钦佩,其实也很好奇当初为什么定义面向对象只有这三大特性,而不是四个,或者两个,是这三大特性已经足够代表对象的所有特性了吗,还是在计算机的世界中已经足够了?我很喜欢计算机世界与现实世界之间的相似性,概念上的相似让人在做研发的时候感觉到自己是在做创造。