Java 基础面试题汇总

沉浸式阅读:Java 基础面试题汇总

1. JVM、JRE和JDK的关系

JavaSE:Java 平台标准版,为 Java EE 和 Java ME 提供了基础。

JDK:Java 开发工具包,JDK 是 JRE 的超集,包含 JRE 中的所有内容,以及开发程序所需的编译器和调试程序等工具。

JRE:Java SE 运行时环境 ,提供库、Java 虚拟机和其他组件来运行用 Java 编程语言编写的程序。主要类库,包括:程序部署发布、用户界面工具类、继承库、其他基础库,语言和工具基础库。

JVM:java 虚拟机,负责JavaSE平台的硬件和操作系统无关性、编译执行代码(字节码)和平台安全性。

image-20210816153922528

JVM 全称 Java Virtual Machine,也就是我们耳熟能详的 Java 虚拟机。它能识别 .class 后缀的文件,并且能够解析它的指令,最终调用操作系统上的函数,完成我们想要的操作。

一个 Java 程序,首先需要经过 javac 编译成 .class 文件,然后 JVM 将其加载到方法区,执行引擎将会执行这些字节码。执行时,会翻译成操作系统相关的函数。JVM 作为 .class 文件的翻译存在,输入字节码,调用操作系统函数。

2. 面向对象的特征

面向对象的三个基本特征是:封装、继承、多态。

封装

封装把一个对象的属性私有化,同时提供一些可以被外界访问的属性的方法,如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。

封装隐藏对象的属性和实现细节,仅对外提供公共访问方式,将变化隔离,便于使用,提高复用性和安全性。

继承

继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。

关于继承如下 3 点请记住:

  1. 子类拥有父类非 private 的属性和方法。
  2. 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
  3. 子类可以用自己的方式实现父类的方法。

多态

多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

实现多态,有二种方式,覆盖,重载。

3. 接口和抽象类

接口的意义

规范,扩展,回调。

抽象类的意义

  1. 为其他子类提供一个公共的类型;
  2. 封装子类中重复定义的内容;
  3. 定义抽象方法,子类虽然有不同的实现,但是定义时一致的。

两者的区别

比较 抽象类 接口
默认方法 抽象类可以有默认的方法实现 java 8之前,接口中不存在方法的实现
实现方式 子类使用extends关键字来继承抽象类,如果子类不是抽象类,子类需要提供抽象类中所声明方法的实现 子类使用implements来实现接口,需要提供接口中所有声明的实现
构造器 抽象类中可以有构造器 接口中不能
访问修饰符 抽象方法可以有public,protected和default等修饰 接口默认是public,不能使用其他修饰符
多继承 一个子类只能存在一个父类 一个子类可以存在多个接口
访问新方法 想抽象类中添加新方法,可以提供默认的实现,因此可以不修改子类现有的代码 如果往接口中添加新方法,则子类中需要实现该方法

4. 父类的静态方法能否被子类重写

不能。重写只适用于实例方法,不能用于静态方法,而子类当中含有和父类相同签名的静态方法,我们一般称之为隐藏。

5. 什么是不可变对象

不可变对象指对象一旦被创建,状态就不能再改变。任何修改都会创建一个新的对象,如 String、Integer及其它包装类。即 final 修饰的类。

6. Overload和Override的区别

方法的重写 Override 和重载 Overload 是Java多态性的不同表现。重写 Override 是父类与子类之间多态性的一种表现,重载 Overload 是一个类中多态性的一种表现。

重载

一个类中允许同时存在一个以上的同名方法,这些方法的参数个数或者类型不同

重写

在子类中将父类的成员方法的名称保留,重新编写成员方法的实现内容,更改方法的访问权限,修改返回类型的为父类返回类型的子类。

如果说你熟悉 JVM,那么你可以在 JVM 的角度去讲解其实现,具体可参考:方法调用的底层实现之重载与重写的区别

7. 什么是值传递和引用传递

值传递

是指在调用函数时将实际参数复制一份传递到函数中,这样在函数中如果对参数进行修改,将不会影响到实际参数。

引用传递

是指在调用函数时将实际参数的地址直接传递到函数中,那么在函数中对参数所进行的修改,将影响到实际参数。

那就有人有疑问了,既然 String 是引用类型,为什么它的值不发生改变?

因为 String,Long 等都是 final 修饰的类,当然不会被修改。

可以参考:值传递和引用传递

8. 基本数据类型和引用类型

Java中一共有四类八种基本数据类型,如下表:

image-20220113145332298

注:String 不是基本数据类型

除了这四类八种基本类型,其它的都是对象,也就是引用类型,包括数组

9. float f = 5.6 是否正确

不正确。5.6 是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,因此需要强制类型转换 float f = (float)5.6;或者写成 float f = 5.6F

10. short s1 = 1; s1 = s1 + 1;有错吗? short s1 = 1;s1 += 1;有错吗

short s1 = 1; s1 = s1 + 1;

由于 1 是 int 类型,因此 s1+1 运算结果也是 int型,需要强制转换类型才能赋值给 short 型,所以错误。

short s1 = 1; s1 += 1;

可以正确编译,因为 s1+= 1; 相当于 s1 = (short(s1 + 1);其中有隐含的强制类型转换。

11. int 和 Integer 有什么区别

Java 是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但是为了能够将这些基本数据类型当成对象操作,Java 为每一个基本数据类型都引入了对应的包装类型(wrapper class),int 的包装类就是 Integer,从 Java 5 开始引入了自动装箱/拆箱机制,使得二者可以相互转换。

Java 为每个原始类型提供了包装类型:

原始类型 boolean char byte short int long float double
包装类型 Boolean Character Byte Short Integer Long Float Double

那么对于下面这段代码输出为什么:

public static void main(String[] args) {
   
   
    Integer a = 100, b = 100, c = 200, d = 200;
    System.out.println(a == b);
    System.out.println(c == d);
}

答案是:第一个为 true,第二个为 false。为什么?

装箱的本质是什么

当我们给一个 Integer 对象赋一个 int 值的时候,会调用 Integer 类的静态方法 valueOf,如下:

public static Integer valueOf(int i) {
   
   
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

即存在一个缓存,如果整型字面量的值在 -128 到 127 之间,那么不会 new 新的 Integer 对象,而是直接引用常量池中的 Integer 对象。

12. final 有什么用

用于修饰类、属性和方法:

  • 被 final 修饰的类不可以被继承;
  • 被 final 修饰的方法不可以被重写;
  • 被 final 修饰的变量不可以被改变,被 final 修饰不可变的是变量的引用,而不是引用指向的内容,引用指向的内容是可以改变的;
  • 被 final 修饰的常量,在编译阶段会存入常量池中。

13. final、finally、finalize 的区别

  • final

    可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表
    示该变量是一个常量不能被重新赋值。

  • finally

    一般作用在 try-catch 代码块中,在处理异常的时候,通常我们将一定要执行的代码方法 finally 代码块
    中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。

  • finalize

    是一个方法,属于 Object 类的一个方法,而 Object 类是所有类的父类,该方法一般由垃圾回收器来调
    用,当我们调用 System.gc() 方法的时候,由垃圾回收器调用 finalize(),回收垃圾,一个对象是否可回收的
    最后判断。

对于 finalize 方法,在进行垃圾回收的时候需要判断对象是否还存活,一般通过可达性分析来判断,但是,即使通过可达性分析判断不可达的对象,也不是非死不可,它还会处于缓刑阶段,真正要宣告一个对象死亡,需要经过两次标记过程,一次是没有找到与 GCRoots 的引用链,它将被第一次标记。随后进行一次筛选(如果对象覆盖了 finalize),我们可以在 finalize 中去拯救。如下:

public class FinalizeGC {
   
   

    public static FinalizeGC instance;

    @Override
    protected void finalize() throws Throwable {
   
   
        super.finalize();
        FinalizeGC.instance = this;
    }

    public static void main(String[] args) throws Exception {
   
   
        //创建对象
        instance = new FinalizeGC();
        
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LBXX_1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值