OO六大设计原则最全分析总结篇,阿里、百度等大厂技术面试题汇总

return scope;

}

}

public class BookStore {

private final List mBookList = new ArrayList<>();

public BookStore() {

setBooks();

}

private void setBooks() {

mBookList.add(new NovelBook(“天龙八部”, 3200, “金庸”));

mBookList.add(new NovelBook(“巴黎圣母院”, 5600, “雨果”));

mBookList.add(new NovelBook(“悲惨世界”, 3500, “雨果”));

mBookList.add(new ComputerBook(“Think in Java”, 4300, “Bruce Eckel”, “编程语言”));

}

public void printSellingBooks() {

for (IBook iBook : mBookList) {

System.out.println( “书籍名称:” + iBook.getName() + “, 书籍售价:”

  • iBook.getPrice()/100.0 + “元, 书籍作者:” + iBook.getAuthor() );

}

}

}

运行程序,打印如下:

这个代码中我们可以看到ComputerBook必须实现来自IBook接口的三个方法约束,因为IComputerBook继承了IBook接口,也就是说IBook对我们新增加的ComputerBook类产生了约束力,还有我们的mBookList是定义成接口的持有而不是实现类,如果你改成List<NovelBook> mBookList = new ArrayList<>()你会发现根本无法往里面添加ComputerBook对象,再者我们的getScope()方法能不能直接添加到IBook接口上而不用新增ComputerBook子类呢,当然不能,因为IBook接口影响的子类NovelBook已经在销售运行中,如果动IBook那么所有NovelBook势必要受到影响。所以这就是所谓的通过抽象约束来实现开闭原则。

  • 封装变化 ,这个在单一职责中就提到过,封装变化包含两层含义:第一,将相同的变化封装到一个接口或抽象类当中,第二,将不同的变化封装到不同的接口或抽象类当中,不应该有两个不同的变化出现在同一个接口或抽象类当中。

开闭原则体现的OO特征:封装、继承、抽象。

开闭原则的好处:

  • 提高可复用性 , 因为我们是对修改关闭,也就是说可以工作的逻辑代码高度集中并且基本不会变的,这部分代码就可以拿来复用的,这也正是我们封装的目标之一。
  • 提高可维护性,遵循开闭原则的一个结果就是每次的修改都不会对之前的代码造成任何影响,那么维护人员也就无需关心之前的代码会出问题,只需要把精力放到本次的扩展修改的代码上。

3. 里氏替换原则(LSP)

定义:所有引用基类(父类)的地方必须能透明地使用其子类的对象。

里氏替换原则英文全称是Liskov Substitution Principle,简称LSP。

对定义简单的理解:凡是父类出现的地方子类就可以出现,而且替换为子类也不会产生任何错误和异常。使用者可能根本不需要关心使用的是父类还是子类,但是,反过来就不行,有子类出现的地方,父类不一定能适应。

举例

我们平时写一个接口或者抽象类,然后编码实现,调用的时候传入接口或者抽象类作为参数,其实这个时候我们已经应用了LSP原则了。下面的例子是经典的射击类游戏CS游戏当中涉及到场景,类图如下:

士兵使用枪来杀敌人,具体是使用什么枪要调用的时候才知道,而枪的职责是负责射击,不同的枪射击方法不一样,具体要在在枪的各个子类中去实现。相关代码实现:

public abstract class AbstractGun {

public abstract void shoot();

}

public class HandGun extends AbstractGun {

@Override

public void shoot() {

System.out.println(“手枪射击”);

}

}

public class Rifle extends AbstractGun {

@Override

public void shoot() {

System.out.println(“步枪射击”);

}

}

public class MachineGun extends AbstractGun {

@Override

public void shoot() {

System.out.println(“机枪扫射”);

}

}

public class Soldier {

//定义士兵的枪

private AbstractGun gun;

//给士兵一支枪

public void setGun(AbstractGun gun) {

this.gun = gun;

}

public void killEnemy() {

System.out.println(“士兵开始杀敌…”);

this.gun.shoot();

}

}

public class Client {

public static void main(String[] args) {

//创造一个士兵

Soldier soldier = new Soldier();

//给士兵一支步枪

soldier.setGun(new Rifle());

soldier.killEnemy();

//给士兵一支手枪

soldier.setGun(new HandGun());

soldier.killEnemy();

//给士兵一支机枪

soldier.setGun(new MachineGun());

soldier.killEnemy();

}

}

运行程序,打印如下:

注意,我们的Soldier类当中定义的gun是AbstractGun类型的,是一把抽象的枪,具体是一把什么样的枪在上战场前setGun才能确定,而在Client类当中我们给士兵一把步枪,士兵便使用步枪开始杀敌,士兵要使用手枪我们就set一个HandGun对象进去,士兵便使用手枪开始杀敌,士兵要使用机枪我们就set一个MachineGun对象进去,士兵便使用机枪开始杀敌。可以看到,在实现Soldier类的代码时我们根本就不需要关心士兵到底使用的是一把什么样的枪,只要是一把枪AbstractGun就行,在实际使用的场景当中它会被替换为一把真正的枪。

假如现在我们有一把玩具枪,把它添加到上面的设计中去,理所当然的,类图被修改如下:

因为玩具枪是不能射击杀人的所以我们不能实现shoot方法,代码修改如下:

public class ToyGun extends AbstractGun {

@Override

public void shoot() {

//玩具枪不能射击杀人,所以这个方法空实现

}

}

假如我们在Client中使用这把玩具枪,结果会如何呢

public class Client {

public static void main(String[] args) {

Soldier soldier = new Soldier();

soldier.setGun(new ToyGun());

soldier.killEnemy();

}

}

运行程序,打印结果:

可以看到我们的士兵在使用玩具枪开始杀敌了,但是玩具枪是不具备射击杀人功能的呀,所以士兵会干着急。那么怎么解决这个问题呢:

  • 在Soldier类的killEnemy方法中使用instanceof判断,如果是玩具枪就不用来杀敌。

if (this.gun instanceof ToyGun) {

//do nothing

} else {

System.out.println(“士兵开始杀敌…”);

this.gun.shoot();

}

这样可以解决问题,但是这样以后每增加一个类型的枪就可能要去修改Soldier类,很显然这违反了OCP原则。

  • ToyGun脱离继承,建立独立的类。类图如下:

在AbstractToy我们将声音和形状委托给AbstractGun处理,这样以后两个基类下面的子类扩展就互不影响了。

上面的例子体现了LSP原则的一个特点:子类必须完全实现父类的方法。如果子类不能完全实现父类的方法,那么最好不再继续使用继承关系,使用依赖、组合关系来代替继承。

前面提到,父类出现的地方可以使用子类代替,但是反过来就不行,子类出现的地方,父类未必能胜任,下面举例说明为什么反过来不行:

我们在步枪的下面添加两种型号的枪,AK47和AUG狙击步枪,类图设计如下:

其中AUG是Rifle的子类,而狙击手Snipper射击严重依赖于AUG狙击步枪。相关代码:

public class AUG extends Rifle {

public void zoomOut() {

System.out.println(“使用望远镜观察敌人…”);

}

@Override

public void shoot() {

System.out.println(“使用AUG射击…”);

}

}

public class Snipper {

private AUG aug;

public void setGun(AUG aug){

this.aug = aug;

}

public void killEnemy() {

this.aug.zoomOut();

this.aug.shoot();

}

}

public class Client {

public static void main(String[] args) {

Snipper snipper = new Snipper();

snipper.setGun(new AUG());

snipper.killEnemy();

}

}

运行程序,打印如下:

好,现在我们将使用子类AUG的地方替换为父类试试,修改Client代码如下:

public class Client {

public static void main(String[] args) {

Snipper snipper = new Snipper();

snipper.setGun((AUG) new Rifle());

snipper.killEnemy();

}

}

运行发现报java.lang.ClassCastException异常,即向下转型是不安全的,所以LSP原则反过来是不行的,子类出现的地方,父类未必可以出现。

里氏替换原则的两个约束条件:

1. 覆写或重载父类方法时输入参数可以被放大

覆写或重载时同名方法的输入参数,子类必须要比父类更加宽松而不能小于父类的参数范围,否则就会出现父类出现的地方子类不能代替,从而违背LSP原则。

public class Father {

public Collection doSomething(HashMap map) {

System.out.println(“父类被执行”);

return map.values();

}

}

public class Son extends Father {

public Collection doSomething(Map map) {

System.out.println(“子类被执行”);

return map.values();

}

}

2. 覆写或重载父类方法时输出结果可以被缩小

父类的一个方法返回值的类型为T,子类相同方法的返回类型为S,LSP原则要求S必须小于等于T,即要么S和T是同一类型,要么S是T的子类。

public class Father {

public List getList() {

System.out.println(“父类被执行”);

List list = new ArrayList<>();

list.add(“a”);

return list;

}

}

public class Son extends Father {

@Override

public ArrayList getList() {

System.out.println(“父类被执行”);

ArrayList list = new ArrayList<>();

list.add(“a”);

return list;

}

}

里氏替换原则体现的OO特征:继承、多态。

里氏替换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。

4. 依赖倒置原则(DIP)

定义:高层模块不应该依赖低层模块,两个都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

依赖倒置原则英文全称是Dependence Inversion Principle, 简称DIP。

高层模块和低层模块的理解: 每一个逻辑的实现都是由原子逻辑组成的,不可分割的原子逻辑就是低层模块,原子逻辑的再组装就是高层模块。

在Java中,抽象就是指接口或者抽象类,两者都是不能直接被实例化的;细节就是实现类,实现接口或者继承抽象类而产生的就是细节,也就是可以通过new产生的对象。高层模块就是调用端,低层模块就是具体实现类。

依赖倒置原则在Java中的体现就是:

  • 模块间通过抽象发生,实现类之间不发生直接依赖关系,其依赖关系是通过接口或者抽象类产生的。

  • 接口或抽象类不依赖于实现类。

  • 实现类依赖接口或抽象类。

一句话总结依赖倒置原则就是:面向接口编程,而不是面向实现编程

举例 NO.1

一个司机驾驶奔驰的例子,类图如下:

其中司机Driver有一个drive方法来驾驶奔驰车,奔驰汽车Benz有一个run方法来启动汽车。Driver类依赖于Benz类, 相关代码:

public class Benz {

public void run() {

System.out.println(“奔驰汽车开始运行…”);

}

}

public class Driver {

public void drive(Benz benz) {

benz.run();

}

}

public class Client {

public static void main(String[] args) {

Driver zhangSan = new Driver();

Benz benz = new Benz();

//张三开奔驰

zhangSan.drive(benz);

}

}

运行程序,我们创造的张三司机可以顺利的驾驶奔驰汽车了,如果现在张三司机不仅要开奔驰还要开宝马,怎么做呢,先生成宝马汽车:

public class BMW {

public void run() {

System.out.println(“宝马汽车开始运行…”);

}

}

现在BMW宝马汽车有了,但是张三却无法驾驶它,为啥,因为司机类Driver的drive方法耦合的是Benz类的对象参数,没法传递新生成的BMW对象,这时你可能会去Driver里添加一个可以专门传递BMW对象的方法,很多时候你都会这样去做,随着业务的增加,我们的Driver会不断的修改,这导致我们的系统不稳定且越来越难以维护,这就说明系统设计存在问题,Driver和Benz严重耦合导致新需求来时只能再去修改Driver类。而依赖倒置就是为了解决这类问题的,我们按照依赖倒置原则重新设计类图如下:

在该类图当中IDriver接口依赖ICar接口,drive方法传递ICar接口类型的参数。子类Driver实现IDriver接口,奔驰和宝马汽车的具体类则实现ICar接口。相关代码:

public interface IDriver {

//是司机就可以驾驶汽车

public void drive(ICar car);

}

public interface ICar {

//是汽车就可以跑

public void run();

}

public class Benz implements ICar {

@Override

public void run() {

System.out.println(“奔驰汽车开始运行…”);

}

}

public class BMW implements ICar {

@Override

public void run() {

System.out.println(“宝马汽车开始运行…”);

}

}

public class Driver implements IDriver {

@Override

public void drive(ICar car) {

car.run();

}

}

public class Client {

public static void main(String[] args) {

IDriver zhangSan = new Driver();

ICar benz = new Benz();

//张三开奔驰

zhangSan.drive(benz);

}

}

可以看到我们的张三司机可以开奔驰汽车,其中IDriver接口的drive方法现在不依赖于具体的Benz或者BMW的实现类,即“抽象不依赖于细节”,而我们的Driver实现类的drive方法也是依赖于ICar而不是具体的Benz或者BMW对象,即“细节依赖于抽象”。抽象的Driver依赖于抽象的Car, 司机开的是抽象的车,在构造接口的阶段我们根本都不需要关心司机将来要开的具体是什么样的鸟车,我们只需要知道司机会驾驶车就可以了。这样的做到的好处就是,假如现在司机要开宝马汽车,好,客户端代码就可以轻松的应对:

public class Client {

public static void main(String[] args) {

IDriver zhangSan = new Driver();

ICar bmw = new BMW();

//张三开宝马

zhangSan.drive(bmw);

}

}

这样宝马车就可以开动起来了,注意在Client代码当中司机和汽车对象变量的引用我们都是使用的接口类型IDriver和ICar, 而不是具体的Driver和BMW对象,在创建对象以后的操作都是完全针对接口类型进行的,这就是“高层模块不依赖于低层”。

举例 NO.2

在前面单一职责中提到的鸭子类设计的例子时候留下了一个坑,这里就利用依赖倒置原则把这个坑填上。在单一职责中把鸭子呱呱叫和飞行两个变化的行为单独提取到了两个接口当中,每个Duck实现类都需要去选择是否实现相应的接口,比较麻烦,现在我们为每一种接口建立一组实现子类。类图设计如下:

现在我们为鸭子类添加呱呱叫和飞行这两种行为的抽象依赖,完整的类图设计如下:

相关实现代码如下:

public interface FlyBehavior {

public void fly();

}

public class FlyWithWings implements FlyBehavior {

@Override

public void fly() {

System.out.println(“I’m Flying”);

}

}

public class FlyNoWay implements FlyBehavior {

@Override

public void fly() {

//do nothing

}

}

public interface QuackBehavior {

public void quack();

}

public class Quack implements QuackBehavior {

@Override

public void quack() {

System.out.println(“Quack”);

}

}

public class Squeak implements QuackBehavior {

@Override

public void quack() {

System.out.println(“Squeak”);

}

}

public class MuteQuack implements QuackBehavior {

@Override

public void quack() {

//do nothing

}

}

public abstract class Duck {

FlyBehavior flyBehavior;

QuackBehavior quackBehavior;

public abstract void display();

public void performFly() {

flyBehavior.fly();

}

public void performQuack() {

quackBehavior.quack();

}

public void setFlyBehavior(FlyBehavior fb) {

this.flyBehavior = fb;

}

public void setQuackBehavior(QuackBehavior qb) {

this.quackBehavior = qb;

}

public void swim() {

System.out.println(“All ducks float, event decoys!”);

}

}

public class MallardDuck extends Duck{

@Override

public void display() {

System.out.println(“I’m real Mallard duck”);

}

}

public class DuckClient {

public static void main(String[] args) {

Duck mallardDuck = new MallardDuck();

QuackBehavior quack = new Quack();

FlyBehavior flyWithWings = new FlyWithWings();

mallardDuck.setFlyBehavior(flyWithWings);

mallardDuck.setQuackBehavior(quack);

mallardDuck.performQuack();

mallardDuck.performFly();

}

}

运行结果:

按照依赖倒置原则,不管我们的抽象类Duck还是实现类MallardDuck都是对FlyBehavior和QuackBehavior进行了抽象的依赖。在更换鸭子行为的时候,我们的Duck实现类跟具体的行为实现完全解耦,Duck类只需要接收抽象的行为接口,任意实现接口的行为都可以快速的完成修改。

这个例子告诉我们一个OO法则:多用组合,少用继承。组合尽量持有对抽象的依赖,这样才能够解耦具体实现类。

依赖倒置中依赖传递的三种方式:

1. 构造函数依赖注入

public interface IDriver {

//是司机就可以驾驶汽车

public void drive();

}

public class Driver implements IDriver {

private ICar car;

public Driver(ICar car) {

this.car = car;

}

@Override

public void drive() {

car.run();

}

}

2. Setter依赖注入

public interface IDriver {

//设置车辆

public void setCar(ICar car);

//是司机就可以驾驶汽车

public void drive();

}

public class Driver implements IDriver {

private ICar car;

@Override

public void setCar(ICar car) {

this.car = car;

}

@Override

public void drive() {

car.run();

}

}

3. 接口依赖注入

public interface IDriver {

//是司机就可以驾驶汽车

public void drive(ICar car);

}

public class Driver implements IDriver{

@Override

public void drive(ICar car) {

car.run();

}

}

很显然,在三种依赖注入方式当中setter依赖注入方式应该是最灵活的,因为可以在任意阶段进行注入,也可以随时切换注入的对象类型,实现动态插拔替换,这正是策略模式的原型。

依赖倒置原则体现的OO特征:抽象、多态。

如何使用依赖倒置原则,遵循以下几个原则:

  • 每个类尽量有接口或者抽象类,接口和抽象类都属于抽象,有了抽象才能依赖倒置。

  • 变量的表面类型尽量是接口或者抽象类

  • 尽量不要覆写基类的方法, 子类尽量不要覆写抽象类已经实现的方法。

  • 开发阶段尽量不要从具体类派生新类,只是在开发阶段,因为在维护阶段是要不断扩展修改的。

依赖倒置原则是开闭原则的强化原则,满足依赖倒置原则也就做到了对扩展开放,对修改关闭,不能满足依赖倒置就很难做到开闭原则。这个原则也是6个设计原则当中最难以实现的原则,但是一旦你实现了依赖倒置就能够打破传统思维,摆脱对具体实现的耦合性依赖,做到以不变应万变。

依赖倒置原则固然好,但设计原则不是万能的,实际当中还是会依赖一些细节,所以不要为了遵循设计原则而去遵循设计原则。就像不要为了考试而去学习一些知识,为了考试而学习知识的结果往往就是应试教育的漩涡,这也是中国现阶段教育体制的严重问题。唯物辩证法告诉我们事物都是普遍联系的,而矛盾是对立统一的,个性和共性是对立统一的,可能性和现实性是对立统一的,现象和本质是对立统一的,相对和绝对是对立统一的,所以要具体问题具体分析,实践是检验真理的唯一标准。

5. 接口隔离原则(ISP)

定义:一个类对另一个类的依赖应该建立在最小的接口上,客户端不应该依赖它不需要的接口。

接口隔离原则英文全称Interface Segregation Principle,简称ISP。

这里接口的含义有两种:

  • 实例接口, 可以用new的实现类都属于实例接口。

  • 类接口,在java中就是interface声明的接口或者是抽象类。

对定义的理解:接口要做到细化单一,不用建立庞大臃肿的接口。接口中的方法尽量少。为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用。如果有一个接口里面包含了各种模块的方法,那么就需要进行细化拆分,但细化拆分并不是无限制的细化每一个方法就对应写一个接口,细化拆分的下限和底线是做到满足单一职责,如果都不满足单一职责了那么接口的细化就没有意义了。在为一个类提供定制服务的时候,只暴露那些客户需要的方法,隐藏那些客户不需要的方法,做到以最少的方法完成最多的事,提高内聚。

举例

现在设计一个接口描述人类的一些特性,比如人会吃饭、睡觉、步行、讲话、工作、思考等等,一个真实的人会具备所有的这些特性,而机器人只具备其中的某些特性,类图设计如下:

Human

可以看到我们在Human接口中的方法描述了人的基本特性,真实的人Man类实现Human接口并实现接口的全部方法,其它的假人Dummy、扫地机器人SweepRobot、双足机器人BipedRobot以及AI机器人AIRobot它们也实现了Human接口,机器人只具备某一些人类的特性,比如扫地机器人它只会扫地别的啥也不会干,AI机器人可能会讲笑话唱歌与人进行简短的对话,双足机器人会像人类一样直立行走,假人则只具备人类的形状,但是由于它们都实现了Human接口,所以Humman中的所有方法即便它们不需要也不得不去实现。这个就是违反接口隔离原则的例子,用代码来实现该设计,你会更容易发现问题:

public interface Human {

public void talk();

public void sleep();

public void walk();

public void eat();

public void work();

public void think();

public void display();

}

/** 真实的人 */

public class Man implements Human {

@Override

public void talk() {

System.out.println(“讲话”);

}

@Override

public void sleep() {

System.out.println(“睡觉”);

}

@Override

public void walk() {

System.out.println(“行走”);

}

@Override

public void eat() {

System.out.println(“吃饭”);

}

@Override

public void work() {

System.out.println(“工作”);

}

@Override

public void think() {

System.out.println(“思考”);

}

@Override

public void display() {

System.out.println(“拥有人类外形”);

}

}

/** 假人 */

public class Dummy implements Human {

@Override

public void display() {

System.out.println(“拥有人类外形”);

}

@Override

public void talk() {}

@Override

public void sleep() {}

@Override

public void walk() {}

@Override

public void eat() {}

@Override

public void work() {}

@Override

public void think() {}

}

/** 扫地机器人 */

public class SweepRobot implements Human {

@Override

public void display() {

System.out.println(“我是圆形的”);

}

@Override

public void work() {

System.out.println(“打扫卫生”);

}

@Override

public void talk() {}

@Override

public void sleep() {}

@Override

public void eat() {}

@Override

public void walk() {}

@Override

public void think() {}

}

/** 双足机器人 */

public class BipedRobot implements Human {

@Override

public void display() {

System.out.println(“拥有人类外形”);

}

@Override

public void walk() {

System.out.println(“双足直立行走”);

}

@Override

public void talk() {}

@Override

public void sleep() {}

@Override

public void eat() {}

@Override

public void work() {}

@Override

public void think() {}

}

/** AI机器人 */

public class AIRobot implements Human {

@Override

public void talk() {

System.out.println(“我能讲笑话、还能聊天哦”);

}

@Override

public void think() {

System.out.println(“我还能回答问题、下围棋”);

}

@Override

public void display() {

System.out.println(“我是虚拟程序,没有外形”);

}

@Override

public void walk() {}

@Override

public void sleep() {}

@Override

public void eat() {}

@Override

public void work() {}

}

以上就是实现代码,你会发现Dummy、SweepRobot、BipedRobot和AIRobot几个类只实现了Human接口的几个方法,其它方法都做了空实现,只有Man类实现了全部方法,也就是说对于机器人相关的实现类而言,接口方法是存在冗余的,所以按照接口隔离原则我们这种情况下就要对接口进行细化拆分,修改类图如下:

这里写图片描述

可以看到我们将接口进行了拆分,相关方法独立成为接口,每一个实现类只依赖跟它直接相关的接口。相关代码修改如下:

public interface Intelligence {

public void talk();

public void think();

}

public interface Work {

public void work();

}

public interface Display {

public void display();

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

学习交流

如果你觉得自己学习效率低,缺乏正确的指导,可以加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧

群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

都做了空实现,只有Man类实现了全部方法,也就是说对于机器人相关的实现类而言,接口方法是存在冗余的,所以按照接口隔离原则我们这种情况下就要对接口进行细化拆分,修改类图如下:

这里写图片描述

可以看到我们将接口进行了拆分,相关方法独立成为接口,每一个实现类只依赖跟它直接相关的接口。相关代码修改如下:

public interface Intelligence {

public void talk();

public void think();

}

public interface Work {

public void work();

}

public interface Display {

public void display();

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-Jsu0Zgkb-1710846506044)]
[外链图片转存中…(img-j4pyXdL3-1710846506045)]
[外链图片转存中…(img-9Grjye4k-1710846506045)]
[外链图片转存中…(img-YQDniwG3-1710846506046)]

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-Y3T7jFsa-1710846506046)]

学习交流

如果你觉得自己学习效率低,缺乏正确的指导,可以加入资源丰富,学习氛围浓厚的技术圈一起学习交流吧

[外链图片转存中…(img-sfs4FIGe-1710846506047)]

[外链图片转存中…(img-6fsA4eFQ-1710846506047)]

群内有许多来自一线的技术大牛,也有在小厂或外包公司奋斗的码农,我们致力打造一个平等,高质量的Android交流圈子,不一定能短期就让每个人的技术突飞猛进,但从长远来说,眼光,格局,长远发展的方向才是最重要的。

35岁中年危机大多是因为被短期的利益牵着走,过早压榨掉了价值,如果能一开始就树立一个正确的长远的职业规划。35岁后的你只会比周围的人更值钱。

  • 25
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值