1
0
mirror of https://github.com/Snailclimb/JavaGuide synced 2025-07-03 10:46:45 +08:00
2019-04-23 11:17:53 +08:00

201 lines
12 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

### String和StringBuffer、StringBuilder的区别是什么String为什么是不可变的
#### String和StringBuffer、StringBuilder的区别
**可变性**
 
简单的来说String 类中使用 final 关键字字符数组保存字符串,`private final char value[]`,所以 String 对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串`char[]value` 但是没有用 final 关键字修饰,所以这两种对象都是可变的。
StringBuilder 与 StringBuffer 的构造方法都是调用父类构造方法也就是 AbstractStringBuilder 实现的,大家可以自行查阅源码。
AbstractStringBuilder.java
```java
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
int count;
AbstractStringBuilder() {
}
AbstractStringBuilder(int capacity) {
value = new char[capacity];
}
```
**线程安全性**
String 中的对象是不可变的也就可以理解为常量线程安全。AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
  
**性能**
每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StirngBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
**对于三者使用的总结:**
1. 操作少量的数据 = String
2. 单线程操作字符串缓冲区下操作大量数据 = StringBuilder
3. 多线程操作字符串缓冲区下操作大量数据 = StringBuffer
#### String为什么是不可变的吗
简单来说就是String类利用了final修饰的char类型数组存储字符源码如下图所以
```java
/** The value is used for character storage. */
private final char value[];
```
#### String真的是不可变的吗
我觉得如果别人问这个问题的话,回答不可变就可以了。
下面只是给大家看两个有代表性的例子:
**1) String不可变但不代表引用不可以变**
```java
String str = "Hello";
str = str + " World";
System.out.println("str=" + str);
```
结果:
```
str=Hello World
```
解析:
实际上原来String的内容是不变的只是str由原来指向"Hello"的内存地址转为指向"Hello World"的内存地址而已,也就是说多开辟了一块内存区域给"Hello World"字符串。
**2) 通过反射是可以修改所谓的“不可变”对象**
```java
// 创建字符串"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
```
结果:
```
s = Hello World
s = Hello_World
```
解析:
用反射可以访问私有成员, 然后反射出String对象中的value属性 进而改变通过获得的value引用改变数组的结构。但是一般我们不会这么做这里只是简单提一下有这个东西。
### 什么是反射机制?反射机制的应用场景有哪些?
#### 反射机制介绍
JAVA反射机制是在运行状态中对于任意一个类都能够知道这个类的所有属性和方法对于任意一个对象都能够调用它的任意一个方法和属性这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
#### 静态编译和动态编译
- **静态编译:**在编译时确定类型,绑定对象
- **动态编译:**运行时确定类型,绑定对象
#### 反射机制优缺点
- **优点:** 运行期类型的判断,动态加载类,提高代码灵活度。
- **缺点:** 性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情性能比直接的java代码要慢很多。
#### 反射的应用场景
反射是框架设计的灵魂。
在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 SpringHibernate 等框架也大量使用到了反射机制。
举例①我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序②Spring框架也用到很多反射机制最经典的就是xml的配置模式。Spring 通过 XML 配置模式装载 Bean 的过程1) 将程序内所有 XML 或 Properties 配置文件加载入内存中;
2)Java类里面解析xml或properties里面的内容得到对应实体类的字节码字符串以及相关的属性信息; 3)使用反射机制根据这个字符串获得某个类的Class实例; 4)动态配置实例的属性
**推荐阅读:**
- [ReflectionJava反射机制的应用场景](https://segmentfault.com/a/1190000010162647?utm_source=tuicool&utm_medium=referral)
- [Java基础之—反射非常重要](https://blog.csdn.net/sinat_38259539/article/details/71799078)
### 什么是JDK?什么是JRE什么是JVM三者之间的联系与区别
这几个是Java中很基本很基本的东西但是我相信一定还有很多人搞不清楚为什么呢因为我们大多数时候在使用现成的编译工具以及环境的时候并没有去考虑这些东西。
**JDK:** 顾名思义它是给开发者提供的开发工具箱,是给程序开发者用的。它除了包括完整的JREJava Runtime EnvironmentJava运行环境还包含了其他供开发者使用的工具包。
**JRE:** 普通用户而只需要安装JREJava 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则子类就不能重写该方法。
### 字符型常量和字符串常量的区别
1) 形式上:
字符常量是单引号引起的一个字符
字符串常量是双引号引起的若干个字符
2) 含义上:
字符常量相当于一个整形值(ASCII值),可以参加表达式运算
字符串常量代表一个地址值(该字符串在内存中存放位置)
3) 占内存大小
字符常量只占一个字节
字符串常量占若干个字节(至少一个字符结束标志)