[Thinking In Java] 之 一切皆对象

一.一切皆对象

Java 是以C++为基础,但是Java是一种更纯粹的面向对象语言.C++允许有多种编程风格,它支持与C语言的向后兼容能力,由于C++是C的一个超集,所以C++有很多特性是C不具备的,因此,C++在某些地方显得复杂.而Java则没有这种”杂合关系”,只希望进行面向对象设计,即:一切皆对象.

 

1.1  用句柄操纵对象

 

我们在处理数据进行操作的时候,时候要知道处理的对象到底是什么,在C中我们使用过自定义变量和指针等操作过对象,在Java中,这一切都简化成”句柄”,尽管一切都看作对象,实际上真正我们是使用”句柄”进行对象操作.

当然,我们也可以只定义句柄,如:

       String s;

      但这里创建的只是句柄,并不是对象。若此时向s 发送一条消息,就会获得一个错误(运行期)。这是由于s 实际并未与任何对象实例连接。因此,一种更安全的做法是:创建一个句柄时,记住无论如何都进行初始化:

String s = "asdf";

然而,这里采用的是一种特殊类型:字串可用加引号的文字初始化。通常,必须为对象使用一种更通用的初始化类型.如:

Object obj = new Object();

 

1.2  保存到什么地方

 

程序运行时,我们最好对数据保存到什么地方做到心中有数。特别要注意的是内存的分配。有六个地方都可以保存数据:

(1) 寄存器。这是最快的保存区域,因为它位于和其他所有保存方式不同的地方:处理器内部。然而,寄存器的数量十分有限,所以寄存器是根据需要由编译器分配。我们对此没有直接的控制权,也不可能在自己的程序里找到寄存器存在的任何踪迹。

(2) 堆栈。驻留于常规RAM(随机访问存储器)区域,但可通过它的“堆栈指针”获得处理的直接支持。堆栈指针若向下移,会创建新的内存;若向上移,则会释放那些内存。这是一种特别快、特别有效的数据保存方式,仅次于寄存器。创建程序时,Java 编译器必须准确地知道堆栈内保存的所有数据的“长度”以及“存在时间”。这是由于它必须生成相应的代码,以便向上和向下移动指针。这一限制无疑影响了程序的灵活性,所以尽管有些Java 数据要保存在堆栈里——特别是对象句柄,但Java 对象并不放到其中。

(3) 堆。一种常规用途的内存池(也在RAM 区域),其中保存了Java 对象。和堆栈不同,“内存堆”或“堆”(Heap)最吸引人的地方在于编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间。因此,用堆保存数据时会得到更大的灵活性。要求创建一个对象时,只需用new 命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存。当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间!

(4) 静态存储。这儿的“静态”(Static)是指“位于固定位置”(尽管也在RAM 里)。程序运行期间,静态存储的数据将随时等候调用。可用static 关键字指出一个对象的特定元素是静态的。但Java 对象本身永远都不会置入静态存储空间。

(5) 常数存储。常数值通常直接置于程序代码内部。这样做是安全的,因为它们永远不会改变。有的常数需要严格地保护,所以可考虑将它们置入只读存储器(ROM)。

(6) 非RAM 存储。若数据完全独立于一个程序之外,则程序不运行时仍可存在,并在序的控制范围之外。其中两个最主要的例子便是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,通常会发给另一台机器。而对于固定对象,对象保存在磁盘中。即使程序中止运行,它们仍可保持自己的状态不变。对于这些类型的数据存储,一个特别有用的技巧就是它们能存在于其他媒体中。一旦需要,甚至能将它们恢复成普通的、基于RAM 的对象。Java 1.1 提供了对Lightweight persistence 的支持。未来的版本甚至可能提供更完整的方案。

 

    对于基本类型数据来说: 

       1) 局部变量

$范围:定义在一个方法内部的变量;

$作用域:其作用域为所在代码块,也称为临时变量、栈变量;

$生命周期:从创建开始到方法(函数)结束为止;

$特点:在使用之前必须要对其初始化;

$一律存放在栈中。

2) 实例变量

$范围:定义在一个类内部但不在方法中的变量;

$作用域:其作用域为整个类,能够被操作的范围是类的内部;

$生命周期:对象被创建的时候开始,对象被垃圾回收的时间结束;

$特点:如未定义其初值,系统会自动对实例变量进行默认初始化(默认值为null),使用之前必须创建类的实例;

$每一个对象的实例变量的值是相互独立的;

$一律存放在堆中。

 

对于复杂类型来说:

引用存放在栈中,而其所指向的值存放在堆中。

 

总的来说:对于方法中局部变量的引用存储在java运行时数据区的栈中,而对于实例变量则存储在java运行时数据区的堆中。

 

比如:
void func()
{
  Object obj = newObject();//这个obj在函数的栈里。
}

class Test
{
  private Object obj =new Object();//这个obj随对应的Test对象分配在堆里
}

 

1.3  特殊情况: 主要类型

 

有一系列类需特别对待;可将它们想象成“基本”、“主要”或者“主”(Primitive)类型,进行程序设计时要频繁用到它们。之所以要特别对待,是由于用new 创建对象(特别是小的、简单的变量)并不是非常有效,因为new 将对象置于“堆”里。对于这些类型,Java 采纳了与C 和C++相同的方法。也就是说,不是用new 创建变量,而是创建一个并非句柄的“自动”变量。这个变量容纳了具体的值,并置于堆栈中,能够更高效地存取。

Java 决定了每种主要类型的大小。就象在大多数语言里那样,这些大小并不随着机器结构的变化而变化。这种大小的不可更改正是Java 程序具有很强移植能力的原因之一。

 

主类型大小 最小值 最大值 封装器类型

boolean 1 位 - - Boolean

char 16 位 Unicode 0 Unicode 2 的16 次方-1 Character

byte 8 位 -128 +127 Byte(注释①)

short 16 位 -2 的15 次方 +2 的15 次方-1 Short(注释①)

int 32 位 -2 的31 次方 +2 的31 次方-1 Integer

long 64 位 -2 的63 次方 +2 的63 次方-1 Long

float 32 位 IEEE754 IEEE754 Float

double 64 位 IEEE754 IEEE754 Double

Void - - -Void(注释①)

①     到Java 1.1 才有,1.0 版没有。

 

定义类成员变量时,基本类型会赋予初始值(局部成员变量不会):

主类型默认值

Boolean false

Char'\u0000'(null)

byte (byte)0

short (short)0

int 0

long 0L

float 0.0f

double 0.0d

 

数值类型全都是有符号(正负号)的,所以不必费劲寻找没有符号的类型。

主数据类型也拥有自己的“封装器”(wrapper)类。这意味着假如想让堆内一个非主要对象表示那个主类型,就要使用对应的封装器。例如:

char c = 'x';

Character C =new Character('c');

也可以直接使用:

Character C =new Character('x');

这样做的原因将在以后的章节里解释。

1. 高精度数字

Java 1.1 增加了两个类,用于进行高精度的计算:BigInteger 和BigDecimal。尽管它们大致可以划分为“封装器”类型,但两者都没有对应的“主类型”。

这两个类都有自己特殊的“方法”,对应于我们针对主类型执行的操作。也就是说,能对int 或float 做的事情,对BigInteger 和BigDecimal 一样可以做。只是必须使用方法调用,不能使用运算符。此外,由于牵涉更多,所以运算速度会慢一些。我们牺牲了速度,但换来了精度。

BigInteger 支持任意精度的整数。也就是说,我们可精确表示任意大小的整数值,同时在运算过程中不会丢失任何信息。

BigDecimal 支持任意精度的定点数字。例如,可用它进行精确的币值计算。

至于调用这两个类时可选用的构建器和方法,请自行参考联机帮助文档。

 

1.4  构建Java程序

 

1.4.1         名字的可见性

假设您在程序的某个模块里使用了一个名字,而另一名程序员在另一个模块里使用了相同的名字。此时,如何区分两个名字,并防止两个名字互相冲突呢?这个问题在C 语言里特别突出。因为程序未提供很好的名字管理方法。C++的类(即Java 类的基础)嵌套使用类里的函数,使其不至于同其他类里的嵌套函数名冲突。然而,C++仍然允许使用全局数据以及全局函数,所以仍然难以避免冲突。为解决这个问题,C++用额外的关键字引入了“命名空间”的概念。

由于采用全新的机制,所以Java 能完全避免这些问题。为了给一个库生成明确的名字,采用了与Internet域名类似的名字。事实上,Java 的设计者鼓励程序员反转使用自己的Internet 域名,因为它们肯定是独一无二的。由于我的域名是BruceEckel.com,所以我的实用工具库就可命名为com.bruceeckel.utility.foibles。反转了域名后,可将点号想象成子目录。在Java 1.0 和Java 1.1 中,域扩展名com,edu,org,net 等都约定为大写形式。所以库的样子就变成:COM.bruceeckel.utility.foibles。然而,在Java 1.2 的开发过程中,设计者发现这样做会造成一些问题。所以目前的整个软件包都以小写字母为标准。

1.4.2         Static 关键字

一旦将什么东西设为static,数据或方法就不会同那个类的任何对象实例联系到一

起。所以尽管从未创建那个类的一个对象,仍能调用一个static 方法,或访问一些static 数据。而在这之前,对于非static 数据和方法,我们必须创建一个对象,并用那个对象访问数据或方法.

 

1.4.3         注释和嵌入文档

注释方式:

A)     /*行注释*/

B)     /**

* 段注释

*/

C)     //单行注释

 

嵌入文档:

所有javadoc 命令都只能出现于“/**”注释中。但和平常一样,注释结束于一个“*/”。主要通过两种方式来使用javadoc:嵌入的HTML,或使用“文档标记”。其中,“文档标记”(Doc tags)是一些以“@”开头的命令,置于注释行的起始处(但前导的“*”会被忽略)。

嵌入H T M L

/**

* <pre>

* System.out.println(new Date());

* </pre>

*/

@ s e e :引用其他类

@see 类名

@see 完整类名

@see 完整类名#方法名

类文档标记

@version

@author

方法文档标记

@param 参数名 说明

@return 说明

@exception 完整类名 说明

@deprecated  建议用户不必再使用一种特定的功能

 

示例:

import java.util.*;

/** The first Thinking in Javaexample program.

* Lists system information oncurrent machine.

* @author Bruce Eckel

* @authorhttp://www.BruceEckel.com

* @version 1.0

*/

public class Property {

/** Sole entry point to class& application

* @param args array of stringarguments

* @return No return value

* @exception exceptions Noexceptions thrown

*/

public static void main(String[]args) {

System.out.println(new Date());

Properties p =System.getProperties();

p.list(System.out);

System.out.println("---Memory Usage:");

Runtime rt =Runtime.getRuntime();

System.out.println("TotalMemory = "

+ rt.totalMemory()

+ " Free Memory = "

+ rt.freeMemory());

}

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值