Java编程思想 4th 第2章 一切都是对象

Java是基于C++的,但Java是一种更纯粹的面向对象程序设计语言,和C++不同的是,Java只支持面向对象编程,因此Java的编程风格也是纯OOP风格的,即一切都是类,所有事情通过类对象协作来完成。

在Java中,使用引用来操纵对象,在Java编程思想的第四版中,使用的术语是“引用(reference)”,之前有读过Java编程思想第三版,在第三版中,使用的术语是“句柄(handle)”,事实上,我觉得第三版的术语“句柄”更加形象传神,就像你用一个门的把柄来开门关门一样,不用管门是什么门,总之你用门把柄就能控制操作这扇门。当然,使用“引用”这个术语对于C++出身的人来说更方便理解,因为在C++中,底层一般使用指针来实现引用,也即通过指针来间接操纵对象。更合拍的是,在C++中,使用new动态分配的对象是匿名的,没有任何办法能够操纵该对象,除了起初new表达式返回的指针,在Java中,对象都是new出来的,是存放于动态内存中的,要想使用对象,就要通过对象的句柄(引用)来操作,这无形中把Java和C++联系了起来,因为二者本质都是一种类似指针的东西。

在Java中,引用可以单独存在,就像在C++中,指针也是一个独立的存在。我们可以把引用关联到对象上,也可以令指针指向对象,因此引用(指针)是独立于对象存在的。同指针一样,不能够随意使用没有关联对象的引用。在Java中,使用未关联对象的引用会导致编译报错;在C++中,这通常会导致内存越界,进而收到操作系统的segmentfault信号,如果未捕捉处理该信号,默认是程序coredump。如下图所示:

C++:

Java:

此处还隐式说明了一个Java语言的特性:String字符串可以用带引号的字面值进行初始化(字面值一词是我从C++中拿过来的术语),示例如下:

String str = "Hello";

一旦创建了引用,除了用上述特性直接进行初始化,通常情况下引用必须管理到一个对象,而Java中对象的创建只能通过new来进行。对于上述例子,可以写成:

String str = new String("Hello");

C++中与寄存器有关的是volatile关键词,然而C++并不能直接操纵寄存器,仅仅通过volatile来阻止编译器利用寄存器可能的优化。除了汇编语言能够直接操纵寄存器,其他的高级语言都无法做到,Java也不例外。

Java会将对象的引用也即“指针”存放在栈中,栈的优点是快,系统自动管理,缺点是大小有限制,就像C++一样,如果栈的用量过大会导致爆栈。

Java会将对象存放在堆中,就是那些new出来的对象,堆的优点是空间大,缺点是程序员的心智负担大,需要自己手动分配和回收资源,如果一直申请堆内存而不及时释放,最终会耗尽系统可用内存(当然在Java世界中不需要程序员手动管理,这一切交给gc垃圾回收系统)。

Java程序和C++程序一样,除了用到的对象及数据外,还有代码指令本身,它也需要被加载到内存。在C++中,代码指令通常存储在text段中,Java也是如此。text代码区的特点是只读不可写,可能会被多个程序共享。

除了高速的寄存器和内存,计算机还有一个海量的、持久化的存储设备:硬盘。硬盘独立于程序之外,可以将一些数据写入到硬盘进行存储,待下次使用时从硬盘中加载读取。

无论Java如何抽象,如何面向对象编程,它最终还是需要数学世界的基本类型来提供用于高级语言抽象的基石。Java中的基本类型是存放在栈中的(之所以放在栈中而不是堆中,我觉得并不是书本所说的效率问题,栈和堆的本质区别是生命周期的长短,如果所有的一切都放在长生命周期的堆中,Java程序很快就会吃光内存,这是不可能接受的行为),它们与对象及其引用不同,基本类型的变量就是对应一个值,不同的变量之间拷贝赋值时,不会像引用那样只是拷贝了引用。Java基本类型所占空间大小是固定的,不随不同硬件平台变化,并且不会像C++那样有有符号和无符号之分。具体如下: 

基本类型 boolean char byte short int long float double void
内存大小  未定义  16 bits  8 bits 16 bits  32 bits  64 bits  32 bits 64 bits   无
对应封装类 Boolean  Character   Byte  Short  Short  Long Float   Double  Void

因为内置的基本类型是存储在栈中的,且仅仅是一个基本类型,不具有其他功能,为了实现更多功能,Java为每个基本类型进行了封装,以提供更多更好用的功能,且封装后的类型的实例化对象是存储在堆中的,可由垃圾回收管理来自动回收对象所耗用的内存。

在基本类型和其封装类型之间可以相互转换,比如:

Character ch1 = 'x';
Character ch2 = new Character('y');
char c = ch1;

除了对每个基本类型进行封装提供对应的包装类,Java还提供了2个用于数值范围大、精确度高的包装类:BigInteger和BigDecimal。这两个封装内没有对应的基本类型,它们运算速度较慢,牺牲了速度,换取了精度。

Java数组和C++不同,Java会确保数组读写不会溢出以及初始化,数组既可以存储基本类型,也能存储对象的引用。当存储对象引用时,它们被初始化为null值,编译器会在数组元素被访问时检查元素是否为null以阻止使用未初始化元素的问题。对于内置类型,如若未初始化,则默认置零。

Java和C++一样具有作用域的概念,并且两者作用域的规则相同,唯一不同的是,Java禁止内层的变量名或者引用覆盖外层变量名/引用,这是比C++安全的一个规定,可以阻止用户不经意的误用。

Java虽然和C++一样具有作用域规则,但作用域生效的对象是基本类型的变量或者对象的引用,而引用所绑定的对象本身则不受作用域限制。

Java中一切都是对象,对象的类型决定对象的状态和行为,也即我们需要定义一种类型,该类型可以被实例化出一个对象,每个对象都有自己的状态和行为,而我们定义的类型是对这些实例化对象的抽象。定义新类型的语法如下:

class TypeName {
    //class body
}

与C++不同的是,Java的类定义不需要以分号结束。

定义类型时需要定义该类型所实例化出来的对象的状态和行为。所谓状态也即数据存储,每个对象都有自己的私有数据,数据存储的信息反应出状态;所谓行为也就是函数方法,同一类对象都能用相同的函数方法去操纵。在Java中数据状态称为field,函数方法称为method。

定义field时,field可以是基本类型,也可以是对象引用,对于前者Java会确保没有初始化的获得一个默认值,对于后者,引用必须初始化。类值类型的默认值全部置零,对于boolean类型来说,默认值为false。

method就是一个函数,其结构是函数的四要素:返回值、函数名、参数列表和函数体。前三者唯一的标识一个函数,也称为函数签名或方法签名。Java中的method必须定义在class内部,否则编译报错。函数一定要有return语句(当然由于历史原因少数使用C语言的“老顽固”为了省事对返回类型为void的函数是不写return语句的,此时由编译器默认插入),return语句的作用有两个,一是转移控制权,另一个是返回一个值。

Java中使用import来导入一个包,也即一个类库,因为Java中所有代码都写在class内部,所以一个Java库也称为类库。除了一次导入一个包,也可以用通配符*来导入一群库。

Java除了有定义在method内部的局部变量以及每个对象都有的field之外,还有第三种变量,那就是static变量。Java中的static变量同C++相同,它改变了static修饰的变量的存放位置,也改变了修饰的变量的生存周期。在Java中,static修饰的变量只能是field,而不能是method内部的local局部变量。static修饰后的变量不再为每一个对象都复制一份,而是整个class类对象共享同一份,因此其访问方式也发生了变化,因为它是属于类本身的,而不是对象本身的,所以可以通过类名直接访问。又因为整个class类对象都共享它,因此也可以通过类对象来访问。和C++不同,当通过类名来访问时,Java使用点运算符,而C++使用作用域运算符来访问。

Java每一个程序会默认导入java.lang包。Java类的名字必须与文件名相同,这也暗示了一个Java源文件只能有一个public类。

Java的注释继承了C/C++的两种风格,一是单行注释//,二是多行注释/*注释内容*/。另外Java还支持文档注释。

原文地址:https://www.cnblogs.com/pluse/p/7242212.html