应聘职位:Android开发工程师
复习概要
1.Java
-
类(class)
在Java中,类文件是以.java为后缀的代码文件,在每个类文件中最多只允许出现一个public类,当有public类的时候,类文件的名称必须和public类的名称相同,若不存在public,则类文件的名称可以为任意的名称(当然以数字开头的名称是不允许的)。
在类内部,对于成员变量,如果在定义的时候没有进行显示的赋值初始化,则Java会保证类的每个成员变量都得到恰当的初始化:
1)对于 char、short、byte、int、long、float、double等基本数据类型的变量来说会默认初始化为0(boolean变量默认会被初始化为false);
2)对于引用类型的变量,会默认初始化为null。
如果没有显示地定义构造器,则编译器会自动创建一个无参构造器,但是要记住一点,如果显示地定义了构造器,编译器就不会自动添加构造器。注意,所有的构造器默认为static的。
3) 类的加载顺序
static 代码块->类属性->类构造函数
-
面向对象三大特性
封装:简单的说就是将一个类针对属性和操作进行封装。
继承:extends ,子类继承父类。
<pre><code>
public class Person {
private String username;//不可继承
private String password;//不可继承
protected String description;//可继承
public void eat(){ //可继承
System.out.print("start eat")
}
}
public class Man extends Person{
@override
public void eat(){//覆盖override
System.out.print("start man eat")
}
//1 和2 属于重载,其中3 返回值为int 不属于。
public void run(int mis){//1
}
public void run(int mis,String name){//2
}
public int run(){//3
}
}
</code>
</pre>
- 所有类隐示继承Object类
- 只允许单继承,同时属于统一package 下才能继承
- 继承public ,protected 关键字修饰的method 和属性, private 属于私有,无法继承
- 如果子类与父类出现属性和方法同名,需要利用super.method/属性,才能进行调用父类的属性和method,不然只能调用this 类的属性和method。
5)子类不能继承父类的构造函数。如果构造函数有参数,子类需要利用super(参数) 进行调用。 - final 修饰的类,无法重载,其中String
多态:允许不同类的对象对同一消息作出响应(多种动物,都可以eat)
1)覆盖 override(子类的方法和父类的方法一样) - 父类引用指向子类
<pre><code>Person person=new Man() </code></pre> - 重载 overload 一个类中的多个同名方法(参数个数,类型) 注意:返回类型不同,不属于重载。
-
Collection 和Map 集合
- collection三大分类:List(有序)和Set(无序),Queue(队列)
- List 常见子类:ArrayList(线程不同步,线程不安全,数组的形式实现),Vector(线程安全,线程同步),LinkedList(线程不安全,链表的形式实现)
3)Set 常见集合:HashSet,TreeSet,EnumSet, 利用迭代器(Iterator)进行遍历
4)map<K,V>常见类:HashMap(线程不安全),HashTable(线程安全)
-
线程(Runnable,Thread)
线程:程序执行的最小单元,有五种状态:新建,就绪,执行,等待,死亡。
- 接口Runnable
<pre><code>public void run() 方法 执行线程</code></pre> - 线程生命周期
<pre><code>new->start()->run()->阻塞(join(),sleep(),wait())->死亡</code></pre>
- 创建线程
<pre><code>
//方式1
new Thread(){
public void run(){
//code
}
}.start();
//方式2
public class MyThread implements Runnable{
public void run(){
//code
}
}
MyThread mythread=new MyThread();
new Thread(mythread).start();
</code></pre>
-
系统线程池
ExecutorService接口(java 自带)- CachedThreadPool 缓冲的线程池,
- FixedThreadPool 自定线程数的线程池
- SingleThreadExecutor 单线程池
- ScheduledThreadPool 定时线程池
线程与进程的区别
线程像是进程的单元细胞。一个进程至少有一个线程,进程有单独的地址空间,然后线程没有。
-
异常(Throwable)
- 异常分类:Error 和Exception
<pre><code> Error : StackOverFlowError(栈溢出),OutOfMemoryError(内存溢出)</pre></code>
Exception:
<pre><code>
RuntimeException(ArrayIndexOutOf,NullPointer,Arrithmetic,
ClassNotFound,IIlegalArgument,UnkownType)
非运行时异常 (IO, SQL)</code>
</pre> - 异常处理
关键字 try catch finally
<pre><code>
try{
// code
}catch(异常类){
// code
}finally{
//无论发生何种异常最后都会走finally 代码块中
}
</code></pre> - throws 关键字 和throw 关键字
throws 用得最多的是将dao 层中的异常抛到service 中进行处理
<pre><code>
dao层: public void getUser() throws Exception
service层:
try{
dao.getUser();
}catch(Exception e){
//code;
}
</code></pre>
throw 关键字主要用于代码块
<pre><code>
if(条件){
throw new CustomException("message");
}
</code></pre> - 自定义异常
自定义异常,CustonException extends Exception
-
String 类
- 为何定义为不变类
String 为何为final 类,需要从几个方法进行讨论
1.字符串常量池的需要
引用->内存->常量池
<pre><code>
String a="abc";
String b="abc";
String c= new String("abc")
if (a==b) // true
if (a==c) // false
if (a==c.intern()) //true
</code></pre>
2.运行缓存HashCode
能够保证hashCode的唯一性,保证String在java 中频繁使用的高性能。
3.安全性
不变模式可以挺高多线程程序的性能,降低多线程程序复杂度,保证多线程下的安全性。 - StringBuffer ,StringBuilder String 三者的区别
<pre><code>
String a="abc"+"def"+"yes"; //1
StringBuffer b="abc".append("def").append("yes");//2 性能高于1
</code></pre>StringBuffer 线程安全
StringBuilder 线程不安全,单线程下StringBuilder 多字符串操作性能最好。
3)==与equals方法 区别
<pre><code>
String a="abc";
String b=new String("abc")
if(a==b) //false 比较的是引用所指向的对象内存地址
if(a.equals(b)) //true 比较两者的value
</code></pre>
-
JVM
- 内部结构
Runtime Data Area(运行区):
1.程序计数器:线程私有
2.本地方法栈:线程私有
3.虚拟机栈:线程私有
4.方法区:线程共享
5.堆区:线程共享
ClassLoader(类加载器):将class 文件装载到方法区
Execution Engine(执行引擎) :执行.class文件指令
NativeInterface(本地接口):与native libraries交互
-
四大引用
1)强引用: 一直不会回收,内存不足时,出现OOM
2)软引用: 当内存不足时,GC进行回收
3)弱引用: 无论内存是否足够,只要看点是弱引用,都进行回收
4)虚引用: 只是跟踪对象回收活动的一个监视队列
-
设计模式
1)单例模式(最优方式:内部类方式)
<pre><code>
public class Singleton{
private Singleton(){
}
private static class SingletonHodler{
private static Singleton instance=newSingleton();
public static Singleton getInstance(){
return SingletonHodler.instance;
}
}
}
</code></pre>
2)工厂模式
3)观察者模式
-
常见陷进
- if-else
<pre><code>
if(true) System.out.println("if");
else System.out.println("else");
//编译,执行,打印输出 if 。可见else 并没有执行
</code></pre> - static ,final
<pre><code>
class Ideone
{ static int i=10;
final static int a=10;
public void run(){
i++;
}
public void run1(){
//a++;出现编译错误,因为final 修饰的是不可以进行更改的
//static int i=0; 在方法中定义static 变量,IntellijIDEA 中直接报错,所有在方法定义是不被允许的。
}
public static void main (String[] args)
{ Ideone ido=new Ideone();
ido.run();
//ido.run1();
System.out.println(i);
}
}
//打印输出为11. 其实一直都认为static 是保持不变,但是打印出来一直是一种错觉。
</code></pre> - 引用传值
<pre><code>
/Created by Darker on 15/8/11. /
public class Test360_2 {
int x=6;
int y=7;
public static void main(String[] args){
Test360_2 test360_2=new Test360_2();
print(test360_2.x,test360_2.y);
System.out.println(test360_2.x);
System.out.println(test360_2.y);
}
static void print(int x,int y){
x++;
y++;
System.out.println(x);
System.out.println(y);
}
}
//打印输出7 8 , 6 7 可以看出,类属性的值x.y并没有改变
</code></pre> - break;return;continue;finally
<pre><code>
/** * Created by Darker on 15/8/11. */
public class Test360_2 {
public static void main(String[] args){
print();
}
static void print(){
try {
return ;
}
catch (Exception e){
}
finally {
System.out.println("yes");
}
}
}
//这行代码打印出yes,也就证明无论如何return也好,都要执行finally 代码块
</code></pre>break 则是直接跳出循环
continue 则是跳出本次循环,进行下一次循环 - ++i;i++
- 函数执行顺序
- for循环中的表达式
- 其他题目见到了再总结出来
2.Android
-
Android 四个组件
1)Activity:活动
1.生命周期
<pre><code>
onCreate() //1 创建activity
onStart() //2 启动activity
onResume() //3 可见
onPause() //4 可见
onStop() //5 停止
onDestory() //6 销毁
onRestart() //7 stop()->onRestart()再次onStart()
</code></pre>
2.context
Activity Context上下文,生命周期与activity一致,两者共存亡,用于加载获取资源,而Application Context 生命周期与整个应用一致,频繁的使用App-Context 容易造成OOM。
3.注册方式
在AndroidManifest.xml 中进行注册
<pre><code>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="xxx.com.xxx" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
</activity>
</application>
</manifest>
</code></pre>
2)Service:服务
1.生命周期
其生命周期与其启动方式密不可分。startService()和bindService() 启动方式,start 方式:其生命周期与应用无关,bind 方式:则是与应用共存亡。
2.与其他组件交互方式
与Activity 通信:binder方式,handlerMessage方式,AIDL方式,static方式
3)BroadcastReceiver:广播
用于异步接收广播Intent,便于各应用/组件进行交互。
基本属性
<pre><code>
1.继承BroadcastReceiver 实现 onReceive 方法
2.指定 action 一种是manifast文件中设置,一直是intent 设置
3.注册
</code></pre>
注册两种方式
<pre><code>
1.AndroidManifest.xml 中静态注册
在应用推出之后依然可以接受到广播消息,造成耗电量,cpu等损耗。
2.代码中 intent.registerReceiver(自定义的Receiver,filter);
这种方式,随着注册的方式,以及灵动性。应用退出,
必须进行unregisterReceiver(自定义的Receiver) 取消绑定
</code></pre>
4)ContentProvider:内容提供器
维护应用数据,方便应用本身或其它应用访问
-
Handler机制
在为了避免Android ANR (Application Not Response 超过5s ,未响应), 一般出现网络请求或者加载大量数据的时候,都会开启子线程,去完成这些任务,UI线程继续加载view,接受用户操作。 等待(子线程)网络请求结束,这时候需要进行对UI线程通信,进行页面刷新,于是就需要用到handler。
1.首先阐述一下handler 机制:Thread,Looper,MessageQueue
Looper 为一个循环,接收handler 发送的message,然后放到MessageQueue中, 等待条件(线程结束),handler 再通过looper从messageQueue中取出message ,处理对应的handler 刷新UI线程。
- 创建方式
<pre><code>
//创建
Handler handler=new Handler(){
public void handlerMessage(Message msg){
//code
}
};
//发送消息,方式1 ,此方式性能上来说比方式2要高效,
//原因是由于此方式从Message池获取message 对象,避免了重复分配新对象
Message msg=handler.obtainMessage();
msg.what=1;
msg.obj=xxx;
msg.sendToTarget();
//发送消息,方式2
Message msg=new Message();
msg.what=1;
msg.obj=xxx;
handler.sendMessage(msg);
</code></pre>
-
AsyncTask 机制
其实这也是一种异步加载的方式,由于在之前的项目中使用不是太多,具体了解不是很够。然后应用asyncTask必须记住几条准则
<pre><code>
1.Task的实例必须在UI thread中创建;
2.execute方法必须在UI thread中调用;
3.不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法;
4.该task只能被执行一次,否则多次调用时将会出现异常;
</code></pre>代码实现
<pre><code>
public class MyAsyncTask extends AsyncTask{
@override
onPreExecute(){
//操作前
}
@override
onPostExecute(){
//再次刷新UI
}
@override
doinBackground(){
//后台代码执行
}
@override
onProgressUpdate(){
//进度更新
}
@override
onCancelled(){
//用户取消操作
}
}
//UI thread
MyAsyncTask task=new MyAsyncTask();
task.execute();
</code></pre>
-
数据存储
1)轻量级数据库sqlite
1.创建数据库
<pre><code>
public class MYSQLiteHelper extends SQLiteOpenHelper {
//构造函数 parm:context
@override
onCreate() //创建
@override
onUpgrade() //更新版本
}
//调用数据库
MYSQLiteHelper helper=new MYSQLiteHelper(this)
SQLiteDatabase db=helper.getWritableDatabase();//可写
//操作数据库,有很多封装好的方法,可以调用这里就不一一说明
String sql="sql语句";
db.execSQL(sql);
</code></pre>
2.曾经使用XUtils 工具类中对object 完成ORM 印射到数据库,使用效率,操作都是很方便的,不凡拿来试用一下。
2)SharedPreference
这个简单的存储<code>用户设置级</code>数据。
3)File
1.内存卡
2.外置SD卡
-
网络请求
网络请求在应用中是一块很重要的组成部分。
1)com.net.HttpConnection
//post方式, get方式
<pre><code>
public void httpRequestWithPost(URL url){
//1 打开链接,接下来可以写一下简单的属性设置
httpConnection=url.openConnection();
httConnection.setRequestMethod("Post");//默认为get
//2 建立链接
httpConnection.connect();
//3 发送请求,记得关闭数据流
DataOutputStream dos = new DataOutputStream(httpConnection.getOutputStream());
dos.write(postData);
dos.flush();
dos.close();
//4 数据处理
httpConnection.getResponseCode ==200 {
//code 处理获取流
}
}
</code></pre>
2)org.apache.http.HttpPost/HttpGet
1.HttpPost
<pre><code>
HttpPost post=new HttpPost(url);
//添加参数
List<NameValuePair> params =newArrayList<NameValuePair>();
params.add(new BasicNameValuePair("id", "helloworld"));
//设置字符集
HttpEntity entity = new UrlEncodedFormEntity(params, HTTP.UTF_8);
//设置参数实体
httpPost.setEntity(entity);
//获取HttpClient对象
HttpClient httpClient = new DefaultHttpClient();
// 获取HttpResponse实例
HttpResponse httpResp = httpClient.execute(httpPost);
//最后进行判断 code==200 处理数据
String result = EntityUtils.toString(httpResp.getEntity(), "UTF-8");
</code></pre>
2.HttpGet
<pre><code>
HttpGet httpGet = new HttpGet(url);
// 获取HttpClient对象
HttpClient httpClient = new DefaultHttpClient();
// 获取HttpResponse实例
HttpResponse httpResp = httpClient.execute(httpGet);
// 判断是够请求成功
if(httpResp.getStatusLine().getStatusCode() == HTTP_200)
{
//获取返回数据
String result = EntityUtils.toString(httpResp.getEntity(), "UTF-8");
}
</code></pre>
- 其他网络请求方式
1.其实用得最多的就是Google 出得第三方Volley,原因是由于项目中经常用到json 数据格式,volley 封装的很好,针对请求线程处理封装,回调很不错,我也将其封装到自己的框架中作为网络处理模块的utils。
2.ImageLoader 用于加载图片的一个工具类,其实功能上还是很强大的。但是由于之前的项目中加载多张图片,出现闪烁的情况。所以决定放弃使用这个第三方,最后还是用Volley加载图片的方式进行解决。
3.当然还有一些处理FTP 网络请求的。具体不记得了。
-
数字签名
之所以写这个问题,是因为在平时发布apk 的时候,是需要一个签名,但是并没有研究这个签名,在android系统中的作用和几个要点。
1)所有应用都需要数字证书,android系统不会接受一个没有数字证书的应用
2)Android 程序包有自带的数字签名,不需要一个有权威的认证机构
3)发布android需要一个合适的私钥来生成数字签名,不能利用打ant等插件进行发布
4)数字签名的有效期,android系统只会在安装应用的时候,检查数字签名的有效期,当安装之后,应用到期依然可以在系统中使用。
-
Activity 启动4 种方式
之所以出来总结一下这几个点,是因为在平时做项目中要用到。在注册activity 时候,需要在AndroidManifast.xml去配置 android:lanuchMode="xxx"
- Standared 模式,也是默认模式。一旦activity被创建都会进入到工作栈中。
- SingleTop模式,需要判断当前activity 是否到栈顶,如果在则不需要重新产生实例,不在则重新创建实例。
- SingleTask模式,这个模式相当霸道,如果本身存在在工作栈中,则将其他处于他之上的activity 全部移除,使自己处于栈顶,没有存在工作栈中,则重新创建,push 到栈顶。
4)SingleInstance模式,保证工作栈中只有一个实例存在,在调用时会移动到栈顶。
-
XML布局中常见模糊的属性
- magin 和padding 的区别
<pre><code>
Padding 为内边框,指该控件内部内容,如文本/图片距离该控件的边距
Margin 为外边框,指该控件距离边父控件的边距
</code></pre> - ListView item 分割线
<pre><code>
android:divider="#fffff" 分割线颜色 value="@null" 为去掉分割线
android:dividerHeight="1px" 分割线高度
</code></pre> - ImageView 中的android:scaleType 属性
<pre><code>
CENTER:超过布局,图片裁剪中心,填充布局。
CENTER_CROP:按比例放大,填充整个布局居中显示。
CENTER_INSIDE:按比例缩小,内容完全显示。
FIT_CENTER:把图片按比例扩大/缩小到View的宽度,居中显示
FIT_XY:不按比例缩放,显示填充整个布局。
</code></pre>
-
Android 推送
之前项目中也是用到推送(极光推送),推送有两种方式.
- pull 方式:客户端轮询发送请求,检查服务器数据是否更新从而实现推送的效果,这种方式由于客户端进行轮询,所以耗资源,电量,流量都很多,但是这个方式,实现起来相对简单。
- push方式:服务器进行推送数据,基于tcp 长连接的方式,客户端利用AlarmManager 进行定时发送心跳包,来维持与服务器的连接。
-
常见Android 异常,及其优化
经过两年多的android开发,大大小小的异常以及bug,见过很多.
- 忘记在AndroidManifest.xml 中 注册activity, service,user-permission 等
- 生命周期应用不准确,导致activity 或者service 销毁,出现处理异常
- 多线程处理数据的时候,造成数据紊乱。
- 未获取view 的id, 使用view 造成空指针错误
- listview 加载数据的时候出现错位
- 控件焦点问题
- 手势冲突
- 图片处理,缓存
- Touch 事件传递
- handler 可能造成内存溢出
实在太多,具体情况,还需要在项目中进行总结。
3.数据结构和算法
-
常见排序算法
其实,几种基本的算法,在平时项目中并没有使用,经常是使用系统封装好的一些类,来进行操作,如果不去参考源码,根本不知道里面的原委,也不了解这几种常见的排序算法,所存在的作用。
- 快速排序
- 选择排序
- 插入排序
- 冒泡排序
- 希尔排序
- 堆排序
-
链表
-
数组
-
队列
-
树
-
图
-
哈希
4.Linux
5.计算机网络
-
IP协议
生活中,经常听到ip 地址这个概念。这个属于网络层的协议,那么IP分为哪几类呢,也是常考选择题之一。
- A 类
<pre><code>
地址范围 1.0.0.1-126.255.255.254
</code></pre>2) B 类
<pre><code>
地址范围 128.1.0.1-191.254.255.254
</code></pre>3) C 类
<pre><code>
地址范围 192.0.1.1-223.255.255.254
</code></pre>4) D 类
<pre><code>
地址范围 224.0.0.1-239.255.255.254
</code></pre>5) E 类
<pre><code>
E类地址保留,仅作实验和开发用
</code></pre>
-
TCP/UDP协议
还记得第一次听到这两个概念,还是在大三的计算机网络的课程上,然而并没有在意,以至于在大三腾讯实习生,面试官问到这个问题,表达的一塌糊涂。
- TCP
TCP 属于计算机网络结构中的传输层,"三次握手"的基于连接方式,需要消耗的系统资源要求要多一些。 - UDP
UDP 同样属于传输层,基于无连接的方式,传递数据,以不靠谱的状态,不保证数据的完整,消耗着系统较少的资源。
-
Http协议
- Http超文本传输协议
在网络传输过程当中,我们用得最多便是http 协议,如今许多公司已经开始支持https,但是我们不由的还是会选择http,一直也是面试官常常提到的一些问题所在,所以来谈谈这个http协议。
http请求体
<pre><code>
//请求行
Method URL Version{
Method:GET,POST,DELETE,PUT,HEAD 等
URL:请求的url
Version:http 的版本(目前1.1)
}
//请求头
Headers{
Accept-Language: 语言
Accept-Charset: 支持字符编码
Accept: 请求类型(image/txt等)
Accept-Encoding: 浏览器支持的压缩类型
User-Agent: 代理(浏览器等)
Host: 域名
Connection: Keep-Alive(持久链接)
}
//请求体
Message Body{
//发送的一些请求数据
}
</code></pre>
http响应体
<pre><code>
//状态行
Version Status-Code Description{
version:http的版本(1.1)
Status-Code:响应状态码{
常见:200(请求成功),500(服务器错误),404(文件不存在)
1xx:
2xx:
3xx:
4xx:
5xx:
}
Description{
描述
}
}
//消息报头{
Date:
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 122
.
}
//响应正文
MessageBody{
一些xml格式,或者json格式
}
</code></pre>
-
Get和Post 区别
- GET 方式:在刚刚进入到it 界的时候,那时,只会get 方式,也只会将参数放在url 上面进行传递,后面发现参数过多,get方式(MAX=2048个字符,虽然这个数字网上大部分这么说,但是不能保证,必须自己求情服务器的时候去测一测)无法满足需求的时候,才研究到了post(人呆板),当然get 也可以在http 种的cookie 中进行传参。
<pre><code>
host:port/test/test.do?name1=value1&name2=value2</code></pre> - POST 方式:直观上讲,post 方式,是将参数内容,放到了http 请求体的body 里面,这样来说参数内容并没有保留在浏览器的请求链接上,从安全性来讲是略胜一筹。然后无论是post 多少参数都可以,是没有参数大小限制。
<pre><code>
POST /test/test.do
HTTP/1.1
Host: xxx.xxx.xxx.xx
data={
json 串
}
</code></pre>