2025 年 Java 校招 120 道面试题目合集及详细答案解析

Java校招120道面试题目合集及答案

一、Java基础

1. Java语言有哪些特点?

答案

  • 简单性:Java语法简洁,去除了C++中一些复杂和容易出错的特性,如指针、多重继承等。例如定义一个简单的类:
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

  • 面向对象:支持封装、继承和多态等面向对象特性。以封装为例,将数据和操作数据的方法封装在类中,提高了代码的安全性和可维护性。

class Person {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

  • 平台无关性:通过Java虚拟机(JVM)实现“一次编写,到处运行”。Java源文件(.java)被编译器编译成字节码文件(.class),不同平台的JVM可以将相同的字节码文件解释成该平台所需的机器码。
  • 可靠性:具有自动垃圾回收机制,减少了程序员手动管理内存的负担,降低了内存泄漏和悬空指针等错误的发生概率。例如,当一个对象不再被引用时,垃圾回收器会自动回收其占用的内存。
  • 安全性:Java提供了安全管理器,严格限制程序对系统资源的访问。在网络应用中,能有效防止恶意代码的攻击。

2. 什么是Java虚拟机(JVM)?它的作用是什么?

答案:Java虚拟机(Java Virtual Machine)是执行字节码文件的虚拟机进程。它的主要作用是:

  • 执行字节码:将Java编译器生成的字节码文件(.class)解释或编译成机器码,以便在不同的硬件和操作系统平台上运行。例如,在Windows系统上,JVM将字节码解释成适用于x86架构的机器码;在Linux系统上,JVM将字节码解释成适用于相应Linux内核和硬件架构的机器码。
  • 提供运行时环境:为Java程序提供内存管理、线程管理、安全检查等运行时支持。比如,JVM负责分配和回收堆内存,管理线程的生命周期和调度。
  • 实现平台无关性:不同的平台装有不同的JVM,它们能够将相同的.class文件解释成不同平台所需的机器码,使得Java程序能够在各种平台上运行,实现“一次编写,到处运行”。

3. “static”关键字是什么意思?

答案:“static”关键字在Java中有多种用途:

  • 修饰成员变量:被static修饰的成员变量属于类,而不是类的某个实例。该变量在内存中只有一份,被所有实例共享。例如:

class Student {
    private static int studentCount = 0;
    private String name;

    public Student(String name) {
        this.name = name;
        studentCount++;
    }

    public static int getStudentCount() {
        return studentCount;
    }
}

在上述代码中,studentCount是一个静态成员变量,记录学生的数量。每创建一个Student对象,studentCount就会自增1。可以通过Student.getStudentCount()来获取学生总数。

  • 修饰成员方法:静态方法同样属于类,可通过类名直接调用,无需创建类的实例。静态方法只能访问静态成员变量和其他静态方法,不能访问非静态成员。例如:
class MathUtils {
    public static int add(int a, int b) {
        return a + b;
    }
}

调用时可使用MathUtils.add(3, 5)

  • 静态代码块:使用static关键字定义的代码块,在类加载时执行,且只执行一次。常用于初始化静态成员变量或执行一些一次性的操作。例如:
class Database {
    private static Connection connection;

    static {
        try {
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "username", "password");
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    public static Connection getConnection() {
        return connection;
    }
}

上述代码中,静态代码块在Database类加载时尝试建立数据库连接,后续可通过Database.getConnection()获取连接。

4. Java支持的数据类型有哪些?

答案:Java支持两种数据类型:

  • 基本数据类型:共8种,包括byte(1字节)、short(2字节)、int(4字节)、long(8字节)、float(4字节)、double(8字节)、boolean(1位,具体实现可能不同)、char(2字节,用于表示Unicode字符)。例如:


byte b = 10;
short s = 100;
int i = 1000;
long l = 10000000000L;
float f = 3.14f;
double d = 3.1415926;
boolean flag = true;
char c = 'A';
  • 引用数据类型:包括类、接口、数组等。引用数据类型的变量存储的是对象在堆内存中的地址。例如:
String str = "Hello";
int[] array = {1, 2, 3};

在上述代码中,strString类的引用,arrayint类型数组的引用。

5. Java中的方法覆盖(Overriding)和方法重载(Overload)是什么意思?

答案

  • 方法覆盖(Overriding)
    • 发生在子类与父类之间,当子类继承父类后,子类可以重新定义父类中已有的方法。
    • 方法覆盖要求子类方法与父类被覆盖方法具有相同的方法名、参数列表和返回类型(返回类型可以是父类被覆盖方法返回类型的子类,这是Java 5.0引入的协变返回类型特性)。
    • 子类方法不能比父类被覆盖方法有更严格的访问权限(例如,父类方法是public,子类覆盖方法不能是protectedprivate)。例如:

class Animal {
    public void makeSound() {
        System.out.println("Animal makes a sound");
    }
}

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

在上述代码中,Dog类覆盖了Animal类的makeSound方法。

  • 方法重载(Overload)
    • 发生在同一个类中,多个方法可以具有相同的方法名,但参数列表必须不同(参数个数、参数类型或参数顺序不同)。
    • 方法重载与返回类型无关,即返回类型可以相同也可以不同。例如:
class Calculator {
    public int add(int a, int b) {
        return a + b;
    }

    public double add(double a, double b) {
        return a + b;
    }

    public int add(int a, int b, int c) {
        return a + b + c;
    }
}

在上述Calculator类中,定义了三个add方法,通过不同的参数列表实现了方法重载。

6. Java中,什么是构造方法?

答案:构造方法是一种特殊的方法,用于创建对象并初始化对象的成员变量。其特点如下:

  • 方法名与类名相同:例如,对于类Person,其构造方法为Person
class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}
  • 没有返回类型:包括void也不能有。
  • 在创建对象时自动调用:通过new关键字创建对象时,会调用相应的构造方法。例如:
Person person = new Person("John", 25);

在上述代码中,创建Person对象时,会调用参数为Stringint的构造方法,将name初始化为"John"age初始化为25

  • 可以有多个构造方法:通过方法重载实现,以满足不同的初始化需求。例如:
class Person {
    private String name;
    private int age;

    public Person() {
        // 无参构造方法,可进行默认初始化
        name = "Unknown";
        age = 0;
    }

    public Person(String name) {
        this.name = name;
        age = 0;
    }

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

在上述Person类中,定义了三个构造方法,分别用于不同的初始化场景。

7. Java支持多继承么?

答案:Java类不支持多继承,即一个类不能同时继承多个父类。这是为了避免多继承带来的菱形继承问题(当一个类从多个父类继承相同的属性或方法时,可能会导致冲突和不确定性)。例如:

// 以下代码会报错,因为Java类不支持多继承
class A {}
class B {}
class C extends A, B {} 

然而,Java通过接口(interface)实现了类似多继承的功能。一个类可以实现多个接口,从而获取多个接口的行为和规范。例如:


interface Flyable {
    void fly();
}

interface Swimmable {
    void swim();
}

class Duck implements Flyable, Swimmable {
    @Override
    public void fly() {
        System.out.println("Duck is flying");
    }

    @Override
    public void swim() {
        System.out.println("Duck is swimming");
    }
}

在上述代码中,Duck类实现了FlyableSwimmable两个接口,具备了飞行和游泳的行为。

8. 什么是值传递和引用传递?

答案

  • 值传递:在方法调用时,实际参数将其值传递给对应的形式参数,方法对形式参数的修改不会影响实际参数的值。例如:
public class ValueTransfer {
    public static void main(String[] args) {
        int num = 10;
        System.out.println("Before method call: num = " + num);
        changeValue(num);
        System.out.println("After method call: num = " + num);
    }

    public static void changeValue(int n) {
        n = 20;
    }
}

在上述代码中,main方法中的num值为10,调用changeValue方法时,将num的值10传递给n,在changeValue方法中修改n的值为20,但这并不会影响main方法中num的值,最终输出Before method call: num = 10After method call: num = 10

  • 引用传递:在方法调用时,实际参数的引用(对象在堆内存中的地址)被传递给形式参数。此时方法中对形式参数所指向对象的修改,会影响到实际参数所指向的对象。例如:

class Person {
    String name;

    public Person(String name) {
        this.name = name;
    }
}

public class ReferenceTransfer {
    public static void main(String[] args) {
        Person person = new Person("John");
        System.out.println("Before method call: person.name = " + person.name);
        changeName(person);
        System.out.println("After method call: person.name = " + person.name);
    }

    public static void changeName(Person p) {
        p.name = "Jane";
    }
}

在上述代码中,main方法创建了一个Person对象,name"John",调用changeName方法时,将person的引用传递给p,在changeName方法中修改p所指向对象的name"Jane",由于pperson指向同一个对象,所以main方法中personname也被修改为"Jane",最终输出Before method call: person.name = JohnAfter method call: person.name = Jane。需要注意的是,在Java中,对象的传递本质上是引用传递,但基本数据类型的传递是值传递。

9. 进程和线程的区别是什么?

答案

  • 定义
    • 进程:是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位。每个进程都有自己独立的内存空间、系统资源(如文件描述符、内存空间等)。例如,当我们运行一个Java程序时,操作系统会为其创建一个进程,该进程拥有独立的堆内存、栈内存等资源。
    • 线程:是进程中的一个执行单元,是程序执行的最小单位。一个进程可以包含多个线程,这些线程共享进程的资源,如内存空间、文件描述符等。例如,在一个Java Web服务器进程中,可以有多个线程同时处理不同客户端的请求,这些线程共享服务器进程的内存和其他资源。
  • 资源分配
    • 进程:拥有独立的资源,进程之间的资源相互隔离。不同进程之间要进行通信和数据共享,需要使用进程间通信(IPC)机制,如管道、消息队列、共享内存等。
    • 线程:共享所属进程的资源,同一进程内的线程之间通信和数据共享更为方便,通过共享内存即可实现。但也正因如此,多线程编程需要注意线程安全问题,避免多个线程同时访问和修改共享资源导致数据不一致。
  • 调度
    • 进程:进程的调度由操作系统负责,进程上下文切换的开销较大,因为需要切换内存空间、寄存器等资源。
    • 线程:线程的调度也由操作系统负责,但线程上下文切换的开销相对较小,因为线程共享进程的资源,不需要切换内存空间,只需切换少量的寄存器等资源。
  • 并发性能
    • 进程:进程之间的并发是真正的并发,因为每个进程都有自己独立的执行流。但由于进程上下文切换开销大,创建和销毁进程的成本也较高,所以在高并发场景下,使用进程来实现并发可能会导致性能问题。
    • 线程:线程之间也能实现并发,而且由于其轻量级的特性,更适合用于高并发场景。通过多线程编程,可以充分利用CPU的多核特性,提高程序的执行效率。但多线程编程也面临着线程安全、死锁等问题,需要谨慎处理。

10. 创建线程有几种不同的方式?

答案:有4种方式可以用来创建线程:

  • 继承Thread类
    • 定义一个类继承自Thread类。
    • 重写run方法,在run方法中编写线程执行的代码。
    • 创建该类的实例,并调用start方法启动线程。例如:

class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
        }
    }
}

public class ThreadCreationExample {
    public static void main(String[] args) {
        MyThread myThread = new MyThread();
        myThread.start();
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
        }
    }
}

在上述代码中,MyThread类继承自Thread类,重写了run方法。在main方法中创建MyThread实例并调用start方法启动线程,同时main方法本身也是一个线程,两个线程并发执行。

  • 实现Runnable接口
    • 定义一个类实现Runnable接口。
    • 实现run方法,编写线程执行的代码。
    • 创建该类的实例,并将其作为参数传递给Thread类的构造函数,然后调用Thread实例的start方法启动线程。例如:

class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println
 ```...


---

你可能感兴趣的:(java开发,java,面试,python)