> For the complete documentation index, see [llms.txt](https://yangsx95.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yangsx95.gitbook.io/notes/programming-language/java.md).

# Java

## 八大基本类型

> Java 是强类型语言，每个变量必须声明一种数据类型。
>
> JavaScript是弱类型语言，变量定义可以不声明数据类型。

Java共有八种基本类型（primitive type）：包含四种整型、两种浮点型、一种字符型、以及一种布尔型

### 关于 bit byte kb mb gb tb

bit 代表一位，比如二进制数据 1101 就是 4 位

byte 代表一个字节， 1 byte = 8 bit ， 二进制数 1101 0011 就是一个字节

kb 1kb = 1024 byte

mb 1mb = 1024 kb

…

### 整型

类型字节数位数最小值最大值默认值byte1byte8bit-2^72^7-10short2byte16bit-2^152^15-10int4byte32bit-2^312^31-10long8byte64bit-2^632^63-10

> long 需要在数字后加上 L/l

#### 8bit 的最大值和最小值为什么是 2^7 – 1 和 -2^7 ？

一个八位的二进制数，可以表示2 ^ 8 个数字，其中正负各占一半。

#### 整型各个进制表示法

`八进制`：0 \*\*；(\*表示0到7任意数字)

`十六进制`：0x\*\* \_；(表示0到f任意数字)

Java 7 新特性：

`二进制`：`0b_** _;`(\_ 表示0或1)

Java7 新特性 ：

`int a = 100_345_001;` 可以使用下划线分割数字，编译时自动忽略下划线，等同于100345001 。 作用，便于阅读

### 浮点型

类型字节数位数默认值float4byte32bit0.0double8byte64bit0.0

> float需要在数字后加上 F/f， 如果不加代表double双精度浮点类型
>
> 浮点类型不适用于禁止出现舍入误差的金融计算中 （2.0-1.1）= 0.8999999999

浮点类型的运算遵循 IEEE754 标准，即IEEE二进制浮点数算术标准。

> 在Java1.2时，提出了关键字 strictfp ，用来修饰方法和类型， 被该关键字修饰后，会完全遵循 IEEE754标准进行浮点运算，早期java的浮点运算是一种近乎我行我素的方式。

### char类型

char类型用于表示单个字符，通常用于表示字符常量，两个字节，16bit， 他的默认值为 0 ，即字符 空格。

可以表示：

* 单个ASCII字符： ‘A’ 即 65
* Unicode编码单元：十六进制值,范围为`\u0000` 到 `\uffff`
* 转义字符：`\b`, `\t`, `\n`, `\r`, `\"`, `\'`, `\\`

#### 代码点和代码单元

**背景**：

Unicode是为了统一世界上所有符号所设置的编码方式，当时认为 unicode使用2个字节表示足以应对所有的文字和符号，但是，随着时间的发展，16位的unicode编码无法表示所有的字符了。

**概念**：

**代码点 code point**，是指一个编码表中某个字符对应的代码值。在Unicode编码表中，一个代码点使用十六进制数表示，并且加上前缀 `U+`, 比如 `U+0041` 是字母 `A` 的代码点, `U+2A000` 是增补字符 `𪀀` 的代码点。

> Unicode 代码点，可以分为17个代码级别（code plane）。第一个代码级别称为 基本的多语言级别（basic multilingual plane），代码点从 `U+0000` 到 `U+FFFF`，主要包含了经典的Unicode代码点。其余的16个附加级别， 代码点从 `U+10000` 到 `U+10FFFF`，包含了一些增补字符

**代码单元 code unit**，utf-16编码使用两个字节表示一个基本语言级别的字符，及一个代码单元，比如 `\u20041`。 当遇到增补字符时，16位无法进行表示，则使用两个代码单元表示，比如汉字 `𪀀` 的字符编码为 `\ud868\udc00`。

在 Java 中char类型用UTF-16编码描述一个代码单元。

> 注意： 增补字符无法用一个char来表示，比如： `char a = '\ud868\udc00';` 就会报错

### boolean类型

默认值 `false`，占用字节大小要根据虚拟机确定。

> 整型和布尔类型之间不能相互转换。

### 有关数据类型的面试题

#### short和int的转换问题

```java
byte b = 128;  // 打印b？：A编译报错 B运行报错 C:可以输出
// 选A

short s = 1;
s = s + 1; // 打印s？：A编译报错 B运行报错 Cs=2
// 选A，类型不匹配
// s + 1 , 因为1是int类型，会自动提升表达式类型为int，而接收的是short，所以编译报错

s += 1; // 正确?
// 正确，正常执行，+=是语法规定的特殊运算符，有对其特殊处理，所以正常执行
```

#### char类型是否可以存储一个汉字？

char 存储一个Unicode代码单元，汉字则是由一个Unicode代码点组成，一个代码点可能由多个代码单元组成。

所以char可以存储一个汉字，但是生僻字就不行了。

#### double 和 float

```java
double a = 100f;  // 正确，小范围类型数据可以赋值给大范围类型，即向上转型，直接自动转换
float b = 100; // 编译报错，100 是一个double类型，大不可给小，会有精度损失，即向下转型，需要强制类型转换帮助
```

#### double 和 long

```java
double a = 100L; // 正确
long b = 100.0; // 编译报错，double比long更精确
long b = 100.0f; // 同样不可以
```

#### 浮点运损失

```java
3 * 0.1 == 0.3  // false 浮点运算都有损失
```

#### 包装类型与基本类型比较

```java
Double d1 = 100.0;
Double d2 = 100.0;
d1 == d2; // false d1 d2 都是包装类型，都是被自动装箱创建出来的

Integer i1 = 128;
Integer i2 = 128;
i1 == i2  // false，同上

Integer i3 = 127;
Integer i4 = 127;
i3 == i4 // true，Integer内部有IntegerCache，是一个享元池，范围在-128~127之间的值，自动装箱时直接从池中获取，提高了java性能
// Short/Byte/Long同样具有范围也是(-128~127)
// 浮点数没有

new Long(100L) == 100L; // true , 基本类型和包装类型比较时，先将包装类型拆箱后再比较

new Long(100).equals(100L); // true
new Long(100).equals(100); // false
// 先比较类型，再比较值
```

## Java中的i++和++i

参考：

* [Java 中 i++和++i的区别 – 日月心诚 – 博客园](https://www.cnblogs.com/Lxiaojiang/p/6718848.html)
* [java中 i = i++和 j = i++ 的区别 – motivated\_Dou – 博客园](https://www.cnblogs.com/Mr24/p/6512137.html)

### i = i++

```java
int i = 0;
i = i++; // 0
```

**解释**：

Java 使用缓存变量的机制， `i=i++` 相当于：

```java
int i = 0;
temp = i;
i++; // i = i + 1;
i = temp;
```

### i++ 和 ++i 多个混合运算

```java

int i=5;
int s=(i++)+(++i)+(i--)+(--i)=24;
解析：
1. i++ => i=6; s'=5;
2. ++i => i=7; s'=7;
3. i-- =>  i=6; s'=7;
4. --i =>  i=5; s'=5;
==>s=5+7+7+5=24;
```

### 金山面试

![file](/files/dFsXqtqUV61X58kN3eGl)

**答案**：

```java
i = 2;
j = 7;
k = 7;
h =3;
p1 = 1;
p2 = 1;
q1 = 1;
q2 = 0;
```

## for、foreach、iterator遍历时修改、删除、增加的问题

Java中，一般存在三种遍历方式：

* for
* foreach
* iterator

### for循环

* **修改**：遍历中修改，不会存在任何问题
* **删除**：遍历中删除某个元素，集合/数组的长度就会变短，这样在遍历时就有可能造成数组下标越界异常（注意，for循环的list.size每次遍历都会获取，所以如果删除一个中间元素，遍历并不会出现下标越界；下表越界只会发生在同一循环内，比如已经删除了最后一个元素，却又获取最后一个元素的情况）

  ```java
  // 代码示例如下
  // 不会抛出异常
  for (int i = 0; i < list.size(); i++) {
  if (i == 0) {
      list.remove(0);
  }
  System.out.println(list.get(i));
  }
  // 会抛出数组下标越界
  for (int i = 0; i < list.size(); i++) {
  if (i == 2) {
      list.remove(0);
  }
  System.out.println(list.get(i));
  }
  ```
* **添加**：遍历中一直添加元素，有可能造成无穷循环的情况

  ```java
  // 代码示例如下
  for (int i = 0; i < list.size(); i++) {
      if (i == list.size() - 1) {
          list.add("a");
      }
  }
  ```

### foreach 与 iterator

foreach 实际上只是 iterator的一个语法糖，如果由如下foreach代码：

```java
for (String item : items) {
    System.out.print(item);
}
```

实际上编译后的代码为：

```java
Iterator<String> iterator = items.iterator();
while (iterator.hasNext()) {
     System.out.print(iterator.next());
}
```

* **修改**：使用iter在遍历时修改，与for循环一样，不会发生任何问题
* **删除**：使用iter在遍历时删除元素，如果使用的是 `iterator.remove()` 方法是可以正常删除的：

  ```java
  // 情况一： 使用集合提供的remove方法：
  for (String s : list) {  // 抛出ConcurrentModificationException 抛出并发修改异常，无法删除
      if (s.equals("0")) {
          list.remove(0);
      }
      System.out.println(s);
  }
  // 情况二：删除倒数第二个元素，不会报错，因为下次hasNext返回false直接退出循环
  Iterator<string> iterator = list.iterator();
  while (iterator.hasNext()) {
      String value = iterator.next();
      if ("1".equals(value)) {
          list.remove(list.size() - 2); // 删除元素1
      }
      System.out.println(value); // 0, 1
  }
  System.out.println(list); // [0, 2]
  // 情况三：使用iterator提供的remove方法：
  Iterator<string> iterator = list.iterator();
  while (iterator.hasNext()) {
      String value = iterator.next();
      if ("0".equals(value)) {
          iterator.remove(); // 不会抛出异常，会正常删除
      }
      System.out.println(value); // 还是会打印出被删除的元素  0,1,2
  }
  System.out.println(list); // 输出的元素确实是删除过后的 [1,2]</string></string>
  ```
* **增加**：增加无论任何情况下，都会抛出 `ConcurrentModificationException` 异常

### Iterator的源码

参考类： `ArrayList$Itr`

```java
// 源码如下：
private class Itr implements Iterator<E> {
    int cursor;       // 下次调用next()获取的元素的指针
    int lastRet = -1; // 最后一次操作的索引，如果上次操作为remove，则会置为-1  是一个记录变量
    int expectedModCount = modCount; // modCount代表当前列表被修改的次数，使用expectedModCount记录当前状态的修改次数

    public boolean hasNext() { // 如果当前指针大小小于list的size大小，说明还有元素没有遍历完
        return cursor != size;
    }

    // 获取下一个元素
    @SuppressWarnings("unchecked")
    public E next() {
        checkForComodification();
        int i = cursor;
        // 判断是否已经遍历到最后一个元素
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1; // 获取cursor位置元素成功，将指针位置后移一位(cursor++)
        return (E) elementData[lastRet = i]; // 使用lastRet记录上一次操作的位置
    }

    // 移除当前元素
    public void remove() {
        // 如果上次操作为remove，则抛出非法参数异常，所以循环直接使用iterator.remove() 就会抛出 IllegalStateException
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            // 调用当前集合的remove()方法，移除当前的元素
            ArrayList.this.remove(lastRet);
            cursor = lastRet; // 指针变为上次操作的索引（删除，数组后面的元素会向前移动，下次遍历的元素位置是coursor+1）
            lastRet = -1; // 标记上次操作为remove
            expectedModCount = modCount; // 更新当前修改次数
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public void forEachRemaining(Consumer<? super E> consumer) {
        Objects.requireNonNull(consumer);
        final int size = ArrayList.this.size;
        int i = cursor;
        if (i >= size) {
            return;
        }
        final Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length) {
            throw new ConcurrentModificationException();
        }
        while (i != size && modCount == expectedModCount) {
            consumer.accept((E) elementData[i++]);
        }
        // update once at end of iteration to reduce heap write traffic
        cursor = i;
        lastRet = i - 1;
        checkForComodification();
    }

    // 检查修改次数是否发生变化： 即检测list是否被修改
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

```

## 按位运算符

### 按位&

非短路与,所有的表达式都会执行，最主要的功能不是用来连接判断条件表达式的.

```
表达式1 & 表达式2 & 表达式3 ....
```

当前仅当所有表达式为true的时候,整个判断结果才是true. 最主要的是具备运算能力.

```
当且仅当全部是1的情况,结果才是1.
                  10&8=8
                  1 0 1 0
                  1 0 0 0
                  -------
                  1 0 0 0->十进制8
```

### 按位 |

非短路或,所有的表达式都会执行，最主要的功能不是用来连接判断条件表达式的.

```
表达式1 | 表达式2 | 表达式3 ....
```

只要有一个表达式是true,那么结果就是true:, 最主要的是具备运算能力

```
只要出现一个1,就是1
                  10|8=8
                  1 0 1 0
                  1 0 0 0
                  -------
                  1 0 1 0->十进制10
```

### 取反

1变0,0变1

```
            ~10
            00000000 00000000 00000000 00001010
            11111111 11111111 11111111 11110101
```

### 异或. ^,相同为0,不同为1

```
        10^8^8 = 10
                1 0 1 0
                1 0 0 0
                --------
                0 0 1 0 -> 2
                1 0 0 0
                -------
                1 0 1 0 -> 10
```

**结论**:一个数连续异或同一个数两次,结果是它本身. **应用**:以后可以利用^进行加密和解密\[IO流...]

### 位移运算符

**面试**:位移运算是计算机中性能最高的运算方式，请您以最高性能的方式计算出2的3次方

```java
System.out.println(2+2+2+2);
System.out.println(2*2*2);
System.out.println(Math.pow(2,3));
System.out.println(2<<2);

<<:左位移...
2<<2
000000 00000000 00000000 0000001000 -> 8

>>:带符号的右位移,移动了多少位,就在最高位补符号位
>>>:不带符号的右位移,移动了多少位,就在最高位补几个0

0 正 1 负
```

## 面向对象基本概念

软件开发主流开发方法：

1. 结构化开发方法
2. 面向对象开发方法

### 结构化程序设计

**结构化程序设计**：自顶向下、逐步求精、模块化

**结构化程序设计步骤**：

1. 结构化分析（Structured Analysis，SA）：需求分析
2. 结构化设计（Structured Design，SD）：系统概要设计、详细设计
3. 结构化编码（Structured Program，SP）

主张按照功能将软件系统划分，也称为 **面向功能程序设计**，每个功能都有输入输出， **函数** 就是结构化程序设计的最小单位。

自顶向下：每个模块包含子模块，子模块又包含子模块

逐步求精：先完成大体结构，再具体到小功能（主函数作为入口， 将其他众多函数串起来）

**缺点**：

1. 不直观，需要将客观世界抽象为具体功能
2. 扩展性差，修改实现方式，需要自顶向下的修改模块结构

### 面向对象程序设计

面向对象：使用类、对象、封装、继承、消息等基本概念进行程序设计，根据客观事物进行抽象来构造软件系统。

**类** 是面向对象程序设计的最小单位，是具有共同属性方法的一类事物，是对客观事物进行抽象模拟，通过公共方法暴露该类提供的功能，从而提高内聚性，降低耦合，类是由方法和属性组成：

```
成员变量（状态数据） + 方法 （行为） = 类定义
```

**对象**：是类的具体实例，反应客观事物，具有标识唯一性、分类性、多态性、封装性、模块独立等。

**消息**：对象直接相互通信合作，需要“消息”机制完成

**面向对象的三大特征**：

1. 封装（Encapsulation）
2. 继承（Inheritance）
3. 多态（Polymorphism）

* **封装** 是指将对象的具体细节隐藏起来，使用公共方法暴露对象的功能
* **继承** 是面向对象实现软件复用的手段
* **多态** 则是指子类对象赋值给父类引用，编译时类型为父类，运行时类型为子类，意味着统一类型对象，可能表现多种特征。

**抽象** 也是面向对象的重要组成部分，但是所有编程语言都需要抽象，抽象只考虑部分可归类的问题，不考虑所有细节。

### 面向对象软件开发三个阶段

面向对象软件开发，需要经过三个阶段：

* OOA（面向对象分析）：建立分析模型，文档化
* OOD（面向对象设计）：使用面向对象对OOA细化
* OOP（面向对象编程）：实现OOD
