引言
在Java开发的面试中,基础知识的掌握程度往往是面试官考察的重点之一。本文整理了Java开发中常见的基础知识点和面试问题,包括基本数据类型、字符串、反射、注解、泛型、内部类以及I/O模型等。通过复习这些内容,可以帮助你更好地准备面试,同时巩固Java基础知识。
Java 基础知识
1. Java中的几种基本数据类型是什么?对应的包装类型是什么?各自占用多少字节呢?
基本数据类型 | 包装类型 | 占用字节数 |
---|---|---|
byte | Byte | 1字节 |
short | Short | 2字节 |
int | Integer | 4字节 |
long | Long | 8字节 |
float | Float | 4字节 |
double | Double | 8字节 |
char | Character | 2字节 |
boolean | Boolean | 无固定大小 |
说明:
-
基本数据类型直接存储在栈内存中,性能高,但不能作为对象使用。
-
包装类型是Java为基本数据类型提供的类,位于
java.lang
包中,支持方法调用和空值(null
)。 -
boolean
类型在JVM中通常占用1字节,但具体取决于JVM实现。
2. String、StringBuffer和StringBuilder的区别是什么?String为什么是不可变的?
String:
-
不可变性:String对象一旦创建,其内容不可修改。每次对String的操作都会生成一个新的String对象。
-
线程安全:由于不可变性,String是线程安全的。
-
性能问题:频繁修改字符串会导致大量临时对象的创建,性能较差。
StringBuffer:
-
可变性:StringBuffer对象的内容可以修改。
-
线程安全:StringBuffer的方法是同步的,线程安全。
-
性能问题:由于同步机制,性能较差。
StringBuilder:
-
可变性:StringBuilder对象的内容可以修改。
-
线程不安全:StringBuilder的方法是非同步的,线程不安全。
-
性能优势:在单线程环境下,性能优于StringBuffer。
总结:
-
如果需要频繁修改字符串且线程安全,使用
StringBuffer
。 -
如果需要频繁修改字符串且线程不安全,使用
StringBuilder
。 -
如果字符串内容不需要修改,使用
String
。
String为什么是不可变的?
-
线程安全:不可变性使得String对象在多线程环境下不会被修改,天然线程安全。
-
缓存优化:JVM可以缓存常用的String对象,提高性能。
-
安全性:不可变性使得String对象可以用作类加载器、数据库用户名/密码等敏感信息的存储。
3. ==
与equals
?hashCode
与equals
?
==
与equals
:
-
==
:-
对于基本数据类型,比较的是值。
-
对于引用数据类型,比较的是内存地址。
-
-
equals
:-
是
Object
类的方法,用于比较两个对象的内容是否相等。 -
默认情况下比较内存地址,但可以被重写。例如,
String
类重写了equals
方法,用于比较字符串的内容。
-
hashCode
与equals
:
-
hashCode
:-
是
Object
类的方法,返回对象的哈希码值。 -
用于哈希表等数据结构中快速定位对象。
-
-
equals
与hashCode
的关系:-
如果两个对象通过
equals
方法相等,那么它们的hashCode
值必须相同。 -
如果两个对象的
hashCode
值相同,它们不一定通过equals
方法相等。 -
重写
equals
方法时,通常需要重写hashCode
方法,以保证哈希表等数据结构的正确性。
-
4. Java反射?反射有什么缺点?你是怎么理解反射的(为什么框架需要反射)?
Java反射:
-
定义:Java反射机制允许程序在运行时查询类、接口、字段和方法的信息,并且可以调用对象的方法和修改对象的属性。
-
用途:
-
动态加载类、创建对象、调用方法。
-
实现框架和库中的通用功能,如Spring、Hibernate等。
-
实现注解处理器。
-
反射的缺点:
-
性能问题:反射操作比直接调用慢,因为需要在运行时动态解析。
-
破坏封装性:反射可以访问私有成员,破坏了类的封装性。
-
安全性问题:反射可以绕过访问控制,可能导致安全问题。
为什么框架需要反射:
-
灵活性:框架可以动态加载和管理类,无需在编译时确定类的具体实现。
-
通用性:框架可以通过反射操作任意对象,实现通用的功能,如依赖注入、事务管理等。
5. 谈谈对Java注解的理解,解决了什么问题?
Java注解:
-
定义:注解是Java 5引入的一种元数据形式,用于为代码提供额外的信息。
-
用途:
-
编译时检查:如
@Override
、@Deprecated
等。 -
运行时处理:如Spring的
@Component
、@Service
等注解,用于标记类的用途。 -
代码生成:如Lombok的
@Getter
、@Setter
等注解,用于生成getter和setter方法。
-
解决的问题:
-
减少样板代码:通过注解减少重复代码,提高开发效率。
-
增强代码可读性:注解可以清晰地表达代码的意图。
-
动态处理:框架可以通过注解动态处理代码,实现依赖注入、事务管理等功能。
6. Java泛型了解么?什么是类型擦除?介绍一下常用的通配符?
Java泛型:
-
定义:泛型允许在定义类、接口和方法时使用类型参数,使得代码更加通用和类型安全。
-
类型擦除:
-
定义:Java泛型在运行时会被擦除,即运行时无法获取泛型的具体类型信息。
-
原因:为了兼容Java 5之前的代码。
-
影响:运行时无法直接获取泛型类型,需要通过其他方式(如反射)获取。
-
常用的通配符:
-
?
:表示任意类型。-
List<?>
:表示一个未知类型的List
。
-
-
? extends T
:表示类型为T
或T
的子类。-
List<? extends Number>
:表示一个List
,其元素类型为Number
或Number
的子类。
-
-
? super T
:表示类型为T
或T
的父类。-
List<? super Integer>
:表示一个List
,其元素类型为Integer
或Integer
的父类。
-
7. 内部类了解吗?匿名内部类了解吗?
内部类:
-
定义:定义在另一个类内部的类。
-
种类:
-
成员内部类:定义在类的成员位置。
-
静态内部类:定义在类的静态成员位置。
-
局部内部类:定义在方法内部。
-
匿名内部类:没有类名的内部类,通常用于实现接口或继承类。
-
匿名内部类:
-
定义:没有类名的内部类,通常用于实现接口或继承类。
-
用途:
-
简化代码:用于实现单个接口或继承单个类时,可以减少代码量。
-
临时对象:用于创建临时对象,不需要单独定义类。
-
示例:
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("Hello from anonymous inner class!");
}
};
8. BIO、NIO、AIO有什么区别?
BIO(Blocking I/O):
-
定义:阻塞式I/O,基于流的I/O操作。
-
特点:
-
每个连接都需要一个线程来处理。
-
线程资源有限,不适合高并发场景。
-
NIO(Non-blocking I/O):
-
定义:非阻塞式I/O,基于通道(Channel)和缓冲区(Buffer)的I/O操作。
-
特点:
-
支持非阻塞模式,一个线程可以管理多个连接。
-
使用选择器(Selector)来管理多个通道,适合高并发场景。
-
AIO(Asynchronous I/O):
- 定义:异步I/O,基于事件和回调的I/O操作。
-
特点:
-
真正的异步操作,I/O操作完全由操作系统完成,无需线程阻塞等待。
-
适合高并发场景,但Java的AIO支持(
java.nio.channels.AsynchronousChannel
)相对较晚且使用复杂。 -
在Java中,AIO的实现依赖于操作系统底层的异步I/O支持(如Linux的
epoll
、Windows的IOCP
)。
-
-
BIO:
-
优点:简单易用,适合低并发场景。
-
缺点:每个连接都需要一个线程,线程资源有限,不适合高并发场景。
-
-
NIO:
-
优点:支持非阻塞模式,一个线程可以管理多个连接,适合高并发场景。
-
缺点:使用复杂,需要管理通道和缓冲区,代码复杂度较高。
-
-
AIO:
-
优点:真正的异步操作,性能最高,适合高并发场景。
-
缺点:实现复杂,依赖操作系统底层支持,Java的AIO支持相对较晚且不够完善。
-