“如果我们说另一种不同的语言,那么我们就会发觉一个有些不同的世界。”
——Luding Wittgerstein (1889 ~ 1951)
一、对象的基本概念与引用机制
1.1 万物皆对象的思想
Java是一种纯粹的面向对象编程语言,其设计哲学建立在"万物皆对象"的基础上。与C++不同,Java中几乎所有的元素都被视为对象(除了基本数据类型),这种设计带来了更高的一致性和简洁性。
在Java中,对象是数据和行为的封装体,程序本质上就是对象的集合,这些对象通过发送消息(即方法调用)来相互通信。这种设计理念使得Java程序能够更自然地映射现实世界的问题域。
1.2 引用与对象的关系
Java中我们真正操作的是对象的引用(reference),而非对象本身。引用可以类比为电视机的遥控器——它提供了操作对象的途径,但本身并不是对象。
String s; // 创建一个String引用,但未指向任何对象
String s = new String("asdf"); // 引用s指向一个新创建的String对象
引用可以独立存在而不必指向对象,但如果试图通过未初始化的引用调用方法,会导致运行时错误。因此,最佳实践是在声明引用的同时进行初始化。
1.3 Java与C++引用的区别
虽然Java中的引用概念与C++中的引用有相似之处,但存在重要差异:
Java引用可以被赋值为null,而C++引用必须始终指向有效对象
Java引用可以重新指向不同的对象,而C++引用一旦初始化就不能改变指向
Java中所有参数传递都是按值传递(包括引用),而C++支持真正的按引用传递
二、对象的创建与存储
2.1 对象的创建方式
Java中创建对象的主要方式是使用new关键字:
String s = new String("Hello"); // 在堆上创建新String对象
对于基本数据类型,Java提供了特殊处理,可以直接创建而不使用new:
int i = 42; // 基本类型,存储在栈上
Integer j = new Integer(42); // 包装类对象,存储在堆上
2.2 数据存储的六个区域
程序运行时,数据可以存储在六个不同的区域:
寄存器(Registers):最快的存储区,位于CPU内部,程序员无法直接控制。
栈(Stack):位于RAM中,存储基本类型数据和对象引用。存取速度快但灵活性低,因为必须知道数据的确切大小和生命周期。
堆(Heap):通用的内存池,用于存储所有Java对象。灵活性高但分配和清理时间较长。
静态存储(Static storage):存放静态成员(static修饰),程序运行期间一直存在。
常量存储(Constant storage):存放常量值,通常直接置于程序代码内部。
非RAM存储(Non-RAM storage):如磁盘文件或网络流中的持久化对象。
2.3 基本类型的特殊处理
Java为效率考虑,对基本类型(primitive types)做了特殊处理:
基本类型 | 大小 | 封装器类 | 默认值 |
---|---|---|---|
boolean | 1位 | Boolean | false |
char | 16位 | Character | '\u0000' |
byte | 8位 | Byte | 0 |
short | 16位 | Short | 0 |
int | 32位 | Integer | 0 |
long | 64位 | Long | 0L |
float | 32位 | Float | 0.0f |
double | 64位 | Double | 0.0d |
基本类型直接存储值而非引用,存放在栈上以提高效率。当需要在堆上存储基本类型时,可以使用对应的包装类。
三、对象的生命周期与作用域
3.1 作用域(Scoping)
在Java中,基本类型的生存范围由花括号({}
)决定——变量只能在其定义的代码块内使用。与C/C++不同,Java不允许在内层作用域中隐藏外层作用域的同名变量:
{
int x = 12;
{
int x = 96; // 错误!在Java中不允许
}
}
3.2 对象的生命周期
对象与基本类型的生命周期有本质区别。当使用new
创建对象时,即使离开定义它的作用域,对象仍然存在于堆中:
{
String s = new String("a string");
} // 引用s消失,但String对象仍然存在
Java通过垃圾回收器(Garbage Collector)自动管理对象内存。垃圾回收器会定期检查堆中的对象,回收那些不再被任何引用指向的对象所占用的内存。这种机制大大简化了内存管理,减少了内存泄漏的风险。
四、类的定义与成员
4.1 类的组成
类定义了对象的外观和行为,包含两种主要元素:
字段(数据成员):可以是基本类型或对象引用
方法(成员函数):定义对象的行为
class MyClass {
int i; // 字段
double d; // 字段
void method() { // 方法
// 方法实现
}
}
4.2 默认初始化
当变量作为类的成员时,Java会为其提供默认初始化值(局部变量则不会自动初始化):
类型 | 默认值 |
---|---|
boolean | false |
char | '\u0000' |
数值类型 | 0 |
对象引用 | null |
尽管有默认值,显式初始化字段仍是良好的编程实践,可以减少潜在的错误。
五、static关键字
5.1 static的含义
static
关键字用于表示某个成员不与类的任何特定实例关联。static成员有以下特点:
无论创建多少个对象,static数据只有一份存储空间
static方法不需要通过对象即可调用
static方法内部不能直接访问非static成员
5.2 static的使用示例
class StaticTest {
static int i = 47; // 静态变量
static void method() { // 静态方法
System.out.println("Static method");
}
}
// 访问静态成员
StaticTest.i++; // 通过类名访问
StaticTest.method();
StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();
// st1.i和st2.i指向同一个存储空间
static方法的一个典型应用是main()
方法,它是程序的入口点,在创建任何对象之前就会被调用。
六、方法、参数与返回值
6.1 方法的定义
方法是类的行为单元,定义格式如下:
返回类型 方法名(参数列表) {
// 方法体
return 返回值; // 如果返回类型不是void
}
当方法不需要返回值时,返回类型应声明为void
。此时可以使用return
提前退出方法。
6.2 参数传递机制
Java中的所有参数传递都是按值传递(pass by value):
对于基本类型,传递的是值的副本
对于对象引用,传递的是引用的副本(因此可以修改对象状态,但不能改变原始引用指向)
void modify(int x, String s) {
x = 10; // 不影响原始值
s = "changed"; // 不影响原始引用
s.concat("!"); // 但可以调用对象方法
}
这种机制常常被误解为"对象是按引用传递",实际上传递的是引用值的副本。
七、数组的特性
Java中的数组是对象,具有以下特点:
创建对象数组时,实际创建的是引用数组,每个元素初始化为null
基本类型数组直接存储值而非引用
数组有范围检查,避免越界访问
数组长度通过
length
属性获取,不是方法
// 数组示例
int[] a1 = {1, 2, 3}; // 基本类型数组
String[] a2 = new String[3]; // 对象数组,元素初始化为null
八、文档注释
Java提供了特殊的文档注释语法,可以通过javadoc
工具生成API文档:
/**
* 类描述信息
* @author 作者
* @version 版本
*/
public class MyClass {
/**
* 方法描述
* @param 参数名 参数描述
* @return 返回值描述
* @throws 异常描述
*/
public int method(String param) throws Exception {
// 方法实现
}
}
文档注释支持多种标签,如@see
、@deprecated
等,可以生成丰富的文档内容。
结语:《Thinking in Java》第二章全面介绍了Java面向对象编程的基础概念,从对象的基本概念到具体实现细节,为后续深入学习Java奠定了坚实基础。