【程序员快速复习系列】JAVA语言基础(1)

0. 前言:

程序员面试本是一件再平常不过的事情,记得刚毕业的时候面试题背的滚瓜烂熟。但是在职程序员面试却是另一回事了,我们往往没有太多时间复习,特别是大龄程序员,工作日忙于工作,周末还要照顾家庭,一旦面临被优化的风险就很被动,难以在短时间内复习并找到工作。不要问我是怎么知道的,都是切身体会,在复习的过程中我也走了不少弯路,所幸最终结果令自己满意。 为了不让和我一样的程序员遇到同样的问题,我打算写这一系列的文章,这些文章不会像其他面经一般大而全,这些文章仅记录我在复习过程中认为重要的知识点,如果能帮助到你就太好了。

1. JRE和JDK有什么区别

一句话回答: JRE是Java程序的运行环境,而JDK是Java开发的工具包,包含了JRE和一系列开发工具。选择使用JRE还是JDK,取决于你是需要运行Java应用程序还是开发Java应用程序。

细节解释: JRE(Java Runtime Environment)

  • 定义:Java运行时环境,是运行Java程序所必需的软件。
  • 组成:包含Java虚拟机(JVM)和Java核心类库。
  • 用途:使Java程序能够在计算机上运行,无需编译Java源代码。 JDK(Java Development Kit)
  • 定义:Java开发工具包,是编写Java程序所需的完整开发环境。
  • 组成:包含JRE的所有组件,加上Java编译器(javac)、Java文档生成器(javadoc)等开发工具。
  • 用途:为Java开发者提供编写、编译、测试和调试Java程序所需的工具。 二者区别
  • 功能:JRE仅用于运行Java程序,而JDK提供编写和编译Java程序所需的工具。
  • 受众:JRE面向最终用户,JDK面向开发者。
  • 组件:JDK包含JRE的所有组件,并额外提供开发工具。

2. Java语言是编译型语言还是解释型语言

一句话回答: Java是一种编译型与解释型相结合的语言,通过编译成字节码再由JVM解释执行,实现跨平台运行。

细节解释: Java是一种编译型和解释型语言的结合体。具体来说:

  • 编译型:Java源代码(.java文件)首先被编译成字节码(.class文件)。这个过程是编译型的,因为它将源代码转换成了一种中间形式,但不是机器码。
  • 解释型:Java字节码随后由Java虚拟机(JVM)在运行时解释执行。这个过程是解释型的,因为JVM逐行读取和执行字节码。 这种设计使得Java能够实现跨平台的特性,即"一次编写,到处运行"。字节码可以在任何安装了JVM的平台上运行,而不需要针对特定操作系统重新编译。以下是Java编译和运行的简要流程:
  1. 编写:开发者使用文本编辑器或集成开发环境(IDE)编写Java源代码。
  2. 编译:使用Java编译器(javac)将源代码编译成字节码。
  3. 运行:使用Java运行时环境(JRE)中的JVM来执行字节码。 Java的这种混合特性,既利用了编译型语言的效率,又保持了解释型语言的灵活性和跨平台能力。

3. 移位操作

一句话回答: 移位操作是Java中用于快速进行位运算的运算符,包括逻辑移位、算术移位和无符号移位。

细节解释: 在Java中,移位操作允许开发者对整数类型的数据进行位级别的操作,这在某些算法优化和底层编程中非常有用。以下是Java支持的三种移位操作:

  1. 逻辑移位(无符号移位) :使用<<运算符,将数字的所有位向左移动指定的位数,右边空出的位用0填充。这种移位操作不会改变数字的符号位。 int result = 1 << 3; // 结果为8,相当于1乘以2的3次方
  2. 算术移位 :使用>>运算符,将数字的所有位向右移动指定的位数,左边空出的位用符号位填充。这种移位操作保留了数字的符号。 int result = -1 >> 2; // 结果为-1,因为符号位被复制填充
  3. 无符号移位 :使用>>>运算符,将数字的所有位向右移动指定的位数,左边空出的位用0填充。这种移位操作不考虑数字的符号,适用于无符号整数类型的操作。 int result = -1 >>> 2; // 结果为1073741823,符号位被0填充 移位操作在进行位运算时比乘除操作更快,因此在需要高效处理大数或位模式时非常有用,在面试中也常被问起。

4. 装箱类型

一句话回答: 装箱类型是Java中自动将基本数据类型转换为对应的包装类对象的过程。

细节解释: 在Java中,基本数据类型(如intdouble等)与对象是不同的概念。基本类型直接存储原始数值,而对象则存储在堆上。装箱类型(或称为自动装箱)允许开发者在需要对象的地方使用基本类型,Java编译器会自动将基本类型转换为对应的包装类对象。 以下是Java的基本数据类型及其对应的装箱类型:

  • int -> Integer
  • double -> Double
  • float -> Float
  • char -> Character
  • long -> Long
  • short -> Short
  • byte -> Byte
  • boolean -> Boolean 例如,当您需要将int类型赋值给Integer类型的变量时,可以使用自动装箱: Integer num = 5; // 自动装箱,int转换为Integer对象 相对的,拆箱(或称为自动拆箱)是将包装类对象转换回基本数据类型的过程: int num = num.intValue(); // 自动拆箱,Integer对象转换为int 装箱和拆箱在集合操作、方法参数、泛型等场景中非常常见。然而,过度使用装箱类型可能导致性能问题,因为每次装箱都会创建一个新的对象。在面试中,了解装箱类型及其潜在的性能影响可以展示您对Java语言特性的深入理解。

5. 重载和重写的区别

一句话回答: 重载(Overloading)是在同一类中定义多个同名方法,但参数列表不同;重写(Overriding)是子类中定义一个与父类同名且参数列表相同的方法,以提供特定的实现。

细节解释:

重载(Overloading)

  • 定义:方法重载是指在同一个类中可以创建多个方法,它们具有相同的方法名,但参数的数量或类型不同。
  • 目的:允许开发者定义多个行为相似但参数不同的方法,增强代码的可读性和灵活性。
  • 实现:编译器通过参数的数量和类型来区分不同的重载方法。
java 复制代码
public class Example {
    void display(int a) {
        System.out.println("Display with int: " + a);
    }
    
    void display(double a) {
        System.out.println("Display with double: " + a);
    }
    
    void display(int a, int b) {
        System.out.println("Display with two ints: " + a + " and " + b);
    }
}

重写(Overriding)

  • 定义:方法重写是指子类提供一个已在父类中定义的方法的具体实现,或提供一个不同的实现。
  • 目的:允许子类根据需要改变父类方法的行为,实现多态。
  • 实现:子类方法必须具有与父类方法相同的方法名、参数列表和返回类型(对于非泛型类型)。
java 复制代码
public class Example {
    void display(int a) {
        System.out.println("Display with int: " + a);
    }
    
    void display(double a) {
        System.out.println("Display with double: " + a);
    }
    
    void display(int a, int b) {
        System.out.println("Display with two ints: " + a + " and " + b);
    }
}

区别:

  • 作用域:重载在同一类中,重写在父子类中。
  • 参数列表:重载要求参数列表不同,重写要求参数列表相同。
  • 返回类型:重载可以改变返回类型,重写必须保持返回类型一致或在其子类型中(协变返回)。
  • 访问级别:重写的方法不能有更严格的访问级别限制,但可以放宽。 在面试中,理解重载和重写的区别以及它们的使用场景,可以展示您对面向对象编程和Java语言特性的掌握。

6. 构造方法

一句话回答: 构造方法是Java中用于初始化新对象的特殊方法,它的名字必须与类名相同,且没有返回类型。

细节解释:

构造方法(Constructor)

  • 定义:构造方法是一种特殊的方法,用于创建对象时初始化对象的状态。
  • 特点 :与类名相同,无返回类型,不能被声明为staticfinalabstractsynchronized
  • 作用:为对象的属性设置初始值,确保对象在使用前处于有效状态。
java 复制代码
public class Example {
    int number;
    String name;

    // 构造方法
    public Example(int number, String name) {
        this.number = number;
        this.name = name;
    }
}

构造方法的坑(常见问题):

  1. 参数名与成员变量名冲突 :如果构造方法的参数名与成员变量名相同,需要使用this关键字来区分。
  2. 无参构造方法:如果类中没有定义任何构造方法,编译器会自动提供一个无参的默认构造方法。如果类显式定义了构造方法,这个默认构造方法将不再提供。
  3. 继承中的构造方法 :子类无法继承父类的构造方法,但可以使用super关键字调用父类的特定构造方法。
  4. 构造方法的重载:可以为类定义多个重载的构造方法,以提供不同的初始化方式。
  5. 构造方法中的异常:构造方法可以声明抛出异常,但通常不推荐这样做,因为它可能会使对象的创建变得复杂。
  6. 构造方法的调用限制 :构造方法不能直接调用其他构造方法,但可以使用this()来调用当前类的其他构造方法。

7. 面向对象的三大特征

一句话回答:面向对象编程的三大特征是封装、继承和多态。

细节解释:

封装(Encapsulation)

  • 定义:封装是将对象的实现细节隐藏起来,只暴露出一个可以被外界访问和操作的接口。
  • 目的:减少系统的复杂性,提高安全性和易于维护性。
  • 实现:通常通过访问修饰符(如private、protected、public)来控制对类成员的访问权限。

继承(Inheritance)

  • 定义:继承是一种机制,允许一个类(子类)获得另一个类(父类或超类)的属性和方法。
  • 目的:实现代码复用,建立类之间的层次结构。
  • 实现 :通过关键字extends来实现类的继承关系。

多态(Polymorphism)

  • 定义:多态是指允许不同类的对象对同一消息做出响应,但具体的行为会根据对象的实际类型而有所不同。
  • 目的:提供接口的统一性,允许编写更通用的代码。
  • 实现:通过方法重写(Override)和方法重载(Overload)以及接口和抽象类来实现。

8. private属性 +setter/getter和public属性有什么区别

一句话回答: 使用private访问修饰符并配备setter和getter与直接定义为public的主要区别在于封装性,在setter/getter方法内可以按照既定逻辑控制用户代码对属性的设置和访问。 细节解释:

使用private并配备setter和getter:

  • 封装性:通过将属性设置为private,可以隐藏其实现细节,防止外部直接访问和修改属性,从而保护对象的状态。
  • 控制访问:通过提供公共的setter和getter方法,可以控制对属性的访问和赋值,可以在setter中加入逻辑来验证赋值是否有效。
  • 灵活性:如果需要改变属性的访问逻辑或添加额外的处理,只需修改setter和getter方法,而不需要修改使用这些属性的代码。
java 复制代码
public class Example {
    private int number; // 私有属性

    public int getNumber() { // 提供getter方法
        return number;
    }

    public void setNumber(int number) { // 提供setter方法
        if (number > 0) { // 可以在这里加入验证逻辑
            this.number = number;
        }
    }
}

直接定义为public:

  • 直接访问:如果属性被定义为public,那么它可以被任何其他类直接访问和修改,没有访问控制。
  • 缺乏封装:直接暴露属性可能会使类的使用者意外地改变对象的状态,违反封装原则。
  • 难以维护:如果属性的访问方式需要改变,可能需要修改所有直接访问这些属性的代码。
java 复制代码
public class Example {
    public int number; // 公共属性,直接访问

    // 没有setter和getter方法
}

9. hashCode和equals方法的区别

一句话回答: hashCodeequals是Java中用于比较对象和散列存储的关键方法,其中equals用于比较对象的逻辑等价性,而hashCode用于确定对象在哈希表中的存储位置。

细节解释:

equals方法:

  • 用途:用于比较两个对象的等价性,即检查两个对象是否相等。
  • 默认实现 :在Object类中,equals方法比较对象的内存地址,即比较它们是否是同一个实例。
  • 重写 :通常需要根据对象的属性来重写equals方法,以实现逻辑上的等同性比较。
java 复制代码
public class Example {
    private int id;

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Example example = (Example) obj;
        return id == example.id; // 基于id比较对象是否相等
    }
}

hashCode方法:

  • 用途:返回对象的哈希码,即一个整数值,用于在哈希表(如HashMap)中确定对象的存储位置。
  • 一致性 :在对象的生命周期内,多次调用hashCode应返回相同的值。
  • 散列冲突:不同的对象可能返回相同的哈希码,但相等的对象必须有相同的哈希码。
  • 重写 :当重写了equals方法时,通常也需要重写hashCode方法,以保持equalshashCode的一致性。
java 复制代码
@Override
public int hashCode() {
    return Objects.hash(id); // 基于id生成哈希码
}

区别和联系:

  • 逻辑等同性 vs. 哈希表存储equals用于逻辑上的比较,而hashCode用于哈希表的存储和检索。
  • 内存地址 vs. 属性值equals默认比较对象的内存地址,而hashCode基于对象的属性值。
  • 重写规则 :如果重写了equals,为了保持一致性,也应该重写hashCode,且保证相等的对象具有相同的哈希码。

10. 检查异常和非受检异常的区别

一句话回答: 检查异常(Checked Exceptions)是编译时异常,必须被显式捕获或声明抛出;非受检异常(Unchecked Exceptions)是运行时异常,包括错误(Errors)和非受检异常(RuntimeExceptions),它们不需要被显式处理。

细节解释:

检查异常(Checked Exceptions):

  • 定义 :继承自Exception类但不是RuntimeException的子类。
  • 特点:在编译时被检查,方法可以声明抛出这些异常,调用者必须捕获这些异常或进一步声明抛出。
  • 目的:强制调用者处理这些异常,从而保证程序的健壮性。
  • 示例IOExceptionSQLException等。
java 复制代码
public void readFile() throws IOException {
    // 可能抛出IOException的代码
}

非受检异常(Unchecked Exceptions):

  • 定义 :包括RuntimeException及其子类,以及Error类及其子类。
  • 特点:在编译时不会被检查,调用者可以选择捕获或忽略。
  • 目的:通常表示程序错误或运行时问题,如逻辑错误或资源问题。
  • 示例NullPointerExceptionIndexOutOfBoundsExceptionOutOfMemoryError等。
java 复制代码
public void process() {
    try {
        // 可能抛出RuntimeException的代码
    } catch (RuntimeException e) {
        // 可选的处理
    }
}

区别:

  1. 编译时检查:检查异常需要编译时检查,非受检异常不需要。
  2. 处理要求:检查异常必须被显式捕获或声明抛出,非受检异常可以被忽略。
  3. 使用场景:检查异常通常用于可预见的、可恢复的错误情况,非受检异常通常用于不可预见的程序错误或运行时问题。
  4. 继承关系 :检查异常是Exception的子类但不是RuntimeException的子类,非受检异常是RuntimeException的子类或Error的子类。

在面试中,能够区分检查异常和非受检异常,并解释它们的使用场景和处理方式,可以展示您对Java异常处理机制的理解。

相关推荐
lyllovelemon2 小时前
🍭🍭🍭五分钟带你掌握next国际化最佳实践
前端·react.js·面试
test6386 小时前
使用ThreadLocal存储用户登录信息
java·后端·面试
施展TIGERB6 小时前
程序员如何做"好"需求判断?
面试·程序员·团队管理
青青草原上的梦想家7 小时前
游戏开发面试题7
开发语言·游戏·面试
杰哥在此10 小时前
Python面试题:如何在 Python 中读取和写入 JSON 文件?
开发语言·python·面试·json·编程
Caramel_biscuit12 小时前
C++专业面试真题(1)学习
c++·学习·面试
杰哥在此18 小时前
Java面试题:讨论持续集成/持续部署的重要性,并描述如何在项目中实施CI/CD流程
java·开发语言·python·面试·编程
青青草原上的梦想家20 小时前
Cocos Creator 游戏性能优化指南
游戏·面试·性能优化·typescript
EthanWsir21 小时前
嵌入式C语言面试相关知识——关键字(不定期更新)
c语言·开发语言·面试
tq021 天前
JavaSE面试题(二)
java·开发语言·面试