Java面试常见问题

宣胤

Java基础

Java语言特点

  • Java为纯面向对象的语言;
  • 具有平台无关性;
  • Java为解释型语言;
  • Java提供了很多内置库。如对多线程支持、对网络通信支持;
  • Java具有较好的安全性和健壮性。Java提供了异常处理和垃圾回收机制。

简述Java访问修饰符

  • default:默认访问修饰符,在同一包内可见
  • private:用private修饰的类成员,只能被该类自身的方法访问和修改,而不能被任何其他类(包括该类的子类)访问和引用,不能修饰外部类
  • protected:对同一包内的类和所有的子类可见,不能修饰外部类
  • public:对所有类可见

构造函数、成员变量、代码块初始化顺序

顺序:静态成员变量 -> 静态代码块 -> 非静态成员变量 -> 非静态代码块 -> 构造函数
详细顺序:父类静态变量、父类静态代码块、子类静态变量、子类静态代码块、父类非静态变量、父类非静态代码块、父类构造函数、子类非静态变量、子类非静态代码块、子类构造函数

Java三大特性

  • 封装:利用抽象数据类型(类)将数据(属性)和基于数据的操作(基于属性的方法)绑定在一起,使其构成一个不可分隔的独立实体。

  • 继承:子类继承自父类,从而获得父类非private的属性和方法,且子类可以修改或新增新的方法
    继承应该遵循里氏替换原则
    那么什么是里氏替换原则呢?:子类对象的行为应该与父类对象期望的行为一致(子类对象的行为造成的结果不能出现父类对象方法中未出现的结果)
    父类引用指向子类对象 称为向上转型
    父类 父类引用 = new 子类() –》 Animal animal = new Cat()

  • 多态:多态是同一个行为具有多个不同表现形式或形态的能力(多态就是同一个接口,使用不同实例而执行不同的操作)

    image-20230525232630820
    image-20230525232630820

    分为编译时多态和运行时多态
    编译时多态主要指方法的重载
    运行时多态指程序中定义的对象引用所指向的具体类型在运行期间才确定
    运行时多态有三个条件:继承、重写、向上转型
    多态:多种不同形态
    如:ArrayList是List的一种形态;LinkedList也是List的一种形态

接口和抽象类的相同点和不同点

相同点:

  • 都不能被实例化
  • 接口的实现类或抽象类需实现接口或抽象类中相应的方法才能被实例化

不同点:

  • 接口只能有方法定义,不能有方法的实现,而抽象类可以有方法的定义与实现
  • 实现接口的关键字为 implements,继承的抽象类的关键字类 extends。一个类可以实现多个接口,只能继承一个抽象类
  • 当子类和父类存在逻辑上的层次结构,体现的是is-a的关系,推荐使用抽象类,有利于功能的累积。当功能不需要,希望支持差别较大的两个或更多对象间的特定交互行为,体现为can的关系,推荐使用接口。使用接口能降低软件系统的耦合度,便于日后维护或添加删除方法

简述内部类及其作用

  • 成员内部类:作为成员对象的内部类。可以访问 private 及以上外部类的属性和方法。外部类想要访问内部类属性或方法时,必须要创建一个内部类对象,然后通过该对象访问内部类的属性或方法。外部类也可访问 private 修饰的内部类属性
  • 局部内部类:存在于方法中的内部类。访问权限类似局部变量,只能访问外部类的 final 变量
  • 匿名内部类:只能使用一次,没有类名,只能访问外部类的 final 变量
  • 静态内部类:类似类的静态成员变量

Java 语言中关键字 static 的作用是什么

  • 为某种特定数据类型或对象 分配 与创建对象个数无关的单一的存储空间。
  • 使得某个方法或属性 与类而不是对象关联在一起,即在不创建对象的情况下可通过类直接调用方法或使用类的属性。

具体而言 static 又可分为 4 种使用方式:

  1. 修饰成员变量。用 static 关键字修饰的静态变量在内存中只有一个副本。只要静态变量所在的类被加载,这个静态变量就会被分配空间,可以使用“类.静态变量”和“对象.静态变量”的方法使用。
  2. 修饰成员方法。static 修饰的方法无需创建对象就可以被调用。static 方法中不能使用 this 和 super 关键字,不能调用非 static 方法,只能访问所属类的静态成员变量和静态成员方法。
  3. 修饰代码块。JVM 在加载类的时候会执行 static 代码块。static 代码块常用于初始化静态变量。static 代码块只会被执行一次。
  4. 修饰内部类。static 内部类可以不依赖外部类实例对象而被实例化。静态内部类不能与外部类有相同的名字,不能访问普通成员变量,只能访问外部类中的静态成员和静态成员方法。

为什么String不可变

String内部使用char数组value[ ]存储数据,该数组被声明为final,这意味着value数组初始化之后,不能再引用其他数组,并且String内部没有改变value数组的方法,因此String不可变。

不可变的好处:

  • String作为HashMap的key。不可变的特性可以使得hash值也不可变。
  • 节省空间,字符串常量存储在JVM的字符串池中可以被用户共享。
  • 安全性,String作为参数,String不可变可以保证参数不变。
  • 线程安全。

== 和 equals的区别

==对比的是栈中的值,比较基本类型,比较的是值;比较引用类型,比较的是堆中的内存地址

equlas是Object类的方法,本质上与==一样,但是有些类重写了equals方法,比如String的equals被重写后,比较的是内存地址,另外重写了equlas后,也必须重写hashcode()方法

简述Object类常用方法

  • hashCode:通过对象计算出散列码。用于map型或 equals方法。需要保证同一个对象多次调用该方法,总返回相同的整型值。
  • equals:判断两个对象是否一致。需保证equals方法相同对应的对象hashCode也相同。
  • toString:用字符串表示该对象。
  • clone:深拷贝一个对象。

简述Java异常的分类

Java异常分为Error(程序无法处理的错误)和Exception(程序本身可以处理的异常)。这两个异常均继承Throwable。

Error常见的有 StackOverFlowError、OutOfMempryError等。

Exception可分为运行时和非运行时异常。运行时异常可以利用try catch进行处理,也可不处理,非运行时异常必须处理。

出现在Java程序中的finally代码块是否一定会执行

当遇到下面情况不会执行:

  • 当程序在进入try语句块之前就出现异常时会直接结束
  • 当程序在try快中强制退出时,如使用System.exit(0),也不会执行finally块中的代码

final、finally 和 finalize 的区别是什么

  • final 用于声明属性、方法和类,分别表示属性不可变、方法不可覆盖、类不可继承
  • finally 作为异常处理的一部分,只能在 try/catch 语句中使用,finally 附带一个语句块用来表示这个语句最终一定被执行,经常被用在需要释放资源的情况下
  • finalize 是 Object 类的一个方法,在垃圾收集器执行的时候会调用被回收对象的 finalize()方法。当垃圾回收器准备好释放对象占用空间时,首先会调用 finalize()方法,并在下一次垃圾回收动作发生时真正回收对象占用的内存

什么是反射机制

Java反射机制是在运行状态中,对于任意一个类,都能知道这个类的属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象的方法的功能称为Java的反射机制。

如何通过反射获得x类的class对象

  1. x.class
  2. x.getClass()
  3. Class.foeName(“类名”)

序列化是什么

序列化是一种将对象转换成字节序列的过程,用于解决在对对象流进行读写操作时所引发的问题。序列化可以将对象的状态写在流里进行网络传输,或者保存到文件、数据库 等系统里,并在需要的时候把该流读取出来重新构造成一个相同的对象

Concurrenthashmap的解析

Concurrenthashmap是Java中线程安全的Map实现,它允许多个线程同时对Map进行读写操作,而不会导致数据不一致或者其他线程安全问题。它的主要特点包括:

  1. 分段锁设计:Concurrenthashmap将整个Map分为多个 segment,每个segment都维护着一个独立的哈希表。在读写时,只需要锁定对应的segment,不需要锁定整个Map,从而提高了并发度。
  2. 基于CAS算法的并发控制:Concurrenthashmap的put()、get()、remove()等操作都是通过CAS实现的。在put()操作中,如果两个线程同时插入了同一个key,只有一个线程的操作会成功,另一个线程的操作会失败,从而保证了数据的正确性。
  3. 支持高并发读操作:Concurrenthashmap的读操作不需要锁定Map,多个线程可以同时对Map进行读操作,不会出现并发安全问题。
  4. 空间动态调整:Concurrenthashmap支持动态扩容和收缩,可以根据当前Map中的数据量自动调整容量。
  5. 不保证Map中的数据是按照插入顺序或者其他顺序排列的,因此不适合用于有序数据的存储和查询。

ArrayList 和 LinkedList 的区别

1、底层数据结构
ArrayList底层采用数组实现,因此支持随机访问,可以通过下标索引访问元素,时间复杂度为O(1)。但是在插入和删除操作时,需要移动后面的元素,时间复杂度为O(n)。
LinkedList底层采用双向链表实现,因此插入和删除操作只需要改变相邻节点的指针,时间复杂度为O(1),但是随机访问需要遍历链表,时间复杂度为O(n)。

2、空间占用
ArrayList的内部是一个数组,当元素个数不足数组容量时,会浪费一部分内存空间。而LinkedList每个元素需要额外的空间来存储前后节点的指针,因此会占用更多的内存空间。

3、迭代器性能
在迭代操作时,ArrayList使用普通迭代器或增强for循环的性能比LinkedList更优。这是因为ArrayList的数据存储在连续的内存中,迭代时可以直接访问内存,而LinkedList需要通过遍历链表来访问每个元素。

4、使用场景
ArrayList适用于随机访问比较多,插入和删除操作较少的场景,例如缓存、排序、搜索等。而LinkedList适用于插入和删除操作较多,随机访问较少的场景,例如队列、栈等。

Collection 和 Collections 有什么区别

  • Collection 是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,所有集合都是它的子类,比如List、Set等
  • Collections 是一个包装类,包含了很多静态方法、不能被实例化,而是作为工具类使用,比如提供的排序方法:Collections.sort(list);提供的反转方法:Collections.reverse(list)。

Spring基础

  • IoC(Inversion of Control)控制反转
    使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象的创建控制权由程序转移到外部,这种思想称为控制反转
    Spring技术对IOC思想进行了实现
    Spring提供了一个容器,称为IoC容器(系统架构中的Core Container),用来充当IoC思想的“外部“
    IoC容器负责对象的创建、初始化等一系列工作,被创建或被管理的对象在IoC容器中被称为Bean

  • DI (Dependency Injection) 依赖注入
    在容器中建立Bena与Bean之间的依赖关系的整个过程,称为依赖注入

  • AOP(Aspect Oriented Programming)面向切面编程
    AOP 的实现方式是通过动态代理或字节码操作,在代码运行期间动态地将切面织入到目标对象的方法执行过程中,从而实现对目标对象方法的增强。

数据库基础

数据库事务四大特性

  1. 原子性:事务中的所有操作要么全部完成,要么全部不完成,不会只完成其中的一部分操作。如果一个操作失败,整个事务将被回滚到事务开始前的状态,所有的操作都将被撤销。
  2. 一致性:事务执行前后,数据库的状态必须保持一致。如果事务执行后,数据库的状态不符合预期,事务将被回滚到事务开始前的状态,以保证数据库的一致性。
  3. 隔离性:事务执行的过程中,对其他事务是隔离的。即每个事务都认为自己是唯一在操作数据库的事务,不受其他事务的干扰。事务隔离级别包括读未提交、读已提交、可重复读和串行化,不同的隔离级别会影响并发性能和数据的一致性。
  4. 持久性:事务完成后,对数据库的修改必须永久保存在数据库中,即使出现了系统故障或断点等情况,也不能丢失,为了实现持久性,数据库通常使用日志来记录所有的事务操作,以便在系统故障恢复后恢复数据。

数据库隔离级别

首先解释一下幻读和不可重复读

幻读是指在同一个事务内,第一次查询某个范围内的数据时,没有查询到某些行,但是在该事务内后续再次查询同一范围内的数据时,却发现有新的数据行被查到,就像是出现了幻觉一样,因此称之为“幻读”。

不可重复读是指在同一个事务内,多次读取同一个数据的结果不一样

  • 读未提交:最低级别,事务可以读取其他事务未提交的数据,也就是脏读。虽然可以提高并发性能,但是会导致数据的不一致,一般不建议使用。
  • 读已提交:事务只能读取其他事务已提交的数据,避免了脏读,但是可能出现不可重复度和幻读的问题。
  • 可重复读:在同一个事务内,多次读取同一个数据的结果都是一样的,不会出现不可重复读的问题。但是可能会出现幻读的问题,即在同一个事务内,一个范围内的数据记录被其他事务修改或删除。
  • 串行化:最高级别,事务之间相互完全隔离,每个事务都像是在独立的系统中执行,避免了所有并发问题,但是对系统性能影响比较大。

TCP三次握手

TCP 是一种可靠的、面向连接的协议,它需要在客户端和服务器之间建立连接,保证数据能够准确、及时地传输。而 TCP 建立连接的方式就是通过三次握手(Three-way Handshake)来完成的。

  1. 客户端向服务器发送 SYN 请求(SYN = Synchronize Sequence Numbers),表示客户端想要建
    立连接。这时,客户端会随机生成一个起始序列号(Sequence Number)x,并将 SYN 标志位置
    为 1,同时等待服务器的响应。
  2. 服务器收到客户端的 SYN 请求后,会回复一个 SYN+ACK(ACK = Acknowledgment)的响应,表
    示服务器已经接受了客户端的请求,并且想要建立连接。这时,服务器会随机生成一个起始序
    列号 y,并将 SYN 和 ACK 标志位置为 1,确认号(Acknowledgment Number)设置为 x+1,同时
    等待客户端的响应。
  3. 客户端收到服务器的 SYN+ACK 响应后,会发送一个 ACK 确认响应,表示客户端已经接受了服务
    器的请求,并且可以开始发送数据了。这时,客户端会将确认号设置为 y+1,表示客户端已经
    收到了服务器的响应,连接建立成功。

常用的设计模式

  1. 工厂模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂模式通过对象的创建与使用分离,从而使代码更加灵活、可扩展,常用于创建对象复杂、种类繁多的场景。
  2. 单例模式:确保一个类只有一个实例,并提供全局访问点。单例模式通过限制一个类只能被实例化一次,从而避免了资源的浪费和竞争条件的产生,常用于需要全局唯一实例的场景。
  3. 观察者模式:定义对象之间一种一对多的依赖关系,当一个对象状态发生改变时,所有依赖它的对象都得到通知并自动更新。观察者模式通过松散耦合的方式,将观察者与被观察者分离,从而使对象之间的关系更加灵活、可扩展。
  4. 装饰器模式:动态地将责任附加到对象上,扩展对象的功能。装饰器模式通过递归组合的方式,使对象可以被无限层次地装饰,从而实现动态地添加、修改、删除对象的功能。
  5. 模板方法模式:定义一个操作中的算法骨架,而将一些步骤延迟到子类中。模板方法模式通过定义一个模板方法,将算法的骨架和具体的实现分离,从而使算法可以在不同的场景下得到重复利用。
  • 标题: Java面试常见问题
  • 作者: 宣胤
  • 创建于: 2023-05-18 21:41:38
  • 更新于: 2023-05-26 12:27:29
  • 链接: http://xuanyin02.github.io/2023/05188946.html
  • 版权声明: 本文章采用 CC BY-NC-SA 4.0 进行许可。
 评论