面试题集01

面试题集01


  1. 什么是Java虚拟机? 为什么Java被称作是”平台无关的编程语言”

    Java虚拟机是一个可以运行Java字节码文件的虚拟机进程,Java源文件被编译成能被Java虚拟机执行的字节码文件; Java程序编写一次就能在不同平台执行,只需要在平台上安装Java虚拟机.由JVM负责和底层通信,把java字节码文件翻译成平台认识的机器码.

  2. JDK和JRE的区别是什么?

    JRE是Java运行时所需要的环境, 包含了java虚拟机,java基础类;JDK是Java开发工具包,JDK包含了JRE,此外还有编译Java源文件的javac,还包含了一些调试分析工具,比如jconsole,jvisualvm等.

  3. static关键字是什么意思? Java中是否可以覆盖(override)一个private或者static方法?

    static关键字用来声明静态的意思,可以修饰成员变量和成员函数,还能修饰内部类.被static修饰的静态方法,只能访问静态成员,不能访问非静态成员.静态是随着类的加载而加载,因此可以直接用类名访问
    override覆盖是重写的意思,重写就是指子类重写父类方法,方法名返回值以及参数都相同; 但子类的访问权限不能低于父类的,private是私有修饰符,该方法不能被继承因此无法重写,static修饰的静态方法也不能重写,因为重写的方法是基于运行状态绑定的, 由实例化后的对象来决定执行那个方法,而static是编译时就静态绑定在类上的,static静态方法与任何对象都无关,所以概念上不适用

  4. Java支持的数据类型有那些? 什么是自动拆箱装箱

    Java语言支持的8种基础数据:byte,short,int,long,float,double,boolean,char
    自动拆箱装箱是指Java编译器在基本数据类型和对应的对象包装类型之间的自动转换.

  5. 进程和线程的区别是什么

    进程是执行着的应用程序,而线程是进程内部的一个执行序列, 一个进程可以有多个线程

  6. 创建线程有几种不同的方式?你喜欢哪一种?为什么?

    创建线程有四种方式

    1. 继承Thread类

    2. 实现Runable接口

    3. 实现Callable接口

    4. 使用Executor创建线程池

      喜欢实现Runable接口, 因为这不需要继承类, java不支持类的多继承,一旦继承Thread就无法在继承其他类了.

  7. 概括解释线程的几种可用状态

    1. 新建(new): 新建了一个线程对象
    2. 可运行(runable): 线程创建后,其他线程调用了该线程对象的start()方法,该状态的线程位于可运行线程池中,等待被线程调度选中,获得CPU使用权
    3. 运行(running): 可运行状态的线程获得了CPU执行权,执行程序代码
    4. 阻塞(block): 阻塞状态是线程因为某种原因放弃了CPU使用权,暂停运行,知道线程重新进入可运行(runable)状态;阻塞情况有三种
      1. 等待阻塞: 运行(running)线程执行o.wait()方法, JVM会把该线程放入等待队列中
      2. 同步阻塞: 运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池.
      3. 其他阻塞: 运行(running)的线程执行Thread.sleep()或t.join()方法时,或者发生I/O请求时,JVM会把该线程设成阻塞状态,当sleep()超时,join()等待线程终止或超时,io处理完,线程重新进入可运行(runable)状态
    5. 死亡(dead): 线程run(),main()方法结束时,或者因异常退出run()方法,则线程结束生命周期,死亡的线程不可再次复生.
  8. 同步方法和同步代码块的区别是什么?

    同步方法默认用this或者当前类class对象作为锁.
    同步代码块可以选择以什么来加锁,比同步方法要更细颗粒度,可以使用同步代码块来修饰可能发生同步问题的部分代码, 而不是整个方法
    同步方法使用synchronized修饰词
    同步代码块使用synchronized(object){}, 括号中的object指的是锁对象, 用this代表调用该方法的对象.

  9. 在监视器(Monitor)内部,是如何做到线程同步的? 程序应该做那种级别的同步?

    在一个对象的头部信息中,保存了锁状态,锁状态存放了一个线程ID,一个锁状态位,还有一个锁监视器标记位,其指针指向Monitor对象,每个对象都拥有对应的Monitor对象.Monitor中有一个执行当前锁持有者线程的指针,还有两个队列,EntryList和WaitList,当线程执行wait时,进入WaitList队列,唤醒后,进入EntryList; 当持有锁的线程释放锁后,指针会执行EntryList中下一个要持有锁的线程.只有Monitor执行的锁持有者才能执行锁中的代码.

  10. 什么是死锁

    死锁是指多个线程因争夺资源进入一种僵持状态,若无外力,这些线程都无法继续执行
    线程争夺互斥资源,且该资源被占有后无法被抢,在一个线程获得互斥资源后,又请求其他资源,而其他资源已被占有,又不释放自己的资源,且整个程序中存在线程资源循环等待链, 比如一个占据了A请求B 而另一个占据了B请求A.

  11. 如何确保N个线程访问N个资源,同时不导致死锁

    指定线程获取锁的顺序, 如果所有线程都按照指定的顺序获取锁,就不会导致死锁了

  12. Java集合框架中的基本接口有
    Collection:代表一组对象的集合,每一个对象都是它的子元素

    List:继承Collection,是有顺序的Collection,可以包含重复元素
    Vector: 线程安全的List
    Set: 不包含重复元素的Collection
    Queue: 用于实现队列的数据结构

    Map:key-value对象,把(键)key映射到值(value)的对象,键不能重复.

  13. 为什么集合类接口没有实现Cloneable和Serializable接口

    接口没有实现, 但是具体的实现类实现了这两个接口,比如HashMap,HashSet,LinkedList,ArrayList.
    Cloneable用于实现克隆; Serializable用于实现序列化,用于网络传输或者写入

  14. 什么是迭代器

    迭代器Iterator接口提供了很多对集合元素进行迭代的方法, 每个集合类都包含了可以返回迭代器的迭代方法,迭代器可以在迭代的过程中操作底层集合的元素,如删除

  15. Iterator和ListIterator的区别是什么?

    Iterator可以依赖遍历Set和List集合,但是ListIterator只能依赖遍历List
    Iterator对集合只能前序遍历, ListIterator既可以前序也可以后序
    ListIterator实现了Iterator接口,并扩展了以下功能, 如增加元素,替换元素,获取前一个和后一个元素的索引等.

  16. fail-fast(快速失败)和fail-safe(安全失败)的区别是什么

    1. fail-fast:
      在使用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的结构进行了修改,(增加,删除),则会抛出ConcurrentModifficationException.
      原理是 迭代器在遍历时直接访问集合中的内容,且在遍历过程中使用一个modCount变量,集合在被遍历期间如果结构发生变化,就会改变modCount的值,每当迭代器使用hashNext(),next()方法时,都会监测modCount变量是否为expectedmodCount值,是的话返回遍历,否则抛出异常.要删除元素是使用iterator中的remove()而不要直接使用集合的remove方法
      java.util下的集合类都是使用fail-fast, 不能在多线程下发生并发迭代中的修改
    2. fail-safe:
      采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制一份集合内容, 在拷贝的集合上操作, 但是只能访问拷贝时的元素, 拷贝后发生的修改是不能访问的
  17. Java中的HashMap的工作原理是什么?

    HashMap是以键值对的形式存储元素,HashMap在jdk1.8中使用数组+链表+红黑树的形式来存放数据, 每个数据都封装成节点, 节点中保存了key-value,HashMap使用链地址法来解决hash冲突,数据的key通过计算hash值映射到数组上,数组的每个位置存放了一个链表头结点,相同hash值存放在相同数组下表里的链表中.
    put方法第一步, 计算元素key对应的hash值对应的索引位置,判断是红黑树还是链表,链表在尾部插入,(链表长度大于8时转成红黑树), 如果是红黑树插入完后进行自平衡. 插入完如果当前容器容量大于阈值,要进行扩容操作, 扩容操作是新建一个长度2倍的数组, 因此每个节点的hash值都要重新计算, 所以扩容操作将耗费巨大资源

  18. hashCode()和equals()方法的重要性体现在什么地方

    Java中的HashMap使用hashCode和equals方法来确定节点索引,根据键计算hash值,然后与内部数组长度取余,得到在数组中的索引,然后用equals来判断key是否相等,查找,添加,删除中, 都需要进行判断key是否相等,而equals比较的正式hashCode计算的值,hashCode默认实现是返回对象地址.

  19. HashMap和HashTable有什么区别

    HashMap和HashTable都实现了Map接口,但是有以下区别
    HashMap允许键和值是null,而HashTable不允许键或值为null
    HashTable是同步的,线程安全,而HashMap不是
    由于HashTable需要维持线程安全,所以效率低于HashMap
    HashTable是遗留类,不推荐使用, 目前有ConcurrentHashMap替代

  20. 数组Array和列表ArrayList有什么区别,什么时候要用Array而不用ArrayList

    Array可以包含基本类型和对象类型, ArrayList只能包含对象类型(基本类型用包装类型来替代)
    Array大小固定,ArrayList可以动态扩容
    ArrayList提供了更多方法和特性
    对于固定长度的数据,可以使用Array,它的效率比ArrayList高

  21. ArrayList和LinkedList有什么区别?

    ArrayList是基于数组实现的集合,LinkedList是使用节点来实现的集合,节点通过指针连接起来
    ArrayList有动态扩容机制, 扩容的时候才会开辟内存,LinkedList添加一次开辟一次内存
    LinkedList的删除,插入,添加操作更快
    LinkedList实现了链表,队列,栈数据结构 ArrayList只是动态数组

  22. Comparable和Comparator接口是干什么的,有什么区别

    Comparable接口提供了一个compareTo()方法, 类实现这个接口后, 可以判断该类的实例化对象间的大小
    Comparator提供了compare()和equals()两个方法,compare()接收两个参数,用来判断大小,equals()方法需要一个对象作为参数,它用来决定输入的参数是否和comparator(Comparator实例对象)相等
    在集合创建中, 构造函数往往接收Comparator对象,用于定义如何比较元素大小
    或者直接让元素实现Comparable接口,定义比较方法. 一个是创建集合是定义, 一个是直接在元素中定义.

  23. 什么是Java优先级队列(PriorityQueue)

    PriorityQueue是一个基于优先级堆的无界队列,它的元素是按照自然顺序排序的,在创建的时候,可以传入比较器定义如何排序,PriorityQueue不允许null值,因为null无法排序, PriorityQueue可以按照任何顺序插入, 但取出是保持排序顺序,它内部使用自我调整二叉树,所以检索的时间复杂度是O(logn); 二分查找

  24. 你了解大O符号嘛,你能给出不同数据结构的例子嘛?

    大O符号描述了一个程序运行时所需要的时间复杂度上界
    大O还可以用来描述空间复杂度

  25. 如何权衡是使用无序的数组还是有序的数组

    有序数组的最大好处是查找的时间复杂度是O(logn)(二分查找),而无序数组是O(n)(遍历查找),
    有序数组的缺点是插入时需要比较数据,找到合适的位置,复杂度O(n),而无序数组可以插入到最后位置,复杂度O(1)

  26. Java集合类框架的最佳实践有哪些

    图来自https://blog.csdn.net/qq_36711757/article/details/80464499
    Java集合类如何选择

  27. Enumeration接口和Iterator接口有什么区别

    Enumeration速度是Iterator2倍,同时占用更少内存,但是Iterator远比Enumeration安全
    Iterator支持fail-fast机制,Enumeration不行
    Iterator可以删除底层集合元素,Enumeration只能遍历访问

  28. HashSet和TreeSet有什么区别

    HashSet基于HashMap实现, 把元素保存为key,实现了不重复元素; TreeSet底层是红黑树结构,
    HashSet是无序的, TreeSet是有序的,一次TreeSet创建时要传入比较器,或者元素实现Comparable

  29. Java中垃圾回收有什么目的? 什么时候进行垃圾回收?

    垃圾回收的目的是识别并且回收不再使用的对象,来释放内存空间
    触发垃圾回收的时机是当前程序空闲, 或者Java堆空间不足时, 或者人为调用GC方法时
    JVM将部分对象分为gc root对象,如栈中引用到的对象. 没有直接或间接被root对象引用的对象,都会被GC(可达性分析)

  30. System.gc()和Runtime.gc()会做什么事?

    两个方法都是用来提示JVM进行垃圾回收,但是是否立即执行GC要看JVM情况, 两者的行为实际上没有什么不同

  31. finalize()方法什么时候被调用? 析构函数(finalization)的目的是什么?

    GC决定回收某对象时,就会运行该对象的finalize()方法,如果子类没有重写这个方法,就直接回收,不做其他事;但是在Java中,如果内存一直充足,GC可能一直不会进行,也就是说finalize()方法可能永远不会执行.
    当你使用到native方法时, 就需要在finalization里调用C的释放函数.

  32. Java堆的结构是什么样子的?什么是堆中的永久代?

    JVM的堆是运行时数据区,所有类的实例和数组都是在堆上分配内存,在JVM启动时创建,JVM堆内存管理采用垃圾回收
    永久代用于存放静态文件, 如Java类,静态变量和静态方法等,在Java8中取消了永久代, 改为了元空间(常量池), 在堆空间之外

  33. 串行(serial)收集器和吞吐量(throughtput)收集器的区别是什么?

    串行GC, 整个垃圾回收过程采用单线程,而且它进行垃圾回收时会暂停其他所有工作,适合单CPU,或者客户端级别, 它是JVM在Client模式下的默认GC
    吞吐量GC, 采用多线程的方式来完成GC,适合于吞吐量要求高的场景, 主要采用复制算法

  34. 在Java中,对象什么时候可以被垃圾回收?

    当一个对象到GC Roots不可达时,进行第一次标记,第一次标记后,判断对象是否重写了finalize()方法和是否已经被调用finalize()方法,若没有重写或者已经被调用过一次了,则进行回收; 把重写了finalize()并且还没被调用过的对象仿佛F-Queue中,
    第二次标记之前,对象如果执行finalize()方法并自救,对象不会被回收,否则完成第二次标记后,进行回收, 但是finalize()方法并不可靠,自救不一定成功

  35. JVM的永久代中会发生垃圾回收嘛?

    会的, 如果永久代满了或者超过了阈值,都会触发Full GC; Java8中取消了永久代

  36. Java中的两种异常类型是什么

    Java中的异常分为受检查异常和非检查异常(又叫运行时异常)
    运行时异常在编译时不会检查,即使没有用try..catch也没有throws,还是会编译通过
    受检查异常,必须要try…catch或throws才能通过编译.
    Error是错误,由JVM生成并抛出

  37. Java中Exception和Error有什么区别?

    Exception和Error都是Throwable的子类,Exeception用于用户程序可以捕获的异常情况,可以捕获然后处理异常,使程序恢复运行;Error定义了不期望被用户捕获的异常,如系统崩溃,JVM错误等等

  38. throw和throws有什么区别?

    throw用于方法内部, throws用于方法声明上.
    throw后跟异常对象,Throws后跟异常类型
    Throw后只能跟一个对象,Throws后可以跟多个异常类型

  39. 异常处理完成以后,Exception对象会发生声明变化

    Exception对象会在下一轮GC中被回收

  40. finally代码块和finalize()方法有什么区别?

    finally跟在try..catch代码块后面, 无论是否有异常, finally代码块都会执行,它主要用来释放资源, 比如关闭流
    finalize()方法是Object定义的方法,它是对象被回收之前由JVM来调用的,通过重写该方法, 可以实现GC自救

  41. 什么是JDBC?

    JDBC用来适配java代码和数据库,允许开发者用Java语言和标准SQL来写数据库应用程序,而不需要关系特定的数据库细节.

  42. Class.forNmae()方法有什么作用?

    加载参数指定的类的.class文件,返回参数指定的类或接口对应的Class对象,每个类对应一个class对象, class对象执行newInstance()方法会放回该类的实例化对象.
    Class.forName()可以根据类名来动态加载类创建对象

  43. 数据库连接池是什么意思

    数据库连接池是一个线程池, 其中维护了与数据库的连接, 连接的建立和销毁都是需要时间的, 数据库连接池维护一组数据库连接, 使用时获取一个连接,用完后归还到池中, 有效复用

  44. 什么是RMI?

    Java远程方法调用(Java RMI)是Java API对远程过程调用(RPC)的一种实现,远程方法调用允许一个Java虚拟机上的对象调用另一个Java虚拟机上的对象的方法,从而使Java编程人员可以方便的在网络环境中作分布式计算