[关闭]
@wjcper2008 2017-04-25T15:53:40.000000Z 字数 33520 阅读 5669

面向对象部分1: 对象和类

Java


参考自《Java语言程序设计 基础篇 10版》, 仅供内部教学讲义使用, 由Teacher Chen整理.

1 为对象定义类

知识点: 类为对象定义属性和行为.

1.1 对象的状态和行为

面向对象程序设计(OOP)就是使用对象进行程序设计. 对象(object)代表现实世界中可以明确标识的一个实体. 例如: 一个学生、一张桌子、一个圆、一个按钮甚至一笔贷款都可以看作是一个对象. 每个对象都有自己独特的标识、状态和行为.

1.2 类和对象的定义

使用一个通用类来定义同一类型的对象.

类和对象之间的关系类似于哈根达斯配方冰淇淋之间的关系. 可以用一种配方做出任意多的冰淇淋来. 图1.1显示名为Circle的类和它的三个对象.

图1.1 类是创建对象的模板
图1.1 类是创建对象的模板

1.3 定义圆对象的类(例子)

Java类使用变量定义数据域, 使用方法定义动作.

类还提供了一种称为构造方法(constructor)的特殊类型的方法, 调用它可以创建一个新对象.

构造方法本身是可以完成任何动作的, 但是设计构造方法是为了完成初始化动作, 例如: 初始化对象的数据域. 图1.2显示定义圆对象的类的例子.

图1.2 类是定义相同类型对象的结构
图1.2 类是定义相同类型对象的结构

Circle类与目前所见过的所有其他类都不同, 它没有main方法, 因此是不能直接运行的;它只是对圆对象的定义. 本书还将涉及包含main方法的类. 为了方便, 本书将包含main方法的类称为主类(main class).

1.4 统一建模语言(UML)——类图

图1.1中类的模板和对象的图示可以使用统一建模语言(Unified Modeling Language,UML) 的图形符号进行标准化, 如图1.3所示, 这种表示方法称为UML类图(UML class diagram), 或简称为类图(class diagram). 在类图中

图1.3 使用UML符号表示Circle类和对象
图1.3 使用UML符号表示Circle类和对象

1.5 面向对象编程一般步骤(要求掌握)

2 示例: 定义类和创建对象

知识点: 类是对象的定义, 对象从类创建.

本节给出两个定义类和使用类创建对象的例子.

2.1 圆Circle类

程序清单1.1是一个定义Circle类并使用该类创建对象的程序. 程序构造了三个圆对象, 其半径分别为1、25和125, 然后显示这三个圆的半径和面积. 然后将第二个对象的半径改为100, 并显示它的新半径和面积.

为了避免与本节后续介绍的Circle类的改进版本有命名冲突, 将本例中的Circle类命名为SimpleCircle, 为简单起见我们仍然将教材中的类称为Circle.

2.1.1 圆Circle类程序清单

程序清单1 TestSimpleCircle.java

  1. public class TestSimpleCircle {
  2. /** Main method */
  3. public static void main(String[] args) {
  4. // Create a circle with radius 1
  5. SimpleCircle circle1 = new SimpleCircle();
  6. System.out.println("The area of the circle of radius "
  7. + circle1.radius + " is " + circle1.getArea());
  8. // Create a circle with radius 25
  9. SimpleCircle circle2 = new SimpleCircle(25);
  10. System.out.println("The area of the circle of radius "
  11. + circle2.radius + " is " + circle2.getArea());
  12. // Create a circle with radius 125
  13. SimpleCircle circle3 = new SimpleCircle(125);
  14. System.out.println("The area of the circle of radius "
  15. + circle3.radius + " is " + circle3.getArea());
  16. // Modify circle radius
  17. circle2.radius = 100; // or circle2.setRadius(100)
  18. System.out.println("The area of the circle of radius "
  19. + circle2.radius + " is " + circle2.getArea());
  20. }
  21. }
  22. // Define the circle class with two constructors
  23. class SimpleCircle {
  24. double radius;
  25. /** Construct a circle with radius 1 */
  26. SimpleCircle() {
  27. radius = 1;
  28. }
  29. /** Construct a circle with a specified radius */
  30. SimpleCircle(double newRadius) {
  31. radius = newRadius;
  32. }
  33. /** Return the area of this circle */
  34. double getArea() {
  35. return radius * radius * Math.PI;
  36. }
  37. /** Return the perimeter of this circle */
  38. double getPerimeter() {
  39. return 2 * radius * Math.PI;
  40. }
  41. /** Set a new radius for this circle */
  42. void setRadius(double newRadius) {
  43. radius = newRadius;
  44. }
  45. }

程序1运行结果

2.1.2 圆Circle类程序解析

程序包括两个类. 其中第一个类TestSimpleCircle是主类. 它的唯一目的就是测试第二个类SimpleCircle. 使用这样的类的程序通常称为该类的Test端. 运行这个程序时, Java运行系统会调用这个主类的main方法.

可以把两个类放在同一个文件中, 但是文件中只能有一个类是公共(public) 类. 此外,公共类(public)必须与文件同名. 因此, 文件名就应该是TestSimpleCircle.java, 因为TestSimpleCircle是公共的.

源代码中的每个类编译成.class文件. 当编译TestSimpleCircle.java时, 产生两个类文件TestSimpleCircle.class和SimpleCircle.class, 如图1.5 所示.
图1.5 源代码中的每个类被编译成一个.Class文件
图1.5 源代码中的每个类被编译成一个.Class文件

  1. 主类包含main方法(第3行) 该方法创建三个对象. 和创建数组一样, 使用new操作符从构造方法创建一个对象.

    • circlel = new SimpleCircle() 创建一个半径为1的对象(第5行),
    • circle2 = new SimpleCircle(25) 创建一个半径为25的对象(第10行),
    • circle3 = new SimpleCircle(125) 创建一个半径为125的对象(第15行).
  2. 这三个对象(通过circle1、circle2和circle3来引用)有不同的数据, 但是有相同的方法. 因此, 可以使用getArea()方法计算它们各自的面积. 可以分别使用circle1.radius、circle2.radius, circle3.radius来通过对象引用访问数据域radius. 对象可以分别使用circle1.getArea()、circle2.getArea()、circle3.getArea()来通过对象引用调用它的计算面积方法.

  3. 这三个对象是独立的. circle2的半径改为100(第20行). 对其他的对象circle1和circle3不影响.

2.1.3 圆Circle类程序(组合)

程序清单2 SimpleCircle.java

  1. public class SimpleCircle {
  2. /** Main method */
  3. public static void main(String[] args) {
  4. // Create a circle with radius 1
  5. SimpleCircle circle1 = new SimpleCircle();
  6. System.out.println("The area of the circle of radius "
  7. + circle1.radius + " is " + circle1.getArea());
  8. // Create a circle with radius 25
  9. SimpleCircle circle2 = new SimpleCircle(25);
  10. System.out.println("The area of the circle of radius "
  11. + circle2.radius + " is " + circle2.getArea());
  12. // Create a circle with radius 125
  13. SimpleCircle circle3 = new SimpleCircle(125);
  14. System.out.println("The area of the circle of radius "
  15. + circle3.radius + " is " + circle3.getArea());
  16. // Modify circle radius
  17. circle2.radius = 100; // or circle2.setRadius(100)
  18. System.out.println("The area of the circle of radius "
  19. + circle2.radius + " is " + circle2.getArea());
  20. }
  21. // Define the circle class
  22. double radius;
  23. /** Construct a circle with radius 1 */
  24. SimpleCircle() {
  25. radius = 1;
  26. }
  27. /** Construct a circle with a specified radius */
  28. SimpleCircle(double newRadius) {
  29. radius = newRadius;
  30. }
  31. /** Return the area of this circle */
  32. double getArea() {
  33. return radius * radius * Math.PI;
  34. }
  35. /** Return the perimeter of this circle */
  36. double getPerimeter() {
  37. return 2 * radius * Math.PI;
  38. }
  39. /** Set a new radius for this circle */
  40. void setRadius(double newRadius) {
  41. radius = newRadius;
  42. }
  43. }

由于组合后的类中有一个main方法, 所以它可以由Java解释器来执行. main方法和程序清单1中的是一样的. 它演示如何通过在一个类中加入main方法来测试这个类.

2.2 电视TV类

2.2.1 TV类UML图

每台电视机都是一个对象, 每个对象都有状态(当前频道、当前音量、电源开或关) 以及动作(转换频道、调节音量、开启/关闭). 可以使用一个类对电视机进行建模. 这个类的UML图1.6所示.

图1.6 TV类的UML
图1.6 TV类的UML及功能描述

2.2.2 TV类程序清单

程序清单3 TV.java

  1. public class TV {
  2. int channel = 1; // Default channel is 1
  3. int volumeLevel = 1; // Default volume level is 1
  4. boolean on = false; // By default TV is off
  5. public TV() {
  6. }
  7. public void turnOn() {
  8. on = true;
  9. }
  10. public void turnOff() {
  11. on = false;
  12. }
  13. public void setChannel(int newChannel) {
  14. if (on && newChannel >= 1 && newChannel <= 120)
  15. channel = newChannel;
  16. }
  17. public void setVolume(int newVolumeLevel) {
  18. if (on && newVolumeLevel >= 1 && newVolumeLevel <= 7)
  19. volumeLevel = newVolumeLevel;
  20. }
  21. public void channelUp() {
  22. if (on && channel < 120)
  23. channel++;
  24. }
  25. public void channelDown() {
  26. if (on && channel > 1)
  27. channel--;
  28. }
  29. public void volumeUp() {
  30. if (on && volumeLevel < 7)
  31. volumeLevel++;
  32. }
  33. public void volumeDown() {
  34. if (on && volumeLevel > 1)
  35. volumeLevel--;
  36. }
  37. }

TV类中的构造方法和其他方法定义为public, 因此可以从其他类中访问.

如果没有打开电视, 那么频道和音量都不能被改变. 在改变它们中的任何一个之前, 要检査它的当前值以确保它在一个正确的范围内. if (on && volumeLevel < 7)

程序清单4 TestTV.java

  1. public class TestTV {
  2. public static void main(String[] args) {
  3. TV tv1 = new TV();
  4. tv1.turnOn();
  5. tv1.setChannel(30);
  6. tv1.setVolume(3);
  7. TV tv2 = new TV();
  8. tv2.turnOn();
  9. tv2.channelUp();
  10. tv2.channelUp();
  11. tv2.volumeUp();
  12. System.out.println("tv1's channel is " + tv1.channel
  13. + " and volume level is " + tv1.volumeLevel);
  14. System.out.println("tv2's channel is " + tv2.channel
  15. + " and volume level is " + tv2.volumeLevel);
  16. }
  17. }

image_1bdb5qhgv1c4bglu654106k182cm.png-14.4kB

程序在第3行和第8行创建两个对象, 然后调用对象中的方法来完成设置频道和音量的动作, 以及增加频道和提高音量的动作. 程序在第14-17行显示输出对象的状态. 比如开启tv1对象, 调用tvl.turnOn()方法(第4行). 另外, 获得tv1对象的频道信息, 则调用tvl.channel来访问(第14行).

2.3 复习题

  1. 描述对象与它的定义类之间的关系.
  2. 如何定义一个类?
  3. 如何声明一个对象引用变量?
  4. 如何创建一个对象?

3 使用构造方法构造对象(初始化对象)

知识点: 构造方法在使用new操作符创建对象的时候被调用.

构造方法是一种特殊的方法. 它们有以下四个特性:

3.1 构造方法的定义

构造方法是用来创建对象的. 使用new操作符调用这个类的构造方法, 如下所示:

  1. new ClassName(arguments);

一个常见的错误就是将关键字void放在构造方法的前面. 例如:

  1. public void Circle() {
  2. //TODO something
  3. }

在这种情况下, Circle()是一个普通方法, 而不是构造方法.

3.2 构造方法的重载

和所有其他方法一样, 构造方法也可以重载(也就是说, 可以有多个同名的构造方法, 但它们要有不同的构造函数签名), 这样更易于用不同的初始数据值来构造对象.

  1. new Circle()使用Circle 类中定义的第一个构造方法创建一个Circle对象.
  2. new Circle(25)调用Circle类中定义的第二个构造方法创建一个Circle对象.

3.3 默认构造方法

通常, 一个类会提供一个没有参数的构造方法, 例如: Circle(). 这样的构造方法称为无参构造方法.

一个类可以不定义构造方法. 在这种情况下, 类中隐含定义一个方法体为空的无参构造方法. 这个构造方法称为默认构造方法(default constructor), 当且仅当类中没有明确定义任何构造方法时才会自动提供它.

3.4 思考题

4 通过引用变量访问对象

知识点: 对象的数据域和方法可以运用点操作符(.), 通过对象的引用变量进行访问.

新建的对象在内存中被分配空间. 而访问则是通过引用变量.

回忆数组变量、数组存储方式、数组访问、浅拷贝、深拷贝.

4.1 引用类型和引用变量

对象是通过"对象引用变量(reference variable)"来访问的, 该变量包含对对象的引用, 使用如下语法格式声明:

  1. ClassName objectRefVar;

本质上来说, 一个类是一个自定义的类型. 类是一种引用类型(reference type), 这意味着该类型的变量可以引用访问该类的实例.

下面的语句声明变量myCircle的类型是Circle类型:

  1. Circle myCircle;

变量myCircle能够引用一个Circle对象. 下面的语句创建一个对象, 并且将它的引用陚给变量myCircle:

  1. myCircle = new Circle();

采用如下所示的语法, 可以写一条包括声明对象引用变量、创建对象以及将对象的引用賦值给这个变量的语句.

  1. ClassName objectRefVar = new ClassName();

变量myCircle中放的是对Circle对象的一个引用. 下面是一个例子:

  1. Circle myCircle = new Circle();

从表面上看, 对象引用变量中似乎存放了一个对象, 但事实上, 它只是包含了对该对象的引用. 严格地讲, 对象的引用变量和对象本身是不同的, 但是大多數情况下, 这种差异是可以忽略的. 因此, 可以简单地说myCircle是一个Circle对象, 而不用冗长地描述说, myCircle 是一个包含对Circle对象引用的变量.

在Java中, 数组被看作是对象. 数组是用new操作符创建的. 一个数组变量实际上是一个包含数组引用的变量.

4.2 访问对象的数据和方法——访问操作符(.)

在面向对象编程中, 对象成员主要包括对象的数据和方法.

在创建一个对象之后, 它的数据和方法可以使用点操作符(.)来访问和调用, 该操作符也称为对象成员访问操作符(object member access operator):

例如: myCircle.radius引用myCircle的半径, 而myCircle.getArea()调用myCircle的getArea()方法.


4.3 实例变量、实例方法、静态变量和静态方法

例如, 在Circle类中, 其数据radius被称作实例变量, 因为它依赖于某个具体的实例(如myCircle). 基于同样的原因, getArea() 方法称为实例方法, 因为该方法只能对具体的实例进行操作.

Math.methodName(arguments)(例如Math.pow(3, 2.5)) 来调用Math类中的方法. Math类中的所有方法都是用关键字static 定义的静态方法. 比如, 人类的人口数.


那么能否用以className.method(arguments)方式调用对象的方法呢?答案是不能.

Circle.getArea()来调用getArea 方法呢? getArea()是实例方法, 它是非静态的. 它必须使用objectRefVar.methodName(参教)的方式(例如: myCircle.getArea() )从对象调用.


4.4 匿名对象

通常, 我们创建一个对象, 然后将它賦值给一个变量, 之后就可以使用这个变量来引用对象. 有时候, 一个对象在创建之后并不需要引用. 在这种情况下, 可以创建一个对象, 而并不将它明确地賦值给一个变量, 如下所示:

  1. new Circle();

或者

  1. System.out.println("Area is " + new Circle(5).getArea());

前面的语句创建了一个Circle对象. 后面的语句创建了一个Circle 对象, 然后调用它的getArea()方法返回其面积. 这种方式创建的对象称为匿名对象(anonymous object).


4.5 引用类型的实例变量和null值

4.5.1 实例变量也可能是引用类型

例如: 下面的Student类包含一个String 类型的name数据, String是一个预定义的Java类.

  1. class Student {
  2. String name; // name has the default value null
  3. int age; // age has the default value 0
  4. boolean isScienceMajor; // isScienceMajor has default value false
  5. char gender; // gender has default value '\u0000'
  6. }

如果一个引用类型的实例变量没有引用任何对象, 那么这个实例变量就有一个特殊的Java值null. null同true 和false一样都是一个直接量. true 和false 是boolean 类型直接量, 而null是引用类型直接量.

4.5.2 实例变量的默认初始值

下面的代码显示Student 对象中数据域name, age, isScienceMajor和gender的默认值:

  1. class Test {
  2. public static void main(String[] args) {
  3. Student student = new Student();
  4. System.out.println("name? " + student.name);
  5. System.out.println("age? " + student.age);
  6. System.out.println("isScienceMajor? " + student.isScienceMajor);
  7. System.out.println("gender? " + student.gender);
  8. }
  9. }

下面代码中的局部变量x和y都没有被初始化, 所以它会出现编译错误:

  1. class Test {
  2. public static void main(String[] args) {
  3. int x; // x has no default value
  4. String y; // y has no default value
  5. System.out.println("x is " + x);
  6. System.out.println("y is " + y);
  7. }
  8. }

4.5.3 NullPointerException错误

NullPointerException是一种常见的运行时错误, 当调用值为null 的引用变量上的方法时会发生此类异常. 在通过引用变量调用一个方法之前, 确保先将对象引用賦值给这个变量.

4.6 基本类型变量和引用类型变量的区别

每个变量都代表一个存储值的内存位置. 声明一个变量时, 就是在告诉编译器这个变量可以存放什么类型的值.


例如: int型变量i的值就是int值1, 而Circle对象c的值存的是一个引用, 它指明这个Circle对象的内容存储在内存中的什么位置.
image_1bdbehfto4fd18kjm451mideet13.png-20.8kB
图8 基本类型变量在内存中存储的是一个基本类型值, 而引用类型
变量存储的是一个引用, 它指向对象在内存中的位置


赋值操作:将一个变量赋值给另一个变量时, 另一个变量就被赋值同样的值.

对于引用类型, 执行完赋值语句c1=c2之后, c1与c2指向同个对象. c1以前引用的对象就不再有用, 因此, 现在它就成为垃圾(garbage). 垃圾会占用内存空间. Java运行系统会检測垃圾并自动回收它所占的空间, 这个过程称为自动垃圾回收(garbage collection).

如果你认为不再需要某个对象时, 可显式地给该对象的引用变量赋null 值. 如果该对象没有被任何引用变量所引用, Java虚拟机将自动回收它所占的空间.


4.7 思考题

  1. //程序1
  2. public class ShowErrors {
  3. public static void main(String[] args) {
  4. ShowErrors t = new ShowErrors(5);
  5. }
  6. }
  7. //程序2
  8. public class ShowErrors {
  9. public static void main(String[] args) {
  10. ShowErrors t = new ShowErrors();
  11. t.x();
  12. }
  13. }
  14. //程序3
  15. public class ShowErrors {
  16. public void method1() {
  17. Circle c;
  18. System.out.println("What is radius " + c.getRadius());
  19. c = new Circle();
  20. }
  21. }
  22. //程序4
  23. public class ShowErrors {
  24. public static void main(String[] args) {
  25. C c = new C(5.0);
  26. System.out.println(c.value);
  27. }
  28. }
  29. class C {
  30. int value = 2;
  31. }
  1. class Test {
  2. public static void main(String[] args) {
  3. A a = new A();
  4. a.print();
  5. }
  6. }
  7. class A {
  8. String s;
  9. A(String newS) {
  10. s = newS;
  11. }
  12. public void print() {
  13. System.out.print(s);
  14. }
  15. }

5 static静态变量、常量和方法

知识点: 静态变量被类中的所有对象所共享. 静态方法不能访问类中的实例成员.

5.1 实例变量

实例变量是绑定到类的某个特定实例的, 它是不能被同一个类的不同对象所共享的.

Circle类的属性radius称为一个实例变量. 例如, 假设创建了如下的两个对象:

  1. Circle circle1 = new Circle();
  2. Circle circle2 = new Circle(5);

circle1中的radius和circle2中的radius是不相关的, 它们存储在不同的内存位置. circle1中radius的变化不会影响circle2中的radius, 反之亦然.

5.2 静态变量

现用户有个需求, 如果想让一个类的所有实例都共享某个数据, 那该如何做?

类的静态变量(static variable), 也称为类变量(class variable).

类变量将变量值存储在一个公共的内存地址. 因为它是公共的地址, 所以如果某一个对象修改了静态变量的值, 那么同一个类的所有对象都会受到影响.

Java支持静态方法和静态变量, 无须创建类的实例就可以调用静态方法(static method).

要声明一个静态变量或定义一个静态方法, 就要在这个变量或方法的声明中加上修饰符static. 静态变量numberOfObjects和静态方法getNumberOfObjects() 可以如下声明:

  1. static int numberOfObjects;
  2. static int getNumberObjects() {
  3. return numberOfObjects;
  4. }

5.3 示例: 为Circle类添加类成员numberOfObjects

修改Circle类, 添加静态变量numberOfObjects统计创建的Circle对象的个
数. 当该类的第一个对象创建后, mimberOfObjects的值是1. 当第二个对象创建后, numberOfObjects的值是2. 新Circle类的UML图如图所示. Circle 类定义了实例变量radius 和静态变董numberOfObjects, 还定义了实例方法getRadius、setRadius和getArea 以及静态方法getNumberOfObjects. (注意, 在UML类图中, 静态变量和静态方法都是以下划线标注的. )

图11 静态变量是被同一个类的所有实例所共享的
图11 静态变量是被同一个类的所有实例所共享的

类中的常量是被该类的所有对象所共享的. 因此, 常量应该声明为final static, 例如, Math类中的常量PI是如下定义的:

  1. final static double PI = 3.14159265358979323846;

新的名为CircleWithStaticMembers的圆类的声明如程序清单所示.

程序清单5 CircleWithStaticMembers.java

  1. public class CircleWithStaticMembers {
  2. /** The radius of the circle */
  3. double radius;
  4. /** The number of the objects created */
  5. static int numberOfObjects = 0;
  6. /** Construct a circle with radius 1 */
  7. CircleWithStaticMembers() {
  8. radius = 1.0;
  9. numberOfObjects++;
  10. }
  11. /** Construct a circle with a specified radius */
  12. CircleWithStaticMembers(double newRadius) {
  13. radius = newRadius;
  14. numberOfObjects++;
  15. }
  16. /** Return numberOfObjects */
  17. static int getNumberOfObjects() {
  18. return numberOfObjects;
  19. }
  20. /** Return the area of this circle */
  21. double getArea() {
  22. return radius * radius * Math.PI;
  23. }
  24. }

CircleWithStaticMembers类中的getNumberOfObjects()方法是一个静态方法. Math类中所有的方法都是静态的. main方法也是静态方法.

  1. 实例方法(例如: getArea()) 和实例数据(例如: radius )都是属于实例的, 所以它们在实例创建之后才能使用. 它们是通过引用变量来访问的.
  2. 静态方法(例如: getNumberOfObjectsO ) 和静态数据(例如: numberOfObjects ) 可以通过引用变量或它们的类名来调用.

程序清单6中的程序演示如何使用实例变量、静态变量、实例方法和静态方法, 还演示了使用它们的效果.

程序清单6 TestCircleWithStaticMembers.java

  1. public class TestCircleWithStaticMembers {
  2. /** Main method */
  3. public static void main(String[] args) {
  4. System.out.println("Before creating objects");
  5. System.out.println("The number of Circle objects is " +
  6. CircleWithStaticMembers.numberOfObjects);
  7. // Create c1
  8. CircleWithStaticMembers c1 = new CircleWithStaticMembers();
  9. // Display c1 before c2 is created
  10. System.out.println("\nAfter creating c1");
  11. System.out.println("c1: radius (" + c1.radius +
  12. ") and number of Circle objects (" +
  13. c1.numberOfObjects + ")");
  14. // Create c2
  15. CircleWithStaticMembers c2 = new CircleWithStaticMembers(5);
  16. // Modify c1
  17. c1.radius = 9;
  18. // Display c1 and c2 after c2 was created
  19. System.out.println("\nAfter creating c2 and modifying c1");
  20. System.out.println("c1: radius (" + c1.radius +
  21. ") and number of Circle objects (" +
  22. c1.numberOfObjects + ")");
  23. System.out.println("c2: radius (" + c2.radius +
  24. ") and number of Circle objects (" +
  25. c2.numberOfObjects + ")");
  26. }
  27. }

结果输出:
image_1bdc3ud2srjesj1mcb1gp319n39.png-44.1kB

代码分析:
1. 静态变量和方法可以在不创建对象的情况下访问. 第6行显示对象的个数为0, 因为还没有创建任何对象.
2. main 方法创建两个圆,c1和c2(第9、18行). c1中的实例变量radius 修改为9(第21行). 这个变化不会影响c2中的实例变量radius, 因为这两个实例变量是独立的. c1创建之后静态变量numberOfObjects 变成1(第9行), 而c2创建之后numberOfObjects 变成2(第18行).

5.4 实例方法和静态/类方法

**使用“ 类名.方法名(参数)” 的方式调用静态方法, 使用“ 类名.静态变量” 的方式访问静态变量. 这会提高可读性, 因为可以很容易地识别出类中的静态方法和数据. **

程序清单6中, PI是一个定义在Math中的常量, 可以使用Math.PI来访问这个常量. 最好使用CircleWithStaticMembers.numberOfObjects 来代替c1.numberOfObjects(第27行)和c2.numberOfObjects(第30行). 这样可以提高可读性, 因为其他程序员可以很容易地识别静态变量. 也可以用CircleWithStaticMembers.getNumber0f0bjects()替换掉
CircleWithStaticMembers.numberOfObjects.

下面的代码具体错在哪里?

  1. public class A {
  2. int i = 5;
  3. static int k = 2;
  4. public static void main(String[] args) {
  5. int j = i; // Wrong because i is an instance variable
  6. m1(); // Wrong because m1() is an instance method
  7. }
  8. public void m1() {
  9. // Correct since instance and static variables and methods
  10. // can be used in an instance method
  11. i = i + k + m2(i, k);
  12. }
  13. public static int m2(int i, int j) {
  14. return (int)(Math.pow(i, j));
  15. }
  16. }

如果用下面的新的代码替换上面的代码, 程序就是正确的, 因为实例数据域i 和方法m1是通过对象a访问的.

  1. public class A {
  2. int i = 5;
  3. static int k = 2;
  4. public static void main(String[] args) {
  5. A a = new A();
  6. int j = a.i; // OK, a.i accesses the object's instance variable
  7. a.m1(); // OK. a.m1() invokes the object's instance method
  8. k = 4;
  9. }
  10. public void m1() {
  11. i = i + k + m2(i, k);
  12. }
  13. public static int m2(int i, int j) {
  14. return (int)(Math.pow(i, j));
  15. }
  16. }

5.5 设计指南

如何判断一个变量或方法应该是实例的还是静态的?

一个常见的设计错误就是将一个本应该声明为静态的方法声明为实例方法. 例如: 方法factorial(int n)应该定义为静态的, 如下所示, 因为它不依赖于任何具体的实例.
image_1bdc4va5bclvrp8mr1vpb1djt13.png-22.5kB
image_1bdc4vonq1ji0196v2kg5psplf1g.png-24.5kB

5.6 思考题

  1. public class Test {
  2. int count;
  3. public ? void main(String[] args) {
  4. ...
  5. }
  6. public ? int getCount() {
  7. return count;
  8. }
  9. public ? int factorial(int n) {
  10. int result = 1;
  11. for (int i = 1; i <= n; i++)
  12. result *= i;
  13. return result;
  14. }
  15. }
  1. public class C {
  2. public static void main(String[] args) {
  3. method1();
  4. }
  5. public void method1() {
  6. method2();
  7. }
  8. public static void method2() {
  9. System.out.println("What is radius " + c.getRadius());
  10. }
  11. Circle c = new Circle();
  12. }

6 可见性修饰符

知识点: 可见性修饰符可以用于确定一个类以及它的成员的可见性.

6.1 可见性修饰符定义

可见性修饰符可以在类、方法和数据域前使用. 共有四种:public, private, protected, 默认(没有修饰符).

6.2 可见修饰符访问范围限定

下图演示类C1中的公共的、默认的和私有的数据域或方法能否被同一个包内的类C2访问, 以及能否被不在同一个包内的类C3访问.
image_1bdc9drjd1ngi18211no51959uj71t.png-73.1kB
图16 私有的修饰符将访问权限限定在它自己的类内, 默认修饰符
将访问权限限定在包内, 而公共的修饰符可以无限制的访问.

如果一个类没有被定义为公共类, 那么它只能在同一个包内被访问. 下图, C2可以访问C1, 而C3不能访问C1.
image_1bdc9i64i14f6k0p211o9ncd42a.png-20.7kB
图17 一个非公共类具有包访问性

可见性修饰符指明类中的数据域和方法是否能在该类之外被访问. 在该类之内, 对数据域和方法的访问是没有任何限制的.

如图18(b)所示, C类的对象c不能引用它的私有成员, 因为c在Test类中. 如图18(a)所示, C类的对象c可以访问它的私有成员, 因为c在自己的类内定义.
image_1bdc9n7677eu19ef9snuk01bf42n.png-83.3kB
图18 如果一个对象是在它自己的类中定义的, 那么这个对象可以访问它的私有成员

7 数据域的封装——面向对象三大特征之一 (重要)

知识点: 将数据域设为私有保护数据, 并且使类易于维护.

7.1 为何要使用封装

在程序清单5中, CircleWithStaticMembers类的数据域radius和numberOfObjects可以直接修改. 例如: cl.radius = 5 或 CircleWithStaticMembers. numberOfObjects=10.

这不是一个好的做法, 原因有两点:

7.2 如何使用封装

设计指南: 为防止数据域被篡改以及使类更易于维护, 最好将数据域声明为私有的.

为了避免对数据域的直接修改, 我们要对需要保护的数据进行封装.

7.3 get和set方法

get方法有如下签名:

  1. public returnType getPropertyName()

如果返回值类型是boolean型, 习惯上如下定义get方法:

  1. public boolean isPropertyName()

set方法有如下签名:

  1. public void setPropertyName(dataType propertyValue)

Eclipse自动为数据域添加get和set方法. 操作如下:
image_1bdh9lm58oe71bcaq1i189pfsm9.png-109.5kB

7.4 封装Circle类的数据

现在来创建一个新的圆类, radius设置为私有数据域, 并有相关的get访问器和set修改器. 类图如下图所示.
image_1bdca77b9uto15uklp117vih3234.png-84.6kB
图19 Circle类封装了圆的属性并提供了get/set方法以及其他方法

程序清单中定义一个名为CircleWithPrivateDataFields的新的圆类.

代码清单 CircleWithPrivateDataFields.java

  1. public class CircleWithPrivateDataFields {
  2. /** The radius of the circle */
  3. private double radius = 1;
  4. /** The number of the objects created */
  5. private static int numberOfObjects = 0;
  6. /** Construct a circle with radius 1 */
  7. public CircleWithPrivateDataFields() {
  8. numberOfObjects++;
  9. }
  10. /** Construct a circle with a specified radius */
  11. public CircleWithPrivateDataFields(double newRadius) {
  12. radius = newRadius;
  13. numberOfObjects++;
  14. }
  15. /** Return radius */
  16. public double getRadius() {
  17. return radius;
  18. }
  19. /** Set a new radius */
  20. public void setRadius(double newRadius) {
  21. radius = (newRadius >= 0) ? newRadius : 0;
  22. }
  23. /** Return numberOfObjects */
  24. public static int getNumberOfObjects() {
  25. return numberOfObjects;
  26. }
  27. /** Return the area of this circle */
  28. public double getArea() {
  29. return radius * radius * Math.PI;
  30. }
  31. }

getRadius()方法(第20-22行)返回半径值, setRadius(newRadius)方法(第25-27行)给对象设置新的半径, 如果新半径为负, 就将这个对象的半径设置为0. 因为这些方法是读取和修改半径的唯一途径, 所以, 你完全控制了如何访问radius属性.

下面程序清单给出一个测试程序, 它使用Circle类创建一个Circle对象, 然后使用setRadius方法修改它的半径. 代码清单如下:
代码清单 TestCircleWithPrivateDataFields.java

  1. public class TestCircleWithPrivateDataFields {
  2. /** Main method */
  3. public static void main(String[] args) {
  4. // Create a Circle with radius 5.0
  5. CircleWithPrivateDataFields myCircle = new CircleWithPrivateDataFields(5.0);
  6. System.out.println("The area of the circle of radius "
  7. + myCircle.getRadius() + " is " + myCircle.getArea());
  8. // Increase myCircle's radius by 10%
  9. myCircle.setRadius(myCircle.getRadius() * 1.1);
  10. System.out.println("The area of the circle of radius "
  11. + myCircle.getRadius() + " is " + myCircle.getArea());
  12. }
  13. }

7.5 思考题

  1. public class Circle {
  2. private double radius = 1;
  3. /** Find the area of this circle */
  4. public double getArea() {
  5. return radius * radius * Math.PI;
  6. }
  7. public static void main(String[] args) {
  8. Circle myCircle = new Circle();
  9. System.out.println("Radius is " + myCircle.radius);
  10. }
  11. }

8 向方法传递对象参数

知识点: 给方法传递一个对象, 是将对象的引用传递给方法.

回忆下, java中的引用类型和基本类型的本质区别.
传递引用类型, 其实传递的是访问具体对象的地址(钥匙), 而非对象本身.

可以将对象传递给方法. 同传递数组一样, 传递对象实际上是传递对象的引用. 下面的代码将myCircle对象作为参数传递给printCircle方法:

  1. public class Test {
  2. public static void main(String[] args) {
  3. // CircleWithPrivateDataFields is defined in Listing 9.8
  4. CircleWithPrivateDataFields myCircle = new
  5. CircleWithPrivateDataFields(5.0);
  6. printCircle(myCircle);
  7. }
  8. public static void printCircle(CircleWithPrivateDataFields c) {
  9. System.out.println("The area of the circle of radius "
  10. + c.getRadius() + " is " + c.getArea());
  11. }
  12. }

在上面的代码中, myCircle的值被传递给printCircle方法. 这个值就是一个对Circle对象的引用值.

8.1 传递引用类型的特点

  1. Java只有一种参数传递方式: 值传递(pass-by-value).
  2. 对象属于引用类型, 引用类型传递的也是变量的值, 只不过这个值是指向具体对象的地址而已.
  3. 方法里面对引用类型的数据进行操作, 会影响方法外部的对象.

8.2 传递引用类型和基本类型的差异

程序清单中的程序展示了传递基本类型值和传递引用值的差异.
程序清单 TestPassObject.java

  1. public class TestPassObject {
  2. /** Main method */
  3. public static void main(String[] args) {
  4. // Create a Circle object with radius 1
  5. CircleWithPrivateDataFields myCircle =
  6. new CircleWithPrivateDataFields(1);
  7. // Print areas for radius 1, 2, 3, 4, and 5.
  8. int n = 5;
  9. printAreas(myCircle, n);
  10. // See myCircle.radius and times
  11. System.out.println("\n" + "Radius is " + myCircle.getRadius());
  12. System.out.println("n is " + n);
  13. }
  14. /** Print a table of areas for radius */
  15. public static void printAreas(CircleWithPrivateDataFields c, int times) {
  16. c = new CircleWithPrivateDataFields();
  17. System.out.println("Radius \t\tArea");
  18. while (times >= 1) {
  19. System.out.println(c.getRadius() + "\t\t" + c.getArea());
  20. c.setRadius(c.getRadius() + 1);
  21. times--;
  22. }
  23. }
  24. }

image_1bdhbbqdjg3nkkq1sil1etcd81m.png-27.2kB

这个程序使用CircleWithPrivateDataFields类的一个对象myCircle和n的整数值调用printAreas(myCicle.n)方法(第10行), 打印出半径为1、2、3、4 和5的圆面积所构成的表格, 如样本输出所示.

下图展示执行程序的这个方法的过程中的内存情况. 注意, 对象是存储在堆中的.
image_1bdhbkran1git7kl5rv1mnr1lu1g.png-70.9kB
图 n的值被传递给times, 而myCircle的引用值被传递给printAreas方法中的c

8.3 引用类型为传共享

  • 传递引用类型的参数时, 传递的是对象的引用. 在这种情况下, c具有与myCircle相同的引用值. 因此, 通过在PrintAreas方法内部的c与在方法外的myCircle 来改变对象的属性, 效果是一样的.
  • 引用上的传值在语义上最好描述为传共享(pass-by-sharing), 也就是说, 在方法中引用的对象和传递的对象是一样的.

8.4 思考题

  1. public class Test {
  2. public static void main(String[] args) {
  3. Circle circle1 = new Circle(1);
  4. Circle circle2 = new Circle(2);
  5. swap1(circle1, circle2);
  6. System.out.println("After swap1: circle1 = " +
  7. circle1.radius + " circle2 = " + circle2.radius);
  8. swap2(circle1, circle2);
  9. System.out.println("After swap2: circle1 = " +
  10. circle1.radius + " circle2 = " + circle2.radius);
  11. }
  12. public static void swap1(Circle x, Circle y) {
  13. Circle temp = x;
  14. x = y;
  15. y = temp;
  16. }
  17. public static void swap2(Circle x, Circle y) {
  18. double temp = x.radius;
  19. x.radius = y.radius;
  20. y.radius = temp;
  21. }
  22. }
  23. class Circle {
  24. double radius;
  25. Circle(double newRadius) {
  26. radius = newRadius;
  27. }
  28. }
  1. public class Test {
  2. public static void main(String[] args) {
  3. int[] a = {1, 2};
  4. swap(a[0], a[1]);
  5. System.out.println("a[0] = " + a[0] + " a[1] = " + a[1]);
  6. }
  7. public static void swap(int n1, int n2) {
  8. int temp = n1;
  9. n1 = n2;
  10. n2 = temp;
  11. }
  12. }

程序2

  1. public class Test {
  2. public static void main(String[] args) {
  3. int[] a = {1, 2};
  4. swap(a[0], a[1]);
  5. System.out.println("a[0] = " + a[0] + " a[1] = " + a[1]);
  6. }
  7. public static void swap(int n1, int n2) {
  8. int temp = n1;
  9. n1 = n2;
  10. n2 = temp;
  11. }
  12. }

程序3

  1. public class Test {
  2. public static void main(String[] args) {
  3. T t = new T();
  4. swap(t);
  5. System.out.println("e1 = " + t.e1 + " e2 = " + t.e2);
  6. }
  7. public static void swap(T t) {
  8. int temp = t.e1;
  9. t.e1 = t.e2;
  10. t.e2 = temp;
  11. }
  12. }
  13. class T {
  14. int e1 = 1;
  15. int e2 = 2;
  16. }

程序4

  1. public class Test {
  2. public static void main(String[] args) {
  3. T t1 = new T();
  4. T t2 = new T();
  5. System.out.println("t1's i = " + t1.i + " and j = " + t1.j);
  6. System.out.println("t2's i = " + t2.i + " and j = " + t2.j);
  7. }
  8. }
  9. class T {
  10. static int i = 0;
  11. int j = 0;
  12. T() {
  13. i++;
  14. j = 1;
  15. }
  16. }

9 对象数组

知识点: 数组既可以存储基本类型值, 也可以存储对象.

下面的语句声明并创建了10个Circle对象的数组:

  1. Circle[] circleArray = new Circle[10];

注意: 当使用new操作符创建对象教组后, 这个数组中的每个元素都是初始值为null的引用变量.

为了初始化数组circleArray, 可以使用如下的for循环:

  1. for (int i = 0; i < circleArray.length; i++) {
  2. circleArray[i] = new Circle();
  3. }

9.1 对象的数组实际上是引用变量的数组 (地址数组)

因此, 调用circleArray[1].getArea()实际上调用了两个层次的引用, 如下图所示. circleArray引用了整个数组, circleArray[1]引用了一个Circle对象.

image_1bdhdc0p2v7o1kc8189u1f5mj7u2a.png-33.8kB
图24 在对象数组中, 数组的每个元素都包含一个对象的引用

9.2 思考题

下面的代码有什么错误? 指出原因.

  1. public class Test {
  2. public static void main(String[] args) {
  3. java.util.Date[] dates = new java.util.Date[10];
  4. System.out.println(dates[0]);
  5. System.out.println(dates[0].toString());
  6. }
  7. }

10 不可变对象和类

知识点: 不可变类--new-->不可变对象. 不可变对象的内容不能被改变.

不可变对象(immutable object)和不可变类(immutable class)的定义:

  • 通常, 创建一个对象后, 它的数据域是允许之后改变的.
  • 有时候也需要创建一个一旦创建其内容就不能再改变的对象. 我们称这种对象为一个不可变对象, 而它的类就称为不可变类.

例如: String类就是不可变的. 如果把程序清单中CircleWithPrivateDataFields类的set方法删掉, 该类就变成不可变类, 因为radius是私有的, 所以如果没有set方法, 它的值就不能再改变.

如果一个类是不可变的, 那么它的所有数据域必须都是私有的, 而且没有对任何一个数据域提供公共的set方法.

注意: 一个类的所有数据都是私有的且没有修改器并不意味着它一定是不可变类.

例如: 下面的Student类, 它的所有数据域都是私有的, 而且也没有set方法, 但它不是一个不可变的类.

  1. public class Student {
  2. private int id;
  3. private String name;
  4. private java.util.Date dateCreated;
  5. public Student(int ssn, String newName) {
  6. id = ssn;
  7. name = newName;
  8. dateCreated = new java.util.Date();
  9. }
  10. public int getId() {
  11. return id;
  12. }
  13. public String getName() {
  14. return name;
  15. }
  16. public java.util.Date getDateCreated() {
  17. return dateCreated;
  18. }
  19. }

如下面的代码所示, 使用getDateCreated()方法返回数据域dateCreated. 它是对Date对象的一个引用. 通过这个引用, 可以改变dateCreated的值.

  1. public class Test {
  2. public static void main(String[] args) {
  3. Student student = new Student(111223333, "John");
  4. java.util.Date dateCreated = student.getDateCreated();
  5. // Now dateCreated field is changed!
  6. dateCreated.setTime(200000);
  7. }
  8. }

要使一个类成为不可变的, 它必须满足下面的要求:

10.2 思考题

  1. public class A {
  2. private int[] values;
  3. public int[] getValues() {
  4. return values;
  5. }
  6. }

11 变置的作用域

知识点: 实例变量和静态变量的作用域是整个类, 无论变量是在哪里声明的.

本节将在类的范围内讨论所有变量的作用域规则.

局部变量: 它的声明和使用都在一个方法的内部, 作用范围方法.

类的数据域 (data field): 一个类的实例变量和静态变量. 作用范围为整个类. 类的数据域只能声明一次.

image_1bdhfft8n7mmcenf6u1q8plfa2n.png-64.8kB

知识点: 如果一个局部变量和一个类变量具有相同的名字, 那么局部变量优先. 而同名的类变量将被隐藏 (hidden).

例如: 在下面的程序中, x被定义为一个实例变量, 也在方法中被定义为局部变量.

  1. public class F {
  2. private int x = 0; // Instance variable
  3. private int y = 0;
  4. public F() {
  5. }
  6. public void p() {
  7. int x = 1; // Local variable
  8. System.out.println("x = " + x);
  9. System.out.println("y = " + y);
  10. }
  11. }

假设f是F的一个实例, 那么f.p()的打印输出是什么呢? f.p()的打印输出是: x为1, y为0. 其原因如下:

提示: 为避免混淆和错误, 除了方法中的参数, 不要将实例变量或静态变量的名字作为局部变量名.

下面程序的输出是什么?

  1. public class Test {
  2. private static int i = 0;
  3. private static int j = 0;
  4. public static void main(String[] args) {
  5. int i = 2;
  6. int k = 3;
  7. {
  8. int j = 3;
  9. System.out.println("i + j is " + i + j);
  10. }
  11. k = i + j;
  12. System.out.println("k is " + k);
  13. System.out.println("j is " + j);
  14. }
  15. }

12 this——对象自身的引用(重要)

知识点:
1. 关键字this引用对象自身, this引用对象自身的实例.
2. 用this关键字引用对象的实例成员.

例如, 图(a)的代码使用this来显式地引用对象的radius以及调用它的getArea()方法. 这种情况下, this引用通常是省略掉的, 如图(b)所示.

image_1bec97h621isb181911gb1hm91itbm.png-98.2kB


12.1 使用this引用隐藏数据域

知识点:

  1. 隐藏数据域: 指当类的方法的形式参数或局部变量<--->类的数据域重名时, 这是类的数据域变成了隐藏数据域. 这时不能通过数据域变量名直接引用.
  2. this用法1: 在实现方法时, 当局部变量或方法形参的命名与类的数据域重名时, 需要通过this关键字来引用类的隐藏数据域.
  3. this用法2: 在引用隐藏数据域以及调用一个重载的构造方法的时候, 一定要用this引用.

列如, 在数据域的set方法中, 经常将类的数据域变量名作为参数名. 为了给它设置新值, 需要在方法中引用隐藏的数据域名. 如图(a)所示.

隐藏的实例变量就需要使用关键字this来引用.
隐藏的静态变量可简单地通过"类名.静态变量"的方式引用.

image_1bec9m6866cbbfb1012og8fp13.png-99.5kB
图 关键宇this引用调用方法的对象

  1. 调用f1.setI(10)时, 执行了this.i=i, 将参数i的值赋给对象f1的数据域i. 关键字this是指调用实例方法setI的对象.
  2. F.k=k这一行指将参数k的值赋给这个类的静态数据域k, k是被类的所有对象所共享.

12.2 使用this调用构造方法

关键字this可以用于调用同一个类的另一个构造方法. 例如, 可以如下改写Circle类:

image_1becve6b2sfc1s2crjd10ni13it9.png-70.6kB

在第二个构造方法中, this(1.0)这一行调用带double值参数的第一个构造方法.

使用攻略:

  1. Java 要求在构造方法中, 语句 this(参数列表) 应在任何其他可执行语句之前出现. this调用写第一句.
  2. 如果一个类有多个构造方法, 最好尽可能使用this(参数列表) 实现它们. 通常, 无参数或参数少的构造方法可以用this(参数列表) 调用参数多的构造方法. 这样做通常可以简化代码, 使类易于阅读和维护.

12.3 复习题

  1. public class C {
  2. private int p;
  3. public C() {
  4. System.out.println("C's no-arg constructor invoked");
  5. this(0);
  6. }
  7. public C(int p) {
  8. p = p;
  9. }
  10. public void setP(int p) {
  11. p = p;
  12. }
  13. }
  1. public class Test {
  2. private int id;
  3. public void m1() {
  4. this.id = 45;
  5. }
  6. public void m2() {
  7. Test.id = 45;
  8. }
  9. }

关键术语

Unified Modeling Language (UML) (统一建模语言)
class (类)
object (对象)
object-oriented programming (OOP)(面向对象程序设计)
reference type (引用类型)
reference variable (引用变童)
anonymous object (匿名对象)
attribute (属性)
behavior (行为)
class's variable (类变量)
date field (数据域)
data field encapsulation (数据域封装)
setter or mutator (设置方法 或 修改器)
package-private or package-access (包私有 或 包访问)

constructor (构造方法)
default constructor (默认构造方法)
no-arg constructor (无参构造方法)
dot operator (.) (点操作符)
getter or accessor (访问器 或 读取器)

instance (实例)
instance method (实例方法)
instance variable (实例变量)
instantiation (实例化)
immutable class (不可变类)
immutable object (不可变对象)

null (空)

private constructor (私有的构造方法)
public class (公共类)
static method (静态方法)
static variable (静态变量)
this (this关键字)

知识点总结

  1. 类是对象的模板. 它定义对象的属性, 并提供用于以创建对象的构造方法以及操作对象的普通方法.
  2. 类也是一种自定义的数据类型. 可以用它声明对象引用变量. 对象引用变量中似乎存放了一个对象, 但事实上, 它包含的只是对该对象的引用. 严格地讲, 对象引用变量和对象是不同的, 但是大多数情况下, 它们的区别是可以忽略的.
  3. 对象是类的实例. 可以使用new操作符创建对象, 使用点搡作符(.)通过对象的引用变量来访问该对象的成员.
  4. 所有传递给方法的参数都是值传递的. 对于基本类型的参数, 传递的是实际值; 而若参数是引用数据类型, 则传递的是对象的引用.
  5. 实例变量或方法属于类的一个实例. 它的使用与各自的实例相关联. 静态变量是被同一个类的所有实例所共享的. 可以在不使用实例的情况下调用静态方法.
  6. 类的每个实例都能访问这个类的静态变量和静态方法. 然而, 为淸晰起见, 最好使用"类名.变量" 和 "类名.方法"来调用静态变量和静态方法.
  7. 可见性修饰符指定类, 方法和数据是如何被访问的. 公共的(public)类,方法或数据可以被任何外部程序所访问, 私有的(private)方法或数据只能在本类内被访问.
  8. 可以提供get(访问器)方法或者set(修改器)方法使程序能够看到或修改数据.
  9. get方法具有方法签名public returnType getPropertyName()。 如果返回值类型(returnType)是boolean型, 则get方法应该定义为 public boolean isPropertyName()。
  10. set方法具有方法签名public void setPropertyName(dataType propertyValue).
  11. Java数组是一个可以包含基本类型值或对象类型值的对象. 当创建一个对象数组时, 它的元素被赋予默认值null.
  12. 实例变童和静态变量的作用域是整个类, 无论该变量在什么位置定义. 为一致性考虑, 建议都在类的开始部分定义.
  13. this关键字可用于引用进行调用的对象. 它也可以用于在构造方法中来调用同一个类的另外一个构造方法。

习题

1. 矩形类Rectangle

(矩形类Rectangle) 遵照2.1节中Circle类的例子, 设计一个名为Rectangle 的类表示矩形. 这个类包括:

2. 股票类Stock

(股票类Stock) 遵照2.1节中Circle类的例子, 设计一个名为Stock的类. 这个类包括:

3. 日期类Date

(使用日期类Date: java.util.Date) 编写程序创建一个Date对象, 设置它的流逝时间分别为10000、100000、1000000、10000000、100000000、1000000000、10000000000、100000000000, 然后使用toString()方法分别显示上述日期.

4. 秒表类Stopwatch

(秒表)设计一个名为Stopwatch的类. 该类包含:

5. 账户类Account

(账户类Account) 设计一个名为Account 的类, 它包括:

6. 风扇类Fan

(风扇类Fan)设计一个名为Fan的类来表示一个风扇. 这个类包括:

7. 正n边形问题

在一个正n边形中, 所有边的长度都相同, 且所有角的度数都相同 (即这个多边形是等边等角的). 设计一个名为ReguUrPolygon的类, 该类包括:

8. 二次方程式

为二次方程式设计一个名为QuadraticEquation 的类. 这个类包括:

9. 线性方程

为一个 的线性方程设计一个名为LinearEquation的类:
image_1bduamvt41kncf4n1379km91bp29.png-16.1kB
这个类包括:

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注