# Java 基础

### JDK, JRE, JVM 区别

1. &#x20;JDK, Java Development Kit, 针对程序员的产品
2. JRE, Java Runtime Environment, Java 运行环境的集合
3. JVM, Java 虚拟机，运行Java字节码文件, 跨平台核心

JDK> JRE >JVM

### Java 有哪些数据类型

#### 基本数据类型: 数值型(byte, short, int, long, float, double), 字符型 char, 布尔型 boolean

#### 引用数据类型: 类 class， 接口 interface, 数组\[]

#### 基本数据类型和引用数据类型的内存分配有何区别

基本数据类型 被放到 JVM 的 栈 中，引用数据类型放到堆中

### 访问修饰符 public， private， protected

|    修饰符    | 当前类 | 同一包内 | 其他包 |
| :-------: | :-: | :--: | :-: |
|   public  |  √  |   √  |  √  |
| protected |  √  |   √  |  ×  |
|  private  |  √  |   ×  |  ×  |

### 面向对象三大特性

**封装：**

* 我们不希望 类的成员变量被直接访问，而是通过变量的 setter 和 getter 函数进行修改和访问， 避免误操作修改成员变量的可能性。

**继承：**

* 一个类可以被另一个类继承。被继承的类称为 父类，继承父类的类称为子类。 子类一旦继承父类，也就继承了
* **Java 中不允许多继承, 也就是一个类只能继承一个父类。**

**多态：**

* 父类中的成员函数可能在子类中有多种表现形式。比如我们起那么提到的 Animal 类 有一个 eat() 函数, 但是他的子类具体 吃 什么是多张多样的， 子类的 eat() 函数表现形式多种多样。

#### 重载 (Overload) 和 重写 (Override) 的区别是什么？

* &#x20;重写发生在子类与父类之间, **外壳不变，核心重写。**&#x91CD;写方法返回值和形参都不能改变，访问修饰符无所谓
* 重载是在一个类里面，方法名字相同, 而参数不同, 返回类型无所谓。

### 接口是什么？

接口是一种标准, 所有实现这个接口的类都必须重写这个类中的方法，看做实现接口的标准。类似于所有这个家用电器的充电器电压都是220V ， 无论电器的种类，价格，都遵循这个标准。Java 不支持多继承，但是支持实现多个接口。

### 类的加载顺序

遵循1-3顺序即可

1. 静态代码块优先
2. 父类优先
3. 非静态代码块优先于构造函数

### == 和 equals 区别是什么

* 如果 == 比较的是基本数据类型，那么比较的是两个基本数据类型的值是否相等
* 如果 == 比较的是引用数据类型，那么比较的是两个对象是否指向同一块内存区域
* equals 主要用于两个对象之间, 检测一个对象的意义是否等于另一个对象，通常需要在类中重写 equals方法进行自定义比较

### equals 和 hashCode 的区别

* equals 主要用于两个对象之间, 检测一个对象的意义是否等于另一个对象，通常需要在类中重写 equals方法进行自定义比较

* hashCode() 返回一个int, 代表对象的内部地址

* 若两个对象equals成立，则hashCode 一定成立

* 若两个对象equals不成立，hashCode可能成立(因为可能有hash冲突)

* 若两个对象hashCode成立，则equals 可能成立(因为可能有hash冲突)

* 若两个对象hashCode不成立，则equals 一定不成立

### 什么是hashCode

hashCode() 的作用是获取哈希码。它实际上是返回一个int整数。这个哈希码的作用是确定该对象在hashMap 或是 hashSet中的索引位置。hashCode() 定义Java所有类的祖宗类 Object 当中，这意味着Java中的任何类都包含有hashCode()函数。&#x20;

### 为什么要有hashCode?&#x20;

**一句话, 减少 equals 次数，提高执行速度**

当把对象加入hashMap 或是 hashSet 中，通过计算hashCode来判断对象插入的索引位置。若发现有相同的hashCode对象，则需要调用equals方法来判断两个对象是否相等，若不想等，则正常插入对象。否则不再插入对象，

### String,StringBuffer, StringBuilder 的区别是什么？

|      | String              | StringBuffer | StringBuilder |
| ---- | ------------------- | ------------ | ------------- |
| 执行速度 | 最差(放在常量池中, final修饰) | 其次           | 最高            |
| 线程安全 | 安全                  | 安全           | 不安全           |
| 使用场景 | 少量字符串操作             | 多线程环境下大量操作   | 单线程环境下大量操作    |

### String为什么要设计成不可变的

1. 便于实现字符串常量池。在Java中，由于会大量的使用String常量，如果每一次声明一个String都创建一个String对象，那将会造成极大的空间资源的浪费。Java提出了String pool的概念，在堆中开辟一块存储空间String pool，当初始化一个String变量时，如果该字符串已经存在了，就不会去创建一个新的字符串变量，而是会返回已经存在了的字符串的引用。
2. 线程安全。在并发场景下，多个线程同时读一个资源，是安全的，不会引发竞争，但对资源进行写操作时是不安全的，不可变对象不能被写，所以保证了多线程的安全。
3. 加快访问速度。因为String是不可变的，所以尽最大可能保证了hashCode的唯一性。这就是为什么Map喜欢讲String 对象作为Key的原因。

### 什么是反射？

反射是一种机制，能够在程序运行时，动态的创建对象，获取类的所有信息， 提高了程序的灵活性。

#### 反射有哪几种重要的API

Class 类核心方法:

* Class.forName(String name), 静态方法, 用于获取关于指定类的 Class 对象
* classObj.newInstance(), 通过默认构造方法,创建新的对象
* classObj.getConstuctor(), 获得指定的public 修饰的构造方法的 Constructor&#x20;
* classObj.getMethod(), 获得指定的public 修饰的方法的 Method 对象
* classObj.getField(), 获得指定的public 修饰的成员变量 Field 对象

### 什么是泛型？

泛型就是将类型参数化，其在编译时才确定具体的参数。

#### 为什么使用泛型?

1. 提高了代码的复用性
2. 早期没有泛型的时候，都是将参数写成Object 类型，然后进行强制转换。而现在在编译时就能找出数据不匹配的问题。

### Java 异常体系

![](https://github.com/chesterzhang/intro_Java/blob/zhc_dev/picture/notes/exception.png?raw=true)

Java 异常体系分为两部分, Erro 和 Exception, 两者都实现了 Throwable 接口。

Exception 需要程序员try catch 或者 throw 出去。而runtimeexception是在运行时的exception，程序员并不一定知道，得等运行时才会知道

Error: 内存溢出，系统崩溃，虚拟机错误，栈溢出，操作系统级别的

### Java IO 中 有几种类型的流

按照输入输出来分： 有输入流和输出流

按照内容来分：字节流和字符流

两两组合，一共四种流

### 说一下JVM内存组成

![](https://github.com/chesterzhang/intro_Java/blob/zhc_dev/picture/notes/JVM.png?raw=true)

JVM 内存分为两部分，共享区和私有区

共享区：被所有线程共享&#x20;

* 堆: 最大的一块，唯一目的存放对象实例，对于那些没有引用的对象，会被进行垃圾回收&#x20;
* 方法区: 虚拟机加载的类的信息, 常量，静态变量

私有区: 线程独享的

* 程序计数器: 指向下一条执行指令的行号，分支，跳转&#x20;
* 虚拟机栈: 每一个方法执行的时候都会创建一个栈帧，用来存储该方法的局部变量，操作数栈，动态链接，方法返回地址&#x20;
* 本地方法栈: 本地方法栈与虚拟机栈的作用是一样的，区别在于虚拟机栈为虚拟机执行java方法服务，而本地方法栈为虚拟机执行native方法服务，native方法为本地方法，不是用java语言写的有可能是c或者c++写的

### 简述Java 垃圾回收(GC)

GC(garbage Collection) 用于回收不再使用的内存

GC 负责3项任务： 分配内容, 确保引用, 回收内存

GC回收的依据是对象有没有被引用，若没有则回收

#### 垃圾回收算法

1. 引用计数法，最简单，效率最低。JVM堆中被引用的对象有个计数器，为0被回收
2. 跟踪回收算法.，构建一张图，图访问不到的对象被回收
3. 压缩回收算法，扫描一遍全部对象，将活动的对象挪到一块集中的区域，将不活动的对象回收
4. 复制回收算法,  将JVM堆平均分成两个部分，当一个部分用完时扫描这个部分，将所有活动的对象复制到另一部分，随后垃圾清理。
5. 按代回收算法，在复制回收算法上对对象进行分类。对于那些经常使用的类扫描频率低一些，不经常使用的扫描频率高一些。

### 简述 Java 内存泄露

内存泄露指的是程序不在使用的对象或变量还在内存中占有空间。

内存泄露场景

* 用 static 修饰变量，被放到 JVM 方法区中，很少被回收
* 各种连接忘记关闭，数据库连接，网络连接，文件连接
* 监听器，用于监听其他对象的行为
* 不合理的作用域。如果一个变量定义范围 > 使用范围

### 请说明对象的浅复制和深复制

* 浅复制：只对对象及其内部的变量进行复制，对象内引用的其他对象不进行复制。直接调用 clone()方法即可
* 深复制:   不仅对 对象 及其内部的变量进行复制，对象内引用的其他对象也进行复制。需要通过序列化进行操作
