java后端开发面试题总结(一)

时间:2025-02-17 09:17:04

文章目录

  • 和JRE及JVM有什么区别?
  • 2.常用数字类型的区别?
  • 3.面向对象的三大特征是什么?
  • 4.接口和抽象类的异同?
  • 5.静态变量和实例变量(方法)的区别?
  • 6.类的执行顺序?
  • 7.说明java的异常体系:
  • 与字符串常量池:
  • ,StringBuilder,StringBuffer的区别:
  • 和Set的区别:
  • ,LinkedList的不同:
  • ,TreeSet的区别?
  • 类hashCode()和equals()的区别
  • IO中有几种类型的流
  • 15.请介绍JVM的内存组成(重点)
  • 16.请简述Java的垃圾回收(GC)
  • 17.垃圾回收(GC)的算法(了解名字)
  • 中内存泄漏的场景
  • 19.请实现对象的浅复制和深复制
  • 生命周期(单例多线程)

和JRE及JVM有什么区别?

<1> JDK是用java程序员用来帮助自己开发的包

<2> JRE是运行java的环境的集合

<3> JVM是java虚拟机,JVM是java的核心,只识别字节码文件,用来把java的代码码编译成字节码或者把字节码又编译成代码的, 实际是上是JVM可以跨平台,帮助java实现跨平台

2.常用数字类型的区别?

<1> 字节(byte) 取值的范围是:-2的7次方 到 2的7次方-1(-128~127), 储存空间 1个字节

<2> 整短数(short) -2的15次方 到 2的15次方-1(-32768~32767) 2个字节

<3> 整数(int) -2的31次方 到 2的31次方-1 (约-21亿~21亿) 4个字节 #重点

<4> 长整数 -2的63次方 到 2的63次方 8个字节

<5> 单精度(float) 2的-149次方 到 2的128次方-1 4个字节 #重点

<6> 双精度(double) 2的-1074次方 到 2的1024次方-1 8个字节 #重点

3.面向对象的三大特征是什么?

<1> 封装:

1- 可以实现专业的分工(用户只需要看到功能,代码底层不需要关注)

2- 减少耦合(做需求时,大家都面向接口开发,可以互不干扰)

3- 可以*修改类的内部结构(使用者不需要关心里面如何实现,换通俗句话说,只要功能可以实现,里面的代码可以变化)

注意:封装在现在开发分工和解耦的大前提下,必须要掌握的技能

<2> 继承

1- 在已有的类中派生出新的类,新的类可以吸收已有的类,还可以扩展新的能力

2- 父类是子类的抽象类,子类是父类的具象类

3- 继承类只能继承一个抽象类(父类),实现类可以实现多个接口

<3> 多台:

1- 多态是同一个行为具有多个不同表现形式或形态的能力

2- 多态是三大特性中最重要的特性,前面两个都是为多态做准备

3- 多态是同一个接口,不同的实例使用时而执行的不同操作

4.接口和抽象类的异同?

<1>相同点:

1- 都是上层的抽象

2- 不能被实例化

3- 都可以包含抽象方法

<2>不同点:

1- 抽象类可以包含方法的实现,而接口只能声明方法

2- 继承类只能继承一个抽象类,接口可以被实现类实现多个接口 #java更推荐用接口来实现抽象

3- 抽象级别: 接口>抽象类>实现类

4- 作用不同,接口是用来约束程序的行为,而继承是用于代码的复用

5.静态变量和实例变量(方法)的区别?

<1> 语法区别: 静态要用static来声明

<2> 隶属区别: 实例变量属于某一个对象的属性,而静态时直接属于类(类可以直接调用,不需要被实例化)

<3> 运行区别:
1-静态变量时在JVM在加载这个类的时候就被创建了,而实例变量要在实例化对象的时候创建 #最根本的区别,面试官关心的区别

2- 静态变量无法在运行过程中被垃圾回收站回收释放的,而实例变量就是不用了就会被回收释放

3- 静态变量存储在JVM的方法区中,而实例变量储存在对象堆的内存中

6.类的执行顺序?

<1> 静态优先

<2> 父类优先

<3> 普通非静态块优先于构造函数

注意:构造函数和普通非静态块都属于非静态块,所以先执行普通非静态库,再执行构造函数

7.说明java的异常体系:

<1> Throwable包括Error(错误)和Exception(异常),Error系统提醒,一般是系统错误(不可控制),Exception则时程序员的错误导致的(一般可控制)

<2> Excption包括runtimeException和其他,runtimeException时运行时的异常,不一定要try catch处理或抛出,而其他的则编写代码时会报错,必须处理

字符串常量池

<1> 声明字符串是保存在常量池

<2> 字符串不可变

<3> 当不同的String对象引用相同字符串时,是引用常量池中同一个内存地址

<4> 两个等号(==)比较的是两个的内存地址是否相等,equals方法比较的是两个的内容是否相等

,StringBuilder,StringBuffer的区别:

<1> 执行速度:StringBuiler>StringBuffer>String

<2> 线程安全:String和StringBuffer是安全的,StringBuiler不安全

<3> 使用的场景

1- String是少字符串操作

2- StringBuffer是多线程下大量操作

3- StringBuiler是单线程下大量操作

和Set的区别:

<1> List允许被重复,Set不允许

<2> List允许为Null,Set不允许

<3> List有序(按插入来排序),Set无序(插入后是无序的,但是可以自己设置排序规则)

<4> List的常用类:ArrayList,LinkedList

<5> Set的常用类: HashSet,LinkedHashSet,TreeSet

,LinkedList的不同:

<1> 前者的存储结构是基于动态数组,遍历方式是连续读取,适用的场景是:大数据量的读取

<2> 后者的存储结构是基于链表,遍历方式是基于指针的,适用的场景是:频繁新增和插入

,TreeSet的区别?

<1> 排序方式:HashSet不能保证顺序,后者按预置规则排序

<2> 底层存储:前者基于HashMap,后者基于TreeMap

<3> 底层实现:前者基于Hash表实现,后者基于二叉树实现

类hashCode()和equals()的区别

<1> 两者都是用来判断两个对象是否“相同”

<2>hashCode返回的是一个对象的内存地址(int),也有小概率下两个不同对象的内存地址一样,这取决于hashCode的算法,所以不是特别可靠(速度快,稳定性差)

<3>equals是相比较hashCode要复杂很多,要对对象的各方面因素进行考量,所以只要equals返回的是flase,那么必然不是同一个对象,比较可靠(速度慢,稳定性好)

<4>java里面的hashMap,hashType等都是第一层用hashCode判断,如果hashCode不相等,那则不相等,如果相等就进行第二层equals的再判断,两者结合提高可靠性和速度

  1- hashCode判断相等,equals不一定判断相等

  2- hashCode判断不相等,equals一定判断不相等

  3- equals判断相等,hashCode一定判断相等

  4- equals判断不相等,hashCode不一定判断不相等
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

IO中有几种类型的流

<1> 字节输入流:InputStream,FileInputStream,BufferedInputStream

<2> 字节输出流:OutputStream,FileOutputStream,BufferedOutputStream

<3> 字符输入流:Reader,FileReader,InputStreamReader,BufferedReader

<4> 字符输出流:Writer,FileWriter,OutputStreamWriter,BufferedWriter

15.请介绍JVM的内存组成(重点)

<1> 共享区:方法区和堆

1- 方法区是存储类的信息,静态变量,静态常量等

2- 堆是存储java的变量和java实例对象

<2> 私有区:程序计数器,虚拟机栈,本地方法栈

1- 程序计数器页可以称作行号计数器,用来行号跳转的,方便程序还原

2- 虚拟机栈是保存调用方法的,每调用一个方法就会在栈上形成一个栈针,而这栈针可以看作方法的实时状态

3- 本地方法栈是在调用操作系统底层方法时才会在这里面存放方法的栈针

16.请简述Java的垃圾回收(GC)

<1> GC(Garbage Collection)用于回收不在使用的内存

<2> GC负责三项任务,分配内存,确保引用,回收内存

<3> GC回收的依据某对象没有任何引用,则可以被回收

<4> 以前的C语言C++等需要程序员自己释放内存,java的GC使程序员从释放内存中解放出来

<5> GC屏蔽了程序员对内存的控制,也避免了程序员因为错误的操作了内存导致程序的奔溃,继而提高了稳定性

<6> 但是GC要通过JVM来对内存进行跟踪,释放没用的对象,对JVM中的堆进行碎片化处理,所以会对JVM增加负担,进而影响了JVM的执行效率

17.垃圾回收(GC)的算法(了解名字)

<1> 引用计数算法(最简单的但是也是效率最低的算法)

原理:被保存在JVM堆中的每一个java对象都又一个被引用的计数器,当又一个变量去引用到这个对象的时候,这个计数器就会+1,当变量被释放或者是断开这个引用的时候计数器就会-1,如果这个引用的计数器变成了0,则代表这个对象可以被垃圾回收

注意:如果两个对象是互相引用,叫循环引用,这个是引用的计数器无法解决的

<2> 跟踪回收算法

原理:JVM里面的对象会被遍历标记,遍历结束后,发现有没有被标记的对象,就是不能够被使用的对象,则会被回收

<3> 压缩回收算法

原理:将JVM中堆中活动的对象放到一个集中的区域中,然后再堆的区域留出一大块空闲的区域进行碎化片处理

注意:对性能不太友好

<4> 复制回收算法

原理:把堆分为两个相同大小的区域,在任何时候只有其中一个区域被使用,知道这个区域被消耗完,此时垃圾回收器就会中断程序的运行,通过遍历的方式将所有活动的对象复制到另外一个区域中,在复制的过程中这些对象是紧密的链接在一起的,从而可以消除在内存中产生的碎片,当复制结束后接着运行,直到被使用完,然后接着往复以上操作

<5> 按代回收算法(现在的垃圾回收主流算法)

原理:把JVM中的堆分成两个或者多个子堆,每个子堆都视为一代,算法在执行过程中都优先回收子堆的对象,对堆中进行多次回收仍然存活的,则把这些对象移到高一级的堆里按照这个按代回收的算法可以把一些稳定的,常用的对象减少对它的扫描次数,进而缩小了扫描的范围,从一定程度上也就是提高了我们回收的效率

内存泄漏的场景

内存泄漏就是一些不被使用的变量还在内存空间,在C++和C语言中的话,内存是程序员手动释放的,但是java是垃圾回收器自动的回收

<1> 场景1: 静态集合类!

用静态关键词static修饰的是存在JVM的方法区中,几乎不会被垃圾回收器扫描回收,数据量大的静态集合无法及时被回收,久而久之越来越多, 而方法区的内存较小,到时候就会出现了内存溢出的情况,进而程序崩溃

<2> 场景2:各种链接!

比如数据库链接,IO链接,网络链接等等,只知道把它打开,忘记把它关闭,而这些在JVM中一直是可达的状态,所以不会被垃圾回收,久而久之导致程序崩溃

<3> 场景3: 监听器!

在Java语言中往往会使用监听器,甚至使用到多个监听器,比如:底层的网络监听器

监听器就是去监听指定的类或对象产生的行为从而做出对应的响应,因为监听器往往是全局存在的,

如果对监听器中存在的对象或者变量没有有效的控制的话,很容易产生内存泄漏

<4> 场景4: 不合理的作用域!

开发又一个最基本的原则:作用域最小化,如果能把一个变量声明在方法中,就不要声明在方法外,能用private,不用public

如果一个变量的定义范围大于它的使用范围,很可能存在内存泄漏的情况,

如果被引用的对象没有被及时的设置成null的话,也有可能导致内存泄漏的产生(这种情况极少发生的)

19.请实现对象的浅复制和深复制

<1> 浅复制:只对对象及变量值进行复制,引用对象地址不变

<2> 深复制:不仅对象及及变量值进行复制,引用对象也进行复制,会形成一个新的对象地址

生命周期(单例多线程)

<1> 装载 - #JVM启动运行程序的时候装载web的xml文件

<2> 创建 - 构造函数 #在第一次访问的时候创建实例化,只最开始执行一次

<3> 初始化 - init() #在第一次访问的时候初始化函数,只最开始执行一次

<4> 提供服务 - service() 请求的总的入口 #多次访问的时候多次执行

<5> 销毁 - destory() web应用程序重启或者关闭的时候会执行destory()方法

注意:Servlet只有一个对象,创建后再访问hashCode地址不会变化的,利用单例多线程为用户提供服务