面向对象(OOP)的三个特性: 封装性(Encapsulation),继承性和多态性。
面向对象(OOP)的五个原则: 单一职责原则(SRP)、开放封闭原则(OCP)、里氏替换原则(LSP)、依赖倒置原则(DIP)、接口隔离原则(ISP)。
面向对象的三个特性: 封装性(Encapsulation),继承性和多态性。
1.1 封装性
封装性:将数据和对数据的操作封装起来,对象状态(属性)由这个对象自己的行为(方法)来读取和改变,隐藏细节部分。
Java 中 属性的封装,无特殊情况都是用的 private 修饰符, 用 private 修饰符声明该属性为私有属性,只允许类的内部直接访问该属性。对于私有属性,要给一对 setter() 和 getter() 方法操作,外部访问私有属性需要通过setter() 和 getter() 方法。 setter() 和 getter() 方法 IDE(idea、eclipse) 可快速生成 。
在类中可以使用 this 关键字访问本类的私有属性,this 关键字只能用于类的方法体中。如this.id,去访问本类的私有属性 id。
良好的封装能够减少耦合;保证属性的操作安全性。
访问权限修饰符还有如下几种:
访问修饰符 | 同一个类 | 同一个包 | 不同包中的子类 | 工程的任意位置 |
private | 可访问 | |||
缺省(default) | 可访问 | 可访问 | ||
protected | 可访问 | 可访问 | 可访问 | |
public | 可访问 | 可访问 | 可访问 | 可访问 |
1.2 继承性
继承性: 把多种有着共同特性的多的类事物抽象成一个类,这个类就是多个类的父类。父类的意义在于可以抽取多个类的共性,代码复用,减少代码量。
例:三个类,Pupil类(小学生),MiddleSchoolStudent类(中学生类),CollegeStudent类(大学生类),他们有一个特性,都要有名字,学号,班级等等属性,都要去上课,考试等行为。
我们可以写一个父类 为 student类(学生),在Student类中实现名字,学号,班级等属性,上课,考试等行为,Pupil类、 MiddleSchoolStudent类、CollegeStudent类 继承 Student 类之后,就不需要自己去实现这些方法,可以直接去访问父类的。上课这个方法你本来要写三遍,继承之后你只用实现一次, 就实现了代码复用,减少代码量。
Java 只能单继承 ,即一个类只能有一个父类(A继承B之后,不能在继承其他的类);可以多层继承(A继承B,B继承C);多个类继承一个类(B和C都继承A)。
子类继承的关键字:extends。 A 类 extends B 类,A是B的子类,B是A的父类。
子类可以访问父类的 public 和 protected成员,四种访问属性在和子类中的含义如下。
父类成员访问属性 | 在父类中的含义 | 在子类中的含义 |
public | 对所有人开放 | 对所有人开放 |
protected | 只有包内其它类、自己和子类可以访问 | 只有包内其它类、自己和子类可以访问 |
缺省(default) | 只有包内其它类可以访问 | 如果子类与父类在同一个包内:只有包内其它类可以访问 否则:相当于private,不能访问 |
private | 只有自己可以访问 | 不能访问 |
继承的缺点:提高了类之间的耦合性。
1.3 多态性
多态性: 程序中定义的引用变量 “所指向的具体类型” 和 “通过该引用变量发出的方法调用” 在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。
静态多态(编译时多态):方法的重载实现 。重载:方法名相同,方法参数个数和类型不同
动态多态(运行时多态): 子类中对父类方法的重写、接口、抽象类和抽象方法。
接下来举几个动态多态的例子。
1.3.1 子类中对父类方法的重写
父类为 Student , 子类为 pupil 。
Student.java :
package duotai;
public class Student {
public void play() {
System.out.println("play");
}
}
pupil.java :
package duotai;
public class Pupil extends Student{
public void play() {
System.out.println("唱儿歌");
}
public static void main(String args[]) {
Student student = new Student();
Pupil pupil = new Pupil();
Student student1 = new Pupil();
student.play();
pupil.play();
student1.play();
}
}
执行 pupil.java 中的 main 方法,运行截图如下:
1.3.2 接口实现多态
List 接口的两个实现类,ArrayList,LinkedList。
public class ListStudy {
public static void main(String args[]) {
List list = new ArrayList<>();
List list1 = new LinkedList<>();
}
}
2.1 单一职责原则(SRP)
一个类应该只有一项工作。
比如在职员类里,将工程师、销售人员、销售经理这些情况都放在职员类里考虑,其结果将会非常混乱,在这个假设下,职员类里的每个方法都要if else判断是哪种情况,从类结构上来说将会十分臃肿,并且上述三种的职员类型,不论哪一种发生需求变化,都会改变职员类!这个是大家所不愿意看到的!
2.2 开放封闭原则(OCP)
对象或实体应该对扩展开放,对修改封闭。
更改封闭即是在我们对模块进行扩展时,勿需对源有程序代码和DLL进行修改或重新编译文件!这个原则对我们在设计类的时候很有帮助,坚持这个原则就必须尽量考虑接口封装,抽象机制和多态技术!
2.3 里氏替换原则(LSP)
子类可以替换父类并且出现在父类能够出现的任何地方,这个原则也是在贯彻GOF倡导的面向接口编程!
在这个原则中父类应该尽量使用抽象类和接口。
2.4 依赖倒置原则(DIP)
高层次的模块不应该依赖于低层次的模块,他们都应该依赖于抽象。具体实现应该依赖于抽象,而不是抽象依赖于实现。
可以这样理解,上面我举例子的时候先说了兔子和绵羊,然后才推出食草动物。但如果我们继续认识了牛、马等食草动物,我们会发现我们需要不断调整食草动物的描述,这样程序会变得僵化,所以我们不应该让子类依赖于实体,不应该让父类模块依赖于子类模块。所以我们需要将食草动物设计为抽象类,即抽象类或接口。这样下层只需要实现相应的细节而不会影响父类。
2.5 接口隔离原则(ISP)
不应强迫客户端实现一个它用不上的接口,或是说客户端不应该被迫依赖它们不使用的方法,使用多个专门的接口比使用单个接口要好的多!
比如,为了减少接口的定义,将许多类似的方法都放在一个接口中,最后会发现,维护和实现接口的时候花了太多精力,而接口所定义的操作相当于对客户端的一种承诺,这种承诺当然是越少越好,越精练越好,过多的承诺带来的就是你的大量精力和时间去维护!