Skip to content

Java 基础 已完结

Java 语言

Java 语言有什么特点

提示

面向对象、跨平台、强语言类型

  1. Java 是面向对象的语言,具有"封装、继承、多态、抽象"四大特性。
  2. Java 可以跨平台运行,编译后的字节码文件运行在 JVM 虚拟机中。
  3. Java 是强类型语言,且有异常捕获处理机制。

Java 和 C++ 的差异

提示

本地方法、指针、GC、单继承

  1. Java 的本地方法由C++实现
  2. C++ 支持多继承,Java 只能单继承
  3. Java 弱化了 C++ 的指针概念,减小的学习成本
  4. Java 有GC垃圾回收机制,不需要手动释放内存,更方便开发。

Java 如何实现跨平台

提示

编译成字节码文件,通过 JVM 编译成适配不同平台的机器码,实现跨平台

Java 先编译成 Class 字节码文件,在 JVM 虚拟机中运行。通过不同系统上的 JVM 虚拟机编译成不同的机器码,实现跨平台。

JVM vs JDK vs JRE

  1. JVM(Java Virtual Machine):Java 虚拟机,用于将 Class 字节码文件翻译成机器码,实现跨平台。
  2. JRE(Java Runtime Environment):Java 运行时环境,包含 JVM 和 Java 核心类库。
  3. JDK(Java Development Kit):Java 开发者包,包含编译器 javac、JavaDoc、调试器jdb 等和 JRE。

什么是字节码?字节码的好处是什么?

提示

效率、跨平台、可读性

字节码是 javac 编译之后的代码。运行时 JVM 将字节码文件翻译成机器码执行。

字节码的好处:

  1. 方便反编译查看代码。
  2. 实现跨平台运行。
  3. Java 是编译和解释共存的语言,比纯解释性语言的效率高

基础语法

注释有哪几种形式

  1. 行末注释
  2. 多行注释
  3. 文档注释

标识符和关键字的区别是什么?

提示

标识符:名字

关键字:特殊的名字

  1. 标识符是一个名字,用于变量名、对象名、类名等
  2. 关键字是 Java 赋予特殊含义的标识符

final、finally、finalize 的区别

  1. final 用于修饰变量、方法,类,表明该变量是常量、该方法不能被重写、该类不能被继承
  2. finally 用于 try-catch-fianlly 语句中,无论是否存在异常都会执行的代码块,常见用于释放锁、释放文件等
  3. finalize 是 Object 类的一个方法,由垃圾回收机制调用

变量

成员变量和局部变量的区别?

提示

语法、内存、生存时间

  1. 语法形式:成员变量定义在类中,局部变量定义在方法中。成员变量可以使用修饰符,局部变量只能使用 final。
  2. 存储方式:成员变量存储在堆中,局部变量存储在栈中。
  3. 生存时间:成员变量跟随其对象的生存时间,局部变量跟随其所在代码块的开始和结束。

静态变量有什么作用?

静态变量的值是属于类的,所有对象共享一份变量。

方法

静态方法为什么不能调用非静态成员?

静态方法是属于类的,在类加载时就已经创建了。而非静态成员需要在对象被创建时才会创建。

静态方法和实例方法的区别?

  1. 调用方式:静态方法可以使用类和对象调用,实例方法只能使用对象。
  2. 类成员限制:静态方法属于类,不能访问成员变量和实例方法;实例方法没有限制。

重载和重写的区别

  1. 重载是一个方法名通过不同的参数、返回值,有着不同的实现
  2. 重写是子类重写并覆盖父类的方法,要求方法名和参数一致

什么是可变长参数

方法传参可以传入不限定个数的参数,底层实现是转换为数组。

基本数据类型

基本类型和包装类型的区别

提示

默认值、存储位置、泛型

  1. 默认值:基本类型存在默认值,包装类型默认值为null
  2. 泛型:包装类可以用于泛型,基本类型不可以
  3. 存储位置:非 static 修饰的基本类型存在栈中,包装类存在堆中。

包装类存在的意义

提示

面向对象

Java 是面向对象的编程语言,很多时候需要将基本类型以对象的形式存在,比如集合。

int 存在的意义是什么

提示

效率

方便进行数值计算,int 存放在栈中,相比对象占用空间少、效率高。

包装类的缓存机制

  1. Byte、Short、Integer、Long 默认缓存 -127 ~ 128
  2. Character 缓存 0 ~ 128

包装类的比较需要使用 equals 方法

Integer 和 int 的区别是什么

提示

类型、默认值、存储位置、比较方式

  1. 类型:int 是基本数据类型,Integer 是包装类型
  2. 默认值:int 默认值是 0, Integer 默认值是 null
  3. 存储位置:int 存储在栈中,Integer 存储在堆中。
  4. 比较方式:int 可以直接比较,Integer 需要避免缓存问题使用 equals 方法。

自动拆箱和装箱:int 和 Integer 可以自动转换

自动装箱与拆箱是什么?原理是什么?

  • 自动装箱:基本类型转换成包装类型
  • 自动拆箱:包装类型转换成基本类型

原理:调用 Integer.valueOf()、num.intValue()

为什么浮点运算会丢失精度

浮点数的位数是无限的,计算机会进行截断,导致精度丢失

如何解决精度丢失问题

使用 Java 的 BigDecimal 类进行浮点数运算

超过 long 的整数应该如何处理

使用 Java 的 BigInteger 类

面向对象

面向对象和面向过程的区别

  1. 面向过程将问题拆成多个方法调用
  2. 面向对象会先抽象出对象,通过调用对象的方法解决问题

如何创建一个对象?对象实例和对象引用的区别

使用 new 关键字可以创建对象

  • 对象实例时创建出来的对象,存放在堆中
  • 对象引用时对象实例的地址,存在在栈中

对象相等和引用相等的区别

  1. 对象相等是指两个对象实例的内容是否相同,引用相等是指两个对象引用所指向的是否为同一个对象实例
  2. 对象相等需要重写 equals 方法后,调用 equals 比较;引用相等使用 == 操作符

如果一个类没有构造方法可以正确执行吗?

可以的,默认会存在默认的无参构造方法。但是如果重写了有参构造方法会导致不存在默认的无参构造。

构造方法有什么特点?是否能被重写?

特点:

  1. 方法的返回值是该类
  2. 方法名是类名
  3. 对象被创建时无需显式调用

不能被重写

面向对象的特征

封装继承多态

  1. 封装:将具体实现封装成方法
  2. 继承:子类可以继承父类,存在从属关系
  3. 多态:同一个方法可以被不同的对象以不同的方式执行。

Java 支持多继承吗?

类不支持多继承,接口支持多继承

接口和抽象类的关系

共同点:

  1. 不能实例化
  2. 可以存在默认方法和抽象方法

差异:

  1. 接口是自上向下的,抽象类的自下而上的。接口是规范实现类的,抽象类是在类中找到共同点,用于代码复用。
  2. 接口可以多继承,抽象类只能单继承,但是可以实现多个接口
  3. 接口中的成员变量需要是 public static fianl 类型的常量,抽象类可以定义成员方法。

深拷贝、浅拷贝和引用拷贝

img

向上转型和向下转型

  1. 向上转型:子类转为父类,导致丢失子类独有的成员变量
  2. 向下转型:父类转为子类,会导致安全问题,可以使用 instanceof 判断类型

Java 是值传递还是引用传递

值传递

常见类

Object 类的常见方法

  1. toString
  2. equals
  3. clone
  4. getClass
  5. hashcode

== 和 equals() 的区别

==:

  • 基本数据类型:比较值的大小
  • 引用数据类型:比较引用地址

equals():

  • 未重写:比较引用地址,相当于 ==
  • 重写:按照重写的规则比较

hashcode 的作用

为对象生成一个整数哈希值,该值可以用于哈希表等数据结构中快速查找和比较对象。

hashcode 存在的意义

hashcode 和 equals 方法都是为了判断两个对象是否相同

hashcode 可以在哈希表结构中快速获取索引,提高查找效率。但是由于哈希冲突问题,哈希值相同时也需要 equals 方法进行比较。

为什么重写 hashcode 方法时必须重写 equals 方法

提示

正确性和一致性

equals 方法和 hashCode 方法应该具有一致性,从而保证哈希表等数据结构的正确性和一致性。

String

String、StringBuilder、StringBuffer的区别

String:不可变的

StringBuffer:可变,线程安全

StringBuilder:可变,线程不安全

为什么String是不可变的

String 中使用 final 修饰的字符数组保存字符串

为什么 String 被设计成不可变的

线程安全、避免篡改、缓存

  1. 避免字符串通过其他对象引用修改
  2. 线程安全,效率高
  3. 可以缓存为字符串常量

字符串拼接用 + 还是 StringBuilder

字符串中的 +、+= 是 Java 中唯二被重载的运算符

对于少量拼接可以使用 + 提高编写效率

对于大量拼接应使用 StringBuilder,因为 + 会被重载,会创建多个 StringBuilder 对象。

String 的 equals 和 Object 的 equals 有什么区别

String 类重写了 equals 方法,用于比较两个字符串是否相同。

Object 类的 equals 方法比较对象的内存地址。

字符串常量池了解吗?

字符串常量池存在堆中。

字符串常量池保存所有在编译期就已经确定的字符串字面量。

在创建字符串时,JVM 会检查是否常量池中是否存在,存在则直接返回引用,不存在会创建后返回引用。避免重复创建相同的字符串。

String s1=new String("abc")这句话创建了几个字符串对象?

1个或2个

intern 方法的作用

将字符串存到常量池并返回引用

异常

Java 的异常体系

Java 的异常体系是由 Throwable 类及其子类构成的。

Throwable 包含两个子类:Error(错误)和 Exception(异常)

  1. Error 表示错误,通常不需要程序员处理,如内存溢出。
  2. Exception 分为受检异常和非受检异常。
    1. 受检异常必须被显式捕获或声明,如 IOException,强制捕获。可以通过 try-catch 语句块或 throws 关键字处理异常,提升程序的健壮性。
    2. 非受检异常包括运行时异常,如 NullPointerException,不强制捕获。

img

throw 和 throws 的区别

  • throw 用于抛出一个具体的异常
  • throws 用于方法签名中,声明该方法存在异常需要处理

finally 的代码在 catch 中遇到 return 会执行吗?

会执行

反射

什么是反射

在运行时动态创建对象,调用对象方法

反射优缺点?

优点:提高代码灵活性,可以用于实现动态代理,解析注解

缺点:提高代码复杂度,存在安全性问题,效率较低

反射的应用场景

  1. 用于实现框架
  2. 实现动态代理
  3. 解析注解

动态代理的几种方式及其差异

CGLIB 动态代理和 JDK 动态代理

  1. 实现⽅式
    1. JDK:反射
    2. CGLIB:继承⽬标类
  2. ⽬标类限制
    1. JDK:⽬标类必须要实现接⼝
    2. CGLIB:没有限制。
  3. 性能:JDK 动态代理相对于 CGLIB 动态代理较低
  4. 对象类型:
    1. JDK:只能代理实现了接⼝的类
    2. CGLIB:通过继承实现,不能代理 final 类
  5. 依赖库:
    1. JDK: Java ⾃带的库,不需要额外的依赖
    2. CGLIB :依赖 cglib 库

泛型

什么是泛型?有什么作用?

泛型允许在定义类或接口时使用类型参数。在使用时使用具体类型替换。

作用:提高代码复用性。

注解的解析方式有什么?

  1. 编译时解析:@override
  2. 运行时通过反射解析:@Bean

SPI

什么是 SPI 机制

服务使用者为服务提供者定义的接口规范,由服务提供者实现。

通过定义接口实现调用方和提供方的解耦。

SPI 和 API 有什么区别

img

SPI 的缺点

  1. 需要遍历所有的实现类,不能按需加载。
  2. 存在多个 ServiceLoader 同时 load,存在并发问题

IO

BIO、NIO、AIO 的区别

  • BIO(同步阻塞式IO):线程阻塞等待 IO 操作
  • NIO(同步非阻塞式IO):后台线程进行 IO 操作,前台线程轮询查看状态
  • AIO(异步非阻塞IO):后台线程执行 IO,通过回调函数通知前台线程

同步和异步的区别?

  1. 同步:调用未得到结果时会一直等待结果
  2. 异步:调用后使用后台线程执行,返回结果时会通知调用方或通过回调函数处理

阻塞和非阻塞的区别

阻塞和非阻塞指的是线程的状态

  1. 阻塞:当前线程在结果返回前会被挂起,得到结果后恢复
  2. 非阻塞:未得到结果时也能继续执行其他代码

序列化和反序列化是什么

  1. 序列化:将 Java 的对象转换为二进制数据
  2. 反序列化:将二进制数据转换回 Java 的对象

常用于数据持久化、网络传输等。

新特性

Java 8 有什么新特性

  1. Stream:流式编程
  2. Lambda 表达式:代替匿名内部类实现函数式接口
  3. Optional:处理 null 值,解决空指针异常
  4. 接口默认方法:接口中可以写默认的方法实现

设计模式

双检锁单例模式

提示

  1. 私有构造方法
  2. volatile 关键字
  3. 双检锁
Java
public class Singleton {
    // 私有构造方法
    private Singleton() {
    }

    // 创建本类对象
    // 需要使用 volatile 关键字
    private static volatile Singleton instance;

    // 提供访问方法
    public static Singleton getInstance() {
        // 如果 instance 不为 null,不需要抢占锁
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

Released under the MIT License.