前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >2025 年一线互联网大厂最新高质量 Java 面试八股文整理含答案及实操要点

2025 年一线互联网大厂最新高质量 Java 面试八股文整理含答案及实操要点

原创
作者头像
啦啦啦191
发布于 2025-06-08 04:38:05
发布于 2025-06-08 04:38:05
1980
举报
文章被收录于专栏:Java开发Java开发

我将从Java基础、集合、多线程等多个重要方面,为你呈现一线互联网大厂最新的高质量Java面试八股文及答案,帮助你高效备考。

一线互联网大厂最新高质量Java面试八股文整理(附答案及实操)

Java基础

1. 面向对象和面向过程的区别(实操)

面向过程实现计算器
代码语言:java
AI代码解释
复制
import java.util.Scanner;

public class CalculatorProcedure {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入第一个数字: ");
        double num1 = scanner.nextDouble();
        System.out.print("请输入操作符(+, -, *, /): ");
        char operator = scanner.next().charAt(0);
        System.out.print("请输入第二个数字: ");
        double num2 = scanner.nextDouble();
        
        double result = 0;
        switch(operator) {
            case '+':
                result = add(num1, num2);
                break;
            case '-':
                result = subtract(num1, num2);
                break;
            case '*':
                result = multiply(num1, num2);
                break;
            case '/':
                result = divide(num1, num2);
                break;
            default:
                System.out.println("无效的操作符");
                return;
        }
        
        System.out.println("计算结果: " + result);
    }
    
    public static double add(double a, double b) {
        return a + b;
    }
    
    public static double subtract(double a, double b) {
        return a - b;
    }
    
    public static double multiply(double a, double b) {
        return a * b;
    }
    
    public static double divide(double a, double b) {
        if(b == 0) {
            throw new IllegalArgumentException("除数不能为0");
        }
        return a / b;
    }
}
面向对象实现计算器
代码语言:java
AI代码解释
复制
import java.util.Scanner;

interface Operation {
    double calculate(double num1, double num2);
}

class Addition implements Operation {
    @Override
    public double calculate(double num1, double num2) {
        return num1 + num2;
    }
}

class Subtraction implements Operation {
    @Override
    public double calculate(double num1, double num2) {
        return num1 - num2;
    }
}

class Multiplication implements Operation {
    @Override
    public double calculate(double num1, double num2) {
        return num1 * num2;
    }
}

class Division implements Operation {
    @Override
    public double calculate(double num1, double num2) {
        if(num2 == 0) {
            throw new IllegalArgumentException("除数不能为0");
        }
        return num1 / num2;
    }
}

class Calculator {
    public double performOperation(double num1, double num2, Operation operation) {
        return operation.calculate(num1, num2);
    }
}

public class CalculatorObject {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入第一个数字: ");
        double num1 = scanner.nextDouble();
        System.out.print("请输入操作符(+, -, *, /): ");
        char operator = scanner.next().charAt(0);
        System.out.print("请输入第二个数字: ");
        double num2 = scanner.nextDouble();
        
        Calculator calculator = new Calculator();
        Operation operation = getOperation(operator);
        
        double result = calculator.performOperation(num1, num2, operation);
        System.out.println("计算结果: " + result);
    }
    
    private static Operation getOperation(char operator) {
        switch(operator) {
            case '+':
                return new Addition();
            case '-':
                return new Subtraction();
            case '*':
                return new Multiplication();
            case '/':
                return new Division();
            default:
                throw new IllegalArgumentException("无效的操作符: " + operator);
        }
    }
}

2. equals与==的区别(实操)

代码语言:java
AI代码解释
复制
public class EqualsVsIdentity {
    public static void main(String[] args) {
        // 基本数据类型比较
        int a = 10;
        int b = 10;
        System.out.println("基本数据类型 == 比较: " + (a == b)); // true
        
        // 引用数据类型比较
        String s1 = new String("hello");
        String s2 = new String("hello");
        System.out.println("引用数据类型 == 比较: " + (s1 == s2)); // false
        System.out.println("引用数据类型 equals 比较: " + s1.equals(s2)); // true
        
        // 自定义对象比较
        User user1 = new User("张三", 20);
        User user2 = new User("张三", 20);
        System.out.println("自定义对象 == 比较: " + (user1 == user2)); // false
        System.out.println("自定义对象 equals 比较: " + user1.equals(user2)); // true
        
        // 未重写equals方法的对象比较
        Person person1 = new Person("李四", 30);
        Person person2 = new Person("李四", 30);
        System.out.println("未重写equals的对象 == 比较: " + (person1 == person2)); // false
        System.out.println("未重写equals的对象 equals 比较: " + person1.equals(person2)); // false
    }
}

class User {
    private String name;
    private int age;
    
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public boolean equals(Object obj) {
        if(this == obj) return true;
        if(obj == null || getClass() != obj.getClass()) return false;
        User other = (User) obj;
        return this.name.equals(other.name) && this.age == other.age;
    }
}

class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

3. final的用法(实操)

代码语言:java
AI代码解释
复制
import java.util.ArrayList;
import java.util.List;

public class FinalExample {
    // 静态常量
    public static final double PI = 3.14159;
    
    public static void main(String[] args) {
        // final修饰基本数据类型
        final int num = 10;
        // num = 20; // 编译错误,不能修改final变量
        
        // final修饰引用数据类型
        final List<String> list = new ArrayList<>();
        list.add("hello"); // 可以修改引用对象的内容
        // list = new ArrayList<>(); // 编译错误,不能修改引用
        
        // final修饰方法
        Child child = new Child();
        child.print(); // 调用父类的final方法
        
        // final修饰类
        // class MyString extends String {} // 编译错误,String是final类,不能被继承
    }
}

class Parent {
    public final void print() {
        System.out.println("Parent method");
    }
}

class Child extends Parent {
    // public void print() {} // 编译错误,不能重写final方法
}

4. static的用法(实操)

代码语言:java
AI代码解释
复制
public class StaticExample {
    // 静态变量
    public static int count = 0;
    // 实例变量
    public int id;
    
    public StaticExample() {
        count++;
        id = count;
    }
    
    // 静态方法
    public static void printCount() {
        System.out.println("Count: " + count);
        // System.out.println(id); // 编译错误,静态方法不能访问实例变量
    }
    
    // 静态代码块
    static {
        System.out.println("静态代码块执行");
    }
    
    // 实例代码块
    {
        System.out.println("实例代码块执行,id: " + id);
    }
    
    public static void main(String[] args) {
        System.out.println("main方法开始");
        
        StaticExample.printCount(); // 直接通过类名调用静态方法
        
        StaticExample s1 = new StaticExample();
        StaticExample s2 = new StaticExample();
        
        System.out.println("s1.id: " + s1.id);
        System.out.println("s2.id: " + s2.id);
        
        StaticExample.printCount();
    }
}

Java集合

1. ArrayList和LinkedList的区别(性能测试)

代码语言:java
AI代码解释
复制
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

public class ArrayListVsLinkedList {
    public static void main(String[] args) {
        int size = 100000;
        
        // 测试ArrayList随机访问性能
        List<Integer> arrayList = new ArrayList<>();
        for(int i = 0; i < size; i++) {
            arrayList.add(i);
        }
        
        long startTime = System.currentTimeMillis();
        for(int i = 0; i < size; i++) {
            arrayList.get(i);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("ArrayList随机访问耗时: " + (endTime - startTime) + "ms");
        
        // 测试LinkedList随机访问性能
        List<Integer> linkedList = new LinkedList<>();
        for(int i = 0; i < size; i++) {
            linkedList.add(i);
        }
        
        startTime = System.currentTimeMillis();
        for(int i = 0; i < size; i++) {
            linkedList.get(i);
        }
        endTime = System.currentTimeMillis();
        System.out.println("LinkedList随机访问耗时: " + (endTime - startTime) + "ms");
        
        // 测试ArrayList插入性能
        arrayList = new ArrayList<>();
        startTime = System.currentTimeMillis();
        for(int i = 0; i < size; i++) {
            arrayList.add(0, i);
        }
        endTime = System.currentTimeMillis();
        System.out.println("ArrayList头部插入耗时: " + (endTime - startTime) + "ms");
        
        // 测试LinkedList插入性能
        linkedList = new LinkedList<>();
        startTime = System.currentTimeMillis();
        for(int i = 0; i < size; i++) {
            linkedList.add(0, i);
        }
        endTime = System.currentTimeMillis();
        System.out.println("LinkedList头部插入耗时: " + (endTime - startTime) + "ms");
    }
}

2. HashMap的工作原理(自定义实现)

代码语言:java
AI代码解释
复制
import java.util.LinkedList;

public class MyHashMap<K, V> {
    private static final int DEFAULT_CAPACITY = 16;
    private LinkedList<Entry<K, V>>[] table;
    
    public MyHashMap() {
        table = new LinkedList[DEFAULT_CAPACITY];
        for(int i = 0; i < DEFAULT_CAPACITY; i++) {
            table[i] = new LinkedList<>();
        }
    }
    
    public void put(K key, V value) {
        int index = getIndex(key);
        LinkedList<Entry<K, V>> bucket = table[index];
        
        for(Entry<K, V> entry : bucket) {
            if(entry.key.equals(key)) {
                entry.value = value;
                return;
            }
        }
        
        bucket.add(new Entry<>(key, value));
    }
    
    public V get(K key) {
        int index = getIndex(key);
        LinkedList<Entry<K, V>> bucket = table[index];
        
        for(Entry<K, V> entry : bucket) {
            if(entry.key.equals(key)) {
                return entry.value;
            }
        }
        
        return null;
    }
    
    private int getIndex(K key) {
        if(key == null) return 0;
        return Math.abs(key.hashCode()) % table.length;
    }
    
    static class Entry<K, V> {
        K key;
        V value;
        
        public Entry(K key, V value) {
            this.key = key;
            this.value = value;
        }
    }
    
    public static void main(String[] args) {
        MyHashMap<String, Integer> map = new MyHashMap<>();
        map.put("apple", 1);
        map.put("banana", 2);
        map.put("cherry", 3);
        
        System.out.println("apple: " + map.get("apple"));
        System.out.println("banana: " + map.get("banana"));
        System.out.println("cherry: " + map.get("cherry"));
        System.out.println("date: " + map.get("date"));
    }
}

3. HashMap和Hashtable的区别(多线程测试)

代码语言:java
AI代码解释
复制
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class HashMapVsHashtable {
    public static void main(String[] args) throws InterruptedException {
        int threadCount = 10;
        int loopCount = 1000;
        
        // 测试HashMap在多线程环境下的表现
        Map<Integer, Integer> hashMap = new HashMap<>();
        ExecutorService executor1 = Executors.newFixedThreadPool(threadCount);
        
        long startTime = System.currentTimeMillis();
        
        for(int i = 0; i < threadCount; i++) {
            executor1.execute(() -> {
                for(int j = 0; j < loopCount; j++) {
                    hashMap.put(j, j);
                }
            });
        }
        
        executor1.shutdown();
        executor1.awaitTermination(1, TimeUnit.MINUTES);
        
        long endTime = System.currentTimeMillis();
        System.out.println("HashMap耗时: " + (endTime - startTime) + "ms, 大小: " + hashMap.size());
        
        // 测试Hashtable在多线程环境下的表现
        Map<Integer, Integer> hashtable = new Hashtable<>();
        ExecutorService executor2 = Executors.newFixedThreadPool(threadCount);
        
        startTime = System.currentTimeMillis();
        
        for(int i = 0; i < threadCount; i++) {
            executor2.execute(() -> {
                for(int j = 0; j < loopCount; j++) {
                    hashtable.put(j, j);
                }
            });
        }
        
        executor2.shutdown();
        executor2.awaitTermination(1, TimeUnit.MINUTES);
        
        endTime = System.currentTimeMillis();
        System.out.println("Hashtable耗时: " + (endTime - startTime) + "ms, 大小: " + hashtable.size());
    }
}

4. 集合框架中线程安全的类(使用示例)

代码语言:java
AI代码解释
复制
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ThreadSafeCollections {
    public static void main(String[] args) throws InterruptedException {
        int threadCount = 5;
        int loopCount = 1000;
        
        // Vector示例
        List<Integer> vector = new Vector<>();
        ExecutorService executor1 = Executors.newFixedThreadPool(threadCount);
        
        for(int i = 0; i < threadCount; i++) {
            executor1.execute(() -> {
                for(int j = 0; j < loopCount; j++) {
                    vector.add(j);
                }
            });
        }
        
        executor1.shutdown();
        executor1.awaitTermination(1, TimeUnit.MINUTES);
        System.out.println("Vector大小: " + vector.size());
        
        // Hashtable示例
        Map<Integer, Integer> hashtable = new Hashtable<>();
        ExecutorService executor2 = Executors.newFixedThreadPool(threadCount);
        
        for(int i = 0; i < threadCount; i++) {
            executor2.execute(() -> {
                for(int j = 0; j < loopCount; j++) {
                    hashtable.put(j, j);
                }
            });
        }
        
        executor2.shutdown();
        executor2.awaitTermination(1, TimeUnit.MINUTES);
        System.out.println("Hashtable大小: " + hashtable.size());
        
        // ConcurrentHashMap示例
        Map<Integer, Integer> concurrentHashMap = new ConcurrentHashMap<>();
        ExecutorService executor3 = Executors.newFixedThreadPool(threadCount);
        
        for(int i = 0; i < threadCount; i++) {
            executor3.execute(() -> {
                for(int j = 0; j < loopCount; j++) {
                    concurrentHashMap.put(j, j);
                }
            });
        }
        
        executor3.shutdown();
        executor3.awaitTermination(1, TimeUnit.MINUTES);
        System.out.println("ConcurrentHashMap大小: " + concurrentHashMap.size());
        
        // CopyOnWriteArrayList示例
        List<Integer> copyOnWriteArrayList = new CopyOnWriteArrayList<>();
        ExecutorService executor4 = Executors.newFixedThreadPool(threadCount);
        
        for(int i = 0; i < threadCount; i++) {
            executor4.execute(() -> {
                for(int j = 0; j < loopCount; j++) {
                    copyOnWriteArrayList.add(j);
                }
            });
        }
        
        executor4.shutdown();
        executor4.awaitTermination(1, TimeUnit.MINUTES);
        System.out.println("CopyOnWriteArrayList大小: " + copyOnWriteArrayList.size());
    }
}

Java多线程

1. 创建线程的方式(实操)

代码语言:java
AI代码解释
复制
// 方式一:继承Thread类
class MyThread extends Thread {
    @Override
    public void run() {
        for(int i = 0; i < 5; i++) {
            System.out.println("继承Thread类的线程: " + i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// 方式二:实现Runnable接口
class MyRunnable implements Runnable {
    @Override
    public void run() {
        for(int i = 0; i < 5; i++) {
            System.out.println("实现Runnable接口的线程: " + i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// 方式三:实现Callable接口
import java.util.concurrent.*;

class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for(int i = 0; i <= 100; i++) {
            sum += i;
        }
        return sum;
    }
}

public class CreateThreads {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        // 方式一:继承Thread类
        MyThread thread1 = new MyThread();
        thread1.start();
        
        // 方式二:实现Runnable接口
        MyRunnable runnable = new MyRunnable();
        Thread thread2 = new Thread(runnable);
        thread2.start();
        
        // 方式三:实现Callable接口
        MyCallable callable = new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread thread3 = new Thread(futureTask);
        thread3.start();
        
        // 获取Callable线程的返回值
        Integer result = futureTask.get();
        System.out.println("Callable线程计算结果: " + result);
        
        // 主线程继续执行
        for(int i = 0; i < 5; i++) {
            System.out.println("主线程: " + i);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

2. 线程的生命周期(状态转换)

代码语言:java
AI代码解释
复制
public class ThreadStateExample {
    public static void main(String[] args) throws InterruptedException {
        Thread target = new Thread(() -> {
            System.out.println("线程状态: " + Thread.currentThread().getState());
            
            try {
                // 线程进入TIMED_WAITING状态
                Thread.sleep(2000);
                System.out.println("线程休眠结束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            
            synchronized (ThreadStateExample.class) {
                try {
                    // 线程进入WAITING状态
                    ThreadStateExample.class.wait();
                    System.out.println("线程被唤醒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            
            System.out.println("线程继续执行");
        });
        
        // 输出新建状态
        System.out.println("新建状态: " + target.getState());
        
        // 启动线程,进入RUNNABLE状态
        target.start();
        System.out.println("启动后状态: " + target.getState());
        
        // 主线程休眠3秒,让目标线程有足够时间休眠
        Thread.sleep(3000);
        System.out.println("休眠3秒后状态: " + target.getState());
        
        // 唤醒目标线程
        synchronized (ThreadStateExample.class) {
            ThreadStateExample.class.notify();
        }
        
        // 主线程休眠1秒,让目标线程有时间被唤醒
        Thread.sleep(1000);
        System.out.println("唤醒后状态: " + target.getState());
        
        // 等待目标线程执行完毕
        target.join();
        System.out.println("线程结束后状态: " + target.getState());
    }
}

3. 线程上下文切换(模拟)

代码语言:java
AI代码解释
复制
public class ContextSwitchSimulation {
    private static final int THREAD_COUNT = 10;
    private static final int LOOP_COUNT = 1000000;
    
    public static void main(String[] args) throws InterruptedException {
        // 创建多个线程模拟上下文切换
        Thread[] threads = new Thread[THREAD_COUNT];
        
        for(int i = 0; i < THREAD_COUNT; i++) {
            final int index = i;
            threads[i] = new Thread(() -> {
                for(int j = 0; j < LOOP_COUNT; j++) {
                    // 简单的计算操作
                    Math.sqrt(Math.random() * 100);
                }
                System.out.println("线程 " + index + " 执行完毕");
            });
        }
        
        long startTime = System.currentTimeMillis();
        
        // 启动所有线程
        for(Thread thread : threads) {
            thread.start();
        }
        
        // 等待所有线程执行完毕
        for(Thread thread : threads) {
            thread.join();
        }
        
        long endTime = System.currentTimeMillis();
        System.out.println("所有线程执行完毕,耗时: " + (endTime - startTime) + "ms");
    }
}

4. 死锁示例与避免(实操)

代码语言:java
AI代码解释
复制
public class DeadlockExample {
    private static final Object resource1 = new Object();
    private static final Object resource2 = new Object();
    
    public static void main(String[] args) {
        // 创建线程1
        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("线程1获取了资源1");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
                System.out.println("线程1尝试获取资源2");
                synchronized (resource2) {
                    System.out.println("线程1获取了资源2");
                }
            }
        });
        
        // 创建线程2
        Thread thread2 = new Thread(() -> {
            synchronized (resource2) {
                System.out.println("线程2获取了资源2");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
                System.out.println("线程2尝试获取资源1");
                synchronized (resource1) {
                    System.out.println("线程2获取了资源1");
                }
            }
        });
        
        // 启动线程
        thread1.start();
        thread2.start();
        
        // 主线程等待一段时间后检查线程状态
        try {
            Thread.sleep(2000);
            System.out.println("线程1状态: " + thread1.getState());
            System.out.println("线程2状态: " + thread2.getState());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

// 避免死锁的示例
class DeadlockAvoidance {
    private static final Object resource1 = new Object();
    private static final Object resource2 = new Object();
    
    public static void main(String[] args) {
        // 创建线程1 - 按照相同顺序获取锁
        Thread thread1 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("线程1获取了资源1");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
                System.out.println("线程1尝试获取资源2");
                synchronized (resource2) {
                    System.out.println("线程1获取了资源2");
                }
            }
        });
        
        // 创建线程2 - 按照相同顺序获取锁
        Thread thread2 = new Thread(() -> {
            synchronized (resource1) {
                System.out.println("线程2获取了资源1");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                
                System.out.println("线程2尝试获取资源2");
                synchronized (resource2) {
                    System.out.println("线程2获取了资源2");
                }
            }
        });
        
        // 启动线程
        thread1.start();
        thread2.start();
        
        // 主线程等待线程执行完毕
        try {
            thread1.join();
            thread2.join();
            System.out.println("所有线程正常执行完毕");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

":"load_by_key","id":"","key":"banner_image_0","width":0,"height":0,"image_type":"search","pages_id":"8142750540428034","genre":"技术文章","artifact_key":8141877036422146}]()

Java基础

1. 面向对象和面向过程的区别?

面向过程:注重解决问题的步骤和过程,按步骤顺序执行方法,将数据和操作分离。例如实现一个简单的计算器功能,面向过程会按照输入数字、选择运算符号、进行运算、输出结果这样的步骤来编写代码。其优点是效率高,无需类加载和对象实例化;缺点是程序耦合度高,维护和扩展困难。

面向对象:注重对象,将数据和操作封装在对象中,通过对象之间的交互来解决问题。还是以计算器为例,会抽象出数字对象、运算符号对象、计算器对象等,每个对象有自己的属性和方法,通过调用对象的方法来完成计算。优点是程序易维护、易复用、易扩展;缺点是相比面向过程效率更低,因为需要类加载和对象实例化。

2. equals与==的区别?

==:比较的是变量(栈)内存中存放的对象的(堆)内存地址,用来判断两个对象是否是同一个对象。对于基本数据类型,比较的是值;对于引用数据类型,比较的是内存地址。例如:

代码语言:java
AI代码解释
复制
int a = 10;
int b = 10;
System.out.println(a == b); // 输出true,因为基本数据类型比较值
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2); // 输出false,因为s1和s2是不同的对象,内存地址不同

equals:用来比较两个对象的内容是否相等。所有类都继承自java.lang.Object类,如果没有重写equals方法,调用的是Object类中的equals方法,其内部实现还是使用==来比较内存地址。通常我们会根据业务需求在自定义类中重写equals方法。例如在自定义的User类中,如果我们认为两个User对象只要用户名和密码相同就相等,就可以重写equals方法:

代码语言:java
AI代码解释
复制
public class User {
    private String username;
    private String password;
    // 省略构造函数、getter和setter方法
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(username, user.username) &&
                Objects.equals(password, user.password);
    }
}

3. final有哪些用法?

  • 修饰类:被final修饰的类不可以被继承。例如String类就是被final修饰的,所以不能有子类继承它。
  • 修饰方法:被final修饰的方法不可以被重写。在父类中如果一个方法被final修饰,子类无法对其进行重写。
  • 修饰变量:被final修饰的变量不可以被改变。如果修饰基本数据类型变量,值不能改变;如果修饰引用类型变量,引用不能再指向其他对象,但对象内部的属性可以改变。例如:
代码语言:java
AI代码解释
复制
final int num = 10;
num = 20; // 编译错误,不能修改final修饰的基本数据类型变量
final List<String> list = new ArrayList<>();
list.add("hello"); // 可以,虽然list引用不能改变,但可以操作其内部元素
list = new ArrayList<>(); // 编译错误,不能改变final修饰的引用类型变量的引用

4. static都有哪些用法?

  • 静态变量:用static修饰的成员变量称为静态变量,它属于类,而不是类的某个对象。无论创建多少个类的对象,静态变量在内存中只有一份。例如:
代码语言:java
AI代码解释
复制
public class StaticExample {
    static int staticVar = 10;
    int instanceVar = 20;
    public static void main(String[] args) {
        StaticExample s1 = new StaticExample();
        StaticExample s2 = new StaticExample();
        s1.staticVar = 30;
        System.out.println(s2.staticVar); // 输出30,因为静态变量是共享的
        System.out.println(s1.instanceVar); // 输出20
        System.out.println(s2.instanceVar); // 输出20,实例变量每个对象都有自己的一份
    }
}
  • 静态方法:用static修饰的方法称为静态方法,静态方法可以直接通过类名调用,不需要创建对象。静态方法中不能访问非静态成员变量和非静态方法,因为非静态成员属于对象,而静态方法在类加载时就存在,此时可能还没有对象。例如:
代码语言:java
AI代码解释
复制
public class StaticMethodExample {
    static void staticMethod() {
        System.out.println("这是一个静态方法");
    }
    void instanceMethod() {
        System.out.println("这是一个实例方法");
    }
    public static void main(String[] args) {
        StaticMethodExample.staticMethod(); // 直接通过类名调用静态方法
        StaticMethodExample s = new StaticMethodExample();
        s.instanceMethod(); // 通过对象调用实例方法
    }
}
  • 静态代码块:用static修饰的代码块称为静态代码块,在类加载时执行,且只执行一次。通常用于初始化静态变量等操作。例如:
代码语言:java
AI代码解释
复制
public class StaticBlockExample {
    static int staticVar;
    static {
        staticVar = 100;
        System.out.println("静态代码块执行");
    }
    public static void main(String[] args) {
        System.out.println(StaticBlockExample.staticVar); // 输出100
    }
}

5. try catch finally,try里有return,finally还执行么?

finally块中的代码无论try块中是否发生异常,也无论try块中是否有return语句,都会执行(除了在finally块之前JVM退出的情况)。不过如果try块中有return语句,会先执行return语句中的表达式,将结果暂存,然后执行finally块中的代码,最后再返回之前暂存的结果。例如:

代码语言:java
AI代码解释
复制
public class TryCatchFinallyExample {
    public static int test() {
        try {
            return 1;
        } finally {
            System.out.println("finally块执行");
        }
    }
    public static void main(String[] args) {
        int result = test();
        System.out.println(result); // 输出:finally块执行 1
    }
}

在上述代码中,try块中的return语句先将1暂存,然后执行finally块中的代码输出“finally块执行”,最后返回暂存的1。

Java集合

1. ArrayList和LinkedList的区别?

  • 数据结构:ArrayList底层使用数组存储元素,LinkedList底层使用双向链表存储元素。
  • 随机访问性能:ArrayList支持随机访问,通过索引可以快速定位元素,时间复杂度为O(1)。例如获取ArrayList中第5个元素,直接通过array5就可以获取。LinkedList不支持高效的随机访问,因为需要从链表头或链表尾开始遍历,时间复杂度为O(n)。如果要获取LinkedList中第5个元素,需要从链表头开始,依次遍历5个节点才能找到。
  • 插入和删除性能:在ArrayList的中间位置插入或删除元素时,需要移动大量元素,时间复杂度为O(n)。例如在ArrayList中间位置插入一个元素,后面的元素都需要向后移动一位。而LinkedList在任意位置插入或删除元素,只需要修改相邻节点的指针,时间复杂度为O(1)。例如在LinkedList中间插入一个节点,只需要修改前一个节点的next指针和后一个节点的prev指针。
  • 内存占用:ArrayList因为是数组,连续内存空间,如果元素数量较多,可能会浪费一些内存空间。LinkedList每个节点除了存储数据,还需要存储两个指针,所以内存占用相对较大。

2. HashMap的工作原理?

HashMap基于哈希表实现,使用数组和链表(JDK1.8及之后引入红黑树)来存储数据。其工作原理如下:

  • 存储:当向HashMap中put一个键值对时,首先计算键的哈希值,通过哈希值与数组长度进行取模运算,得到该键值对在数组中的存储位置(桶的索引)。如果该位置没有元素,则直接将键值对存入该位置;如果该位置已经有元素(发生哈希冲突),则以链表(或红黑树)的形式将新元素插入到该位置。例如:
代码语言:java
AI代码解释
复制
HashMap<String, Integer> map = new HashMap<>();
map.put("apple", 1);
map.put("banana", 2);

假设“apple”计算出的哈希值对应的数组索引为3,且该位置为空,就将“apple”和1存入数组索引为3的位置。如果“banana”计算出的哈希值也对应数组索引为3,那么就将“banana”和2以链表形式插入到索引为3位置已有的元素后面。

  • 获取:当通过键获取值时,同样先计算键的哈希值,通过哈希值与数组长度取模得到存储位置,然后在该位置的链表(或红黑树)中查找与该键相等的节点,找到后返回对应的值。例如:
代码语言:java
AI代码解释
复制
Integer value = map.get("apple");
System.out.println(value); // 输出1
  • 扩容:当HashMap中的元素数量达到负载因子(默认0.75)与数组容量的乘积时,会进行扩容。扩容时会创建一个新的更大的数组,将原数组中的元素重新计算哈希值并放入新数组中,这个过程称为rehash。扩容的目的是减少哈希冲突,提高查找效率。

3. HashMap和Hashtable的区别?

  • 继承关系:HashMap继承自AbstractMap类,Hashtable继承自Dictionary类。
  • 线程安全性:HashMap是线程不安全的,在多线程并发环境下可能会出现数据不一致等问题,例如在多线程同时进行put操作时可能会导致链表成环。Hashtable是线程安全的,它的每个方法都使用synchronized关键字修饰,因此可以直接在多线程环境中使用,但由于同步带来的开销,性能相对较低。
  • 对null的支持:HashMap的键可以为null,且只能有一个键为null的键值对,值可以有多个为null。例如:
代码语言:java
AI代码解释
复制
HashMap<String, Integer> hashMap = new HashMap<>();
hashMap.put(null, 1);
hashMap.put("key", null);

Hashtable的键和值都不能为null,如果尝试插入null键或null值会抛出NullPointerException。

  • 初始容量和扩容机制:HashMap的初始容量为16,扩容时新容量为原来的2倍;Hashtable的初始容量为11,扩容时新容量为原来的2倍加1。

4. 集合框架中哪些是线程安全的?

  • Vector:与ArrayList类似,但它是线程安全的,方法都使用synchronized修饰。例如在多线程环境下向Vector中添加元素:
代码语言:java
AI代码解释
复制
Vector<Integer> vector = new Vector<>();
vector.add(1);

多个线程同时调用add方法不会出现数据不一致问题。

  • Hashtable:前面已介绍,是线程安全的哈希表。
  • ConcurrentHashMap:线程安全的哈希表,相比Hashtable,它采用了更细粒度的锁机制(分段锁,JDK1.8之后采用CAS和synchronized结合的机制),性能更高。例如在多线程环境下进行并发的put和get操作:
代码语言:java
AI代码解释
复制
ConcurrentHashMap<String, Integer> concurrentHashMap = new ConcurrentHashMap<>();
concurrentHashMap.put("key1", 1);
Integer value = concurrentHashMap.get("key1");

可以高效地在多线程环境下工作。

  • CopyOnWriteArrayList:写时复制的ArrayList,对其进行读操作时,不需要加锁,因为读操作是基于原数组进行的;写操作时,会先复制一份原数组,在新数组上进行修改,完成后将原数组引用指向新数组,写操作是线程安全的。适用于读多写少的场景。例如:
代码语言:java
AI代码解释
复制
CopyOnWriteArrayList<Integer> list = new CopyOnWriteArrayList<>();
list.add(1);
int size = list.size();

多个线程同时进行读操作不会受写操作影响。

Java多线程

1. 创建线程有哪几种方式?

  • 继承Thread类:定义一个类继承自Thread类,重写run方法,在run方法中编写线程执行的逻辑。然后创建该类的对象,并调用start方法启动线程。例如:
代码语言:java
AI代码解释
复制
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("线程执行中:" + Thread.currentThread().getName());
    }
}
public class ThreadCreationExample {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
    }
}
  • 实现Runnable接口:定义一个类实现Runnable接口,实现run方法。然后创建该类的对象,并将其作为参数传递给Thread类的构造函数,最后调用start方法启动线程。例如:
代码语言:java
AI代码解释
复制
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("线程执行中:" + Thread.currentThread().getName());
    }
}
public class RunnableCreationExample {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start();
    }
}
  • 实现Callable接口:与实现Runnable接口类似,但Callable接口的call方法有返回值且可以抛出异常。需要使用FutureTask类来包装Callable对象,然后将FutureTask对象作为参数传递给Thread类的构造函数来启动线程。例如:
代码语言:java
AI代码解释
复制
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
class MyCallable implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("线程执行中:" + Thread.currentThread().getName());
        return 100;
    }
}
public class CallableCreationExample {
    public static void main(String[] args) throws Exception {
        MyCallable myCallable = new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask<>(myCallable);
        Thread thread = new Thread(futureTask);
        thread.start();
        Integer result = futureTask.get();
        System.out.println("线程返回结果:" + result);
    }
}

2. 线程的生命周期有哪些阶段?

  • 新建(New):当创建一个Thread对象时,线程处于新建状态,此时线程还没有开始执行。例如:
代码语言:java
AI代码解释
复制
Thread thread = new Thread();
  • 就绪(Runnable):调用线程的start方法后,线程进入就绪状态,此时线程已经准备好运行,但还没有获得CPU时间片。例如:
代码语言:java
AI代码解释
复制
thread.start();
  • 运行(Running):当线程获得CPU时间片开始执行run方法中的代码时,线程处于运行状态。
  • 阻塞(Blocked):线程在运行过程中,可能会因为某些原因进入阻塞状态,例如调用了sleep方法、等待I/O操作完成、等待获取锁等。例如:
代码语言:java
AI代码解释
复制
try {
    Thread.sleep(1000);
} catch (InterruptedException e) {
    e.printStackTrace();
}
  • 死亡(Dead):线程的run方法执行完毕或者出现未捕获的异常导致线程终止,线程进入死亡状态,此时线程生命周期结束。

3. 什么是线程上下文切换?

线程上下文切换是指当CPU从一个线程切换到另一个线程执行时,需要保存当前线程的上下文信息(包括寄存器的值、程序计数器的值、栈指针等),并恢复要切换到的线程的上下文信息,以便该线程能够继续正确执行。例如,当一个线程在执行过程中,时间片用完,操作系统会将其上下文信息保存到内存中,然后从就绪队列中选择另一个线程,将该线程的上下文信息从内存中读取出来并恢复到CPU寄存器等硬件中,从而让新的线程开始执行。线程上下文切换会带来一定的开销,频繁的上下文切换会影响系统性能。

4. 什么是死锁?如何避免死锁?

死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,如果没有外力干涉,这些线程将永远无法继续执行。例如,线程A持有资源1,等待获取资源2,而线程B持有资源2,等待获取资源1,此时两个线程就陷入了死锁。

避免死锁的方法:

  • 破坏死锁的四个必要条件:
    • 互斥条件:某些资源在一段时间内只能由一个线程占有,一般无法破坏。
    • 占有并等待条件:可以要求线程一次性获取所有需要的资源,而不是逐步获取,这样就不会出现占有部分资源又等待其他资源的情况。
    • 不可剥夺条件:可以允许线程在获取不到某些资源时,主动释放已经占有的资源。
    • 循环等待条件:可以对资源进行排序,线程按照一定的顺序获取资源,避免形成循环等待。
  • 使用定时锁:在获取锁时设置一个超时时间,如果在规定时间内没有获取到锁,则放弃获取,避免无限等待。例如使用ReentrantLock的tryLock(long timeout, TimeUnit unit)方法。

2025 年,一线互联网大厂,Java



资源地址:

https://pan.quark.cn/s/14fcf913bae6


原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一线互联网大厂最新高质量Java面试八股文整理(附答案及实操)
    • Java基础
      • 1. 面向对象和面向过程的区别(实操)
      • 2. equals与==的区别(实操)
      • 3. final的用法(实操)
      • 4. static的用法(实操)
    • Java集合
      • 1. ArrayList和LinkedList的区别(性能测试)
      • 2. HashMap的工作原理(自定义实现)
      • 3. HashMap和Hashtable的区别(多线程测试)
      • 4. 集合框架中线程安全的类(使用示例)
    • Java多线程
      • 1. 创建线程的方式(实操)
      • 2. 线程的生命周期(状态转换)
      • 3. 线程上下文切换(模拟)
      • 4. 死锁示例与避免(实操)
    • Java基础
      • 1. 面向对象和面向过程的区别?
      • 2. equals与==的区别?
      • 3. final有哪些用法?
      • 4. static都有哪些用法?
      • 5. try catch finally,try里有return,finally还执行么?
    • Java集合
      • 1. ArrayList和LinkedList的区别?
      • 2. HashMap的工作原理?
      • 3. HashMap和Hashtable的区别?
      • 4. 集合框架中哪些是线程安全的?
    • Java多线程
      • 1. 创建线程有哪几种方式?
      • 2. 线程的生命周期有哪些阶段?
      • 3. 什么是线程上下文切换?
      • 4. 什么是死锁?如何避免死锁?
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档