引言
Java开发实习生面试通常考察候选人对Java核心概念的理解、编程能力以及解决实际问题的思路。本文将系统性地梳理Java开发实习生面试中常见的核心知识点,包括基础语法、面向对象、集合框架、多线程及数据库操作等,通过详细的解释和完整的代码示例帮助你深入理解这些概念,为面试做好充分准备。
一、Java基础语法
1.1 数据类型与变量
Java是强类型语言,主要分为基本数据类型和引用数据类型。
基本数据类型:
- 整型:byte(1字节)、short(2字节)、int(4字节)、long(8字节)
- 浮点型:float(4字节)、double(8字节)
- 字符型:char(2字节)
- 布尔型:boolean
引用数据类型:类、接口、数组等
示例代码:
public class DataTypeExample {
public static void main(String[] args) {
// 基本数据类型
byte b = 100;
short s = 1000;
int i = 10000;
long l = 100000L; // L后缀表示long类型
float f = 3.14f; // f后缀表示float类型
double d = 3.1415926;
char c = 'A';
boolean bool = true;
// 引用数据类型
String str = "Hello Java";
int[] arr = {1, 2, 3, 4, 5};
System.out.println("byte: " + b);
System.out.println("short: " + s);
System.out.println("int: " + i);
System.out.println("long: " + l);
System.out.println("float: " + f);
System.out.println("double: " + d);
System.out.println("char: " + c);
System.out.println("boolean: " + bool);
System.out.println("String: " + str);
System.out.println("Array: " + Arrays.toString(arr));
}
}
1.2 运算符
Java提供了丰富的运算符,包括算术运算符、关系运算符、逻辑运算符、位运算符等。
示例代码:
public class OperatorExample {
public static void main(String[] args) {
// 算术运算符
int a = 10, b = 3;
System.out.println("a + b = " + (a + b)); // 13
System.out.println("a - b = " + (a - b)); // 7
System.out.println("a * b = " + (a * b)); // 30
System.out.println("a / b = " + (a / b)); // 3 (整数除法)
System.out.println("a % b = " + (a % b)); // 1 (取余)
// 关系运算符
System.out.println("a > b: " + (a > b)); // true
System.out.println("a < b: " + (a < b)); // false
System.out.println("a == b: " + (a == b)); // false
System.out.println("a != b: " + (a != b)); // true
// 逻辑运算符
boolean x = true, y = false;
System.out.println("x && y: " + (x && y)); // false (短路与)
System.out.println("x || y: " + (x || y)); // true (短路或)
System.out.println("!x: " + (!x)); // false (非)
// 位运算符
int m = 5; // 二进制: 0101
int n = 3; // 二进制: 0011
System.out.println("m & n: " + (m & n)); // 1 (按位与)
System.out.println("m | n: " + (m | n)); // 7 (按位或)
System.out.println("m ^ n: " + (m ^ n)); // 6 (按位异或)
System.out.println("~m: " + (~m)); // -6 (按位取反)
System.out.println("m << 1: " + (m << 1)); // 10 (左移)
System.out.println("m >> 1: " + (m >> 1)); // 2 (右移)
// 三元运算符
int max = (a > b) ? a : b;
System.out.println("max: " + max); // 10
}
}
1.3 控制流语句
Java的控制流语句包括条件语句和循环语句。
示例代码:
public class ControlFlowExample {
public static void main(String[] args) {
// if-else语句
int score = 85;
if (score >= 90) {
System.out.println("优秀");
} else if (score >= 80) {
System.out.println("良好"); // 输出: 良好
} else if (score >= 60) {
System.out.println("及格");
} else {
System.out.println("不及格");
}
// switch语句
int day = 3;
switch (day) {
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
case 3:
System.out.println("星期三"); // 输出: 星期三
break;
default:
System.out.println("其他");
}
// for循环
System.out.println("\nfor循环:");
for (int i = 0; i < 5; i++) {
System.out.print(i + " "); // 输出: 0 1 2 3 4
}
// while循环
System.out.println("\nwhile循环:");
int j = 0;
while (j < 5) {
System.out.print(j + " "); // 输出: 0 1 2 3 4
j++;
}
// do-while循环
System.out.println("\ndo-while循环:");
int k = 0;
do {
System.out.print(k + " "); // 输出: 0 1 2 3 4
k++;
} while (k < 5);
// break和continue
System.out.println("\nbreak和continue:");
for (int i = 0; i < 10; i++) {
if (i == 3) {
continue; // 跳过本次循环
}
if (i == 7) {
break; // 退出循环
}
System.out.print(i + " "); // 输出: 0 1 2 4 5 6
}
}
}
1.4 数组
数组是相同类型元素的集合,在Java中是对象。
示例代码:
import java.util.Arrays;
public class ArrayExample {
public static void main(String[] args) {
// 一维数组
int[] arr1 = new int[5]; // 声明并分配内存
arr1[0] = 10;
arr1[1] = 20;
arr1[2] = 30;
arr1[3] = 40;
arr1[4] = 50;
// 直接初始化
int[] arr2 = {1, 2, 3, 4, 5};
// 遍历数组
System.out.println("arr1元素:");
for (int num : arr1) {
System.out.print(num + " "); // 输出: 10 20 30 40 50
}
// 二维数组
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println("\n二维数组:");
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix[i].length; j++) {
System.out.print(matrix[i][j] + " ");
}
System.out.println();
}
// 数组工具类
System.out.println("\n数组工具类:");
int[] sorted = {5, 2, 8, 1, 9};
Arrays.sort(sorted);
System.out.println("排序后: " + Arrays.toString(sorted)); // [1, 2, 5, 8, 9]
int index = Arrays.binarySearch(sorted, 5);
System.out.println("5的索引: " + index); // 2
// 数组复制
int[] copy = Arrays.copyOf(sorted, 3);
System.out.println("复制的数组: " + Arrays.toString(copy)); // [1, 2, 5]
}
}
二、面向对象编程
2.1 类与对象
类是对象的蓝图,对象是类的实例。
示例代码:
// 定义类
class Student {
// 成员变量(属性)
private String name;
private int age;
private double score;
// 构造方法
public Student() {
this.name = "未知";
this.age = 0;
this.score = 0.0;
}
public Student(String name, int age, double score) {
this.name = name;
this.age = age;
this.score = score;
}
// 成员方法
public void study() {
System.out.println(name + "正在学习");
}
public void showInfo() {
System.out.println("姓名: " + name + ", 年龄: " + age + ", 成绩: " + score);
}
// Getter和Setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age > 0 && age < 150) {
this.age = age;
}
}
public double getScore() {
return score;
}
public void setScore(double score) {
if (score >= 0 && score <= 100) {
this.score = score;
}
}
}
// 使用类
public class ObjectExample {
public static void main(String[] args) {
// 创建对象
Student student1 = new Student(); // 使用无参构造
student1.setName("张三");
student1.setAge(20);
student1.setScore(85.5);
Student student2 = new Student("李四", 21, 92.0); // 使用有参构造
// 调用方法
student1.study();
student1.showInfo();
student2.study();
student2.showInfo();
// 访问属性
System.out.println(student2.getName() + "的年龄是: " + student2.getAge());
}
}
2.2 封装、继承、多态
封装:将数据和操作数据的方法绑定在一起,隐藏实现细节。 继承:子类继承父类的属性和方法,实现代码复用。 多态:同一个接口,不同的实现方式。
示例代码:
// 父类
class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println(name + "正在吃东西");
}
public void sleep() {
System.out.println(name + "正在睡觉");
}
}
// 子类Dog
class Dog extends Animal {
public Dog(String name) {
super(name); // 调用父类构造方法
}
@Override
public void eat() {
System.out.println(name + "正在吃狗粮");
}
public void bark() {
System.out.println(name + "汪汪叫");
}
}
// 子类Cat
class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(name + "正在吃鱼");
}
public void catchMouse() {
System.out.println(name + "正在抓老鼠");
}
}
// 多态示例
public class InheritancePolymorphismExample {
public static void main(String[] args) {
// 多态:父类引用指向子类对象
Animal animal1 = new Dog("旺财");
Animal animal2 = new Cat("咪咪");
// 编译时类型是Animal,运行时类型是Dog
animal1.eat(); // 输出: 旺财正在吃狗粮
animal1.sleep(); // 输出: 旺财正在睡觉
// 编译时类型是Animal,运行时类型是Cat
animal2.eat(); // 输出: 咪咪正在吃鱼
animal2.sleep(); // 输出: 咪咪正在睡觉
// 向下转型(需要强制类型转换)
if (animal1 instanceof Dog) {
Dog dog = (Dog) animal1;
dog.bark(); // 输出: 旺财汪汪叫
}
if (animal2 instanceof Cat) {
Cat cat = (Cat) animal2;
cat.catchMouse(); // 输出: 咪咪正在抓老鼠
}
// 多态数组
Animal[] animals = {new Dog("大黄"), new Cat("小白"), new Dog("小黑")};
for (Animal animal : animals) {
animal.eat(); // 分别调用Dog和Cat的eat方法
}
}
}
2.3 抽象类与接口
抽象类:包含抽象方法的类,不能被实例化。 接口:完全抽象的类,只能包含常量和抽象方法(Java 8后可以有默认方法和静态方法)。
示例代码:
// 抽象类
abstract class Shape {
protected String color;
public Shape(String color) {
this.color = color;
}
// 抽象方法
public abstract double area();
// 具体方法
public void display() {
System.out.println("这是一个" + color + "的图形");
}
}
// 接口
interface Drawable {
void draw(); // 抽象方法
default void printInfo() {
System.out.println("这是一个可绘制的图形");
}
static void showVersion() {
System.out.println("Drawable接口版本1.0");
}
}
// 实现抽象类和接口
class Circle extends Shape implements Drawable {
private double radius;
public Circle(String color, double radius) {
super(color);
this.radius = radius;
}
@Override
public double area() {
return Math.PI * radius * radius;
}
@Override
public void draw() {
System.out.println("绘制一个" + color + "的圆形,半径为" + radius);
}
}
class Rectangle extends Shape implements Drawable {
private double width;
private double height;
public Rectangle(String color, double width, double height) {
super(color);
this.width = width;
this.height = height;
}
@Override
public double area() {
return width * height;
}
@Override
public void draw() {
System.out.println("绘制一个" + color + "的矩形,宽" + width + ",高" + height);
}
}
public class AbstractInterfaceExample {
public static void main(String[] args) {
// 创建对象
Circle circle = new Circle("红色", 5.0);
Rectangle rectangle = new Rectangle("蓝色", 4.0, 6.0);
// 使用抽象类引用
Shape shape1 = circle;
Shape shape2 = rectangle;
System.out.println("圆形面积: " + shape1.area());
System.out.println("矩形面积: " + shape2.area());
shape1.display();
shape2.display();
// 使用接口引用
Drawable drawable1 = circle;
Drawable drawable2 = rectangle;
drawable1.draw();
drawable2.draw();
// 调用接口默认方法
drawable1.printInfo();
// 调用接口静态方法
Drawable.showVersion();
// 多态数组
Shape[] shapes = {circle, rectangle};
for (Shape shape : shapes) {
shape.display();
System.out.println("面积: " + shape.area());
}
}
}
三、集合框架
3.1 集合框架概述
Java集合框架主要分为两大类:
- Collection:单列集合,包括List、Set、Queue
- Map:双列集合,键值对存储
3.2 List接口及实现类
List是有序、可重复的集合。
示例代码:
import java.util.*;
public class ListExample {
public static void main(String[] args) {
// ArrayList - 动态数组,查询快,增删慢
List<String> arrayList = new ArrayList<>();
arrayList.add("Java");
arrayList.add("Python");
arrayList.add("C++");
arrayList.add("Java"); // 允许重复
System.out.println("ArrayList: " + arrayList);
System.out.println("索引1的元素: " + arrayList.get(1));
System.out.println("是否包含Java: " + arrayList.contains("Java"));
// LinkedList - 双向链表,增删快,查询慢
List<String> linkedList = new LinkedList<>();
linkedList.add("Apple");
linkedList.add("Banana");
linkedList.add("Orange");
System.out.println("LinkedList: " + linkedList);
// Vector - 线程安全的动态数组(已过时,推荐使用ArrayList)
List<String> vector = new Vector<>();
vector.add("A");
vector.add("B");
vector.add("C");
System.out.println("Vector: " + vector);
// List常用方法
System.out.println("\nList常用方法:");
System.out.println("大小: " + arrayList.size());
System.out.println("是否为空: " + arrayList.isEmpty());
System.out.println("第一个元素: " + arrayList.get(0));
System.out.println("最后一个元素: " + arrayList.get(arrayList.size() - 1));
// 修改元素
arrayList.set(1, "JavaScript");
System.out.println("修改后: " + arrayList);
// 删除元素
arrayList.remove(2);
System.out.println("删除索引2后: " + arrayList);
// 遍历List
System.out.println("\n遍历List:");
// 1. 普通for循环
for (int i = 0; i < arrayList.size(); i++) {
System.out.println("索引" + i + ": " + arrayList.get(i));
}
// 2. 增强for循环
System.out.println("\n增强for循环:");
for (String str : arrayList) {
System.out.println(str);
}
// 3. 迭代器
System.out.println("\n迭代器遍历:");
Iterator<String> iterator = arrayList.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 4. ListIterator(双向遍历)
System.out.println("\nListIterator遍历:");
ListIterator<String> listIterator = arrayList.listIterator();
while (listIterator.hasNext()) {
System.out.println(listIterator.next());
}
// 5. Java 8 Stream API
System.out.println("\nStream API遍历:");
arrayList.stream().forEach(System.out::println);
}
}
3.3 Set接口及实现类
Set是无序、不可重复的集合。
示例代码:
import java.util.*;
public class SetExample {
public static void main(String[] args) {
// HashSet - 基于哈希表实现,无序,查询快
Set<String> hashSet = new HashSet<>();
hashSet.add("Java");
hashSet.add("Python");
hashSet.add("C++");
hashSet.add("Java"); // 重复元素不会被添加
System.out.println("HashSet: " + hashSet);
System.out.println("大小: " + hashSet.size());
System.out.println("是否包含Java: " + hashSet.contains("Java"));
// LinkedHashSet - 保持插入顺序
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("Java");
linkedHashSet.add("Python");
linkedHashSet.add("C++");
System.out.println("LinkedHashSet: " + linkedHashSet);
// TreeSet - 基于红黑树实现,自动排序
Set<Integer> treeSet = new TreeSet<>();
treeSet.add(5);
treeSet.add(3);
treeSet.add(8);
treeSet.add(1);
treeSet.add(9);
System.out.println("TreeSet: " + treeSet); // [1, 3, 5, 8, 9]
// 自定义对象的TreeSet(需要实现Comparable接口)
Set<Person> personSet = new TreeSet<>();
personSet.add(new Person("张三", 20));
personSet.add(new Person("李四", 18));
personSet.add(new Person("王五", 22));
System.out.println("Person TreeSet: " + personSet);
// Set常用方法
System.out.println("\nSet常用方法:");
System.out.println("大小: " + hashSet.size());
System.out.println("是否为空: " + hashSet.isEmpty());
System.out.println("是否包含Python: " + hashSet.contains("Python"));
// 删除元素
hashSet.remove("C++");
System.out.println("删除C++后: " + hashSet);
// 遍历Set
System.out.println("\n遍历Set:");
// 1. 增强for循环
for (String str : hashSet) {
System.out.println(str);
}
// 2. 迭代器
System.out.println("\n迭代器遍历:");
Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
// 3. Java 8 Stream API
System.out.println("\nStream API遍历:");
hashSet.stream().forEach(System.out::println);
}
}
// 自定义Person类(实现Comparable接口用于TreeSet排序)
class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person other) {
// 按年龄升序排序
return Integer.compare(this.age, other.age);
}
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
3.4 Map接口及实现类
Map存储键值对,键唯一。
示例代码:
import java.util.*;
public class MapExample {
public static void main(String[] args) {
// HashMap - 基于哈希表,无序,查询快
Map<String, Integer> hashMap = new HashMap<>();
hashMap.put("Java", 85);
hashMap.put("Python", 90);
hashMap.put("C++", 78);
hashMap.put("Java", 88); // 键重复,值会被覆盖
System.out.println("HashMap: " + hashMap);
System.out.println("Java的分数: " + hashMap.get("Java"));
System.out.println("是否包含键Python: " + hashMap.containsKey("Python"));
System.out.println("是否包含值90: " + hashMap.containsValue(90));
// LinkedHashMap - 保持插入顺序
Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
linkedHashMap.put("Java", 85);
linkedHashMap.put("Python", 90);
linkedHashMap.put("C++", 78);
System.out.println("LinkedHashMap: " + linkedHashMap);
// TreeMap - 基于红黑树,按键排序
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("Java", 85);
treeMap.put("Python", 90);
treeMap.put("C++", 78);
treeMap.put("JavaScript", 92);
System.out.println("TreeMap: " + treeMap); // 按键的字母顺序排序
// Map常用方法
System.out.println("\nMap常用方法:");
System.out.println("大小: " + hashMap.size());
System.out.println("是否为空: " + hashMap.isEmpty());
System.out.println("获取Java的值: " + hashMap.get("Java"));
System.out.println("获取不存在的键: " + hashMap.getOrDefault("Ruby", 0));
// 删除元素
hashMap.remove("C++");
System.out.println("删除C++后: " + hashMap);
// 遍历Map
System.out.println("\n遍历Map:");
// 1. 使用entrySet
System.out.println("使用entrySet:");
for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
System.out.println("键: " + entry.getKey() + ", 值: " + entry.getValue());
}
// 2. 使用keySet
System.out.println("\n使用keySet:");
for (String key : hashMap.keySet()) {
System.out.println("键: " + key + ", 值: " + hashMap.get(key));
}
// 3. 使用values
System.out.println("\n使用values:");
for (Integer value : hashMap.values()) {
System.out.println("值: " + value);
}
// 4. Java 8 forEach
System.out.println("\nJava 8 forEach:");
hashMap.forEach((key, value) -> System.out.println("键: " + key + ", 值: " + value));
// 5. Java 8 Stream API
System.out.println("\nJava 8 Stream API:");
hashMap.entrySet().stream()
.forEach(entry -> System.out.println("键: " + entry.getKey() + ", 值: " + entry.getValue()));
}
}
四、多线程
4.1 线程基础
Java中创建线程的两种主要方式:
- 继承Thread类
- 实现Runnable接口(推荐)
示例代码:
// 方式1:继承Thread类
class MyThread extends Thread {
private String name;
public MyThread(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + " - 线程执行: " + i);
try {
Thread.sleep(100); // 线程休眠100毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 方式2:实现Runnable接口
class MyRunnable implements Runnable {
private String name;
public MyRunnable(String name) {
this.name = name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + " - 线程执行: " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadExample {
public static void main(String[] args) throws InterruptedException {
// 方式1:继承Thread类
MyThread thread1 = new MyThread("线程1");
MyThread thread2 = new MyThread("线程2");
// 启动线程
thread1.start();
thread2.start();
// 等待线程执行完成
thread1.join();
thread2.join();
System.out.println("线程1和线程2执行完成");
// 方式2:实现Runnable接口
MyRunnable runnable1 = new MyRunnable("线程3");
MyRunnable runnable2 = new MyRunnable("线程4");
Thread thread3 = new Thread(runnable1);
Thread thread4 = new Thread(runnable2);
thread3.start();
thread4.start();
thread3.join();
thread4.join();
System.out.println("线程3和线程4执行完成");
// 使用Lambda表达式创建线程(Java 8+)
Thread thread5 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
System.out.println("Lambda线程 - 执行: " + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread5.start();
thread5.join();
System.out.println("Lambda线程执行完成");
}
}
4.2 线程同步与锁
多线程访问共享资源时需要同步,防止数据不一致。
示例代码:
// 线程不安全的计数器
class UnsafeCounter {
private int count = 0;
public void increment() {
count++; // 非原子操作
}
public int getCount() {
return count;
}
}
// 线程安全的计数器(使用synchronized)
class SafeCounter {
private int count = 0;
public synchronized void increment() {
count++;
}
public int getCount() {
return count;
}
}
// 线程安全的计数器(使用Lock)
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
class LockCounter {
private int count = 0;
private Lock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
return count;
}
}
public class SynchronizationExample {
public static void main(String[] args) throws InterruptedException {
// 测试线程不安全的计数器
System.out.println("测试线程不安全的计数器:");
UnsafeCounter unsafeCounter = new UnsafeCounter();
Thread[] unsafeThreads = new Thread[10];
for (int i = 0; i < 10; i++) {
unsafeThreads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
unsafeCounter.increment();
}
});
unsafeThreads[i].start();
}
for (Thread t : unsafeThreads) {
t.join();
}
System.out.println("不安全计数器结果: " + unsafeCounter.getCount()); // 通常小于10000
// 测试线程安全的计数器(synchronized)
System.out.println("\n测试线程安全的计数器(synchronized):");
SafeCounter safeCounter = new SafeCounter();
Thread[] safeThreads = new Thread[10];
for (int i = 0; i < 10; i++) {
safeThreads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
safeCounter.increment();
}
});
safeThreads[i].start();
}
for (Thread t : safeThreads) {
t.join();
}
System.out.println("安全计数器结果: " + safeCounter.getCount()); // 10000
// 测试线程安全的计数器(Lock)
System.out.println("\n测试线程安全的计数器(Lock):");
LockCounter lockCounter = new LockCounter();
Thread[] lockThreads = new Thread[10];
for (int i = 0; i < 10; i++) {
lockThreads[i] = new Thread(() -> {
for (int j = 0; j < 1000; j++) {
lockCounter.increment();
}
});
lockThreads[i].start();
}
for (Thread t : lockThreads) {
t.join();
}
System.out.println("Lock计数器结果: " + lockCounter.getCount()); // 10000
}
}
4.3 线程池
线程池可以有效管理线程资源,避免频繁创建和销毁线程。
示例代码:
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) throws InterruptedException {
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(3); // 固定大小的线程池
// 提交任务
for (int i = 0; i < 5; i++) {
final int taskId = i;
executor.submit(() -> {
System.out.println("任务" + taskId + "开始执行,线程: " + Thread.currentThread().getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任务" + taskId + "执行完成");
});
}
// 关闭线程池
executor.shutdown();
// 等待所有任务完成
if (executor.awaitTermination(10, TimeUnit.SECONDS)) {
System.out.println("所有任务已完成");
} else {
System.out.println("部分任务未完成");
}
// 使用Callable和Future获取返回值
System.out.println("\n使用Callable和Future:");
ExecutorService executor2 = Executors.newFixedThreadPool(2);
// 提交Callable任务
Future<Integer> future1 = executor2.submit(() -> {
Thread.sleep(2000);
return 100;
});
Future<Integer> future2 = executor2.submit(() -> {
Thread.sleep(1000);
return 200;
});
try {
// 获取结果(会阻塞直到任务完成)
Integer result1 = future1.get();
Integer result2 = future2.get();
System.out.println("任务1结果: " + result1);
System.out.println("任务2结果: " + result2);
System.out.println("总和: " + (result1 + result2));
} catch (ExecutionException e) {
e.printStackTrace();
}
executor2.shutdown();
// 使用ScheduledExecutorService定时任务
System.out.println("\n使用ScheduledExecutorService:");
ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(1);
// 延迟执行
scheduledExecutor.schedule(() -> {
System.out.println("延迟任务执行");
}, 3, TimeUnit.SECONDS);
// 定期执行
scheduledExecutor.scheduleAtFixedRate(() -> {
System.out.println("定期任务执行,时间: " + new Date());
}, 1, 2, TimeUnit.SECONDS);
// 5秒后关闭定时任务
Thread.sleep(5000);
scheduledExecutor.shutdown();
}
}
五、数据库操作
5.1 JDBC基础
JDBC(Java Database Connectivity)是Java连接数据库的标准API。
示例代码:
import java.sql.*;
public class JdbcExample {
// 数据库连接信息
private static final String URL = "jdbc:mysql://localhost:3306/testdb";
private static final String USER = "root";
private static final String PASSWORD = "password";
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
// 1. 加载驱动(MySQL 8.0+)
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 建立连接
connection = DriverManager.getConnection(URL, USER, PASSWORD);
System.out.println("数据库连接成功");
// 3. 创建Statement
statement = connection.createStatement();
// 4. 执行查询
String sql = "SELECT * FROM users";
resultSet = statement.executeQuery(sql);
// 5. 处理结果集
System.out.println("查询结果:");
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String email = resultSet.getString("email");
System.out.println("ID: " + id + ", 姓名: " + name + ", 邮箱: " + email);
}
// 6. 插入数据
String insertSql = "INSERT INTO users (name, email) VALUES ('张三', 'zhangsan@example.com')";
int rowsAffected = statement.executeUpdate(insertSql);
System.out.println("插入了 " + rowsAffected + " 行数据");
// 7. 更新数据
String updateSql = "UPDATE users SET email = 'zhangsan_new@example.com' WHERE name = '张三'";
rowsAffected = statement.executeUpdate(updateSql);
System.out.println("更新了 " + rowsAffected + " 行数据");
// 8. 删除数据
String deleteSql = "DELETE FROM users WHERE name = '张三'";
rowsAffected = statement.executeUpdate(deleteSql);
System.out.println("删除了 " + rowsAffected + " 行数据");
} catch (ClassNotFoundException e) {
System.err.println("找不到数据库驱动: " + e.getMessage());
} catch (SQLException e) {
System.err.println("数据库操作异常: " + e.getMessage());
e.printStackTrace();
} finally {
// 9. 关闭资源
try {
if (resultSet != null) resultSet.close();
if (statement != null) statement.close();
if (connection != null) connection.close();
System.out.println("数据库连接已关闭");
} catch (SQLException e) {
System.err.println("关闭资源异常: " + e.getMessage());
}
}
}
}
5.2 PreparedStatement
PreparedStatement可以防止SQL注入,提高性能。
示例代码:
import java.sql.*;
public class PreparedStatementExample {
private static final String URL = "jdbc:mysql://localhost:3306/testdb";
private static final String USER = "root";
private static final String PASSWORD = "password";
public static void main(String[] args) {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
// 1. 加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 建立连接
connection = DriverManager.getConnection(URL, USER, PASSWORD);
// 3. 插入数据(使用PreparedStatement)
String insertSql = "INSERT INTO users (name, email) VALUES (?, ?)";
preparedStatement = connection.prepareStatement(insertSql);
// 设置参数
preparedStatement.setString(1, "李四");
preparedStatement.setString(2, "lisi@example.com");
// 执行更新
int rowsAffected = preparedStatement.executeUpdate();
System.out.println("插入了 " + rowsAffected + " 行数据");
// 4. 查询数据(使用PreparedStatement)
String selectSql = "SELECT * FROM users WHERE name = ?";
preparedStatement = connection.prepareStatement(selectSql);
preparedStatement.setString(1, "李四");
ResultSet resultSet = preparedStatement.executeQuery();
System.out.println("查询结果:");
while (resultSet.next()) {
int id = resultSet.getInt("id");
String name = resultSet.getString("name");
String email = resultSet.getString("email");
System.out.println("ID: " + id + ", 姓名: " + name + ", 邮箱: " + email);
}
// 5. 批量操作
String batchInsertSql = "INSERT INTO users (name, email) VALUES (?, ?)";
preparedStatement = connection.prepareStatement(batchInsertSql);
// 添加批处理
for (int i = 1; i <= 5; i++) {
preparedStatement.setString(1, "用户" + i);
preparedStatement.setString(2, "user" + i + "@example.com");
preparedStatement.addBatch();
}
// 执行批处理
int[] batchResults = preparedStatement.executeBatch();
System.out.println("批处理插入了 " + batchResults.length + " 个批次");
} catch (ClassNotFoundException e) {
System.err.println("找不到数据库驱动: " + e.getMessage());
} catch (SQLException e) {
System.err.println("数据库操作异常: " + e.getMessage());
e.printStackTrace();
} finally {
// 关闭资源
try {
if (preparedStatement != null) preparedStatement.close();
if (connection != null) connection.close();
} catch (SQLException e) {
System.err.println("关闭资源异常: " + e.getMessage());
}
}
}
}
5.3 事务管理
事务确保数据库操作的原子性、一致性、隔离性和持久性。
示例代码:
import java.sql.*;
public class TransactionExample {
private static final String URL = "jdbc:mysql://localhost:3306/testdb";
private static final String USER = "root";
private static final String PASSWORD = "password";
public static void main(String[] args) {
Connection connection = null;
PreparedStatement updateStatement = null;
PreparedStatement insertStatement = null;
try {
// 1. 加载驱动
Class.forName("com.mysql.cj.jdbc.Driver");
// 2. 建立连接
connection = DriverManager.getConnection(URL, USER, PASSWORD);
// 3. 关闭自动提交,开启事务
connection.setAutoCommit(false);
System.out.println("事务已开启");
// 4. 执行多个数据库操作
// 操作1:更新用户邮箱
String updateSql = "UPDATE users SET email = ? WHERE name = ?";
updateStatement = connection.prepareStatement(updateSql);
updateStatement.setString(1, "new_email@example.com");
updateStatement.setString(2, "李四");
int updateRows = updateStatement.executeUpdate();
System.out.println("更新了 " + updateRows + " 行数据");
// 操作2:插入新用户
String insertSql = "INSERT INTO users (name, email) VALUES (?, ?)";
insertStatement = connection.prepareStatement(insertSql);
insertStatement.setString(1, "王五");
insertStatement.setString(2, "wangwu@example.com");
int insertRows = insertStatement.executeUpdate();
System.out.println("插入了 " + insertRows + " 行数据");
// 5. 提交事务
connection.commit();
System.out.println("事务已提交");
} catch (ClassNotFoundException e) {
System.err.println("找不到数据库驱动: " + e.getMessage());
// 回滚事务
if (connection != null) {
try {
connection.rollback();
System.out.println("事务已回滚");
} catch (SQLException ex) {
System.err.println("回滚事务异常: " + ex.getMessage());
}
}
} catch (SQLException e) {
System.err.println("数据库操作异常: " + e.getMessage());
// 回滚事务
if (connection != null) {
try {
connection.rollback();
System.out.println("事务已回滚");
} catch (SQLException ex) {
System.err.println("回滚事务异常: " + ex.getMessage());
}
}
e.printStackTrace();
} finally {
// 6. 关闭资源
try {
if (updateStatement != null) updateStatement.close();
if (insertStatement != null) insertStatement.close();
if (connection != null) {
// 恢复自动提交
connection.setAutoCommit(true);
connection.close();
}
System.out.println("数据库连接已关闭");
} catch (SQLException e) {
System.err.println("关闭资源异常: " + e.getMessage());
}
}
}
}
六、面试常见问题及解答
6.1 Java基础常见问题
问题1:String、StringBuilder和StringBuffer的区别?
解答:
- String:不可变字符串,每次修改都会创建新对象,适用于字符串不经常修改的场景。
- StringBuilder:可变字符串,线程不安全,适用于单线程环境下的字符串频繁修改。
- StringBuffer:可变字符串,线程安全,适用于多线程环境下的字符串频繁修改。
示例代码:
public class StringExample {
public static void main(String[] args) {
// String不可变性
String str1 = "Hello";
String str2 = str1 + " World";
System.out.println(str1); // Hello (str1未改变)
System.out.println(str2); // Hello World
// StringBuilder可变性
StringBuilder sb = new StringBuilder("Hello");
sb.append(" World");
System.out.println(sb); // Hello World
// StringBuffer可变性(线程安全)
StringBuffer sbf = new StringBuffer("Hello");
sbf.append(" World");
System.out.println(sbf); // Hello World
// 性能测试
long startTime, endTime;
// String拼接(慢)
startTime = System.currentTimeMillis();
String result = "";
for (int i = 0; i < 10000; i++) {
result += i;
}
endTime = System.currentTimeMillis();
System.out.println("String拼接耗时: " + (endTime - startTime) + "ms");
// StringBuilder拼接(快)
startTime = System.currentTimeMillis();
StringBuilder sbResult = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sbResult.append(i);
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder拼接耗时: " + (endTime - startTime) + "ms");
}
}
问题2:==和equals()的区别?
解答:
- ==:比较基本数据类型时比较值,比较引用数据类型时比较内存地址。
- equals():默认比较内存地址(与==相同),但String等类重写了equals()方法,比较内容是否相等。
示例代码:
public class EqualsExample {
public static void main(String[] args) {
// 基本数据类型
int a = 10;
int b = 10;
System.out.println("a == b: " + (a == b)); // true
// 引用数据类型
String str1 = new String("Hello");
String str2 = new String("Hello");
String str3 = "Hello";
String str4 = "Hello";
// ==比较内存地址
System.out.println("str1 == str2: " + (str1 == str2)); // false
System.out.println("str3 == str4: " + (str3 == str4)); // true (字符串常量池)
// equals比较内容
System.out.println("str1.equals(str2): " + str1.equals(str2)); // true
System.out.println("str3.equals(str4): " + str3.equals(str4)); // true
// 自定义类
Person p1 = new Person("张三", 20);
Person p2 = new Person("张三", 20);
// 未重写equals时
System.out.println("p1 == p2: " + (p1 == p2)); // false
System.out.println("p1.equals(p2): " + p1.equals(p2)); // false
// 重写equals后
Person p3 = new Person("李四", 25);
Person p4 = new Person("李四", 25);
System.out.println("p3.equals(p4): " + p3.equals(p4)); // true
}
}
6.2 面向对象常见问题
问题3:重写(Override)和重载(Overload)的区别?
解答:
- 重写(Override):子类重新定义父类的方法,方法名、参数列表、返回类型必须相同,访问权限不能更严格。
- 重载(Overload):同一个类中方法名相同但参数列表不同(参数类型、个数、顺序),与返回类型无关。
示例代码:
class Parent {
public void show() {
System.out.println("Parent show");
}
public void display(int a) {
System.out.println("Parent display: " + a);
}
public void display(String s) {
System.out.println("Parent display: " + s);
}
}
class Child extends Parent {
// 重写(Override)
@Override
public void show() {
System.out.println("Child show");
}
// 重载(Overload)
public void display(double d) {
System.out.println("Child display: " + d);
}
}
public class OverrideOverloadExample {
public static void main(String[] args) {
Parent parent = new Parent();
parent.show(); // Parent show
parent.display(10); // Parent display: 10
parent.display("Hello"); // Parent display: Hello
Child child = new Child();
child.show(); // Child show (重写)
child.display(10); // Parent display: 10 (重载)
child.display("Hello"); // Parent display: Hello (重载)
child.display(3.14); // Child display: 3.14 (重载)
}
}
问题4:抽象类和接口的区别?
解答:
- 抽象类:
- 可以包含具体方法和抽象方法
- 可以有成员变量
- 单继承(一个类只能继承一个抽象类)
- 用于表示”is-a”关系
- 接口:
- 只能包含抽象方法(Java 8后可以有默认方法和静态方法)
- 只能有常量(public static final)
- 多实现(一个类可以实现多个接口)
- 用于表示”can-do”关系
6.3 集合框架常见问题
问题5:ArrayList和LinkedList的区别?
解答:
- ArrayList:
- 基于动态数组实现
- 查询快(O(1)),增删慢(需要移动元素)
- 内存连续,缓存友好
- 默认初始容量10,扩容1.5倍
- LinkedList:
- 基于双向链表实现
- 查询慢(O(n)),增删快(只需要修改指针)
- 内存不连续,缓存不友好
- 每个元素需要额外存储前后指针
问题6:HashMap的工作原理?
解答:
- 存储结构:数组+链表/红黑树(Java 8+)
- 哈希过程:通过key的hashCode()计算哈希值,再通过扰动函数((h = key.hashCode()) ^ (h >>> 16))减少碰撞
- 解决哈希冲突:链地址法(拉链法),当链表长度超过8且数组长度超过64时,链表转为红黑树
- 扩容机制:当元素数量超过容量*负载因子(默认0.75)时,扩容为原来的2倍
示例代码:
import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
public static void main(String[] args) {
Map<String, Integer> map = new HashMap<>();
// 添加元素
map.put("Java", 85);
map.put("Python", 90);
map.put("C++", 78);
// 获取元素
System.out.println("Java的分数: " + map.get("Java"));
// 遍历Map
System.out.println("遍历Map:");
for (Map.Entry<String, Integer> entry : map.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// 计算哈希值
String key = "Java";
int hashCode = key.hashCode();
int hash = hashCode ^ (hashCode >>> 16);
System.out.println("\nKey: " + key);
System.out.println("hashCode: " + hashCode);
System.out.println("hash: " + hash);
// 计算数组索引(假设数组长度为16)
int index = hash & 15; // 16-1=15
System.out.println("数组索引: " + index);
}
}
6.4 多线程常见问题
问题7:synchronized和Lock的区别?
解答:
- synchronized:
- Java关键字,内置锁
- 自动获取和释放锁
- 不可中断,非公平锁
- 重量级锁(Java 6后优化为轻量级锁、偏向锁)
- Lock:
- Java API,需要手动实现
- 需要手动获取和释放锁(通常在finally中)
- 可中断,可设置公平锁
- 提供了更多功能(tryLock、lockInterruptibly等)
问题8:volatile关键字的作用?
解答:
- 保证可见性:一个线程修改volatile变量,其他线程能立即看到
- 禁止指令重排序:防止编译器和处理器对指令进行重排序
- 不保证原子性:volatile不能保证复合操作的原子性(如i++)
示例代码:
public class VolatileExample {
private static volatile boolean flag = false;
public static void main(String[] args) throws InterruptedException {
// 线程1:等待flag变为true
Thread thread1 = new Thread(() -> {
System.out.println("线程1开始等待");
while (!flag) {
// 空循环
}
System.out.println("线程1检测到flag变为true");
});
// 线程2:修改flag
Thread thread2 = new Thread(() -> {
System.out.println("线程2开始执行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
System.out.println("线程2将flag设置为true");
});
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("程序结束");
}
}
6.5 数据库常见问题
问题9:JDBC连接数据库的步骤?
解答:
- 加载数据库驱动(Class.forName())
- 建立数据库连接(DriverManager.getConnection())
- 创建Statement或PreparedStatement对象
- 执行SQL语句(executeQuery()或executeUpdate())
- 处理结果集(ResultSet)
- 关闭资源(ResultSet、Statement、Connection)
问题10:PreparedStatement和Statement的区别?
解答:
- PreparedStatement:
- 预编译SQL,提高性能
- 防止SQL注入攻击
- 可以设置参数
- 适合执行多次相同结构的SQL
- Statement:
- 每次执行都需要编译SQL
- 容易受到SQL注入攻击
- 不能设置参数
- 适合执行一次性的SQL
七、总结
本文详细介绍了Java开发实习生面试必备的核心知识点,包括基础语法、面向对象、集合框架、多线程和数据库操作。每个知识点都配有完整的代码示例,帮助你深入理解这些概念。
面试准备建议:
- 理解原理:不仅要会用,还要理解背后的原理
- 动手实践:多写代码,多调试,加深理解
- 总结归纳:将知识点系统化,形成知识网络
- 模拟面试:找同学或朋友进行模拟面试,锻炼表达能力
- 关注最新技术:了解Java 8+的新特性(Lambda、Stream API等)
常见面试技巧:
- 回答问题时先给出结论,再解释原因
- 结合实际项目经验回答问题
- 遇到不会的问题,尝试从相关知识点入手分析
- 保持自信,展示解决问题的思路
通过系统学习和实践,相信你一定能够轻松应对Java开发实习生的面试挑战!祝你面试顺利!
