返回列表 发帖

SUN JAVA标准教程

第六节  子 类
5.6.1  is a 关系
Is a 关系
    Employee类
         public class Employee {
             String name;
             Date hireDate;
             Date dateOfBirth;
         }
    在编程中,常常要创建某件事的模型(如:一个职员),然后需要一个该基本模型的更专业化的版本。比如,可能需要一个经理的模型。显然经理实际上是一个职员,只是一个带有附加特征的职员。
    看看下面的例子中类声明所阐述的:
public class Employee {
    String name;
    Date hireDate;
    Date dateOfBirth;
    String jobTitle;
    int grade;
    ...
}
这个例子阐述了在Manager和Employee类之间的数据复制。此外,还可能有许多适用于 Employee和 Manager两者的方法。因此,需要有一种办法从现有类来创建一个新类。这就叫做子类。
5.6.2  Extends关键字
Extends关键字
public class Employee {
  String name;
  Date hireDate;
  Date dateOfBirth;
  String jobTitle;
  int grade;
  ...
}
public class Manager extends Employee {
  String department;
  Employee [] subordinates;
}
  在面向对象的语言中,提供了特殊的机制,允许程序员用以前定义的类来定义一个类。如下所示,可用关键字extends来实现:
public class Employee {
  String name;
  Date hireDate;
  Date dateOfBirth;
  String jobTitle;
  int grade;
  ...
}
public class Manager extends Employee {
  String department;
  Employee [] subordinates;
  ...
}
在这样的安排中,Manager类被定义,具有 Employee 所拥有的所有变量及方法。所有这些变量和方法都是从父类的定义中继承来的。所有的程序员需要做的是定义额外特征或规定将适用的变化。
注--这种方法是在维护和可靠性方面的一个伟大进步。如果在Employee类中进行修改,那么, Manager类就会自动修改,而不需要程序员做任何工作,除了对它进行编译。

注—对一个继承的方法或变量的描述只存在于对该成员进行定义的类的API文档中。当浏览探索一个(子)类的时候,一定要检查父类和其它祖先类中的继承成员。

5.6.3  参数和异类收集
参数和异类收集
    具有共同点的类的收集被称做同类收集,如:数组
    具有不同对象的收集叫异类收集,如:从各种其它类继承的类的收集
    可以创建具有共同类的对象的收集(如数组)。这种收集被称作同类收集。
    Java编程语言有一个对象类,因此,由于多态性,它能收集所有种类的元素,正如所有类都扩展类对象一样。这种收集被称作异类收集。
    创建一个Manager并慎重地将其引用赋到类型Employee的变量中似乎是不现实的。但这是可能的,而且有很多为什么要取得这种效果的理由。
    用这种方法,可以写出一个能接受通用对象的方法,在这种情况下,就是类Employee,并在它的任何子类的对象上正确地运作。然后可以在应用类中产生一个方法,该应用类抽取一个职员,并将它的薪水与某个阈值进行比较来决定该职员的纳税责任。利用多态性,可以做到这些:
// In the Employee class
public TaxRate findTaxRate(Employee e) {
  // do calculations and return a tax rate for e
}
// Meanwhile, elsewhere in the application class
Manager m = new Manager();
:
TaxRate t = findTaxRate(m);
    这是合法的,因为一个经理就是一个职员。
    异类收集就是不相似的东西的收集。在面向对象语言中,可以创建许多东西的收集。所有的都有一个共同的祖先类-Object类。如:
Employee [] staff = new Employee[1024];
staff[0] = new Manager();
staff[1] = new Employee();
甚至可以写出一个排序的方法,它将职员按年龄或薪水排序,而忽略其中一些人可能是经理。
注-每个类都是Object的一个子类,因此,可以用Object数组作为任何对象的容器。唯一不能被增加到Object数组中的唯一的东西就是基本变量(以及包装类,将在模块6中讨论,请注意这一点)。比Object数组更好的是向量类,它是设计来贮存异类收集对象的。

TOP

SUN JAVA标准教程

5.6.4  单继承性
单继承性
当一个类从一个唯一的类继承时,被称做单继承性。单继承性使代码更可靠。界面提供多继承性的好处,而且没有(多继承的)缺点。
    Java编程语言允许一个类仅能扩展成一个其它类。这个限制被称做单继承性。单继承性与多继承性的优点是面向对象程序员之间广泛讨论的话题。Java编程语言加强了单继承性限制而使代码更为可靠,尽管这样有时会增加程序员的工作。模块6讨论一个被叫做界面(Interface)的语言特征,它允许多继承性的大部分好处,而不受其缺点的影响。
    使用继承性的子类的一个例子如图5-1所示:



图5-1      继承
5.6.5  构造函数不能被继承
构造函数不能被继承
子类从超类(父类)继承所有方法和变量
子类不从超类继承构造函数
包含构造函数的两个办法是
    使用缺省构造函数
    写一个或多个显式构造函数
    尽管一个子类从父类继承所有的方法和变量,但它不继承构造函数,掌握这一点很重要。
一个类能得到构造函数,只有两个办法。或者写构造函数,或者根本没有写构造函数,类有一个缺省构造函数。
注-除了子构造函数外,父类构造函数也总是被访问。这将在后面的模块中详细讨论。
5.6.6  多态性
多态性
    有能力拥有有许多不同的格式,就叫做多态性。比如,经理类能访问职员类方法。
    一个对象只有一个格式
    一个变量有许多格式,它能指向不同格式的对象
    将经理描述成职员不只是描述这两个类之间的关系的一个简便方法。回想一下,经理类具有父类职员类的所有属性、成员和方法。这就是说,任何在Employee上的合法操作在Manager上也合法。如果Employee 有raiseSalary()和fire()两个方法,那么Manager 类也有。
一个对象只有一个格式(是在构造时给它的)。但是,既然变量能指向不同格式的对象,那么变量就是多态性的。在Java编程语言中,有一个类,它是其它所有类的父类。这就是java.lang.object类。因此,实际上,以前的定义被简略为:
public class Employee extends Object and
public class Manager extends Employee
    Object类定义许多有用的方法,包括toString(),它就是为什么Java软件中每样东西都能转换成字符串表示法的原因。(即使这仅具有有限的用途)。
    象大多数面向对象语言一样,Java实际上允许引用一个带有变量的对象,这个变量是父类类型中的一个。因此,可以说:
Employee e = new Manager()
    使用变量e是因为,你能访问的对象部分只是Employee的一个部分;Manager的特殊部分是隐藏的。这是因为编译者应意识到,e 是一个Employee,而不是一个Manager。因而,下述情况是不允许的:
e.department = " Finance " ; // illegal
注-多态性是个运行时问题,与重载相反,重载是一个编译时问题。
5.6.7  关键字super
关键字super
    super被用在类中引用其超类。
    super被用来调用超类的成员变量。
    超类行为就被调用,就好象对象是超类的组件。
    调用行为不必发生在超类中,它能自动向上层类追溯。
    关键字super可被用来引用该类中的超类。它被用来引用超类的成员变量或方法。
    通常当覆盖一个方法时,实际目的不是要更换现有的行为,而是要在某种程度上扩展该行为。
    用关键字super 可获得:
public class Employee {
  private String name;
  private int salary;
public String getDetails() {
  return Name: " + name + "\nSalary: " + salary;
  }
}
public class Manager extends Employee {
  private String department;
  public String getDetails() {
    return super.getDetails() + // call parents'
                         // method
      "\nDepartment: " + department;
  }
}
    请注意,super.method()格式的调用,如果对象已经具有父类类型,那么它的方法的整个行为都将被调用,也包括其所有副面效果。该方法不必在父类中定义。它也可以从某些祖先类中继承。
5.6.8  Instanceof 运算符
Instanceof  运算符
public class Employee extends Object
public class Manager extends Employee
public class Contractor extends Employee
public void method(Employee e) {
  if (e instanceof Manager) {
    // Get benefits and options along with salary
  }else if (e instanceof Contractor) {
    // Get hourly rates
  }else {
    // temporary employee
  }
}
    假如能使用引用将对象传递到它们的父类中,那么有时你想知道实际有什么,这就是instanceof运算符的目的。假设类层次被扩展,那么你就能得到:
public class Employee extends Object
public class Manager extends Employee
public class Contractor extends Employee
注—记住,在可接受时, extends Object 实际上是多余的。在这里它仅作为一个提示项。
    如果通过Employee类型的引用接受一个对象,它变不变成Manager或Contractor都可以。可以象这样用instanceof 来测试:
public void method(Employee e) {
  if (e instanceof Manager) {
    // Get benefits and options along with salary
  }else if (e instanceof Contractor) {
    // Get hourly rates
  }else {
    // regular employee
  }
}
注—在C++中,可以用RTTI(运行时类型信息)来做相似的事,但在Java编程语言中的instanceof功能更强大。

TOP

SUN JAVA标准教程

5.6.9  对象的类型转换
对象的类型转换
    使用instanceof来测试一个对象的类型。
    用类型转换来恢复一个对象的全部功能。
    用下述提示来检查类型转换的正确性:
  向上的类型转换是隐含地实现的。
  向下的类型转换必须针对子类并由编译器检查。
    当运行时错误发生时,运行时检查引用类型。
    在你接收父类的一个引用时,你可以通过使用Instanceof运算符判定该对象实际上是你所要的子类,并可以用类型转换该引用的办法来恢复对象的全部功能。
public void method(Employee e) {
  if (e instanceof Manager) {
    Manager m = (Manager)e;
    System.out.println( " This is the manager of " + m.department);
  }
  // rest of operation
}
    如果不用强制类型转换,那么引用e.department的尝试就会失败,因为编译器不能将被称做department的成员定位在Employee类中。
    如果不用instanceof做测试,就会有类型转换失败的危险。通常情况下,类型转换一个对象引用的尝试是要经过几种检查的:
    向上强制类型转换类层次总是允许的,而且事实上不需要强制类型转换运算符。可由简单的赋值实现。
    对于向下类型转换,编译器必须满足类型转换至少是可能的这样的条件。比如,任何将Manager引用类型转换成Contractor引用的尝试是肯定不允许的,因为Contractor不是一个Manager。类型转换发生的类必须是当前引用类型的子类。
    如果编译器允许类型转换,那么,该引用类型就会在运行时被检查。比如,如果instanceof检查从源程序中被省略,而被类型转换的对象实际上不是它应被类型转换进去的类型,那么,就会发生一个运行时错误(exception)。异常是运行时错误的一种形式,而且是后面模块中的主题。
第七节  覆盖方法
覆盖方法
    子类可以修改从父类继承来的行为
子类能创建一个与父类方法有不同功能的方法,但具有相同的
    名称
  返回类型
         参数表
    除了能用附加额外特征的办法在旧类基础上产生一个新类,它还可以修改父类的当前行为。
    如果在新类中定义一个方法,其名称、返回类型及参数表正好与父类中方法的名称、返回类型及参数相匹配,那么,新方法被称做覆盖旧方法。
注—记住,在同类中具有相同名称不同参数表的方法是被简单覆盖。这导致编译器使用所提供的参数来决定调用哪个方法。
    考虑一下在Employee和Manager类中的这些方法的例子:
public class Employee {
  String name;
  int salary;
  public String getDetails() {
    return " Name: " + name + " \n " +
       "Salary: " + salary;
  }
}
public class Manager extends Employee {
  String department;
  public String getDetails() {
    return " Name: " + name + " \n " +
         " Manager of " + department;
  }
}
Manager类有一个定义的getDetails()方法,因为它是从Employee类中继承的。基本的方法被子类的版本所代替或覆盖了。
覆盖方法
    虚拟方法调用
           Employee e = new Manager();
           e.getDetails();
    编译时类型与运行时类型  
假设第121页上的例子及下述方案是正确的:
Employee e = new Employee();
Manager m = new Manager();
   如果请求e.getDetails()和m.getDetails,就会调用不同的行为。Employee对象将执行与Employee有关的getDetails版本,Manager对象将执行与Manager有关的getDetails()版本。
   不明显的是如下所示:
Employee e = new Manager();
e.getDetails();
    或某些相似效果,比如一个通用方法参数或一个来自异类集合的项。
    事实上,你得到与变量的运行时类型(即,变量所引用的对象的类型)相关的行为,而不是与变量的编译时类型相关的行为。这是面向对象语言的一个重要特征。它也是多态性的一个特征,并通常被称作虚拟方法调用。
    在前例中,被执行的e.getDetails()方法来自对象的真实类型,Manager。
注—如果你是C++程序员,就会在Java编程语言和C++之间得出一个重要的区别。在C++中,你要想得到该行为,只能在源程序中将方法标记成virtual。然而,在纯面向对象语言中,这是不正常的。当然,C++这么做是要提高执行速度。

TOP

SUN JAVA标准教程

第八节  调用覆盖方法
覆盖方法的规则
    必须有一个与它所覆盖的方法相同的返回类型
    不能比它所覆盖的方法访问性差
    不能比它所覆盖的方法抛出更多的异常。
覆盖方法的规则
    记住,子方法的名称以及子方法参数的顺序必须与父类中的方法的名称以及参数的顺序相同以便该方法覆盖父类版本。下述规则适用于覆盖方法:
    覆盖方法的返回类型必须与它所覆盖的方法相同。
覆盖方法不能比它所覆盖的方法访问性差
覆盖方法不能比它所覆盖的方法抛出更多的异常。(异常将在下一个模块中讨论。)
    这些规则源自多态性的属性和Java编程语言必须保证“类型安全”的需要。考虑一下这个无效方案:
public class Parent {
  public void method() {
  }
}
public class Child extends Parent {
  private void method() {
  }
}
public class UseBoth {
  public void otherMethod() {
    Parent p1 = new Parent();
    Parent p2 = new Child();
    p1.method();
    p2.method();
  }
}
Java编程语言语义规定,p2.method()导致方法的Child版本被执行,但因为方法被声明为private,p2(声明为Parent)不能访问它。于是,语言语义冲突。
第九节  调用父类构造函数
调用父类构造函数
    对象的初始化是非常结构化的。
    当一个对象被初始化时,下述行为按顺序发生:
    存储空间被分配并初始化到0值
    层次中的每个类都进行显式初始化
    层次中的每个类都调用构造函数
    在Java编程语言中,对象的初始化是非常结构化的。这样做是要保证安全。在前面的模块中,看到了当一个特定对象被创建的实例时发生了什么。由于继承性,图象被完成,而且下述行为按顺序发生:
    存储空间被分配并初始化到0值
    进行显式初始化
        调用构造函数
    层次中的每个类都会发生最后两个步骤,从最上层开始。
    Java技术安全模式要求在子类执行任何东西之前,描述父类的一个对象的各个方面都必须初始化。因此,Java编程语言总是在执行子构造函数前调用父类构造函数的版本。
调用父类构造函数
    在许多情况下,使用缺省构造函数来对父类对象进行初始化。
public class Employee {
  String name;
  public Employee(String n) {
    name = n;
  }
}
public class Manager extends Employee {
  String department;
  public Manager(String s, String d) {
    super(s);
    department = d;
  }
}

    无论是super还是this,都必须放在构造函数的第一行
调用父类构造函数
通常要定义一个带参数的构造函数,并要使用这些参数来控制一个对象的父类部分的构造。可能通过从子类构造函数的第一行调用关键字super的手段调用一个特殊的父类构造函数作为子类初始化的一部分。要控制具体的构造函数的调用,必须给super()提供合适的参数。当不调用带参数的super时,缺省的父类构造函数(即,带0个参数的构造函数)被隐含地调用。在这种情况下,如果没有缺省的父类构造函数,将导致编译错误。
public class Employee {
  String name;
  public Employee(String n) {
    name = n;
  }
}
public class Manager extends Employee {
  String department;
  public Manager(String s, String d) {
    super(s); // Call parent constructor
           // with String argument
  department = d;
  }
}
注—调用super()能将任意数量的合适的参数赋到父类中的不同构造函数中,但它必须是构造函数中的第一个语句。
    当被使用时,super或this必须被放在构造函数的第一行。显然,两者不能被放在一个单独行中,但这种情况事实上不是一个问题。如果写一个构造函数,它既没有调用super(…)也没有调用this(…),编译器自动插入一个调用到父类构造函数中,而不带参数。其它构造函数也能调用super(…)或this(…),调用一个Static方法和构造函数的数据链。最终发生的是父类构造函数(可能几个)将在链中的任何子类构造函数前执行。
第十节  编组类
5.10.1  包

    包声明必须在源程序文件的开始被声明
    根据源程序文件,只允许有一个包声明
           // Class Employee of the Finance department for the
           // ABC company
           package abc.financedept;
           public class Employee {
           ...
           }
   包名称是分层的,由圆点隔开
    Java编程语言提供package机制作为把相关类组成组的途径。迄今为止,所有的这些例子都属于缺省或未命名包。
    可以用package语句表明在源程序文件中类属于一个特殊的包。
           // Class Employee of the Finance department for the
           // ABC company
           package abc.financedept;
           public class Employee {
           ...
           }
    包声明,如果有的话,必须在源程序文件的开始处。可以以空白和注解开始,而没有其它方式。只允许有一个包声明并且它控制整个源程序文件。
包名称是分层的,由圆点分隔。通常情况下,包名称的元素被整个地小写。然而,类名称通常以一个大写字母开始,而且每个附加单词的首字母可以被大写以区分类名称中的单词。
5.10.2  import语句
Import语句
告诉编译器到哪儿寻找类来使用,必须先于所有类声明
        import abc.financeDept.*;
        public class Manager extends Employee {
          String department;
          Employee [] subordinates;
        }
    当需要使用包时,使用import语句来告诉编译器到哪儿去寻找类。事实上,包名称在包中(比如,abc.financedept)形成类的名称的一部分。你可以引用Employee类作为abc.financedept.Employee,或可以使用import语句及仅用类名称Employee。
import abc.financeDept.*;
        public class Manager extends Employee {
          String department;
          Employee [] subordinates;
        }
注—import语句必须先于所有类声明。                  
当使用一个包声明时,不必引入同样的包或该包的任何元素。记住,import语句被用来将其它包中的类带到当前名空间。当前包,不管是显式的还是隐含的,总是当前名空间的一部分。
5.10.3  目录布局及CLASSPATH环境变量
目录布局及CLASSPATH环境变量
    包被贮存在包含包名称的目录树中。
  package abc.financedept
  public class Employee  {
  }
  
  javac –d .  Employee.java
Employee.class的目录路径是什么?

   包被贮存在包含包名称分支的目录树中。例如,来自前面页中的Employee.class文件必须存在于下述目录中:
      path\abc\financeDept
    查寻类文件的包的目录树的根目录是在 CLASSPATH中。
    编译器的-d选项规定了包层次的根,类文件被放在它里面(前面所示的path)。
    Java技术编译器创建包目录,并在使用-d选项时将编译的类文件移到它当中。
c:\jdk1.2\source\> javac -d . Employee.java
将创建目录结构abc\financedept 在当前目录中(“.”)。
    CLASSPATH变量以前没有使用过,因为如果它没被设定,那么,工具的缺省行为将自动地包括类分布的标准位置和当前工作目录。如果想访问位于其它地方的包,那么必须设定CLASSPATH变量来显式地覆盖缺省行为。
c:\jdk1.2\source\>javac -d c:\mypackages Employee.java
    为了让编译器在编译前页中的Manager.java 文件时给abc.financedept. Employee类定位, CLASSPATH环境变量必须包括下述包路径:
      CLASSPATH=c:\mypackages;.

TOP

SUN JAVA标准教程

练习:使用对象及类
    练习目的—会写、编译及运行三个程序,通过模仿使用银行帐目使用继承、构造函数及数据隐藏等面向对象概念。
一、准备
  为了成功地完成该实验,必须理解类和对象的概念。
二、任务
    一级实验:银行帐目
1.创建一个类,Account.java,它定义银行帐目。决定应该做什么样的帐目,需要贮存什么样的数据,以及将用什么样的方法。
2.使用一个包,bank,来包含类
    二级实验:帐目类型
1.修改一级实验,因而会针对CheckingAccount类的细节对Account划分子类。
2.允许检查帐目来提供溢出保护。
    三级实验:在线帐目服务
1.创建一个简单的应用程序,Teller.java,它使用一级或二级实验来提供一个在线帐目开户服务。
三、练习总结
    讨论—花几分钟时间来讨论一下实验练习中的经验、问题或发现。
-经验 解释 总结 应用
四、检查进步情况
    在继续下一个模块前,检查一下,确信你能:
-定义封装、多态性和继承
-使用访问修饰符private和public
-开发一个程序段来创建并初始化一个对象
-在一个特定的对象上调用方法
-描述构造函数和方法重载
-描述this引用起什么作用
-讨论为什么Java应用程序代码是可重用的                                    
    在Java软件程序中,确认:
-package语句
-import语句
-类成员函数和变量
-构造函数
-重载方法
-覆盖方法
-父类构造函数
五、思考
    既然理解了对象和类,如何在当前或今后工作中使用这个信息?

TOP

SUN JAVA标准教程

第六章 高级语言特征
本模块讨论Java编程语言更多的面向对象特征。
第一节  相关问题
    讨论-下述问题与本模块中出现的材料相关:
-如何保持一个类或方法不被分成子类或被覆盖?
-如何将数组概念的使用扩展到对象?
第二节  目 的
    完成本模块的学习后,应该能
-描述static变量,方法和初始程序
-描述final类,方法和变量
-列出访问控制级别
-确认降级类并解释如何从JDK1.0升迁到JDK1.1到JDK1.2
-描述如何应用收集和反射
-在Java软件程序中,确认
-static方法和变量
-public,private,protected和缺省变量
-使用abstract类和方法
-解释如何以及何时使用内部类
-解释如何以及何时使用接口
-描述==和equals()之间的不同
第三节  类(static)变量
类(static)变量
    在所有类的实例中共享
    可以被标记为public或private
    如果被标记为public而没有该类的实例,可以从该类的外部访问
           public class Count {
             private int serialNumber;
             private static int counter = 0;
             public Count() {
               counter++;
               serialNumber = counter;
             }
           }
    有时想有一个可以在类的所有实例中共享的变量。比如,这可以用作实例之间交流的基础或追踪已经创建的实例的数量。
    可以用关键字static来标记变量的办法获得这个效果。这样的变量有时被叫做class variable,以便与不共享的成员或实例变量区分开来。
   public class Count {
             private int serialNumber;
             private static int counter = 0;
             public Count() {
               counter++;
               serialNumber = counter;
             }
           }
    在这个例子中,被创建的每个对象被赋于一个独特的序号,从1开始并继续往上。变量counter在所有实例中共享,所以,当一个对象的构造函数增加counter时,被创建的下一个对象接受增加过的值。  
    Static变量在某种程度上与其它语言中的全局变量相似。Java编程语言没有这样的全局语言,但static变量是可以从类的任何实例访问的单个变量。
    如果static变量没有被标记成private,它可能会被从该类的外部进行访问。要这样做,不需要类的实例,可以通过类名指向它。
public class StaticVar {
  public static int number;
}
public class OtherClass [
  public void method() {
    int x = StaticVar.number;
  }
}

TOP

SUN JAVA标准教程

第四节类(static)方法
类(static)方法
    没有它所属的类的任何实例,static方法可以被调用
        public class GeneralFunction {
             public static int addUp(int x, int y) {
               return x + y;
             }
           }
           public class UseGeneral {
             public void method() {
               int a = 9;
               int b = 10;
               int c = GeneralFunction.addUp(a, b);
               System.out.println("addUp() gives " + c);
             }
           }
    当没有一个特殊对象变量的实例的时候,有时需要访问程序代码。用关键字static标记的方法可以这样使用,有时被称做class method。static方法可以用类名而不是引用来访问,如:
public class GeneralFunction {
             public static int addUp(int x, int y) {
               return x + y;
             }
           }
           public class UseGeneral {
             public void method() {
               int a = 9;
               int b = 10;
               int c = GeneralFunction.addUp(a, b);
               System.out.println("addUp() gives " + c);
             }
           }
    因为static方法不需它所属的类的任何实例就会被调用,因此没有this值。结果是,static方法不能访问与它本身的参数以及static变量分离的任何变量。访问非静态变量的尝试会引起编译错误。
注-非静态变量只限于实例,并只能通过实例引用被访问。
public class Wrong {
  int x;
  public static void main(String args[]) {
    x = 9; // COMPILER ERROR!
  }
}
Import points to remember about static methods:
    Main()是静态的,因为它必须在任何实例化发生前被顺序地访问,以便应用程序的运行。
    静态方法不能被覆盖成非静态。
第五节静态初始化程序
静态初始化程序
    在static block中,类可以包含方法程序中不存在的代码。
当类被装载时,静态代码块只执行一次。
    方法程序体中不存在的代码在static block中类可以包含该代码,这是完全有效的。当类被装载时,静态块代码只执行一次。类中不同的静态块按它们在类中出现的顺序被执行。
public class StaticInitDemo {
static int i = 5;
  static {
    System.out.println("Static code i= "+ i++ );
  }
}
public class Test {
  public static void main(String args[]) {
    System.out.println("Main code: i="
      + StaticInitDemo.i);
  }
}
将打印出:
Static code: i=5
Main code: i=6
Static 方法和数据

TOP

SUN JAVA标准教程

){
23.  MyClass instance1 = new MyClass(1,2.0);
24.  MyClass instance2 = new MyClass(3,4.0);
25.  
26.  MyClass.statMethod(); //Outputs:statInt=4;
27.         //statDouble=16.0
28.
29.  instance1.instMethod(); //Outputs:instInt=1;
30.        //instDouble=2.0
31.  instance1.statMethod(); //Outputs:statInt=4;
32.        //statDouble=16.0
33.  
34.  instance2.instMethod(); //Outputs:instInt=3;
35.        //instDouble=4.0
36.  instance2.statMethod(); //Outputs:statInt=4;
37.        //statDouble=16.0
38.}
39.}
37.
    图6-1是MyClass类定义的框图。这个例子阐述了:
1.Static方法和数据的单个(共享)副本是因为类和该类的所有实例而存在。通过一个实例或通过类本身可以访问static成员。
2.非静态数据只限于实例,只能通过该实例的非静态方法对它进行访问。非静态数据定义对象之间互不相同的特点,非静态方法在它们所作用的非静态数据的基础上对每个对象的行为互不相同。
    考虑一下模仿汽车的特殊类型的一个对象的实例。轮子的大小,对该类型的所有汽车来说是个常量,可能被模仿成一个静态变量。颜色根据对象的不同而不同,其行为也根据对象的不同而不同,在它所作用的非静态数据的基础上对不同对象返回不同的颜色。

TOP

SUN JAVA标准教程

第七节  关键字final
6.7.1  Final类
关键字final
    Final类不能被分成子类
Final方法不能被覆盖
Final变量是常数
  Java编程语言允许关键字Final被应用到类中。如果这样做了,类便不能被子分成子类。比如,类Java.lang.String就是一个final类。这样做是出于安全原因,因为它保证,如果方法有字符串的引用,它肯定就是类String的字符串,而不是某个其它类的字符串,这个类是String的被修改过的子类,因为String可能被恶意窜改过。
6.7.2  Final方法
    个体方法也可以被标记为final。被标记为final的方法不能被覆盖。这是由于安全原因。如果方法具有不能被改变的实现,而且对于对象的一致状态是关键的,那么就要使方法成为final。
    被声明为final的方法有时被用于优化。编译器能产生直接对方法调用的代码,而不是通常的涉及运行时查找的虚拟方法调用。
    被标记为static或private的方法被自动地final,因为动态联编在上述两种情况下都不能应用。
6.7.3  Final变量
    如果变量被标记为final,其结果是使它成为常数。想改变final变量的值会导致一个编译错误。下面是一个正确定义final变量的例子:
public final int MAX_ARRAY_SIZE = 25;
注-如果将引用类型(即,任何类的类型)的变量标记为final,那么该变量不能指向任何其它对象。但可能改变对象的内容,因为只有引用本身是final。
第八节抽象类
抽象类
    声明方法的存在而不去实现它的类被叫做抽象类
    可以通过关键字abstract进行标记将类声明为抽象
           public abstract class Drawing {
           public abstract void drawDot(int x, int y);
           public void drawLine(int x1, int y1,
                               int x2, int y2) {
             // draw using the drawDot() method repeatedly.
           }
         }
一个abstract类可以包含非抽象方法和变量
    有时在库开发中,要创建一个体现某些基本行为的类,并为该类声明方法,但不能在该类中实现该行为。取而代之,在子类中实现该方法。知道其行为的其它类可以在类中实现这些方法。
    例如,考虑一个Drawing类。该类包含用于各种绘图设备的方法,但这些必须以独立平台的方法实现。它不可能去访问机器的录像硬件而且还必须是独立于平台的。其意图是绘图类定义哪种方法应该存在,但实际上,由特殊的从属于平台子类去实现这个行为。
    正如Drawing类这样的类,它声明方法的存在而不是实现,以及带有对已知行为的方法的实现,这样的类通常被称做抽象类。通过用关键字abstract进行标记声明一个抽象类。被声明但没有实现的方法(即,这些没有程序体或{}),也必须标记为抽象。
           public abstract class Drawing {
           public abstract void drawDot(int x, int y);
           public void drawLine(int x1, int y1,
                               int x2, int y2) {
             // draw using the drawDot() method repeatedly.
           }
         }
    不能创建abstract类的实例。然而可以创建一个变量,其类型是一个抽象类,并让它指向具体子类的一个实例。不能有抽象构造函数或抽象静态方法。
    Abstract类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类。
public class MachineDrawing extends Drawing {
  public void drawDot (int mach x, intmach y) {
           // Draw the dot
  }
}
Drawing d = new MachineDrawing();
第九节  接 口
接  口
    接口是抽象类的变体。
    在接口中,所有方法都是抽象的。
    多继承性可通过实现这样的接口而获得。
    句法是:
           public interface Transparency {
             public static final int OPAQUE=1;
             public static final int BITMASK=2;
             public static final int TRANSLUCENT=3;
             public int getTransparency();
           }
  接口是抽象类的变体。接口中的所有方法都是抽象的,没有一个有程序体。接口只可以定义static final成员变量。
    接口的好处是,它给出了屈从于Java技术单继承规则的假象。当类定义只能扩展出单个类时,它能实现所需的多个接口。
    接口的实现与子类相似,除了该实现类不能从接口定义中继承行为。当类实现特殊接口时,它定义(即,将程序体给予)所有这种接口的方法。然后,它可以在实现了该接口的类的任何对象上调用接口的方法。由于有抽象类,它允许使用接口名作为引用变量的类型。通常的动态联编将生效。引用可以转换到接口类型或从接口类型转换,instanceof运算符可以用来决定某对象的类是否实现了接口。
    接口是用关键字interface来定义的,如下所述:
           public interface Transparency {
             public static final int OPAQUE=1;
             public static final int BITMASK=2;
             public static final int TRANSLUCENT=3;
             public int getTransparency();
           }
    类能实现许多接口。由类实现的接口出现在类声明的末尾以逗号分隔的列表中,如下所示:
     public class MyApplet extends Applet implements
       Runnable, MouseListener{
         "..."
       }
   下例表示一个简单的接口和实现它的一个类:
    interface SayHello {
        void printMessage();
    }
    class SayHelloImpl implements SayHello {
        void printMessage() {
            System.out.println("Hello");
        }
    }
    interface SayHello强制规定,实现它的所有的类必须有一个称做printMessage的方法,该方法带有一个void返回类型且没有输入参数。
接  口
    对于下述情况,界面是有用的:
    声明方法,期望一个或更多的类来实现该方法
    决定一个对象的编程界面,而不揭示类的实际程序体
    捕获无关类之间的相似性,而不强迫类关系
    描述“似函数”对象,它可以作为参数被传递到在其它对象上调用的方法中
    对于下述情况,接口是有用的:
    声明方法,期望一个或更多的类来实现该方法。
    揭示一个对象的编程接口,而不揭示类的实际程序体。(当将类的一个包输送到其它开发程序中时它是非常有用的。)
    捕获无关类之间的相似性,而不强迫类关系。
    描述“似函数”对象,它可以作为参数被传递到在其它对象上调用的方法中。它们是“函数指针”(用在C和C++中)用法的一个安全的替代用法。

TOP

SUN JAVA标准教程

第十节  高级访问控制
高级访问控制
  修饰符      同类       同包      子类      通用性
  公共        是         是        是          是
  受保护      是         是        是
  缺省        是         是
  私有        是                                   
    变量和方法可以处于四个访问级别的一个中;公共,受保护,缺省或私有。类可以在公共或缺省级别。
    变量、方法或类有缺省访问性,如果它没有显式受保护修饰符作为它的声明的一部分的话。这种访问性意味着,访问可以来自任何方法,当然这些方法只能在作为目标的同一个包中的成员类当中。
    以修饰符protected标记的变量或方法实际上比以缺省访问控制标记的更易访问。一个protected方法或变量可以从类当中的任何方法进行访问,这个类可以是同一个包中的成员,也可以是从任何子类中的任何方法进行访问。当它适合于一个类的子类但不是不相关的类时,就可以使用这种受保护访问来访问成员。
            表6-1总结访问性标准
表6-1  访问性标准
                                                     
修饰符      同类       同包      子类      通用性
  公共        是         是        是          是
  受保护      是         是        是
  缺省        是         是
  私有        是                                    
受保护访问甚至被提供给子类,该子类驻留在与拥有受保护特征的类的不同包中。
第十一节降级
降    级
    降级就是过时的构造函数和方法调用。
    过时的方法和构造函数由具有更标准化的命名规则的方法所取代。
    当升迁代码时,用-deprecation标志来编译代码:
        javac -deprecation MyFile.java
    在JDK1.1中,对方法名称的标准化做了重大努力。因此,在JDK1.2中,大量的类构造函数和方法调用过时。它们由根据更标准化的命名规则规定的方法名称所取代,总的说来,使程序员的生活简单化。
    例如,在JDK1.1版本中的Java.awt.Component类:
  改变或获得组件大小的方法是resize()和size()。
  改变或获得组件矩形框的方法是reshape()和bounds()。
    在JDK1.0版本中的Java.awt.Component,这些方法被降级并被以set和get开头表示该方法的初级运算的方法所代替。
  setSize()和getSize()
  setBounds()getBounds()
    无论什么时候将代码从JDK1.0升迁到JDK1.1或更高版本中,或者即使使用以前用在JDK1.0中的代码,对用-deprecation标志来编译代码都是一个好主意。
c:\ javac -deprecation MyFile.java
    -deprecation标志将报告在降级过的类中使用的任何方法。例如,看一个叫做DateConverter的实用类,它将mm/dd/yy格式的日期转换成星期几:
1.package myutilities;
2.import java.util.*;
3.import java.text.*;
4.public final class DateConverter {
5.
6.  private static String day_of_the_week [] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
7.
8.public static String getDayOfWeek (String theDate){
9.  int month, day, year;
10.
11.  StringTokenizer st =
12.    new StringTokenizer (theDate, "/");
13.  month = Integer.parseInt(st.nextToken ());
14.  day = Integer.parseInt(st.nextToken());
15.  year = Integer.parseInt(st.nextToken());
16.  Date d = new Date (year, month, day);
17.  
18.  return (day_of_the_week[d.getDay()]);
19.  }
20.}
    当这个代码用-deprecation标志在JDK1.2中被编译时,会得到:
c:\ javac -deprecation DateConverter.java
DateConverter.java:16: Note: The constructor java.util.Date(int,int,int) has been deprecated.
            Date d = new Date (year, month, day);
                   ^
DateConverter.java:18: Note: The method int getDay() in class
java.util.Date has been deprecated.
                  return (day_of_the_week[d.getDay()]);
                                              ^
Note: DateConverter.java uses a deprecated API.Please consult the documentation for a better alternative. 3 warnings
    重写的DateConverter类看起来象这样:
1.package myutilities;
2.import java.util.*;
3.import java.text.*;
4.public final class DateConverter {
5.
6.private static String day_Of_The_Week [] =
{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
      7.  
8.  public static String getDayOfWeek (String theDate){
9.    Date d = null;
10. SimpleDateFormat sdf =
11.   new SimpleDateFormat ("MM/dd/yy");
12. try {
13.   d = sdf.parse (theDate);
14. } catch (ParseException e) {
15.   System.out.println (e);
16.   e.printStackTrace();
17. }
18. // Create a GregorianCalendar object
19.Calendar c =
20. new GregorianCalendar (TimeZone.getTimeZone ("EST"),         Locale.US);
21.c.setTime (d);
22.return(day_Of_The_Week
               [(c.get(Calendar.DAY_OF_WEEK)-1)]);
23.  }
24.}
     
在这里,1.2版本使用两个新类:SimpleDateFormat,用来采用任何String日期格式并创建一个Date对象的类;以及GregorianCalendar类,用来创建一个带有当地时区和场所的日历。

TOP

SUN JAVA标准教程

第十二节==运算符与equals()方法
==运算符与equals()方法
    equals()和==方法决定引用值是否指向同一对象
    equals()在类中被覆盖,为的是当两个分离的对象的内容和类型相配的话,返回真值。
    Java.lang包中的Object类有public boolean equals (Object obj)方法。它比较两个对象是否相等。仅当被比较的两个引用指向同一对象时,对象的equals()方法返回true。
    ==运算符也进行等值比较。也就是说,对于任何引用值X和Y,当且仅当X和Y指向同一对象时, X==Y返回真。
    当两个分离的对象的内容和类型相配的话,String,Date,File类和所有其它override equals()的包装类(Integer,Double,等等)将返回真。
    例如,String类中的equals()方法返回true,当且仅当参数不为null并且是一个String对象,该对象与调用该方法的String对象具有相同的字符顺序。
String s1=new String(“JDK1.2”);
String s2=new String(“JDK1.2”);
方法s1.equals(s2)返回真,尽管s1和s2指向两个不同的对象。
第十三节toString()方法
toString()方法
    被用来将一个对象转换成String
    被用来将一个基本类型转换成String
    toString方法被用来将一个对象转换成String表达式。当自动字符串转换发生时,它被用作编译程序的参照。System.out.println()调用下述代码:
      Date now = new Date()
        System.out.println(now)
    将被翻译成:
          System.out.println(now.toString());
    对象类定义缺省的toString()方法,它返回类名称和它的引用的地址(通常情况下不是很有用)。许多类覆盖toString()以提供更有用的信息。例如,所有的包装类覆盖toString()以提供它们所代表的值的字符串格式。甚至没有字符串格式的类为了调试目的常常实现toString()来返回对象状态信息。
第十四节  内部类
内部类
    被附加到JDK1.1
    允许一个类定义被放到另一个类定义里
    把类在逻辑上组织在一起
    可访问它们所嵌套的类的范围
    内部类,有时叫做嵌套类,被附加到JDK1.1及更高版本中。内部类允许一个类定义被放到另一个类定义里。内部类是一个有用的特征,因为它们允许将逻辑上同属性的类组合到一起,并在另一个类中控制一个类的可视性。
6.14.1  内部类基础
    下述例子表示使用内部类的共同方法:
1.import java.awt.*;
2.import java.awt.event.*;
3.public class MyFrame extends Frame{
4.    Button myButton;
5.    TextArea myTextArea;
6.    int count;;
7.
8.    public MyFrame(){
9.        super("Inner Class Frame");
10.     myButton = new Button("click me");
11.     myTextArea = new TextArea();
12.     add(myButton,BorderLayout,CENTER);
13.     add(myTextArea,BorderLayout,NORTH);
14.     ButtonListener bList = new ButtonListener();
15.     myButton.addActionListener(bList);
16. }
17. class ButtonListener implements ActionListener{
18.   public void actionPerformed(ActionEvent e){
19.   count ++
20.     myTextArea.setText("button clicked" + {
21.         count + "times");
22.   }
23. }// end of innerclass ButtonListener
24.
25. public static void main(String args[]){
26.   MyFrame f = new MyFrame();
27.   f.setSize(300,300);
28.   f.setVisible(true);
29. }
30.} // end of class MyFrame
    前面的例子包含一个类MyFrame,它包括一个内部类ButtonListener。编译器生成一个类文件,MyFrame$ButtonListener.class以及toMyFrame.class。它包含在MyFrame.class中,是在类的外部创建的。
6.14.2  如何做内部类工作?
    内部类可访问它们所嵌套的类的范围。所嵌套的类的成员的访问性是关键而且很有用。对嵌套类的范围的访问是可能的,因为内部类实际上有一个隐含的引用指向外部类上下文(如外部类“this”)。
1.public class MyFrame extends Frame{
2.    Button myButton;
3.    TextArea myTextarea;
4.    public MyFrame(){
5.      ......................
6.      ......................
7.      MyFrame$ButtonListener bList = new
8.               MyFrame$ButtonListener(this);
9.      myButton.addActionListener(bList);
10.   }
11.   class MyFrame$ButtonListener implements
12.   ActionListener{
13.   private MyFrame outerThis;
14.   Myframe$ButtonListener(MyFrame outerThisArg){
15.      outerThis = outerThisArg;
16.   }
17.   
18.   public void actionPerformed(ActionEvent e) {
19.   outerThis.MyTextArea.setText("buttonclicked");
20.   ......................
21.   ......................
22.   }
23.   public static void main(String args[]){
24.     MyFrame f = new MyFrame();
25.     f.setSize(300,300);
26.     f.setVisible(true);
27.   }
28.}
    有时可能要从static方法或在没有this的某些其它情况下,创建一个内部类的一个实例(例如,main)。可以如下这么做:
public static void main(String args[]){
    MyFrame f = new MyFrame();
    MyFrame.ButtonListener bList =
           f.new ButtonListener();
    f.setSize(50,50);
    f.setVisible(true);
}
6.14.3  内部类属性
内部类属性
    类名称只能用在定义过的范围中,除非用限定的名称。
    内部类的名称必须与所嵌套的类不同。
    内部类可以被定义在方法中。
    任何变量,不论是本地变量还是正式参数,如果变量被标记为final,那么,就可以被内部类中的方法访问。
    内部类有如下属性:
    类名称只能用在定义过的范围中,除非用在限定的名称中。内部类的名称必须与所嵌套的类不同。
    内部类可以被定义在方法中。这条规则较简单,它支配到所嵌套类方法的变量的访问。任何变量,不论是本地变量还是正式参数,如果变量被标记为final,那么,就可以被内部类中的方法访问。
内部类可以使用所嵌套类的类和实例变量以及所嵌套的块中的本地变量。
内部类可以被定义为abstract.
    属性
    只有内部类可以被声明为private或protected,以便防护它们不受来自外部类的访问。访问保护不阻止内部类使用其它类的任何成员,只要一个类嵌套另一个。
    一个内部类可以作为一个接口,由另一个内部类实现。
    被自动地声明为static的内部类成为顶层类。这些内部类失去了在本地范围和其它内部类中使用数据或变量的能力。
    内部类不能声明任何static成员;只有顶层类可以声明static成员。因此,一个需求static成员的内部类必须使用来自顶层类的成员。
注-内部类有常常被用作创建事件适配器的方便特征。事件适配器将在后面模块中讨论。
第十五节  包装类
包装类
用来将基本数据元素看作对象,包装类可以被用作:
                                    
            基本数据类型     包装类
              boolean             Boolean
                   byte                Byte
                   char                Char
                   short               Short
                   int                 Int
                   long                Long
                   float               Float
                   double              Double
    Java编程语言不把基本数据类型看作对象。例如,在基本格式本身当中,数字、布尔及字符型数据都被看作是为了提高效率。Java编程语言提供包装类来将基本数据元素看作对象。这样的数据元素被包裹在创建于它们周围的对象中,每个Java基本数据类型在Java.lang包中都有一个相应的wrapper class。每个包装类对象封装一个基本值。
              包装类列表如下:
              表6-2  包装类
                                    
            基本数据类型     包装类
              boolean        Boolean
              byte           Byte
              char           Char
              short          Short
              int            Int
              long           Long
              float          Float
              double         Double
    可以通过将被包裹的值传递到合适的构造函数中来构造包装类对象。例如:
int pInt = 500;
Integer wInt = new Integer(pInt);
第十六节  收集API
收集API
    收集(或容器)是代表一个对象组的单个对象,被认为是它的元素。
    收集类Vector,Bits,Stack,Hashtable,LinkedList等等都被支持。
    收集API包括将对象保持为下述情况的界面:
      收集-没有具体顺序的一组对象
      设定-没有复制的一组对象
      列表-有序对象组,允许复制
    收集(或容器)是代表一个对象组的单个对象,被认为是它的元素。收集典型地处理许多对象的类型,所有的类型都有一个特殊的种类(也就是说,它们都是从一个共同父类型延续来的)。Java编程语言支持收集类Vector,Bits, Stack,Hashtable ,LinkedList等等。例如,Stack实现后进先出(LIFO)的顺序,Hashtable提供一个相关的对象数组。
    收集保持处理Object类型的对象。这允许在收集中贮存任何对象。它还可以,在使用对象前、从收集中检索到它之后,使用正确的类型转换。
    收集API典型地由将对象保持为下述情况的接口而组成:
      收集-没有具体顺序的一组对象
      设定-没有复制的一组对象
      列表-有序对象组,允许复制
    API还包括诸如HashSet, ArraySet, ArrayList, LinkedList和Vector等等的类,它们实现这些接口。API还提供了支持某些算法的方法,如:排序,二进制搜索,评估列表中的最小和最大,以及收集等。

TOP

SUN JAVA标准教程

= { 'a', 'b', 'c', 'd'};
44.  String s = new String ("Hi there!");
45.
46.  v.addInt(digit);
47.  v.addFloat(real);
48.  v.addString(s);
49.  v.addCharArray(letters);
50.
51.  v.printVector();
52. }
53.}
    这个程序产生下列输出:
$ java MyVector
Number of vector elements is 4 and are:
5
3.14
Hi there!
Abcd

TOP

SUN JAVA标准教程

第十八节  反射API
反射API
    可以用作
      构造新类实例和新数组
      访问并修改对象和类的字段
      调用对象和类中的方法
      访问并修改数组的元素
    Java反射API提供一套类,可以用它们来决定一个类文件的变量和方法。因为被共同用于动态发现和执行代码的目的,因此API可以被用于:
  构造新类实例和新数组
  访问并修改对象和类的字段
  调用对象和类中的方法
  访问并修改数组的元素
    只要安全策略允许,这些操作是可能的。在需要运行时检索并处理信息的情况下,反射API是有用的。例如,如果正在编写一个Java软件解释程序或调试程序,可以使用它。
第十九节  反射API特征
反射API特征
Java.lang.Class
    Java.lang.reflect.Field
    Java.lang.reflect.Method
    Java.lang.reflect.Array
Java.lang.reflect.Constructor
    定义类和方法的核心反射API的主要特征如下:
      Java.lang.Class类提供方法,该方法可获得有关类及其字段、构造函数以及方法的信息。
      Java.lang.reflect.Field提供方法,该方法设定/获得有关类中的字段的信息。
      Java.lang.reflect.Method提供方法,该方法访问并调用类中的方法,并获得它们的签名。
      Java.lang.reflect.Array能使数组对象自省。
      Java.lang.reflect.Constructor提供反射访问到构造函数。
第二十节  反射API安全模型
反射API安全模型
Java安全管理器一个类接一个类地控制对核心API的访问。
    当下述情况发生时,标准的Java编程语言访问控制得到加强:
  Field被用来获得或设定一个字段值
  Method被用来调用一个方法
      Constructor被用来创建并初始化一个新的类的实例
    Java安全管理器一个类接一个类地控制对核心API的访问。当下述情况发生时,标准的Java编程语言访问控制得到加强:
  Field被用来获得或设定一个字段值
  Method被用来调用一个方法
      Constructor被用来创建并初始化一个新的类的实例
练习:用高级语言特征工作
    练习目的—使用银行帐户模型和采用高级面向对象特征,如:内部类,矢量类和接口等,重写、编译并运行三个程序。
一、准备
    为了成功地完成该实验,必须熟悉本模块及前面模块中所讲的面向对象概念。
二、任务
    一级实验:修改银行帐户问题
1.定义只包含两个方法deposit和withdraw的接口Personal。
2.从模块5中,运用Personal接口来定义一套不同的帐户类型,重新定义类Account.Java。它必须能处理个人帐户,进一步分成支票和存款两个帐户。
3.设计并开发提供保护的方法。例如,如果一个客户有一个存款和支票帐户,须确保支票帐户受存款帐户保护。
    二级实验:使用内部类
1.创建一个叫做BasicArray的类,声明并初始化一个叫做thisArray的数组,它包含四个整数。
2.创建一个名为Factorial的类,它包含一个计算它的参数的阶乘的方法。
3.从BasicArray的主要方法创建Factorial类的一个实例,然后调用其方法来计算四个整数中每一个的阶乘。
4.编译并测试该程序。
5.将Factorial类中的所有东西都移到BasicArray类中。Factorial现在就是BasicArray的一个内部类。
6.编译并测试该程序。
    三级实验:将find和delete方法附加到MyVector类中
1.将Find方法附加到MyVector类中,它将返回被作为参数传递的元素的位置。
如果未发现该参数,让方法返回-1。
例如:
$ java MyVectorFind 3.14
3.14 is located at index 1
Number of vector elements is 4 and are:
5
3.14
Hi there!
abcd
$ java MyVectorFind c
args[0]=c, not found in vector
Number of vector elements is 4 and are:
5
3.14
Hi there!
abcd
2.将delete方法附加到MyVector类中,该类将所有与参数相配的元素移走。
方法必须返回true或false:如果删除成功,为true;否则为false(元素存在或不存在于矢量中)。
例如:
$ java MyVectorDelete 3.14
Elements 3.14 successfully deleted from vector.
Number of vector elements is 3 and are:
5
Hi there!
abcd
三、练习总结
    讨论—花几分钟来讨论实验练习中遇到的经验、问题或发现。
经验  解释   总结  应用
四、检查进步情况
    在继续学习下个模块前,检查一下你确实能:
  描述static变量、方法和初始化程序
  描述final类、方法和变量
  列出访问控制级别
  确认降级类并解释如何从JDK1.0升迁到JDK1.1、JDK1.2。
  在Java软件程序中,确认:
    static方法和变量
    public,private,protected和缺省变量
  使用abstract类和方法
  解释如何及何时使用内部类
  解释如何及何时使用接口
  描述= =和equals()之间的不同
五、思考
    Java编程语言具有什么特征,从而可以直接处理运行时错误情况?

TOP

SUN JAVA标准教程

);
11.    } catch (ArrayIndexOutOfBoundsException e){
12.    System.out.println( "Re-setting Index Value");
13.    i = -1;
14.    } finally {
15.    System.out.println("This is always printed");
16.    }
17.    i++;
18.    } // end while()
19.    } // end main()
    当循环被执行时,下述在屏幕上出现的信息将改变。
1.Hello world!
2.This is always printed
3.No, I mean it!
4.This is always printed
5.HELLO WORLD!!
6.This is always printed
7.Re-setting Index Value
8.This is always printed

TOP

SUN JAVA标准教程

第五节  异常分类
    在Java编程语言中,异常有三种分类。Java.lang.Throwable类充当所有对象的父类,可以使用异常处理机制将这些对象抛出并捕获。在Throwable类中定义方法来检索与异常相关的错误信息,并打印显示异常发生的栈跟踪信息。它有Error和Exception两个基本子类,如下图所示:
    Throwable类不能使用,而使用子类异常中的一个来描述任何特殊异常。每个异常的目的描述如下:
-Error表示恢复不是不可能但很困难的情况下的一种严重问题。比如说内存溢出。不可能指望程序能处理这样的情况。
-RuntimeException表示一种设计或实现问题。也就是说,它表示如果程序运行正常,从不会发生的情况。比如,如果数组索引扩展不超出数组界限,那么,ArrayIndexOutOfBoundsException异常从不会抛出。比如,这也适用于取消引用一个空值对象变量。因为一个正确设计和实现的程序从不出现这种异常,通常对它不做处理。这会导致一个运行时信息,应确保能采取措施更正问题,而不是将它藏到谁也不注意的地方。
-其它异常表示一种运行时的困难,它通常由环境效果引起,可以进行处理。例子包括文件未找到或无效URL异常(用户打了一个错误的URL),如果用户误打了什么东西,两者都容易出现。这两者都可能因为用户错误而出现,这就鼓励程序员去处理它们。
第六节  共同异常
共同异常
-ArithmeticException
-NullPointerException
-NegativeArraySizeException
-ArrayIndexoutofBoundsException
-SecurityException
    Java编程语言提供几种预定义的异常。下面是可能遇到的更具共同性的异常中的几种:
-ArithmeticException—整数被0除,运算得出的结果。
-int I =12 / 0;
-NullPointerException—当对象没被实例化时,访问对象的属性或方法的尝试:
-Date d= null;
-System.out.println(d.toString());
-NegativeArraySizeException—创建带负维数大小的数组的尝试。
-ArrayIndexoutofBoundsException—访问超过数组大小范围的一个元素的尝试。
-SecurityException—典型地被抛出到浏览器中,SecurityManager类将抛出applets的一个异常,该异常企图做下述工作(除非明显地得到允许):
-访问一个本地文件
-打开主机的一个socket,这个主机与服务于applet的主机不是同一个。
-在运行时环境中执行另一个程序
第七节  处理或声明规则
处理或声明规则
-用try-catch-finally块来处理异常
-使用throws子句声明代码能引起一个异常
     为了写出健壮的代码,Java编程语言要求,当一个方法在栈(即,它已经被调用)上发生Exception(它与Error或RuntimeException不同)时,那么,该方法必须决定如果出现问题该采取什么措施。
    程序员可以做满足该要求的两件事:
    第一,通过将Try{}catch(){}块纳入其代码中,在这里捕获给被命名为属于某个超类的异常,并调用方法处理它。即使catch块是空的,这也算是处理情况。
    第二,让被调用的方法表示它将不处理异常,而且该异常将被抛回到它所遇到的调用方法中。它是按如下所示通过用throws子句标记的该调用方法的声明来实现的:
     public void troublesome() throws IOException
    关键字throws之后是所有异常的列表,方法可以抛回到它的调用程序中。尽管这里只显示了一个异常,如果有成倍的可能的异常可以通过该方法被抛出,那么,可以使用逗号分开的列表。
    是选择处理还是选择声明一个异常取决于是否给你自己或你的调用程序一个更合适的候选的办法来处理异常。
注—由于异常类象其它类一样被组编到层次中,而且由于无论何时想要使用超类都必须使用子类, 因此,可以捕获异常“组”并以相同的捕获代码来处理它们。例如,尽管IOExceptions(EOFException,FileNotFoundException等等)有几种不同的类型,通过俘获IOException,也可以捕获IOException任何子类的实例。
第八节  创建自己的异常
7.8.1  介绍
    用户定义异常是通过扩展Exception类来创建的。这种异常类可以包含一个“普通”类所包含的任何东西。下面就是一个用户定义异常类例子,它包含一个构造函数、几个变量以及方法:
1.public class ServerTimedOutException extends Exception {
2.    private String reason;
3.    private int port;
4.    public ServerTimedOutException (String reason,int port){
5.      this.reason = reason;
6.      this.port = port;
7.    }
8.    public String getReason() {
9.    return reason;
10.    }
11.    public int getPort() {
12.      return port;
13.    }
14. }
    使用语句来抛出已经创建的异常:
throw new ServerTimedOutException
("Could not connect", 80);
7.8.2  实例
    考虑一个客户服务器程序。在客户代码中,要与服务器连接,并希望服务器在5秒钟内响应。如果服务器没有响应,那么,代码就如下所述抛出一个异常(如一个用户定义的ServerTimedOutException)。
1.  public void connectMe(String serverName) throws
     ServerTimedOutException {
2.int success;
3.int portToConnect = 80;
4.success = open(serverName, portToConnect);
5.if (success == -1) {
6.throw new ServerTimedOutException(
7."Could not connect", 80);
8.}
9.}
    要捕获异常,使用try语句:
1.public void findServer() {
2.. . .
3.try {
4.connectMe(defaultServer);
5.} catch(ServerTimedOutException e) {
6.System.out.println("Server timed out, trying alternate");
7.try {
8.connectMe(alternateServer);
9.} catch (ServerTimedOutException e1) {
10.System.out.println("No server currently available");
11.}
12.}
13... .
注—try和catch块可以如前例所述那样被嵌套。
也可能部分地处理一个异常然后也将它抛出。如:
try {
.....
.....
} catch (ServerTimedOutException e) {
  System.out.println("Error caught ");
  throw e;
}
练习:处理并创建异常
    练习目的—通过编写可以创建并处理异常的Java软件程序,可以获得异常机制的经验。
一、准备
    为了成功地完成该实验,必须理解处理运行时错误的异常的概念。
二、任务
    一级实验:处理一个异常
    1. 使用第169页上的样板异常程序在数组索引超出数组大小时创建一个异常。(或修改自己的程序以便创建一个异常。)
    2. 使用try和catch语句从异常进行恢复。
    二级实验:创建自己的异常
使用模块5中创建的bank包并附加下述异常:
AccountOverdrawnException—当有了这个要取出比帐户上更多的钱的尝试时。
InvalidDepositException—当无效钱数(小于0)存入时。
三、练习总结
    讨论—花几分钟时间讨论实验练习中所取得的经验、问题或发现。
经验  解释 总结 应用
四、检查进步情况
    在继续下一个模块前,检查一下,确信能够:
-定义异常
-使用try,catch和finally语句
-描述异常分类
-确认共同异常
-开发程序来处理自己的异常
五、思考
Java应用环境有什么特征,使它支持用户界面的开发?

TOP

返回列表 回复 发帖