[持续更新]计算机经典面试题基础篇Day1

[通用]计算机经典面试题基础篇Day1

1、jvm的组成

  1. 类加载器(Class Loader):负责将编译后的Java类加载到JVM中,并在运行时动态加载所需的类。
  2. 运行时数据区(Runtime Data Area):是JVM的内存管理区域,主要包括方法区、堆、栈、程序计数器等。
  3. 执行引擎(Execution Engine):负责执行字节码指令,可以通过解释器、即时编译器(JIT)等方式来实现。
  4. 垃圾回收器(Garbage Collector):负责自动管理堆内存的分配和释放,回收无用对象的内存。
  5. 本地方法接口(Native Interface):允许Java代码调用本地的C、C++等语言编写的代码。
  6. JNI(Java Native Interface):允许Java代码与本地代码进行交互,调用本地的C、C++等语言编写的方法。

2、jvm中的堆和栈的理解

  1. 方法区(Method Area):方法区是JVM的一部分,用于存储类的结构信息,如类的字段、方法、常量池、静态变量等。它是所有线程共享的区域。在JVM的规范中,并没有明确规定方法区的实现方式,不同的JVM实现可以有不同的方法区实现方式。
  2. 堆(Heap):堆是JVM中存储对象实例的区域,是Java程序中创建的对象所分配的内存区域。堆是所有线程共享的区域。在JVM启动时就会创建一个堆,堆的大小可以通过启动参数进行调整。堆被划分为年轻代和老年代,其中年轻代又分为Eden空间、Survivor空间(From和To)。
  • Eden空间:对象的初始分配都在Eden空间中进行,当Eden空间满时触发Minor GC,并将存活的对象复制到Survivor空间。
  • Survivor空间:存放从Eden空间中幸存的对象,当Survivor空间满时触发Minor GC,并将存活的对象复制到另一个Survivor空间,同时清空当前Survivor空间。
  • 老年代:存放长时间存活的对象,当老年代满时触发Full GC。
  1. 栈(Stack):栈是JVM中的一个线程私有区域,用于存储方法的局部变量、操作数栈、方法返回值和异常处理信息等。每个线程在执行方法时都会创建一个栈帧,栈帧中保存了方法的局部变量、操作数栈等信息。随着方法的调用和返回,栈帧会被入栈和出栈。
  2. 程序计数器(Program Counter):程序计数器是JVM中的一个线程私有区域,用于记录当前线程执行的字节码指令的地址。每个线程都有一个独立的程序计数器,它指向当前正在执行的指令,在线程切换时会保存和恢复当前线程的程序计数器的值。程序计数器是线程私有的,不会发生内存溢出的情况,并且不会进行垃圾回收。

3、java类加载的三种方式

Java类加载是指将类的字节码文件加载到JVM中,并进行类的初始化和连接的过程。Java类加载有三种方式:

  1. 隐式加载(隐式类加载):当程序在执行过程中使用new关键字创建对象或者调用静态方法、静态变量时,JVM会自动加载相应的类。这是最常见的类加载方式,也是默认的类加载方式。
  2. 显式加载(显式类加载):使用Class.forName()方法来显式加载类。Class.forName()方法会根据提供的类的全限定名(包括包名)来加载类并返回对应的Class对象。这种方式可以动态加载类,根据运行时的条件决定加载哪个类。
  3. 被动加载(被动类加载):当一个类被引用,但并没有实际使用到该类的时候,类不会被加载。只有在真正使用到类时,才会加载该类。常见的被动加载场景包括通过子类引用父类的静态字段、通过数组定义引用类、常量在编译阶段会存入调用类的常量池等。

4、说说你对java中双亲委派机制的理解

双亲委派机制是Java类加载机制中的一种。它的核心思想是当类加载器需要加载一个类时,它会先委托给父类加载器去尝试加载,只有当父类加载器无法加载该类时,才会由当前类加载器去加载。这样可以保证类的唯一性和安全性,避免重复加载和恶意代码的加载。双亲委派机制在Java中起到了重要的作用,实现了类的共享和重用。

5、请你说说对gc的理解

GC(Garbage Collection)是一种自动内存管理机制,用于自动回收不再使用的对象所占用的内存空间。它通过标记、清除和压缩等步骤来完成垃圾回收的过程。程序员无需手动触发,由JVM的垃圾回收器自动执行。合理的编码和内存管理可以最大程度发挥GC的作用,提高程序性能和稳定性。

6、 hashmap容怎么实现扩容

HashMap在插入元素时,会根据负载因子(load factor)来判断是否需要进行扩容操作。负载因子是指哈希表中已存储元素个数与实际容量的比值。

当HashMap的负载因子超过设定的阈值时(默认为0.75),就会触发扩容操作。扩容会创建一个新的更大的哈希表,并将原有的元素重新分配到新的哈希表中,以减少哈希冲突,提高查询效率。

HashMap的扩容过程大致包括以下几个步骤:

  1. 创建新的哈希表,其容量是原哈希表的两倍。新哈希表的容量一般会选择最接近且大于原容量的2的幂次方。
  2. 遍历原哈希表中的每个桶(bucket),将桶中的元素重新计算哈希值并分配到新哈希表中的对应桶中。这一步骤会重新计算元素的哈希值和索引位置,确保元素在新哈希表中的位置发生变化。
  3. 将新哈希表设置为当前哈希表,原哈希表则成为垃圾对象等待垃圾回收。

扩容操作可能会对性能产生一定的影响,因为需要重新计算哈希值和重新分配元素。为了减少扩容的频率,可以通过调整负载因子的大小来控制HashMap的容量和性能之间的平衡。较小的负载因子会使哈希表更快地扩容,但会占用更多的内存空间,较大的负载因子则会减少扩容的次数,但可能会导致哈希冲突增多。

7、谈谈你对hashtable和currenthashmap的理解

Hashtable和ConcurrentHashMap都是Java中的线程安全的哈希表实现,它们在功能和使用方式上有一些相似之处,但在内部实现和性能方面有一些区别。

  1. Hashtable: Hashtable是最早引入的哈希表实现,它是线程安全的,所有的操作都是同步的(通过synchronized关键字实现)。由于同步的原因,HashTable在多线程环境下的性能比较低,只能通过同一时刻只允许一个线程访问的方式来保证线程安全。
  2. ConcurrentHashMap: ConcurrentHashMap是Java 5中引入的高性能线程安全的哈希表实现。它通过使用分段锁(Segment)来实现并发访问的高效性。每个Segment相当于一个小的HashTable,可以独立地进行操作,不同的Segment之间可以并发地进行读写操作。这样,在大多数情况下,不同的线程可以同时操作不同的Segment,提高了并发访问的效率。ConcurrentHashMap在并发环境下具有较高的性能和扩展性。
  3. 区别:
  • 线程安全性:Hashtable是通过同步的方式实现线程安全,而ConcurrentHashMap是通过分段锁(Segment)实现高效的并发访问。
  • 性能:ConcurrentHashMap相对于Hashtable在多线程环境下具有更好的性能,可以支持更高的并发度。
  • 迭代器弱一致性:Hashtable的迭代器是强一致性的,即在迭代过程中不会发生修改。而ConcurrentHashMap的迭代器是弱一致性的,可以在迭代过程中发生修改,但不会抛出ConcurrentModificationException异常。

如果需要在多线程环境下使用哈希表且对性能要求较高,推荐使用ConcurrentHashMap。

如果在单线程环境下或性能要求不高的情况下,可以使用HashTable。

8、说一下tcp的三次握手和四次挥手

TCP(Transmission Control Protocol)是一种可靠的、面向连接的网络传输协议。在建立和关闭TCP连接时,需要进行三次握手和四次挥手的过程。

三次握手(Three-way Handshake)的过程如下:

  1. 第一次握手:客户端发送一个带有SYN(同步)标志的TCP报文段给服务器,请求建立连接。此时客户端进入SYN_SENT状态。
  2. 第二次握手:服务器接收到客户端的请求后,回复一个带有SYN和ACK(确认)标志的报文段给客户端。此时服务器进入SYN_RECEIVED状态。
  3. 第三次握手:客户端接收到服务器的回复后,再次发送一个带有ACK标志的报文段给服务器,表示连接已建立。此时连接建立完成,客户端和服务器都进入ESTABLISHED状态,可以开始进行数据传输。

四次挥手(Four-way Handshake)的过程如下:

  1. 第一次挥手:客户端发送一个带有FIN(结束)标志的报文段给服务器,表示客户端不再发送数据。客户端进入FIN_WAIT_1状态。
  2. 第二次挥手:服务器接收到客户端的结束请求后,发送一个带有ACK标志的报文段给客户端,表示服务器收到了结束请求。此时服务器进入CLOSE_WAIT状态。
  3. 第三次挥手:服务器发送一个带有FIN标志的报文段给客户端,表示服务器不再发送数据。服务器进入LAST_ACK状态。
  4. 第四次挥手:客户端接收到服务器的结束请求后,发送一个带有ACK标志的报文段给服务器,表示客户端收到了结束请求。客户端进入TIME_WAIT状态,等待一段时间后关闭连接。服务器接收到ACK后,关闭连接,进入CLOSED状态。

通过三次握手,客户端和服务器建立起可靠的连接;通过四次挥手,双方完成数据传输并安全地关闭连接。这样可以确保数据的可靠传输和连接的正常释放。

9、说一下tcp和udp的区别

TCP(Transmission Control Protocol)和UDP(User Datagram Protocol)是两种常用的传输层协议,它们在特点和适用场景上有以下几点区别。

  1. 连接性:
  • TCP是面向连接的协议,通过三次握手建立连接,提供可靠的、有序的、面向字节流的数据传输。TCP确保数据的完整性和可靠性,适用于对数据准确性要求较高的场景。
  • UDP是无连接的协议,不需要建立连接,直接发送数据包。UDP提供了一种简单的、无拥塞控制的数据传输方式,适用于对实时性要求较高,但对可靠性要求相对较低的场景。
  1. 数据传输特性:
  • TCP提供可靠的数据传输,通过序列号和确认机制,保证数据的按序到达且不丢失。TCP还提供流量控制和拥塞控制机制,以避免网络拥塞和数据丢失。
  • UDP提供不可靠的数据传输,数据包可能会丢失、重复、乱序。UDP没有流量控制和拥塞控制机制,数据传输速度更快,但不保证数据的可靠性。
  1. 数据报大小:
  • TCP没有固定的最大数据报大小限制,可以传输大量的数据,适用于大文件传输。
  • UDP的数据报大小有限制(64KB),适用于传输较小的数据包。
  1. 效率:
  • TCP在保证可靠性的同时,会引入较大的延迟,数据传输速度相对较慢。
  • UDP则没有TCP的拥塞控制和重传机制,传输效率更高,但由于可靠性较低,不适用于对数据准确性要求较高的场景。

10、 arrayList和LinkedList的区别

ArrayList和LinkedList都是Java中常用的集合类,它们有以下几点区别:

  1. 内部实现结构:
  • ArrayList底层使用数组实现,通过索引可以快速地访问和修改元素。
  • LinkedList底层使用双向链表实现,每个节点包含了当前元素的值和指向前后节点的引用。
  1. 插入和删除操作:
  • ArrayList对于插入和删除操作效率较低,因为需要移动其他元素来填补被删除或插入的位置。
  • LinkedList对于插入和删除操作效率较高,只需要修改节点的指针即可。
  1. 随机访问:
  • ArrayList支持随机访问,即通过索引直接访问元素,时间复杂度为O(1)。
  • LinkedList不支持随机访问,需要从头节点开始遍历到目标位置,时间复杂度为O(n)。
  1. 内存占用:
  • ArrayList在内存中需要连续的存储空间,因此在插入和删除元素时可能需要进行数组的扩容和复制,占用的内存空间较大。
  • LinkedList在内存中使用链表结构,每个节点只需存储当前元素和前后节点的引用,占用的内存空间相对较小。

如果需要频繁进行随机访问,而对于插入和删除操作性能要求不高,可以选择ArrayList。如果需要频繁进行插入和删除操作,而对于随机访问的性能要求不高,可以选择LinkedList。在选择使用哪个集合类时,需要根据具体的应用场景和需求进行权衡

你可能感兴趣的:(计算机面试题,面试,java,职场和发展)