引言

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中创建线程的两种主要方式:

  1. 继承Thread类
  2. 实现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:抽象类和接口的区别?

解答

  • 抽象类
    1. 可以包含具体方法和抽象方法
    2. 可以有成员变量
    3. 单继承(一个类只能继承一个抽象类)
    4. 用于表示”is-a”关系
  • 接口
    1. 只能包含抽象方法(Java 8后可以有默认方法和静态方法)
    2. 只能有常量(public static final)
    3. 多实现(一个类可以实现多个接口)
    4. 用于表示”can-do”关系

6.3 集合框架常见问题

问题5:ArrayList和LinkedList的区别?

解答

  • ArrayList
    1. 基于动态数组实现
    2. 查询快(O(1)),增删慢(需要移动元素)
    3. 内存连续,缓存友好
    4. 默认初始容量10,扩容1.5倍
  • LinkedList
    1. 基于双向链表实现
    2. 查询慢(O(n)),增删快(只需要修改指针)
    3. 内存不连续,缓存不友好
    4. 每个元素需要额外存储前后指针

问题6:HashMap的工作原理?

解答

  1. 存储结构:数组+链表/红黑树(Java 8+)
  2. 哈希过程:通过key的hashCode()计算哈希值,再通过扰动函数((h = key.hashCode()) ^ (h >>> 16))减少碰撞
  3. 解决哈希冲突:链地址法(拉链法),当链表长度超过8且数组长度超过64时,链表转为红黑树
  4. 扩容机制:当元素数量超过容量*负载因子(默认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
    1. Java关键字,内置锁
    2. 自动获取和释放锁
    3. 不可中断,非公平锁
    4. 重量级锁(Java 6后优化为轻量级锁、偏向锁)
  • Lock
    1. Java API,需要手动实现
    2. 需要手动获取和释放锁(通常在finally中)
    3. 可中断,可设置公平锁
    4. 提供了更多功能(tryLock、lockInterruptibly等)

问题8:volatile关键字的作用?

解答

  1. 保证可见性:一个线程修改volatile变量,其他线程能立即看到
  2. 禁止指令重排序:防止编译器和处理器对指令进行重排序
  3. 不保证原子性: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连接数据库的步骤?

解答

  1. 加载数据库驱动(Class.forName())
  2. 建立数据库连接(DriverManager.getConnection())
  3. 创建Statement或PreparedStatement对象
  4. 执行SQL语句(executeQuery()或executeUpdate())
  5. 处理结果集(ResultSet)
  6. 关闭资源(ResultSet、Statement、Connection)

问题10:PreparedStatement和Statement的区别?

解答

  • PreparedStatement
    1. 预编译SQL,提高性能
    2. 防止SQL注入攻击
    3. 可以设置参数
    4. 适合执行多次相同结构的SQL
  • Statement
    1. 每次执行都需要编译SQL
    2. 容易受到SQL注入攻击
    3. 不能设置参数
    4. 适合执行一次性的SQL

七、总结

本文详细介绍了Java开发实习生面试必备的核心知识点,包括基础语法、面向对象、集合框架、多线程和数据库操作。每个知识点都配有完整的代码示例,帮助你深入理解这些概念。

面试准备建议

  1. 理解原理:不仅要会用,还要理解背后的原理
  2. 动手实践:多写代码,多调试,加深理解
  3. 总结归纳:将知识点系统化,形成知识网络
  4. 模拟面试:找同学或朋友进行模拟面试,锻炼表达能力
  5. 关注最新技术:了解Java 8+的新特性(Lambda、Stream API等)

常见面试技巧

  • 回答问题时先给出结论,再解释原因
  • 结合实际项目经验回答问题
  • 遇到不会的问题,尝试从相关知识点入手分析
  • 保持自信,展示解决问题的思路

通过系统学习和实践,相信你一定能够轻松应对Java开发实习生的面试挑战!祝你面试顺利!