Skip to content

Commit 455d88c

Browse files
author
hollis.zhl
committed
增加部分链接
1 parent 5f2e51a commit 455d88c

File tree

6 files changed

+772
-5
lines changed

6 files changed

+772
-5
lines changed

README.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,23 +58,23 @@ Java的继承与实现
5858

5959
#### 自动拆装箱
6060

61-
什么是包装类型、什么是基本类型、什么是自动拆装箱
61+
[什么是包装类型、什么是基本类型、什么是自动拆装箱](/basics/java-basic/boxing-unboxing.md)
6262

63-
Integer的缓存机制
63+
[Integer的缓存机制](/basics/java-basic/integer-cache.md)
6464

6565
#### String
6666

67-
字符串的不可变性
67+
[字符串的不可变性](/basics/java-basic/final-string.md)
6868

69-
JDK 6和JDK 7中substring的原理及区别
69+
[JDK 6和JDK 7中substring的原理及区别](/basics/java-basic/substring.md)
7070

7171
replaceFirst、replaceAll、replace区别、
7272

7373
String对“+”的重载、字符串拼接的几种方式和区别
7474

7575
String.valueOf和Integer.toString的区别、
7676

77-
switch对String的支持
77+
[switch对String的支持](/basics/java-basic/switch-string.md)
7878

7979
字符串池、常量池(运行时常量池、Class常量池)、intern
8080

basics/java-basic/boxing-unboxing.md

Lines changed: 324 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,324 @@
1+
本文主要介绍Java中的自动拆箱与自动装箱的有关知识。
2+
3+
## 基本数据类型
4+
5+
基本类型,或者叫做内置类型,是Java中不同于类(Class)的特殊类型。它们是我们编程中使用最频繁的类型。
6+
7+
Java是一种强类型语言,第一次申明变量必须说明数据类型,第一次变量赋值称为变量的初始化。
8+
9+
Java基本类型共有八种,基本类型可以分为三类:
10+
11+
> 字符类型`char`
12+
>
13+
> 布尔类型`boolean`
14+
>
15+
> 数值类型`byte``short``int``long``float``double`
16+
17+
数值类型又可以分为整数类型`byte``short``int``long`和浮点数类型`float``double`
18+
19+
Java中的数值类型不存在无符号的,它们的取值范围是固定的,不会随着机器硬件环境或者操作系统的改变而改变。
20+
21+
实际上,Java中还存在另外一种基本类型`void`,它也有对应的包装类 `java.lang.Void`,不过我们无法直接对它们进行操作。
22+
23+
### 基本数据类型有什么好处
24+
25+
我们都知道在Java语言中,`new`一个对象是存储在堆里的,我们通过栈中的引用来使用这些对象;所以,对象本身来说是比较消耗资源的。
26+
27+
对于经常用到的类型,如int等,如果我们每次使用这种变量的时候都需要new一个Java对象的话,就会比较笨重。所以,和C++一样,Java提供了基本数据类型,这种数据的变量不需要使用new创建,他们不会在堆上创建,而是直接在栈内存中存储,因此会更加高效。
28+
29+
### 整型的取值范围
30+
31+
Java中的整型主要包含`byte``short``int``long`这四种,表示的数字范围也是从小到大的,之所以表示范围不同主要和他们存储数据时所占的字节数有关。
32+
33+
先来个简答的科普,1字节=8位(bit)。java中的整型属于有符号数。
34+
35+
先来看计算中8bit可以表示的数字:
36+
37+
最小值:10000000 (-128)(-2^7)
38+
最大值:01111111(127)(2^7-1)
39+
40+
41+
整型的这几个类型中,
42+
43+
* byte:byte用1个字节来存储,范围为-128(-2^7)到127(2^7-1),在变量初始化的时候,byte类型的默认值为0。
44+
45+
* short:short用2个字节存储,范围为-32,768 (-2^15)到32,767 (2^15-1),在变量初始化的时候,short类型的默认值为0,一般情况下,因为Java本身转型的原因,可以直接写为0。
46+
47+
* int:int用4个字节存储,范围为-2,147,483,648 (-2^31)到2,147,483,647 (2^31-1),在变量初始化的时候,int类型的默认值为0。
48+
49+
* long:long用8个字节存储,范围为-9,223,372,036,854,775,808 (-2^63)到9,223,372,036, 854,775,807 (2^63-1),在变量初始化的时候,long类型的默认值为0L或0l,也可直接写为0。
50+
51+
### 超出范围怎么办
52+
53+
上面说过了,整型中,每个类型都有一定的表示范围,但是,在程序中有些计算会导致超出表示范围,即溢出。如以下代码:
54+
55+
int i = Integer.MAX_VALUE;
56+
int j = Integer.MAX_VALUE;
57+
58+
int k = i + j;
59+
System.out.println("i (" + i + ") + j (" + j + ") = k (" + k + ")");
60+
61+
62+
输出结果:i (2147483647) + j (2147483647) = k (-2)
63+
64+
**这就是发生了溢出,溢出的时候并不会抛异常,也没有任何提示。**所以,在程序中,使用同类型的数据进行运算的时候,**一定要注意数据溢出的问题。**
65+
66+
## 包装类型
67+
68+
Java语言是一个面向对象的语言,但是Java中的基本数据类型却是不面向对象的,这在实际使用时存在很多的不便,为了解决这个不足,在设计类时为每个基本数据类型设计了一个对应的类进行代表,这样八个和基本数据类型对应的类统称为包装类(Wrapper Class)。
69+
70+
包装类均位于java.lang包,包装类和基本数据类型的对应关系如下表所示
71+
72+
| 基本数据类型 | 包装类 |
73+
| ------- | --------- |
74+
| byte | Byte |
75+
| boolean | Boolean |
76+
| short | Short |
77+
| char | Character |
78+
| int | Integer |
79+
| long | Long |
80+
| float | Float |
81+
| double | Double |
82+
83+
在这八个类名中,除了Integer和Character类以后,其它六个类的类名和基本数据类型一致,只是类名的第一个字母大写即可。
84+
85+
### 为什么需要包装类
86+
87+
很多人会有疑问,既然Java中为了提高效率,提供了八种基本数据类型,为什么还要提供包装类呢?
88+
89+
这个问题,其实前面已经有了答案,因为Java是一种面向对象语言,很多地方都需要使用对象而不是基本数据类型。比如,在集合类中,我们是无法将int 、double等类型放进去的。因为集合的容器要求元素是Object类型。
90+
91+
为了让基本类型也具有对象的特征,就出现了包装类型,它相当于将基本类型“包装起来”,使得它具有了对象的性质,并且为其添加了属性和方法,丰富了基本类型的操作。
92+
93+
## 拆箱与装箱
94+
95+
那么,有了基本数据类型和包装类,肯定有些时候要在他们之间进行转换。比如把一个基本数据类型的int转换成一个包装类型的Integer对象。
96+
97+
我们认为包装类是对基本类型的包装,所以,把基本数据类型转换成包装类的过程就是打包装,英文对应于boxing,中文翻译为装箱。
98+
99+
反之,把包装类转换成基本数据类型的过程就是拆包装,英文对应于unboxing,中文翻译为拆箱。
100+
101+
在Java SE5之前,要进行装箱,可以通过以下代码:
102+
103+
Integer i = new Integer(10);
104+
105+
106+
## 自动拆箱与自动装箱
107+
108+
在Java SE5中,为了减少开发人员的工作,Java提供了自动拆箱与自动装箱功能。
109+
110+
自动装箱: 就是将基本数据类型自动转换成对应的包装类。
111+
112+
自动拆箱:就是将包装类自动转换成对应的基本数据类型。
113+
114+
Integer i =10; //自动装箱
115+
int b= i; //自动拆箱
116+
117+
118+
`Integer i=10` 可以替代 `Integer i = new Integer(10);`,这就是因为Java帮我们提供了自动装箱的功能,不需要开发者手动去new一个Integer对象。
119+
120+
## 自动装箱与自动拆箱的实现原理
121+
122+
既然Java提供了自动拆装箱的能力,那么,我们就来看一下,到底是什么原理,Java是如何实现的自动拆装箱功能。
123+
124+
我们有以下自动拆装箱的代码:
125+
126+
public static void main(String[]args){
127+
Integer integer=1; //装箱
128+
int i=integer; //拆箱
129+
}
130+
131+
132+
对以上代码进行反编译后可以得到以下代码:
133+
134+
public static void main(String[]args){
135+
Integer integer=Integer.valueOf(1);
136+
int i=integer.intValue();
137+
}
138+
139+
140+
从上面反编译后的代码可以看出,int的自动装箱都是通过`Integer.valueOf()`方法来实现的,Integer的自动拆箱都是通过`integer.intValue`来实现的。如果读者感兴趣,可以试着将八种类型都反编译一遍 ,你会发现以下规律:
141+
142+
> 自动装箱都是通过包装类的`valueOf()`方法来实现的.自动拆箱都是通过包装类对象的`xxxValue()`来实现的。
143+
144+
## 哪些地方会自动拆装箱
145+
146+
我们了解过原理之后,在来看一下,什么情况下,Java会帮我们进行自动拆装箱。前面提到的变量的初始化和赋值的场景就不介绍了,那是最简单的也最容易理解的。
147+
148+
我们主要来看一下,那些可能被忽略的场景。
149+
150+
### 场景一、将基本数据类型放入集合类
151+
152+
我们知道,Java中的集合类只能接收对象类型,那么以下代码为什么会不报错呢?
153+
154+
List<Integer> li = new ArrayList<>();
155+
for (int i = 1; i < 50; i ++){
156+
li.add(i);
157+
}
158+
159+
160+
将上面代码进行反编译,可以得到以下代码:
161+
162+
List<Integer> li = new ArrayList<>();
163+
for (int i = 1; i < 50; i += 2){
164+
li.add(Integer.valueOf(i));
165+
}
166+
167+
168+
以上,我们可以得出结论,当我们把基本数据类型放入集合类中的时候,会进行自动装箱。
169+
170+
### 场景二、包装类型和基本类型的大小比较
171+
172+
有没有人想过,当我们对Integer对象与基本类型进行大小比较的时候,实际上比较的是什么内容呢?看以下代码:
173+
174+
Integer a=1;
175+
System.out.println(a==1?"等于":"不等于");
176+
Boolean bool=false;
177+
System.out.println(bool?"真":"假");
178+
179+
180+
对以上代码进行反编译,得到以下代码:
181+
182+
Integer a=1;
183+
System.out.println(a.intValue()==1?"等于":"不等于");
184+
Boolean bool=false;
185+
System.out.println(bool.booleanValue?"真":"假");
186+
187+
188+
可以看到,包装类与基本数据类型进行比较运算,是先将包装类进行拆箱成基本数据类型,然后进行比较的。
189+
190+
### 场景三、包装类型的运算
191+
192+
有没有人想过,当我们对Integer对象进行四则运算的时候,是如何进行的呢?看以下代码:
193+
194+
Integer i = 10;
195+
Integer j = 20;
196+
197+
System.out.println(i+j);
198+
199+
200+
反编译后代码如下:
201+
202+
Integer i = Integer.valueOf(10);
203+
Integer j = Integer.valueOf(20);
204+
System.out.println(i.intValue() + j.intValue());
205+
206+
207+
我们发现,两个包装类型之间的运算,会被自动拆箱成基本类型进行。
208+
209+
### 场景四、三目运算符的使用
210+
211+
这是很多人不知道的一个场景,作者也是一次线上的血淋淋的Bug发生后才了解到的一种案例。看一个简单的三目运算符的代码:
212+
213+
boolean flag = true;
214+
Integer i = 0;
215+
int j = 1;
216+
int k = flag ? i : j;
217+
218+
219+
很多人不知道,其实在`int k = flag ? i : j;`这一行,会发生自动拆箱。反编译后代码如下:
220+
221+
boolean flag = true;
222+
Integer i = Integer.valueOf(0);
223+
int j = 1;
224+
int k = flag ? i.intValue() : j;
225+
System.out.println(k);
226+
227+
228+
这其实是三目运算符的语法规范。当第二,第三位操作数分别为基本类型和对象时,其中的对象就会拆箱为基本类型进行操作。
229+
230+
因为例子中,`flag ? i : j;`片段中,第二段的i是一个包装类型的对象,而第三段的j是一个基本类型,所以会对包装类进行自动拆箱。如果这个时候i的值为`null`,那么久会发生NPE。([自动拆箱导致空指针异常][1]
231+
232+
### 场景五、函数参数与返回值
233+
234+
这个比较容易理解,直接上代码了:
235+
236+
//自动拆箱
237+
public int getNum1(Integer num) {
238+
return num;
239+
}
240+
//自动装箱
241+
public Integer getNum2(int num) {
242+
return num;
243+
}
244+
245+
246+
## 自动拆装箱与缓存
247+
248+
Java SE的自动拆装箱还提供了一个和缓存有关的功能,我们先来看以下代码,猜测一下输出结果:
249+
250+
public static void main(String... strings) {
251+
252+
Integer integer1 = 3;
253+
Integer integer2 = 3;
254+
255+
if (integer1 == integer2)
256+
System.out.println("integer1 == integer2");
257+
else
258+
System.out.println("integer1 != integer2");
259+
260+
Integer integer3 = 300;
261+
Integer integer4 = 300;
262+
263+
if (integer3 == integer4)
264+
System.out.println("integer3 == integer4");
265+
else
266+
System.out.println("integer3 != integer4");
267+
268+
}
269+
270+
271+
我们普遍认为上面的两个判断的结果都是false。虽然比较的值是相等的,但是由于比较的是对象,而对象的引用不一样,所以会认为两个if判断都是false的。在Java中,==比较的是对象应用,而equals比较的是值。所以,在这个例子中,不同的对象有不同的引用,所以在进行比较的时候都将返回false。奇怪的是,这里两个类似的if条件判断返回不同的布尔值。
272+
273+
上面这段代码真正的输出结果:
274+
275+
integer1 == integer2
276+
integer3 != integer4
277+
278+
279+
原因就和Integer中的缓存机制有关。在Java 5中,在Integer的操作上引入了一个新功能来节省内存和提高性能。整型对象通过使用相同的对象引用实现了缓存和重用。
280+
281+
> 适用于整数值区间-128 至 +127。
282+
>
283+
> 只适用于自动装箱。使用构造函数创建对象不适用。
284+
285+
具体的代码实现可以阅读[Java中整型的缓存机制][2]一文,这里不再阐述。
286+
287+
我们只需要知道,当需要进行自动装箱时,如果数字在-128至127之间时,会直接使用缓存中的对象,而不是重新创建一个对象。
288+
289+
其中的javadoc详细的说明了缓存支持-128到127之间的自动装箱过程。最大值127可以通过`-XX:AutoBoxCacheMax=size`修改。
290+
291+
实际上这个功能在Java 5中引入的时候,范围是固定的-128 至 +127。后来在Java 6中,可以通过`java.lang.Integer.IntegerCache.high`设置最大值。
292+
293+
这使我们可以根据应用程序的实际情况灵活地调整来提高性能。到底是什么原因选择这个-128到127范围呢?因为这个范围的数字是最被广泛使用的。 在程序中,第一次使用Integer的时候也需要一定的额外时间来初始化这个缓存。
294+
295+
在Boxing Conversion部分的Java语言规范(JLS)规定如下:
296+
297+
如果一个变量p的值是:
298+
299+
-128至127之间的整数(§3.10.1)
300+
301+
true 和 false的布尔值 (§3.10.3)
302+
303+
‘\u0000’至 ‘\u007f’之间的字符(§3.10.4)
304+
305+
306+
范围内的时,将p包装成a和b两个对象时,可以直接使用a==b判断a和b的值是否相等。
307+
308+
## 自动拆装箱带来的问题
309+
310+
当然,自动拆装箱是一个很好的功能,大大节省了开发人员的精力,不再需要关心到底什么时候需要拆装箱。但是,他也会引入一些问题。
311+
312+
> 包装对象的数值比较,不能简单的使用`==`,虽然-128到127之间的数字可以,但是这个范围之外还是需要使用`equals`比较。
313+
>
314+
> 前面提到,有些场景会进行自动拆装箱,同时也说过,由于自动拆箱,如果包装类对象为null,那么自动拆箱时就有可能抛出NPE。
315+
>
316+
> 如果一个for循环中有大量拆装箱操作,会浪费很多资源。
317+
318+
## 参考资料
319+
320+
[Java的自动拆装箱][3]
321+
322+
[1]: http://www.hollischuang.com/archives/435
323+
[2]: http://www.hollischuang.com/archives/1174
324+
[3]: https://www.jianshu.com/p/cc9312104876

basics/java-basic/final-string.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
* * *
3+
4+
## 定义一个字符串
5+
6+
String s = "abcd";
7+
8+
9+
![String-Immutability-1][1]
10+
11+
`s`中保存了string对象的引用。下面的箭头可以理解为“存储他的引用”。
12+
13+
## 使用变量来赋值变量
14+
15+
String s2 = s;
16+
17+
18+
![String-Immutability-2][2]
19+
20+
s2保存了相同的引用值,因为他们代表同一个对象。
21+
22+
## 字符串连接
23+
24+
s = s.concat("ef");
25+
26+
27+
![string-immutability][3]
28+
29+
`s`中保存的是一个重新创建出来的string对象的引用。
30+
31+
## 总结
32+
33+
一旦一个string对象在内存(堆)中被创建出来,他就无法被修改。特别要注意的是,String类的所有方法都没有改变字符串本身的值,都是返回了一个新的对象。
34+
35+
如果你需要一个可修改的字符串,应该使用StringBuffer 或者 StringBuilder。否则会有大量时间浪费在垃圾回收上,因为每次试图修改都有新的string对象被创建出来。
36+
37+
[1]: http://www.programcreek.com/wp-content/uploads/2009/02/String-Immutability-1.jpeg
38+
[2]: http://www.programcreek.com/wp-content/uploads/2009/02/String-Immutability-2.jpeg
39+
[3]: http://www.programcreek.com/wp-content/uploads/2009/02/string-immutability-650x279.jpeg

0 commit comments

Comments
 (0)