如果我们说另一种不同的语言,那么我们就会发觉一个有些不同的世界。——Luduing Wittgerstein(1889-1951)
Java是基于C++的,C++和Java都是混合/杂合型语言。杂合型语言允许多种编程风格;C++之所以成为一种杂合型语言主要是因为它支持与C语言的向后兼容。因为C++是C的一个超集,包括许多C语言不具备的特性,这些特性使C++在某些方面显得过于复杂。
用引用操作对象
一切都看作对象,操纵的标识符实际上是对象的一个“引用(reference)”。例如String s;
这里所创建的只是引用,并不是对象。此时向s发送一个消息,就会返回一个运行时错误。因此一种安全的做法是:创建一个引用的同时便进行初始化。String s="abcd";
(Java语言的一个特性:字符串可以用带引号的文本初始化)。
必须由你创建所有对象
new
关键字的意思是“给我一个新对象”。String s = new String("abcd");
它不仅表示了“给我一个新的字符串”,而且通过提供一个初始字符串,给出了怎样产生这个String
的信息。
存储到什么地方
- 寄存器。这是最快的存储区,位于处理器的内部。由于寄存器数量极其有限,所以寄存器根据需求进行分配。不能直接控制(C和C++允许向编译器建议寄存器的分配)。
- 堆栈。位于通用RAM(随机访问存储器)中,但通过堆栈指针可以从处理器那里获得直接支持。堆栈指针向下移动,则分配新的内存;向上移动,则释放那些内存。仅次于寄存器。Java系统必须知道存储在堆栈内所有项的确切生命周期,以便上下移动堆栈指针。这一约束限制了程序的灵活性,所以虽然Java某些数据存储在堆栈中——特别是对象引用,但是Java对象并不存储其中。
- 堆。一种通用的内存池(也位于RAM区),用于存放所有的Java对象。堆不同于堆栈的好处是:编译器不需要知道存储的数据在堆里存活多长时间。用堆进行存储分配和清理可能比用堆栈进行存储分配需要更多的时间(如果确实可以在Java中像C++中那样在栈中创建对象)。
- 常量存储。常量值通常直接存放在程序代码内部,这样做是安全的,因为它们永不会被改变。可以选择将其存放在ROM(只读存储器)中。
-
非RAM存储。如果数据完全存活于程序之外i,那么它可以不受程序的任何控制,在程序没有运行时也可以存在。两个例子如
流对象
和持久化对象
。
基本类型
对于这些类型,new
创建一个对象——特别是小的,简单的变量,往往不是很有效,因此,Java采取与C和C++相同的方法。创建一个并非是引用的“自动”变量,这个变量直接存储“值”并置于堆栈中,因此更加高效。
Java要确定每种基本类型所占存储空间的大小。这种所占存储空间大小的不变性是Java程序比用其他大多数语言编写的程序更具可移植性的原因之一。
基本类型 | 包装器类型 | 大小 |
---|---|---|
boolean | Boolean | - |
char | Character | 16-bit |
byte | Byte | 8 bits |
short | Byte | 16 bits |
int | Integer | 32 bits |
long | Long | 64 bits |
float | Float | 32 bits |
double | Double | 64 bits |
void | Void | - |
boolean类型所占存储空间的大小没有明确指定,仅定义为能够取字面值true
和false
。
高精度数字
Java提供了两个用于高精度计算的类:BigInteger和BigDicimal。
BigInteger支持任意精度的整数。BigDicimal支持任意精度的定点小数。
Java中的数组
在C和C++中使用数组很危险,因为数组就是内存块,如果一个程序要访问其自身内存卡块之外的数组,或在数组初始化前使用内存,都会产生难以预料的后果。Java确保数组会被初始化,而且不能在它的范围之外被访问。这种范围检查,是以每个数组上少量的内存开销及运行时的下标检查为代价的。当创建一个数组对象时,就是创建了一个引用数组,并且每个引用都会自动被初始化为一个特定值null
。如果试图使用一个还是null的引用,在运行时就会报错。
永远不需要销毁对象
作用域
在C、C++和Java中,作用域(scope)
由花括号的位置决定。在作用域里定义的变量只可用于作用域结束之前。尽管以下代码在C和C++中是合法的,但是在Java中却不能这样书写:
{
int x=12;
{
int x=96;
}
}
编译器会报告变量x已经定义过。
对象的作用域
Java对象不具备和基本类型一样的生命周期。当用new创建一个Java对象时,它可以存活于作用域之外。
{
String s= new String("Abcs");
}
引用s在作用域的终点就消失了。然后s指向的String对象仍继续占据内存空间。事实上,由new创建的对象,只要你需要,就会一直保留下去。Java有一个垃圾回收器,用来监视用new创建的所有对象,并辨别那些不会再被引用的对象。随后,释放这些对象的内存空间,以便供其他新的对象使用。
创建新的数据类型:类
class
关键字表示一个新类型的对象是什么样子的。
字段和方法
一旦定义了一个类,就可以在类中设置两种类型的元素:字段(数据成员)和方法(成员函数)。如果字段是对某个对象的引用,那么必须初始化该引用,以便与一个实际的对象相关联。每个对象都有用来存储其字段的空间;普通字段不能在对象间共享。
基本成员默认值
若类的某个成员是基本数据类型,即使没进行初始化,Java也会确保它获得一个默认值。
基本类型 | 默认值 |
---|---|
boolean | false |
char | '\u0000'(null) |
byte | (byte)0 |
short | (short)0 |
int | 0 |
long | 0L |
float | 0.0f |
double | 0.0d |
当变量作为类的成员时,Java才确保给定其默认值(C++没有此功能),然而上述确保初始化的方法不适用于“局部”变量。如果忘记对局部变量初始化,Java会在编译时返回一个错误(C++编译器给予警告)。
方法、参数和返回值
方法的基本组成包括:名称、参数、返回值和方法体。方法名和参数列表(它们合起来被称为“方法签名”)唯一地标识出某个方法。
Java中的方法只能作为类的一部分来创建。方法只有通过对象才能被调用(或针对类的调用),且这个对象必须能执行这个方法调用。这种调用方法的行为通常被称为发送消息给对象。
参数列表
方法的参数列表指定要传递给方法什么样的信息,采用的都是对象形式。因此,在参数列表中必须指定每个所传递的对象的类型及名字。这里的传递实际上也是引用(基本数据类型是传递的值)。
static关键字
执行new来创建对象时,数据存储空间才被分配。有两种情形是上述方法无法解决的。一种情形是,只想为某特定域分配单一存储空间,而不去考虑究竟要创建多少对象,甚至不创建对象。另一种情况是,希望某个方法不与包含它的类的任何对象关联在一起。也就是说,不创建对象也能调用这个方法。
当声明一个事务是static
时,就意味着这个域或方法不会与包含它的那个类的任何对象实例关联在一起。对于static方法,不能直接访问非static方法,必须要与某一特定对象关联。类数据和类方法代表那些数据和方法是作为整个类而存在的。即使创建了多个对象实例,类数据也只有一份存储空间,可以通过对象访问也可以通过类名直接引用。
static方法的一个重要用法就是在不创建任何对象的前提下调用它,这对定义main()方法很重要,这个方法是运行一个应用时的入口点。