Skip to content

Commit f3e94c1

Browse files
author
hollis.zhl
committed
Merge branch 'master' of github.com:hollischuang/toBeTopJavaer
2 parents 517004a + 31fb3cd commit f3e94c1

4 files changed

Lines changed: 57 additions & 58 deletions

File tree

basics/java-basic/boxing-unboxing.md

Lines changed: 50 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,13 @@ Java中的整型主要包含`byte`、`short`、`int`和`long`这四种,表示
5151
### 超出范围怎么办
5252

5353
上面说过了,整型中,每个类型都有一定的表示范围,但是,在程序中有些计算会导致超出表示范围,即溢出。如以下代码:
54+
```java
55+
int i = Integer.MAX_VALUE;
56+
int j = Integer.MAX_VALUE;
5457

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-
58+
int k = i + j;
59+
System.out.println("i (" + i + ") + j (" + j + ") = k (" + k + ")");
60+
```
6161

6262
输出结果:i (2147483647) + j (2147483647) = k (-2)
6363

@@ -99,9 +99,9 @@ Java语言是一个面向对象的语言,但是Java中的基本数据类型却
9999
反之,把包装类转换成基本数据类型的过程就是拆包装,英文对应于unboxing,中文翻译为拆箱。
100100

101101
在Java SE5之前,要进行装箱,可以通过以下代码:
102-
102+
```java
103103
Integer i = new Integer(10);
104-
104+
```
105105

106106
## 自动拆箱与自动装箱
107107

@@ -110,10 +110,10 @@ Java语言是一个面向对象的语言,但是Java中的基本数据类型却
110110
自动装箱: 就是将基本数据类型自动转换成对应的包装类。
111111

112112
自动拆箱:就是将包装类自动转换成对应的基本数据类型。
113-
113+
```java
114114
Integer i =10; //自动装箱
115115
int b= i; //自动拆箱
116-
116+
```
117117

118118
`Integer i=10` 可以替代 `Integer i = new Integer(10);`,这就是因为Java帮我们提供了自动装箱的功能,不需要开发者手动去new一个Integer对象。
119119

@@ -122,20 +122,20 @@ Java语言是一个面向对象的语言,但是Java中的基本数据类型却
122122
既然Java提供了自动拆装箱的能力,那么,我们就来看一下,到底是什么原理,Java是如何实现的自动拆装箱功能。
123123

124124
我们有以下自动拆装箱的代码:
125-
125+
```java
126126
public static void main(String[]args){
127127
Integer integer=1; //装箱
128128
int i=integer; //拆箱
129129
}
130-
130+
```
131131

132132
对以上代码进行反编译后可以得到以下代码:
133-
133+
```java
134134
public static void main(String[]args){
135135
Integer integer=Integer.valueOf(1);
136136
int i=integer.intValue();
137137
}
138-
138+
```
139139

140140
从上面反编译后的代码可以看出,int的自动装箱都是通过`Integer.valueOf()`方法来实现的,Integer的自动拆箱都是通过`integer.intValue`来实现的。如果读者感兴趣,可以试着将八种类型都反编译一遍 ,你会发现以下规律:
141141

@@ -150,89 +150,89 @@ Java语言是一个面向对象的语言,但是Java中的基本数据类型却
150150
### 场景一、将基本数据类型放入集合类
151151

152152
我们知道,Java中的集合类只能接收对象类型,那么以下代码为什么会不报错呢?
153-
153+
```java
154154
List<Integer> li = new ArrayList<>();
155155
for (int i = 1; i < 50; i ++){
156156
li.add(i);
157157
}
158-
158+
```
159159

160160
将上面代码进行反编译,可以得到以下代码:
161-
161+
```java
162162
List<Integer> li = new ArrayList<>();
163163
for (int i = 1; i < 50; i += 2){
164164
li.add(Integer.valueOf(i));
165165
}
166-
166+
```
167167

168168
以上,我们可以得出结论,当我们把基本数据类型放入集合类中的时候,会进行自动装箱。
169169

170170
### 场景二、包装类型和基本类型的大小比较
171171

172172
有没有人想过,当我们对Integer对象与基本类型进行大小比较的时候,实际上比较的是什么内容呢?看以下代码:
173-
174-
Integer a=1;
175-
System.out.println(a==1?"等于":"不等于");
176-
Boolean bool=false;
177-
System.out.println(bool?"真":"假");
178-
173+
```java
174+
Integer a=1;
175+
System.out.println(a==1?"等于":"不等于");
176+
Boolean bool=false;
177+
System.out.println(bool?"":"");
178+
```
179179

180180
对以上代码进行反编译,得到以下代码:
181-
182-
Integer a=1;
183-
System.out.println(a.intValue()==1?"等于":"不等于");
184-
Boolean bool=false;
185-
System.out.println(bool.booleanValue?"真":"假");
186-
181+
```java
182+
Integer a=1;
183+
System.out.println(a.intValue()==1?"等于":"不等于");
184+
Boolean bool=false;
185+
System.out.println(bool.booleanValue?"":"");
186+
```
187187

188188
可以看到,包装类与基本数据类型进行比较运算,是先将包装类进行拆箱成基本数据类型,然后进行比较的。
189189

190190
### 场景三、包装类型的运算
191191

192192
有没有人想过,当我们对Integer对象进行四则运算的时候,是如何进行的呢?看以下代码:
193+
```java
194+
Integer i = 10;
195+
Integer j = 20;
193196

194-
Integer i = 10;
195-
Integer j = 20;
196-
197-
System.out.println(i+j);
198-
197+
System.out.println(i+j);
198+
```
199199

200200
反编译后代码如下:
201-
202-
Integer i = Integer.valueOf(10);
203-
Integer j = Integer.valueOf(20);
204-
System.out.println(i.intValue() + j.intValue());
205-
201+
```java
202+
Integer i = Integer.valueOf(10);
203+
Integer j = Integer.valueOf(20);
204+
System.out.println(i.intValue() + j.intValue());
205+
```
206206

207207
我们发现,两个包装类型之间的运算,会被自动拆箱成基本类型进行。
208208

209209
### 场景四、三目运算符的使用
210210

211211
这是很多人不知道的一个场景,作者也是一次线上的血淋淋的Bug发生后才了解到的一种案例。看一个简单的三目运算符的代码:
212-
212+
```java
213213
boolean flag = true;
214214
Integer i = 0;
215215
int j = 1;
216216
int k = flag ? i : j;
217-
217+
```
218218

219219
很多人不知道,其实在`int k = flag ? i : j;`这一行,会发生自动拆箱。反编译后代码如下:
220-
220+
```java
221221
boolean flag = true;
222222
Integer i = Integer.valueOf(0);
223223
int j = 1;
224224
int k = flag ? i.intValue() : j;
225225
System.out.println(k);
226-
226+
```
227227

228228
这其实是三目运算符的语法规范。当第二,第三位操作数分别为基本类型和对象时,其中的对象就会拆箱为基本类型进行操作。
229229

230-
因为例子中,`flag ? i : j;`片段中,第二段的i是一个包装类型的对象,而第三段的j是一个基本类型,所以会对包装类进行自动拆箱。如果这个时候i的值为`null`那么久会发生NPE。([自动拆箱导致空指针异常][1]
230+
因为例子中,`flag ? i : j;`片段中,第二段的i是一个包装类型的对象,而第三段的j是一个基本类型,所以会对包装类进行自动拆箱。如果这个时候i的值为`null`那么就会发生NPE。([自动拆箱导致空指针异常][1]
231231

232232
### 场景五、函数参数与返回值
233233

234234
这个比较容易理解,直接上代码了:
235-
235+
```java
236236
//自动拆箱
237237
public int getNum1(Integer num) {
238238
return num;
@@ -241,12 +241,12 @@ Java语言是一个面向对象的语言,但是Java中的基本数据类型却
241241
public Integer getNum2(int num) {
242242
return num;
243243
}
244-
244+
```
245245

246246
## 自动拆装箱与缓存
247247

248248
Java SE的自动拆装箱还提供了一个和缓存有关的功能,我们先来看以下代码,猜测一下输出结果:
249-
249+
```java
250250
public static void main(String... strings) {
251251

252252
Integer integer1 = 3;
@@ -264,9 +264,8 @@ Java SE的自动拆装箱还提供了一个和缓存有关的功能,我们先
264264
System.out.println("integer3 == integer4");
265265
else
266266
System.out.println("integer3 != integer4");
267-
268267
}
269-
268+
```
270269

271270
我们普遍认为上面的两个判断的结果都是false。虽然比较的值是相等的,但是由于比较的是对象,而对象的引用不一样,所以会认为两个if判断都是false的。在Java中,==比较的是对象应用,而equals比较的是值。所以,在这个例子中,不同的对象有不同的引用,所以在进行比较的时候都将返回false。奇怪的是,这里两个类似的if条件判断返回不同的布尔值。
272271

@@ -321,4 +320,4 @@ Java SE的自动拆装箱还提供了一个和缓存有关的功能,我们先
321320

322321
[1]: http://www.hollischuang.com/archives/435
323322
[2]: http://www.hollischuang.com/archives/1174
324-
[3]: https://www.jianshu.com/p/cc9312104876
323+
[3]: https://www.jianshu.com/p/cc9312104876

basics/java-basic/inheritance-composition.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,11 @@ Java是一个面向对象的语言。每一个学习过Java的人都知道,封
5757

5858
> 继承要慎用,其使用场合仅限于你确信使用该技术有效的情况。一个判断方法是,问一问自己是否需要从新类向基类进行向上转型。如果是必须的,则继承是必要的。反之则应该好好考虑是否需要继承。《[Java编程思想][5]
5959
>
60-
> 只有当子类真正是超类的子类型时,才适合用继承。换句话说,对于两个类A和B,只有当两者之间确实存在[`is-a`][1]关系的时候,类B才应该继续类A。《[Effective Java][6]
60+
> 只有当子类真正是超类的子类型时,才适合用继承。换句话说,对于两个类A和B,只有当两者之间确实存在[`is-a`][1]关系的时候,类B才应该继承类A。《[Effective Java][6]
6161
6262
[1]: https://zh.wikipedia.org/wiki/Is-a
6363
[2]: http://www.hollischuang.com/wp-content/uploads/2016/03/Generalization.jpg
6464
[3]: https://en.wikipedia.org/wiki/Has-a
6565
[4]: http://www.hollischuang.com/wp-content/uploads/2016/03/Composition.jpg
6666
[5]: http://s.click.taobao.com/t?e=m%3D2%26s%3DHzJzud6zOdocQipKwQzePOeEDrYVVa64K7Vc7tFgwiHjf2vlNIV67vo5P8BMUBgoEC56fBbgyn5pS4hLH%2FP02ckKYNRBWOBBey11vvWwHXSniyi5vWXIZhtlrJbLMDAQihpQCXu2JnPFYKQlNeOGCsYMXU3NNCg%2F&pvid=10_125.119.86.125_222_1458652212179
67-
[6]: http://s.click.taobao.com/t?e=m%3D2%26s%3DwIPn8%2BNPqLwcQipKwQzePOeEDrYVVa64K7Vc7tFgwiHjf2vlNIV67vo5P8BMUBgoUOZr0mLjusdpS4hLH%2FP02ckKYNRBWOBBey11vvWwHXSniyi5vWXIZvgXwmdyquYbNLnO%2BjzYQLqKnzbV%2FMLqnMYMXU3NNCg%2F&pvid=10_125.119.86.125_345_1458652241780
67+
[6]: http://s.click.taobao.com/t?e=m%3D2%26s%3DwIPn8%2BNPqLwcQipKwQzePOeEDrYVVa64K7Vc7tFgwiHjf2vlNIV67vo5P8BMUBgoUOZr0mLjusdpS4hLH%2FP02ckKYNRBWOBBey11vvWwHXSniyi5vWXIZvgXwmdyquYbNLnO%2BjzYQLqKnzbV%2FMLqnMYMXU3NNCg%2F&pvid=10_125.119.86.125_345_1458652241780

basics/java-basic/switch-string.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Java 7中,switch的参数可以是String类型了,这对我们来说是一个很方便的改进。到目前为止switch支持这样几种数据类型:`byte` `short` `int` `char` `String` 。但是,作为一个程序员我们不仅要知道他有多么好用,还要知道它是如何实现的,witch对整型的支持是怎么实现的呢?对字符型是怎么实现的呢?String类型呢?有一点Java开发经验的人这个时候都会猜测switch对String的支持是使用equals()方法和hashcode()方法。那么到底是不是这两个方法呢?接下来我们就看一下,switch到底是如何实现的。
1+
Java 7中,switch的参数可以是String类型了,这对我们来说是一个很方便的改进。到目前为止switch支持这样几种数据类型:`byte` `short` `int` `char` `String` 。但是,作为一个程序员我们不仅要知道他有多么好用,还要知道它是如何实现的,switch对整型的支持是怎么实现的呢?对字符型是怎么实现的呢?String类型呢?有一点Java开发经验的人这个时候都会猜测switch对String的支持是使用equals()方法和hashcode()方法。那么到底是不是这两个方法呢?接下来我们就看一下,switch到底是如何实现的。
22

33
<!--more-->
44

@@ -145,6 +145,6 @@ Java 7中,switch的参数可以是String类型了,这对我们来说是一
145145
}
146146

147147

148-
看到这个代码,你知道原来字符串的switch是通过`equals()`和`hashCode()`方法来实现的。**记住,switch中只能使用整型**,比如`byte`。`short`,`char`(ackii码是整型)以及`int`。还好`hashCode()`方法返回的是`int`,而不是`long`。通过这个很容易记住`hashCode`返回的是`int`这个事实。仔细看下可以发现,进行`switch`的实际是哈希值,然后通过使用equals方法比较进行安全检查,这个检查是必要的,因为哈希可能会发生碰撞。因此它的性能是不如使用枚举进行switch或者使用纯整数常量,但这也不是很差。因为Java编译器只增加了一个`equals`方法,如果你比较的是字符串字面量的话会非常快,比如”abc” ==”abc”。如果你把`hashCode()`方法的调用也考虑进来了,那么还会再多一次的调用开销,因为字符串一旦创建了,它就会把哈希值缓存起来。因此如果这个`siwtch`语句是用在一个循环里的,比如逐项处理某个值,或者游戏引擎循环地渲染屏幕,这里`hashCode()`方法的调用开销其实不会很大。
148+
看到这个代码,你知道原来字符串的switch是通过`equals()`和`hashCode()`方法来实现的。**记住,switch中只能使用整型**,比如`byte`。`short`,`char`(ackii码是整型)以及`int`。还好`hashCode()`方法返回的是`int`,而不是`long`。通过这个很容易记住`hashCode`返回的是`int`这个事实。仔细看下可以发现,进行`switch`的实际是哈希值,然后通过使用equals方法比较进行安全检查,这个检查是必要的,因为哈希可能会发生碰撞。因此它的性能是不如使用枚举进行switch或者使用纯整数常量,但这也不是很差。因为Java编译器只增加了一个`equals`方法,如果你比较的是字符串字面量的话会非常快,比如”abc” ==”abc”。如果你把`hashCode()`方法的调用也考虑进来了,那么还会再多一次的调用开销,因为字符串一旦创建了,它就会把哈希值缓存起来。因此如果这个`switch`语句是用在一个循环里的,比如逐项处理某个值,或者游戏引擎循环地渲染屏幕,这里`hashCode()`方法的调用开销其实不会很大。
149149

150150
好,以上就是关于switch对整型、字符型、和字符串型的支持的实现方式,总结一下我们可以发现,**其实swich只支持一种数据类型,那就是整型,其他数据类型都是转换成整型之后在使用switch的。**

basics/java-basic/variable.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
### 类变量、成员变量和局部变量
22

33
Java中共有三种变量,分别是类变量、成员变量和局部变量。他们分别存放在JVM的方法区、堆内存和栈内存中。
4-
4+
```java
55
/**
66
* @author Hollis
77
*/
@@ -25,5 +25,5 @@ Java中共有三种变量,分别是类变量、成员变量和局部变量。
2525
int d;
2626
}
2727
}
28-
29-
上面定义的三个变量中,变量a就是类变量,变量b就是成员变量,而变量c和d是局部变量。
28+
```
29+
上面定义的三个变量中,变量a就是类变量,变量b就是成员变量,而变量c和d是局部变量。

0 commit comments

Comments
 (0)