# 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


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yangsx95.gitbook.io/notes/programming-language/java.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
