Java 基础常见面试题整理

目录

    • 1、java的基本数据类型有哪些?
    • 2、java为什么要有包装类型?
    • 3、String a = "123" 和 String a = new String("123") 区别?
    • 4、String、StringBuilder和StringBuffer的区别?
    • 5、如何理解面向对象和面向过程?
    • 6、面向对象的三大基本特征是什么?如何理解?
    • 7、java是面向对象还是面向过程?
    • 8、什么是反射?为什么需要反射?
    • 9、为什么不能用浮点数表示金额? 有什么方法解决?
    • 10、为什么不能用字符串来存储金额?
    • 11、为什么需要克隆?如何实现对象的克隆?深拷贝和浅拷贝的区别?
    • 12、try-catch-finally中,如果catch中return了,finally还会执行吗?
    • 13、String为什么设计成不可变的?
    • 14、Error和Exception的区别和联系?以及常见的RuntimeException?
    • 15、抽象类和接口的区别是什么?
    • 16、==和equals的区别
    • 17、super和this的区别是什么?
    • 18、Java中的集合类有哪些?说说他们的特点?
    • 19、集合的排序方式的实现方案?
    • 20、ArrayList、LinkedList与Vector的区别?
    • 21、HashMap、Hashtable和ConcurrentHashMap的区别?
    • 22、HashMap的初始化,put流程,get流程,扩容流程说明
    • 23、HashMap、HashSet、ArrayList是线程安全的吗?
    • 24、创建线程的几种方式?
    • 25、线程同步的方式有哪些方式?
    • 26、synchronized如何使用?加在普通方法上和加在静态方法的区别?
    • 27、java级别的锁都有哪些?你怎么分类?
    • 28、什么情况下需要对对象进行序列化?
    • 29、什么是AIO、BIO和NIO?
    • 30、synchronized的锁升级过程是怎样的?
    • 31、synchronized的锁优化是怎样的?
    • 32、synchronized和reentrantLock区别?
    • 33、volatile能保证原子性吗?为什么?
    • 34、JUC并发包常用的工具类,分别有什么特性,适用场景?
    • 35、ConcurrentHashMap在哪些地方做了并发控制,保证线程安全的?
    • 36、SimpleDateFormat线程安全性?
    • 37、AQS和CAS分别是什么,如何理解?
    • 38、 ConcurrentHashMap为什么1.8取消了使用ReentrantLock锁?
    • 39、HashMap是如何解决Hash冲突的?解决hash冲突都有哪些方案?
    • 40、HashMap和ArrayLsit区别,以及他们的key是否可重复/为空?
    • 41、平常用过线程池吗?你该注意哪些问题?
    • 42、线程池的等待队列有哪几种实现方式?
    • 43、java的四种引用,强弱软虚的区别?
    • 44、为什么说CAS是乐观锁?底层对应那个类?
    • 45、ThreadLocal有使用过吗?底层原理是什么?使用它该注意什么?有什么问题?
    • 46、JMM内存模型是什么?为什么需要JMM内存模型?
    • 47、如何设计一个高并发系统?
    • 48、aio 和 nio 它是跟什么相关的?是 Java 做的还是系统底层做的
    • 49、io流有哪些
    • 50、为什么我们常用的是NIO而不是AIO?
    • 51、java里面线程分为哪两类
    • 52、java内存模型 三大特性是什么
    • 53、java内存模型和jvm区别是什么
    • 54、线程状态有哪些,如何让线程中断
    • 55、线程安全问题的本质是什么?JMM解决了什么问题
    • 56、怎么知道synchronized锁在哪一个状态
    • 57、synchronized 方法块、普通方法、静态方法 分别锁的对象是什么
    • 58、核心线人数和那个队列长度一般怎么样去配置?
    • 59、多线程如何实现任务编排?
    • 60、reentrantlock 如何实现公平和非公平锁的
    • 61、链路追踪中的父子线程传递怎么实现(threadlocal)
    • 62、线程死锁的条件是什么,如何解决
    • 63、为什么从永久代变成元空间
    • 64、sleep()和wait()的区别

1、java的基本数据类型有哪些?

Java的基本数据类型包括以下八种:

  • boolean: 用于存储逻辑值,只能存储true和false。
  • byte: 用于存储字节数据,占用8位(一个字节)内存空间。
  • short: 用于存储较短整数,占用16位(两个字节)内存空间。
  • int: 用于存储整数,占用32位(四个字节)内存空间。
  • long: 用于存储长整数,占用64位(八个字节)内存空间。
  • float: 用于存储单精度浮点数,占用32位(四个字节)内存空间。
  • double: 用于存储双精度浮点数,占用64位(八个字节)内存空间。
  • char: 用于存储单个字符,占用16位(两个字节)内存空间,采用Unicode编码。

2、java为什么要有包装类型?

主要原因包括以下几点:

  • 处理基本数据类型的 null 值:基本数据类型(如 int,double 等)不能直接赋值为 null,而包装类型(如 Integer、Double)可以表示 null 值,这对于某些业务逻辑和数据处理来说非常有用。
  • 提供额外功能:包装类型提供了一些额外的方法和功能,这些方法可以用于对基本数据类型进行操作和处理。例如,Integer 类提供了
    parselnt()方法用于将字符串转换为整数。
  • 泛型支持:泛型只能接受对象类型,无法直接使用基本数据类型。因此,使用包装类型可以很方使地在泛型中使用基本数据类型。
  • 自动装箱与拆箱: Java 提供了自动装箱(autoboxing)和自动拆箱(unboxing)的功能,使得基本类型与其对应的包装类型之间的转换更加便捷。
  • 与对象集合的兼容性: Java的集合类(如ArrayList, HashMap等)只能存储对象,不能直接存储基本数据类型。使用包装类型可以方便地将基本数据类型转换为对象类型,从而与集合类兼容。

3、String a = “123” 和 String a = new String(“123”) 区别?

String a = "123"; // a 是一个变量,对字符串常量池中“123”这个对象的引用
// "123" 是一个字符串字面量(字符串对象),它会被存储在字符串常量池中(String Constant Pool)。
// 当字符串常量池不存在“123”对象会创建一个对象,存在则不会创建  
  
String b = new String("123"); // b 是一个变量,但它引用堆的 String 对象  
// 这里,首先 JVM 会检查字符串常量池中是否已经有 "123" 这个字符串。  
// 如果有,它会用常量池中的字符串对象作为参数去调用 String 类的构造函数来创建一个新的 String 对象。  
// 这个新的 String 对象会被分配在堆内存中,并且它的内容(字符数组)会是常量池中字符串对象的一个拷贝。  
// 变量 b 持有对这个新创建的堆上 String 对象的引用,而不是直接引用常量池中的字符串。
// 最多创建2个对象,最少一个

4、String、StringBuilder和StringBuffer的区别?

String、StringBuilder和StringBuffer是Java中用来处理字符串的类,它们之间的区别主要在于性能和线程安全性。

  • String: 一旦创建就不能被修改,任何对String的操作都会产生一个新的String对象。
    不可变性使得String在并发环境下是线程安全的,但是频繁的字符串操作会产生大量临时对象,影响性能。

  • StringBuilder: StringBuilder是可变的,可以进行插入、追加、删除等操作而不会产生新的对象。
    由于StringBuilder是非线程安全的,所以在单线程环境下比StringBuffer具有更好的性能。

  • StringBuffer: 与StringBuilder类似,也是可变的,但是它是线程安全的,所有的方法都是同步的。
    在多线程环境下,为了确保线程安全性,可以使用StringBuffer,但性能相对较差,因为是通过在方法上面加synchronized 关键字来保证同步的。

因此,如果在单线程环境下需要频繁修改字符串,建议使用StringBuilder;如果在多线程环境下需要频繁修改字符串,则应该使用StringBuffer;如果字符串不需要被修改,那么使用String即可。

5、如何理解面向对象和面向过程?

面向过程是一种以过程为中心的编程方法,它将问题分解为一系列步骤函数。每个函数负责完成一个特定的任务,通过依次调用这些函数来解决问题。

其优点包括:

  • 流程清晰:按照步骤进行编程,易于理解和调试。

然而,它也存在一些局限性:

  • 代码复用性差:功能封装在函数中,但难以复用和扩展。
  • 维护困难:代码结构较为松散,修改可能影响多个部分。

面向对象则是一种以对象为中心的编程方法。它将问题抽象为对象,每个对象具有属性和行为。
其优点包括:

  • 代码复用性高:通过继承和多态实现代码的重用和扩展。
  • 维护方便:修改一个对象的行为不会影响其他部分。
  • 模拟现实世界:更符合人类的思维方式。

面向对象编程的关键概念包括:

  • 对象:表示现实世界中的实体。
  • 类:对象的抽象描述。
  • 封装:隐藏对象的内部实现,只暴露必要的接口。
  • 继承:实现代码的重用和扩展。
  • 多态:不同对象对同一消息的不同响应。

总之,面向对象编程更加注重代码的封装、复用和可扩展性,使得代码更易于维护和扩展。而面向过程编程则更适合一些简单、流程性强的问题。在实际编程中,可以根据具体情况选择合适的编程方法。

6、面向对象的三大基本特征是什么?如何理解?

面向对象的三大基本特征是封装、继承和多态

封装是将对象的属性和行为封装在一起,对外只提供必要的接口。它的意义在于:

  • 信息隐藏:隐藏内部实现细节,提高安全性和可靠性。
  • 模块独立性:减少模块之间的耦合,便于模块的独立开发和测试。

继承允许子类继承父类的属性和方法,从而实现代码的重用和扩展。理解继承可以从以下几个方面考虑:

  • 代码复用:避免重复编写相同的代码。
  • 扩展功能:子类可以在父类的基础上添加新的功能。

多态是指同一个方法在不同的对象上有不同的实现。它的好处包括:

  • 灵活性:根据具体对象的类型执行相应的操作。
  • 可扩展性:方便添加新的子类并实现不同的行为。

7、java是面向对象还是面向过程?

Java 是一种面向对象的编程语言。

8、什么是反射?为什么需要反射?

反射是指在运行时动态地访问和操作类的信息。

需要反射的原因包括:

  • 灵活性:可以在运行时获取类的信息、创建对象、调用方法等,提供了更大的灵活性。
  • 动态加载:支持在运行时动态加载和使用类,无需在编译时确定。
  • 框架和工具开发:便于开发通用的框架和工具,可适应不同的业务需求。
  • 插件机制:支持插件式的扩展,方便添加新的功能。
  • XML 配置:与配置文件结合,实现基于配置的动态功能。
  • 类操作:对类进行各种操作,如修改属性、方法等。
  • 跨模块交互:方便不同模块之间的交互和集成。

通过反射,程序可以在运行时动态地了解和操作类的结构和行为,从而实现更灵活和可扩展的系统设计。

9、为什么不能用浮点数表示金额? 有什么方法解决?

浮点数在计算机内部的表示方法采用的是 IEEE 标准,它兼顾了数据的精度和大小。32 位的浮点数由 1 比特的符号位、8 比特的阶码和 23 比特的尾数组成。浮点数能表示的数据大小范围由阶码决定,而能够表示的精度完全取决于尾数的长度。对于金额,舍去不能表示的部分,就会产生精度丢失
十进制的 0.1 在二进制下将是一个无线循环小数,同样,在进行加法、减法、乘法和除法等运算时,这种精度损失可能会累积,导致结果不正确。为了避免这种情况,建议使用 java.math.BigDecimal 类来表示和计算金额。BigDecimal 提供了用于高精度算术运算的方法,能够精确地表示十进制小数,避免浮点数表示和计算中的精度损失问题。

10、为什么不能用字符串来存储金额?

使用字符串来存储金额有一些局限性:

  • 计算困难:进行数值计算时,需要额外的处理来转换和解析字符串。
  • 效率较低:在涉及大量金额操作时,性能可能受到影响。
  • 不支持数学运算:无法直接进行加减乘除等常见的数学运算。
  • 易出错:处理字符串转换可能引入错误。
  • 数据类型不一致:与其他数值类型的交互可能需要额外的转换。

然而,在某些情况下,可能会选择使用字符串来存储金额:

  • 格式灵活:可以方便地表示特定的金额格式。
  • 非数值场景:如仅用于显示或存储。

为了更准确和高效地处理金额,通常使用专门的数值类型或类,例如 Java 中的BigDecimal,它提供了高精度的数值计算功能,能够避免常见的数值计算问题。

11、为什么需要克隆?如何实现对象的克隆?深拷贝和浅拷贝的区别?

需要克隆的原因有以下几点:

  • 独立性:创建独立的副本,避免对原始对象的修改影响到其他使用该对象的部分。
  • 隔离性:在不同的上下文或操作中使用相同对象的副本,以防止冲突。
  • 安全性:确保原始对象的完整性和稳定性。

实现对象的克隆有多种方式,以下是一种常见的方法:

  • 实现 Cloneable 接口:确保类实现了 Cloneable 接口。
  • 重写 clone 方法:在类中重写 clone 方法。

深拷贝和浅拷贝的区别在于:

深拷贝:复制对象及其引用的所有嵌套对象。

  • 副本与原始对象完全独立。
  • 修改副本不会影响原始对象。

浅拷贝:只复制对象本身,不复制引用的嵌套对象。

  • 嵌套对象仍与原始对象共享。
  • 修改副本的嵌套对象会影响原始对象。

深拷贝确保了副本的完全独立性,而浅拷贝在处理嵌套对象时可能会出现问题。在需要完全独立的副本时,应使用深拷贝。

12、try-catch-finally中,如果catch中return了,finally还会执行吗?

在 try-catch-finally 语句中,即使在 catch 块中执行了 return 语句,finally 块仍然会执行。

finally 块的作用是确保无论在 try 块中是否发生异常,某些特定的操作都会被执行,例如资源的释放、清理等。

当执行到 catch 块中的 return 语句时,会先将返回值保存起来,然后执行 finally 块中的代码。最后,再返回保存的返回值。

finally 块的执行具有以下特点:

  • 无论是否发生异常,finally 块都会执行。
  • 即使在 finally 块中抛出异常,也会继续执行。
  • finally 块中的返回值不会影响 try 或 catch 块中的返回值。

使用 finally 块可以确保资源的正确释放,以避免资源泄漏等问题。

13、String为什么设计成不可变的?

String 被设计成不可变有以下几个原因:

  1. 安全性和稳定性:避免在多个线程中同时修改字符串时可能出现的竞态条件。
  2. 效率:许多操作可以直接利用字符串的不可变性进行优化。
  3. 缓存友好:相同字符串可以共享,减少内存占用。
  4. 线程安全:无需进行额外的同步操作。
  5. 代码简洁:无需处理可变字符串带来的复杂情况。
  6. 避免误操作:防止意外修改字符串。
  7. 易于实现和理解:简化字符串的内部实现。

不可变的特性使得 String 在多线程环境中更加可靠,并且提高了性能和内存效率。

14、Error和Exception的区别和联系?以及常见的RuntimeException?

Java 基础常见面试题整理_第1张图片

区别:

  • Error: Error是指Java虚拟机无法解决的严重问题,通常由系统内部错误引起,程序无法通过捕获错误来恢复。一般来说,程序不应该捕获Error类型的异常,而应该在发生Error时让程序终止。常见的Error包括OutOfMemoryError(内存耗尽)和StackOverflowError(栈溢出)等。
  • Exception: Exception是指程序运行时可能发生的问题,它可以通过捕获和处理来使程序继续执行。Exception又分为受检异常(Checked Exception)和非受检异常(Unchecked Exception)。受检异常是指在程序编译时需要处理的异常,而非受检异常是指在编译时不需要处理的异常。常见的Exception包括IOException(输入输出异常)和SQLException(数据库访问异常)等。

联系:

Error和Exception都继承自Throwable类,因此它们具有一些共同的特性,如堆栈跟踪和异常信息等。

常见的RuntimeException:

  • NullPointerException(空指针异常): 当试图在一个空对象上调用方法或访问属性时抛出。
  • ArrayIndexOutOfBoundsException(数组下标越界异常): 当试图访问数组中不存在的索引时抛出。
  • IllegalArgumentException(非法参数异常): 当传递给方法的参数不符合方法的要求时抛出。
  • ArithmeticException(算术异常): 当出现除以零的运算时抛出。
  • ClassCastException(类转换异常): 当试图将一个对象转换为它不是的类型时抛出。

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

抽象类和接口是面向对象编程中两种不同的概念,它们在Java中有着明显的区别。

抽象类(Abstract Class):

  • 抽象类可以包含抽象方法和非抽象方法。抽象方法是没有实际实现的方法,需要子类去实现。非抽象方法有默认实现,子类可以选择性地重写这些方法。
  • 一个类只能继承一个抽象类(单继承性)。
  • 抽象类可以包含成员变量,可以有构造函数,可以拥有普通方法。
  • 抽象类可以有访问控制修饰符,可以定义成员变量,可以包含构造方法。

接口(Interface):

  • 接口中所有的方法都是抽象方法,没有方法体。
  • 一个类可以实现多个接口(多继承性)。
  • 接口中的成员变量隐式地是static和final的。
  • 接口不能包含构造函数。
  • 接口中的方法默认是public的,可以省略访问控制符,不能使用其他访问修饰符。

16、==和equals的区别

在 Java 中,== 和 equals() 方法的区别主要包括以下几点:

  • ==:它是一种基本的数据比较操作符,用于比较两个对象的引用(内存地址)是否相等。
  • equals():它是 Object 类中的方法,通常用于定义对象之间的逻辑相等性。

Object默认是判断地址是否相等, Long、Integer、Date、String等都重写了equals方法。

Object类

public boolean equals(Object obj) {
   
        return (this == obj);
    }

Long类

public boolean equals(Object obj) {
   
        if (obj instanceof Long) {
   
            return value == ((Long)obj).longValue();
        }
        return false

你可能感兴趣的:(Java,java,开发语言,面试)