前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 后台开发面试题分享六

Java 后台开发面试题分享六

作者头像
RendaZhang
发布2020-11-04 15:19:44
4600
发布2020-11-04 15:19:44
举报
文章被收录于专栏:RendaRenda

堆(Heap)和栈(Stack)的区别

(1)管理方式不同。栈由操作系统自动分配释放,无需手动控制;堆的申请和释放工作由程序员控制,容易产生内存泄漏。

(2)空间大小不同。每个进程拥有的栈的大小要远远小于堆的大小。理论上,程序员可申请的堆大小为虚拟内存的大小,进程栈的大小 64bits 的 Windows 默认 1MB,64bits 的 Linux 默认 10MB。

(3)生长方向不同。堆的生长方向向上,内存地址由低到高;栈的生长方向向下,内存地址由高到低。

(4)分配方式不同。堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是由操作系统完成的,比如局部变量的分配。动态分配由 alloc 函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由操作系统进行释放,无需我们手工实现。

(5)分配效率不同。栈由操作系统自动分配,会在硬件层级对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是由 C/C++ 提供的库函数或运算符来完成申请与管理,实现机制较为复杂,频繁的内存申请容易产生内存碎片。显然,堆的效率比栈要低得多。

(6)存放内容不同。栈存放的内容,函数返回地址、相关参数、局部变量和寄存器内容等。当主函数调用另外一个函数的时候,要对当前函数执行断点进行保存,需要使用栈来实现,首先入栈的是主函数下一条语句的地址,即扩展指针寄存器的内容(EIP),然后是当前栈帧的底部地址,即扩展基址指针寄存器内容(EBP),再然后是被调函数的实参等,一般情况下是按照从右向左的顺序入栈,之后是被调函数的局部变量,注意静态变量是存放在数据段或者 BSS 段,是不入栈的。出栈的顺序正好相反,最终栈顶指向主函数下一条语句的地址,主程序又从该地址开始执行。堆,一般情况堆顶使用一个字节的空间来存放堆的大小,而堆中具体存放内容是由程序员来填充的。

死锁是什么

死锁:多线程相互嵌套就会造成死锁。

比如线程在获得一个锁 L1 的情况下再去申请另外一个锁 L2,也就是锁 L1 想要包含了锁 L2,也就是说在获得了锁 L1,并且没有释放锁 L1 的情况下,又去申请获得锁 L2,就会产生死锁。

在编写程序时应该尽量避免出现死锁。下面有几种常见的方式用来解决死锁问题:

  1. 避免多次锁定。尽量避免同一个线程对多个 Lock 进行锁定。例如上面的死锁程序,主线程要对 A、B 两个对象的 Lock 进行锁定,副线程也要对 A、B 两个对象的 Lock 进行锁定,这就埋下了导致死锁的隐患。
  2. 具有相同的加锁顺序。如果多个线程需要对多个 Lock 进行锁定,则应该保证它们以相同的顺序请求加锁。比如上面的死锁程序,主线程先对 A 对象的 Lock 加锁,再对 B 对象的 Lock 加锁;而副线程则先对 B 对象的 Lock 加锁,再对 A 对象的 Lock 加锁。这种加锁顺序很容易形成嵌套锁定,进而导致死锁。如果让主线程、副线程按照相同的顺序加锁,就可以避免这个问题。
  3. 使用定时锁。程序在调用 acquire() 方法加锁时可指定 timeout 参数,该参数指定超过 timeout 秒后会自动释放对 Lock 的锁定,这样就可以解开死锁了。
  4. 死锁检测。死锁检测是一种依靠算法机制来实现的死锁预防机制,它主要是针对那些不可能实现按序加锁,也不能使用定时锁的场景的。

自旋锁

自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

列出常见的几种 RunException

NullPointerException - 空指针引用异常

ClassCastException - 类型强制转换异常

IllegalArgumentException - 传递非法参数异常

ArithmeticException - 算术运算异常

ArrayStoreException - 向数组中存放与声明类型不兼容对象异常

IndexOutOfBoundsException - 下标越界异常

NegativeArraySizeException - 创建一个大小为负数的数组错误异常

NumberFormatException - 数字格式异常

SecurityException - 安全异常

UnsupportedOperationException - 不支持的操作异常

NegativeArrayException - 数组负下标异常

EOFException - 文件已结束异常

FileNotFoundException - 文件未找到异常

SQLException - 操作数据库异常

IOException - 输入输出异常

NoSuchMethodException - 方法未找到异常

java.lang.AbstractMethodError - 抽象方法错误。当应用试图调用抽象方法时抛出。

java.lang.AssertionError - 断言错。用来指示一个断言失败的错误。

java.lang.ClassCircularityError - 类循环依赖错误。在初始化一个类时,若检测到类之间循环依赖则抛出该异常。

java.lang.ClassFormatError - 类格式错误。当 Java 虚拟机试图从一个文件中读取 Java 类,而检测到该文件的内容不符合类的有效格式时输出。

java.lang.Error - 错误。是所有错误的基类,用于标识严重的程序运行问题。这些问题通常描述一些不应被应用程序捕获的反常情况。

Java 的 transient 修饰的作用?

作用:在对象序列化的时候,有些变量不需要序列化,比如密码等,可以使用 transient 关键字来解决这个问题;transient 修饰的变量不会被序列化。

什么是 java 序列化,如何实现 java 序列化?

序列化是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化。可以对流化后的对象进行读写操作,也可将流化后的对象传输于网络之间。序列化是为了解决在对对象流进行读写操作时所引发的问题。

序列化的实现:将需要被序列化的类实现 Serializable 接口,该接口没有需要实现的方法,implements Serializable 只是为了标注该对象是可被序列化的,然后使用一个输出流(如:FileOutputStream)来构造一个 ObjectOutputStream(对象流)对象,接着,使用 ObjectOutputStream 对象的 writeObject(Object obj) 方法就可以将参数为 obj 的对象写出(即保存其状态),要恢复的话则用输入流。

Java 的 final,finally,finalize() 的区别?

final - 修饰符

代码语言:javascript
复制
类:此类不能被继承。
1. 为了类的安全,不允许子类随意更改;
2. 类中有很多方法,方法之间有复杂的调用关系;
3. 类是最终版本的类,不需要扩展了。

方法:此方法不能被子类重写,所有的子类调用的是同一个版本的方法。

变量: 常量,值不能更改。
常量规范 - 每个单词字母都大写,多个单词用下划线连接,如 MAX_VALUE
值不能改:
1. 基本类型:值不能改。
2. 引用类型:对象不能改,但对象的属性值可以改。
3. 数组:数组的元素可以改。

finally

代码语言:javascript
复制
作为异常处理的一部分,它只能用在 try/catch 语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下。System.exit() 语句,终止了 Java 虚拟机的运行,会让 finally 的语句无法执行。

finalize()

代码语言:javascript
复制
是在 java.lang.Object 里定义的,也就是说每一个对象都有这么个方法。这个方法在 gc 启动,该对象被回收的时候被调用。其实 gc 可以回收大部分的对象(凡是 new 出来的对象,gc 都能搞定,一般情况下又不会用 new 以外的方式去创建对象),所以一般是不需要程序员去实现 finalize 的。 

特殊情况下,需要程序员实现 finalize,当对象被回收的时候释放一些资源,比如:一个 socket 链接,在对象初始化时创建,整个生命周期内有效,那么就需要实现 finalize,关闭这个链接。 

finalize() 的调用具有不确定行,只保证方法会调用,但不保证方法里的任务会被执行完。一个对象的 finalize() 方法只会被调用一次,而且 finalize() 被调用不意味着 gc 会立即回收该对象,所以有可能调用 finalize() 后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会调用 finalize(),产生问题。 所以,推荐不要使用 finalize() 方法。

Java 的 abstract class 和 interface 有什么区别?

相同点:

代码语言:javascript
复制
- 都不能被实例化。
- 接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化。

不同点:

代码语言:javascript
复制
- 抽象类中可以有构造方法, 而接口不可以有构造方法
- 接口只有定义,java  之前不能有方法的实现,java 1.8 中可以定义默认方法和静态方法,Java  新增支持私有方法;而抽象类可以有定义与实现,方法可在抽象类中实现。
- 实现接口的关键字为 implements,继承抽象类的关键字为 extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
- 接口强调特定功能的实现,而抽象类强调所属关系。
- 接口成员变量默认为 public static final,必须赋初值,不能被修改;其所有的成员方法都是 public、abstract 的。抽象类中成员变量默认 default,可在子类中被重新定义,也可被重新赋值;抽象方法被 abstract 修饰,不能被 private、static、synchronized 和 native 等修饰,必须以分号结尾,不带花括号。
- 接口强调减少代码间的耦合度,不同实现类只是共享某些同样的方法声明,配合多态,支持动态方法解析;抽象类强调继承关系,为多个子类定义同样的行为,倾向于充当公共类的角色,强调代码的可重用性。
- 实现复杂功能时用抽象类的继承,实现简单功能时用接口,开发中一般采用面向接口编程
- 接口隐藏了更多的细节,只展现了重要的内容。

JDK、JRE、JVM 分别是什么?包含关系是怎样的?

JDK - Java Development Kit,Java 开发工具包。是 Java 开发的核心,包括了 Java 运行环境 jre,很多的 Java 工具,以及一些 Java 基础类库。

JRE - Java Runtime Environment,Java 运行环境。是运行基于 Java 语言编写的程序所不可缺少的运行环境。

JVM - Java Virtual Machine,Java 虚拟机。是 Java 实现跨平台的最核心部分。所有的 java 程序会首先被编译为 .class 的类文件,这种类文件可以在虚拟机上执行。由虚拟机将程序解释给本地系统执行。

包含关系:JDK 包含 JRE,JRE 包含 JVM。

Java 的 Collection 跟 Collections 区别?

Collection 是集合类的上级接口,子接口主要有 Set 和 List。 Collections 是针对集合类的一个帮助类,提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-10-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Renda 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 堆(Heap)和栈(Stack)的区别
  • 死锁是什么
  • 自旋锁
  • 列出常见的几种 RunException
  • Java 的 transient 修饰的作用?
  • 什么是 java 序列化,如何实现 java 序列化?
  • Java 的 final,finally,finalize() 的区别?
  • Java 的 abstract class 和 interface 有什么区别?
  • JDK、JRE、JVM 分别是什么?包含关系是怎样的?
  • Java 的 Collection 跟 Collections 区别?
相关产品与服务
文件存储
文件存储(Cloud File Storage,CFS)为您提供安全可靠、可扩展的共享文件存储服务。文件存储可与腾讯云服务器、容器服务、批量计算等服务搭配使用,为多个计算节点提供容量和性能可弹性扩展的高性能共享存储。腾讯云文件存储的管理界面简单、易使用,可实现对现有应用的无缝集成;按实际用量付费,为您节约成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档