2月17日中午收到腾讯的面试短信,说看到我投递的实习简历预约面试时间,预约在2月18日13:00,QQ视频面试。
于是我就紧张的准备知识,刚过完年来,对知识体系还有算法还没有在状态,然后我快速整理了四方面的内容。
一.Android基础知识:
移动开发基础知识梳理:
1.activity是四大组件之一,一个用户交互界面对应一个activity,activity是Context的子类,我开发常用的fragmentactivity,很少用到ListActivity,preferenceactivity等。如果界面有共同的特点或者功能的时候,还会自己定义一个BaseActivity.
2.Activity生命周期的描述
生命周期描述的是一个类从创建到死亡的过程中会执行的方法,在这个过程中会针对不同的生命阶段会调用不同的方法。
Activity从创建到销毁有多种状态,从一种状态到另一种状态时会激发相应的回调方法,这些回调方法包括:oncreate ondestory onstop onstart onresumeonpause其实这些方法都是两两对应的。
onCreate创建和onDestroy销毁;
onStart可见与onStop不可见;
onResume可编辑(即焦点)与onPause;
这六个方法是相对应的,那么就只剩下一个onRestart方法,这个方法是在activity被onStop后,但是没有被onDestory,再次启动此activity时就调用onRestart方法。如果被ondestory了,则调用onCreate方法。
3.两个Activity之间跳转时必然会执行的是哪几个方法。
一般情况下比如有两个activity叫AB,当在A里面激活B组件的时候,A会调用onPause()方法,然后B调用onCreate(),onStart(),onResume().这个时候B可见,A会调用onStop()方法。
4.横竖屏切换时候activity的生命周期
这个生命周期跟清单文件里的配置有关系。
不设置android:configChanges时,切屏会重新调用各个生命周期,默认首先销毁当前activity,然后重新加载。
设置activity的android:configChanges时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法。
5.你后台的Activity被系统 回收怎么办?如果后台的Activity由于某原因被系统回收可了,如何在被系统回收之前保存当前状态?
6.除了在栈顶的activity,其他的activity都有可能在内存不足的时候被系统回收,一个activity越处于栈底,被回收的可能性越大。
Protected voidonSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
outState.putLong(“id”,1234567890);
}
Public voidonCreate(Bundle savedInstanceState){
//判断savedInstanceState是不是空。
//如果不为空就取出来
Super.onCreate(saveInstanceState);
}
7.对android主线程的运用和理解? 主ui线程不能执行耗时的操作,
8.当启动一个Activity并且新的Activity执行完后需要返回到启动它的Activity来执行 的回调函数是startActivityResult()
9. GC内存泄露
出现情况:
数据库的cursor没有关闭
构造adapter时,没有使用缓存contentview
衍生listview的优化问题-----减少创建view的对象,充分使用contentview,可以使用一静态类来优化处理getview的过程/
Bitmap对象不使用时采用recycle()释放内存
activity中的对象的生命周期大于activity
调试方法: DDMS==>HEAPSZIE==>dataobject==>[Total Size]
9、什么是ANR 如何避免它?
答:ANR:Application Not Responding,五秒
在Android中,活动管理器和窗口管理器这两个系统服务负责监视应用程序的响应。当出现下列情况时,Android就会显示ANR对话框了:
对输入事件(如按键、触摸屏事件)的响应超过5秒
意向接受器(intentReceiver)超过10秒钟仍未执行完毕
Android应用程序完全运行在一个独立的线程中(例如main)。这就意味着,任何在主线程中运行的,需要消耗大量时间的操作都会引发ANR。因为此时,你的应用程序已经没有机会去响应输入事件和意向广播(Intent broadcast)。
因此,任何运行在主线程中的方法,都要尽可能的只做少量的工作。特别是活动生命周期中的重要方法如onCreate()和onResume()等更应如此。潜在的比较耗时的操作,如访问网络和数据库;或者是开销很大的计算,比如改变位图的大小,需要在一个单独的子线程中完成(或者是使用异步请求,如数据库操作)。但这并不意味着你的主线程需要进入阻塞状态已等待子线程结束 -- 也不需要调用Therad.wait()或者Thread.sleep()方法。取而代之的是,主线程为子线程提供一个句柄(Handler),让子线程在即将结束的时候调用它(xing:可以参看Snake的例子,这种方法与以前我们所接触的有所不同)。使用这种方法涉及你的应用程序,能够保证你的程序对输入保持良好的响应,从而避免因为输入事件超过5秒钟不被处理而产生的ANR。这种实践需要应用到所有显示用户界面的线程,因为他们都面临着同样的超时问题。
二.Java方面的知识
1.九种基本数据类型的大小,以及他们的封装类。
答:java提供了一组基本数据类型,包括boolean, byte, char, short, int, long, float,double, void. 同时,java也提供了这些类型的封装类,分别为Boolean, Byte,Character, Short, Integer, Long, Float, Double, Void。
既然提供了基本类型,为什么还要使用封装类呢?
- 某些情况下,数据必须作为对象出现,此时必须使用封装类来将简单类型封装成对象。
- 比如,如果想使用List来保存数值,由于List中只能添加对象,因此我们需要将数据封装到封装类中再加入List。在JDK5.0以后可以自动封包,可以简写成list.add(1)的形式,但添加的数据依然是封装后的对象。
- 另外,有些情况下,我们也会编写诸如func(Object o)的这种方法,它可以接受所有类型的对象数据,但对于简单数据类型,我们则必须使用封装类的对象。
- 某些情况下,使用封装类使我们可以更加方便的操作数据。比如封装类具有一些基本类型不具备的方法,比如valueOf(), toString(), 以及方便的返回各种类型数据的方法,如Integer的shortValue(), longValue(), intValue()等。
基本数据类型与其对应的封装类由于本质的不同,具有一些区别:
- 基本数据类型只能按值传递,而封装类按引用传递。
- 基本类型在堆栈中创建;而对于对象类型,对象在堆中创建,对象的引用在堆栈中创建。基本类型由于在堆栈中,效率会比较高,但是可能会存在内存泄漏的问题。
2. Switch能否用string做参数?
答:在 Java 7之前,switch 只能支持 byte、short、char、int或者其对应的封装类以及 Enum 类型。在 Java 7中,String支持被加上了。
1 2 3 4 5 6 7 8 9 10 | switch (ctrType) { case "01" : exceptionType = "读FC参数数据"; break; case "03" : exceptionType = "读FC保存的当前表计数据"; break; default: exceptionType = "未知控制码:"+ctrType; } |
其中ctrType为字符串。
如在jdk 7 之前的版本使用, 会提示如下错误:
Cannot switch on a value oftype String for source level below 1.7. Only convertible int values or enumvariables are permitted
意为jdk版本太低,不支持。
3. equals与==的区别。
答:==操作比较的是两个变量的值是否相等,对于引用型变量表示的是两个变量在堆中存储的地址是否相同,即栈中的内容是否相同。
equals操作表示的两个变量是否是对同一个对象的引用,即堆中的内容是否相同。
4.Object有哪些公用方法?
答:
protectedObject clone()创建并返回此对象的一个副本。
booleanequals(Object obj)指示其他某个对象是否与此对象“相等”。
protectedvoid finalize()当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。
Class<?>getClass()返回此 Object 的运行时类。
inthashCode()返回该对象的哈希码值。
voidnotify()唤醒在此对象监视器上等待的单个线程。
voidnotifyAll()唤醒在此对象监视器上等待的所有线程。
StringtoString()返回该对象的字符串表示。
voidwait()在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。
voidwait(long timeout)在其他线程调用此对象的 notify() 方法或 notifyAll()方法,或者超过指定的时间量前,导致当前线程等待。
voidwait(long timeout, int nanos)在其他线程调用此对象的 notify() 方法或 notifyAll()方法,或者其他某个线程中断当前线程,或者已超过某个实际时间量前,导致当前线程等待。
5.Java的四种引用,强弱软虚,用到的场景。
答:对大部分对象而言,程序里会有一个引用变量引用该对象,这是最常见的引用方式。除此之外,java.lang.ref包下提供了3个类:SoftReference、PhantomReference和WeakReference,它们分别代表了系统对对象的3种引用方式:软引用、虚引用和弱引用。因此,java语言对对象的引用有如下4种方式。
(1)强引用(StrongReference)
这是java程序中最常见的引用方式。程序创建一个对象,并把这个对象赋给一个引用变量,程序通过该引用变量来操作实际的对象,前面介绍的对象和数组都采用了这种强引用的方式。当一个对象被一个或一个以上的引用变量所引用时,它处于可达状态,不可能被系统垃圾回收机制回收。
(2)软引用(SoftReference)
软引用需要通过SoftReference类来实现,当一个对象只有软引用时,它有可能被垃圾回收机制回收。对于只有软引用的对象而言,当系统内存空间足够时,它不会被系统回收,程序也可以使用该对象;当系统内存空间不足时,系统可能会回收它。软引用通常用于对内存敏感的程序中。
(3)弱引用(WeakReference)
弱引用通过WeakReference类实现,弱引用和软引用很像,但弱引用的引用级别更低。对于只有弱引用的对象而言,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存。当然,并不是说当一个对象只有弱引用时,它就会立即被回收——正如那些失去引用的对象一样,必须等到系统垃圾回收机制运行时才会被回收。
(4)虚引用(PhantomReference)
虚引用通过PhantomReference类实现,虚引用完全类似于没有引用。虚引用对对象本身没有太大的影响,对象甚至感觉不到虚引用的存在。如果一个对象只有一个虚引用时,那么它和没有引用的效果大致相同。虚引用主要用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,虚引用必须和引用队列(ReferenceQueue)联合使用。
以上三个引用类都包含了一个get()方法,用于获取被它们所引用的对象。
一下看下demo
- import java.lang.ref.WeakReference;
- public class ReferenceTest {
- public static void main(String[] args) throws Exception {
- String str=new String("疯狂java讲义");
- WeakReference wr=new WeakReference(str);
- str=null;
- System.out.println(wr.get());//打印出疯狂java讲义
- System.gc();
- System.runFinalization();
- //强制垃圾回收之后再次取出弱引用所引用的对象
- System.out.println(wr.get());//打印出null
- }
- }
6.Hashcode的作用。
答:hashcode方法返回该对象的哈希码值。支持该方法是为哈希表提供一些优点,例如,java.util.Hashtable 提供的哈希表。
hashCode 的常规协定是:
在 Java 应用程序执行期间,在同一对象上多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是对象上 equals 比较中所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。
如果根据 equals(Object) 方法,两个对象是相等的,那么在两个对象中的每个对象上调用 hashCode 方法都必须生成相同的整数结果。
以下情况不 是必需的:如果根据 equals(java.lang.Object) 方法,两个对象不相等,那么在两个对象中的任一对象上调用 hashCode 方法必定会生成不同的整数结果。但是,程序员应该知道,为不相等的对象生成不同整数结果可以提高哈希表的性能。
实际上,由 Object 类定义的 hashCode 方法确实会针对不同的对象返回不同的整数。(这一般是通过将该对象的内部地址转换成一个整数来实现的,但是 JavaTM 编程语言不需要这种实现技巧。)
当equals方法被重写时,通常有必要重写 hashCode 方法,以维护 hashCode 方法的常规协定,该协定声明相等对象必须具有相等的哈希码。
以上这段官方文档的定义,我们可以抽出成以下几个关键点:
1、hashCode的存在主要是用于查找的快捷性,如Hashtable,HashMap等,hashCode是用来在散列存储结构中确定对象的存储地址的;
2、如果两个对象相同,就是适用于equals(java.lang.Object) 方法,那么这两个对象的hashCode一定要相同;
3、如果对象的equals方法被重写,那么对象的hashCode也尽量重写,并且产生hashCode使用的对象,一定要和equals方法中使用的一致,否则就会违反上面提到的第2点;
4、两个对象的hashCode相同,并不一定表示两个对象就相同,也就是不一定适用于equals(java.lang.Object) 方法,只能够说明这两个对象在散列存储结构中,如Hashtable,他们“存放在同一个篮子里”。
7. ArrayList、LinkedList、Vector的区别。
Java 中Vector、ArrayList和LinkedList 的区别
SDK提供了有序集合接口java.util.List的几种实现,其中三种最为人们熟知的是Vector、ArrayList和LinkedList。有关这些List类的性能差别是一个经常被问及的问题。在这篇文章中,我要探讨的就是LinkedList和Vector/ArrayList之间的性能差异。
为全面分析这些类之间的性能差异,我们必须知道它们的实现方法。因此,接下来我首先从性能的角度出发,简要介绍这些类的实现特点。
一、Vector和ArrayList的实现
Vector和ArrayList都带有一个底层的Object[]数组,这个Object[]数组用来保存元素。通过索引访问元素时,只需简单地通过索引访问内部数组的元素:
public Object get(int index)
{ //首先检查index是否合法...此处不显示这部分代码 return
elementData[index]; }
内部数组可以大于Vector/ArrayList对象拥有元素的数量,两者的差值作为剩余空间,以便实现快速添加新元素。有了剩余空间,添加元素变得非常简单,只需把新的元素保存到内部数组中的一个空余的位置,然后为新的空余位置增加索引值:
public boolean add(Object o)
{ ensureCapacity(size + 1); //稍后介绍 elementData[size++] = o; returntrue;
//List.add(Object) 的返回值 }
把元素插入集合中任意指定的位置(而不是集合的末尾)略微复杂一点:插入点之上的所有数组元素都必须向前移动一个位置,然后才能进行赋值:
public void add(int index, Objectelement) {
//首先检查index是否合法...此处不显示这部分代码
ensureCapacity(size+1);
System.arraycopy(elementData,index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
剩余空间被用光时,如果需要加入更多的元素,Vector/ArrayList对象必须用一个更大的新数组替换其内部Object[]数组,把所有的数组元素复制到新的数组。根据SDK版本的不同,新的数组要比原来的大50%或者100%(下面显示的代码把数组扩大100%):
public void ensureCapacity(intminCapacity) {
int oldCapacity =elementData.length;
if (minCapacity > oldCapacity){
Object oldData[] = elementData;
int newCapacity =Math.max(oldCapacity * 2, minCapacity);
elementData = newObject[newCapacity];
System.arraycopy(oldData, 0,elementData, 0, size);
}
}
Vector类和ArrayList类的主要不同之处在于同步。除了两个只用于串行化的方法,没有一个ArrayList的方法具有同步执行的能力;相反,Vector的大多数方法具有同步能力,或直接或间接。因此,Vector是线程安全的,但ArrayList不是。这使得ArrayList要比Vector快速。对于一些最新的JVM,两个类在速度上的差异可以忽略不计:严格地说,对于这些JVM,这两个类在速度上的差异小于比较这些类性能的测试所显示的时间差异。
通过索引访问和更新元素时,Vector和ArrayList的实现有着卓越的性能,因为不存在除范围检查之外的其他开销。除非内部数组空间耗尽必须进行扩展,否则,向列表的末尾添加元素或者从列表的末尾删除元素时,都同样有着优秀的性能。插入元素和删除元素总是要进行数组复制(当数组先必须进行扩展时,需要两次复制)。被复制元素的数量和[size-index]成比例,即和插入/删除点到集合中最后索引位置之间的距离成比例。对于插入操作,把元素插入到集合最前面(索引0)时性能最差,插入到集合最后面时(最后一个现有元素之后)时性能最好。随着集合规模的增大,数组复制的开销也迅速增加,因为每次插入操作必须复制的元素数量增加了。
二、LinkedList的实现
LinkedList通过一个双向链接的节点列表实现。要通过索引访问元素,你必须查找所有节点,直至找到目标节点:
public Object get(intindex) {
//首先检查index是否合法...此处不显示这部分代码
Entry e = header; //开始节点
//向前或者向后查找,具体由哪一个方向距离较
//近决定
if (index < size/2) {
for (int i = 0; i <= index;i++)
e = e.next;
} else {
for (int i = size; i > index;i--)
e = e.previous;
}
return e;
}
把元素插入列表很简单:找到指定索引的节点,然后紧靠该节点之前插入一个新节点:
public void add(int index, Objectelement) {
//首先检查index是否合法...此处不显示这部分代码
Entry e = header; //starting node
//向前或者向后查找,具体由哪一个方向距离较
//近决定
if (index < size/2) {
for (int i = 0; i <= index;i++)
e = e.next;
} else {
for (int i = size; i > index;i--)
e = e.previous;
}
Entry newEntry = newEntry(element, e, e.previous);
newEntry.previous.next =newEntry;
newEntry.next.previous =newEntry;
size++;
}
线程安全的LinkedList和其他集合
如果要从Java SDK得到一个线程安全的LinkedList,你可以利用一个同步封装器从Collections.synchronizedList(List)得到一个。然而,使用同步封装器相当于加入了一个间接层,它会带来昂贵的性能代价。当封装器把调用传递给被封装的方法时,每一个方法都需要增加一次额外的方法调用,经过同步封装器封装的方法会比未经封装的方法慢二到三倍。对于象搜索之类的复杂操作,这种间接调用所带来的开销不是很突出;但对于比较简单的方法,比如访问功能或者更新功能,这种开销可能对性能造成严重的影响。
这意味着,和Vector相比,经过同步封装的LinkedList在性能上处于显著的劣势,因为Vector不需要为了线程安全而进行任何额外的间接调用。如果你想要有一个线程安全的LinkedList,你可以复制LinkedList类并让几个必要的方法同步,这样你可以得到一个速度更快的实现。对于所有其它集合类,这一点都同样有效:只有List和Map具有高效的线程安全实现(分别是Vector和Hashtable类)。有趣的是,这两个高效的线程安全类的存在只是为了向后兼容,而不是出于性能上的考虑。
对于通过索引访问和更新元素,LinkedList实现的性能开销略大一点,因为访问任意一个索引都要求跨越多个节点。插入元素时除了有跨越多个节点的性能开销之外,还要有另外一个开销,即创建节点对象的开销。在优势方面,LinkedList实现的插入和删除操作没有其他开销,因此,插入-删除开销几乎完全依赖于插入-删除点离集合末尾的远近。
ArrayList和Vector通常比LinkedList和同步封装之后的LinkedList有着更好的性能。即使在你认为LinkedList可能提供更高性能的情况下,你也可以通过修改元素加入的方式从ArrayList争取更好的性能,例如翻转集合元素的次序。
有些情况下LinkedList会有更好的性能,例如,当大量元素需要同时加入到大型集合的开头和末尾时。但一般而言,我建议你优先使用ArrayList/Vector类,只有当它们存在明显的性能问题而LinkedList能够改进性能时,才使用LinkedList。
还有参考连接:
http://www.cnblogs.com/mgod/archive/2007/08/05/844011.html
8. String、StringBuffer与StringBuilder的区别。
java中String、StringBuffer、StringBuilder是编程中经常使用的字符串类,他们之间的区别也是经常在面试中会问到的问题。现在总结一下,看看他们的不同与相同。
1.可变与不可变
String类中使用字符数组保存字符串,如下就是,因为有“final”修饰符,所以可以知道string对象是不可变的。
privatefinal char value[];
StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串,如下就是,可知这两种对象都是可变的。
char[]value;
2.是否多线程安全
String中的对象是不可变的,也就可以理解为常量,显然线程安全。
AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。
StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。看如下源码:
1public synchronized StringBuffer reverse() {
2 super.reverse();
3 return this;
4 }
5
6 public int indexOf(String str) {
7 return indexOf(str, 0); //存在 public synchronized int indexOf(String str,int fromIndex) 方法
8 }
StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
3.StringBuilder与StringBuffer共同点
StringBuilder与StringBuffer有公共父类AbstractStringBuilder(抽象类)。
抽象类与接口的其中一个区别是:抽象类中可以定义一些子类的公共方法,子类只需要增加新的功能,不需要重复写已经存在的方法;而接口中只是对方法的申明和常量的定义。
StringBuilder、StringBuffer的方法都会调用AbstractStringBuilder中的公共方法,如super.append(...)。只是StringBuffer会在方法上加synchronized关键字,进行同步。
最后,如果程序不是多线程的,那么使用StringBuilder效率高于StringBuffer。
9. Map、Set、List、Queue、Stack的特点与用法。
1.Java集合类基本概念
在编程中,常常需要集中存放多个数据。从传统意义上讲,数组是我们的一个很好的选择,前提是我们事先已经明确知道我们将要保存的对象的数量。一旦在数组初始化时指定了这个数组长度,这个数组长度就是不可变的,如果我们需要保存一个可以动态增长的数据(在编译时无法确定具体的数量),java的集合类就是一个很好的设计方案了。
集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类。所以的集合类都位于java.util包下,后来为了处理多线程环境下的并发安全问题,java5还在java.util.concurrent包下提供了一些多线程支持的集合类。
在学习Java中的集合类的API、编程原理的时候,我们一定要明白,"集合"是一个很古老的数学概念,它远远早于Java的出现。从数学概念的角度来理解集合能帮助我们更好的理解编程中什么时候该使用什么类型的集合类。
Java容器类类库的用途是"保存对象",并将其划分为两个不同的概念:
1)Collection
一组"对立"的元素,通常这些元素都服从某种规则
1.1) List必须保持元素特定的顺序
1.2) Set不能有重复元素
1.3) Queue保持一个队列(先进先出)的顺序
2) Map
一组成对的"键值对"对象
Collection和Map的区别在于容器中每个位置保存的元素个数:
1)Collection 每个位置只能保存一个元素(对象)
2) Map保存的是"键值对",就像一个小型数据库。我们可以通过"键"找到该键对应的"值"
主要是简历中的技术点,listview的优化。对图片和视频压缩是如何压缩的。
四.简历技术点