我要做 Android 之面笔试总结

说说什么是工厂模式 ps:之前只是单纯了解过工厂模式,不知道其实有三种工厂模式

一:工厂模式

工厂模式就有三种,它们分别是简单工厂模式(并不在23中模式之中),工厂方法模式以及抽象工厂模式,其中我们通常所说的工厂模式指的是工厂方法模式,工厂方法模式是日常开发中使用频率最高的一种设计模式。

简单工厂模式

简单工厂模式其实并不算是一种设计模式,更多的时候是一种编程习惯。

定义:
  定义一个工厂类,根据传入的参数不同返回不同的实例,被创建的实例具有共同的父类或接口。

适用场景:
  其实由定义也大概能推测出其使用场景,首先由于只有一个工厂类,所以工厂类中创建的对象不能太多,否则工厂类的业务逻辑就太复杂了,其次由于工厂类封装了对象的创建过程,所以客户端应该不关心对象的创建。总结一下适用场景:
  (1)需要创建的对象较少。
  (2)客户端不关心对象的创建过程。
以上就是简单工厂模式简单工厂模式的适用场景,下面看一个具体的实例。

实例:
  创建一个可以绘制不同形状的绘图工具,可以绘制圆形,正方形,三角形,每个图形都会有一个draw()方法用于绘图,不看代码先考虑一下如何通过该模式设计完成此功能。

由题可知圆形,正方形,三角形都属于一种图形,并且都具有draw方法,所以首先可以定义一个接口或者抽象类,作为这三个图像的公共父类,并在其中声明一个公共的draw方法。

public interface Shape {
    void draw();
}

这里定义成抽象类也是可以的,只不过接口是更高一级的抽象,所以习惯定义成接口,而且接口支持多实现,方便以后扩展。

下面就是编写具体的图形,每种图形都实现Shape 接口
圆形

public class CircleShape implements Shape {

    public CircleShape() {
        System.out.println(  "CircleShape: created");
    }

    @Override
    public void draw() {
        System.out.println(  "draw: CircleShape");
    }

}

正方形

public class RectShape implements Shape {
    public RectShape() {
       System.out.println(  "RectShape: created");
    }

    @Override
    public void draw() {
       System.out.println(  "draw: RectShape");
    }

}

三角形

public class TriangleShape implements Shape {

    public TriangleShape() {
        System.out.println(  "TriangleShape: created");
    }

    @Override
    public void draw() {
        System.out.println(  "draw: TriangleShape");
    }

}

下面是工厂类的具体实现

public class ShapeFactory {
          public static final String TAG = "ShapeFactory";
          public static Shape getShape(String type) {
              Shape shape = null;
              if (type.equalsIgnoreCase("circle")) {
                  shape = new CircleShape();
              } else if (type.equalsIgnoreCase("rect")) {
                  shape = new RectShape();
              } else if (type.equalsIgnoreCase("triangle")) {
                  shape = new TriangleShape();
              }
              return shape;
          }
   }

在这个工厂类中通过传入不同的type可以new不同的形状,返回结果为Shape 类型,这个就是简单工厂核心的地方了。
客户端使用

画圆形

  Shape shape= ShapeFactory.getShape("circle");
    shape.draw();

画正方形

Shape shape= ShapeFactory.getShape("rect");
    shape.draw();

画三角形

Shape shape= ShapeFactory.getShape("triangle");
    shape.draw();

只通过给ShapeFactory传入不同的参数就实现了各种形状的绘制。以上就是简单工厂方式。

工厂方法模式

工厂方法模式是简单工厂的仅一步深化, 在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的对象,而是针对不同的对象提供不同的工厂。也就是说每个对象都有一个与之对应的工厂。

定义:
  定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。
  这次我们先用实例详细解释一下这个定义,最后在总结它的使用场景。

实例:
  现在需要设计一个这样的图片加载类,它具有多个图片加载器,用来加载jpg,png,gif格式的图片,每个加载器都有一个read()方法,用于读取图片。下面我们完成这个图片加载类。

首先完成图片加载器的设计,编写一个加载器的公共接口。

public interface Reader {
    void read();
}

Reader 里面只有一个read()方法,然后完成各个图片加载器的代码。

Jpg图片加载器

public class JpgReader implements Reader {
    @Override
    public void read() {
        System.out.print("read jpg");
    }
}

Png图片加载器

public class PngReader implements Reader {
    @Override
    public void read() {
        System.out.print("read png");
    }
}

Gif图片加载器

public class GifReader implements Reader {
    @Override
    public void read() {
        System.out.print("read gif");
    }
}

现在我们按照定义所说定义一个抽象的工厂接口ReaderFactory

public interface ReaderFactory {
    Reader getReader();
}

里面有一个getReader()方法返回我们的Reader 类,接下来我们把上面定义好的每个图片加载器都提供一个工厂类,这些工厂类实现了ReaderFactory 。

Jpg加载器工厂

public class JpgReaderFactory implements ReaderFactory {
    @Override
    public Reader getReader() {
        return new JpgReader();
    }
}

Png加载器工厂

public class PngReaderFactory implements ReaderFactory {
    @Override
    public Reader getReader() {
        return new PngReader();
    }
}

Gif加载器工厂

public class GifReaderFactory implements ReaderFactory {
    @Override
    public Reader getReader() {
        return new GifReader();
    }
}

在每个工厂类中我们都通过复写的getReader()方法返回各自的图片加载器对象。

客户端使用

读取Jpg

ReaderFactory factory=new JpgReaderFactory();
Reader  reader=factory.getReader();
reader.read();

读取Png

ReaderFactory factory=new PngReaderFactory();
Reader  reader=factory.getReader();
reader.read();

读取Gif

ReaderFactory factory=new GifReaderFactory();
Reader  reader=factory.getReader();
reader.read();

可以看到上面三段代码,分别读取了不同格式的图片,不同之处在于针对不同的图片格式声明了不同的工厂,进而创建了相应的图片加载器。

适用场景:

(1)客户端不需要知道它所创建的对象的类。例子中我们不知道每个图片加载器具体叫什么名,只知道创建它的工厂名就完成了床架过程。
  (2)客户端可以通过子类来指定创建对应的对象。
以上场景使用于采用工厂方法模式。

抽象工厂模式

这个模式最不好理解,而且在实际应用中局限性也蛮大的,因为这个模式并不符合开闭原则。实际开发还需要做好权衡。
  抽象工厂模式是工厂方法的仅一步深化,在这个模式中的工厂类不单单可以创建一个对象,而是可以创建一组对象。这是和工厂方法最大的不同点。

定义:
  提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。( 在抽象工厂模式中,每一个具体工厂都提供了多个工厂方法用于产生多种不同类型的对象)
  
抽象工厂和工厂方法一样可以划分为4大部分:
AbstractFactory(抽象工厂)声明了一组用于创建对象的方法,注意是一组。
ConcreteFactory(具体工厂):它实现了在抽象工厂中声明的创建对象的方法,生成一组具体对象。
AbstractProduct(抽象产品):它为每种对象声明接口,在其中声明了对象所具有的业务方法。
ConcreteProduct(具体产品):它定义具体工厂生产的具体对象。
下面还是先看一个具体实例。

实例:
  现在需要做一款跨平台的游戏,需要兼容Android,Ios,Wp三个移动操作系统,该游戏针对每个系统都设计了一套操作控制器(OperationController)和界面控制器(UIController),下面通过抽闲工厂方式完成这款游戏的架构设计。

由题可知,游戏里边的各个平台的UIController和OperationController应该是我们最终生产的具体产品。所以新建两个抽象产品接口。

抽象操作控制器

public interface OperationController {
    void control();
}

抽象界面控制器

public interface UIController {
    void display();
}

然后完成各个系统平台的具体操作控制器和界面控制器
Android

public class AndroidOperationController implements OperationController {
    @Override
    public void control() {
        System.out.println("AndroidOperationController");
    }
}

public class AndroidUIController implements UIController {
    @Override
    public void display() {
        System.out.println("AndroidInterfaceController");
    }
}

Ios

public class IosOperationController implements OperationController {
    @Override
    public void control() {
        System.out.println("IosOperationController");
    }
}

public class IosUIController implements UIController {
    @Override
    public void display() {
        System.out.println("IosInterfaceController");
    }
}

Wp

public class WpOperationController implements OperationController {
    @Override
    public void control() {
        System.out.println("WpOperationController");
    }
}
public class WpUIController implements UIController {
    @Override
    public void display() {
        System.out.println("WpInterfaceController");
    }
}

下面定义一个抽象工厂创建OperationController和UIController

public interface SystemFactory {
    public OperationController createOperationController();
    public UIController createInterfaceController();
}

在各平台具体的工厂类中完成操作控制器和界面控制器的创建过程

Android

public class AndroidFactory implements SystemFactory {
    @Override
    public OperationController createOperationController() {
        return new AndroidOperationController();
    }

    @Override
    public UIController createInterfaceController() {
        return new AndroidUIController();
    }
}

Ios

public class IosFactory implements SystemFactory {
    @Override
    public OperationController createOperationController() {
        return new IosOperationController();
    }

    @Override
    public UIController createInterfaceController() {
        return new IosUIController();
    }
}

Wp

public class WpFactory implements SystemFactory {
    @Override
    public OperationController createOperationController() {
        return new WpOperationController();
    }

    @Override
    public UIController createInterfaceController() {
        return new WpUIController();
    }
}

客户端调用:

  SystemFactory mFactory;
    UIController interfaceController;
    OperationController operationController;

    //Android
    mFactory=new AndroidFactory();
    //Ios
    mFactory=new IosFactory();
    //Wp
    mFactory=new WpFactory();

    interfaceController=mFactory.createInterfaceController();
    operationController=mFactory.createOperationController();
    interfaceController.display();
    operationController.control();

针对不同平台只通过创建不同的工厂对象就完成了操作和UI控制器的创建。小伙伴们可以对比一下,如果这个游戏使用工厂方法模式搭建需要创建多少个工厂类呢?下面总结一下抽象工厂的适用场景。

适用场景:
(1)和工厂方法一样客户端不需要知道它所创建的对象的类。
(2)需要一组对象共同完成某种功能时。并且可能存在多组对象完成不同功能的情况。
(3)系统结构稳定,不会频繁的增加对象。(因为一旦增加就需要修改原有代码,不符合开闭原则)

什么是装饰者模式?

1、定义

装饰模式(Decorator Pattern) :动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。

tips: 要使用装饰者模式,需要满足以下设计原则:
1、多用组合,少用继承
2、开放-关闭原则:类应该对拓展开放,对修改关闭

装饰者模式的结构:

我要做 Android 之面笔试总结_第1张图片
07.png

从上图中可以看出,装饰模式一共有四部分组成:

1、抽象组件角色(Component):定义一个对象接口,以规范准备接受附加责任的对象,即可以给这些对象动态地添加职责。
2、具体组件角色(ConcreteComponent) :被装饰者,定义一个将要被装饰增加功能的类。可以给这个类的对象添加一些职责。
3、抽象装饰器(Decorator):维持一个指向构件Component对象的实例,并定义一个与抽象组件角色Component接口一致的接口。
4、具体装饰器角色(ConcreteDecorator):向组件添加职责。

源代码

抽象构件角色“齐天大圣”接口定义了一个move()方法,这是所有的具体构件类和装饰类必须实现的。

//大圣的尊号
public interface TheGreatestSage {
    
    public void move();
}

具体构件角色“大圣本尊”猢狲类

public class Monkey implements TheGreatestSage {

    @Override
    public void move() {
        //代码
        System.out.println("Monkey Move");
    }

}

抽象装饰角色“七十二变”

public class Change implements TheGreatestSage {
    private TheGreatestSage sage;
    
    public Change(TheGreatestSage sage){
        this.sage = sage;
    }
    @Override
    public void move() {
        // 代码
        sage.move();
    }

}

具体装饰角色“鱼儿”

public class Fish extends Change {
    
    public Fish(TheGreatestSage sage) {
        super(sage);
    }

    @Override
    public void move() {
        // 代码
        System.out.println("Fish Move");
    }
}

具体装饰角色“鸟儿”

public class Bird extends Change {
    
    public Bird(TheGreatestSage sage) {
        super(sage);
    }

    @Override
    public void move() {
        // 代码
        System.out.println("Bird Move");
    }
}

客户端类

public class Client {

    public static void main(String[] args) {
        TheGreatestSage sage = new Monkey();
        // 第一种写法
        TheGreatestSage bird = new Bird(sage);
        TheGreatestSage fish = new Fish(bird);
        // 第二种写法
        //TheGreatestSage fish = new Fish(new Bird(sage));
        fish.move(); 
    }

}

“大圣本尊”是ConcreteComponent类,而“鸟儿”、“鱼儿”是装饰类。要装饰的是“大圣本尊”,也即“猢狲”实例。

上面的例子中,系统把大圣从一只猢狲装饰成了一只鸟儿(把鸟儿的功能加到了猢狲身上),然后又把鸟儿装饰成了一条鱼儿(把鱼儿的功能加到了猢狲+鸟儿身上,得到了猢狲+鸟儿+鱼儿)。

装饰模式的优点:

  • 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
  • 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。
  • 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
  • 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”

装饰模式的缺点:

  • 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。
  • 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。

三:如何减少换页错误?

首先我们需要了解什么是换页错误:换页错误又称缺页错误

起因是一道笔试题,如下:

如何减少换页错误?()

1,进程倾向于占用CPU

2,访问局部性(localilty of reference)满足进程要求

3,进程倾向于占用I/O

4,使用基于最短剩余时间(shortest remaining time)的调度机制

5,减少页大小

1 最短剩余时间优先算法

最短剩余时间优先(Shortest Remaining Time Next,SRTN)调度算法多用于剥夺式的调度中。 在进程调度中,每次调度时,系统把处理机分配给就绪队列中运行完所需时间最短的进程。 最短剩余时间优先算法也可用于不剥夺式调度方式中,此时退化为短作业优先算法。

2 局部性原理

局部性原理是一个经典的原理,分为时间局部性和空间局部性,意思是应用一旦访问了一个空间的数据,则这个空间中的相邻区域的内容也很可能被访问,一旦访问了某一段代码,则其周围的代码也很可能被访问。局部性原理的运用就是根据这个,把可能访问的数据和代码都加在到内存中,以后访问就不用加载了(因为主要的消耗在IO端)

4选项不一定能减少换页错误。进程频繁切换不一定意味着换页错误多,可以考虑如下两种情况:

1、如果分配的帧很多,可以装下所有的页,根本就不会置换页表。

2、采用合理的置换策略,即使进程频繁切换,也可能可以减少换页错误。

所以换页错误和进程切不切换没有确切的联系。

所以应该选第二个选项:采用局部性原理。

四:同一进程下的线程可以共享什么?

同一进程下的线程可以共享()

  • stack
    
  • data section
    
  • register set
    
  • thread ID
    

出自笔试题:答案:B

线程共享的环境包括:进程代码段、进程的公有数据(利用这些共享的数据,线程很容易的实现相互之间的通讯)、进程打开的文件描述符、信号的处理器、进程的当前目录和进程用户ID与进程组ID。

tips:同一个进程的多个线程有自己独立的堆栈和局部变量

A.栈(私有) B.数据段,线程共享 C.寄存器组 D.线程ID

设计模式中属于结构模式的有哪些?

答:设计模式主要分三个类型:创建型、结构型和行为型。

结构型设计模式是从程序的结构上解决模块之间的耦合问题(好像是句废话),GoF23种设计模式中结构型设计模式有7种,分别是:Adapter适配器模式、Bridge桥接模式、Composite组合模式、Decorator装饰模式、Facade外观模式、Flyweight享元模式和Proxy代理模式。下面分别总结一下这几种模式:

设计模式 GoF的描述 我的理解
Adapter适配器模式 将一个类的接口转换成客户希望的另一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的类可以一起工作 转换接口,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用,但是,新环境要求的接口是现存对象所不满足的,此时我们可以通过添加一层Adapter对现有的对象加入一些接口,使其适应新的应用环境。
Bridge桥接模式 将抽象部分与实现部分分离,使他们可以独立的变化 分离接口(抽象)与其实现,当某个类型具有两个或两个以上的纬度变化(或者说是变化点),通过以继承接口的方式隔离变化,以减少因变化带来的代码的修改量。
Composite组合模式 将对象组合成树形结构以表示“部分-整体”的层次结构。Composite模式使得客户对单个对象和组合对象的使用具有一致性 解决客户程序与复杂对象容器的解耦,一类具有“容器特征”的对象——即他们在充当对象的同时,又是其他对象的容器的情况,通过继承统一的接口,我们可以将容器对象及其子对象看成同一类对象使用,以减少对象使用中的复杂度。
Decorator装饰模式 动态的给一个对象添加一些额外的职责。就增加功能而言,Decorator模式比生成子类更为灵活 在稳定接口的前提下为对象扩展功能,主要是解决用继承的方式为对象扩展大量功能而造成的子对象数量膨胀的问题
Facade外观模式 为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用 简化接口,对于复杂子系统或子对象调用的封装。从客户程序角度看,只能看见Facade提供的接口。换句话说是对子对象调用的封装,将客户程序对子对象的调用与子对象的变化分离。
Flyweight享元模式 运用共享技术有效的支持大量细粒度的对象 主要是解决由于相同对象数量过大而造成系统内存开销过大的问题。实际上是相同的对象引用指向同一个对象空间。在使用Flyweight模式前要做一个评估,如果使用不当会适得其反
Proxy代理模式 为其他对象提供一种代理以控制这个对象的访问 解决直接访问某些对象是出现的问题,如:访问远程的对象

在学习的过程中感觉,从代码的角度看Adapter适配器模式和Proxy代理模式有些类似,Adapter适配器模式是解决现有对象在新的环境中的不足,而Proxy代理模式是解决直接访问对象时出现的问题

CISC 指令和 RISC 指令

RISC(精简指令集计算机)和CISC(复杂指令集计算机)是当前CPU的两种架构。它们的区别在于不同的CPU设计理念和方法。

复杂指令集CPU内部为将较复杂的指令译码,也就是指令较长,分成几个微指令去执行,正是如此开发程序比较容易(指令多的缘故),但是由于指令复杂,执行工作效率较差,处理数据速度较慢

(1) 指令系统:RISC 设计者把主要精力放在那些经常使用的指令上,尽量使它们具有简单高效的特色。对不常用的功能,常通过组合指令来完成。因此,在RISC 机器上实现特殊功能时,效率可能较低。但可以利用流水技术和超标量技术加以改进和弥补。而CISC 计算机的指令系统比较丰富,有专用指令来完成特定的功能。因此,处理特殊任务效率较高。

(2) 存储器操作:RISC 对存储器操作有限制,使控制简单化;而CISC 机器的存储器操作指令多,操作直接。
(3) 程序:RISC 汇编语言程序一般需要较大的内存空间,实现特殊功能时程序复杂,不易设计;而CISC 汇编语言程序编程相对简单,科学计算及复杂操作的程序设计相对容易,效率较高。
(4) 中断:RISC 机器在一条指令执行的适当地方可以响应中断;而CISC 机器是在一条指令执行结束后响应中断。
(5) CPU:RISC CPU 包含有较少的单元电路,因而面积小、功耗低;而CISC CPU 包含有丰富的电路单元,因而功能强、面积大、功耗大。
(6) 设计周期:RISC 微处理器结构简单,布局紧凑,设计周期短,且易于采用最新技术;CISC 微处理器结构复杂,设计周期长。
(7) 用户使用:RISC 微处理器结构简单,指令规整,性能容易把握,易学易用;CISC微处理器结构复杂,功能强大,实现特殊功能容易。
(8) 应用范围:由于RISC 指令系统的确定与特定的应用领域有关,故RISC 机器更适合于专用机;而CISC 机器则更适合于通用机。

排序算法中初始数据顺序对于算法性能无影响的是?

堆排序的数据顺序对于算法性能没有影响,其他类似于 冒泡,插入,选择都有影响。

总结:说实话有些无力吐槽的感觉,很多知识点确实课都学过但是基本都属于不听或者听了也不常用记不得的那种,但是笔试就是考这种题目。很偏冷门。无奈,但是没办法,我们只能随波逐流而已。所以就是不断学习,不断总结,相信更好的明天在等着你。终有一天你会耀眼。

愿我们成为真实的自己。

你可能感兴趣的:(我要做 Android 之面笔试总结)