跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

Java 基础面试考点精讲

系统梳理了 Java 基础面试高频考点,包括跨平台原理、八种基本数据类型、面向对象四大特征、包装类型机制、String 类特性、集合框架对比、常见异常处理及多线程创建方式。通过源码分析与代码示例,深入讲解 HashMap、ArrayList、线程池等核心知识点,助力求职者夯实基础。

Kubernet发布于 2026/3/27更新于 2026/9/971 浏览
Java 基础面试考点精讲

1. 简单说下什么是跨平台

术语:操作系统指令集、屏蔽系统之间的差异

由于各种操作系统所支持的指令集不是完全一致,所以在操作系统之上加个虚拟机可以来提供统一接口,屏蔽系统之间的差异。

2. Java 有几种基本数据类型

有八种基本数据类型。

数据类型字节默认值
byte10
short20
int40
long80
float40.0f
double80.0d
char2'\u0000'
boolean4false

各自占用几字节也记一下。

3. 面向对象特征

面向对象的编程语言有封装、继承、抽象、多态等 4 个主要的特征。

  1. 封装:把描述一个对象的属性和行为的代码封装在一个模块中,也就是一个类中,属性用变量定义,行为用方法进行定义,方法可以直接访问同一个对象中的属性。
  2. 抽象:把现实生活中的对象抽象为类。分为过程抽象和数据抽象
    • 数据抽象 --> 鸟有翅膀,羽毛等 (类的属性)
    • 过程抽象 --> 鸟会飞,会叫 (类的方法)
  3. 继承:子类继承父类的特征和行为。子类可以有父类的方法,属性(非 private)。子类也可以对父类进行扩展,也可以重写父类的方法。缺点就是提高代码之间的耦合性。
  4. 多态:多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定 (比如:向上转型,只有运行才能确定其对象属性)。方法覆盖和重载体现了多态性。

4. 为什么要有包装类型

术语:让基本类型也具有对象的特征

基本类型包装器类型
booleanBoolean
charCharacter
intInteger
byteByte
shortShort
longLong
floatFloat
doubleDouble

为了让基本类型也具有对象的特征,就出现了包装类型(如我们在使用集合类型 Collection 时就一定要使用包装类型而非基本类型)因为容器都是装 object 的,这是就需要这些基本类型的包装器类了。

自动装箱:new Integer(6);,底层调用:Integer.valueOf(6)

自动拆箱:int i = new Integer(6);,底层调用 i.intValue(); 方法实现。

Integer i = 6;
Integer j = 6;
System.out.println(i == j);

答案在下面这段代码中找:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
二者的区别:
  1. 声明方式不同:基本类型不使用 new 关键字,而包装类型需要使用 new 关键字来在堆中分配存储空间;
  2. 存储方式及位置不同:基本类型是直接将变量值存储在栈中,而包装类型是将对象放在堆中,然后通过引用来使用;
  3. 初始值不同:基本类型的初始值如 int 为 0,boolean 为 false,而包装类型的初始值为 null;
  4. 使用方式不同:基本类型直接赋值直接使用就好,而包装类型在集合如 Collection、Map 时会使用到。

5. ==和 equals 区别

  • == 较的是两个引用在内存中指向的是不是同一对象(即同一内存空间),也就是说在内存空间中的存储位置是否一致。如果两个对象的引用相同时(指向同一对象时),"=="操作符返回 true,否则返回 false。
  • equals 用来比较某些特征是否一样。我们平时用的 String 类等的 equals 方法都是重写后的,实现比较两个对象的内容是否相等。

我们来看看 String 重写的 equals 方法:

它不止判断了内存地址,还增加了字符串是否相同的比较。

public boolean equals(Object anObject) {
    //判断内存地址是否相同
    if (this == anObject) {
        return true;
    }
    // 判断参数类型是否是 String 类型
    if (anObject instanceof String) {
        // 强转
        String anotherString = (String)anObject;
        int n = value.length;
        // 判断两个字符串长度是否相等
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            // 一一比较 字符是否相同
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

6. String、StringBuffer 和 StringBuilder 区别

java 中 String、StringBuffer、StringBuilder 是编程中经常使用的字符串类,他们之间的区别也是经常在面试中会问到的问题。现在总结一下,看看他们的不同与相同。

1. 数据可变和不可变
  1. String 底层使用一个不可变的字符数组 private final char value[]; 所以它内容不可变。
  2. StringBuffer 和 StringBuilder 都继承了 AbstractStringBuilder 底层使用的是可变字符数组:char[] value;
2. 线程安全
  • StringBuilder 是线程不安全的,效率较高;而 StringBuffer 是线程安全的,效率较低。

通过他们的 append() 方法来看,StringBuffer 是有同步锁,而 StringBuilder 没有:

@Override
public synchronized StringBuffer append(Object obj) {
    toStringCache = null;
    super.append(String.valueOf(obj));
    return this;
}
@Override
public StringBuilder append(String str) {
    super.append(str);
    return this;
}
3. 相同点

StringBuilder 与 StringBuffer 有公共父类 AbstractStringBuilder。

最后,操作可变字符串速度:StringBuilder > StringBuffer > String,这个答案就显得不足为奇了。

7. 讲一下 Java 中的集合

  1. Collection 下:List 系 (有序、元素允许重复) 和 Set 系 (无序、元素不重复)

set 根据 equals 和 hashcode 判断,一个对象要存储在 Set 中,必须重写 equals 和 hashCode 方法

  1. Map 下:HashMap 线程不同步;TreeMap 线程不安全
  2. Collection 系列和 Map 系列:Map 是对 Collection 的补充,两个没什么关系

8. ArrayList 和 LinkedList 区别?

之前专门有写过 ArrayList 和 LinkedList 源码的文章。

  1. ArrayList 是实现了基于动态数组的数据结构,LinkedList 基于链表的数据结构。
  2. 对于随机访问 get 和 set,ArrayList 觉得优于 LinkedList,因为 LinkedList 要移动指针。
  3. 对于新增和删除操作 add 和 remove,LinedList 比较占优势,因为 ArrayList 要移动数据。

9. ConcurrentModificationException 异常出现的原因

public class Test {
    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(2);
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer==2)
                list.remove(integer);
        }
    }
}

执行上段代码是有问题的,会抛出 ConcurrentModificationException 异常。

原因:调用 list.remove() 方法导致 modCount 和 expectedModCount 的值不一致。

final void checkForComodification() {
    if (modCount != expectedModCount)
    throw new ConcurrentModificationException();
}

解决办法:在迭代器中如果要删除元素的话,需要调用 Iterator 类的 remove 方法。

public class Test {
    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>();
        list.add(2);
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer==2)
                iterator.remove();   //注意这个地方
        }
    }
}

10. HashMap 和 HashTable、ConcurrentHashMap 区别?

相同点:

  1. HashMap 和 HashTable 都实现了 Map 接口
  2. 都可以存储 key-value 数据

不同点:

  1. HashMap 可以把 null 作为 key 或 value,HashTable 不可以
  2. HashMap 线程不安全,效率高。HashTable 线程安全,效率低。
  3. HashMap 的迭代器 (Iterator) 是 fail-fast 迭代器,而 Hashtable 的 enumerator 迭代器不是 fail-fast 的。

什么是 fail-fast? 就是最快的时间能把错误抛出而不是让程序执行。

10.2 如何保证线程安全又效率高?

Java 5 提供了 ConcurrentHashMap,它是 HashTable 的替代,比 HashTable 的扩展性更好。

ConcurrentHashMap 将整个 Map 分为 N 个 segment(类似 HashTable),可以提供相同的线程安全,但是效率提升 N 倍,默认 N 为 16。

10.3 我们能否让 HashMap 同步?

HashMap 可以通过下面的语句进行同步: Map m = Collections.synchronizeMap(hashMap);

11. 拷贝文件的工具类使用字节流还是字符流

答案:字节流

11.1 什么是字节流,什么是字符流?

字节流:传递的是字节(二进制),

字符流:传递的是字符

11.2 答案

我们并不支持下载的文件有没有包含字节流 (图片、影像、音源),所以考虑到通用性,我们会用字节流。

12. 线程创建方式

这个之前自己做过总结,也算比较全面。

方法一:继承 Thread 类,作为线程对象存在(继承 Thread 对象)
public class CreatThreadDemo1 extends Thread{
    /**
     * 构造方法:继承父类方法的 Thread(String name);方法
     * @param name
     */
    public CreatThreadDemo1(String name){
        super(name);
    }
    @Override
    public void run() {
        while (!interrupted()){
            System.out.println(getName()+"线程执行了...");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args) {
        CreatThreadDemo1 d1 = new CreatThreadDemo1("first");
        CreatThreadDemo1 d2 = new CreatThreadDemo1("second");
        d1.start();
        d2.start();
        d1.interrupt();  //中断第一个线程
    }
}

常规方法,不多做介绍了,interrupted 方法,是来判断该线程是否被中断。(终止线程不允许用 stop 方法,该方法不会施放占用的资源。所以我们在设计程序的时候,要按照中断线程的思维去设计,就像上面的代码一样)。

让线程等待的方法
  • Thread.sleep(200); //线程休息 2ms
  • Object.wait(); //让线程进入等待,直到调用 Object 的 notify 或者 notifyAll 时,线程停止休眠
方法二:实现 runnable 接口,作为线程任务存在
public class CreatThreadDemo2 implements Runnable {
    @Override
    public void run() {
        while (true){
            System.out.println("线程执行了...");
        }
    }
    public static void main(String[] args) {
        //将线程任务传给线程对象
        Thread thread = new Thread(new CreatThreadDemo2());
        //启动线程
        thread.start();
    }
}

Runnable 只是来修饰线程所执行的任务,它不是一个线程对象。想要启动 Runnable 对象,必须将它放到一个线程对象里。

方法三:匿名内部类创建线程对象
public class CreatThreadDemo3 extends Thread{
    public static void main(String[] args) {
        //创建无参线程对象
        new Thread(){
            @Override
            public void run() {
                System.out.println("线程执行了...");
            }
        }.start();
       //创建带线程任务的线程对象
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程执行了...");
            }
        }).start();
        //创建带线程任务并且重写 run 方法的线程对象
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("runnable run 线程执行了...");
            }
        }){
            @Override
            public void run() {
                System.out.println("override run 线程执行了...");
            }
        }.start();
    }
}

创建带线程任务并且重写 run 方法的线程对象中,为什么只运行了 Thread 的 run 方法。我们看看 Thread 类的源码,可以看到 Thread 实现了 Runnable 接口,而 Runnable 接口里有一个 run 方法。所以,我们最终调用的重写的方法应该是 Thread 类的 run 方法。而不是 Runnable 接口的 run 方法。

方法四:创建带返回值的线程
public class CreatThreadDemo4 implements Callable {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CreatThreadDemo4 demo4 = new CreatThreadDemo4();
        FutureTask<Integer> task = new FutureTask<Integer>(demo4); //FutureTask 最终实现的是 runnable 接口
        Thread thread = new Thread(task);
        thread.start();
        System.out.println("我可以在这里做点别的业务逻辑...因为 FutureTask 是提前完成任务");
        //拿出线程执行的返回值
        Integer result = task.get();
        System.out.println("线程中运算的结果为:"+result);
    }
    //重写 Callable 接口的 call 方法
    @Override
    public Object call() throws Exception {
        int result = 1;
        System.out.println("业务逻辑计算中...");
        Thread.sleep(3000);
        return result;
    }
}

Callable 接口介绍:

public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

返回指定泛型的 call 方法。然后调用 FutureTask 对象的 get 方法得道 call 方法的返回值。

方法五:定时器 Timer
public class CreatThreadDemo5 {
    public static void main(String[] args) {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("定时器线程执行了...");
            }
        },0,1000);   //延迟 0,周期 1s
    }
}
方法六:线程池创建线程
public class CreatThreadDemo6 {
    public static void main(String[] args) {
        //创建一个具有 10 个线程的线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(10);
        long threadpoolUseTime = System.currentTimeMillis();
        for (int i=0;i<10;i++){
            threadPool.execute(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Thread.currentThread().getName()+"线程执行了...");
                }
            });
        }
        long threadpoolUseTime1 = System.currentTimeMillis();
        System.out.println("多线程用时"+(threadpoolUseTime1-threadpoolUseTime));
        //销毁线程池
        threadPool.shutdown();
        threadpoolUseTime = System.currentTimeMillis();
    }
}
方法七:利用 java8 新特性 stream 实现并发

lambda 表达式不懂的,可以看看我的 java8 新特性文章。

public class CreatThreadDemo7 {
    public static void main(String[] args) {
        List<Integer> values = Arrays.asList(10,20,30,40);
        //parallel 平行的,并行的
        int result = values.parallelStream().mapToInt(p -> p*2).sum();
        System.out.println(result);
        //怎么证明它是并发处理呢
        values.parallelStream().forEach(p-> System.out.println(p));
    }
}

输出:

200 40 10 20 30

怎么证明它是并发处理呢,他们并不是按照顺序输出的。

目录

  1. 1. 简单说下什么是跨平台
  2. 2. Java 有几种基本数据类型
  3. 3. 面向对象特征
  4. 4. 为什么要有包装类型
  5. 二者的区别:
  6. 5. ==和 equals 区别
  7. 6. String、StringBuffer 和 StringBuilder 区别
  8. 1. 数据可变和不可变
  9. 2. 线程安全
  10. 3. 相同点
  11. 7. 讲一下 Java 中的集合
  12. 8. ArrayList 和 LinkedList 区别?
  13. 9. ConcurrentModificationException 异常出现的原因
  14. 10. HashMap 和 HashTable、ConcurrentHashMap 区别?
  15. 10.2 如何保证线程安全又效率高?
  16. 10.3 我们能否让 HashMap 同步?
  17. 11. 拷贝文件的工具类使用字节流还是字符流
  18. 11.1 什么是字节流,什么是字符流?
  19. 11.2 答案
  20. 12. 线程创建方式
  21. 方法一:继承 Thread 类,作为线程对象存在(继承 Thread 对象)
  22. 让线程等待的方法
  23. 方法二:实现 runnable 接口,作为线程任务存在
  24. 方法三:匿名内部类创建线程对象
  25. 方法四:创建带返回值的线程
  26. 方法五:定时器 Timer
  27. 方法六:线程池创建线程
  28. 方法七:利用 java8 新特性 stream 实现并发

更多推荐文章

查看全部
  • SpringBoot 整合 Neo4j 图数据库项目实战
  • Java ResourceBundle 与 .NET RESX 国际化方案对比
  • Python 数据分析与可视化:金融量化基础与双均线策略
  • 基于 DeepFace 与 OpenCV 的实时情绪分析系统
  • OpenClaw QQ 机器人接入指南
  • TapNow 影视级 AI 视频平台实测:物理一致性与导演级控制
  • OpenClaw 接入飞书机器人并集成 Ollama 本地大模型实战
  • AnythingLLM 集成 Whisper 实战:构建高效语音转文本方案
  • OnlyOffice 社区版 Docker 部署及文档无法保存问题排查
  • OpenClaw WebUI 启动后显示 Not Found 问题修复
  • 国产主流 AI 大模型工具功能对比与使用场景指南
  • Java 体育课程拼班系统架构与功能设计
  • AI 产品经理薪资涨幅显著,行业趋势与核心能力解析
  • 开源本地 AI 助手 OpenClaw 安装与配置指南
  • C++ set 与 map 底层实现及高频算法实战
  • C++ 核心知识点解析
  • MySQL 表基础语法与核心操作技巧
  • Python 网络爬虫基础与实战详解
  • 技术博客搭建指南:如何选择发布平台
  • 树莓派 4B 连接大疆 M300 RTK 无人机开发指南

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online