百度移动端面试回忆

百度一面:

1. 自我介绍

2. 悲观锁和乐观锁

乐观锁:

 总是认为不会产生并发问题,每次去取数据的时候总认为不会有其他线程对数据进行修改,因此不会上锁,但是在更新时会判断其他线程在这之前有没有对数据进行修改,一般会使用版本号机制或CAS操作实现。

  •  version方式(版本号机制):一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。
  • CAS操作方式:即compare and swap 或者 compare and set,涉及到三个操作数,数据所在的内存值,预期值,新值。当需要更新时,判断当前内存值与之前取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不断的重试。

悲观锁:

 总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起。可以依靠数据库实现,如行锁、读锁和写锁等,都是在操作之前加锁,在Java中,synchronized的思想也是悲观锁

3. Android跨线程通信, Handler

Android进阶——Android消息机制之Looper、Handler、MessageQueen

Android消息机制图解

4. Android跨进程通信,Binder

Android跨进程通信:图文详解 Binder机制 原理

5. Android的性能优化

6. 动态链接和静态链接的区别

  • 静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件。
  • 动态链接就是把调用的函数所在文件模块(DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因此需要相应DLL文件的支持。 动态链接的主要目的:把程序与他们使用的特定的函数库版本中分离开来。

静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令都全部被直接包含在最终生成的 EXE 文件中了。但是若使用 DLL,该 DLL 不必被包含在最终 EXE 文件中,EXE 文件执行时可以“动态”地引用和卸载这个与 EXE 独立的 DLL 文件。 静态链接库和动态链接库的另外一个区别在于静态链接库中不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。

动态链接的优点:

  1. 可执行文件的体积可以非常小,节省磁盘空间和虚拟内存,能更加有效的利用磁盘空间。
  2. 链接-编辑阶段的时间会缩短(因为链接器的有些工作被推迟到载入)
  3. 所有动态链接到某个特定函数库的可执行文件在运行时共享该函数库在内存中的一个单独拷贝。这就提供了更好的I/O和交换空间利用率,节省了物理内存,提高了系统整体性能。

不足之处:

  • 使用静态链接生成的可执行文件体积较大,包含相同的公共代码,造成浪费;     
  •  使用动态链接库的应用程序不是自完备的,它依赖的DLL模块也要存在,如果使用载入时动态链接,程序启动时发现DLL不存在,系统将终止程序并给出错误信息。而使用运行时动态链接,系统不会终止,但由于DLL中的导出函数不可用,程序会加载失败;速度比静态链接慢。当某个模块更新后,如果新模块与旧的模块不兼容,那么那些需要该模块才能运行的软件,统统撕掉。这在早期Windows中很常见。

8. TCP三次握手,四次挥手

TCP建立连接和释放连接过程

9. 如何实现线程同步

  • 同步方法。使用synchronized关键字修饰的方法。由于java的每个对象都有一个内置锁,当用关键字修饰此方法时,内置锁会保护整个方法。在调用该方法前,需要获得内置锁,否则该线程就处于阻塞状态。 代码如:public synchronized void save(){} synchronized关键字也可以静态方法,此时如果调用该静态方法,将会锁住整个类。
  • 同步代码块。即有synchronized关键字修饰的语句块。被关键字修饰的的语句块会自动加上内置锁,从而实现同步。 代码如:synchronized(object){ }   同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要去同步整个方法,使用关键字synchronized修饰关键代码即可
  • 使用特殊域变量修饰符volatile实现线程同步。不保证对变量的任何操作都是原子性的
  • 使用重入锁实现线程同步,ReentrantLock
  • 使用ThreadLocal管理局部变量实现线程同步 

14. 设计模式,讲解一下观察者模式的使用场景,给一个简单的实现案例

15. 手撕代码:将任意一个double型的角度转化为0-360度

public double convertToAngle(double angle){
		int circle = (int) (angle / 360);
		if(angle > 0){
			angle-=circle*360;
		}else{
			angle+=360*(1-circle);
		}
		return angle;
	}

16. 手撕代码:找出一个有序数组中某个数字出现的区间(数字在排序数组出现得次数)

public class Solution {
    public int GetNumberOfK(int [] array , int k) {
        int length = array.length;
        if(length == 0){
            return 0;
        }
        int firstK = getFirstK(array, k, 0, length-1);
        int lastK = getLastK(array, k, 0, length-1);
        if(firstK != -1 && lastK != -1){
             return lastK - firstK + 1;
        }
        return 0;
    }
    //递归写法
    private int getFirstK(int [] array , int k, int start, int end){
        if(start > end){
            return -1;
        }
        int mid = (start + end) >> 1;
        if(array[mid] > k){
            return getFirstK(array, k, start, mid-1);
        }else if (array[mid] < k){
            return getFirstK(array, k, mid+1, end);
        }else if(mid-1 >=0 && array[mid-1] == k){
            return getFirstK(array, k, start, mid-1);
        }else{
            return mid;
        }
    }
    //循环写法
    private int getLastK(int [] array , int k, int start, int end){
        int length = array.length;
        int mid = (start + end) >> 1;
        while(start <= end){
            if(array[mid] > k){
                end = mid-1;
            }else if(array[mid] < k){
                start = mid+1;
            }else if(mid+1 < length && array[mid+1] == k){
                start = mid+1;
            }else{
                return mid;
            }
            mid = (start + end) >> 1;
        }
        return -1;
    }
}

百度二面:

1. 自我介绍

2. 问项目细节,问实习经历

3. 死锁的定义,死锁产生的条件,解决死锁的方法

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程

死锁产生条件:

1)互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放。

2)请求和保持条件:指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放。

3)不剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放。

4)环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合{P0,P1,P2,···,Pn}中的P0正在等待一个P1占用的资源;P1正在等待P2占用的资源,……,Pn正在等待已被P0占用的资源。

处理死锁:

1)预防死锁。该方法是通过设置某些限制条件,去破坏产生死锁的四个必要条件中的一个或几个来预防产生死锁。

2)避免死锁。在资源的动态分配过程中,用某种方法防止系统进入不安全状态,从而可以避免产生死锁。(银行家算法)

3)检测死锁。通过检测机构及时地检测出死锁的发生,然后采取适当的措施,把进程从思索中解脱出来。

4)解除死锁。当检测到系统中已发生死锁时,就采取相应的措施,将进程从死锁状态中解脱出来。常用方法是---撤销一些进程,回收他们的资源,将他们分配给已处于阻塞状态的进程,使其能继续运行。

4. 读过哪些Android源代码,Android消息队列,EventBus等等

5. MVC和MVP的区别

6. 红黑树

7. HashMap的实现原理

8. Android内存管理

Android内存管理分析总结

9. Volatile关键字

Java并发编程:volatile关键字解析

注意:Volatile关键字不保证操作的原子性

10. 手写代码:从一个Activity中向另外一个Activity传送BitMap

1) . 通过Intent的Bundle

Resources res=getResources();  
Bitmap bmp=BitmapFactory.decodeResource(res, R.drawable.ic_launcher);  
          
Bundle b = new Bundle();  
b.putParcelable("bitmap", bmp);  
          
Intent intent = new Intent(this, MainActivity2.class);  
intent.putExtras(b);  
startActivity(intent);  

 

2). 把Bitmap写进字节流

Resources res=getResources();  
Bitmap bmp=BitmapFactory.decodeResource(res, R.drawable.ic_launcher);  
ByteArrayOutputStream baos = new ByteArrayOutputStream();  
bmp.compress(Bitmap.CompressFormat.PNG, 100, baos);  
byte[] bytes=baos.toByteArray();  
          
Bundle b = new Bundle();  
b.putByteArray("bitmap", bytes);  
Intent intent = new Intent(this, MainActivity2.class);  
intent.putExtras(b);  
startActivity(intent);  

11. Android FrameWork源码

12. 手撕代码: 找出数组中最小的k个数


import java.util.ArrayList;
public class Solution {
   public ArrayList<Integer> GetLeastNumbers_Solution(int[] input, int k) {

		ArrayList<Integer> result = new ArrayList<Integer>();
		int len = input.length;
		if (k > len || k <= 0 || len == 0) {
			return result;
		}

		int index = Partition(input,len,0,len-1);
		while(index != k - 1){
			if(index > k - 1){
				index = Partition(input,len,0,index - 1);
			}else{
				index = Partition(input,len,index + 1,len -1 );
			}
		}

		for(int i = 0 ; i < k ; i++){
			result.add(input[i]);
		}
		return result;
	}

	public int Partition(int[] array, int length, int start, int end) {
		if (array == null || length == 0 || start < 0 || end >= length) {
			return (Integer) null;
		}

		int key = array[start];
		int low = start;
		int high = end;
		while (low < high) {
			while (low < high && array[high] >= key) {
				high--;
			}
			array[low] = array[high];

			while (low < high && array[low] <= key) {
				low++;
			}
			array[high] = array[low];
		}
		array[low] = key;
		return low;
	}
}

百度三面:

1. 自我介绍

2. 栈和队列的区别,优先队列的使用场景

优先队列(可实现自动排序):

一个基于优先级堆的无界优先级队列。优先级队列的元素按照其自然顺序进行排序,或者根据构造队列时提供的 Comparator 进行排序,具体取决于所使用的构造方法。优先级队列不允许使用 null 元素。依靠自然顺序的优先级队列还不允许插入不可比较的对象(这样做可能导致 ClassCastException)。

此队列的 是按指定排序方式确定的最小 元素。如果多个元素都是最小值,则头是其中一个元素——选择方法是任意的。队列获取操作 pollremovepeekelement 访问处于队列头的元素。

优先级队列是无界的,但是有一个内部容量,控制着用于存储队列元素的数组大小。它通常至少等于队列的大小。随着不断向优先级队列添加元素,其容量会自动增加。无需指定容量增加策略的细节。

3. 手撕代码:实现斐波那契数列

public class Solution {
    public int Fibonacci(int n) {
        if(n <= 1){
            return n;
        }
        int fibNMinusTwo = 0; // Fibonacci(n-2)
        int fibNMinusOne = 1; // Fibonacci(n-1);
        int fibN = 0;
        for(int i = 2; i <=n ; i++){
        	//Fibonacci(n) = Fibonacci(n-1) + Fibonacci(n-2) 
            fibN = fibNMinusOne + fibNMinusTwo; 
            fibNMinusTwo = fibNMinusOne;
            fibNMinusOne = fibN;
        }
       return fibN;
    }
}

4. MVC和MVP的区别

5. 实习和项目经历

6. C++和Java的区别

  • java为解释性语言(源代码通过java编译器编译成字节码交由JVM解释执行),C/C++为编译型语言(源代码经过编译和链接后生成可执行的二进制编码). 注:java的执行速度比C/C++的执行速度慢,java能够跨平台执行,C/C++不可以。
  • java纯面向对象(所有代码在类中实现,除基本类型外,所有类型都为类),C++兼具面向过程和面向对象编程的特点。 注:java不存在全局变量或者全局函数,C++可以定义全局变量和全局函数。
  • java中没有指针的概念,防止了C++语言中操作指针可能引起的系统问题,使程序变得更加安全。
  • java不支持多继承(java引入了接口的概念,可以同时实现多个接口,也具有多态性)  C++支持多继承
  • C++需要开发人员去管理对内存的分配,包括申请和释放。(释放资源的代码放在析构函数中)  java自己提供了垃圾回收器来实现垃圾的自动回收,不需要显示的管理内存的分配。(java不存在析构函数,引入了finalize()方法,当垃圾回收将要释放无用内存时,会首先调用该方法)
  • C++支持运算符重载;java不支持运算符重载
  • C++支持预处理,java没有预处理器,不支持预处理功能(包括头文件和宏的定义) 注:java提供了import 机制与C++的预处理器功能类似。
  • C++支持默认的函数参数,java不支持默认函数参数。
  • C++支持goto语句,java不支持goto语句(java中goto是保留关键字)。 注:goto语句也称为无条件转移语句,其一般格式如下:  goto 语句标号; 其中语句标号是按标识符规定书写的符号, 放在某一语句行的前面,标号后加冒号(:)。语句标号起标识语句的作用,与goto 语句配合使用。  如: label: i++;  loop: while(x<7);
  • C++支持自动强制类型转换,java不支持强制类型转换(java需要开发人员显示的强制类型转换)。 注:java引入泛型的概念,更好的处理类型的问题。
  • java具有平台无关性。(每种类型分配固定的长度)  C++具有平台性。(同一种类型在不同的平台分配的长度不同)
  • java包含一些标准库,用于完成特定的任务,同时这些库简单易用,能够大大的缩短开发周期。(提供了访问数据库的JDBC库,用于实现分布式对象的RMI等标准库)  C++依靠一些非标准的,由其他厂商提供的库。

7. 值传递和引用传递,那个效率更高

值传递:方法调用时,实际参数把它的值传递给对应的形式参数,方法执行中形式参数值的改变不影响实际参数的值。

引用传递:也称为传地址。方法调用时,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,方法执行中形式参数值的改变将会影响实际参数的值。 

引用传递的效率高于值传递(直接访问内存)

在 Java 应用程序中永远不会传递对象,而只传递对象引用。因此是按引用传递对象。但重要的是要区分参数是如何传递的。Java 应用程序按引用传递对象这一事实并不意味着 Java 应用程序按引用传递参数。参数可以是对象引用,而 Java 应用程序是按值传递对象引用的。

Java 应用程序中的变量可以为以下两种类型之一:引用类型或基本类型。当作为参数传递给一个方法时,处理这两种类型的方式是相同的。两种类型都是按值传递的;没有一种按引用传递。


按 值传递意味着当将一个参数传递给一个函数时,函数接收的是原始值的一个副本。因此,如果函数修改了该参数,仅改变副本,而原始值保持不变。按引用传递意味 着当将一个参数传递给一个函数时,函数接收的是原始值的内存地址,而不是值的副本。因此,如果函数修改了该参数,调用代码中的原始值也随之改变。


当传递给函数的参数不是引用时,传递的都是该值的一个副本(按值传递)。区别在于引用。在 C++ 中当传递给函数的参数是引用时,您传递的就是这个引用,或者内存地址(按引用传递)。在 Java 应用程序中,当对象引用是传递给方法的一个参数时,您传递的是该引用的一个副本(按值传递),而不是引用本身。


Java 应用程序按值传递所有参数,这样就制作所有参数的副本,而不管它们的类型。

Java的参数传递到底是引用传递还是值传递

8. 什么时候接触Android的,为什么会做Android

9. 是否有读研的想法

10. 操作系统页面置换算法

页面置换算法

11. LRU算法的实现

猜你喜欢

转载自blog.csdn.net/weixin_37886683/article/details/82796403