Java语言假设程序员只进行面向对象编程, 因此Java中(几乎)万物皆对象.
目录
Chapter3 万物皆对象
如果我们说另一种不同的语言, 我们会发觉一个不同的世界! —— Ludwig Wittgenstein(1889-1951)
对象操纵
所有的编程语言都会操纵内存中的元素.
- C/C++中对象的操纵是通过指针来完成的.
- Java利用万物皆对象的思想和单一一致的语法方式来简化问题, 采用引用(Reference) 操纵对象.
对象创建
Java中使用引用关联对象.
通常使用关键字new
创建对象实例.
1 | String s = new String("hello world"); |
数据存储
- 寄存器(Registers): 最快的存储器, 位于CPU内部. 但数量有限, 且Java无法直接控制(C/C++允许开发者像编译器建议寄存器的分配).
- 栈内存(Stack): 位于随机访问存储器(Random Access Memory, RAM) 中, CPU可通过栈指针直接访问. 栈指针下移分配内存, 上移释放内存. 但栈上的数据要求提前知晓对象生存周期, 因此栈内存上只存放部分Java数据, 对象本身存储在堆内存.
- 堆内存(Heap): RAM区域的一种通用内存池, 存放着所有的Java对象. 相较于栈区, 堆区的使用更灵活, 但代价是分配和清理对象会比栈区更耗时.
- 常量存储(COnstant Storage): 通常直接放在代码中, 可考虑置于只读存储器(Read Only Memory, ROM).
- 非RAM存储(Non-RAM Storage): 数据存在于程序之外, 包括序列化对象和持久化对象.
基本类型存储
Java中使用和C/C++一样策略的基本类型数据, 存放在栈中, 其内存占用大小固定.
基本类型 | 大小 | 最小值 | 最大值 | 包装类型 |
---|---|---|---|---|
boolean | --- | --- | --- | Boolean |
char | 16bits | \(Unicode\ 0\) | \(Unicode\ 2^{16}-1\) | Character |
byte | 8bits | \(-128\) | \(+127\) | Byte |
short | 16bits | \(-2^{15}\) | \(+2^{15}-1\) | Short |
int | 32bits | \(-2^{31}\) | \(+2^{31}-1\) | Integer |
long | 64bits | \(-2^{63}\) | \(+2^{63}-1\) | Long |
float | 32bits | \(IEEE754\) | \(IEEE754\) | Float |
double | 64bits | \(IEEE754\) | \(IEEE754\) | Double |
void | --- | --- | --- | Void |
在堆区表示基本类型数据, 需要使用包装类:
1 | char c = 'x'; |
高精度数值
BigInteger
& BigDecimal
数组存储
Java设计主要目标之一是安全性, 尽管牺牲了部分效率, 但换来了安全性.
代码注释
1 | /* |
对象清理
作用域
C/C++和Java中的作用域由{}
决定.
1 | { |
需要注意的是, 以下操作在Java中非法:
1 | { |
Tips: 尽管作者在书中如此说明, 但在新版的Java中(笔者使用为JDK17), 该操作是允许的, 仅仅是IDE给出一个提示.
对象作用域
Java中的对象与基本类型具有不同的生命周期.
1 | { |
Java的内存回收不同于C++, 采用的是垃圾收集器机制, 通过不可达的判断回收对象. 可以有效避免内存泄漏问题.
类的创建
类型
Java中使用class
关键字描述新对象.
1 | class Example{ |
字段(Field)
字段可以是基本类型或引用类型.
与C++中对应则是成员变量.
基本类型默认值
默认值仅在Java初始化类时使用.
基本类型会被赋值为
0
(boolean
为false
).但局部变量不会被赋默认值, 如果不显式指定初始化, 编译器会报错.
方法使用
对应C++中的成员函数.
1 | [return type] [method name](/*args list*/){ |
返回类型
- 方法签名(Signature of the Method) 是方法的唯一标识, 包括方法名和参数列表. (返回类型不是方法签名的一部分)
参数列表
Java中参数也是通过对象的形式传递, 也就是说, 传递的是对象引用(不包括基本类型).
程序编写
命名可见性
C++通过命名空间(namespace) 解决命名冲突.
Java为一个类库生成一个明确的名称来解决冲突. 使用反向URL关联命名空间和文件路径会导致源代码管理出现混乱, 比如说空目录问题(用于表示反向URL).
import
1 | import java.util.ArrayList; |
static
在类中的使用与C++基本一致.
- 有时Java使用类数据(class data) 和类方法(class method) 来表示静态成员.
编码风格
Java编程语言编码规范(Code Conventions for the Java Programming Language). 采用驼峰命名法. 其中类首字母大写, 字段和方法首字母小写. 对于常量, 可采用全大写 + 下划线的命名方式.
Chapter4 运算符
运算符操纵数据.
Beginning
运算符接受一个或多个参数并生成新值.
副作用(Side Effect): 运算符可改变运算对象的值.
优先级&关联性

Tips: 节选自[菜鸟教程|Java运算符]
赋值
=
表示, 将右边的值赋给左边的变量.基本类型的赋值是值传递.
对象的赋值是引用传递.
Tips: 实际上引用传递也是值传递, 只是这里传递的是内存地址.
Additional: [知乎|Java 到底是值传递还是引用传递?]
算术运算符
+
,-
,*
,/
,%
自增&自减
++
,--
1 | a = 1; |
关系运算符
>
,<
,==
,>=
,<=
,!=
接受两个操作数, 返回一个
boolean
值表示操作数之间的关系.
尽管==
和!=
可以比较对象,
但更推荐的方法是使用equals()
, 需要注意的是,
自定义类型使用equals()
需要手动覆写(Override).
逻辑运算符
&&
,||
,!
根据参数逻辑关系生成布尔值
true
,false
.
短路
当逻辑运算符运算结果确定时, 便会发生短路(short-circuiting).
1 | class ShortCircuit{ |
上述代码执行结果只会得到一条test
而非三条,
&&
在运算时发生了短路.
字面值常量(Literal)
可以指定字面值来确定常量的类型.
1 | int i1 = 0x2f; //16进制 |
位运算符
&
,|
,~
(取反),^
(异或),<<
,>>
,>>>
(按位右移补零)
三目运算符
?:
->variable x = (expression) ? value if true : value if false
Chapter5 控制流
程序必须在执行过程中控制它的世界并作出选择. 在Java中, 程序员需要执行控制语句来做出选择.
Java支持的关键字包括if-else
, while
,
do-while
, for
, return
,
break
, switch
.
需要注意的是,
所有条件语句的Expression应当是boolean
类型.
if-else
1 | //允许省略else |
for while do-while
- 循环语句又称迭代语句.
1 | //while |
return
- 退出当前方法, 返回方法返回值.
break&continue
break
与continue
均用在循环体中.break
表示跳出当前循环;continue
表示停止本次循环, 进行下一次循环.
标签
- 源于
goto
机制, 在Java中, 允许为循环体定义label, 并允许break
和continue
使用label.
具体规则如下:
continue
会回退到最内层循环开头, 并继续执行.continue label
会到达标签位置, 并重新进入紧接着那个标签后的循环.break
会中断当前循环, 并移离当前标签末尾.break label
会中断当前循环, 并移离由那个标签指示的循环末尾.
我们来看两个例子:
1 | public class Main { |
执行结果为:
1 | i: 0, j: 0 |
1 | public class Main { |
执行结果为:
1 | i: 0, j: 0 |
switch
其基本格式如下:
1 | switch(integral-selector OR string-selector){ |
在switch
体中, default
可以省略.
如果缺少break
, switch
将会发生穿透.
示例如下:
1 | public class Main { |
其结果如下:
1 | case 2 |