面向对象的概念
万物皆对象。对象的实质是属性和行为。
类是具有共同属性和行为的对象的集合。类定义了对象的属性和方法。
面向对象编程以对象和类为主体,基本组成要素是类和对象。
类
是具有相同特征的对象的抽象,是一种数据类型。
基本结构
- 属性:对象数据的描述
- 方法:对象的行为
- 构造方法:用于实例化对象
- 内部类:在类中声明的类(inner class)
- 块:分静态块与实例块
- 类的声明:private,default final,abstract,synchronized class 类名 { 主体 }
对象
是客观实体的映射,在程序设计上对象是类的具体示例
面向对象三大要素
封装
指利用抽象数据类型把数据和操作封装在一起,用户只能看到对象的外部信息,对象的内部细节是隐藏的。
一方面封装表示对象的属性和方法是一个整体,是独立的,另一方面它表示对象的内部是隐藏的。
可以将青少年封装成一个类,有姓名、性别、年龄的属性,以及吃、睡的行为,如下:
class Teenagers {
private String name;
private String sex;
private String age;
public void eat() {
}
public void sleep() {
}
}
继承
反映了两个类之间的一种关系,当一个类拥有另一个类的所有属性和方法时称这个类继承了另一个类,这两个类具有继承的关系,是一种创建新类的机制.
学生也是青少年,所以可以继承青少年的属性及行为;另一方面,他有自己的属性和行为。如下,学生有作业的属性,学习的行为:
class Student extends Teenagers {
private Map homework;
public void study() {
}
}
多态
就是多种状态,即同一个实体具有多种形式。多态一般在程序运行的过程中表现出来,即同一种类型在不同条件下表现不同的结果。多态也称为动态绑定,一般在运行时刻才能确定方法的具体执行对象。
实现多态的方式:
- 方法重写
- 方法重载
- 父类引用指向子类对象
举例如下:
Teenagers tee = new Student();
以上就是父类的引用指向子类对象,指向了Teenager的子类对象Student。
在student中重写eat方法:
class Student extends Teenagers {
private Map homework;
@Override
public void eat() {
System.out.println("peach");
}
public void study() {
}
}
调用
tee.eat();
将会打印出peach。即通过方法重写的方式实现了多态。
面向对象的设计原则
六大基本原则:
- 单一职责原则(Single-Responsibility Principle)
- 开闭原则(Open-Closed Principle)
- 里氏替换原则(Liskov-Substitution Principle)
- 接口隔离原则(Interface-Segregation Principle)
- 依赖倒置原则(Dependency-Inversion Principle)
- 迪米特法则(Law of Demeter)
单一职责原则
- 基本概念:一个类只负责一个功能领域中的相应职责。就一个类而言,应该只有一个引起它变化的原因。
- 优点:
- 低耦合性,影响范围小
- 降低类的复杂度,职责分明,提高了可读性
- 变更引起的风险低,利于维护
举例:
对于一部手机有打电话,发短信,触摸控制等功能
定义接口
// 接口定义单一职责
interface Callable {
void call();
}
interface Touchable {
void touch();
}
interface Message {
void sendMessage();
}
// 接口实现单一职责
class StandardCall implements Callable {
@Override
public void call() {
System.out.println("我要打电话了~");
}
}
class StandardTouch implements Touchable {
@Override
public void touch() {
System.out.println("我要开始触摸操作了~");
}
}
class StandardMessage implements Message {
@Override
public void sendMessage() {
System.out.println("我要发短信了~");
}
}
(上述方式完全依照单一职责原则,但在实际应用中,多选择采用针对方法的单一职责原则)
产品应用
class Phone implements Callable, Touchable, Message {
// 无需重新定义需要有的行为,直接用已有的
Callable callable = new StandardCall();
Touchable touchable = new StandardTouch();
Message message = new StandardMessage();
@Override
public void call() {
callable.call();
}
@Override
public void touch() {
touchable.touch();
}
@Override
public void sendMessage() {
message.sendMessage();
}
}
public class Demo {
public static void main(String[] args) {
Phone phone = new Phone();
phone.call();
phone.touch();
phone.sendMessage();
}
}
此时若我们的手机从电阻触屏更换到了电容触屏技术,只需要换掉Touchable实现类中的行为方法体,别的地方都不需要修改
class StandardTouch implements Touchable {
@Override
public void touch() {
System.out.println("我是新的电容触摸,我要开始触摸操作了~");
}
}
开闭原则
- 概念:一个软件实体(软件实体可以指一个软件模块、一个由多个类组成的局部结构、一个独立的类甚至一个函数)应当对扩展开放,对修改关闭。即软件实体应尽量在不修改原有代码的情况下进行扩展。
- 优点:
- 提高系统的灵活性、可复用性和可维护性
同样以手机为例,我们的手机拥有自己的属性和行为,在线下进行疯狂的售卖中,此时,双十一来了,要展开一波促销活动,但促销活动是暂时的。
手机类
// 手机类
public class SamsungPhone {
private String model;
private String color;
private double price;
public SamsungPhone(String model, String color, double price) {
this.model = model;
this.color = color;
this.price = price;
}
public String getModel() {
return model;
}
public String getColor() {
return color;
}
public double getPrice() {
return price;
}
}
//售卖
public class Demo {
public static void main(String[] args) {
SamsungPhone samsungPhone = new SamsungPhone("Galaxy S8","green",200);
System.out.println(samsungPhone.getColor() + "的" +samsungPhone.getModel() + "售价为:" + samsungPhone.getPrice() + "元");
}
}
双十一来临,开闭原则拥抱变化,对扩展开放,对修改关闭
// 扩展促销手机类
public class SamsungSale extends SamsungPhone{
public SamsungSale(String model, String color, double price) {
super(model, color, price);
}
public double getPrice() {
return super.getPrice() * 0.8;
}
}
里氏替换原则
- 概念:强调的是设计和实现要依赖于抽象而非具体;子类只能去扩展基类,而不是隐藏或者覆盖基类。即设计不要破坏继承关系。
- 优点:
- 开闭原则的体现,约束继承泛滥
- 提高系统的健壮性、扩展性和兼容性
接口隔离原则
- 概念:接口中的方法应该尽量少,不要使接口过于臃肿;接口尽量细化。
- 优点:
- 高内聚、低耦合
- 可读性高、易于维护
案例:
一水果店的进货出货收银系统
违背接口隔离原则(将所有方法都集中在一个接口类中)
public interface FruitStoreInterface {
/**
* 计算价格
*/
public void method1();
/**
* 收银
*/
public void method2();
/**
* 点货
*/
public void method3();
/**
* 进货
*/
public void method4();
/**
* 出货
*/
public void method5();
}
根据接口隔离原则修改后
// 进货
public interface StockPurchaseInterface {
/**
* 点货
*/
public void method3();
/**
* 进货
*/
public void method4();
}
// 出货
public interface OutPurchaseInterface {
/**
* 出货
*/
public void method5();
}
// 收银
public interface CalMoneyInterface {
/**
* 计算价格
*/
public void method1();
/**
* 收银
*/
public void method2();
}
依赖倒置原则
- 概念:系统抽象化的具体实现,要求面向接口编程
- 优点:
- 减少类间的耦合性,提高系统的稳定性
- 降低并行开发引起的风险
- 提高代码的可读性和可维护性
案例:
学生读书
违背依赖倒置原则设计:
// 学生类
public class Student {
public void doSomething(YuwenBook book){
book.readBook();
}
}
//书本类语文
public class YuwenBook {
public void readBook(){
System.out.println("I am reading yuwen book!");
}
}
//调用
public class Demo {
public static void main(String[] args) {
Student student = new Student();
YuwenBook yuwenBook = new YuwenBook();
student.doSomething(yuwenBook);
}
}
若需求有变,我们不看语文书了,转看数学书,需要改动的地方就多了。依照依赖倒置原则修改如下:
//书本统一接口类
public interface Book {
public void readBook();
}
//学生类
public class Student {
public void doSomething(Book book){
book.readBook();
}
}
// 语文书
public class YuwenBook implements Book{
public void readBook(){
System.out.println("I am reading yuwen book!");
}
}
//数学书
public class ShuxueBook implements Book {
public void readBook() {
System.out.println("I am reading shuxue book");
}
}
//调用
public class Demo {
public static void main(String[] args) {
Student student = new Student();
//读语文书
YuwenBook yuwenBook = new YuwenBook();
student.doSomething(yuwenBook);
//读数学书
ShuxueBook shuxueBook = new ShuxueBook();
student.doSomething(shuxueBook);
}
}
迪米特法则
- 概念:一个对象应该对其他对象保持最少的了解;降低系统的耦合度,使一个模块的修改尽量少的影响其他模块,扩展会相对容易。
- 优点:
- 使软件有更好的可维护性和适应性
- 对象较少依赖其他对象的内壁结构,可以改变对象容器而不用改变他的调用者
案例:
朋友A问朋友B,朋友C在干嘛,此时B也不知道,于是,B打电话给C询问
违背迪米特法则
public class FriendA {
public String askB(FriendB b) {
b.call();
b.sayHello();
b.askWhere();
return b.askWhatDo();
}
}
public class FriendB {
public void call() {
}
public void sayHello() {
}
public void askWhere() {
}
public String askWhatDo() {
return "跳绳";
}
}
根据迪米特法则,A只想知道C在干嘛,而不用知道B是通过什么手段或者方式得知C的行为,修改后:
public class FriendA {
public String askB(FriendB b) {
return b.call();
}
}
public class FriendB {
public String call() {
sayHello();
askWhere();
return askWhatDo();
}
private void sayHello() {
}
private void askWhere() {
}
private String askWhatDo() {
return "跳绳";
}
}
结论:面向对象的设计原则都是为了拥抱变化而研究出来的一系列捷径