最最最常见的Java面试题总结(二)

发布 : 2019-02-21 分类 : JAVA 浏览 :

一、String 和 StringBuffer、StringBuilder的区别是什么?String为什么是不可变的?

String 和 StringBuffer、StringBuilder的区别

可变性
String 类中使用 final 关键字字符数组保存字符串,private final char value[] ,所以 String 对象是不可变的。而 StringBuilderStringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串 char[] value 但是没有用 final 关键字修饰, 所以这两种对象都是可变的。

StringBuilder 与 StringBuffer 的构造方法都是调用父类构造方法也就是 AbstractStringBuilder 实现的,可以自行查阅源码。
AbstractStringBuilder.java

1
2
3
4
5
6
7
8
9
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
}

线程安全性
String 中的对象是不可变的,也就可以理解为常量,线程安全。 AbstractStringBuilderStringBuilderStringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacityappendinsertindexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。 StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

性能
每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。
StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10% ~ 15% 左右的性能提升,但即要冒多线程不安全的风险。

对于三者使用的总结:
1、操作海量的数据 = String
2、单线程操作字符串缓冲区下操作大量数据 = StringBuilder
3、多线程操作字符串缓冲区下操作大量数据 = StringBuffer

String 为什么是不可变的吗?

简单来说就是 String 类利用了 final 修饰的 char 类型数组存储字符,源码如下:

1
2
/** The value is used for character storage. */
private final char value[];

String 真的是不可变的吗?

String不可变但不代表引用不可以变

1
2
3
String str = "Hello";
str = str + " World";
System.out.println("str=" + str);

结果:

1
str=Hello World

实际上,原来 String 的内容是不变的,只是str由原来指向 Hello 的内存地址转为指向 Hello World 的内存地址而已,也就是说多开辟了一块内存区域给 Hello World 字符串。

通过反射是可以修改所谓的 不可变 对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 创建字符串"Hello World", 并赋给引用s
String s = "Hello World";

System.out.println("s = " + s); // Hello World

// 获取String类中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");

// 改变value属性的访问权限
valueFieldOfString.setAccessible(true);

// 获取s对象上的value属性的值
char[] value = (char[]) valueFieldOfString.get(s);

// 改变value所引用的数组中的第5个字符
value[5] = '_';

System.out.println("s = " + s); // Hello_World

结果

1
2
s = Hello World
s = Hello_World

用反射可以访问私有成员,然后反射出 String 对象中的 value 属性,进而改变通过获得的 value 引用改变数组的结构。但是一般我们不会这么做。知道有这个东西就可以了。

什么是反射机制?反射机制的应用场景有哪些?

反射机制介绍

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

静态编译和动态编译

  • 静态编译:在编译时确定类型,绑定对象
  • 动态编译:运行时确定类型,绑定对象

反射机制优缺点

  • 优点: 运行期类型的判断,动态加载类,提高代码灵活度。
  • 缺点: 性能瓶颈,反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的 JAVA 代码要慢很多。

反射的应用场景

反射是框架设计的灵魂

在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码,动态代理设计模式也采用了反射机制,还有我们日常使用的 SpringHibernate 等框架也大量使用到了反射机制。

举例:①我们在使用 JDBC 连接数据库时使用 Class.forName() 通过反射加载数据库的驱动程序。② Spring 框架也用到很多反射机制,最经典的就是 XML 的配置模式, Spring 通过 XML 配置模式装载 Bean 的过程:1)将程序内所有 XMLProperties 配置文件加载入内存中。2)JAVA 类里面解析 XMLProperties 里面的内容,得到对应实体类的字节码字符串以及相关的属性信息,3)使用反射机制,根据这个字符串获得某个类的 Class 实例,4)动态配置实例的属性。
推荐阅读:

什么是 JDK? 什么是 JRE? 什么是 JVM?三者之间的联系与区别

这几个是Java中很基本很基本的东西,但是我相信一定还有很多人搞不清楚!为什么呢?因为我们大多数时候在使用现成的编译工具以及环境的时候,并没有去考虑这些东西。

JDK:顾名思义它是给开发者提供的开发工具箱,是给程序开发者用的。它除了包括完整的JRE(Java Runtime Environment),Java运行环境,还包含了其他供开发者使用的工具包。

JRE: 普通用户而只需要安装JRE(Java Runtime Environment)来运行Java程序。而程序开发者必须安装JDK来编译、调试程序。

JVM: 当我们运行一个程序时,JVM负责将字节码转换为特定机器代码,JVM提供了内存管理/垃圾回收和安全机制等。这种独立于硬件和操作系统,正是java程序可以一次编写多处执行的原因。

区别与联系:

1、JDK用于开发,JRE用于运行JAVA程序。
2、JDK和JRE中包含JVM。
3、JVM是Java编程语言的核心并且具有平台独立性。

什么是字节码?采用字节码的最大好处是什么?

先看下Java中的编译器和解释器:
Java中引入了虚拟机的概念,即在机器和编译程序之间加入了一层抽象的虚拟的机器。这台虚拟的机器在任何平台上都提供给编译程序一个共同的接口。编译程序只需要面向虚拟机,生成虚拟机能够理解的代码,然后由包解释器来将虚拟机代码转换为特定系统的机器码执行。在Java中,这种虚拟机理解的代码叫做 字节码 (即扩展名为 .class 的文件),它不面向任何特定的处理器,只面向虚拟机。每一种平台的解释器是不同的,但是实现的虚拟机是相同的。Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条执行的字节码发送给解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行。这也就解释了Java的编译与解释并存的特点。

Java源代码 ——> 编译器 ——> Jvm可执行的Java字节码(即虚拟指令)——> Jvm ——> Jvm中解释器 ——> 机器可执行的二进制机器码 ——> 程序运行。

采用字节码的好处:
Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以Java程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此,Java程序无须重新编译便可在多种不同的计算机上运行。

Java 和 C++ 的区别?

我知道很多人没学过C++,但是面试官就是没事喜欢拿咱们Java和C++比呀!没办法!!!就算没学过C++,也要记下来!

  • 都是面向对象的语言,都支持封装、继承和多态
  • Java不提供指针来直接访问内存,程序内存更加安全
  • Java的类是单继承的,C++支持多重继承,虽然Java的类不可以多继承,但是接口可以多继承。
  • Java有自动内存管理机制,不需要程序员手动释放无用内存

接口和抽象类的区别是什么?

1、接口的方法默认是 public,所有方法在接口中不能有实现,抽象类可以有非抽象的方法
2、接口中的实例变量默认是 final 类型的, 而抽象类中则不定
3、一个类可以实现多个接口,但最多只能实现一个抽象类
4、一个类实现接口的话要实现接口的所有方法,而抽象类不一定
5、接口不能用 new 实例化,但可以声明,但是必须引用一个实现该接口的对象从设计层面来说,抽象是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的规范
注意:Java8 后接口可以有默认实现(default)。

成员变量与局部变量的区别有哪些?

1、从语法形式上,看成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数;成员变量可以被 public ,private,static 等修饰符所修饰,而局部变量不能被访问控制修饰符及static所修饰;但是,成员变量和局部变量都能被final所修饰。
2、从变量在内存中的存储方式来看,成员变量是对象的一部分,而对象存在于堆内存,局部变量存在于栈内存。
3、从变量在内存中的生存时间上看,成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动消失。
4、成员变量如果没有被赋初值,则会自动以类型的默认值而赋值(一种情况例外被final修饰但没有被static修饰的成员变量必须显示地赋值),而局部变量则不会自动赋值。

重载和重写的区别?

重载: 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。   
重写: 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为private则子类就不能重写该方法。

字符型常量和字符串常量的区别?

形式上:
字符常量是单引号引起的一个字符
字符串常量是双引号引起的若干个字符
含义上:
字符常量相当于一个整形值(ASCII值),可以参加表达式运算
字符串常量代表一个地址值(该字符串在内存中存放位置)
占内存大小
字符常量只占一个字节
字符串常量占若干个字节(至少一个字符结束标志)

本文作者 : KYRIECAO
原文链接 : https://caozongpeng.github.io/2019/02/21/最最最常见的Java面试题总结-二/
版权声明 : 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!

知识 & 情怀 | 二者兼得

微信扫一扫, 向我投食

微信扫一扫, 向我投食

支付宝扫一扫, 向我投食

支付宝扫一扫, 向我投食

留下足迹