5道大厂的Java基础面试题
2024-10-20 本文已影响0人
李老头探索
前言
各种框架眼花缭乱,各种逻辑需求,CRUD。久而久之,写的1000行代码中都是if else,@autowired等等,等出去面试的时候,基础题不断,而且还是不常用,或者说不在意的,往往这些就容易把人问懵。今日加深下基础映像吧
闲着无事,看看理解, 加深映像!!!
JAVA 中的几种基本数据类型是什么,各自占用多少字节。
- 四种整型,二种浮点型,一种字符型,一种布尔型
- byte:占用 1 字节(8 位),范围是 -128 到 127。
- short:占用 2 字节(16 位),范围是 -32,768 到 32,767。
- int:占用 4 字节(32 位),范围是 -2,147,483,648 到 2,147,483,647。
- long:占用 8 字节(64 位),范围是 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。
- float:占用 4 字节(32 位),用于表示单精度浮点数。
- double:占用 8 字节(64 位),用于表示双精度浮点数。
- char:占用 2 字节(16 位),用于表示单个字符(Unicode)。
boolean:占用 1 字节(8 位),表示 true 或 false(实际占用可能因 JVM 实现而异)。
String 类能被继承吗,为什么
先看源码
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
}
- final 关键字:String 类被声明为 final,这意味着不能创建 String 类的子类。使用 final 关键字可以防止类被继承,从而确保其行为不被改变。
- 扩展下,如此设定了final关键字,代表着String不希望被动手脚,这种设计提高了安全性和性能,特别是在多线程环境中。禁止继承有助于保持这种不可变性。
- 性能优化,怎么优化呢,既然字符串是不能变了,使用字符串池(String Pool)来存储常量字符串,当你创建一个字符串字面量时,Java 会首先检查字符串池中是否已经存在相同的字符串。如果存在,Java 会重用这个字符串的引用,而不是创建一个新的对象。这种机制可以节省内存并提高性能,特别是在处理大量相同字符串时
String,Stringbuffer,StringBuilder 的区别。
老生常谈了,分为几类
- 不可变性 vs 可变性:上面说了,String是final,不可变,StringBuffer和StringBuilder是可变的
- 线程安全:String和StringBuffer是线程安全的,StringBuilder是非线程安全。String不可变,自然是安全。StringBuffer方法是同步的,因此线程安全。StringBuilder方法非同步,所以单线程环境下性能更好。
-
性能:String: 由于不可变性,频繁修改会导致性能下降,因为每次修改都会创建新的对象。StringBuffer: 由于线程安全,性能相对较低,适合需要安全的多线程操作。StringBuilder: 性能最佳,适合需要频繁修改字符串的单线程操作。
以上总结使用场景:
String: 用于常量字符串或不需要修改的字符串。
StringBuffer: 用于需要线程安全的字符串操作。
StringBuilder: 用于性能要求高且在单线程环境中操作的字符串。
ArrayList 和 LinkedList 有什么区别
有时间的话,可以看看源码,对理解更有帮助,对数据结构也有很大的认知。这边简要说明下(熟悉下源码,更好的理解下面所说)
-
结构不同:
- ArrayList: 基于动态数组实现。内部使用数组来存储元素,支持随机访问。
- LinkedList: 基于双向链表实现。每个元素(节点)包含对前一个和后一个节点的引用。
-
存储方式
- ArrayList: 元素在内存中是连续存储的,适合快速随机访问。
- LinkedList: 元素在内存中不连续存储,适合频繁插入和删除操作。
-
访问速度
- ArrayList: 随机访问速度快(O(1)),因为可以通过索引直接访问。
- LinkedList: 随机访问速度慢(O(n)),需要从头遍历到指定位置。
-
插入和删除速度
- ArrayList: 在中间插入或删除元素时,可能需要移动大量元素,时间复杂度为 O(n)。
- LinkedList: 在任意位置插入或删除元素时,只需调整节点的引用,时间复杂度为 O(1)(已知位置)。
-
内存使用
- ArrayList: 由于使用数组,可能会有额外的内存开销(如数组扩容时,需要重新开辟新的空间去存储)。
- LinkedList: 每个节点需要额外存储前后节点的引用,内存开销较大。
-
适用场景
- ArrayList: 适合频繁读取和随机访问的场景。
- LinkedList: 适合频繁插入和删除的场景,尤其是在列表的两端。
类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当new的时候,他们的执行顺序。
-
首先,Java 加载父类的静态数据(静态变量和静态块)。
-
父类静态初始化: 执行父类的静态初始化块(如果有)。
-
子类静态数据: 然后,加载子类的静态数据(静态变量和静态块)。
-
子类静态初始化: 执行子类的静态初始化块(如果有)。
-
创建对象: 当调用 new 创建对象时,执行以下步骤:
- 父类构造函数: 首先调用父类的构造函数。
- 父类字段初始化: 在父类构造函数中,父类的实例字段会被初始化。
- 子类构造函数: 然后调用子类的构造函数。
- 子类字段初始化: 在子类构造函数中,子类的实例字段会被初始化。
class Parent {
static {
System.out.println("Parent static block");
}
Parent() {
System.out.println("Parent 构造方法");
}
}
class Child extends Parent {
static {
System.out.println("Child static block");
}
Child() {
System.out.println("Child 构造方法");
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
}
}
Parent static block
Child static block
Parent 构造方法
Child 构造方法
最后
在这些简要的答案中,还能更深层次的去挖,面试多加加分,例如String以及Stringbuffer,StringBuilder 源码的分析,从而知道其中的区别,实例化方面也可以深入到JVM,整个过程中JVM的变化。