构造器

  1. 一个对象变量并没有实际包含一个对象,而仅仅引用一个对象。在Java 中,任何对象变量的值都是对存储在另外一个地方的一个对象的引用。new 操作符的返回值也是一个引用。

  2. 构造器产生机制
    很多人错误地认为Java 对象变量与C++ 的引用类似。然而,在C++中没有空引用, 并且引用不能被赋值。可以将Java 的对象变量看作C++ 的对象指针。例如,

    1
    Date birthday; // Java

    实际上, 等同于

    1
    Date* birthday; // C++

    一旦理解了这一点, 一切问题就迎刃而解了。当然, 一个Date* 指针只能通过调用new进行初始化。就这一点而言,c++与Java 的语法几乎是一样的。

    1
    Date* birthday = new Date() ; // C++

    如果把一个变量的值賦给另一个变量, 两个变量就指向同一个日期, 即它们是同一个对象的指针。在 Java 中的null 引用对应 C++ 中的 NULL 指针。所有的 Java 对象都存储在堆中。当一个对象包含另一个对象变量时, 这个变量依然包含着指向另一个堆对象的指针。
    在 C++ 中,指针十分令人头疼,并常常导致程序错误。稍不小心就会创建一个错误的指针,或者造成内存溢出。在 Java 语言中, 这些问题都不复存在。如果使用一个没有初始化的指针,运行系统将会产生一个运行时错误,而不是生成一个随机的结果,同时,不必担心内存管理问题,垃圾收集器将会处理相关的事宜。
    C++ 确实做了很大的努力,它通过拷贝型构造器和复制操作符来实现对象的自动拷贝。例如,一个链表( linked list ) 拷贝的结果将会得到一个新链表,其内容与原始链表相同, 但却是一组独立的链接。这使得将同样的拷贝行为内置在类中成为可能。在 Java 中, 必须使用 clone 方法获得对象的完整拷贝。

  3. 使用静态工厂方法来代替构造器
    这个技巧或者说注意事项在effective java中的第一章第一节就强调过,他有很多便捷之处,其中一个重要的就是可以根据产生的具体对象的特征来对方法命名,而构造器只能有一种名字(即类的名字)

  4. 访问器和更改器
    访问器:只访问对象元素,不会对对象本身进行修改
    修改器:会对对象本身进行修改
    在C++ 中,带有 const 后缀的方法是访问器方法;默认为更改器方法。但是,在Java 语言中,访问器方法与更改器方法在语法上没有明显的区别,

自定义的类

  1. 构造器使用注意事项
    Java 构造器的工作方式与 C++ 一样。但是,要记住所有Java 对象都是在堆中构造的,构造器总是伴随着 new 操作符一起使用。
    请注意,不要在构造器中定义与实例域重名的局部变量。例如,下面的构造器将无法设置 salary 。

    1
    2
    3
    4
    5
    6
    public Employee(St ring n, double s, . .
    {
    String name = n; // Error
    double salary = s; // Error
    ...
    }
  2. this与隐式参数
    在每一个方法中, 关键字 this 表示隐式参数。
    C++ 注释: 在 C++ 中, 通常在类的外面定义方法:

    1
    2
    3
    4
    void Employee::raiseSalary(double byPercent) // C++, not Java
    {
    ...
    }

    如果在类的内部定义方法,这个方法将自动地成为内联(inline)方法

    1
    2
    3
    4
    5
    class Employee
    {
    int getNameQ{
    return name;
    } // inline in C++

    在 Java 中,所有的方法都必须在类的内部定义,但并不表示它们是内联方法。是否将某个方法设置为内联方法是Java 虚拟机的任务。即时编译器会监视调用那些简洁、经常被调用、没有被重载以及可优化的方法。

静态域与静态方法

  1. 静态域
    如果将域定义为 static , 每个类中只有一个这样的域。而每一个对象对于所有的实例域却都有自己的一份拷贝。

  2. 静态方法
    静态方法是一种不能向对象实施操作的方法。
    静态方法不能有实例域, 因为它不能操作对象。但是,静态方法可以访问自身类中的静态域。
    在下面两种情况下使用静态方法:

    • 一方法不需要访问对象状态,其所需参数都是通过显式参数提供(例如:Math.pow)
    • 一个方法只需要访问类的静态域(例如:Employee.getNextld)

    静态工厂方法
    静态工厂的功能和构造器基本一致,都是产生一个对象实例。
    为什么这些类不利用构造器完成这些操作呢?这主要有两个原因:

    • 无法命名构造器。构造器的名字必须与类名相同。但是,这里希望将得到的不同特征的实例采用不用的名字
    • 当使用构造器时,无法改变所构造的对象类型。只能是自己类型,不能是其子类
  3. 父类的静态方法能不能被重写?(2019-5-14 更新)
    答案是不能。因为静态方法从程序开始运行后就已经分配了内存,也就是说已经写死了。所有引用到该方法的对象(父类的对象也好子类的对象也好)所指向的都是同一块内存中的数据,也就是该静态方法。子类中如果定义了相同名称的静态方法,并不会重写,而应该是在内存中又分配了一块给子类的静态方法,没有重写这一说。

方法参数

  1. Java 程序设计语言总是采用按值调用
    也就是说,方法得到的是所有参数值的一个拷贝特别是,方法不能修改传递给它的任何参数变量的内容。 在这里对象是个特殊情况,可以改变传入对象中成员变量的值,但是基本类型例如int、boolean并不能改变(不会报错,但跳出方法外值不变,因为变得是拷贝,而不是其本身)

  2. 然而,虽然对象是特殊情况,但对象仍然是按值调用,不是引用调用;

    • 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)。
    • 一个方法可以改变一个对象参数的状态。
    • 一个方法不能让对象参数引用一个新的对象。

对象与其他

对象构造

  1. 调用另一个构造器
    如果构造器的第一个语句形如this(…) ,这个构造器将调用同一个类的另一个构造器。下面是一个典型的例子:

    1
    2
    3
    4
    5
    public Employee(double s){
    // calls Employee(String, double)
    this("Employee #" + nextld, s);
    nextld++;
    }

    当调用 new Employee(60000) 时,Employee(double) 构造器将调用 Employee(String ,double) 构造器。采用这种方式使用 this 关键字非常有用,这样对公共的构造器代码部分只编写一次即可。

  2. 对象析构与 finalize 方法
    在析构器中,最常见的操作是回收分配给对象的存储空间。由于 Java 有自动的垃圾回收器,不需要人工回收内存,所以 Java 不支持析构器。

  1. 包的导入
    在C++中,与包机制类似的是命名空间(namespace) 。在Java 中,package 与 import 语句类似于 C++ 中的 namespace 和 using 指令。

类的设计技巧

  1. 一定要保证数据私有
  2. 一定要对数据初始化
  3. 不要在类中使用过多的基本类型
  4. 不是所有的域都需要独立的域访问器和域更改器
  5. 将职责过多的类进行分解
  6. 类名和方法名要能够体现它们的职责