一、面向对象的编程
概念:面向对象即OOP(Object Oriented Programming)
以对象作为基本单元来构建系统,具有三大特征和五大原则,先呈上枯燥的概念。
1.三大特征
封装:隐藏对象属性和实现细节,仅对外提供公共访问方式,提高安全性。
继承:提高代码复用性,是实现多态的前提
多态:父接口定义的引用变量指向子类或具体实现类的实例对象,提高代码扩展性
权限类关键字及其适用范围
同一类中 | 同一包中 | 子类中 | 全局范围 | |
---|---|---|---|---|
private | √ | - | - | - |
default | √ | √ | - | - |
protected | √ | √ | √ | - |
public | √ | √ | √ | √ |
2.五大原则(了解即可)
单一职责原则SRP:类的功能要单一(Single Responsibility Principle)
开放封闭原则OCP:扩展开放,修改关闭(Open - Close Principle)
里式替换原则LSP:子类可以替换父类出现在父类能够出现的任何地方(the Liskov Substitution Principle)
依赖倒置原则DIP:高层次模块不应依赖低层次模块,他们都依赖于抽象。抽象不应依赖于具体实现,具体实现应依赖于抽象(theDependency Inversion Principle)
接口分离原则ISP:设计时采用多个与特定客户类有关的接口比一个通用接口要好(the Interface Segregation Principle)
二、封装
核心思想:隐藏细节,保护数据安全
通过 private(私有权限) 对对象的属性进行封装操作,只有在同一类中才能直接访问,不同类中需要创建一个对应的对象才能获取和修改该属性
// 代码呈现
public class Encapsulation {
// 成员属性私有化
private String name;
//提供 getter 和 setter 方法来访问
public String getName() {
// 获取
return name;
}
public void setName(String name) {
// 修改
this.name = name;
}
}
class Test {
public static void main(String[] args) {
Encapsulation test = new Encapsulation();
// 通过 setter 方法设置属性值
test.setName("封装");
// 通过 getter 方法获取值
System.out.println("测试" + test.getName());
}
}
三、继承
1.核心思想:解决代码冗余,提高代码复用性
2.继承关系:满足 is-a 关系(继承关系,a is-a b, b 就是 a 的父类),父类更通用,子类更具体。
但 Java 中不允许多重继承 (只能单继承) ,子类会继承父类的所有属性和方法,private 属性和方法虽然可以继承,但无法直接使用
(隐式继承)
相当于DNA的继承关系,显性基因和隐性基因
3.继承是向上转型的必要条件,是实现多态的基础。
// 将子类中重复部分提取为父类
public class Animal {
// "动物名"和其所需"食物"就是我们描述一个"动物吃什么"的重复部分
private String name;
private String food;
// 在此赋值
public Animal(String name,String food) {
this.name = name;
this.food = food;
}
// 无参构造
public void eat() {
System.out.println(name + "正在吃" + food);
}
}
// 子类继承父类,对父类进行扩展
public class Dog extends Animal {
// 继承动物类变成 狗 子类
public Dog(String name,String food) {
// 通过 super 关键字向上调用 Animal 类的无参构造
super(name,food);
}
}
public class Cat extends Animal {
// 继承动物类变成 猫 子类
public Dog(String name,String food) {
super(name,food);
}
}
public Static Test {
public static void main(String[] args) {
// 创建一个 猫子 对象
Animal cat = new Cat("三三","鱼");
// 调用该对象的 吃 的方法
cat.eat();
Animal dog = new Dog("二哈","香肠");
dog.eat();
}
}
四、多态
1.核心思想:提高代码可维护性和可扩展性
2.实现多态的三个必要条件:继承、重写、父类引用指向子类对象(向下转型)
3.多态的实现方法:重写、接口、抽象类和抽象方法
1.先描述一下转型
向上转型 | 向下转型 | |
---|---|---|
1 | 参数统一化,降低使用者使用难度 | 重新拾回子类中独有方法 |
2 | 子类对象转为父类,父类可以是接口 | 父类对象转为子类,先要向上转型才能发生 |
3 | 类名称 类引用 = new 该类对象(); | ------------------------------------ |
4 | Dog doge = new Dog(); // 最常见 | ------------------------------------ |
5 | 父类名称 父类引用 = new 子类对象 | 子类名称 子类引用 = (子类对象)父类引用; |
6 | Father f = new Son(); | Son s = (Son)f; |
7 | Animal dog = new Dog(); | Animal cat = new Cat(); // 先向上转型 Cat cat = (Cat)cat;// 再向下转型 |
8 | 方法接收一个类/子类,参数指定为相应父类 | 需要用到子类的扩展方法 |
注:毫无关系的两种类型不能作为方法重写的返回值,例如:父类中的 int 返回值,子类中的 boolean 返回值,不算方法重写。
值得注意的转型报错
Animal cat = new Cat;// 向上转型,相当于又创建一个猫子对象
Cat cat1 = (Cat)cat;// 向下转型,编译和运行不会出错,cat 指向的是子类对象
Animal cat = new Animal();// 直接指向父类,相当于创建了一个新的 Animal 对象
Cat cat2 = (Cat)cat;// 不安全的向下转型,转型报错"java.lang.ClassCastException:"
转型示例代码
public class Animal {
public void sleep() {
System.out.println("zzz...");
}
public static void main(String[] args) {
Animal cat = new Cat(); // 向上转型
Cat.sleep(); // Me zzz...
Animal dog = new Dog(); // 向上转型
dog.sleep(); // You zzz...
// dog.speak(); // error : Dog 类独有的说话方法,Animal 类中没有此方法
Dog doge = (Dog)dog; // 向下转型 才能获取狗子类的独有方法
doge.speak(); // 狗子说话了,看它说了什么(doge)
}
}
class Cat extends Animal {
@Override
public void sleep() {
System.out.print("Cat zzz...");
}
// public void speak() {
// System.out.print("I am cat");
// }
}
class Dog extends Animal {
@Override
public void sleep() {
System.out.print("Dog zzz...");
}
public void speak() {
System.out.print("You are Doge");
}
}
2.接下来是抽象类 (abstract)
抽象类是普通类的"超集",只是比普通类多了一些抽象方法(普通类有的抽象类全都有)
1.核心思想:让代码有更强的扩展性
2.特点:
(1) 抽象类不能实例化对象 (new);
(2) 如果一个类包含抽象方法,那么该类必须是抽象类,但抽象类不一定包括抽象方法;
(3) 任何子类都必须重写父类的抽象方法 (子类是普通类),一个子类只能继承一个抽象类 (单继承);
(4) 抽象方法不能与 final 关键字同时出现,因为抽象类方法必须被覆写,也不能被 static 修饰,构造方法也不能声明为抽象方法;
(5) 抽象类中的抽象方法只有方法声明,每方法体 ( {} );
注意:本地方法 native 没有方法体,不是抽象方法
public abstract class Sharp {
public abstract void print(); // 该 print 方法没有方法体,是空实现
}
抽象方法示例
abstract class A {
abstract void printA();
}
// B 是抽象类,可选择覆写父类抽象方法
abstract class B extends A {
abstract void printB();
//若 B 中已经覆写了 A 的抽象方法,则 C 中不需要覆写
@Override
void printA(){}
}
// C 是普通类,必须覆写 B 中的所有抽象方法
Class C extends B {
@Override
void printB() {}
}
3.接口(interface)
比抽象类更加纯粹的抽象概念
1.使用 interface 关键字定义,子类实现接口使用 implements , 接口之间存在继承关系,接口不能继承一个类
2.接口定义时,public | abstract | final | static 都可以省略
3.接口允许多实现,一个类具备多个能力,同时实现多个父接口
public C extends A implements IB,IC...{}
模拟接口应用
// 接口使用 interface 关键字定义,只有全局常量(1%接口)和抽象方法(99%都是抽象方法)
public interface USB {
// 插入
public abstract void plugIn();
// 工作
public abstract void work();
}
// 鼠标
public class Mouse implements USB {
@Override
public void plugIn() {
System.out.println("鼠标驱动安装中...");
}
@Override
public void work() {
System.out.println("鼠标正常工作...");
}
}
// 键盘
public class Keybroad implements USB {
@Override
public void plugIn() {
System.out.println("键盘驱动安装中...");
}
@Override
public void work() {
System.out.println("键盘开始正常工作...");
}
}
//启动
public class Computer {
public static void main(String[] args) {
Computer computer = new Computer();
Mouse mouse = new Mouse();
// 插入鼠标
computer.fun(mouse);
Keyboard keyboard = new Keyboard();
// 插入键盘
computer.fun(keyboard);
}
// 向上转型只是为了兼容所有 USB 类的对象
// fun(Mouse mouse) -> 这个接口只能插鼠标,键盘无法被识别,这是两个无关类
public void fun(USB usb) {
usb.plugIn();
usb.work();
}
}
接口和抽象类的区别:
1.抽象类是一个非常严格的 is-a 关系,子类和抽象类是一棵继承树上的关系
2.接口和实现子类更多是水平方向,表示混合关系,一个子类通常会具备多个能力或者行为