面试题-Java基础(新更新版)
前言
Java基础部分的题目,是我根据Java Guide的面试突击版本V3.0再整理出来的,其中,我选择了一些比较重要的问题,并重新做出相应回答,希望对大家起到一定的帮助。
修改记录
日期 | 修改内容 |
---|---|
20240822 | 优化7题答案 |
20240823 | 增加16题访问级别;增加17题类重名问题 |
Java基础
-
什么是面向对象编程?
面向对象编程是指使用类作为代码组织的基本单元,并将封装、继承、多态作为代码设计和实现的基石。 -
面向对象编程和面向过程编程有什么区别?
和面向对象相比,面向过程的代码组织基本单元是方法,数据和方法是相分离的,并且不支持面向对象的特性,比如:封装继承多态等等 -
Java ⾯向对象编程三⼤特性: 封装 继承 多态
- 封装:安全性(对代码自身来说);易用性(对代码调用者来说)
- 继承:解决代码复用的问题
- 多态:提高代码的扩展性和复用性,以遍历数据结构为例
- 如果没有多态,print方法的参数需要传递不同的数据结构,编写多个;有了多态,直接传递接口即可---》提高了代码的复用性
- 有多态,我们要添加新的数据结构时,只需要编写一个新的类实现接口即可,不需要修改print的代码。---》提高了代码的可扩展性
-
重载和重写的区别?
- 重载是指 同一个类中可以有多个方法名相同,但参数不同的方法。使用重载的目的是当方法功能相同而参数不同时,程序员不需要记忆很多不同的方法名,方便程序员调用方法。
- 重写是指 子类可以对父类的非final且可访问的方法功能进行重新编写。使用重写的目的一般都是子类为了扩展父类的方法功能。(注意:重写抛出的异常不能大于父类的异常,访问级别不能低于父类的访问级别)
-
构造器是否可以重写?
构造器不可以被重写,但可以被重载。
-
String、StringBuilder和StringBuffer的区别?
- String是不可变的,另外两个都是可变的。
- String不变所以线程安全,Buffer因为加了sync锁,所以也线程安全;builder不是线程安全的。
- 如何应用?
- 如果不需要构造大量变化字符串数据,使用String就好
- 如果需要构造大量变化字符串,单线程的用Builder,多线程的用Buffer
-
在 Java 中定义⼀个不做事且没有参数的构造⽅法的作⽤?
- 无参数构造方法的作用:设置默认值
- 当编写的类没有提供任何构造方法时,系统会自动添加一个无参数构造方法,并且数值型设置为0,把布尔型设置为false,对象设置为null
- 报错的情况:当类中至少提供了一个构造器,且没有提供无参数构造器时
- 实例化时没有提供参数就会出错
- 子类实例化前会先实例化父类,如果没有显示调用super,系统会默认调用无参构造器,这时也会出错
-
接⼝和抽象类的区别是什么?
- 抽象类最主要是为了解决代码复用的问题,子类和抽象父类是is-a的关系。(具体项目中,我把协议中的相同部分抽取出来,编写了AbstraceCommand和AbstraceMessage,里面包含了所有指令相同的部分,包括:消息头、类型字段等等,每个子类也有自己的成员属性,在子类构造方法中会调用一个父类的抽象方法来进行子类成员属性具体的解码;父类的构造方法中负责解码相同的部分。)
- 接口最主要是用于适应需求变动带来的实现变化问题,实现类和接口是has-a关系。如果需求几乎不会改变或者只有一种实现,可以考虑直接用实现类。(具体项目中,发送指令的驱动类就设计成了接口,提供了一种发送指令的方法,因为需要兼容不同的协议;利用一个工厂类来实现根据协议类型自动选择不同的驱动发送类实现,或者也可以使用spring容器,利用不同的协议类型名来获取实现类也可以)
- 抽象类代表了一种自下而上的编程思路,先有了重复的子类,后有抽象的父类;接口代表了一种自上而下的编程思路,编程时先关注接口对应的功能,后考虑具体的实现。
-
== 与 equals(重要)
- == 如果是基本数据类型,比较的是值;如果是引用类型,比较的是地址
- 基本数据类型没有equals方法;引用类型,如果不重写,和==的语义一样;具体实践中应该依照具体的业务需求来重写。
-
为什么会有hashcode方法?hashcode方法和equals方法的关系是什么?
hashcode方法可以返回对象的hash值,使用hash值可以用O(1)的时间复杂度来找到插入位置并且判断是否有冲突。
hashcode和equals方法用在Hash相关的数据结构中,比如hashmap,判断逻辑是,先hashcode找到位置,然后判断位置上是否有元素,如果没有直接插入;如果有再比较equals。
-
为什么 Java 中只有值传递?
值传递指的是,传递方法参数时,传递的是原始数据的拷贝。针对引用数据类型来说,拷贝的是一个地址,指向的还是原来的对象。
-
final关键字可以用在哪里,分别有什么作用呢?
- final关键字可以用于类,变量和方法上。
- 用于类:代表这个类不可以继承,并且所有的成员方法都会被隐式的加入一个final修饰
- 用于变量:
- 如果是基本数据类型:代表这个变量初始化后就不能再修改值
- 如果是引用类型:这个引用类型引用的对象在初始化后不能修改
- 用于方法:
- 防止继承类重写这个方法
- final关键字可以用于类,变量和方法上。
-
简单说说,Java中的异常处理?
在java中,所有的异常都有一个共同的父类,Throwable类,下面有两个重要的分支
- error:程序无法处理的错误
- exception:程序可以处理的异常
-
在catch和finally语句中,是否可以更改返回值?
- finally中有return的话,就以finally中的return为准。
- finally中没有return,如果finally修改的是基础类型,那么不会修改成功;修改的是引用类型的成员变量,那么可以修改成功。
-
深拷⻉ vs 浅拷⻉
- 浅拷贝:原始对象和clone后的对象引用指向同一个对象
- 深拷贝:原始对象和clone后的对象引用指向的是不同的对象
-
说说Java中访问级别的作用
- 访问级别是用来控制类本身 类中的成员 对其他类的可见性
- 对类来说,有两种访问级别:public 和 包访问级别
- 如果是包访问级别,那么意味着只有同包的类可以访问,包外的类不可以访问
- 如果是public,意味着除了包内类可以访问,其他包的类也可以访问这个类
- 对类中的成员来说,访问级别有四种:private,包访问级别,protected,public
- private:只有类内部可见
- 包访问级别:在private基础上,增加同包的类可见
- protected:在包访问级别的基础上,增加子类可见
- public:在protected访问级别基础上,增加其他包的类可见
-
如果使用*语句导入多个包,且导入的多个包中存在同名类,写代码时会有什么问题,如何解决?
- 问题:编译器会不清楚使用的是哪个包的类,需要明确。
- 如果只使用其中某一个类,那么再加入一条import语句,明确要使用的是哪个包中的类即可。如:import java.util.Date
- 如果两个同名类都需要使用,那么需要在代码中写出完整名称:
- java.util.Date deadline = new java.util.Date();
- java.sql.Date today = new java.sql.Date(...);