最近在考虑换工作,连续面试了几家公司,这里整理一些比较有意思的面试题(普通面试题和算法题,本人算法基础比较薄,算法题不是强项)。
面试题
Java中static静态代码块的调用时机。
Java中的静态变量和静态代码块是在类加载的时候就执行的,实例化对象时,先声明并实例化变量再执行构造函数。如果子类继承父类,则先执行父类的静态变量和静态代码块,再执行子类的静态变量和静态代码块。同样,接着在执行父类和子类非静态代码块和构造函数。
注意:(静态)变量和(静态)代码块的也是有执行顺序的,与代码书写的顺序一致。在(静态)代码块中可以使用(静态)变量,但是被使用的(静态)变量必须在(静态)代码块前面声明。
最后给出执行步骤:
1、父类静态变量和静态代码块(先声明的先执行);
2、子类静态变量和静态代码块(先声明的先执行);
3、父类的变量和代码块(先声明的先执行);
4、父类的构造函数;
5、子类的变量和代码块(先声明的先执行);
6、子类的构造函数。
Android数据库结构升级容易遇到哪些问题,如何解决?
Android运行时权限。
Android进程保活手段。
AMS、WMS它们之间的关系以及协作状态。
Android内存问题,造成内存泄漏的原因,如何避免内存泄漏。
ThreadLocal是什么,有什么作用。
String,StringBuffer与StringBuilder的区别?
String 字符串常量StringBuffer 字符串变量(线程安全)StringBuilder 字符串变量(非线程安全)
是否可以使用Application中开辟子线程来代替Service开启子线程?
理论上可以在Application中开启子线程,但是会有弊端:
Application中逻辑过多会拖慢APP启动速度
Application生命周期过长,后天任务不一定需要这么长的生命周期,使用IntentService可以实现任务结束自动关闭
Application生命周期无法延续,在被强杀后,后台任务会关闭;而服务可以指定新进程进行保活。考虑音乐播放器的功能,有些音乐播放器,即使应用退出后依然可以进行前台服务的保留以便于随时恢复
唯一好处是在Application中开启线程较之Service启动新线程,开销较小
算法题
简述几种排序算法和它们的时间复杂度。
我回答了冒泡、快速、选择和堆排,这里索性把几种排序的Java实现也写一下,查漏补缺。
冒泡排序
/** * 冒泡排序 * @param array 目标数组 */ public static void bubbleSort(int []array) { for(int i =0;i<array.length-1;i++) { for(int j=0;j<array.length-i-1;j++) {//-1为了防止溢出 //数组内元素两两比较,顺序不一致就交换位置 if(array[j]>array[j+1]) { int temp = array[j]; array[j]=array[j+1]; array[j+1]=temp; } } } }
快速排序
/** * 快速排序 * @param array 目标数组 * @param low 快排起始坐标 * @param high 快排终止坐标 */ private static void quickSort(int[] array,int low,int high) { if(low > high) { return; } //进行第一次快排,将目标区间一分为2 int i = low; int j = high; int key = array[low]; while(i < j) { //从右往左找到第一个比目标值小的数值位置,标记为j while(i < j && array[j] >= key) { j--; } //从左往右找到第一个比目标数值大的数值位置,标记为i while(i < j && array[i] <= key) { i++; } //互换位置 if(i < j) { int temp = array[i]; array[i] = array[j]; array[j] = temp; } } //调整目标基准值的位置 array[low] = array[i]; array[i] = key; quickSort(array,low,i-1); quickSort(array,i+1,high); }
选择排序
/** * 选择排序 * @param array 目标数组 */ public static void selectSort(int[] array) { for(int i = 0;i < array.length;i++) { int min = array[i];//假定起始位置的数值为最小数值 int pivotPosition = i;//记录起始位置坐标 for(int j = i;j < array.length;j++) { //找出剩余数组中最小的值,记录其坐标 if(array[j] < min) { pivotPosition = j; min = array[pivotPosition]; } } //将最小值放入起始位置 if(i != pivotPosition) { array[pivotPosition] = array[i]; array[i] = min; } } }
插入排序
/** * 插入排序 * @param array 目标数组 */ public static void insertSort(int[] array) { //从第二个数据开始做循环,我们假定第一条数据已经在它应在的位置了 for(int i = 1;i < array.length;i++) { //取出目标位置的数值 int j = i; int target = array[i]; //对比目标位置的数值,与左侧排好序的数组比较,若发现有比目标值大的数,将比目标值大的数位置的数值往前提,游标后移 while(j > 0 && target < array[j - 1]) { array[j] = array[j - 1]; j--; } //跳出循环后将当前target插入游标位置 if(j != i) { array[j] = target; } } }
实现一个链表的反转。
public class Node { private int index; private Node next; public Node(int index, Node next) { this.index = index; this.next = next; } public int getIndex() { return index; } public void setIndex(int index) { this.index = index; } public Node getNext() { return next; } public void setNext(Node next) { this.next = next; } //非递归 public static Node reverseList(Node head) { Node prev = null; while(head!=null){ Node tmp = head.next; head.next = prev; prev = head; head = tmp; } return prev; } //递归 public static Node reverseList2(Node head) { if(head==null || head.next ==null) { return head; } Node prev = reverseList2(head.next); head.next.next = head; head.next = null; return prev; } }
实现排序数组的二分查找。
public class SearchUtils { /** * 二分查找,递归 * @param array 目标数组 * @param target 查找内容 * @param low 查找起始位置 * @param high 查找结束位置 * @return 内容所在位置,-1代表不存在 */ public static int recursionBinarySearch(int[] array,int target,int low,int high) { if(low > high) { //起始位置大于结束位置,未找到,返回-1 return -1; } if(low == high) { //起始位置等于结束位置,判断是否等于目标值 if(array[low] == target) { return low; } else { return -1; } } int middle = (high + low)/2;//初始化中间位置 if(array[middle] > target) { return recursionBinarySearch(array,target,low,middle-1); } else if(array[middle] < target) { return recursionBinarySearch(array,target,middle+1,high); } else { return middle; } } /** * 二分查找,非递归 * @param array 目标数组 * @param target 查找内容 * @return 内容所在位置,-1代表不存在 */ public static int binarySearch(int[] array,int target) { int low = 0; int high = array.length - 1; int middle = 0; while(high > low) { middle = (high + low)/2;//获取中间位置 if(array[middle] > target) { high = middle - 1; } else if(array[middle] < target) { low = middle + 1; } else { return middle; } } if(array[low] == target) { return low; } else { return -1; } } }
实现二叉树的按层遍历。
/** * 按层遍历二叉树 * @param root 需要遍历的二叉树 */ public static void levelTree(TreeNode root) { if(root == null) return; Queue<TreeNode> queue = new LinkedList<TreeNode>() ; queue.add(root); while(queue.size() != 0) { int len = queue.size(); for(int i=0;i <len; i++) { TreeNode temp = queue.poll(); System.out.print(temp.data+" "); if(temp.left != null) queue.add(temp.left); if(temp.right != null) queue.add(temp.right); } } }
我的个人博客