1.1.可以先自我介绍一下吗?
1.2.学了这么多东西,你觉得你学的最好的是哪一块?
1.3.有没有什么实战项目做出来呢?
要点:平时注意多上GitHub、各种网课上或者找导师多扒些项目去做
表达的效率
!当被前辈提问时,简单扼要
地把重点说明白!
切忌
拖泥带水含糊不清,没有效率地说了一堆话,很扣分
!项目最难的地方
,最有价值的问题
和解决方案
,最好自己总结好,
交代好项目重点之后顺带提出来,不然面试官前辈会问下面 1.4.
这个问题。
所以务必注重回答问题的完整性
和严谨性
;1.4. 你觉得在这个项目当中做起来最难的地方是在哪里?
所用技术及所涉及知识点
、设计思路
、解决方法
等方向归纳回答;1.5 除了做Android的话,对C/C++这一块了解吗?
这里可以从C++和Java的异同入手回答:
1.Android端跟后台通讯的时候用的是什么协议?
你们对HTTP平常用得熟悉吗?
2.(接1. )HTTP是哪一层上面在用的呢?
3.(接2.)那它是基于什么协议去实现的?
4.在项目中,你有可能需要维持APP跟后台之间的长时间连接,那么在实际运用中你是如何实现维持长时间连接的?就是你的这种app跟后台服务的通讯,是一种 短连接 还是一种 长连接 的方式呢?
考点:网络的(短连接跟)长连接(即持久连接)问题
HTTPURLConnection
的封装类,
首先将一个web UR
L传给一个URL
对象的构造方法,创建出一个URL
实例,
用这个URL实例调用其openConnection()
方法,会返回一个对象,
将其返回的对象转型为HttpURLConnection
对象并付给一个HttpURLConnection实例
,
接着就可以调用HttpURLConnection
实例的一系列set
方法对这个请求做各种设置,
其中调用方法setRequestProperty("Connection", "Keep-Alive")
即可完成这个请求的长连接
的实现;
当然除了以上Android端的配置意外,我们还需要在服务器
设置好Keep- Alive长连接模式
,
是否能完成一个完整的Keep- Alive连接和服务器设置也相关; URL realUrl = new URL(url); HttpURLConnection conn = (HttpURLConnection) realUrl .openConnection(); // 发送POST请求必须设置如下两行 conn.setDoOutput(true); conn.setDoInput(true); // POST方法 conn.setRequestMethod("POST"); // 设置通用的请求属性 conn.setRequestProperty("accept", "\*/\*"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)"); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.connect();
什么是长连接、短连接
:
- 在HTTP/1.0中默认使用短连接。
也就是说,客户端和服务器每进行一次HTTP操作,就建立一次连接,任务结束就中断连接。
当客户端浏览器访问的某个HTML或其他类型的Web页中包含有其他的Web资源(如JavaScript文件、图像文件、CSS文件等),每遇到这样一个Web资源,浏览器就会重新建立一个HTTP会话。 - 而从HTTP/1.1起,默认使用长连接,用以保持连接特性。 使用长连接的HTTP协议,会在响应头加入这行代码:
在使用长连接的情况下,当一个网页打开完成后,客户端和服务器之间用于传输HTTP数据的TCP连接不会关闭,客户端再次访问这个服务器时,会继续使用这一条已经建立的连接。
Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间。
实现长连接需要客户端和服务端都支持长连接。
- HTTP协议的长连接和短连接,实质上是TCP协议的长连接和短连接。
- **`长连接,短连接的适用场景?`**
而像WEB网站的http服务一般都用短链接,
因为长连接对于服务端来说会耗费一定的资源,
而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,
如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。
所以并发量大,但每个用户无需频繁操作情况下需用短连好。
相关阅读:
5.客户端到服务器拉数据的时候你是用GET还是用POST去取的?
参考:拉数据的时候是用GET去取的,另外:
GET
和POST
本质上是没有区别的,它们都是**TCP
**链接,- **GET和POST能做(能做——具备实现的能力)的事情一样一样**。 技术上要给GET加上request body,给POST带上url参数,也是行的通的。
- 只不过
应用过程上的区别
:- **00 关于服务器(2点):**
- **GET是从服务器上获取数据,**
- GET产生一个TCP数据包, 浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
POST产生两个TCP数据包,浏览器先发送header,
服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
也就是说,
- http协议从未规定GET/POST的请求长度限制是多少; 但是实际应用上,
- 从`本质意义`上讲,GET是安全的,POST不安全:
- GET没有更改服务器内容;
- POST对服务器就行写入、覆盖,会更改服务器内容;
- 幂等性(同样的一个操作,它一次或者多次地操作,对系统资源产生的影响是一样的)
- GET具备幂等性;
- POST不具备; (原因参照对服务器是否有修改)
- GET与POST都有自己的语义,不能随便混用。
- 据研究,在网络环境好的情况下, 发一次包的时间和发两次包的时间差别基本可以无视。
而在网络环境差的情况下,
两次包的TCP在验证数据包完整性上,有非常大的优点。
- 并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。
相关阅读:
6.在做的过程中有没有遇到过HTTP的错误,比如说HTTP的那些错误码?
- 1xx:表示服务器已接收了客户端请求,客户端可继续发送请求
- 2xx:表示服务器已成功接收到请求并进行处理
- 200 OK:表示客户端请求成功
- 3xx:表示服务器要求客户端重定向
- 4xx:表示客户端的请求有非法内容
- 400 Bad Request:表示客户端请求有语法错误,不能被服务器所理解
- 401 Unauthonzed:表示请求未经授权,该状态代码必须与 WWW-Authenticate 报头域一起使用
- 403 Forbidden:表示服务器收到请求,但是拒绝提供服务,通常会在响应正文中给出不提供服务的原因
- 404 Not Found:请求的资源不存在,例如,输入了错误的URL
- 414 :Request-URI 太长
- 5xx:表示服务器未能正常处理客户端的请求而出现意外错误
- 500 Internal Server Error:表示服务器发生不可预期的错误,导致无法完成客户端的请求 503 Service Unavailable:表示服务器当前不能够处理客户端的请求,在一段时间之后,服务器可能会恢复正常
相关阅读:
1.String、StringBuffer、StringBuilder三个的分别?
综述:
- String:不可变字符串;
- StringBuffer:可变字符串、效率低、线程安全;
- StringBuilder:可变字符序列、效率高、线程不安全;
- 执行速度:StringBuilder > StringBuffer > String; (StringBuffer 很多方法可以带有synchronized关键字,在做字符串操作时需要做对应的操作,所以运行性能比StringBuilder稍微差些)
-undefined
StringBuffer、 StringBuilder的API基本一样:
详析:
- **`String`****的值是不可变的,**
JVM对于上图是这样处理的,
首先创建一个String对象str,并把“hello”赋值给str,
然后str=str+" world":
其实JVM又创建了一个新的对象也名为str,
然后再把原来的str的值和“ world”加起来再赋值给新的str,
而原来的str就会被JVM的垃圾回收机制(GC)给回收掉了,
所以,最开始的str实际上并没有被更改,
也就是前面说的String对象一旦创建之后就不可更改了(String的值是不可变的)。
这样短短的两个字符串,却需要开辟三次内存空间,不得不说这是对内存空间的极大浪费。
所以,
Java中对String对象进行的操作,
实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,执行速度很慢。
- `StringBuffer`是**可变的、线程安全**的字符串操作类, 任何对它指向的字符串的操作都不会产生新的对象。
每个StringBuffer对象都有一定的缓冲区容量,
当字符串大小
- String:适用于操作少量的字符串/数据的情况
- StringBuilder:适用于单线程操作字符串缓冲区下操作大量数据的情况
- StringBuffer:适用于多线程操作字符串缓冲区下操作大量数据的情况
相关阅读:
2.有没有用过Java对应的数据结构的类?
参考:概述诸多
集合类的特性
,尤其注意底层实现
、内部算法实现
、线程安全
、同类区别
等问题;
ArrayList
类
a. 实现了可变的数组
,允许保存所有元素
,包括null
,并可以根据索引位置对集合进行快速的随机访问;
b. 缺点是向指定的索引位置插入对象或删除对象的速度较慢。LinkedList
类
a.采用链表结构
保存对象。
b.优点是便于向集合中插入和删除对象,需要向集合中插入、删除对象
时,使用LinkedList类实现的List集合的效率较高:
c. 但对于随机访问集合中的对象
,使用LinkedList类实现List集合的效率较低。HashSet
类
a. 实现Set接口
,由哈希表(实际上是一个HashMap实例)
支持。
b. 它不保证Set的迭代顺序
,特别是它不保证该顺序恒久不变
。
c .此类允许使用null元素
。TreeSet
类
a. 不仅实现了Set接口
,还实现了java.util.SortedSet接口
,
b. 因此,TreeSet类实现的Set集合在遍历集合时
按照自然顺序递增排序
;*********%%%%%%%%%%***********
c. 也可以按照指定比较器
递增排序;
d. 即可以通过比较器对用TreeSet类实现的Set集合中的对象进行排序。HashMap
类
a .继承于AbstractMap
,实现了Map
、Cloneable
、java.io.Serializable
接口;
b. 此实现提供所有可选的映射操作
,并允许使用null值和null键
,但必须保证键的唯一性
。
c .HashMap通过哈希表
对其内部的映射关系进行快速查找
。
d. 此类不保证
映射的顺序,特别是它不保证该顺序恒久不变。TreeMap
类
a. 不仅实现了Map接口,还实现了java.util.SortedMap接囗,因此,集合中的映射关系具有一定的顺序。
b. 但在添加、删除和定位映射关系
时,TreeMap类比HashMap类性能稍差
。
c. 由于TreeMap类实现的Map集合中的映射关系是根据键对象
按照一定的顺序排列
的,因此不允许键对象是null
。HashTable
类
a. 继承于Dictionary
,实现了Map
、Cloneable
、java.io.Serializable
接口。
b.Hashtable 的函数都是同步
的,这意味着它是线程安全
的。
它的key、value都不可以为null。
此外,Hashtable中的映射不是有序的。
c .HashMap通过哈希表
对其内部的映射关系进行快速查找
。Vector
类
a. Vector
是矢量队列,它是JDK1.0
版本添加的类。继承于AbstractList
,实现了List
, RandomAccess
, Cloneable
这些接口。
b. Vector
继承了AbstractList
,实现了List
;所以,它是一个队列,支持相关的添加、删除、修改、遍历等功能。
c. Vector
实现了RandmoAccess
接口,即提供了随机访问功能。RandmoAccess是java中用来被List实现,为List提供快速访问功能的。在Vector中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。
d. Vector
实现了Cloneable
接口,即实现clone()
函数。它能被克隆。
e. 和ArrayList
不同,Vector
中的操作是线程安全的。Stack
类
a. Stack
是栈。它的特性
是:先进后出
(FILO, First In Last Out)。
b. java工具包中的Stack是继承于Vector(矢量队列)的,由于Vector是通过数组实现的,
这就意味着,Stack也是通过数组实现的,而非链表。
当然,我们也可以将LinkedList当作栈来使用;Set s = Collections.synchronizedSet(new HashSet(...));
synchronizedSet()返回的Set是同步的;补充:
ArrayList
与LinkedList
.- Set集合中的对象`不按特定的方式排序`,只是`简单地把对象加入集合中`;
- Set集合中`不能包含重复对象`;
- Set集合由`Set接口`和`Set接口的实现类`组成。
- Set接口继承了`Collection接口`,因此包含`Collection接口的所有方法`。
Set接口常用的实现类有HashSet类
与TreeSet类
。
- Map集合没有继承`Collection`接口,其提供的是`key到value的映射`;
- Map中不能包含相同的`key`,每个`key`只能映射一个`value`;
- key还决定了`存储对象在映射中的存储位置`, 但不是由
- Map接口
- Map接口提供了将key映射到值的对象。
- 一个映射不能包含重复的key,每个key最多只能映射到一个值。
- Map接口中同样提供了集合的常用方法,除此之外还包括如下表所示的常用方法:
注意:Map集合中允许值对象是null,而且没有个数限制,例如,可通过“map.put("05",null)”语句向集合中添加对象。
- Map接口的实现类
- Map接口常用的实现类有`HashMap`和`TreeMap`;
- 建议使用`HashMap类`实现Map集合;
- 由`HashMap类`实现的Map集合`添加和删除映射关系效率更高`;
- HashMap是基于`哈希表`的`Map接口`的实现;
- HashMap通过`哈希码`对其内部的映射关系进行`快速查找`;
- `TreeMap`中的映射关系存在`一定的顺序`;
- 如果希望Map集合中的对象也存在一定的顺序,应该使用TreeMap类实现Map集合。
相关阅读:
底层实现
与源码分析
详析可以见此:
3.HashMap跟HashTable有什么分别?
HashMap和Hashtable的相同点:
HashMap和Hashtable的不同点:
详细见此文章:Java 集合系列14之 Map总结(HashMap, Hashtable, TreeMap, WeakHashMap等使用场景)
相关阅读:
4.HashMap里面的Hash函数是用什么算法去写的?或者是说当中的Hash有没有可能出现冲突的?
基于JDK1.6.0_45
“拉链法”
实现的散列表
。存储思想
:通过table
数组存储,数组的每一个元素都是一个Entry
;
而一个Entry
就是一个单向链表
,Entry链表
中的 每一个**节点
** 就保存了key-value键值对数据
。
关于table定义的两行源码:
static class Entry<K,V> implements Map.Entry<K,V>
transient Entry[] table;
添加key-value键值对
:
首先根据key值
计算出哈希值
,再计算出数组索引
(即,该key-value节点在table中的索引)。
然后,根据数组索引
找到Entry
(即,单向链表),再遍历单向链表,将key和链表中的每一个节点的key进行对比。
若key
已经存在Entry链表
中(冲突
),则用该value值取代旧的value值;
若key
不存在Entry链表
中,则新建一个key-value节点,并将该节点插入Entry链表的表头位置。key值
**计算出**哈希值
**” 一言的更深一步理解:
即我们在用HashMap(或者HashMTable)的put(K key, V value)
方法时,
实参值会传给形参变量key,这时候JVM会为形参key分配一块内存,并赋予其地址,
随后在put方法中,进行int hash = hash(key.hashCode());
,即双重哈希,减少冲突率;
计算出哈希值
;删除key-value键值对
:
删除键值对
的逻辑相比于添加键值对
简单一些。
首先,还是根据key计算出哈希值,再计算出数组索引(即,该key-value在table中的索引)。
然后,根据索引找出Entry(即,单向链表)。
若节点key-value存在与链表Entry中(冲突
),则删除链表中的节点即可。基于JDK1.8
“拉链法”
进行了 “升级”,
即引入了 红黑树,大程度优化了HashMap的性能;
在增加key-value键值对
时候,
某个数组元素 table[i]
的链表长度
如果大于8
,
则把链表
转换为红黑树
,在红黑树中执行插入操作,
否则仍旧进行链表
的插入操作(似同JDK1.6);相关阅读:
5.Java中实现多线程的方式有哪几种?
参考:
继承Thread类
以及实现Runnable接口
; 继承Thread类
创建一个类去继承Thread类并重写run()方法,
使用的时候构造一个这个类的对象去调用start()方法,
即可让run()方法中的代码在子线程中运行;class MyThread extends Thread{ @Override public void run(){ //处理具体逻辑 } }
new MyThread().start();
实现Runnable接口
的方式来定义一个线程; 实现Runnable接口
- 2.1 定义一个类,让它实现Runnable接口并重写run()方法,
使用的时候构造一个这个类的对象,
然后将这个类对象当做参数传给Thread类的构造方法,
构造好Thread之后调用start()方法即可让run()方法中的代码在子线程中运行;class MyThread implements Runnable{ @Override public void run(){ //处理具体逻辑 } }
MyThread myThread = new MyThread(); new Thread(myThread).start;//Thread的构造函数接收一个Runnable参数
new Thread(new Runnable(){ @Override public void run(){ //处理具体逻辑 } }).start();
实际上归根到底,
以上也便是实现线程执行单元
(线程的执行单元
就是run
方法)的两种方式,
而创建线程的方式
永远只有一种——构造Thread类
,
方才罗列的两类方法最终归结到new Thread().start();
上来。
实际上在运用中,我们都可以围绕Thread的诸多构造方法,
做各种不一样实现线程执行单元
的方式:
6.Java当中的内存管理是怎么做的?它和C++对应的有什么分别呢?
Java 程序运行时的内存分配策略有三种,分别是
静态分配
、栈式分配
和堆式分配
, 三种方式所使用的内存空间分别是静态存储区(方法区)
、栈区
和堆区
。
静态存储区(方法区)
:主要存放静态变量
。
这块「内存」在程序编译时就已经分配好了,
并且在程序整个运行期间都存在。栈区
:当方法被执行时,
方法体内的局部变量(包括基础数据类型、对象的引用)都在栈上创建,
并在方法执行结束时,
这些局部变量所持有的内存将会自动被释放。
因为栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。堆区
:又称动态内存分配,通常就是指程序运行时直接 new 出来的内存,也就是对象的实例,这部分「内存」在不使用时将会被 Java 垃圾回收器来负责回收。JVM会将它所管理的内存划分为 线程私有数据区 和 线程共享数据区两大类:
- **`线程私有数据区`**包含:
- **`程序计数器`**:是当前线程所执行的字节码的行号指示器
- **`虚拟机栈`**:是Java方法执行的内存模型
- **`本地方法栈`**:是虚拟机使用到的Native方法服务
- **`线程共享数据区`**包含:
- **`Java堆`**:用于存放几乎所有的对象实例和数组; 是垃圾收集器管理的主要区域,也被称做“GC堆”;
是Java虚拟机所管理的内存中最大的一块
-
相关阅读:
7.对于虚拟机那些个新生代和持久代的,你有看过他们的策略吗?
参考回答:
判定对象可回收有两种方法
:- **`引用计数算法`**:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。然而在主流的Java虚拟机里未选用引用计数算法来管理内存,主要原因是它难以解决对象之间相互循环引用的问题,所以出现了另一种对象存活判定算法。
- **`可达性分析法`**:通过一系列被称为『GC Roots』的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。其中可作为GC Roots的对象:虚拟机栈中引用的对象,主要是指栈帧中的本地变量、本地方法栈中Native方法引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象
回收算法有以下四种
:- **`分代收集算法`**:是当前商业虚拟机都采用的一种算法,根据对象存活周期的不同,将Java堆划分为新生代和老年代,并根据各个年代的特点采用最适当的收集算法。
- **`新生代`**:大批对象死去,只有少量存活。使用『复制算法』,只需复制少量存活对象即可。
- **`复制算法`**:把可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用尽后,把还存活着的对象『复制』到另外一块上面,再将这一块内存空间一次清理掉。
- **`老年代`**:对象存活率高。使用『标记—清理算法』或者『标记—整理算法』,只需标记较少的回收对象即可。
- **`标记-清除算法`**:首先『标记』出所有需要回收的对象,然后统一『清除』所有被标记的
- **`标记-整理算法`**:首先『标记』出所有需要回收的对象,然后进行『整理』,使得存活的对象都向一端移动,最后直接清理掉端边界以外的内存。
相关阅读:
8.Java当中判断两个对象是否相同的时候有哪些方法?
==
或者equals()
; ==
是比较两个对象在JVM中的地址
。
cequals()
是根类Obeject
中的方法,
查看源码我们可以知道默认的equals()
方法中,
首选也是直接调用==
,比较对象地址: 当然真正使用的时候,我们需要在自定义类中对equals()
进行重载,
从而能使重载后的equals()
除了==
的判断作用之外,
还可以判断两个对象中具体各成员的值或者构造
是否相同;
而基本数据类型的实例
就不用我们费心了,JDK中已经重载好了。
相关阅读:
1. 能不能在子线程里面做UI更新(界面更新)?为什么?
如此,既然UI不能上锁,非线程安全,
那自然是只能有一个线程有权操作它,
在整个程序中,只能有一个线程,那主线程(UI线程)自然当仁不让了。
2.有没有遇到过内存泄漏的场景?
- 类的静态变量持有大数据对象 静态变量长期维持到大数据对象的引用,阻止垃圾回收。
- 非静态内部类的静态实例
非静态内部类会维持一个到外部类实例的引用,
如果非静态内部类的实例是静态的,
就会间接长期维持着外部类的引用,阻止被回收掉。
- 资源对象未关闭
资源性对象如Cursor、File、Socket,应该在使用后及时关闭。
未在finally中关闭,会导致异常情况下资源对象未被释放的隐患。
- 注册对象未反注册
未反注册会导致观察者列表里维持着对象的引用,阻止垃圾回收。
- Handler临时性内存泄露
Handler通过发送Message与主线程交互,
Message发出之后是存储在MessageQueue中的,
有些Message也不是马上就被处理的。
在Message中存在一个 target,是Handler的一个引用,
如果Message在Queue中存在的时间越长,就会导致Handler无法被回收。
如果Handler是非静态的,则会导致Activity或者Service不会被回收。
由于AsyncTask内部也是Handler机制,同样存在内存泄漏的风险。
此种内存泄露,一般是临时性的。
3.Android中对于多个Activity之间的通讯,你是怎么做的?
service
和broadcast
都作为多个Activity之间通讯
的媒介
;
所有绑定了媒介service
的一系列Activity
,
都可以把需要处理的数据
传给service
;
经过service
中子线程的sendBroadcast()
之手,
以广播的方式(把处理完毕的数据参数),
发送到同系列(绑定了同样的媒介service
,注册了同样的媒介Receiver
)的
各个Activity
手中(包括发送待处理数据参数的Activity
自身);- 各个`Activity`通过`绑定媒介service`,调用`service`中的`方法`, 把
结合以上模型图以及下面这篇博文可以进一步详细理解;
4.Activity的生命周期是什么样子的?
- onCreate()表示Activity 正在创建,常做初始化工作,如setContentView界面资源、初始化数据
- onStart()表示Activity 正在启动,这时Activity 可见但不在前台,无法和用户交互
- onResume()表示Activity 获得焦点,此时Activity 可见且在前台并开始活动
- onPause()表示Activity 正在停止,可做 数据存储、停止动画等操作
- onStop()表示activity 即将停止,可做稍微重量级回收工作,如取消网络连接、注销广播接收器等
- onDestroy()表示Activity 即将销毁,常做回收工作、资源释放
- 另外,当Activity由后台切换到前台,由不可见到可见时会调用onRestart(),表示Activity 重新启动
5.具体的场景,横竖屏切换的时候,Activity的生命周期是什么样子的?
当非人为终止Activity时, 比如系统配置发生改变时导致Activity被杀死并重新创建、资源内存不足导致低优先级的Activity被杀死, 会调用 onSavaInstanceState()来保存状态。 该方法调用在**
onStop
**之前,但和**onPause
**没有时序关系。 Activity被重新创建时会调用onRestoreInstanceState(该方法在onStart之后), 并将onSavaInstanceState保存的Bundle对象作为参数传到onRestoreInstanceState与onCreate方法。
- 竖(横)屏启动: onCreate -->onStart-->onResume
- 切换横(竖)屏:
onPause -->onSaveInstanceState -->onStop -->onDestroy -->onCreate-->onStart -->
onRestoreInstanceState-->onResume
(Android 6.0 Android 7.0 Android 8.0)
android:configChanges="orientation"
- 竖(横)屏启动:: onCreate -->onStart-->onResume
- 切换横(竖)屏:
- onPause -->onSaveInstanceState -->onStop -->onDestroy -->onCreate-->onStart --> onRestoreInstanceState-->onResume
(Android 6.0)
- onConfigurationChanged-->onPause -->onSaveInstanceState -->onStop -->onDestroy -->
onCreate-->onStart -->onRestoreInstanceState-->onResume
(Android 7.0)
- onConfigurationChanged
(Android 8.0)总结:
设置了configChanges属性为orientation之后,Android6.0 同没有设置configChanges情况相同;
Android 7.0则会先回调onConfigurationChanged方法,剩下的流程跟Android 6.0 保持一致;
Android 8.0 则只是回调了onConfigurationChanged方法。3.AndroidManifest设置了configChanges
常用的排序算法有哪些,各自的时间复杂度是怎么样的?
参考:如下表:
我看你学过一点神经网络对吧?
(简书之前因为导师鞭策,写了不少关于Python和机器学习的文章)
参考:根据自身所掌握的知识回答,自然是了解多少答多少, 内容可以涉及神经网络,神经网络节点,激励函数,节点的输入输出关系等等,有条件的同学可以讲一下TensorFlow。
再接再厉