目录
-
- 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 被设计成不可变有以下几个原因:
- 安全性和稳定性:避免在多个线程中同时修改字符串时可能出现的竞态条件。
- 效率:许多操作可以直接利用字符串的不可变性进行优化。
- 缓存友好:相同字符串可以共享,减少内存占用。
- 线程安全:无需进行额外的同步操作。
- 代码简洁:无需处理可变字符串带来的复杂情况。
- 避免误操作:防止意外修改字符串。
- 易于实现和理解:简化字符串的内部实现。
不可变的特性使得 String 在多线程环境中更加可靠,并且提高了性能和内存效率。
14、Error和Exception的区别和联系?以及常见的RuntimeException?
区别:
- 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