• ThinkInJava4读书笔记之第二章一切都是对象


         尽管以C++为基础,但Java 是一种更纯粹的面向对象程序设计语言。无论C++还是Java 都属于杂合语言。Java 语言首先便假定了我们只希望进行面向对象的程序设计。

     引用操纵对象

         在Java 里,任何东西都可看作对象。但操纵的标识符实际是指向一个对象的“句柄”(Handle)或引用。可将对象和引用的关系想象成电视机和遥控器。,即使没有电视机,遥控器亦可独立存在。也就是说,只是由于拥有一个句柄,并不表示必须有一个对象同它连接。所以如果想容纳一个词或句子,可创建一个String 句柄:String s;但这里创建的只是句柄,并不是对象。若此时向s 发送一条消息,就会获得一个错误(运行期)。因此,一种更安全的做法是:创建一个句柄时,记住无论如何都进行初始化:

    String s = "asdf";
    

    所有对象都必须创建

        创建句柄时,我们希望它同一个新对象连接。通常用new 关键字达到这一目的。如:

    String s = new String("asdf");
    

    保存到什么地方

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

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

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

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

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

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

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

    基本类型

        有一系列类需特别对待;可将它们想象成“基本”、“主要”类型,进行程序设计时要频繁用到它们。之所以要特别对待,是由于用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

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

    char c = 'x';

    Character C = new Character('c');

    高精度数字

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

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

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

    Java的数组

         一个Java数组 可以保证被初始化,而且不可在它的范围之外访问。由于系统自动进行范围检查,所以必然要付出一些代价:针对每个数组,以及在运行期间对索引的校验,都会造成少量的内存开销。但由此换回的是更高的安全性,以及更高的工作效率。为此付出少许代价是值得的。创建对象数组时,实际创建的是一个句柄数组。而且每个句柄都会自动初始化成一个特殊值,并带有自己的关键字:null(空)。一旦Java 看到null,就知道该句柄并未指向一个对象。正式使用前,必须为每个句柄都分配一个对象。若试图使用依然为null 的一个句柄,就会在运行期报告问题。

    对象的作用域

        Java 对象不具备与主类型一样的存在时间。用new 关键字创建一个Java 对象的时候,它会超出作用域的范围之外。所以假若使用下面这段代码:

    {
    
    String s = new String("a string");
    
    } /* 作用域的终点 */
    

      那么句柄s 会在作用域的终点处消失。然而,s 指向的String 对象依然占据着内存空间。在上面这段代码里,我们没有办法访问对象,因为指向它的唯一一个句柄已超出了作用域的边界。这样造成的结果便是:对于用new 创建的对象,只要我们愿意,它们就会一直保留下去。。Java 有一个特别的“垃圾收集器”,它会查找用new 创建的所有对象,并辨别其中哪些不再被引用。随后,它会自动释放由那些闲置对象占据的内存,以便能由新对象使用。

    类、字段和方法

         Java中使用“class”关键字定义一个类。定义一个类时,可在自己的类里设置两种类型的元素:数据成员(有时也叫“字段”或“属性”)以及成员函数(通常叫“方法”)。其中,数据成员是一种对象(通过它的句柄与其通信),可以为任何类型。每个对象都为自己的数据成员保有存储空间;数据成员不会在对象之间共享(深入Java虚拟机中对此有详细讲解)。

    基本类型的默认值

        若某个主数据类型属于一个类成员,那么即使不明确(显式)进行初始化,也可以保证它们获得一个默认值。

    主类型      默认值

    Boolean    false

    Char       '\u0000'(null)

    byte       (byte)0

    short      (short)0

    int         0

    long       0L

    float      0.0f

    double   0.0d

         一旦将变量作为类成员使用,就要特别注意由Java 分配的默认值。然而,这种保证却并不适用于“局部”变量。

    方法

         Java 的“方法”决定了一个对象能够接收的消息。方法的基本组成部分包括名字、自变量、返回类型以及主体。Java 的方法只能作为类的一部分创建。只能针对某个对象调用一个方法,而且那个对象必须能够执行那个方法调用。

    构建Java程序

        在Java中,为了给一个库生成明确的名字,采用了与Internet域名类似的名字。事实上,Java 的设计者鼓励程序员反转使用自己的Internet 域名,因为它们肯定是独一无二的。如JDK中swing的类库为com.sun.java.swing。

         使用其他类或组件。大多数时候,我们直接采用来自标准Java 库的组件(部件)即可,它们是与编译器配套提供的。使用这些组件时,没有必要关心冗长的保留域名(包名)。使用import可导入需要的类。

    static关键字

        一旦将什么东西设为static,数据或方法就不会同那个类的任何对象实例联系到一起。所以尽管从未创建那个类的一个对象,仍能调用一个static 方法,或访问一些static 数据。而在这之前,对于非static 数据和方法,我们必须创建一个对象,并用那个对象访问数据或方法。

     

    第一个Java程序

    import java.util.*;
    
     
    
    public class Property {
    
    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("Total Memory = "
    
    + rt.totalMemory()
    
    + " Free Memory = "
    
    + rt.freeMemory());
    
    }
    
    }
    

      运行结果:

    Sat Mar 09 09:32:49 CST 2013
    -- listing properties --
    java.runtime.name=Java(TM) SE Runtime Environment
    sun.boot.library.path=D:\touch\jdk\bin
    java.vm.version=20.10-b01
    java.vm.vendor=Sun Microsystems Inc.
    java.vendor.url=http://java.sun.com/
    path.separator=;
    java.vm.name=Java HotSpot(TM) Client VM
    file.encoding.pkg=sun.io
    user.country=CN
    sun.java.launcher=SUN_STANDARD
    sun.os.patch.level=
    java.vm.specification.name=Java Virtual Machine Specification
    user.dir=D:\WorkSpace\eclipse\ThinkInJava4
    java.runtime.version=1.6.0_35-b10
    java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment
    java.endorsed.dirs=D:\touch\jdk\lib\endorsed
    os.arch=x86
    java.io.tmpdir=D:\Users\aaa\AppData\Local\Temp\
    line.separator=
    
    java.vm.specification.vendor=Sun Microsystems Inc.
    user.variant=
    os.name=Windows NT (unknown)
    sun.jnu.encoding=GBK
    java.library.path=D:\touch\jdk\bin;D:\Windows\Sun\Java\...
    java.specification.name=Java Platform API Specification
    java.class.version=50.0
    sun.management.compiler=HotSpot Client Compiler
    os.version=6.2
    user.home=D:\Users\aaa
    user.timezone=Asia/Shanghai
    java.awt.printerjob=sun.awt.windows.WPrinterJob
    file.encoding=GBK
    java.specification.version=1.6
    user.name=aaa
    java.class.path=D:\WorkSpace\eclipse\ThinkInJava4\bui...
    java.vm.specification.version=1.0
    sun.arch.data.model=32
    java.home=D:\touch\jdk
    sun.java.command=object2.Property
    java.specification.vendor=Sun Microsystems Inc.
    user.language=zh
    awt.toolkit=sun.awt.windows.WToolkit
    java.vm.info=mixed mode, sharing
    java.version=1.6.0_35
    java.ext.dirs=D:\touch\jdk\lib\ext;D:\Windows\Sun\J...
    sun.boot.class.path=D:\touch\jdk\lib\resources.jar;D:\tou...
    java.vendor=Sun Microsystems Inc.
    file.separator=\
    java.vendor.url.bug=http://java.sun.com/cgi-bin/bugreport...
    sun.cpu.endian=little
    sun.io.unicode.encoding=UnicodeLittle
    sun.desktop=windows
    sun.cpu.isalist=pentium_pro+mmx pentium_pro pentium+m...
    --- Memory Usage:
    Total Memory = 16252928 Free Memory = 15847680
    

      如使用控制台运行可能需要添加以下代码暂停输出:

    try {
    
      Thread.currentThread().sleep(5 * 1000);
    
    } catch(InterruptedException e) {}
    
    }
    

      在每个程序文件的开头,都必须放置一个import 语句,导入那个文件的代码里要用到的所有额外的类。注意我们说它们是“额外”的,因为一个特殊的类库会自动导入每个Java 文件:java.lang。基本类型的包装类都在这个类库中,所有我们从不需要导入String、Integer等类却能直接使用它们。类名与文件是一样的。若象现在这样创建一个独立的程序,文件中的一个类必须与文件同名。

  • 相关阅读:
    LeetCode
    已知二叉树的先序遍历和中序遍历序列求后序遍历序列
    LeetCode
    LeetCode
    LeetCode
    LeetCode
    LeetCode
    TCP协议的基本规则和在Java中的使用
    Java中UDP协议的基本原理和简单用法
    LeetCode
  • 原文地址:https://www.cnblogs.com/waimai/p/2951264.html
Copyright © 2020-2023  润新知