1. 什么是面向对象?

面向对象三大特征:继承、封装、多态

关键语:一切事物皆对象。将现实的事物抽象出来

优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护

面向对象的六大原则
  1. 单一职责原则
    一个类中应该是一组相关性很高的函数、数据的封装。如何划分一个类,一个函数的职责,每个人都有自己的看法,这需要个人经验、具体的业务逻辑而定。但是它也有一些基本的指导思想,例如:两个完全不一样的功能就不应该放在一个类中。一个类中应该是一组相关性很高的函数、数据的封装
  2. 开闭原则
    软件中的对象(类、模块、函数等)应该是对于扩展是开放的,对于修改是封闭的。当软件需要变化时,应该尽量通过扩展的方式来实现变化,而不是通过修改已有的代码来实现
  3. 里氏替换原则
    所有引用基类的地方必须能透明地使用其子类的对象。通俗点讲:只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或者异常,使用者可能根本就不需要知道是父类还是子类,但是反过来就不行了,有子类出现的地方父类未必能适应
  4. 依赖倒置原则
    依赖倒置原则指代了一种特定的解耦形式,使得高层次的模块不依赖于底层地的模块的实现细节的目的。以下几个关键点:
    • 高层模块不应该依赖底层模块,两者都应该依赖其抽象
    • 抽象不应该依赖细节
    • 细节应该依赖抽象

抽象就是指接口或抽象类,两者都是不能直接被实例化的;细节就是实现类,实现接口或集成抽象类而产生的类就是细节,其特点就是:可以直接被实例化,也就是可以加上一个关键字new产生一个对象。高层模块就是调用端,底层模块就是具体实现类。在java的表现是:模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生和的 。一句话概括:面向接口编程,或者说面向对象编程,这里的抽象指的是接口或者抽象类

  1. 接口隔离原则
    定义是:客户端不应该依赖它不需要的接口。另一种定义是:类之间的依赖关系应该建立在最小的接口上。接口隔离原则非常庞大、臃肿的接口拆分成更小的和更具体的接口,这样在客户将会只需要知道他们感兴趣的方法。接口隔离原则的目的是系统解开耦合,从而容易重构、更改和重新部署
    试想一下,如果在只是需要关闭一个对象时,它却暴露出了其他的接口函数,如OutputStream的write方法,这就使得更多的细节暴露在客户端代码面前,不仅没有很好地隐藏实现,还增加了接口的使用难度,而通过Closeable接口可关闭的对象抽象起来,这样只需要客户端依赖于Closeable就可以对客户端隐藏其他的接口信息,客户端只需要知道这个对象可关闭(只可以调用close方法)即可
  2. 迪米特原则
    一个对象应该对其他对象有最少的了解,通俗讲:一个类应该对自己需要耦合或者调用的类知道得最少,类的内部如何实现与调用者或者依赖者没关系,调用者或者依赖者只需要知道它需要知道的方法即可,其他一概不用管。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另外一个类影响也越大

2.内存的优化

内存抖动

Android里内存抖动是指内存频繁地分配和回收,而频繁的gc会导致卡顿,严重时还会导致OOM
一个很经典的案例是string拼接创建大量小的对象(比如在一些频繁调用的地方打字符串拼接的log的时候,或在for里面创建对象、在onDraw函数里创建对象)
而内存抖动为什么会引起OOM呢?

主要原因还是有因为大量小的对象频繁创建,导致内存碎片,从而当需要分配内存时,虽然总体上还是有剩余内存可分配,而由于这些内存不连续,导致无法分配,系统直接就返回OOM了

内存泄漏

  1. 单例(主要原因还是因为一般情况下单例都是全局的,有时候会引用一些实际生命周期比较短的变量,导致其无法释放)
  2. 静态变量(同样也是因为生命周期比较长
  3. Handler内存泄漏
  4. 匿名内部类(匿名内部类会引用外部类,导致无法释放,比如各种回调)
  5. 资源使用未关闭(BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap)

3.电量的优化

以下几种造成耗电的场景

  1. 唤醒屏幕(还有屏幕的亮暗)
  2. CPU唤醒使用
  3. 蜂窝式无线(WIFI)
  4. 传感器

优化电量的方法

  1. 监听手机充电状态(有相关API获取);获取充电状态信息后,可以有针对的对部分代码优化。当在充电时才去执行一些非常耗电的操作。具体的业务是考虑将一些不需要及时与用户交互的操作放到充电时,例如:自动备份上传图片
  2. 屏幕唤醒;当 Android 设备空闲时,屏幕会变暗,然后关闭屏幕,最后会停止 CPU 的运行,这样可以防 止电池电量掉的快。但有些时候我们需要改变 Android 系统默认的这种状态:比如玩游戏时我们需要保持屏幕常亮,比如一些下载操作不需要屏幕常亮但需要 CPU 一直运行直到任务完成
  3. 传感器;使用传感器,选择合适的采样率,越高的采样率类型则越费电
  4. GPS;选择合适的定位,如果 App 只是需要一个粗略的定位那么就不需要使用 GPS 进行定位,既耗费电量,定位的耗 时也久
  5. JobScheduler;自 Android 5.0 发布以来,JobScheduler 已成为执行后台工作的很好的方式,其工作方式有 利于用户在适当的时机执行正确的事情。应用可以在安排作业的同时允许系统基于内存、电源 和连接情况进行优化。JobSchedule 的宗旨就是把一些不是特别紧急的任务放到更合适的时机批量处理。这样做有两个好处
    1. 避免频繁的唤醒硬件模块,造成不必要的电量消耗
    2. 避免在不合适的时间(例如低电量情况下、弱网络或者移动网络情况下的)执行过多的 任务消耗电量
  6. WakeLock;Android为了节省电量,会在用户无操作一段时间之后进入休眠状态。Wake Lock是一种锁的机制,只要有人拿着这个锁,系统就无法进入休眠。一些App为了能在后台持续做事情,就会持有一个WakeLock,那么手机就不会进入休眠状态,App要做的事情能做了,但是也更加耗电
  7. 无网络状态避免网络请求

4.网络的优化

  1. 请求合并
  2. 减少请求数据的大小
  3. 减少返回数据大小
  4. 对数据缓存
  5. 进行增量更新
  6. IP直连
  7. 文件、图片等的下载,采用断点续传,不浪费用户之前消耗过的流量
  8. 图片使用WEBP格式

5.其他

  1. Handler中有Loop死循环,为什么没有阻塞主线程?主线程的死循环一直运行是不是特别消耗CPU资源呢?

    其实不然,这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。 Gityuan–Handler(Native层)

6.Java中的集合

Collection

  1. List:Collection接口的子接口,该接口体系特点是:存储有序,元素可重复
    1. ArrayList:底层是数组数据结构,特点:查询快,增删慢。数组实现,查找快,增删慢由于是数组实现,在增和删的时候 会牵扯到数组增容,以及拷贝元素所以慢。数组是可以直接按索引查找,所以查找时较快
    2. LinkedList:底层是链表数据结构,特点:增删快,查询慢。链表实现,增删快,查找慢,由于链表实现,增加时只要让前一个元素记住自己就可以,删除时让前一个元素记住后一个元素,后一个元素记住前一个元素,这样的增删效率较高但查询时需要一个一个的遍历,所以效率较低
    3. Vector:底层是数组数据结构,特点:线程同步,效率低
  2. Set:Collection接口的子接口,该接口体系类特点是:存储无序,元素不可重复
    1. HashSet:底层是哈希表数据结构,特点:存取都快。通过hashCode和equals方法保证元素的唯一性
    2. TreeSet:底层是二叉树数据结构,特点:存取都快,通过比较性来保证元素的唯一性。底层是使用了红黑树(二叉树)的数据结构实现的。往 TreeSet添加元素的时候,如果添加的元素具备了自然顺序的特性,那么treeSet会按照元素的自然顺序进行排序存储。否则需要自定义比较规则,不然编译会报错

Map集合的根接口,特点是:存在键和值的映射关系,键有唯一性

  1. HashMap:底层是哈希表数据结构,特点:存取都快,通过hashCode和equals方法保证的唯一性。存储无序,底层是哈希表数据结构,线程是不同步的,可以存入null键,null值。要保证键的唯一性
  2. TreeMap:可以对进行自然排序,所以存储有序。底层是二叉树数据结构。可以对map集合中的键进行排序。需要实现Comparable接口复写 compareTo方法或者自定义类实现Comparator接口,创建比较器来对键值进行比较排序来判断键的唯一性
  3. HashTable:底层是哈希表数据结构,线程是同步的,不可以存入null键,null 值。效率较低,被HashMap 替代 | Collection | 我们需要保存若干个对象的时候使用集合 | | —- | —- | | List | 如果我们需要保留存储有顺序, 并且保留重复元素, 使用List
    如果查询较多, 那么使用ArrayList
    如果存取较多, 那么使用LinkedList
    如果需要线程安全, 那么使用Vector | | Set | 如果我们不需要保留存储顺序, 并且需要去掉重复元素, 使用Set
    如果我们需要将元素排序, 那么使用TreeSet
    如果我们不需要排序, 使用HashSet, HashSet比TreeSet效率高
    如果我们需要保留存储顺序, 又要过滤重复元素, 那么使用LinkedHashSet |

看到array,就要想到角标

看到link,就要想到first,last

看到hash,就要想到hashCode,equals

看到tree,就要想到两个接口。Comparable,Comparator