Java 面试问题以及答案 (2020)
in 面试题 with 0 comment

Java 面试问题以及答案 (2020)

in 面试题 with 0 comment

当前对面试中遇到的面试题进行记录归纳,以防犯下重复的错误

Java 基础问题

1、重载和重写的区别?

  • 重载就是在同一个类中,根据传入的数据不同,进行不同的处理。重载发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同。
  • 重写是子类覆写父类的方法。方法名、参数、返回值都必须与父类的相同,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;父类的构造方法不能被重写;父类方法中被final、static、private 修饰的不能被重写。

2、接口是什么?为什么要使用接口而不使用具体类?

  • 接口是一组规则的集合,规定了实现本接口的类或者接口必须拥有的一组规则。
  • 接口是对行为的抽象,是一种行为的规范。
  • 接口是一种用来定义程序的集合,它描述可属于任何类或结构的一组行为。
     
  • 增强系统灵活性:当下层需要改变时,只要接口以及接口功能不变,则上层不需要任何改变。
  • 只要接口保持一致,不同层次或部件的开发人员可以并行开工。
  • 对修改封闭,对扩展开放

3、接口和抽象类的区别是什么?

  • 接口中方法只能使用 public 修饰,所有的方法都不能有实现(Java 8 中可以有默认方法);抽象类中可以有具体方法实现(非抽象方法),也可以使用public以外的修饰符修饰。
  • 接口中变量除了使用final 、static 修饰的外,不能有其他变量;抽象类则不是。
  • 一个类可以实现多个接口;一个类只能继承一个类。(使用接口可以间接实现类的多继承)
  • 从设计层面来看,抽象是对类的抽象,抽象类是一种模板设计;接口是对行为的抽象,接口是一种行为规范。

4、== 与 equals 的区别?

  • ==:对基本类型来说,==比较两个基本类型值的大小;对引用类型来说,比较他们的引用地址是否相同。
  • equals:判断两个对象值是否相等。如果没有覆写 equals() 方法,则其还是调用 == 比较。覆写了 equals() 则按照自己的规则比较两个对象的大小。

5、List 和 Map 的区别。

  • List 实现了 Collection 接口,Map 与其没有关系。
  • List 是存储单列数据的集合,Map 是双列存储数据,通过 key-value 的形式。
  • List 存储的数据是有序的、可重复的;Map 存储数据 key 不允许重复,value 允许重复。

6、进程、线程以及程序的概念。

  • 程序:是含有指令和数据的文件,被存储在磁盘或者其他的存储设备当中,也就是说程序是静态的代码。
  • 进程:是程序执行一次的过程,是系统运行程序的基本单位,因此进程是动态的。系统运行一个程序,即是一个进程从创建、运行到消亡的过程。
  • 线程:与进程类似,线程比进程更小的一种执行单位。一个进程执行过程中会有多个线程产生。与进程不同的是,同一进程下,多个线程共享内存和一块资源空间。所以,系统产生一个线程,或者在多个线程中切换消耗的资源要比进程小很多。正因为如此,线程被称为轻量级进程。(Java 程序即为多线程,main 方法为主线程,GC 为一个守护线程)

7、线程有哪些状态?

  • NEW : 新建状态,线程对象已经创建,但是未启动。
  • RUNNABLE: 就绪状态,可运行状态,调用了 start() 方法。
  • BLOCKED: 堵塞状态。
  • WAITING : 等待状态。
  • TIMED_WAITING : 线程等待一段时间。
  • TERMINATED : 终止。

8、实现多线程有哪些方式?

1、实现 Ruunable 接口,重写 run() 方法;

public class MyThread implements Runnable {

    @Override
    public void run() {
        System.out.println("线程启动 ["+Thread.currentThread().getName()+"]");
    }

    public static void main(String[] args){
        MyThread myThread = new MyThread();
        Thread thread = new Thread(myThread);
        System.out.println("主线程 ["+Thread.currentThread().getName()+"]");
        thread.start();
    }
}

2、继承 Thread 类,重写 run() 方法;

public class MyThread2 extends Thread{

    @Override
    public void run() {
        System.out.println("线程 >>> "+Thread.currentThread().getName());
    }

    public static void main(String[] args){
        MyThread2 myThread = new MyThread2();
        Thread thread = new Thread(myThread);
        System.out.println("主线程 ["+Thread.currentThread().getName()+"]");
        thread.start();
    }
}

3、 实现 callable 接口 ;重写 call() 方法。Callable可以返回执行结果,是个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。并且其 call() 方法允许抛出异常。

public class MyThread3 implements Callable<String>{
    @Override
    public String call() throws Exception {
        System.out.println("线程 >>> "+Thread.currentThread().getName());
        return Thread.currentThread().getName();
    }

    public static void main(String[] args){
        MyThread3 myThread3 = new MyThread3();
        FutureTask<String> futureTask = new FutureTask<>(myThread3);
        System.out.println("主线程 ["+Thread.currentThread().getName()+"]");
        new Thread(futureTask).start();
        try {
            Thread.sleep(1000);
            System.out.println("线程返回值 >>> "+futureTask.get());
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

9、sleep() 与 wait() 的区别。

  • sleep() 方法是 Thread 类的静态方法;wait() 方法是 Object 类的方法。
  • 调用 sleep() 方法会使线程暂停执行,但是不会释放对象锁,等待指定时间结束后,会自动恢复; 调用 wait() 方法会导致当前线程放弃对象锁,进入对象的 等待池(wait pool) 中,直到对象调用 notify()notifyAll() 方法才能唤醒等待池的线程进入 等锁池(lock pool), 如果线程重新获取对象锁就会进入就绪状态。

10、synchronized 与 lock 的区别。

  • synchronized 可以给类、代码块、方法加锁; lock 只能给代码块加锁。
  • synchronized 不需要手动加锁和释放锁;lock 需要手动加锁和释放锁,如果没有使用 unLock() 释放锁就会造成死锁。
  • lock 可以知道有没有成功获得锁,而 synchronize 无法办到。

11、volatile 关键字。

  • 保证变量的可见性。当变量改变时,会立即更新到主内存中,读取变量时,会立即从内存刷新。
  • 禁止指令重排序优化。(指令重排序:是指CPU采用了允许将多条指令不按程序规定的顺序分开发送给各相应电路单元处理)

12、什么是单例模式和原型模式?

单例模式 (singleton) : 保证类在内存中只创建一次。

  • 在资源共享的情况下,避免由于资源操作时导致的性能损耗等。
  • 在控制资源的情况下,方便资源之间的互相通信(例如:线程池)。

原型模式 (prototype) : 通过对象实例确定创建对象的种类,并通过拷贝创建一个新的实例。

  • 原型模式就是从一个对象创建一个新的对象,使新的对象有原对象的特征。

13、产生死锁的条件。

  • 互斥条件。(进程独占资源)
  • 请求与保持。(进程因为请求资源而阻塞时,对以获取的资源不释放)
  • 不剥夺条件。(进程已经获得的资源,在未使用完之前,不能强行剥夺)
  • 循环等待。(若干个进程之间形成了一种头尾相接循环等待资源的状态)

Spring 框架问题

1、为什么要使用Spring?

  • Spring 提供 控制反转(IoC)技术,通过 IoC 容器管理依赖的对象,不需要自己创建和管理对象的依赖,更加轻松的实现解耦。
  • Spring 提供了事务支持,使得事务操作很方便。
  • Spring 提供了面向切面编程,这使得处理某一类问题变得很方便,如:日志打印,权限控制等。
  • Spring 能更方便的集成一些框架,如: MyBatis、Struts2、Spring MVC 等。

2、什么是 AOP?

  • AOP : 面向切面编程。通过预编译方式和运行期动态代理的方式,实现程序功能统一维护的一种技术。例如: 日志打印、权限控制、异常处理等。

3、什么是 IoC ?

  • IoC : 控制反转,是Spring的核心功能。对Spring 框架来说,就是由Spring来控制对象的生命周期和对象间的关系。

4、Spring 注入的方式有哪些?

  • XML配置注册Bean,通过 setter、构造器、工厂注入。
  • 通过注解的方式注入

5、 @Autowired 和 @Resources 注解的区别?

  • @Autowired 注解是Spring的注解,@Resource 注解为java注解
  • @Autowired 注解只能通过byType方式注入Bean;@Resource 注解默认通过byName方式注入Bean,如果没找到会再使用byType方式注入。
  • @Autowired 当根据类型找不到依赖对象时会抛出异常(设置required属性值为false 可以在找不到是返回NULL而不抛出异常),如果我们需要通过名字注入,可以与@Qualifier 配合使用;@Resourc 如果没有指定name属性,并且按照默认的名称仍然找不到依赖对象时, @Resource 注解会回退到按类型装配,如果指定了name属性,则只能通过名字注入。

6、Spring 中开启事务的注解是哪个?

  • @Transactional 配置 其 rollbackFor 属性可以自定义回滚的异常:rollbackFor=Exception.class

7、Spring 中事务的隔离级别有哪些?

  • 默认级别 : 跟随使用的数据库的默认隔离级别。
  • 未提交读 : 最低级的隔离级别,一个事务中未提交的数据,能被另一个事务读到。
  • 提交读     : 一个事务提交之后,才能被其他事务读取到。
  • 可重复读 : 表示多次读取统一数据时,都和事务开始时读取的一致,禁止读取其他事务未提交的数据。
  • 可串行化 : 表示所有的事务串行化执行。

MyBatis 框架

1、#{} 与 ${} 的区别?

  • ${} 是变量占位符 ,属于静态文本替换。
  • #{} SQL的参数占位符,MyBatis 会将 SQL 中的#{} 替换为 ?,SQL 执行前会使用PreparedStatement 按顺序给?占位符设置参数。能有效的防止SQL注入。

2、MyBatis 中 inserSelective 和 insert 的区别?

  • 使用 inserSelective 可以选择性的插入字段,有些字段可以不需要插入。
  • 使用 insert 则每个字段都需要插入。
1. inserSelective
   insert into book (name) value ('测试书籍'); 
2. insert
   insert into book (id,name,price) value (1,'测试书籍','100.00');

数据库问题

1. HAVING 关键字的作用?

  • 当HAVING 单独使用时,和 where 关键字类似;下面两个语句结果相同
SELECT * FROM USER WHERE user='root';
SELECT * FROM USER HAVING user='root';
  • HAVING 可以和GROUP BY 配合使用,对分组后的数据进一步筛选 ( 查询包含市大于20的省份 )
SELECT province_name,COUNT(*) AS total FROM city WHERE status=0 GROUP BY province_name HAVING total > 20;

2、PreparedStatement 与 Statement 的区别。

  • PreparedStatement 代码可读性和可维护性较强:虽然使用 PreparedStatement 代码行数变多,但是可读性和可维护性增强。
  • PreparedStatement 批处理效率高,执行速度快:批处理时缓存一个执行计划,重复使用。
  • PreparedStatement 安全性高,能防止 SQL 注入。

3、数据库的隔离级别有哪些?MySQL数据的默认隔离级别是什么?

  • 未提交读 (READ UNCOMMITTED): 事务中的修改,即使没有提交,对其他事务来说也是可见的。
  • 提交读 (READ COMMITTED):一个事务所做的修改,在还未提交时,对其他事务来说是不可见的。
  • 可重复读 (REPEATATABLE READ):保证一个事务中多次读取同样数据的结果都是一致的。
  • 可串行化 (SERIALIXABLE):强制事务串行执行。
  • 在 MySQL 数据库中,默认的隔离级别为 可重复读。

4、数据库设计的三大范式。

  • 第一范式 :
  • 第二范式 :
  • 第三范式 :

5、 并发一致性问题。

在并发环境下,一个事务如果受到另一个事务的影响,那么事务操作就无法满足一致性条件。

  • 丢失数据 :T1与T2两个事务都对同一个数据进行修改,T1先修改,T2随后修改,T2修改的数据会覆盖T1的修改。
  • 脏读 :T1修改一个数据,T2随后读取这个数据。如果T1这时候回滚撤销了本次修改,那么T2读取的数据就是脏数据。
  • 不可重复读 :T2读取一个数据,T1对此数据进行了修改,如果T2再次读取此数据,就会发现此时读取的数据和第一次读取的数据结果不一样。
  • 幻读 : T1 读取一个范围内的数据,T2对此范围内的数据进行删除或者增加,当T1再次读取此范围内的数据时,就会和第一次结果不一样。

6、数据库发生死锁的原因是什么?怎么解决?

发生死锁的原因:

  • 是指两个或者两个以上的进程在执行过程中,因争抢资源而造成的一种互相等待的现象,如果没有外力作用下,它们都将无法推进下去。例如: 线程1 锁住记录1 并等待记录2,线程2 锁住纪录2 并等待记录1,则这两个线程就会发生死锁现象。
  • 系统资源不足; 进程运行推进的顺序不合适;资源分配不当等。

解决的办法:

  • 按同一顺序访问对象。
  • 避免事务中的用户交互。
  • 保持事务简短并在一个批处理中。
  • 使用低隔离级别。
  • 使用绑定连接。