基础题
题目 1:Java中的==
和equals()
方法有什么区别?
问题描述:
解释 ==
和 equals()
方法在比较两个对象时的区别,并举例说明。
答案:
public class EqualsVsEqualsOperator {
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("hello");
// 使用 ==
System.out.println(s1 == s2); // 输出: false (比较的是引用地址)
// 使用 equals()
System.out.println(s1.equals(s2)); // 输出: true (比较的是内容)
}
}
解析:
==
比较的是两个对象的引用地址是否相同。equals()
方法默认比较的是引用地址(除非被重写),但在String
等类中被重写为比较内容是否相等。
题目 2:Java中的final
关键字有哪些用法?
问题描述:
请解释 final
关键字在Java中的三种主要用法,并提供代码示例。
答案:
// 1. final变量:一旦赋值后不可更改
final int x = 10;
// x = 20; // 编译错误
// 2. final方法:不能被子类重写
class Parent {
final void display() {
System.out.println("This is a final method.");
}
}
class Child extends Parent {
// @Override
// void display() { } // 编译错误:无法重写final方法
}
// 3. final类:不能被继承
final class FinalClass {
void show() {
System.out.println("This is a final class.");
}
}
// class SubClass extends FinalClass { } // 编译错误:无法继承final类
解析:
final
变量:表示常量,赋值后不可修改。final
方法:防止子类重写该方法。final
类:防止其他类继承该类。
题目 3:Java中的异常处理机制是什么样的?
问题描述:
请解释 try-catch-finally
的作用,并编写一个示例程序来展示如何处理异常。
答案:
public class ExceptionHandling {
public static void main(String[] args) {
try {
int result = 10 / 0; // 抛出 ArithmeticException
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
System.out.println("Caught an exception: " + e.getMessage());
} finally {
System.out.println("This will always execute.");
}
}
}
输出:
Caught an exception: / by zero
This will always execute.
解析:
try
块用于包裹可能抛出异常的代码。catch
块用于捕获并处理特定类型的异常。finally
块无论是否发生异常都会执行,通常用于释放资源。
题目 4:Java中的多线程是如何实现的?
问题描述:
请列举两种实现多线程的方式,并分别给出代码示例。
答案:
// 方式1:继承 Thread 类
class MyThread extends Thread {
public void run() {
System.out.println("Thread running via extending Thread class.");
}
}
// 方式2:实现 Runnable 接口
class MyRunnable implements Runnable {
public void run() {
System.out.println("Thread running via implementing Runnable interface.");
}
}
public class MultiThreadingExample {
public static void main(String[] args) {
// 使用继承 Thread 类的方式
MyThread thread1 = new MyThread();
thread1.start();
// 使用实现 Runnable 接口的方式
Thread thread2 = new Thread(new MyRunnable());
thread2.start();
}
}
解析:
- 继承
Thread
类:通过重写run()
方法实现线程逻辑。 - 实现
Runnable
接口:将Runnable
对象传递给Thread
构造器,推荐这种方式,因为 Java 不支持多重继承。
题目 5:什么是Java中的装箱和拆箱?
问题描述:
请解释装箱(Boxing)和拆箱(Unboxing)的概念,并提供代码示例。
答案:
public class BoxingUnboxingExample {
public static void main(String[] args) {
// 装箱:将基本数据类型转换为对应的包装类
Integer boxedInt = 10; // 自动装箱
System.out.println("Boxed Integer: " + boxedInt);
// 拆箱:将包装类转换为对应的基本数据类型
int unboxedInt = boxedInt; // 自动拆箱
System.out.println("Unboxed int: " + unboxedInt);
}
}
解析:
- 装箱:将基本数据类型(如
int
)转换为对应的包装类(如Integer
)。 - 拆箱:将包装类(如
Integer
)转换为对应的基本数据类型(如int
)。
Java 提供了自动装箱和拆箱的功能,简化了开发者的操作。
编程题
题目 1:反转一个单链表
问题描述:
给定一个单链表,编写一个方法来反转它。
示例:
输入: 1 -> 2 -> 3 -> 4 -> 5
输出: 5 -> 4 -> 3 -> 2 -> 1
答案:
class ListNode {
int val;
ListNode next;
ListNode(int val) { this.val = val; }
}
public class ReverseLinkedList {
public ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode nextTemp = curr.next; // 保存下一个节点
curr.next = prev; // 当前节点指向前一个节点
prev = curr; // 前一个节点移动到当前节点
curr = nextTemp; // 当前节点移动到下一个节点
}
return prev; // 返回新的头节点
}
}
解析:
通过迭代的方式,逐步将每个节点的 next
指针指向前一个节点,最终完成链表的反转。
题目 2:判断一个字符串是否是回文(使用栈)
问题描述:
给定一个字符串,判断它是否是回文(忽略大小写和非字母字符)。
示例:
输入: "A man, a plan, a canal: Panama"
输出: true
答案:
import java.util.Stack;
public class PalindromeChecker {
public boolean isPalindrome(String s) {
StringBuilder filtered = new StringBuilder();
for (char c : s.toCharArray()) {
if (Character.isLetterOrDigit(c)) {
filtered.append(Character.toLowerCase(c));
}
}
Stack<Character> stack = new Stack<>();
String filteredStr = filtered.toString();
// 将字符串的前半部分压入栈中
for (int i = 0; i < filteredStr.length() / 2; i++) {
stack.push(filteredStr.charAt(i));
}
// 从中间开始,与栈中的字符进行比较
int startIdx = (filteredStr.length() + 1) / 2;
for (int i = startIdx; i < filteredStr.length(); i++) {
if (stack.pop() != filteredStr.charAt(i)) {
return false;
}
}
return true;
}
}
解析:
利用栈的特性(先进后出),先将字符串的前半部分压入栈中,然后逐一弹出并与后半部分进行比较,从而判断是否为回文。
题目 3:实现一个LRU缓存(最近最少使用)
问题描述:
设计并实现一个LRU缓存机制。它应该支持以下操作:获取数据 get(key)
和写入数据 put(key, value)
。
get(key)
:如果关键字存在,返回其对应的值;否则返回-1
。put(key, value)
:如果关键字已经存在,更新其值;如果关键字不存在且缓存已满,则移除最近最少使用的元素,并插入新元素。
示例:
LRUCache cache = new LRUCache(2); // 缓存容量为2
cache.put(1, 1);
cache.put(2, 2);
cache.get(1); // 返回 1
cache.put(3, 3); // 移除关键字 2
cache.get(2); // 返回 -1 (未找到)
cache.put(4, 4); // 移除关键字 1
cache.get(1); // 返回 -1 (未找到)
cache.get(3); // 返回 3
cache.get(4); // 返回 4
答案:
import java.util.HashMap;
import java.util.Map;
class LRUCache {
class Node {
int key, value;
Node prev, next;
Node(int key, int value) { this.key = key; this.value = value; }
}
private Map<Integer, Node> map = new HashMap<>();
private int capacity;
private Node head, tail;
public LRUCache(int capacity) {
this.capacity = capacity;
head = new Node(0, 0);
tail = new Node(0, 0);
head.next = tail;
tail.prev = head;
}
public int get(int key) {
if (map.containsKey(key)) {
Node node = map.get(key);
remove(node);
add(node);
return node.value;
}
return -1;
}
public void put(int key, int value) {
if (map.containsKey(key)) {
remove(map.get(key));
}
if (map.size() == capacity) {
remove(tail.prev);
}
add(new Node(key, value));
}
private void add(Node node) {
Node nextNode = head.next;
head.next = node;
node.prev = head;
node.next = nextNode;
nextNode.prev = node;
map.put(node.key, node);
}
private void remove(Node node) {
map.remove(node.key);
node.prev.next = node.next;
node.next.prev = node.prev;
}
}
解析:
使用双向链表维护访问顺序,head
表示最近使用的节点,tail
表示最久未使用的节点。结合哈希表快速定位节点位置,保证 get
和 put
操作的时间复杂度为 O(1)。
题目 4:合并两个有序数组
问题描述:
给定两个按升序排列的整数数组 nums1
和 nums2
,以及它们的长度 m
和 n
,请将 nums2
合并到 nums1
中,使 nums1
成为一个有序数组。
示例:
输入:
nums1 = [1, 2, 3, 0, 0, 0]
, m = 3
nums2 = [2, 5, 6]
, n = 3
输出: [1, 2, 2, 3, 5, 6]
答案:
public class MergeSortedArray {
public void merge(int[] nums1, int m, int[] nums2, int n) {
int i = m - 1, j = n - 1, k = m + n - 1;
while (i >= 0 && j >= 0) {
if (nums1[i] > nums2[j]) {
nums1[k--] = nums1[i--];
} else {
nums1[k--] = nums2[j--];
}
}
while (j >= 0) {
nums1[k--] = nums2[j--];
}
}
}
解析:
从两个数组的末尾开始比较,将较大的元素放到 nums1
的末尾,直到所有元素都合并完成。