面经-基础篇-二

本文最后更新于:2021年3月28日 晚上

以下内容并非博主亲身经历的面试问题,只是对网络中的面经做一个总结、记录。

1.11 抽象类和接口的区别,类可以继承多个类么,接口可以继承多个接口么,可以实现多个接口么?

  • 不同点:

    • 抽象类要被子类继承,接口要被类实现。
    • 接口只能做方法申明,抽象类中可以做方法申明,也可以做方法实现
    • 接口里定义的变量只能是公共的静态的常量,抽象类中的变量是普通变量。
  • 共同点:

    • 抽象类和接口都不能直接实例化,如果要实例化,抽象类变量必须指向实现所有抽象方法的子类对象,接口变量必须指向实现所有接口方法的类对象。
    • 抽象类里的抽象方法必须全部被子类所实现,如果子类不能全部实现父类抽象方法,那么该子类只能是抽象类。同样,一个实现接口的时候,如不能全部实现接口方法,那么该类也只能为抽象类。
    • 抽象方法只能申明,不能实现。abstract void abc();不能写成abstract void abc(){}。
  • 其它:

    • 抽象类里可以没有抽象方法
    • 如果一个类里有抽象方法,那么这个类只能是抽象类
    • 抽象方法要被实现,所以不能是静态的,也不能是私有的。
  • 类只能继承一个类,但是可以实现多个接口。
  • 接口只能继承接口,但是可多继承接口。

1.12 继承和聚合的区别在哪?

  • 继承指的是一个类继承另外的一个类的功能,并可以增加它自己的新功能的能力,继承是类与类或者接口与接口之间最常见的关系;在Java中此类关系通过关键字extends明确标识。

  • 聚合体现的是整体与部分、拥有的关系,此时整体与部分之间是可分离的,他们可以具有各自的生命周期;比如计算机与CPU、公司与员工的关系等;在java中表现在代码层面,只能从语义级别来区分。

扩展:

  • 实现:指的是一个class类实现interface接口(可以是多个)的功能;实现是类与接口之间最常见的关系;在Java中此类关系通过关键字implements明确标识
  • 依赖:可以简单的理解,就是一个类A使用到了另一个类B,而这种使用关系是具有偶然性的、临时性的、非常弱的,但是B类的变化会影响到A;比如某人要过河,需要借用一条船,此时人与船之间的关系就是依赖;表现在代码层面,为类B作为参数被类A在某个method方法中使用。
  • 关联:体现的是两个类、或者类与接口之间语义级别的一种强依赖关系,表现在代码层面,为被关联类B以类属性的形式出现在关联类A中,也可能是关联类A引用了一个类型为被关联类B的全局变量。
  • 组合:组合也是关联关系的一种特例,他体现的是一种contains-a的关系,这种关系比聚合更强,也称为强聚合;他同样体现整体与部分间的关系,但此时整体与部分是不可分的,整体的生命周期结束也就意味着部分的生命周期结束;比如你和你的大脑;表现在代码层面,和关联关系是一致的,只能从语义级别来区分。

1.13 什么是IO模型?有哪些,讲讲你理解的nio ,他和bio,aio的区别是啥,谈谈reactor模型。

  • 什么是IO模型?
    • IO模型就是说用什么样的通道进行数据的发送和接收,Java共支持3种网络编程IO模式:NIO、BIO、AIO
  • 介绍:
    • JAVA NIO:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有IO请求时才启动一个线程进行处理
    • JAVA BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程并处理,如果这个连接不做任何事情会造成不必要的开销,当然可以通过线程池机制改善
    • JAVA AIO(NIO2):异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理
  • 使用场景:
    • NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
    • BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
    • AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

1.14 反射的原理,反射创建类实例的三种方式是什么?

  • 反射的原理:
    • 我们都知道java类的执行需要编译、加载、连接、初始化四步。反射的原理就是加载时,jvm通过字节码class文件,生成相应的对象。
  • 反射创建类实例的三种方式:
    • 对象.getClass()
    • 类.class
    • Class.forName(“全路径名称”)
  • 详细介绍请移步:反射的那些事

1.15 反射中,Class.forName和ClassLoader区别 。

  • Java中Class.forName和classloader都可以用来对类进行加载。
  • Class.forName除了将类的 .class文件 加载到jvm中之外,还会对类进行解释,执行类中的static块。
  • 而classloader只干一件事情,就是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
  • Class.forName(name,initialize,loader)带参数也可控制是否加载static块。

1.16 描述动态代理的几种实现方式,分别说出相应的优缺点

常见的动态代理有 jdk动态代理cglib动态代理

  • jdk动态代理是由java内部的反射机制来实现的,它是jdk原生就支持的一种代理方式,它的实现原理,就是通过让target类和代理类实现同一接口,代理类持有target对象,来达到方法拦截的作用,这样通过接口的方式有两个弊端,一个是必须保证target类有接口,第二个是如果想要对target类的方法进行代理拦截,那么就要保证这些方法都要在接口中声明,实现上略微有点限制。

  • cglib动态代理是通过继承来实现的,底层则是借助asm(Java 字节码操控框架)来实现的(采用字节码的方式,给A类创建一个子类B,子类B使用方法拦截的技术拦截所有父类的方法调用)。

  • 优缺点:

    • jdk动态代理有一定的局限性,只能基于接口。
    • cglib这种第三方类库实现的动态代理应用更加广泛,且在效率上更有优势。但无法处理final的情况,通过继承。

1.17 jdk动态代理与cglib动态代理的区

  • jdk动态代理只能对实现了接口的类生成代理,而不能针对类 ;
  • cglib是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法 ;
  • 因为是继承,所以该类或方法最好不要声明成final ,final可以阻止继承和多态。

1.18 写出三种单例模式的实现

1.18.1 单例模式之饿汉式

在类加载时就初始化一个类对象,使用静态加载,但是在类加载时就实例化对象,单例对象较大的时候会影响系统加载速度

public class Singleton {

    private Singleton() {
        System.out.println("This is constructor.");
    }

    private static Singleton instance = new Singleton();

    public static Singleton getInstance () {
        return instance;
    }

    public static void main(String[] args) {
        Singleton.getInstance();
    }
}

1.18.2 延迟加载的singleton

只有在访问到单例对象的时候才去检查和实例化单例对象,满足延迟加载,但多线程访问时需要加线程同步,影响访问效率

public class LazySingleton {

    private LazySingleton() {}
    private static LazySingleton instance ;

    public synchronized static LazySingleton getInstance () { //对于多线程访问的需加synchronized
        if (instance == null) {
            instance = new LazySingleton();
        }
        return instance;
    }

}

1.18.3 使用静态内部类来作为singleton的容器

既能延迟加载,又能保证线程安全

public class StaticSingleton {

    private StaticSingleton() {}
    private static class SingletonHolder { //私有内部类在StaticSingleton 加载时,不初始化
        private static StaticSingleton instance = new StaticSingleton();
    }

    public static StaticSingleton getInstance () {
        return SingletonHolder.instance;
    }
}

1.19 请结合OO设计理念,谈谈访问修饰符public、private、protected、default在应用设计中的作用。

  • OO 即 Object Oriented 面向对象的设计模式。
  • public:Java语言中访问限制最宽的修饰符,一般称之为“公共的”。被其修饰的类、属性以及方法不仅可以跨类访问,而且允许跨包(package)访问。
  • private:Java语言中对访问权限限制的最窄的修饰符,一般称之为“私有的”。被其修饰的类、属性以及方法只能被该类的对象访问,其子类不能访问,更不能允许跨包访问。
  • protected:介于public 和 private 之间的一种访问修饰符,一般称之为“保护形”。被其修饰的类、属性以及方法只能被类本身的方法及子类访问,即使子类在不同的包中也可以访问。
  • default:即不加任何访问修饰符,通常称为“默认访问模式“。该模式下,只允许在同一个包中进行访问。
  • 作用域基本如下,越往下越小。
    作用域当前类同一package子孙类其它package
    public
    protected×
    default××
    private×××

1.20 深拷贝和浅拷贝区别。

  • 浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。即对象的浅拷贝会对“主”对象进行拷贝,但不会复制主对象里面的对象。”里面的对象“会在原来的对象和它的副本之间共享。
  • 简而言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象
  • 深拷贝:深拷贝是一个整个独立的对象拷贝,深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。
  • 简而言之,深拷贝把要复制的对象所引用的对象都复制了一遍

00. 参考链接


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!