面试复习

C++

  • 虚函数
    虚函数实现的一般模型: 每个类有一个虚表,内含该类之中有作用的虚函数的地址,然后每个对象有一个vptr, 指向虚表的所在.
    每一个类有一个虚表,每一个类的对象有一个指向虚表的指针vptr
    对于非虚函数,会转化为一个全局函数,调用方法时,对象作为第一个参数,隐式传给这个函数
  • 传值与传参问题

    ++
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 引自<<剑指Offer>>
    class A{
    private: 
    int value;
    public:
    A(int n){value = n; }
    A(A other){value=other.value;}
    void print(){ std::cout << value << std::endl;}
    }
    int _tmain(int argc, _TCHAR* argv[]){
    A a = 10;
    A b = a;
    b.print();
    }

    上述代码中,复制构造函数A(A other)传入的参数是A的一个实例.由于是传值参数,我们把形参复制到实参会调用复制构造函数.因此如果允许复制构造函数传值,就会在复制构造函数内调用复制构造函数,就会形成永无休止的递归调用从而导致栈溢出.

  • 浅拷贝和深拷贝
    在某些情况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,对象里的值完全复制给另一个对象,如A=B.这时如果B中有一个成员变量指针已经申请了内存,那么A中的那个成员变量也指向同一块内存.当B把内存释放了,这时A内的指针就是野指针了,会出现运行错误.

Java

  • Spring Framework
    很好的面试资料: 69道Spring面试题和答案

  • 面向对象编程(OOP)
    优点:
    代码开发模块化,更易维护和修改
    代码复用
    增强代码的可靠性,灵活性,可读性
    面向对象编程特性:

    • 封装,通过隐藏对象的属性来保护内部的状态;提高代码的可用性和可维护性;禁止对象之间不良交互提高模块化
    • 继承,代码重用,添加新特性
    • 多态,程序中定义的应用变量所指向的具体类型和通过引用变量调用的方法在编译时并不确定,而在程序运行时才能确定.
    • 抽象,把想法从具体的实例抽离出来的步骤.
      封装与抽象的不同:
      抽象关注对象的行为,封装关注对象行为的细节.
  • Java虚拟机(JVM)
    Java虚拟机是可以执行Java字节码的虚拟机进程,使跨平台变为可能.

  • 自动拆装箱
    自动装箱是Java编译器在基本数据类型和对应的对象包装类型之间做的一个转化,比如int->Integer.反之就是自定拆箱.

  • 死锁
    两个进程都在等待对方执行完毕才能继续执行的时候就发生了死锁.结果两个进程都陷入了无限的等待中.

  • 活锁
    活锁指的是任务或者执行者没有被阻塞,由于某些条件没有被满足,导致一直重复尝试,失败,尝试,失败.

  • 死锁活锁的区别
    处于活锁的实体是在不断的改变状态,所谓的”活”,而处于死锁的实体表现为等待;活锁有可能解开,死锁则不能.

  • 如何确保N个线程可以访问N个资源同时又不导致死锁?
    同样的顺序加锁和释放锁 : 指定获取锁的顺序,并强制线程按照指定的顺序获得锁.

  • 快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?
    Iterator的安全失败是基于对底层集合做拷贝,因此它不受原集合上修改的影响.jva.util包下的所有集合类都是快速失败的,而java.util.concurrent包下的所有类都是安全失败的.
    快速失败的迭代器会抛出ConcurrentModificationException异常,而安全失败的迭代器不会抛出这样的异常.
    推荐使用CopyOnWriteArrayList. CopyOnWriteArrayList是ArrayList的一个线程安全的变体,其中所有可变操作(add,set等)都是通过对底层数组进行一次新的复制来实现的.
    适用场景: 不想进行同步遍历,但又需要从并发线程中排除冲突;遍历操作远大于可变操作.

  • try,catch,finnally,return
    finally 块的语句在try或catch中的return语句 执行之后返回之前 执行且finally里的修改语句可能影响也可能不影响try或catch中return已确定的返回值,若finally里也有return语句则覆盖try或catch中的return语句直接返回.
    参考Java finally语句到底是在return之前还是之后执行?

  • 串行(serial)收集器和吞吐量收集器的区别是什么?
    吞吐量收集器使用并行版本的新生代垃圾收集器,它用于中等规模和大规模数据的应用程序.而串行收集器对于大多数的小应用就足够了.

  • 多线程
    Top 15 Java Multithreading, Concurrency Interview Questions Answers asked in Investment banks
    1) 现在有T1,T2,T3三个线程,怎样保证T2在T1执行后执行,T3在T2执行后执行
    使用join方法实现
    2) 在java中lock接口比synchronized块的优势是什么?你需要实现一个高效的缓存,它允许多个用户度,但只允许一个用户写,一次来保持它的完整性,你会怎么去实现它?
    synchronized和lock的用法区别: 在需要同步的对象加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示要锁的对象.
    lock:需要显式指定起始位置和终止位置. 一般使用ReentrantLock类作为锁,多个线程中必须使用一个ReentrantLock类作为对象才能保证锁的生效,且在加锁和解锁处需要通过lock()和unlock()显式指出.所以一般在finally块中谢unlock()以防死锁.
    synchronized和lock的原理:
    synchronized原始采用的是CPU悲观锁机制,即线程获得的是独占锁.独占锁意味着其他线程只能依靠阻塞来等待线程释放锁.而在CPU转换线程阻塞时会引起线程上下文切换,当有很多线程竞争锁的时候,会引起CPU频繁的上下文切换导致效率很低.
    而lock采用的是乐观锁方式.所谓乐观锁就是每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止.乐观锁实现的机制就是CAS操作(Compare and Swap).研究ReentrantLock的源代码,会发现其中一种比较重要的获取锁的方法是compareAndSetState,其实就是调用CPU提供的特殊指令.
    现代的CPU提供了指令,可以自动更新共享数据,而且能够检测其他线程的干扰,而compareAndSet就用这些代替了锁定.这个算法称作非阻塞算法,意思是一个线程的失败或者挂起不应该引起其他线程的失败或挂起的算法.

    3) 在java中wait与sleep的不同
    在等待是wait会释放锁,而sleep一直持有锁.
    wait通常被用于线程间交互,sleep通常被用于暂停执行.
    4) 用java实现阻塞队列
    阻塞队列与普通队列的区别在于: 当队列为空时,从队列中取元素的操作将会被阻塞; 当队列是满时,往队列里添加元素的操作会被阻塞.试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素.同样,试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或多个元素,或者完全清空队列.
    从5.0开始,JDK在java.util.concurrent包里提供了阻塞队列的官方实现.

数据结构

  • 数组
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    int GetSize(int data[]){
    return sizeof(data);
    }
    int _tmain(int argc, _TCHAR* argv[]){
    int data1[] = {1,2,3,4,5};
    int size1 = sizeof(data1);

    int* data2 = data1;
    int size2 = sizeof(data2);

    int size3 = GetSize(data1);

    printf("%d, %d, %d", size1, size2, size3);
    }
    // output: 20, 4, 4

在C/C++中,当数组作为函数的参数进行传递时,数组就自动退化为同类型的指针.

  • 满二叉树
    满二叉树(full binary tree): 除最后一层无任何子节点外,每一层的所有节点都有两个子节点的二叉树.额\节点数达到最大值.所有叶子节点必须在同一层上.
    (满二叉树->完全二叉树)
  • 完全二叉树
    完全二叉树(complete binary tree): 除了最后一层外,每一层上的节点数均达到最大值;在最后一层上只缺少右边若干节点.完全二叉树是由满二叉树引出来的.对于深度为K的,有n个节点的二叉树,当且仅当其每一个节点都与深度为K的满二叉树中编号从1至n的节点一一对应使称为完全二叉树.

  • 最大堆
    最大堆:跟节点是所有节点中的最大者,且是完全二叉树.

语言特性

  • C++中struct和class的区别
    • 访问权限: 如果没有标明成员函数或者成员变量的访问权限级别,在struct中默认是public,而在class中默认是private.

设计模式

  • singleton

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Singleton {
    // 第一步:私有构造函数
    private Singleton(){}
    // 第二步:私有变量
    private Singleton instance = null;
    // 第三步:静态方法
    public static getInstance(){
    if(instance==null){
    synchronized{
    if(instance==null){
    return new Singleton();
    }
    }
    }
    return instance;
    }
    }
  • 引用和指针
    引用与指针的区别在于表现形式(引用初始化后不能再指向其他对象);
    传值参数与引用参数的区别在于形参的声明.

外部排序

外部排序指的是大文件的排序,即待排序的记录存储在外存储器上,待排序的文件无法一次性装入内存,需要在内存和外部存储器之间进行多次数据交换,以达到排序整个文件的目的.

其他

理解数据之间的依赖性.