读thinking in java笔记(一):一切都是对象

如果我们说另一种不同的语言,那么我们就会发觉一个有些不同的世界。
1. 用引用操纵对象
    每种编程语言都有自己的操纵内存中元素的方式。有时候,程序员必须注意将要处理的数据是什么类型。你是直接操纵元素,还是用某种基于特殊语法的间接表示来操纵对象?
    所有这一切在Java里都得到了简化。一切都被视为对象,因此可采用单一固定的语法。尽管一切都看作对象,但操纵的标识符实际上是对象的一个“引用”(reference)。可以将这一情形想象成用遥控器(引用)来操纵电视机(对象)。只要握住这个遥控器,就能保持与电视机的连接。当有人想改变频道或者减小音量时,实际操纵的是遥控器(引用),再由遥控器来调控电视机(对象)。如果想在房间里四处走动,同时仍能调控电视机,那么只需携带遥控器(引用)而不是电视机(对象)。
    此外,即使没有电视机,遥控器亦可独立存在。也就是说,你拥有一个引用,并不一定要有一个对象与它关联。因此,如果想操纵一个词或句子,则可以创建一个String引用:String s; 但这里所创建的只是引用,并不是对象。如果此时向s发送一个消息,就会返回一个运行时错误。这是因为此时s实际上没有与任何事物相关联(即,没有电视机)。因此,一种安全的做法是:创建一个引用的同时便进行初始化。String s = "asdf"; 但这里用到Java语言的一个特性:字符串可以用带引号的文本初始化。
    一旦创建了一个引用,就希望它能与一个新的对象相关联。通常用new操作符来实现这一目的。new关键字的意思是“给我一个新对象”。所以,前面的例子可以写成:String s = new String("asdf"); 它不仅表示“给我一个新的字符串”,而且通过提供一个初始字符串,给出了怎样产生这个String的信息。多说一句:通常我们不会用String s = new String();这样的方式来创建一个字符串对象,它表示一个空字符序列,因为字符串的不可变性,这样的对象其实并没有什么意义。
    当然,除了String类型,Java提供了大量过剩的现成类型。重要的是,你可以自行创建类型。事实上,这是Java程序设计中一项基本行为。
2. 特例:基本类型
    在程序设计中经常用到一系列类型,他们需要特殊处理。可以把它们想象成“基本”类型。之所以特殊对待,是因为new将对象存储在“堆”里,故用new创建一个对象,特别是小的、简单的变量,往往不是很有效。因此,对于这些类型,不用new来创建变量,而是创建一个并非是引用的“自动”变量。这个变量直接存储值,并置于堆栈中,因此更加高效。
    Java中要确定每种基本类型所占存储空间的大小。这种所占存储空间大小的不变性是Java程序比用其他大多数语言编写的程序更具有可移植性的原因之一。

基本类型          大小            包装器类型
boolean          -              Boolean
byte             1 bytes        Byte
char             2 bytes        Character
short            2 bytes        Short
int              4 bytes        Int
long             8 bytes        Long
float            4 bytes        Float
double           8 bytes        Double
void             _              Void

    所有数值类型都有正负号,所以不要去寻找无符号的数值类型。
    boolean类型所占存储空间的大小没有明确指定,仅定义为能够取字面值true或false。
    基本类型具有的包装器类,使得可以在堆中创建一个非基本对象,用来表示对应的基本类型。例如:

char c = 'x';
Character ch = new Character(c);
也可以这样用:
Character ch = new Character('x');
java5.0的自动包装功能将自动将基本数据类型转换为包装器类型:
Character ch = 'x';
并且可以反向转换:
char c = ch;

  2.1 高精度数字
    java提供了两个用于高精度计算的类:BigInteger和BigDecimal。虽然他们大体上属于包装器类的范畴,但二者都没有对应的基本类型。
    不过,这两个类包含的方法,提供的操作与对基本类型所能执行的操作相似。也就是所能作用于int或者float的操作,也同样能作用于BigInteger和BigDecimal,只不过必须以方法调用的方式。
    BigInteger支持任意精度的整数。也就是说,在运算中,可以准备的表示任何大小的整数值,而不会丢失任何信息。
    BigDecimal支持任何精度的定点数,例如:可以用它进行精确的货币运算。
  2.2 Java中的数组
    当创建一个数组对象时,实际上就是创建了一个引用,并且每个引用都会被自动初始化为一个特定值,该值拥有自己的关键字null。一旦Java看到null,就知道这个引用还没有指向某个对象。在使用任何引用前,必须为其指定一个对象;如果试图使用一个还是null的引用,在运行时将会报错。
    还可以创建用来存放基本数据类型的数组。同样,编译器也能确保这种数组的初始化。
3. 永远不要销毁对象
  3.1 作用域
    大多数过程型语言都有作用域(scope)的概念。作用域决定了在其内定义的变量名的可见性和生命周期,作用域由花括号的位置决定。在作用域定义的变量只可用于作用域结束之前。
  3.2 对象的作用域
    Java对象不具备和基本类型一样的生命周期。当用new创建一个Java对象时,它可以存活于作用域之外。所以假如你采用代码:

{
    String s = new String("a string");
} // end of scope

    引用s在作用域终点就消失了。然而,s指向的String对象仍继续占据内存空间。在这一小段代码中,我们无法在这个作用域之后访问这个对象,因为对它唯一的引用已超出了作用域的范围。
    事实证明,由new创建的对象,只要你需要就会一直保留下去。这样便带来一个有趣的问题。如果Java让对象继续存在,那么靠什么才能防止这些对象填满内存空间,进而阻塞你的程序呢?Java有一个垃圾回收器,用来监视用new创建的所有对象,并辨别那些不会再被引用的对象。随后,释放这些对象的内存空间,以便供其他新的对象使用。也就是说,你根本不必担心内存回收的问题。你只需要创建对象,一旦不再需要,它们就会自行消失。这样做就消除了这类编程问题(即:”内存泄漏”),这是由于程序员忘记释放内存而产生的问题。
4. 创建新的数据类型:类
    如果一切都是对象,那么是什么决定了某一类对象的外观和行为呢?换句话说,是什么确定了对象的类型?大多数面向对象的程序设计语言习惯用关键字class来表示“我准备告诉你一种新类型的对象看起来像什么样子”。class这个关键字之后紧跟着的是新类型的名称。例如:

class ATypeName{
    //class body goes here
}

    这就引入了一种新的类型,尽管类主题仅包含一条注释语句。因此,你还不能用它做太多的事情。然而你已经可以用new来创建这种类型的对象:

ATypeName a = new ATpyeName();

    但是在定义它的所有方法之前,还没有办法能让它去做更多的事情(也就是说,不能向它发送任何有意义的消息)。
  4.1 字段和方法
    一旦定义了一个类(在Java中你所做的全部工作就是定义类,产生那些类的对象,以及发送消息给这些对象),就可以在类中设置两种类型的元素,字段(成员变量)和方法(成员方法)。字段可以是任意类型的对象,可以通过其引用与其进行通信,也可以是基本类型中的一种。如果字段是对某个对象的引用,那么必须初始化该引用,以便使其与一个实际的对象相关联。 每个对象都有用来存储其字段的空间;普通字段不能在对象间共享。下面是一个具有某些字段的类:

class DataOnly{
    int i;
    double d;
    boolean b;
}

    尽管这个类除了存储数据之外什么也不能做,但是仍旧可以像下面这样创建它的一个对象:DataOnly data = new DataOnly(); 可以给字段赋值,但首先必须知道如何引用一个对象的成员。具体的实现为:在对象引用的名称之后紧接着一个句点,然后再接着是对象内部的成员名称。例如:data.i = 47; data.d = 1.1; data.b = false;想修改的数据也有可能位于对象所包含的其他对象中。在这种情况下,只需要再使用连接句点即可。例如:myplane.leftTank.capacity = 100; DataOnly类除了保存数据外没别的用,因为它没有任何成员方法。
    4.1.1 基本成员默认值

基本类型            默认值                实测结果
boolean            false                false
char               '\u0000'(null)       
byte               (byte)0              0
short              (short)0             0
int                0                    0   
long               0L                   0
float              0.0f                 0.0
double             0.0d                 0.0

    若类的某个成员是基本数据类型,即使没有进行初始化,Java也会确保它获得一个默认值,当变量作为类的成员变量时,Java才确保给其默认值,以确保那些是基本类型的成员变量得到初始化,防止产生查询错误。但是这些初始化值对你的程序来说可能是不正确的,甚至是不合法的。所以,最好明确的对变量进行初始化。
    然而上述确保初始化的方法并不适用于局部变量,局部变量如果没有被初始化,Java在编译时就会返回一个错误。即:局部变量在使用前必须初始化。
5. 方法、参数和返回值
    许多程序设计语言(像C和C++)用函数这个术语来描述命名子程序;而在Java中,常用方法这个术语来表示“做某些事情的方式”。实际上,继续把它看作是函数也无妨。尽管这只是用词的差别。这里将沿用Java的惯用语,即用术语“方法”而不是“函数”来描述。
    java的方法决定了一个对象能够接受什么样的消息。方法的基本组成部分包括:名称、参数、返回值和方法体。下面是它最基本的形式:

ReturnType methodName(/*Argument list*/){
    /*Method body*/
}

    返回类型描述的是在调用方法之后从方法返回的值。参数列表给出了要传给方法的信息的类型和名称。方法名和参数列表(它们合起来被称为“方法签名”)唯一的标识出某个方法。
  5.1 参数列表
    方法的参数列表指定要传递给方法什么样的信息。正如你可能料想的那样,这些信息像Java中的其他信息一样,采用的都是对象形式。因此在参数列表中必须指定每个对象所传递对象的类型和名字。像Java中任何传递对象的场合一样,这里传递的实际上也是引用(对于8种基本数据类型来说是一个例外),并且引用的类型必须正确。
    return关键字的用法,它包括两方面:
    ①,它代表“已经做完,离开此方法”。
    ②,如果此方法产生一个值,这个值要放在return语句后面。
    若方法返回值类型是void,return关键字的作用只是用来退出方法。因此,没有必要再方法结束才离开,可在任何地方返回。但如果返回类型不是void,那么无论在何处返回,编译器都会强制返回一个正确类型的返回值。

6. static关键字
    通常来说,当创建类时,就是在描述那个类的对象的外观和行为。除非用new创建那个类对象,否则,实际上并未获得任何对象。执行new来创建对象时,数据存储空间才被分配,其方法才供外界调用。
    有两种情形用上述方法是无法解决的。
    1. 只想为某特定域(这里的域就是成员变量)分配单一存储空间,而不用考虑究竟要创建多少对象,甚至根本就不创建任何对象。
    2. 希望某个方法不与包含它的类的任何对象联系在一起。也就是说,即使没有创建对象,也能够调用这个方法。
    通过static关键字可以满足这两方面的需求。当声明一个事物是static时,就意味着这个域或方法不会与包含它的那个类的任何对象实例关联在一起。所以,即使从未创建某个类的任何对象,也可以调用其static方法或者访问static域。通常,你必须创建一个对象,并用它来访问数据和方法。因为非static域或方法必须知道它们一起运作的特定对象。(当然,由于在用static方法前不需要创建任何对象;所以对于static方法,不能简单通过调用其他非static域或方法而没有指定某个命名对象,来直接访问非static域或方法(因为非static域或方法必须与某一特定对象关联),即所谓的静态不能访问非静态,访问非静态要通过对象)
    有些面向对象语言采用类类型的变量和类方法两个术语,代表那些变量和方法只是作为整个类,而不是类的某个特定对象而存在。有时,一些Java文献也用这两个术语。
    只须将static关键字放在定义之前,就可以将字段或方法设为static。例如下面的代码就生成了一个static字段,并对其进行了初始化:

Class StaticTest{
    static int i = 47;
}
现在,即使你创建了两个StaticTest对象,StaticTest.i也只有一份存储空间,这两个对象共享同一个i.
StaticTest st1 = new StaticTest();
StaticTest st2 = new StaticTest();
在这里,st1.i和st2.i指向同一存储空间,因此它们具有相同的值47.

    引用static变量有两种方式,可以通过一个对象去定位它,如st1.i;也可以通过其类名直接引用,而这对于非静态成员则不行。

StaticTest.i++;
此时,st1.i和st2.i仍具有相同的值48.
使用类名是引用static变量的首选方式,这不仅是因为它强调了变量的static结构,而且在某些情况下它还为编译器进行优化提供了更好的机会。

    尽管当static作用于某个字段时,肯定会改变数据的创建方式(因为一个static字段对每个类来说都是只有一份存储空间,而非static字段则是对每个对象有一个存储空间),但是static作用于某个方法,差别却没有那么大。static方法的一个作用就是在不创建任何对象的前提下就可以调用它。
7. 注释和嵌入式文档
    Java中有两种注释风格。一种是传统的/* */。注意:许多程序员在连续的注释内容的每一行都以一个“*”开头,所以经常看到这样的写法:

/* this is a comment
 * that continues
 * across lines
 * /

但请记住,进行编译时,/*和*/之间的所有东西都会被忽略,所以上述注释和下面这段注释并没有什么两样:
/* this is a commnet that continus across lines */

    第二种风格的注释也是源于C++。这种注释是“单行注释”,以一个“//”开头,直到句末。这种风格的注释因为书写容易,所以更方便、更常用。

//this is a one-line comment

  7.1 注释文档
    代码文档撰写的最大问题,大概就是对文档的维护了。如果文档和代码是分离的,在每次修改代码时,都需要修改相应的文档。解决的方法似乎很简单,将代码和文档“链接起来”。为达到这个目的,最简单的方法是将所有东西都放在同一个文件内。然而,为实现这一目的,还必须使用一种特殊的注释语法来标记文档;此外还需要一个工具,用于提取那些注释,并将其转换成有用的形式。这正是Java所做的。
    javadoc便是用于提取注释的工具,它是jdk安装的一部分。它采用了Java编译器的某些技术,查找程序内的特殊注释标签。它不仅解析由这些标签标记的信息,也将毗邻注释的类名和方法名抽取出来。如此,我们就可以用最少的工作量,生成相当好的程序文档。
    javadoc输出的是一个html,可以用web浏览器查看。这样,该工具就使得我们只需创建和维护单一的源文件,并能自动生成有用的文档。有了javadoc,就有了创建文档的简明直观的标准,我们可以期望,甚至要求所有的Java类库都提供相关的文档。

  7.2 语法
    所有javadoc命令都只能在“/**”注释中出现,和通常一样,注释结束符*/使用javadoc的方式主要有两种:嵌入HTML或“文档标签”。独立文档标签是一些以@字符开头的命令,且要置于注释行的最前面(但是不算前导“*”,应该说是除了前导的最前面)。而“行内文档标签”则可以出现在javadoc注释中的任何地方,它们也是以@字符开头,但要括在花括号内。
    共有三种类型的注释文档,分别对应于注释位置后面的三种元素:类、域、方法。也就是说,类注释正好位于类定义之前;域注释正好位于域定义之前;而方法注释也正好位于方法定义之前。

例如:
//: object/Documentation1.java
/** A class comment */
public class Documentation1 {
    /** A field comment */
    public int i;
    /** A method comment */
    public void f(){}
} ///:~

    注意: javadoc只能为public和protected成员进行文档注释。private和包内可访问成员调度注释会被忽略掉,所以输出结果中看不到他们。这样做是有道理的,因为只要public和protected成员才能在文件之外被使用。
    上述代码的输出结果是一个HTML文件,它与其他Java文档具有相同的标准格式。

  7.3 嵌入式HTML
    javadoc通过生成的HTML文档传送HTML命令,这使得能充分的利用HTML。当然,其主要目的还是为了对代码进行格式化,例如:

//: object/Documentation2.java
/** 
 * <pre>
 * System.out.println(new Date());
 * </pre>
 */
 ///:~

    也可以像在其他web文档中那样运用HTML,对普通文本按照你自己所描述的进行格式化:

//: object/Documentation3.java
/**
 * you can <em>even</em> insert a list; 
 * <ol>
 * <li>Item one
 * <li>Item two
 * <li>Item three
 * </ol>
 */
 ///:~

    注意,在文档注释中,位于每一行开头的星号和前导空格都会被javadoc丢弃。javadoc会对所有内容重新格式化,使其与标准的文档外观一致。不要在嵌入式文档中使用标题标签,例如<h1>或<hr>,因为javadoc会插入自己的标题,而你的标题可能和他们发生冲突。所有类型的注释文档(类、域和方法)都支持嵌入式HTML。
  7.4 一些标签示例
    7.4.1 @see引用其他类
    @see标签允许用户引用其他类的文档。javadoc会在其生成的HTML文件中,通过@see标签链接到其他文档。格式如下:

@see classname
@see fully-qualified-classname
@see fully-qualified-classname#menthod-name

    上述每种格式都会在生成的文档中加入一个具有超链接的“see alse”(参见)条目。但是javadoc不会检查你所提供的超链接是否有效。
    7.4.2 {@link package.class#member label}
    该标签与@see及其相似,只是它用于行内,并且是用label作为超链接文本而不用“see also”.
    7.4.3 {@docRoot}
    该标签产生到文档根目录的相对路径,用于文档树页面的显示超链接。
    7.4.4 {@inheritDoc}
    该标签从当前这个类的最直接的基类中继承相关文档到当前文档注释中。
    7.4.5 @version
    该标签的格式如下:

@version version-information

    其中,“version-information”可以是任何你认为适合包含在版本说明中的重要信息。如果javadoc命令行使用了“-versioin”标记,那么就从生成的HTML文档中特别提取出版本信息。
    7.4.6 @author
     该标签的格式如下:

@author author-information

       其中,author-information一看便知识你的姓名,但是也可以包括电子邮件地址或者其他任何适宜的信息。如果javadoc命令行使用了-author标记,那么就从生成的HTML文档中特别提取作者信息。可以使用多个标签,一遍列出所有作者,但是它们必须连续放置。全部作者信息会合并到同一段落,置于生成的HTML中。
    7.4.7 @since
    该标签允许你指定程序代码最早使用的版本,可以在HTML Java文档中看到它被用来制动所用的jdk版本的情况。
    7.4.8 @param
    该标签用于方法文档中,形式为:

@param parameter-name description

    其中,parameter-name是方法调度参数列表中的标识符,description是可延续数行的文本,终止于新的文档标签出现之前。可以使用任意多个这种标签,大约每个参数都会有这样一个标签。
    7.4.9 @return
    该标签用于方法文档,格式如下:

@return description

其中,description用来描述返回值的含义,可以延续数行。

    7.4.10 @throws
    “异常”将在第九章论述。简言之,他们是由于某个方法调用失败而“抛出”的对象。尽管在调用一个方法时,只出现一个异常对象,但是某个特殊方法可能会产生任意多个不同类型的异常,所以这些异常都需要进行说明。所以,异常标签的格式如下:

@throws fully-qualified-class-name description

    其中fully-qualified-class-name给出一个异常类的无歧义的名字,而该异常类在别处定义。description(同样可以延续数行)告诉你为什么此特殊类型的异常会在方法调用中出现。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值