2016校招题

2016校招题目

J2EE 部分

Switch能否用string做参数?

  在 Java 7  之前, switch 只能支持byte,short,char,int 或者其对应的封装类以及 Enum 类型。在JAVA 7中,String 支持被加上了。

 

equals与==的区别:

  ==是判断两个变量或实例是不是指向同一个内存空间 

equals是判断两个变量或实例所指向的内存空间的值是不是相同

 

Object有哪些公用方法?

  方法equals测试的是两个对象是否相等

  方法clone进行对象拷贝

  方法getClass返回和当前对象相关的Class对象

方法notify, notifyall, wait都是用来对给定对象进行线程同步的

 

Java的四种引用,强弱软虚,用到的场景

  强引用:如果一个对象具有强引用,它就不会被垃圾回收器回收。即使当前内存空间不足,JVM 也不会回收它,而是抛出 OutOfMemoryError 错误,使程序异常终止。如果想中断强引用和某个对象之间的关联,可以显式地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象

  软引用:在使用软引用时,如果内存的空间足够,软引用就能继续被使用,而不会被垃圾回收器回收,只有在内存不足时,软引用才会被垃圾回收器回收。

  弱引用:具有弱引用的对象拥有的生命周期更短暂。因为当 JVM 进行垃圾回收,一旦发现弱引用对象,无论当前内存空间是否充足,都会将弱引用回收。不过由于垃圾回收器是一个优先级较低的线程,所以并不一定能迅速发现弱引用对象

  虚引用:顾名思义,就是形同虚设,如果一个对象仅持有虚引用,那么它相当于没有引用,在任何时候都可能被垃圾回收器回收。

 

使用场景:

  利用软引用和弱引用解决OOM问题:用一个HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题

通过软引用对象重获方法实现Java对象的高速缓存:比如我们创建了一Employee的类,如果每次需要查询一个雇员的信息。哪怕是几秒钟之前刚刚查询过的,都要重新构建一个实例,这是需要消耗很多时间的。我们可以通过软引用和 HashMap 的结合,先是保存引用方面:以软引用的方式对一个Employee对象的实例进行引用并保存该引用到HashMap 上,key 为此雇员的 id,value为这个对象的软引用,另一方面是取出引用,缓存中是否有该Employee实例的软引用,如果有,从软引用中取得。如果没有软引用,或者从软引用中得到的实例是null,重新构建一个实例,并保存对这个新建实例的软引用

 

Hashcode的作用,与 equal 有什么区别

同样用于鉴定2个对象是否相等的,java集合中有 list 和 set 两类,其中 set不允许元素重复实现,那个这个不允许重复实现的方法,如果用 equal 去比较的话,如果存在1000个元素,你 new 一个新的元素出来,需要去调用1000次 equal 去逐个和他们比较是否是同一个对象,这样会大大降低效率。hashcode实际上是返回对象的存储地址,如果这个位置上没有元素,就把元素直接存储在上面,如果这个位置上已经存在元素,这个时候才去调用equal方法与新元素进行比较,相同的话就不存了,散列到其他地址上

 

String、StringBuffer与StringBuilder的区别

  String 类型和 StringBuffer 类型的主要性能区别其实在于 String 是不可变的对象

  StringBuffer和StringBuilder底层是 char[]数组实现的

StringBuffer是线程安全的,而StringBuilder是线程不安全的

 

Override和Overload的含义去区别

  Overload顾名思义是重新加载,它可以表现类的多态性,可以是函数里面可以有相同的函数名但是参数名、返回值、类型不能相同;或者说可以改变参数、类型、返回值但是函数名字依然不变。

  Override就是ride(重写)的意思,在子类继承父类的时候子类中可以定义某方法与其父类有相同的名称和参数,当子类在调用这一函数时自动调用子类的方法,而父类相当于被覆盖(重写)了。

 

抽象类和接口的区别

  一个类只能继承单个类,但是可以实现多个接口

  接口强调特定功能的实现,而抽象类强调所属关系

  抽象类中的所有方法并不一定要是抽象的,你可以选择在抽象类中实现一些基本的方法。而接口要求所有的方法都必须是抽象的

 

解析XML的几种方式的原理与特点:DOM、SAX、PULL

  DOM:消耗内存:先把xml文档都读到内存中,然后再用DOM API来访问树形结构,并获取数据。这个写起来很简单,但是很消耗内存。要是数据过大,手机不够牛逼,可能手机直接死机

  SAX:解析效率高,占用内存少,基于事件驱动的:更加简单地说就是对文档进行顺序扫描,当扫描到文档(document)开始与结束、元素(element)开始与结束、文档(document)结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。

PULL:与 SAX 类似,也是基于事件驱动,我们可以调用它的next()方法,来获取下一个解析事件(就是开始文档,结束文档,开始标签,结束标签),当处于某个元素时可以调用XmlPullParser的getAttributte()方法来获取属性的值,也可调用它的nextText()获取本节点的值。

 

wait()和sleep()的区别

1>sleep来自Thread类,和wait来自Object类

2>调用sleep()方法的过程中,线程不会释放对象锁。而 调用 wait 方法线程会释放对象锁

3>sleep睡眠后不出让系统资源,wait让出系统资源其他线程可以占用CPU

4>sleep(milliseconds)需要指定一个睡眠时间,时间一到会自动唤醒

 

JAVA 中堆和栈的区别,说下java 的内存机制

  基本数据类型比变量和对象的引用都是在栈分配的

  堆内存用来存放由new创建的对象和数组

  类变量(static修饰的变量),程序在一加载的时候就在堆中为类变量分配内存,堆中的内存地址存放在栈中

  实例变量:当你使用java关键字new的时候,系统在堆中开辟并不一定是连续的空间分配给变量,是根据零散的堆内存地址,通过哈希算法换算为一长串数字以表征这个变量在堆中的"物理位置”,实例变量的生命周期--当实例变量的引用丢失后,将被GC(垃圾回收器)列入可回收“名单”中,但并不是马上就释放堆中内存

局部变量: 由声明在某方法,或某代码段里(比如for循环),执行到它的时候在栈中开辟内存,当局部变量一但脱离作用域,内存立即释放

 

JAVA多态的实现原理 

  抽象的来讲,多态的意思就是同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)

实现的原理是动态绑定,程序调用的方法在运行期才动态绑定,追溯源码可以发现,JVM 通过参数的自动转型来找到合适的办法。

 

JAVA 垃圾回收机制

  什么是垃圾回收机:释放那些不再持有引用的对象的内存

 

怎么判断一个对象是否需要收集?

  引用计数(最简单古老的方法):指将资源(可以是对象、内存或磁盘空间等等)的被引用次数保存起来,当被引用次数变为零时就将其释放的过程

  对象引用遍历(现在大多数 jvm 使用的方法):对象引用遍历从一组对象开始,沿着整个对象图上的每条链接,递归确定可到达(reachable)的对象。如果某对象不能从这些根对象的一个(至少一个)到达,则将它作为垃圾收集

 

几种垃圾回收机制 

  标记回收法:遍历对象图并且记录可到达的对象,以便删除不可到达的对象,一般使用单线程工作并且可能产生内存碎片

  标记-压缩回收法:前期与第一种方法相同,只是多了一步,将所有的存活对象压缩到内存的一端,这样内存碎片就可以合成一大块可再利用的内存区域,提高了内存利用率

  复制回收法:把现有内存空间分成两部分,gc运行时,它把可到达对象复制到另一半空间,再清空正在使用的空间的全部对象。这种方法适用于短生存期的对象,持续复制长生存期的对象则导致效率降低。 

  分代回收发:把内存空间分为两个或者多个域,如年轻代和老年代,年轻代的特点是对象会很快被回收,因此在年轻代使用效率比较高的算法。当一个对象经过几次回收后依然存活,对象就会被放入称为老年的内存空间,老年代则采取标记-压缩算法

 

讲讲 Java 中的集合有多少种,区别是什么?

  ArrayList、LinkedList、Vector的区别:ArrayList 和Vector底层是采用数组方式存储数据,Vector由于使用了synchronized方法(线程安全)所以性能上比ArrayList要差,LinkedList使用双向链表实现存储,随机存取比较慢

  HashMap的底层源码实现:当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。

  Fail-Fast机制:在使用迭代器的过程中有其他线程修改了map,那么将抛出ConcurrentModificationException,这就是所谓fail-fast机制。这一机制在源码中的实现是通过modCount域,modCount顾名思义就是修改次数,对HashMap内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的expectedModCount。在迭代过程中,判断modCount跟expectedModCount是否相等,如果不相等就表示已经有其他线程修改了Map.

 

  HashMap和 HashTable 的区别:

  HashTable比较老,是基于Dictionary 类实现的,HashTable 则是基于 Map接口实现的

  HashTable 是线程安全的, HashMap 则是线程不安全的

  HashMap可以让你将空值作为一个表的条目的key或value

 

 

Android部分

注册广播有哪几种方式,有什么区别

  静态注册 & 动态注册

  两种注册类型的区别是:

     1)第一种不是常驻型广播,也就是说广播跟随程序的生命周期。

     2)第二种是常驻型,也就是说当应用程序关闭后,如果有信息广播来,程序也会被系统调用自动运行。

 

绘制 Activity 的生命流程图

onCreate->onStart->onResume->onPause->onStop->onDestroy

onStop->onRestart->onStart->onResume....

 

注册Service需要注意什么

Service分为本地服务(LocalService)和远程服务(RemoteService)。

1、本地服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,也不需要AIDL。相应bindService会方便很多。主进程被Kill后,服务便会终止。本地Service 是运行在主进程的 main 线程上的

2、远程服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。

 

注意点:

1、你应当知道在调用 bindService 绑定到Service的时候,你就应当保证在某处调用 unbindService 解除绑定(尽管 Activity 被 finish 的时候绑定会自动解除,并且Service会自动停止);

2、你应当注意 使用 startService 启动服务之后,一定要使用 stopService停止服务,不管你是否使用bindService;

3、同时使用 startService 与 bindService 要注意到,Service 的终止,需要unbindService与stopService同时调用,才能终止 Service,不管 startService 与 bindService 的调用顺序,如果先调用 unbindService 此时服务不会自动终止,再调用 stopService 之后服务才会停止,如果先调用 stopService 此时服务也不会终止,而再调用 unbindService 或者 之前调用 bindService 的 Context 不存在了(如Activity 被 finish 的时候)之后服务才会自动停止;

4、当在旋转手机屏幕的时候,当手机屏幕在“横”“竖”变换时,此时如果你的 Activity 如果会自动旋转的话,旋转其实是 Activity 的重新创建,因此旋转之前的使用 bindService 建立的连接便会断开(Context 不存在了),对应服务的生命周期与上述相同。

5、在 sdk 2.0 及其以后的版本中,对应的 onStart 已经被否决变为了 onStartCommand,不过之前的 onStart 任然有效。这意味着,如果你开发的应用程序用的 sdk 为 2.0 及其以后的版本,那么你应当使用 onStartCommand 而不是 onStart。

 

Service与Activity怎么实现通信

1>通过Binder对象

1)创建新类继承Service, 在新类中创建自定义Binder类继承自Binder

2)在Service中的onBind返回自定义Binder类对象

3)在Activity中创建ServiceConnection实例对象,并重写方法onServiceConnected

4)通过onServiceConnected方法获取自定义Binder类对象,持有Service的引用

bindService(intent, conn, Context.BIND_AUTO_CREATE);  

2>通过broadcast(广播)的形式

 

Handler通信具体到源码,是怎么实现的


 

Handler的机制

  创建消息:每一个消息都需要被指定的Handler处理,通过Handler创建消息便可以完成此功能。Android消息机制中引入了消息池。Handler创建消息时首先查询消息池中是否有消息存在,如果有直接从消息池中取得,如果没有则重新初始化一个消息实例。使用消息池的好处是:消息不被使用时,并不作为垃圾回收,而是放入消息池,可供下次Handler创建消息时使用。消息池提高了消息对象的复用,减少系统垃圾回收的次数。

发送消息:UI主线程初始化第一个Handler时会通过ThreadLocal创建一个Looper,该Looper与UI主线程一一对应。使用ThreadLocal的目的是保证每一个线程只创建唯一一个Looper。之后其他Handler初始化的时候直接获取第一个Handler创建的Looper。Looper初始化的时候会创建一个消息队列MessageQueue。至此,主线程、消息循环、消息队列之间的关系是1:1:1。Handler、Looper、MessageQueue的初始化流程如图所示:Hander持有对UI主线程消息队列MessageQueue和消息循环Looper的引用,子线程可以通过Handler将消息发送到UI线程的消息队列MessageQueue中。

处理消息:UI主线程通过Looper循环查询消息队列UI_MQ,当发现有消息存在时会将消息从消息队列中取出。首先分析消息,通过消息的参数判断该消息对应的Handler,然后将消息分发到指定的Handler进行处理。

 

怎么实现ListView多种布局?

1>继承BaseAdapter

2>重载 public View getView(int position, View convertView, ViewGroup parent) , 并在该函数里通过接口来加载不同Item的布局

3>重载BaseAdapter中的 public int getViewTypeCount() & public int getItemViewType(int position) 两个函数. 其中, 前者是用来告诉ListView有多少种Item布局, 而后者是用来告诉ListView应该根据位于position的元素返回第n(0,1,...N-1)种布局, 而ListView则根据该值来判断在调用getView时应该传入已缓存的convertView.

 

ListView与数据库绑定的实现

1>使用SimpleCursorAdapter绑定数据


2>使用SimpleAdapter绑定数据


3>使用自定义适配器绑定数据,继承自BaseAdapter

 

怎么实现一个部分更新的 ListView?

1>继承自BaseAdapter

2>需要局部更新ListView的时候,可通过判断当前可视的第一个索引值,从而计算需要更新条目的索引


3>更新数据时,不要调用notifyDataSetChanged(),调用上述方法即可

 

ListView卡顿的原因与性能优化,说的越多越好

1>Adapter的getView方法里面convertView没有使用setTag和getTag方式;

2>在getView方法里面ViewHolder初始化后的赋值或者是多个控件的显示状态和背景的显示没有优化好,抑或是里面含有复杂的计算和耗时操作;

3>在getView方法里面 inflate的row 嵌套太深(布局过于复杂)或者是布局里面有大图片或者背景所致;

4>Adapter多余或者不合理的notifySetDataChanged;

5>listview 被多层嵌套,多次的onMessure导致卡顿,如果多层嵌套无法避免,建议把listview的高和宽设置为fill_parent. 如果是代码继承的listview,那么也请你别忘记为你的继承类添加上LayoutPrams,注意高和宽都是fill_parent的;

 

Android中的动画有哪些,区别是什么

  View Animation:基于View的渐变动画,她只改变了View的绘制效果,而实际属性值未变。比如动画移动一个按钮位置,但按钮点击的实际位置仍未改变。在代码中定义动画,可以参考AnimationSet类和Animation的子类;而如果使用XML,可以在res/anim/文件夹中定义XML文件。

  Drawable Animation:加载一系列Drawable资源来创建动画,这种传统动画某种程度上就是创建不同图片序列,顺序播放,就像电影胶片。在代码中定义动画帧,使用AnimationDrawable类;XML文件能更简单的组成动画帧,在res/drawable文件夹,使用采用来定义不同的帧。感觉只能设置的属性是动画间隔时间。

  Property Animation:动画的对象除了传统的View对象,还可以是Object对象,动画之后,Object对象的属性值被实实在在的改变了。Property animation能够通过改变View对象的实际属性来实现View动画。任何时候View属性的改变,View能自动调用invalidate()来试试刷新。

 

JNI怎么使用

1>用Java编写新类,加载so库以及声明本地native方法


2>通过命令,进入到项目根目录的bin/classes/下,执行命令javah 包名.TestJNI即可生成.h头文件

3>实现JNI原生函数源文件,新建xxxx.c文件

4>编译生成so库

编译xxxx.c成so库可以和app一起编译,也可以都单独编译。

在当前目录下建立jni文件夹:根目录/jni/下建立Android.mk,并将xxxx.c和 xxxx.h 拷贝到进去,编写编译生成so库的Android.mk文件

LOCAL_PATH:= $(call my-dir)

# 一个完整模块编译

include $(CLEAR_VARS)

LOCAL_SRC_FILES:=xxxx.c

LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)

LOCAL_MODULE := libTestJni

LOCAL_SHARED_LIBRARIES := libutils

LOCAL_PRELINK_MODULE := false

LOCAL_MODULE_TAGS :=optional

include $(BUILD_SHARED_LIBRARY)

 

LOCAL_PATH - 编译时的目录

$(call 目录,目录….) 目录引入操作符

如该目录下有个文件夹名称 src,则可以这样写 $(call src),那么就会得到 src 目录的完整路径

  include $(CLEAR_VARS) -清除之前的一些系统变量

  LOCAL_MODULE - 编译生成的目标对象

  LOCAL_SRC_FILES - 编译的源文件

  LOCAL_C_INCLUDES - 需要包含的头文件目录

  LOCAL_SHARED_LIBRARIES - 链接时需要的外部库

  LOCAL_PRELINK_MODULE - 是否需要prelink处理 

  include$(BUILD_SHARED_LIBRARY) - 指明要编译成动态库

 

说说内存泄露的情况有哪些

1>查询数据库而没有关闭Cursor

2>调用registerReceiver后未调用unregisterReceiver()

3>未关闭InputStream/OutputStream

4>Bitmap使用后未调用recycle()

5>Context泄露

 

OOM是怎么引起的?怎么尽量避免 OOM 问题的出现

1、内存泄漏:当出现对Activity、View或drawable等类的对象长期持有无用的引用,就会造成被引用的对象无法在GC时回收,而是长期占用堆空间,此时就会发生内存泄漏。简单来说,就是保留下来却永远不再使用的对象引用。

2、内存溢出:如果应用程序在消耗光了所有的可用堆空间(16M到48M),那么再试图在堆上分配新对象时就会引起OOM(OutOf Memory Error)异常,此时应用程序就会崩溃退出。

3、两者的区别:简单的说,就是内存溢出是占用内存太大,超过了其可以承受的范围;内存泄漏是回收不及时甚至是没有被回收,而在推空间中产生的许多无用的引用。于是过多的内存泄漏就会导致内存溢出,从而迫使程序崩溃退出。

 

1>持有Context引用造成的泄漏:尽量使用Application这种Context类型, 注意对Context的引用不要超过它本身的生命周期,慎重的对Context使用“static”关键字,Context里如果有线程,一定要在onDestroy()里及时停掉。 

2>线程之间通过Handler通信引起的内存泄漏:Android中线程之间进行通信时最常用的作法是通过接收消息的目标线程所持有Handler对象来创建Message对象,然后再向目标线程发送该Message。在目标线程中Handler在执行handleMessage()时会根据相应Message来执行相应不同功能。另外一种作法是通过Handler对象向目标线程直接发送Runnable对象来执行该Runnable对象中不同的功能代码。在通过Handler进行通信时如果不注意,也很有可能引起内存泄漏。在sendMessage完成之后显示的将msg成员变量置为null,并且在退出整个应用程序之前,将handler置为null。

3>将变量的作用域设置为最小:最小化作用域意味着对垃圾收集器更快的可见。变量定义在方法级中,当方法执行完毕后,该变量不再被引用,可以被垃圾回收。但若变量是类级的,垃圾回收器就需要等待直到对该类的所有的引用都被移除后才能进行垃圾回收。

4>构造Adapter时,没有使用缓存的convertView:不去使用convertView,而是每次都在getView()中重新实例化一个View对象的话,即浪费时间,也造成内存垃圾,给垃圾回收增加压力

5>Bitmap对象不再使用时调用recycle()释放内存:Bitmap对象比较占内存,Bitmap对象在不使用时,我们应该先调用recycle()释放内存,然后才它设置为null。虽然recycle()从源码上看,调用它应该能立即释放Bitmap的主要内存,但是测试结果显示它并没能立即释放内存。

6>资源对象没关闭造成的内存泄露:源性对象比如(Cursor,File文件等)往往都用了一些缓冲。不使用的时候,应该调用它的close()函数,将其关闭掉,并置为null。

7>各种注册没取消:注册的广播以及监听器,在不使用的情况下需要注销。

8>集合容器对象没清理造成的内存泄露:在退出程序之前,将集合里的东西clear,然后置为null,再退出程序。

9>static关键字的滥用:这些资源不会随着对象的回收而回收,会一直存在,所以在使用static关键字定义成员变量的时候要慎重。

10>WebView对象没有销毁:不使用WebView对象时,应该调用它的destory()函数来销毁它,并释放其占用的内存,否则其占用的内存长期也不能被回收,从而造成内存泄露。

11>GridView的滥用:GridView和ListView的实现方式不太一样。GridView的View不是即时创建的,而是全部保存在内存中的。比如一个GridView有100项,虽然我们只能看到10项,但是其实整个100项都是在内存中的。所以在应用程序退出之前,要讲gridView和adapter全部置为null。

 

什么是 ANR 问题?为什么会引起 ANR 问题?

ANR全称:Application Not Responding。

ANR定义:如果你的应用程序有一段时间响应不够灵敏,系统会向用户显示一个对话框,这个对话框称作应用程序无响应(ANR:Application Not Responding)对话框。用户可以选择“等待”而让程序继续运行,也可以选择“强制关闭”。默认情况下,在android中Activity的最长执行时间是5秒,BroadcastReceiver的最长执行时间则是10秒。

ANR引发:

1)在5秒内没有响应输入的事件(例如,按键按下,屏幕触摸)

2)BroadcastReceiver在10秒内没有执行完毕

避免ANR:

1)运行在主线程里的任何方法都尽可能少做事情。特别是,Activity应该在它的关键生命周期方法(如onCreate()和onResume())里尽可能少的去做创建操作。

2)应用程序应该避免在BroadcastReceiver里做耗时的操作或计算。

3)避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。如果你的应用程序在响应Intent广播时需要向用户展示什么,你应该使用Notification Manager来实现。

 

Socker编程的步骤

 

 

设计一个图片缓存加载机制

1>图片可以缓存到本地磁盘

2>判断图片是否已经被缓存。首先通过软引用判断图片是否已经缓存加载在内存中,如果有加载,没有则进行3>

3>判断图片是否已经缓存到本地,如果已缓存,从本地通过软引用加载,如果未缓存,从网络加载,并将图片缓存到本地

 

Fragment嵌套多个Fragment会出现bug吗

1>嵌套多层的Fragment的时候,第一个主要点就是第一层的FragmentManager,可以通过getSupportFragmentManager()或者getFragmentManager()获得,但是在第二层的fragment中如果想获得FragmentManager就不能这样了,必须用getChildFragmentManager()。

2>在复杂的Fragment管理中,经常会遇到 Fragment already added 错误,解决这样的错误方法就是,每次添加Fragment,先findFragmentByTag,如果找到了fragment.isAdded(),那么就return,跳出,如果Fragment没在栈中,那就把Fragment Add上去。

3>commitAllowingStateLoss和commit的区别是当退出activity时,防止提交后的状态丢失。对于你觉得可以丢失提交的状况,使用 commitAllowingStateLoss()。

 

Activity中如何动态的添加Fragment

1>获取到FragmentManager,在Activity中可以直接通过getFragmentManager得到。

2>开启一个事务,通过调用beginTransaction方法开启。

3>向容器内加入Fragment,一般使用replace方法实现(也可以使用add方法),需要传入容器的id和Fragment的实例。

4>提交事务,调用commit或commitAllowingStateLoss方法提交。

 

内存不足时,怎么保持Activity的一些状态,在哪个方法里面做具体操作?

  内存不足的时候,Activity只能在执行完onStop()之后才能杀死Activity进程。保存Activity的一些状态,可以在onSaveInstanceState和onPause方法中进行保存,由于onSaveInstanceState(Bundle)方法不是activity生命周期中的回调方法之一,所以在activity被杀死的时候,它是不能保证百分百的被执行的。因此在onPause方法中保存状态更为合理。

 

Scrollview怎么判断是否滑倒底部

滚动到顶部判断:getScrollY() == 0

滚动到底部判断:

View childView = getChildAt(0);

childView.getMeasuredHeight() <= getScrollY() + getHeight();

其中getChildAt表示得到ScrollView的childView

childView.getMeasuredHeight()表示得到子View的高度, getScrollY()表示得到y轴的滚动距离,getHeight()为scrollView的高度

getScrollY()达到最大时加上scrollView的高度就的就等于它内容的高度了.

 

1>实现OnTouchListener来监听是否滑动到最底部


2>重写ScrollView的onScrollChanged的方法,在onScrollChanged函数中判断


 

ViewPager 的怎么做性能优化

ViewPager.setOffscreenPageLimit(n):设置缓存View的个数,当缓存的View个数越多,滑动翻页时会更流畅,但需注意缓存的视图越多,会消耗更大的内存。

 

AsyncTask具体用法?

AsyncTask是一个抽象类,如果要使用它,必须创建一个子类去继承它。继承时需要指定三个泛型参数:AsyncTask<Params, Progress, Result>

1)Params:在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。

2)Progress:后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。

3)Result:当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。

 

1>onPreExecute:在后台任务开始执行之前调用,用于进行一些界面上的初始化操作

2>Result doInBackground(Params ...):在子线程中运行,处理所有耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果返回。在这个方法中是不可以进行UI操作的,如果需要更新UI,可以调用publishProgress(Progress...)

3>onProgressUpdate():在后台任务中调用了publishProgress(Progresss...)方法后,这个方法就会很快被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法可以对UI进行操作,利用参数中的数值就可以对界面元素进行更新。

4>onPostExecute(Result):当后台任务执行完毕并通过return语句进行返回时,这个方法很快就会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据进行UI操作更新。

 

new AsyncTask().execute();

 

AsyncTask的doInBackground方法是怎么通知UI线程刷新进度条的?

AsyncTask的内部实现是一个线程池,所有提交的异步任务都会在这个线程池中的工作线程内执行,当工作线程需要跟UI线程交互时,工作线程会通过向在UI线程创建的Handler传递消息的方式,调用相关的回调函数,从而实现UI界面的更新。

 

AsyncTask的doInBackground方法默认是返回 true ,表示任务完成,如果想返回具体的数据呢,怎么做。如果Activity被销毁了,还会执行到postexcutd方法吗?

如果需要返回具体的数据,在创建子类继承AsyncTask时,指定泛型的时候将第三个泛型值指定为需要返回具体数据的类型,然后在doInBackground任务完成时,return具体的数据即可。

AsyncTask会一直执行, 直到doInBackground()方法执行完毕。然后,如果 cancel(boolean)被调用, 那么onCancelled(Result result) 方法会被执行;否则,执行onPostExecute(Result result) 方法。如果我们的Activity销毁之前,没有取消 AsyncTask,这有可能让我们的AsyncTask崩溃(crash)。因为它想要处理的view已经不存在了。所以,我们总是必须确保在销毁活动之前取消任务。总之,我们使用AsyncTask需要确保AsyncTask正确地取消。另外,即使我们正确地调用了cancle() 也未必能真正地取消任务。因为如果在doInBackgroud里有一个不可中断的操作,比如BitmapFactory.decodeStream(),那么这个操作会继续下去。

 

View中onTouch,onTouchEvent,onClick的执行顺序

View中包含dispatchTouchEvent和onTouchEvent方法。

onTouch是View调用setOnTouchListener中重写的方法。

onClick是View调用setOnClickListener中重写的方法。


dispatchTouchEvent---->onTouch---->onTouchEvent----->onClick

 

不使用动画,怎么实现一个动态的 View?

创建新类继承自View,创建Handler对象,通过Handler不断发送消息,处理消息是对View进行重绘。

 

Invalidate和postInvalidate的更新view区别?

invalidate是在UI线程自身中使用,而postInvalidate在非UI线程中使用。 

 

Asset与raw都能存放资源,他们有什么区别?

res/raw和assets的相同点:

1>两者目录下的文件在打包后会原封不动的保存在apk包中,不会被编译成二进制。

 

res/raw和assets的不同点:

1>res/raw中的文件会被映射到R.java文件中,访问的时候直接使用资源ID即R.id.filename;assets文件夹下的文件不会被映射到R.java中,访问的时候需要AssetManager类。

2>res/raw不可以有目录结构,而assets则可以有目录结构,也就是assets目录下可以再建立文件夹

 

读取文件资源:

1>读取res/raw下的文件资源,通过以下方式获取输入流来进行写操作

InputStream is = getResources().openRawResource(R.id.filename);  

2>读取assets下的文件资源,通过以下方式获取输入流来进行写操作

AssetManager am = getAssets();  

InputStream is = am.open("filename");  

 

如何自定义ViewGroup?

绘制布局由两个遍历过程组成:测量过程和布局过程。

测量过程由measure(int, int)方法完成,该方法从上到下遍历视图树。在递归遍历过程中,每个视图都会向下层传递尺寸和规格。当measure方法遍历结束,每个视图都保存了各自的尺寸信息。

布局过程由layout(int,int,int,int)方法完成,该方法也是由上而下遍历视图树,在遍历过程中,每个父视图通过测量过程的结果定位所有子视图的位置信息。”

简而言之,第一步是测量ViewGroup的宽度和高度,在onMeasure()方法中完成,ViewGroup遍历所有子视图计算出它的大小。第二步是根据第一步获取的尺寸去布局所有子视图,在onLayout()中完成。

 

什么是 MVC 模式?MVC 模式的好处是什么?

MVC (Model-View-Controller):M是指逻辑模型,V是指视图模型,C则是控制器。一个逻辑模型可以对于多种视图模型。使用MVC的目的是将M和V的实现代码分离,从而使同一个程序可以使用不同的表现形式,而C存在的目的则是确保M和V的同步,一旦M改变,V应该同步更新,这与《设计模式》中的观察者模式是完全一样。

MVC好处:采用MVC模式的好处是便于UI界面部分的显示和业务逻辑,数据处理分开。有了很好的可扩展和维护性,当需要改变UI显示的时候,无需修改Contronller(控制器)Activity的代码和Model(模型)WeatherModel模型中的业务逻辑代码,很好的将业务逻辑和界面显示分离。

 

优点:(1)耦合性低。所谓耦合性就是模块代码之间的关联程度。利用MVC框架使得View(视图)层和Model(模型)层可以很好的分离,这样就达到了解耦的目的,所以耦合性低,减少模块代码之间的相互影响。

(2)可扩展性好。由于耦合性低,添加需求,扩展代码就可以减少修改之前的代码,降低bug的出现率。

(3)模块职责划分明确。主要划分层M,V,C三个模块,利于代码的维护。

 

缺点:控制器Activity主要是起到解耦作用,将View视图和Model模型分离,虽然Activity起到交互作用,但是找Activity中有很多关于视图UI的显示代码,因此View视图和Activity控制器并不是完全分离的,也就是说一部分View视图和Contronller控制器Activity是绑定在一个类中的。

 

Android中界面部分也采用了当前比较流行的MVC框架,在Android中: 

  1) 视图层(View):一般采用XML文件进行界面的描述,使用的时候可以非常方便的引入。当然,如何你对Android了解的比较的多了话,就一定可以想到在Android中也可以使用JavaScript+HTML等的方式作为View层,当然这里需要进行Java和JavaScript之间的通信,幸运的是,Android提供了它们之间非常方便的通信实现。     

  2) 控制层(Controller):Android的控制层的重任通常落在了众多的Acitvity的肩上,这句话也就暗含了不要在Acitivity中写代码,要通过Activity交割Model业务逻辑层处理,这样做的另外一个原因是Android中的Acitivity的响应时间是5s,如果耗时的操作放在这里,程序就很容易被回收掉。

  3) 模型层(Model):对数据库的操作、对网络等的操作都应该在Model里面处理,当然对业务计算等操作也是必须放在的该层的。就是应用程序中二进制的数据。

在Android SDK中的数据绑定,也都是采用了与MVC框架类似的方法来显示数据。在控制层上将数据按照视图模型的要求(也就是Android SDK中的Adapter)封装就可以直接在视图模型上显示了,从而实现了数据绑定。比如显示Cursor中所有数据的ListActivity,其视图层就是一个ListView,将数据封装为ListAdapter,并传递给ListView,数据就在ListView中现实。 

 

JVM 和Dalvik虚拟机的区别

JVM(Java虚拟机):是一个虚构出来的运行Java程序的运行时,是通过在实际的计算机上仿真模拟各种计算机功能的实现。它具有完善的硬件架构(如处理器、堆栈、寄存器等),还具有相应的指令系统,使用JVM就是使Java程序支持与操作系统无关。理论上在任何操作系统中,只要有对应的JVM,即可运行Java程序。

Dalvik VM:是在Android系统上运行Android程序的虚拟机,其指令集是基于寄存器架构的,执行特有的文件格式-dex字节码来完成对象生命周期管理、堆栈管理、线程管理、安全异常管理、垃圾回收等重要功能。

 

1>Dalvik和标准Java虚拟机之间的首要差别在于Java是以基于栈的虚拟机Dalvik是基于寄存器的虚拟机。基于寄存器的虚拟机对于更大的程序来说,在它们编译的时候,花费的时间更短。

2> JVM字节码由.class文件组成,每个文件一个class。JVM在运行的时候为每一个类装载字节码。相反的,Dalvik程序只包含一个.dex文件,这个文件包含了程序中所有的类。Java编译器创建了JVM字节码之后,Dalvik的dx编译器删除.class文件,重新把它们编译成Dalvik字节码,然后把它们写进一个.dex文件中。

3>Dalvik 和 Java SDK的SDK不同。

4>Dalvik 和 Java 运行环境的区别:Dalvik 经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik 应用作为一个独立的Linux 进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。

5>Dalvik主要是完成对象生命周期管理,堆栈管理,线程管理,安全和异常管理,以及垃圾回收等等重要功能。Dalvik负责进程隔离和线程管理,每一个Android应用在底层都会对应一个独立的Dalvik虚拟机实例,其代码在虚拟机的解释下得以执行。   

 

应用常驻后台,避免被第三方杀掉的方法,讲讲方法?

1>Service重写onStartCommand方法,返回值设置成START_STICKY,kill 后会被重启(等待5秒左右),保持与重启前一样

2>通过 startForeground将进程设置为前台进程,做前台服务,优先级和前台应用一个级别,除非在系统内存非常缺,否则此进程不会被kill。startForeground(int id, Notification notification)。

3>双进程Service:让2个进程互相保护,其中一个Service被清理后,另外没被清理的进程可以立即重启进程

4>QQ黑科技:在应用退到后台后,另起一个只有 1 像素的页面停留在桌面上,让自己保持前台状态,保护自己不被后台清理工具杀死

5>在已经root的设备下,修改相应的权限文件,将App伪装成系统级的应用(Android4.0系列的一个漏洞,已经确认可行)。对于放在/system/app下的应用,需要在其Manifest.xml文件中设置persistent属性,如应用程序'Phone'的AndroidManifest.xml文件:


6>Android系统中当前进程(Process)fork出来的子进程,被系统认为是两个不同的进程。当父进程被杀死的时候,子进程仍然可以存活,并不受影响。鉴于目前提到的在Android-Service层做双守护都会失败,我们可以fork出c进程,多进程守护。死循环在那检查是否还存在,具体的思路如下(Android5.0以下可行)

1)用C编写守护进程(即子进程),守护进程做的事情就是循环检查目标进程是否存在,不存在则启动它。

2)在NDK环境中将1中编写的C代码编译打包成可执行文件(BUILD_EXECUTABLE)。

3)主进程启动时将守护进程放入私有目录下,赋予可执行权限,启动它即可。

7>联系厂商,加入白名单

 

数据持久化的四种方式有哪些?

Android系统一共提供了四种数据存储方式。分别是:SharePreference、SQLite、Content Provider和File。由于Android系统中,数据基本都是私有的的,都是存放于“data/data/程序包名”目录下,所以要实现数据共享,正确方式是使用Content Provider。

  SQLite: SQLite是一个轻量级的数据库,支持基本SQL语法,是常被采用的一种数据存储方式。Android为此数据库提供了一个名为SQLiteDatabase的类,封装了一些操作数据库的API。

  SharedPreference: 除SQLite数据库外,另一种常用的数据存储方式,其本质就是一个xml文件,常用于存储较简单的参数设置。

  File: 即常说的文件(I/O)存储方法,常用语存储大数量的数据,但是缺点是更新数据将是一件困难的事情。

  ContentProvider: Android系统中能实现所有应用程序共享的一种数据存储方式,由于数据通常在各应用间的是互相私密的,所以此存储方式较少使用,但是其又是必不可少的一种存储方式。例如音频,视频,图片和通讯录,一般都可以采用此种方式进行存储。每个Content Provider都会对外提供一个公共的URI(包装成Uri对象),如果应用程序有数据需要共享时,就需要使用Content Provider为这些数据定义一个URI,然后其他的应用程序就通过Content Provider传入这个URI来对数据进行操作。

PS: URI由3个部分组成:"content://"、数据的路径、标识ID(可选)。

 

 

数据结构与算法部分

1. 给最外层的rootview,把这个根视图下的全部button背景设置成红色,手写代码,不许用递归★

2. 给一串字符串比如abbbcccd,输出a1b3c3d1,手写代码(注意有个别字符可能会出现十次以上的情况)☆

3. 一个序列,它的形式是12349678,9是最高峰,经历了一个上升又下降的过程,找出里面的最大值的位置,要求效率尽可能高★

4. 二叉查找树的删除操作,手写代码☆

5. 反转链表,手写代码☆

6. 二分查找,手写代码☆

7. 有海量条 url,其中不重复的有300万条,现在希望挑选出重复出现次数最高的 url,要求效率尽可能的高★

8. 一篇英语文章,去掉字符只留下k个,如何去掉才能使这k个字符字典序最小

9. Floyd算法和 Dijkstra算法的区别?复杂度是多少?讲讲 Dijkstra算法的具体过程☆

10. 反转字符串,要求手写代码,优化速度、优化空间☆

11. 给出两个无向图,找出这2个无向图中相同的环路。手写代码

12. 单例模式,手写代码☆

13. 生产者与消费者,手写代码☆

14. 二叉树镜像,手写代码

15. 最长不重复子串(最长重复子串),手写代码

 

 

操作系统部分

分别从操作系统的内存角度与进程线程角度解释分析堆,栈二者的区别

栈(操作系统):由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈,栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放

堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。堆则是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些

 

堆:是进程线程共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配,运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。

栈:是线程独有的,保存其运行状态和局部自动变量的。栈在线程开始的时候初始化,每个线程的栈互相独立,因此,栈是 thread safe的。操作系统在切换线程的时候会自动的切换栈,就是切换SS/ESP寄存器。栈空间不需要在高级语言里面显式的分配和释放。

 

什么是事务?

  事务是作为一个逻辑单元执行的一系列操作,一个逻辑工作单元必须有四个属性,称为 ACID(原子性、一致性、隔离性和持久性)属性,只有这样才能成为一个事务:

 

OSI七层模型有哪些,各层次的作用

1>物理层:正确利用介质。要传递信息就要利用一些物理媒体,为它的上一层提供一个物理连接。

2>数据链路层:连通每个结点。负责在两个相邻结点间的线路上,传送以帧为单位的数据。

3>网络层:选择走哪条路。选择合适的网间路由和交换结点,确保数据及时传送。

4>传输层:找到对方主机。根据通信子网的特性来最佳地利用网络资源,为两个端系统(源站和目的站)的会话层之间,提供建立、维护和取消传输连接的功能。

5>会话层:决定该谁说,该谁听,从何处听。提供包括访问验证和会话管理在内的建立和维护应用之间通信的机制。

6>表示层:决定用什么语言交谈。提供格式化的表示和转换数据服务。

7>应用层:指出做什么事。确定进程之间的性质以满足用户需要以及提供网络与用户应用软件之间的接口服务。

 

TCP的三次握手过程,四次挥手过程,为什么需要三次?

  三次握手:为了防止已失效的连接请求报文段突然又传送到了服务端,因而产生错误。

四次挥手:原因是因为tcp是全双工模式,接收到FIN时意味将没有数据再发来,但是还是可以继续发送数据。

 

三次握手过程状态: 

1>LISTEN: 这个也是非常容易理解的一个状态,表示服务器端的某个SOCKET处于监听状态,可以接受连接了。 

2>SYN_SENT: 当客户端SOCKET执行CONNECT连接时,它首先发送SYN报文,因此也随即它会进入到了SYN_SENT状态,并等待服务端的发送三次握手中的第2个报文。

3>SYN_SENT状态表示客户端已发送SYN报文。(发送端)

4>SYN_RCVD: 这个状态与SYN_SENT遥想呼应这个状态表示接受到了SYN报文,在正常情况下,这个状态是服务器端的SOCKET在建立TCP连接时的三次握手会话过程中的一个中间状态,很短暂,基本上用netstat你是很难看到这种状态的,除非你特意写了一个客户端测试程序,故意将三次TCP握手过程中最后一个 ACK报文不予发送。因此这种状态时,当收到客户端的ACK报文后,它会进入到ESTABLISHED状态。(服务器端)

5>ESTABLISHED:这个容易理解了,表示连接已经建立了。

 

说说操作系统中进程的通信方式

进程间的四种通讯方式:

1>管道,FIFO:管道(Pipe)是一种具有两个端点的通信通道:有一端句柄的进程可以和有另一端句柄的进程通信。管道可以是单向-一端是只读的,另一端点是只写的;也可以

是双向的一管道的两端点既可读也可写。

2>信号:信号是比较复杂的通信方式,用于通知接受进程有某种事件发生,除了用于进

程间通信外,进程还可以发送信号给进程本身。除了系统内核和root之外,只有具

备相同uid、gid的进程才可以使用信号进行通信。

    /etc/passwd:uid与账号对应

/etc/group:gid与群组对应

3>消息队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消

息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺。

4>共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。

 

浏览器输入地址之后,之后的过程

1>查找域名对应的IP地址。这一步会依次查找浏览器缓存,系统缓存,路由器缓存,ISPNDS缓存,根域名服务器。

2>向IP对应的服务器发送请求。

3>服务器响应请求,发回网页内容。

4>浏览器解析网页内容。

由于网页可能有重定向,或者嵌入了图片,AJAX,其它子网页等等,这4个步骤可能反复进行多次才能将最终页面展示给用户。

 

访问百度:

1>浏览器查找域名的IP地址

2>浏览器给web服务器发送一个HTTP请求

3>百度服务的永久重定向响应

4>浏览器跟踪重定向地址

5>服务器“处理”请求

6>服务器发回一个HTML响应

7>浏览器开始显示HTML

8>浏览器发送获取嵌入在HTML中的对象

9>浏览器发送异步(AJAX)请求

 

谈谈 HTTP 中Get 和 Post 方法的区别?

1>GET一般用于获取/查询资源信息,而POST一般用于更新资源信息。

2>GET请求的数据会附在URL之后,POST把提交的数据则放置在是HTTP包的包体中

3>GET方式提交的数据最多只能是1024字节,理论上POST没有限制,可传较大量的数据

4>POST的安全性要比GET的安全性高


  题目是最近网上post出来的,答案是自己根据自己的见解和网上查询获得并整理的,如果有什么不正确的欢迎各位提出,共同进步,谢谢。

 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值