Skip to content

Commit

Permalink
feat(post): 完善文章
Browse files Browse the repository at this point in the history
  • Loading branch information
李济芝 committed Jun 25, 2024
1 parent d05cc40 commit 951740b
Show file tree
Hide file tree
Showing 9 changed files with 426 additions and 261 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,72 @@ Type erasure ensures that no new classes are created for parameterized types; co
```

#### new Integer(12)与int b=12是否相等
这道题考察的是Integer缓存池。`Integer`缓存池的大小默认为`-128~127`。
所以`Integer`、`int `在 `-127~128`之间是不会创建新的对象的,即
```
Integer a = new Integer(12);
int b = 12;
System.out.println(a==b);//true
```
#### new String("abc")会产生几个对象
答案两个字符串对象,前提是`String`常量池中还没有 "abc" 字符串对象。
第一个对象是"abc",它属于字符串字面量,因此编译时期会在字符串常量池中创建一个字符串对象,指向这个 "abc" 字符串字面量,而使用`new`的方式会在堆中创建一个字符串对象。
来证明一下,到底是不是创建了两个对象,先看一下JDK8 中`new String()`源代码:
```java
/**
* Initializes a newly created {@code String} object so that it represents
* the same sequence of characters as the argument; in other words, the
* newly created string is a copy of the argument string. Unless an
* explicit copy of {@code original} is needed, use of this constructor is
* unnecessary since Strings are immutable.
*
*/
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
```
文档注释大意:初始化新创建的`String`对象,使其表示与实参相同的字符序列,换句话说,新创建的字符串是实参字符串的副本。
除非需要显式复制形参的值,否则没有必要使用这个构造函数,因为字符串是不可变的。

用字节码看一下,创建一个测试类,其`main`方法中使用这种方式来创建字符串对象。
```java
public class MainTest {
public static void main(String[] args) {
String s = new String("abc");
}
}
```

使用`javap -verbose`命令进行反编译,得到以下内容:
```
// ...
Constant pool:
// ...
#2 = Class #18 // java/lang/String
#3 = String #19 // abc
// ...
#18 = Utf8 java/lang/String
#19 = Utf8 abc
// ...
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=2, args_size=1
0: new #2 // class java/lang/String
3: dup
4: ldc #3 // String abc
6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)V
9: astore_1
// ...
```
`Constant Pool`中,`#19`存储这字符串字面量`"abc"``#3``String Pool`的字符串对象,它指向`#19`这个字符串字面量。
`main`方法中,`0:`行使用`new #2`在堆中创建一个字符串对象,并且使用`ldc #3``String Pool`中的字符串对象作为`String`构造函数的参数。
所以能看到使用`new String()`的方式创建字符串是创建两个对象。

### 多线程
#### 说说你对并发编程的理解
Expand Down
35 changes: 15 additions & 20 deletions blog-site/content/posts/java/Java数据类型.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ slug: "rookie-datatype"

## 基本类型
Java语言提供了八种基本类型。六种数值类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型,俗称4类8种。

这里只介绍称4类8种,实际上,Java中还存在另外一种基本类型 `void`,它也有对应的包装类`java.lang.Void`,不过我们无法直接对它们进行操作。

| 类型 | 位数 | 范围 | 默认值 | 示例 | 备注 |
Expand Down Expand Up @@ -56,7 +55,6 @@ JVM 会在编译时期将 boolean 类型的数据转换为 int,使用 1 来表

#### 显式类型转换
显式类型转换也叫强制类型转换,显式类型转换在可能出现数据丢失或溢出的风险的情况下进行,需要使用强制类型转换运算符。

基本类型的显式转换顺序:
```
double -> float -> long -> int -> short -> byte
Expand Down Expand Up @@ -84,7 +82,6 @@ char charValue = (char) intVal;

#### 隐式类型转换
隐式类型转换是指编译器在编译时自动完成的类型转换,也称为自动类型转换。与显示类型转换相反,这种转换通常在没有数据丢失风险的情况下进行。

Java 会自动将较小的基本数据类型转换为较大的基本数据类型,以避免数据丢失。转换顺序如下:
```
byte -> short -> int -> long -> float -> double
Expand Down Expand Up @@ -179,7 +176,7 @@ public static void main(String[]args){

其实自动装箱和自动拆箱是Java中的语法糖,用于简化基本数据类型和其对应包装类型之间的转换操作。
当将基本数据类型赋值给对应的包装类型时,编译器会调用包装类型的`valueOf()`方法来创建一个包装对象,并将基本数据类型的值传递给这个方法;
当需要使用包装类型对象中的值进行基本数据类型的操作时,编译器会自动调用包装类型对象的`xxxValue()`方法,将包装对象转换为对应的基本数据类型值
当需要使用包装类型对象中的值进行基本数据类型的操作时,编译器会自动调用包装类型对象的`xxxValue()`方法,将包装对象转换为对应的基本数据类型值

自动装箱和拆箱虽然方便,但是在使用的时候也需要注意一些问题:
- 比较:包装对象的数值比较,不能简单的使用`==`,虽然 -128 到 127 之间的数字可以,但是这个范围之外还是需要使用 `equals`方法进行比较;
Expand All @@ -201,7 +198,7 @@ public static void main(String[]args){

使用`Integer.valueOf()`方法来创建`Integer`对象时,会优先从缓冲池中获取对象,如果缓冲池中不存在该值的对象,则会创建一个新的对象。
`new Integer(123)`,这种方式会显式地创建一个新的`Integer`对象,每次调用`new Integer(123)`都会创建一个新的对象实例。
```
```java
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y); // false 因为是两个不同的对象实例
Expand All @@ -220,7 +217,7 @@ public static Integer valueOf(int i) {
```

在 Java 8 中,`Integer`缓存池的大小默认为 -128~127,在源码中也能看到具体的实现。
```
```java
static final int low = -128;
static final int high;
static final Integer cache[];
Expand Down Expand Up @@ -259,7 +256,7 @@ Integer n = 123;
System.out.println(m == n); // true
```
所以`Integer``int ``-127~128`之间是不会创建新的对象的,即
```
```java
Integer a = new Integer(12);
int b = 12;
System.out.println(a==b);//true
Expand Down Expand Up @@ -347,7 +344,6 @@ public class Main {

### 对象的比较
在Java中,引用类型的比较涉及到比较对象的引用值,而不是对象的内容。这是因为在Java中,每个对象变量存储的是对象的引用地址,而不是对象本身。

使用`==`操作符比较两个对象引用时,比较的是它们在内存中的地址是否相同,即是否引用同一个对象。
```java
String str1 = new String("hello");
Expand Down Expand Up @@ -458,7 +454,7 @@ public class HelloWorld {
而虚拟机并不关心字节码是有哪种语言编译而来的。

来看一下字节码文件,将下面代码通过`javac`命令编译:
```
```java
public class HelloWorld {
public static void main(String[] args) {
String s = "123";
Expand Down Expand Up @@ -542,7 +538,7 @@ Class常量池是用来保存常量的一个媒介场所,并且是一个中间
而Java中的字节码需要数据支持,通常这种数据会很大以至于不能直接存到字节码里,换另一种方式,可以存到常量池,这个字节码包含了指向常量池的引用,在动态链接的时候会用到运行时常量池。

比如:如下的代码:
```
```java
public class SimpleClass {
public void sayHello() {
System.out.println("hello");
Expand All @@ -563,7 +559,7 @@ public class SimpleClass {

### 为什么JDK9改变了存储结构
在Java8中,`String`内部使用`char`数组存储数据。
```
```java
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
Expand All @@ -572,7 +568,7 @@ public final class String
```

在Java9之后,`String`类的实现改用`byte`数组存储字符串,同时使用 `oder`来标识使用了哪种编码。
```
```java
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
Expand All @@ -588,9 +584,8 @@ public final class String
> The current implementation of the String class stores characters in a char array, using two bytes (sixteen bits) for each character.
Data gathered from many different applications indicates that strings are a major component of heap usage and,
moreover, that most String objects contain only Latin-1 characters. Such characters require only one byte of storage,
hence half of the space in the internal char arrays of such String objects is going unused.
>
>We propose to change the internal representation of the String class from a UTF-16 char array to a byte array plus an encoding-flag field.
hence half of the space in the internal char arrays of such String objects is going unused.<br>
We propose to change the internal representation of the String class from a UTF-16 char array to a byte array plus an encoding-flag field.
The new String class will store characters encoded either as ISO-8859-1/Latin-1 (one byte per character),
or as UTF-16 (two bytes per character), based upon the contents of the string.
The encoding flag will indicate which encoding is used.
Expand All @@ -612,7 +607,7 @@ The encoding flag will indicate which encoding is used.
`String`是Java中一个不可变的类,所以一旦一个string对象在内存中被创建出来,他就无法被修改。

下面代码说明String是不可变的:
```
```java
public class MainTest {
String str = new String("good");
char [] ch = {'t','e','s','t'};
Expand Down Expand Up @@ -647,7 +642,7 @@ public class MainTest {
第一个对象是"abc",它属于字符串字面量,因此编译时期会在字符串常量池中创建一个字符串对象,指向这个 "abc" 字符串字面量,而使用`new`的方式会在堆中创建一个字符串对象。

来证明一下,到底是不是创建了两个对象,先看一下JDK8 中`new String()`源代码:
```
```java
/**
* Initializes a newly created {@code String} object so that it represents
* the same sequence of characters as the argument; in other words, the
Expand Down Expand Up @@ -1064,8 +1059,8 @@ abstract class AbstractStringBuilder implements Appendable, CharSequence {
与`String`类不同的是,`StringBuffer`和`StringBuilder`类的对象能够被多次的修改,并且不产生新的未使用对象。
如果你需要一个可修改的字符串,应该使用`StringBuffer`或者`StringBuilder`,但是会有大量时间浪费在垃圾回收上,因为每次试图修改都有新的`String`对象被创建出来。
通过`StringBuilder`的`append()`方式添加字符串的效率,要远远高于`String`的字符串拼接方法.
在实际开发中还可以进行优化,`StringBuilder` 的空参构造器,默认的字符串容量是16,如果需要存放的数据过多,容量就会进行扩容,我们可以设置默认初始化更大的长度,来减少扩容的次数。
通过`StringBuilder`的`append()`方式添加字符串的效率,要远远高于`String`的字符串拼接方法
在实际开发中还可以进行优化,`StringBuilder`的空参构造器,默认的字符串容量是16,如果需要存放的数据过多,容量就会进行扩容,我们可以设置默认初始化更大的长度,来减少扩容的次数。
如果我们能够确定,前前后后需要添加的字符串不高于某个限定值,那么建议使用构造器创建一个阈值的长度。
#### 常用方法
Expand Down Expand Up @@ -1146,7 +1141,7 @@ ONSTANT_Utf8_info {

所以一个字符串字面量最多可以包含65534个字符,但它的存储空间(字节数)并不会超过65535字节。

所以`String`类长度限制是多少?
那么`String`类长度限制是多少?
- 在编译期间,长度不能超过65535个字符,即最大65534个字符,但65534个字符存储空间不会超过65535字节;
- 在运行期,`String`对象的长度理论上可以达到`Integer.MAX_VALUE (2^31 - 1)`个字符,即 2,147,483,647 个字符,大概4G。
在实际应用中,字符串的最大长度受限于可用内存和JVM配置;
Expand Down
6 changes: 3 additions & 3 deletions blog-site/content/posts/java/Java运算.md
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,7 @@ public static void main(String[] args) {
}
}
```
总的来说正则表达式是对字符串操作的一种逻辑公式,用事先定义好的一些特定字符、及这些特定字符的组合,组成一个"规则字符串",这个"规则字符串"用来表达对字符串的一种过滤逻辑。
总的来说,正则表达式是对字符串操作的一种逻辑公式,用事先定义好的一些特定字符、及这些特定字符的组合,组成一个"规则字符串",这个"规则字符串"用来表达对字符串的一种过滤逻辑。
正则表达式的灵活性、逻辑性和功能性非常的强,可以迅速地用极简单的方式达到字符串的复杂控制,但是对于刚接触的人来说比较晦涩难懂。以下是常用正则表达式:

| 描述 | 正则表达式 |
Expand Down Expand Up @@ -921,7 +921,7 @@ Java在`java.math`包中提供的API类`BigDecimal`,用来对超过16位有效
Java中提供了大数字(超过16位有效位)的操作类,即`java.math.BinInteger`类和`java.math.BigDecimal`类,用于高精度计算。
其中`BigInteger`类是针对大整数的处理类,而`BigDecimal`类则是针对大小数的处理类。`BigDecimal`类的实现用到了`BigIntege`类,不同的是`BigDecimal`加入了小数的概念。

### 基本使用
### 基本用法
`BigDecimal`一共有两种方法可以进行初始化赋值,构造器初始化赋值,`valueOf`方法初始化赋值。
在使用`BigDecimal`中参数为`double`类型的构造器时,发现存储结果并不准确。
```java
Expand Down Expand Up @@ -1158,7 +1158,7 @@ public static void main(String[] args) {
## Math
`java.lang.Math`,该类和Java中的运算息息相关。`Math`类是一个工具类,被`final`修饰,构造方法是私有的,大部分方法都被`public static`修饰。

基本使用
基本用法
```java
public static void main(String[] args) {
// 计算平方根
Expand Down
Loading

0 comments on commit 951740b

Please sign in to comment.