Java面试题总结(一)

Java面试题总结(一),第1张

Java面试题总结(一)

文章目录
      • 1. JDK、JRE、JVM的区别和联系
      • 2. ==与equals
      • 3. final关键字的作用及为什么局部内部类和匿名内部类只能访问局部final变量?
      • 4. String、StringBuild、StringBuffer
      • 5. 重载和重写
      • 6. 接口和抽象类的区别
      • 7. Set与List的区别
      • 8. hashCode与equals
      • 9. ArrayList与linkedList区别
      • 10. HashMap与HashTable的区别?底层实现是什么?
      • 11. ConcurrentHashMap原理
      • 12. Java类加载器
      • 13. 双亲委派模型
      • 14. Java中的异常体系
      • 15. GC是如何判断对象是否可以被回收
      • 16. 线程的生命周期,线程有哪些状态?
      • 17. sleep()、wait()、join()、yield()的区别
      • 18. 守护线程的理解
      • 19. ThreadLocal的原理和使用场景
      • 20. 并发的三大特性:
      • 21. 为什么使用线程池?线程池的参数说明?
      • 22. Redis中的击穿、穿透、雪崩?
      • 23. Spring事务什么时候会失效?
      • 24. SpringBoot 、Spring、SpringMVC有什么区别?
      • 25. SpringMVC工作流程

1. JDK、JRE、JVM的区别和联系

JDK是java开发工具包,供java开发人员使用;

JRE:是Java运行时环境,用户想要运行java程序就需要有JRE

JVM是Java虚拟机,Java源代码编译成class文件后,由JVM解释为对应 *** 作系统的字节码。

JDK包含了JRE,JRE包含了JVM。

2. ==与equals

Object类中的equals实现就是用==,一般来说==对比的基本类型比较的是值,引用类型比较的是引用地址,String类重写了equals方法

3. final关键字的作用及为什么局部内部类和匿名内部类只能访问局部final变量?

final关键字可以修饰:

  1. 类:不可被继承
  2. 方法:不可被重写
  3. 变量:
    1. 值类型:不可修改
    2. 引用类型:引用地址不可变,但是引用内的值可以修改。

局部内部类或匿名内部类只能访问局部final变量的原因是内部类编译后实际上是单独的类文件,如果外部类调用完后被标记为垃圾,可能会被gc去回收,此时内部类调用局部变量如果不是final就存在问题了。

4. String、StringBuild、StringBuffer

String类被final修饰,表示不可变的最终类。StringBuffer和StringBuilder都是可变字符串,一般在不变的字符串中使用String,性能优先的情况下使用StringBuilder,在多线程中确保同步时使用StringBuffer

5. 重载和重写

重载:两同(同一个类,方法名相同)、三不同(参数:类型、个数、顺序不同)与方法返回值和访问修饰符无关。

重写:有继承关系的父子类中,子类覆盖父类同名的方法,方法声明相同,实现不同。

总结:重载看参数,重写看方法实现

6. 接口和抽象类的区别

抽象类中可以有普通方法,接口中都是抽象方法

只能继承一个抽象类,但是可以实现多个接口,接口之间可以有多继承。

抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final 类型

从设计目的看,接口是一种规定类“有”什么的约束(like a);而抽象类的目的是代码的复用,当某些类有一些相同的行为时就可以派生出一个抽象类(is a)。

7. Set与List的区别

List是有序,可重复的集合,按对象进入的顺序保存对象,允许有多个null,获取元素时可以用iterator取出元素再遍历,也可以用get下标的方式获取指定元素。

Set是无序不可重复的集合,最多有一个null,取元素时只能用iterator遍历获取。

都是Collection的子接口

8. hashCode与equals

两个对象相等,则它们的hashCode值也是相同的;这两个对象分别调用equals方法返回的都是true。

但是如果两个hashCode值相同,这两个对象不一定相等。因此,equals方法被重写,则hashCode方法也要被重写。

9. ArrayList与linkedList区别

ArrayList是动态数组实现的,使用连续的内存,适合用下标直接访问。扩容时会新建一个数组,把旧数组所有元素复制过来,这样比较消耗性能。一般如果在创建ArrayList集合时指定合适的初始大小,并采用尾部插入的方式效率会更高。

linkedList是链表实现,在内存中分散存储。需要用Iterator获取元素。

10. HashMap与HashTable的区别?底层实现是什么?

区别:

HashMap是线程不安全的,HashTable是线程安全的(方法加了synchronized),如果保证多线程安全可以采用juc包下的ConcurrentHashMap;HashMap允许key和value为null,HashTable不允许。

HashMap底层实现: HashMap由数组+链表实现。
从jdk8开始,当链表高度达到8,数组长度达到64时,链表转变为红黑树,元素以内部类Node节点存在。

计算key的hash值,二次hash后对数组长度取模,对应到数组下标。
如果该下标没有值,那么直接将Node存入该位置。
如果有值,也就是产生了hash冲突,先进行equal比较,相同则取代该元素。不同的话就判断链表高度插入链表。链表高度达到8,数组长度达到64时,链表转变为红黑树,长度低于6时则红黑树转变为链表。
如果key为null,存在下标为0的位置。

11. ConcurrentHashMap原理

数据结构:synchronized+CAS(乐观锁)+Node+红黑树,Node的val和next都使用了volatile修饰,保证可见性。查找、替换、赋值 *** 作都使用了CAS。

读 *** 作不加锁。

12. Java类加载器

JDK自带的有三个类加载器:BootstrapClassLoader、ExtClassLoader、AppClassLoader

Bootstrap 默认负责加载 %JAVA_HOME%/lib下的jar包和class文件

ExtClassLoader负责加载%JAVA_HOME%/lib/ext文件夹下的jar包和class类

AppClassLoader负责加载classpath下的类文件

自定义类加载器

13. 双亲委派模型

14. Java中的异常体系
  • Throwable
    • Exception
      • RuntimeException 程序运行过程中
      • CheckedException 程序编译过程中
    • Error
15. GC是如何判断对象是否可以被回收

可达性分析法:从GC Roots开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GCRoots没有任何引用链时,证明此对象是不可用的。那么虚拟机就判断是可回收对象。

GCRoots的对象有:

  • 虚拟机栈(栈帧中的本地变量表)中引用的对象
  • 方法区中类静态属性引用的对象
  • 方法区中常量引用的对象
  • 本地方法栈中JNI(Native)引用的对象。
16. 线程的生命周期,线程有哪些状态?

线程状态:创建、就绪、执行、阻塞(等待阻塞、同步阻塞、其他阻塞如sleep等)、死亡

17. sleep()、wait()、join()、yield()的区别

sleep()是Thread类的方法,wait()是Object类的方法,sleep()执行后不会释放锁,在到达时间后会直接处于就绪状态争夺CUP执行全,wait()会释放锁,需要被其他线程调用notify()或notifyAll()唤醒,唤醒后重写获取锁。

sleep()可以在任何地方使用,wait()只能配合synchronized使用

yield()执行后线程直接进入就绪状态,马上释放CUP执行权,但依然保留了争夺CUP的执行资格,可能下次CUP进行线程调度时还会让这个线程继续执行。

join()执行后线程进入阻塞状态,如:在主线程中A线程调用了join()方法,则主线程进入阻塞,直到A线程执行结束或中断线程,主线程才继续执行。

18. 守护线程的理解

为所有非守护线程服务的线程,任何一个守护线程都是整个JVM中所有非守护线程的保姆。

注意:守护线程的终止是自身无法控制的,所以不要把IO、File等重要 *** 作逻辑分配给它。

经典的守护线程如:GC垃圾回收线程。

thread.setDaemon(true)必须在thread.start()之前设置,否则会抛异常。

在守护线程中产生的线程也是守护线程。

19. ThreadLocal的原理和使用场景

ThreadLocal是线程本地存储,在每次线程创建时都会有一个ThreadLocalMap对象,以当前线程作为key,访问内部的value。

使用场景:JDBC Connection、Session

ThreadLocal引起的内存泄露的处理:

  • 每次使用完ThreadLocal都调用它的remove()方法清除数据
  • 将ThreadLocal变量定义为private static ,这样就一直存在ThreadLocal的强引用,也就能保证任何时候都能通过ThreadLocal的弱引用访问到Entry的value值,进而清除掉。

ThreadLocal引起的一个错误:

  1. 在多租户项目中,每个租户用的一个唯一的平台号,程序中有地方把当前用户的平台号放在一个ThreadLocal变量中,出现问题:有时候用户的平台号会错乱,就是一个人的平台号会出现在另一个登录用户中,最后定位到问题是在线程池上。ThreadLocal中的值实际上保存在线程对象中。在有线程池的情况下,线程用完以后会回到线程池中,下次需要线程是就从线程池中获取一个。如果ThreadLocal中的值没有被清空,这个值就会保留下来继续使用,导致出现问题。

    解决办法:使用完ThreadLocal后调用remove()方法清除数据。

20. 并发的三大特性:

原子性、一致性、可见性

21. 为什么使用线程池?线程池的参数说明?

线程的创建和销毁都是比较消耗资源的,线程池可以复用线程,避免频繁的创建和销毁线程,提升效率。

	public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

线程池参数:

corePoolSize– 池中要保留的线程数,即使它们处于空闲状态,除非设置了 allowCoreThreadTimeOut
maximumPoolSize– 池中允许的最大线程数
keepAliveTime– 当线程数大于核心时,这是多余空闲线程在终止前等待新任务的最长时间
unit– keepAliveTime 参数的时间单位
workQueue– 用于在执行任务之前保存任务的队列。这个队列将只保存 execute 方法提交的 Runnable 任务。
threadFactory– 执行程序创建新线程处理程序时使用的工厂
handler–由于达到线程边界和队列容量而阻塞执行时使用的处理程序

22. Redis中的击穿、穿透、雪崩?

穿透:当请求一个不存在的key时会直接对持久化层发起请求,这样就失去了缓存的意义。比如id是从0开始的,用-1去请求;处理办法有两种,一种是增加校验,另一种是布隆过滤器。

击穿:一个非常热点key,当它失效时数据库会承受大量的并发访问。解决办法:热点数据永不过期,或者加上互斥锁。

雪崩:大量的缓存失效或者缓存不可用时直接请求数据库;解决办法:缓存层设计为高可用,分布式部署;设置热点key永不过期;缓存过期时间设置随机,防止大量数据同时过期。

23. Spring事务什么时候会失效?
  1. 发生自调用,类里边使用this调用本类的方法(this通常省略),此时this对象不是代理类,而是实际类本身。
  2. 数据库不支持事务(如MySQL使用了MyISAM引擎)
  3. 没有被Spring管理
  4. 异常被吃掉,事务不会回滚(或者抛出的异常没有被定义,默认为RuntimeException)
24. SpringBoot 、Spring、SpringMVC有什么区别?

Spring框架是一个IOC容器,用来管理bean。使用DI实现控制反转,可以方便整合各种框架。AOP功能可以减少重复代码,将一下与业务无关的代码通过切面抽取,比如日志、异常等。

SpringMVC是Spring对web框架的一种解决方案。提供了一个总的前端控制器Servlet,用来接受请求,然后定义一套路由策略(url到handle的映射)及适配执行handle,将handle结果使用视图解析技术生成视图展示给前端。

SpringBoot是Spring提供的一个快速开发工具包,让程序员能更方便快捷的开发spring应用,简化了配置,整合了一系列解决方案(starter机制),可以开箱即用。

25. SpringMVC工作流程

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5636696.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存