面试专区|【53道Java基础高频题整理(附答案背诵版)】

Java为什么被称为平台无关性语言?

Java被称为平台无关性语言,是因为一旦Java代码被编译成字节码,这些字节码就可以在任何安装了Java虚拟机(JVM)的设备上运行,无论这个设备使用的是什么操作系统。这就是“一次编写,到处运行”的理念。

Java的这种平台无关性主要得益于Java虚拟机(JVM)。当我们编译Java代码时,生成的不是特定于某个平台的机器代码,而是一种名为字节码的中间代码。然后在运行时,JVM会将字节码转换为运行平台的机器代码。

因为每个平台(Windows,Linux,Mac等)都有自己的JVM,这些JVM知道如何在其对应的平台上运行字节码,所以Java程序可以在任何平台上运行,只要该平台安装了JVM。

这种设计使得Java具有极高的可移植性,这也是Java成为企业级应用开发首选语言的重要原因之一。

现在JDK的最新版本是什么?

目前Java的发布周期是每半年发布一次,大概在每年的3月份和9月份都会发布新版本。

在2023年3月份的时候发布了JDK 20。那么根据正常的发布节奏,接下来的发布情况应该是:

2023-09 ——> JDK 21
2024-03 ——> JDK 22
2024-09 ——> JDK 23
2025-03 ——> JDK 24
2025-09 ——> JDK 25

在JDK 20及之前的版本中,最后一个LTS版本(Long Term Support)是JDK 17。

JDK新版本中都有哪些新特性?

JDK 8中推出了Lambda表达式、Stream、Optional、新的日期API等
JDK 9中推出了模块化
JDK 10中推出了本地变量类型推断
JDK 12中增加了switch表达式
JDK 13中增加了text block
JDK 14中增加了Records
JDK 14中增加了instance模式匹配
JDK 15中增加了封闭类
JDK 17中扩展了switch模式匹配
JDK 19中增加了协程

解释下什么是面向对象?面向对象和面向过程的区别?

面向对象是一种基于面向过程的编程思想,是向现实世界模型的自然延伸,这是一种“万物皆对象”的编程思想。由执行者变为指挥者,在现实生活中的任何物体都可以归为一类事物,而每一个个体都是一类事物的实例。面向对象的编程是以对象为中心,以消息为驱动。

区别:
(1)编程思路不同:面向过程以实现功能的函数开发为主,而面向对象要首先抽象出类、属性及其方法,然后通过实例化类、执行方法来完成功能。

(2)封装性:都具有封装性,但是面向过程是封装的是功能,而面向对象封装的是数据和功能。

(3)面向对象具有继承性和多态性,而面向过程没有继承性和多态性,所以面向对象优势很明显

面向对象的三大特性?分别解释下?

面向对象的三大特性是封装、继承和多态。

  1. 封装:封装是指将对象的状态(属性)和行为(方法)结合在一起形成一个独立的整体,并尽可能隐藏对象内部的细节,只提供有限的对外接口。封装可以增强安全性和简化编程,使用者不需要了解具体的实现细节,而只需要通过对象的接口,以特定的访问权限来使用类的成员。

  2. 继承:继承是一种能够使用已存在的类的定义作为基础建立新类的方法,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。继承可以提高代码的复用性,同时可以增加类与类之间的逻辑层次关系。

  3. 多态:多态是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量到底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实例对象上,实现了运行时的动态绑定。

面向对象的五大基本原则?

面向对象编程的五大基本原则也被称为SOLID原则,分别是:

  1. 单一职责原则(Single Responsibility Principle, SRP):一个类应该只有一个引起它变化的原因。这意味着,一个类应该只负责一项职责。

  2. 开放封闭原则(Open Closed Principle, OCP):软件实体(类、模块、函数等等)应该可以扩展,但是不可修改。也就是说,对于扩展是开放的,对于修改是封闭的。

  3. 里氏替换原则(Liskov Substitution Principle, LSP):子类型必须能够替换它们的基类型,也就是说,使用基类型的地方,一定适用于子类型。

  4. 接口隔离原则(Interface Segregation Principle, ISP):客户端不应该依赖它不需要的接口。也就是说,一个类对另一个类的依赖应该建立在最小的接口上。

  5. 依赖反转原则(Dependency Inversion Principle, DIP):高层模块不应该依赖低层模块,它们都应该依赖于抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

这五个原则为面向对象设计提供了指导,遵循这些原则可以帮助我们创建出更加可维护、可复用和可扩展的系统。

Java和C++主要区别有哪些?各有哪些优缺点?

Java和C++都是广泛使用的编程语言,但它们在许多方面存在着显著的差异:

  1. 内存管理:在C++中,程序员负责为对象分配和释放内存。这提供了更大的灵活性,但也增加了内存泄漏的风险。相比之下,Java有一个垃圾收集器来自动管理内存,这使得编程更简单,但可能会对性能产生影响。

  2. 平台独立性:Java是平台无关的,这意味着编写的Java代码(字节码)可以在任何支持Java的平台上运行。而C++编译的是机器代码,它是平台相关的,所以你需要为每个目标平台编译你的代码。

  3. 错误处理:Java使用异常处理来管理运行时错误,这使得错误处理更加结构化和可预测。C++使用错误代码和异常处理来管理错误。

  4. 运行环境:Java需要Java运行时环境(JRE)才能运行,而C++程序可以直接在操作系统上运行。

  5. 性能:C++通常比Java更快,因为它编译成机器语言,而Java代码首先被编译成字节码,然后在运行时被解释执行。

  6. 多重继承:C++支持多重继承(一个类可以有多个父类),而Java不支持。但是,Java支持接口,可以实现类似的功能。

每种语言都有其优点和缺点。Java的优点包括简单的内存管理、平台无关性和强大的标准库。它的缺点包括性能(相对于C++)和需要JRE才能运行。

C++的优点包括更高的性能、更多的控制权(例如内存管理)和支持多重继承。它的缺点包括复杂的语法、更难管理的内存和缺乏跨平台的能力。

Java与C的参数方法有什么区别?

C语言是通过指针的引用传递

void swap(int *i, int *j) {
    int temp = *i;
    int *i = *j;
    *j = tmp;
}

Java会拷贝当前栈中的值传递过去

public class Test {

    private int num;

    public Test(int num) {
        this.num = num;
    }

    @Override
    public String toString() {
        return "Test{" +
                "num=" + num +
                '}';
    }

    public static void main(String[] args) {
        int i = 10;
        int j = 20;
        swap(i,j);

        //10
        System.out.println(i);
        //20
        System.out.println(j);

        Test ii = new Test(i);
        Test jj = new Test(j);
        
        swapInstance(ii, jj);
    	//Test{num=10}
        System.out.println(ii);
        //Test{num=20}
        System.out.println(jj);
    }

    private static void swap(int i, int j) {
        int temp = i;
        i = j;
        j = temp;
    }

    private static void swapInstance(Test i, Test j) {
        Test temp = i;
        i = j;
        j = temp;
    }
}

编程语言中需要进行方法间的参数传递,这个传递的策略叫做求值策略。

在程序设计中,求值策略有很多种,比较常见的就是值传递和引用传递。还有一种值传递的特例——共享对象传递。

值传递和引用传递最大的区别是传递的过程中有没有复制出一个副本来,如果是传递副本,那就是值传递,否则就是引用传递。

Java对象的传递,是通过复制的方式把引用关系传递了,因为有复制的过程,所以是值传递,只不过对于Java对象的传递,传递的内容是对象的引用。

Java 中的参数传递时传值呢?还是传引用?

Java 的参数是以值传递的形式传入方法中,而不是引用传递。

当传递方法参数类型为基本数据类型(数字以及布尔值)时,一个方法是不可能修改一个基本数据类型的参数。

当传递方法参数类型为引用数据类型时,一个方法将修改一个引用数据类型的参数所指向对象的值。即使 Java 函数在传递引用数据类型时,也只是拷贝了引用的值罢了,之所以能修改引用数据是因为它们同时指向了一个对象,但这仍然是按值调用而不是引用调用。

JDK、JRE、JVM 三者之间的关系?

JDK、JRE和JVM是Java程序运行所需要的三个核心组件,它们之间的关系如下:

  1. JVM(Java Virtual Machine):Java虚拟机,是Java程序运行的环境,负责执行Java字节码。JVM是跨平台的,这也是Java能做到“一次编写,到处运行”的原因。

  2. JRE(Java Runtime Environment):Java运行时环境,包含了JVM和Java类库,以及一些模块等。JRE实际上是运行Java程序的核心部分。

  3. JDK(Java Development Kit):Java开发工具包,是Java开发的环境,它包含了JRE,同时还有编译器(javac)、调试器(jdb)等一些工具,以及很多基础的开发文档。

所以,从包含关系上来看,JDK > JRE > JVM。JDK是最大的集合,JRE是JDK的一部分,而JVM是JRE的一部分。在Java开发中,我们需要使用JDK,因为它包含了开发工具。在Java程序运行中,只需要使用JRE就可以了,因为它包含了JVM和运行程序所需要的类库。

= =和equals有什么区别?

= =:如果比较的对象是基本数据类型,则比较的是数值是否相等;如果比较的是引用数据类型,则比较的是对象的地址值是否相等。

equals 方法:用来比较两个对象的内容是否相等。注意:equals 方法不能用于比较基本数据类型的变量。如果没有对 equals 方法进行重写,则比较的是引用类型的变量所指向的对象的地址(很多类重写了 equals 方法,比如 String、Integer 等把它变成了值比较,所以一般情况下 equals 比较的是值是否相等)。

讲一下equals()与hashcode(),什么时候重写,为什么重写,怎么重写?

在Java中,equals()hashCode()是Object类的两个方法,用于比较对象的相等性和生成对象的哈希码。

equals()方法用于判断两个对象是否相等。在Object类中,这个方法默认比较的是对象的引用。如果我们希望比较对象的内容(例如,对于String类,我们希望比较字符串的内容),那么我们就需要在自己的类中重写equals()方法。

hashCode()方法用于生成对象的哈希码。哈希码通常用于确定对象在哈希表(如HashMap)中的存储位置。在Object类中,这个方法默认返回的是对象的内存地址。如果我们重写了equals()方法,那么通常也需要重写hashCode()方法,以保证相等的对象返回相同的哈希码。

以下是一个例子,说明如何重写equals()hashCode()方法:

public class Person {
    private String name;
    private int age;

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        Person person = (Person) obj;
        return age == person.age && name.equals(person.name);
    }

    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + age;
        return result;
    }
}

在这个例子中,我们重写了equals()方法,使其能够比较Person对象的内容(name和age)。我们也重写了hashCode()方法,使其返回的哈希码基于Person对象的内容。这样,如果两个Person对象的name和age都相同,那么它们的equals()方法返回true,hashCode()方法也返回相同的值。

重写equals()hashCode()方法是很常见的做法,特别是当我们的类被用作哈希表的键时。如果我们不这样做,那么即使两个对象的内容相同,它们也可能被哈希表视为不同的键,这通常不是我们想要的结果。

为什么重写 equals() 就一定要重写 hashCode() 方法?

这个问题应该是有个前提,就是你需要用到 HashMap、HashSet 等 Java 集合,用不到哈希表的话,其实仅仅重写 equals() 方法也可以。而工作中的场景是常常用到 Java 集合,所以 Java 官方建议重写 equals() 就一定要重写 hashCode() 方法。

对于对象集合的判重,如果一个集合含有 10000 个对象实例,仅仅使用 equals() 方法的话,那么对于一个对象判重就需要比较 10000 次,随着集合规模的增大,时间开销是很大的。但是同时使用哈希表的话,就能快速定位到对象的大概存储位置,并且在定位到大概存储位置后,后续比较过程中,如果两个对象的 hashCode 不相同,也不再需要调用 equals() 方法,从而大大减少了 equals() 比较次数。

所以从程序实现原理上来讲的话,既需要 equals() 方法,也需要 hashCode() 方法。那么既然重写了 equals(),那么也要重写 hashCode() 方法,以保证两者之间的配合关系。

hashCode()与equals()的相关规定

1、如果两个对象相等,则 hashCode 一定也是相同的;

2、两个对象相等,对两个对象分别调用 equals 方法都返回 true;

3、两个对象有相同的 hashCode 值,它们也不一定是相等的;

4、因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖;

5、hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。

两个对象的 hashCode() 相同,则 equals() 也一定为 true 吗?

两个对象的 hashCode() 相同,equals() 不一定为 true。因为在散列表中,hashCode() 相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等【散列冲突】。

while(true)和for(;;)哪个性能好?

while(true)和for(;;)都是做无限循环的代码,他俩有啥区别呢?

关于这个问题,网上有很多讨论,说那么多没用,直接反编译,看看字节码有啥区别就行了。

准备两段代码:

public class HollisTest {

    public static void main(String[] args) {
        for(;;){
            System.out.println("this is hollis testing....");
        }
    }
}
    
    public static void main(String[] args) {
        while (true){
            System.out.println("this is hollis testing....");
        }
    }
}

分别将他们编译成class文件:

javac HollisTest.java

然后再通过javap对class文件进行反编译,然后我们就会发现,两个文件内容,一模一样!!!

Classfile /Users/hollis/workspace/chaojue/HLab/src/main/java/HollisTest.class
  Last modified 2023-6-18; size 463 bytes
  MD5 checksum 38eddb7d25748625d7c9aa377b6f66d3
  Compiled from "HollisTest.java"
public class HollisTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #6.#16         // java/lang/Object."<init>":()V
   #2 = Fieldref           #17.#18        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = String             #19            // this is hollis testing....
   #4 = Methodref          #20.#21        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #5 = Class              #22            // HollisTest
   #6 = Class              #23            // java/lang/Object
   #7 = Utf8               <init>
   #8 = Utf8               ()V
   #9 = Utf8               Code
  #10 = Utf8               LineNumberTable
  #11 = Utf8               main
  #12 = Utf8               ([Ljava/lang/String;)V
  #13 = Utf8               StackMapTable
  #14 = Utf8               SourceFile
  #15 = Utf8               HollisTest.java
  #16 = NameAndType        #7:#8          // "<init>":()V
  #17 = Class              #24            // java/lang/System
  #18 = NameAndType        #25:#26        // out:Ljava/io/PrintStream;
  #19 = Utf8               this is hollis testing....
  #20 = Class              #27            // java/io/PrintStream
  #21 = NameAndType        #28:#29        // println:(Ljava/lang/String;)V
  #22 = Utf8               HollisTest
  #23 = Utf8               java/lang/Object
  #24 = Utf8               java/lang/System
  #25 = Utf8               out
  #26 = Utf8               Ljava/io/PrintStream;
  #27 = Utf8               java/io/PrintStream
  #28 = Utf8               println
  #29 = Utf8               (Ljava/lang/String;)V
{
  public HollisTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 1: 0

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String this is hollis testing....
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: goto          0
      LineNumberTable:
        line 5: 0
      StackMapTable: number_of_entries = 1
        frame_type = 0 /* same */
}
SourceFile: "HollisTest.java"

可以看到,都是通过goto来干的,所以,这两者其实是没啥区别的。用哪个都行

有人愿意用while(true)因为他更清晰的看出来这里是个无限循环。有人愿意用for(;😉,因为有些IDE对于while(true)会给出警告。至于你,爱用啥用啥。

由于内容太多,更多内容以链接形势给大家,点击进去就是答案了

16. == 和 equals 的区别?

17. 两个对象的 hashCode() 相同,则 equals() 也一定为 true 吗?

18. 为什么重写 equals() 就一定要重写 hashCode() 方法?

19. & 和 && 的区别?

20. Java 中的参数传递时传值呢?还是传引用?

21. Java 中的 Math.round(-1.5) 等于多少?

22. 如何实现对象的克隆?

23. 深克隆和浅克隆的区别?

24. 什么是 Java 的序列化,如何实现 Java 的序列化?

25. 什么情况下需要序列化?

26. Java 的泛型是如何工作的 ? 什么是类型擦除 ?

27. 什么是泛型中的限定通配符和非限定通配符 ?

28. List< ? extends T > 和 List < ? super T > 之间有什么区别 ?

29. Java 中的反射是什么意思?有哪些应用场景?

30. 反射的优缺点?

31. Java 中的动态代理是什么?有哪些应用?

32. 怎么实现动态代理?

33. static 关键字的作用?

34. super 关键字的作用?

35. finally 块中的代码什么时候被执行?

36. finally 是不是一定会被执行到?

37. try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?

38. try-catch-finally 中那个部分可以省略?

39. Error 和 Exception 的区别?

40. 运行时异常与受检异常有何异同?

41. throw 和 throws 的区别?

42. 常见的异常类有哪些?

43. 主线程可以捕获到子线程的异常吗?

44. 字节和字符的区别?

45. String 为什么要设计为不可变类?

46. String、StringBuilder、StringBuffer 的区别?

47. String 字符串修改实现的原理?

48. String str = “i” 与 String str = new String(“i”) 一样吗?

49. String 类的常用方法都有那些?

50. final 修饰 StringBuffer 后还可以 append 吗?

51. Java 中的 IO 流的分类?说出几个你熟悉的实现类?

52. 字节流和字符流有什么区别?

53. BIO、NIO、AIO 有什么区别?

  • 12
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值