返回列表 发帖

SUN JAVA标准教程

3.3.4  逻辑表达式

逻辑表达式
支持的位运算符如下:
!──NOT          &──AND
|──XOR          ^──OR
布尔运算符如下:
~ ──Complement(取反)      &──AND
^──XOR                | |──OR
多数Java运算符是从其它语言借取得并且和人们一般期待的功能一样。
关系和逻辑运算符返回布尔结果,int到boolean不能自动转换。
     int  i = 1;
     if ( i ) //generates a compile error
     if (i !=0) // Correct
支持的位运算符是!、&、|、^, 支持的布尔逻辑运算符是NOT、AND、XOR和OR。每个运算都返回布尔结果。运算符&&和||是运算符&和|的布尔等价物。布尔逻辑运算符将在下一页中讨论。
3.3.5  位运算
Java编程语言支持整数数据类型的位运算,它们的运算符~、&、^和|分别表示位运算的NOT(为求反)、位AND、位XOR和位OR。移位运算符将稍后在本课程中讨论。
3.3.6  布尔逻辑运算符

布尔逻辑运算符
运算符是&&(AND)和||(OR)
运算符使用举例:
       见3-11页程序
运算符&& (定义为AND)和||(定义为OR)执行布尔逻辑表达式。请看下面的例子:
MyDate d = null;
if ((d! = null) && (d,day() > 31)) {
// do something with d

形成if ()语句自变量的布尔表达式是合法且安全的。这是因为当第一个子表达式是假时,第二个子表达式被跳过,而且当第一个子表达式是假时,整个表达式将总是假,所以不必考虑第二个子表达式的值。类似的,如果使用||运算符,而且第一个表达式返回真, 则第二个表达式不必求值,因为整个表达式已经被认为是真。

3.3.7  用加号(+)进行串链接
用加号(+)进行串链接
运算符 +  能够:
进行String链接
生成一个新的String
例如:
        String salutation = "Dr. ";
        String name = "Jack " + "Arthur";
        String title = salutation + name;
    最后一行的结果是:
        Dr. Jack Arthur
一个自变量必须是String对象
非串被自动转换为String对象
运算符 +  能够进行String对象的链接并生成一个新的String:
        String salutation = "Dr. ";
        String name = "Jack " + "Arthur";
        String title = salutation + name;
      最后一行的结果是:
        Dr. Jack Arthur
如果+运算符中有一个自变量为String对象,则其它自变量将被转换成String。所有对象都可被自动转换成String,尽管这样做的结果可能是意义含糊的。不是串的对象是通过使用toString() 成员函数而转换成串的等价物的。

TOP

SUN JAVA标准教程

3.3.8 右移位运算符>>和>>> 右移位运算符>>和>>> 算术或符号右移位如下所示: 见3-13页 在移位的过程中, 符号位被拷贝 逻辑或非符号右移位运算符(>>>) 作用于位图 在移位的过程中, 符号位不被拷贝 Java编程语言提供了两种右移位运算符 运算符>>进行算术或符号右移位。移位的结果是第一个操作数被2的幂来除,而指数的值是由第二个数给出的。例如: 128 >> 1 gives 128/2 1 = 64 256 >> 4 gives 256/2 4 = 16 -256 >> 4 gives -256/2 4 = -16 逻辑或非符号右移位运算符>>>主要作用于位图,而不是一个值的算术意义;它总是将零置于最重要的位上。例如: 1010 ... >> 2 gives 111010 ... 1010 ... >>> 2 gives 001010 ...在移位的过程中, >>运算符使符号位被拷贝。 注意──移位运算符将它们右侧的操作数模32简化为int类型左侧操作数, 模64简化为long类型右侧操作数。因而, 任何int x, x >>> 32都会导致不变的x值, 而不是你可能预计的零。 注意------值得称赞的重要一点是: >>>运算符仅被允许用在整数类型, 并且仅对int和long值有效。如果用在short或byte值上, 则在应用>>>之前, 该值将通过带符号的向上类型转换被升级为一个int。有鉴于此,无符号移位通常已成为符号移位。 3.3.9 左移位运算符(<<) 左移位运算符(<<) 128 << 1 等于 128*21 = 256 16 <<2 等于 16*22 =64 运算符<<执行一个左移位。移位的结果是:第一个操作数乘以2的幂,指数的值是由第二个数给出的。例如: 128 << 1 gives 128*21 = 256 16<<2 gives 16*22 =64 3.3.10 类型转换 类型转换 如果赋值的信息丢失,程序员必须采用类型转换的方法确认赋值 在short和char之间的赋值需要一个显式转型 见3-16程序 在赋值的信息可能丢失的地方,编译器需要程序员用类型转换(typecast)的方法确认赋值。例如,它可以“挤压”一个long值到一个int变量中。显式转型做法如下: long bigValue =99L; int squashed =(int) (bigValue); 在上述程序中,期待的目标类型被放置在圆括号中,并被当作表达式的前缀,该表达式必须被更改。一般来讲,建议用圆括号将需要转型的全部表达式封闭。否则,转型操作的优先级可能引起问题。 注意──重温short的范围是:-215至215-1;char的范围是:0至215-1。 因而在short和char之间的赋值总需要一个显式转型

TOP

SUN JAVA标准教程

3.3.11  升级和表达式的类型转换
升级和表达式的类型转换
-变量被自动升级为一个较长的形式(如:int至long的升级)
-如果变量类型至少和表达式类型一样大(位数相同),则表达式是赋值兼容的。
当没有信息丢失时,变量可被自动升级为一个较长的形式(如:int至long的升级)
    long bigval = 6; // 6 is an int type, OK
    int smallval = 99L; // 99L is a long, illegal
    double z = 12.414F; // 12.414F is float, OK
    float z1 = 12.414; // 12.414 is double, illegal
一般来讲,如果变量类型至少和表达式类型一样大(位数相同),则你可认为表达式是赋值兼容的。
对 + 运算符来说,当两个操作数是原始数据类型时,其结果至少有一个int,并且有一个通过提升操作数到结果类型、或通过提升结果至一个较宽类型操作数而计算的值,这可能会导致溢出或精度丢失。例如:
short  a,b,c
a=1;
b=2;
c= a+b;
上述程序会因为在操作short之前提升每个short至int而出错。然而,如果c被声明为一个int,或按如下操作进行类型转换:
    c = (short)(a+b);
则上述代码将会成功通过。
第四节  分支语句
分支语句
if, else语句
     if (布尔表达式){
        语句或块;
       }
     if (条件为真) {
        语句或块;
       }   else  {
        语句或块;
       }

条件语句使部分程序可根据某些表达式的值被有选择地执行。Java编程语言支持双路if和多路switch分支语句。
3.4.1  if, else语句
if, else语句的基本句法是:

     if (布尔表达式){
        语句或块;
       }  else  {
        语句或块;
       }
例:
int count;
   1.count = getCount(); // a method defined in the program
   2.if (count < 0) {
   3.System.out.println("Error: count value is negative.");
   4.}else {
   5.System.out.println("There will be " + count +
   6." people for lunch today.");
   7.}
在Java编程语言中,if ()用的是一个布尔表达式,而不是数字值,这一点与C/C++不同。前面已经讲过,布尔类型和数字类型不能相互转换。因而,如果出现下列情况:
if  (x)  // x is int
你应该使用下列语句替代:
if  (x ! = 0)
全部else部分是选择性的,并且当测试条件为假时如不需做任何事,else部分可被省略。
3.4.2  switch语句


分支语句
  switch语句
     switch语句的句法是:
     switch (expr1){
         case expr2:
         statements;
         break;
         case expr3:
         statements;
         break;
         default:
         statements;
         break;
      }

     switch语句的句法是:
     switch (expr1){
         case expr2:
         statements;
         break;
         case expr3:
         statements;
         break;
         default:
         statements;
         break;
      }
注意──在switch (expr1) 语句中,expr1必须与int类型是赋值兼容的;byte, short或char类型可被升级;不允许使用浮点或long表达式。

TOP

SUN JAVA标准教程

当变量或表达式的值不能与任何case值相匹配时,可选缺省符(default)指出了应该执行的程序代码。如果没有break语句作为某一个case代码段的结束句,则程序的执行将继续到下一个case,而不检查case表达式的值。 例1: 1.switch (colorNum) { 2.case 0: 3.setBackground(Color.red); 4.break; 5.case 1: 6.setBackground(Color.green); 7.break; 8.default: 9.setBackground(Color.black); 10.break; 11.} 例2: 1.switch (colorNum) { 2.case 0: 3.setBackground(Color.red); 4.case 1: 5.setBackground(Color.green); 6.default: 7.setBackground(Color.black); 8.break; 9.} 例2设定背景颜色为黑色,而不考虑case变量colorNum的值。如果colorNum的值为0,背景颜色将首先被设定为红色,然后为绿色,再为黑色。 第五节 循环语句 循环语句 for语句 for (init_expr; boolean testexpr; alter_expr){ statement or block } 循环语句使语句或块的执行得以重复进行。Java编程语言支持三种循环构造类型:for, while和do loops。for和while循环是在执行循环体之前测试循环条件,而do loops是在执行完循环体之后测试循环条件。这就意味着for和while循环可能连一次循环体都未执行, 而do loops将至少执行一次循环体。 3.5.1 for循环 for循环的句法是: for (init_expr; boolean testexpr; alter_expr){ statement or block } 例如: for (int i = 0; i < 10; i++) { System.out.println("Are you finished yet?"); } System.out.println("Finally!"); 注意──Java编程语言允许在for () 循环结构中使用逗号作为分隔符。 例如,for (i= 0, j = 0; j<10; i++, j++)是合法的;它将i值初始化为零,并在每执行完一次循环体后,增加一次它们的值。 3.5.2 while循环 循环语句 while循环 while (布尔表达式) { 语句或块 } while循环的句法是: while (布尔表达式) { 语句或块 } 例如: int i = 0; while (i < 10) { System.out.println("Are you finished yet?"); i++; } System.out.println("Finally!"); 请确认循环控制变量在循环体被开始执行之前已被正确初始化,并确认循环控制变量是真时,循环体才开始执行。控制变量必须被正确更新以防止死循环。 3.5.3 do循环 循环语句 do/while语句 do { 语句或块; } while (布尔测试) do循环的句法是: do { 语句或块; } while (布尔测试) 例如: int i = 0; do { System.out.println("Are you finished yet?"); i++; } while (i < 10); System.out.println("Finally!"); 象while循环一样,请确认循环控制变量在循环体中被正确初始化和测试并被适时更新。 作为一种编程惯例,for循环一般用在那种循环次数事先可确定的情况,而while和do用在那种循环次数事先不可确定的情况。

TOP

SUN JAVA标准教程

第六节  特殊循环流程控制

特殊循环流程控制
break [标注];
continue[标注];
label: 语句;// where statement must be any
               // legal statement.
下列语句可被用在更深层次的控制循环语句中:
break [标注];
continue[标注];
label: 语句;// where statement must be any
       // legal statement.
break语句被用来从switch语句、loop语句和预先给定了label的块中退出。
continue语句被用来略过并跳到循环体的结尾。
label可标识控制需要转换到的任何有效语句,它被用来标识循环构造的复合语句。
例如
loop: while (true) {
for (int i=0; i < 100; i++) {
switch (c = System.in.read()) {
case -1:
case ` \n ` :
// jumps out of while-loop to line #12
break loop;
....
}
} // end for
} // end while
test: for (...) {
....
while (...) {
if (j > 10) {
// jumps to the increment portion of
// for-loop at line #13
continue test;
}
} // end while
} // end for

练习:使用表达式
练习目标──你将编写、编译并运行两个使用标识符、表达式和控制结构的算法程序。
一、准备
为成功地完成本练习,你必须具备编译和运行Java程序的能力,并且熟悉流程控制构造。
二、任务
1级:创建一个阶乘应用程序
一个数X的阶乘(通常记作X!)等于X*(X-1)*(X-2)……*1。例如4!等于4×3×2×1=24。
创建一个称作Factor的应用程序,利用该应用程序可打印2,4,6和10的阶乘。
2级:求解一个几何题程序
已知一个直角三角形,其弦(最长边)的长度由下列公式给出:
编写一个称作hypotenuse的Java软件程序,从已知直角三角形的直角边计算最长边。
提示:从mod3/templates目录中提供的模板解决方案入手,从命令行输入;同时注意java.lang.Math类。
三、练习小结
    讨论──用几分钟的时间讨论一下在以上练习中你所获得的经验、感想和发现。
经验  解释  总结  应用
四、检查你的进度
   在进入下一模块之前,请确认你已经能够:
-区分实例变量和局部变量;
-描述实例变量是如何被初始化的;
-确认并更正“可能的赋值前的引用”的编译器错误;
-辨认、描述并使用Java软件运算符;
-区分合法和非法原始类型赋值;
-确认boolean表达式和它们在控制构造中的要求;
-辨认赋值兼容性和在基本类型中的必要转型;
-使用if, switch,for,while和do句型结构和break和continue的标注形式作为程序的流程控制结构。
五、思考题
-多数编程语言都使用什么数据类型来集合相似的数据元素?
-你怎样对一组元素进行相同的操作(如一个数组)?
-Java编程语言使用什么数据类型?

TOP

SUN JAVA标准教程

第四章  数 组
本模块将描述Java编程语言中如何定义、初始化和使用数组。

第一节  相关问题
讨论──下列问题与本模块阐述的论题相关:
- 一个数组的用途是什么?
第二节目 标
完成本模块的学习后,你应该能够:
- 声明并创建原始数组、类数组或数组类型
- 解释为什么数组的元素需初始化
- 给出数组定义并初始化数组元素
- 确定一个数组中元素的数量
- 创建多维数组
- 编写从一个数组类型到另一个数组类型数组值的拷贝代码
第三节  数组的声明
声明数组
-相同类型的成组数据对象
-原始类型或类类型数组声明
-为一个引用创建空间
-数组是一个对象,而不是为原始类型储备的存储器

典型的数组是用来集合相同类型的对象并通过一个名称来引用这个集合。
你可以声明任何类型的数组──原始类型或类类型:
char s[ ];
Point p  ; // where point is a class
在Java编程语言中,即使数组是由原始类型构成,甚或带有其它类类型,数组也是一个对象。声明不能创建对象本身,而创建的是一个引用,该引用可被用来引用数组。数组元素使用的实际存储器可由new语句或数组初始化软件动态分配。
在以下部分,你将看到如何创建和初始化实际数组。
上述这种将方括号置于变量名之后的声明数组的格式,是用于C、C++和Java编程语言的标准格式。这种格式会使声明的格式复杂难懂,因而,Java编程语言允许一种替代的格式,该格式中的方括号位于变量名的左边:
char[ ]s;
Point[ ]p;
这样的结果是,你可以认为类型部分在左,而变量名在右。上述两种格式并存,你可选择一种你习惯的方式。声明不指出数组的实际大小。
注意----当数组声明的方括号在左边时,该方括号可应用于所有位于其右的变量

TOP

SUN JAVA标准教程

第四节  创建数组

创建数组
     使用关键字new 创建一个数组对象
           s = new char 20;
           p = new Point 100;
           p0 = new Point();
           p1 = new Point();
            •
            •

     你可以象创建对象一样,使用关键字new 创建一个数组。
           s = new char 20;
           p = new Point 100;
     第一行创建了一个20个char值的数组,第二行创建了一个100个类型Point的变量。r然而,它并不创建100个Point对象;创建100个对象的工作必须分别完成如下:
           p0 = new Point();
           p1 = new Point();
        •
        •
        •
用来指示单个数组元素的下标必须总是从0开始,并保持在合法范围之内--大于0或等于0并小于数组长度。任何访问在上述界限之外的数组元素的企图都会引起运行时出错。下面还要谈到一些更好的数组初始化方法。
第五节  初始化数组
初始化数组
-初始化一个数组元素
-用初始化值创建一个数组
         String names  = 
            “Georgianna”,
            “Jen”,
            “Simon”,
           ;
当创建一个数组时,每个元素都被初始化。在上述char数组s的例子中,每个值都被初始化为0 (\u0000-null)字符;在数组p的例子中, 每个值都被初始化为null,表明它还未引用一个Point对象。在经过赋值 p0 = new Point()之后,数组的第一个元素引用为实际Point对象。
   
注意--所有变量的初始化(包括数组元素)是保证系统安全的基础,变量绝不能在未初始化状态使用。

    Java编程语言允许使用下列形式快速创建数组:
           String names  = 
            “Georgianna”,
            “Jen”,
            “Simon”,
           ;
其结果与下列代码等同:
           String names  ;
           names = new String 3;
           names 0 = “Georgianna”;
           names 1 = “Jen”;
           names 2 = “Simon”;
这种”速记”法可用在任何元素类型。例如:
       Myclass array  = 
         new Myclass (),
         new Myclass (),        
         new Myclass ()
        ;
适当的类类型的常数值也可被使用:
       Color palette   = 
           color.blue,
           color.red,
           color.white
        ;

TOP

SUN JAVA标准教程

第六节  多维数组
多维数组
数组的数组
    int twoDim [][] = new int [4][];
    twoDim[0] = new int[5];
twoDim[1] = new int[5];
    int twoDim [][] = new int [][4]; 非法
每个数组有5个整数类型的4个数组的数组   
   int twoDim [][] = new int [4][5];
   twoDim[0] = new int[5];
   twoDim[1] = new int[5];
Java编程语言没有象其它语言那样提供多维数组。因为一个数组可被声明为具有任何基础类型,所以你可以创建数组的数组(和数组的数组的数组,等等)。一个二维数组如下例所示:
int twoDim [][] = new int [4][];
twoDim[0] = new int[5];
twoDim[1] = new int[5];
首次调用new而创建的对象是一个数组,它包含4个元素,每个元素对类型array of int的元素都是一个null引用并且必须将数组的每个点分别初始化。
注意-尽管声明的格式允许方括号在变量名左边或者右边,但此种灵活性不适用于数组句法的其它方面。例如: new int 4是非法的。

多维数组
非矩形数组的数组
twoDim0 = new int 2;
twoDim1 = new int 4;
twoDim2 = new int 6;
twoDim3 = new int 8;
每个数组有5个整数类型的4个数组的数组
    int twoDim   =  new int 45;

多维数组
因为这种对每个元素的分别初始化,所以有可能创建非矩形数组的数组。也就是说,twoDim的元素可按如下方式初始化:
twoDim0 = new int 2
twoDim1 = new int 4;
                  twoDim2 = new int 6;
                  twoDim3 = new int 8;
由于此种初始化的方法烦琐乏味,而且矩形数组的数组是最通用的形式,因而产生了一种”速记”方法来创建二维数组。例如:
            int twoDim   =  new int 45;
可被用来创建一个每个数组有5个整数类型的4个数组的数组。
第七节  数组界限
数组界限
     所有数组的下标都从0开始
            int list  = new int 10;
            for (int i= 0; i< list.length; i++)
              System.out.println(listi);
在Java编程语言中,所有数组的下标都从0开始。 一个数组中元素的数量被作为具有length属性的部分数组对象而存储; 这个值被用来检查所有运行时访问的界限。如果发生了一个越出界限的访问,那么运行时的报错也就出现了。
使用length属性的例子如下:
            int list  = new int 10;
            for (int i= 0; i< list.length; i++)
              System.out.println(listi);
使用length属性使得程序的维护变得更简单。

TOP

SUN JAVA标准教程

第八节  拷贝数组

拷贝数组
-不能调整数组的大小
-可使用相同的引用变量来引用一个全新的数组
          int elements  = new int 6;
          elements = new int 10;
数组一旦创建后,其大小不可调整。然而,你可使用相同的引用变量来引用一个全新的数组:
          int myArray  = new int 6;
          myArray = new int 10;
    在这种情况下,第一个数组被有效地丢失,除非对它的其它引用保留在其它地方。
拷贝数组
            
拷贝数组

        System.arraycopy()方法
       // original array
   1.int myArray[] = { 1, 2, 3, 4, 5, 6 };
   2.
   3.// new larger array
   4.int hold[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
   5.// copy all of the myArray array to the hold
   6.// array, starting with the 0th index
   7.System.arraycopy(myArray, 0, hold, 0,
   8.myArray.length);

Java编程语言在System类中提供了一种特殊方法拷贝数组,该方法被称作arraycopy()。例如,araycopy可作如下使用:
// original array
   1.int myArray[] = { 1, 2, 3, 4, 5, 6 };
   2.
   3.// new larger array
   4.int hold[] = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 };
   5.// copy all of the myArray array to the hold
   6.// array, starting with the 0th index
   7.System.arraycopy(myArray, 0, hold, 0,
   8.myArray.length);
    在这一点,数组hold有如下内容:1,2,3,4,5,6,4,3,2,1。
注意—在处理对象数组时,System.arraycopy()拷贝的是引用,而不是对象。对象本身不改变。

练习:  使用数组
  练习目标--在定义并初始化数组后,你将在程序中使用数组。
一、准备
为成功地完成本练习,请务必理解基本的矩阵概念并了解如何定位一个数组以获取它的值。
二、任务
    1级:基本数组的使用
1. 创建一个称作BasicArray的类,在...main()方法中声明两个变量,一个是thisArray,另一个是thatArray,它们应属类型array of int。
2. 创建一个数组,它有10个int值,范围从1至10。分配这个第三数组的引用给变量thisArray。
3. 使用for()循环打印thisArray的所有值。如何控制循环的次数?
4. 编译并运行程序。多少值被打印?这些值是什么?
5. 对每个thisArray的元素,建立它的值为索引值的阶乘。打印数组的值。
6. 编译并运行程序。
7. 分配thisArray的引用给变量thatArray。打印thatArray的所有元素。
8. 编译并运行程序。tyatArray的多少值被显示?这些值是什么?它们来自何处。
9. 修改thisArray的某些元素,打印thatArray的值。
10. 编译并运行程序;在thatArray的值中,你注意到了什么?
11. 创建一个有20个int值的数组。分配新数组的引用给变量thatArray,打印thatArray的值。
12. 编译并运行程序。每个数组有多少值被显示? 这些值是什么?
13. 拷贝thisArray的值给thatArray。你将使用什么方法调用? 你将如何限制拷贝元素的数量? thatArray的元素10至19有什么变化?
14. 打印thatArray的值。
15. 编译并运行程序。你所显示的值都是正确的吗?如果不是,你知道有那些内容理解得不对吗?
16. 改变thatArray的某些值;打印thisArray和thatArray。
17. 编译并运行程序。这些值是你所期待的吗?
  2级:数组的数组
1. 创建一个称作Array2D的类,在main()方法中声明一个称作twoD的变量,它应属类型array of array of int。
2. 创建一个元素类型为int的数组,该数组应包括4个元素并被赋值到变量twoD的elements0。
3. 编写两个嵌套for()循环语句以打印twoD的全部值。以矩阵的格式安排输出 (可采用System.out.print()方法)。
4. 编译并运行程序。 你应该能发现此时出现了运行错误(空指针异常) ,这是因为twoD的elements1 至 3未被初始化。
5. 分别创建包括5个、6个和7个元素的int数组,将这些数组的引用分别赋予twoD的elements 1,2和3;确认完成上述操作的代码是在第3步所描述的嵌套for()循环之前插入的。
6. 编译并运行程序。这次你应该看到一个零值的非矩形布局。
7. 赋予twoD数组的每个元素一个明显的非零值(提示:使用Math.random() 以获得随机值)。
8. 声明一个属类型array of int的称作oneD的变量。然后, 创建一个包括4个元素的int数组。将该数组的引用分别赋予数组array twoD和oneD的第一个元素。赋值后,打印oneD和twoD数组。
9.编译并运行程序。请注意通过打印oneD的值而显示的单个数组与twoD数组的元素是相同的。
    3级:字谜游戏
1. 创建一个称作WordScrambler的应用程序,它具有一个词数组(长度最大为8个字符) ,用它可将一个词的字母拼凑(重排顺序) ,然后组成一个新词。
2.允许使用者看到词的拼凑过程,并分解5个游戏的运行过程。
三、练习小结
    讨论──用几分钟的时间讨论一下在以上练习中你所获得的经验、感想和发现。
经验  解释  总结  应用

四、检查你的进度
      在进入下一模块之前,请确认你已经能够:
- 声明并创建原始数组、类数组或数组类型
- 解释为什么数组的元素需初始化
- 给出数组定义并初始化数组元素
- 确定一个数组中元素的数量
- 创建多维数组
- 编写从一个数组类型到另一个数组类型数组值的拷贝代码
五、思考题
     你怎样创建一个三维数组?
     使用数组的缺点是什么?

TOP

SUN JAVA标准教程

第五章  对象和类
本模块是Java编程语言中讨论面向对象语句及面向对象特征两部分中的第一部分。
第一节  相关问题
讨论—下面的问题与本模块中出现的材料相关:
-到目前为止学习的Java编程语言的元素存在于大部分语言中,不管它们是否是面向对象语言。
-Java编程语言拥有什么特征使它成为一个面向对象语言?
-“面向对象”这个术语真正的含义是什么?
第二节  目 标
学完本模块,你便能:
-定义封装、多态性以及继承
-使用private及public访问修饰符
-开发程序段创建并初始化一个对象
-对一个特殊对象调用一个方法  
-描述构造函数及方法重载
-描述this引用的用途
-讨论为什么Java应用程序代码是可重复使用的  
-在一个Java程序中,确认:
-package语句
-import语句
-类、成员函数以及变量
-构造函数  
-重载方法
-覆盖方法
-父类构造函数
   
第三节  对象基础
    面向对象程序(OOP)语句能使现实世界中的概念在计算机程序中变成模块。它包括构造程序的特征以及组织数据和算法的机制。OOP语言有三个特征:封装、多态性及继承。所有这些特征与类的概念是息息相关的。
5.3.1  抽象数据类型
    当数据类型是由数据项组成时,可以定义许多程序段或方法在该类型数据上专门运行。当程序语言定义一个基本类型如整数时,它同时也定义了许多运算方法(如加法、减法、乘法和除法),因而它可以在该类型的实例中运行。
在许多程序语言中,一旦一个集合数据类型已经定义,程序员定义应用函数在该类型的变量上运行,该变量在代码和集合类型(除非可能在命名规则中)之间无任何联系。
    有些程序语言,包括Java,允许在数据类型的声明和操作该类型变量的代码的声明之间有紧密的联系。这种联系通常被称为抽象数据类型。

TOP

SUN JAVA标准教程

5.3.2  类和对象
    Java编程语言中的抽象数据类型概念被认为是class。类给对象的特殊类型提供定义。它规定对象内部的数据,创建该对象的特性,以及对象在其自己的数据上运行的功能。因此类就是一块模板。Objects是在其类模块上建立起来的,很象根据建筑图纸来建楼。同样的图纸可用来建许多楼房,而每栋楼房是它自己的一个对象。
    应该注意,类定义了对象是什么,但它本身不是一个对象。在程序中只能有类定义的一个副本,但可以有几个对象作为该类的实例。在Java编程语言中使用new运算符实例化一个对象。
    在类中定义的数据类型用途不大,除非有目的地使用它们。方法定义了可以在对象上进行的操作,换言之,方法定义类来干什么。因此Java编程语言中的所有方法都属于一类。不象C++程序,Java软件程序不可能在类之外的全局区域有方法。
看一个类的例子:
    class EmpInfo {
       String name;
       String designation;
       String department;
     }
    这些变量(name, designation和department)被称为类EmpInfo的成员。
实例化一个对象,创建它,然后如下所述对其成员赋值:
      EmpInfo employee = new EmpInfo();  //creates instance
      employee.name = Robert Javaman ";   // initializes
      employee.designation = " Manager " ;  // the three
      employee.department = " Coffee Shop " ;  // members
EmpInfo类中的employee对象现在就可以用了。例如:
     System.out.println(employee.name + " is " +
        employee.designation + " at " +
        employee.department);
打印结果如下:
     Robert Javaman is Manager at Coffee Shop
   如下所述,现在可以在类中放入方法print( )来打印数据了。数据和代码可以封装在一个单个实体中,这是面向对象语言的一个基本特征。定名为print( )的代码段可以被作为一个方法而调用,它是术语“函数”的面向对象的称法。
class EmpInfo {
   String name;
   String designation;
   String department;
   void print() {
        System.out.println(name + " is " + designation + " at " + department);
   }
}
    一旦对象被创建并被实例化,该方法就打印出类成员的数据。按下述步骤实现:
     EmpInfo employee = new EmpInfo(); // creates instance
     employee.name = " Robert Javaman " ; // initializes
     employee.designation = " Manager " ; // the three
     employee.department = " Coffee Shop " ; // members
     employee.print();// prints the details
    看看集合数据类型MyDate 和对下一个日期赋值的函数tomorrow( )。
    按如下所述在MyDate类型和tomorrow( )方法之间创建一种联系:
     public class MyDate {
        private int day, month, year;
        public void tomorrow() {
          // code to increment day
       }
     }
注—本声明中的“private”一词将在后文描述。         
方法不是(作为分离的实体)在数据上运行,而数据(作为对象的一部分)对它本身进行操作。
     MyDate d = new MyDate();
     d.tomorrow();
    这个注释表明行为是由对象而不是在对象上完成的。记住,可以用点记号来指向MyDate类中的字段。
    这就意味着“MyDate对象的day字段由变量d.调用。于是,前面的例子“MyDate对象的tomorrow行为由变量d.调用” ,换言之,就是d对象对它本身进行tomorrow()运算。
    方法是一个对象的属性并且能作为单个单元的一部分与它所在对象的数据发生密切的相互作用,这个是一个关键的面向对象的概念。(如果与这个概念不同,即,方法是分离的实体,从外部引入,作用在数据上。)message passing(消息传递)这个术语通常用来表达这样一个概念,即:指示一个对象在它本身数据上做某项工作,一个对象的方法定义了该对象能在它本身数据上做什么。

TOP

SUN JAVA标准教程

5.3.3 定义方法 定义方法 方法声明采取这样的格式: ([]) [throws ] {} public void addDays(int days) { } Java编程语言使用一种与其它语言,尤其是C和C++,非常相似的办法来定义方法。其声明采用以下格式: ([]) [throws ] {} 可以是任何合法标识符,并带有用已经使用的名称为基础的某些限制条件。 表示方法返回值的类型。如果方法不返回任何值,它必须声明为void(空)。Java技术对返回值是很严格的,例如,如果声明某方法返回一个int值,那么方法必须从所有可能的返回路径中返回一个int(只能在等待返回该int值的上下文中被调用。) 段能承载许多不同的修饰符,包括公共的、受保护的,以及私有的。公共访问修饰符表示方法可以从任何其它代码调用。私有表示方法只可以由该类中的其它方法来调用。受保护将在以后的课程中讨论。 允许将参数值传递到方法中。列举的元素由逗号分开,而每一个元素包含一个类型和一个标识符。 throws 子句导致一个运行时错误(异常)被报告到调用的方法中,以便以合适的方式处理它。非正常的情况在中有规定。 例如: public void addDays(int days) { } 告诉方法的本体,用,来接受表示将天数增加到当前日期中的那个参数。 在这种方法中,值是以标识符days来引用的。 5.3.4 值传递 值传递 Java编程语言只由值传递参数 当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。 对象的内容可以在被调用的方法中改变,但对象的引用是永远不会改变的。 Java编程语言只由值传递参数,也就是说,参数不能由被调用的方法来改变。当一个对象实例作为一个参数传递到方法中时,参数的值就是对象的引用。对象的内容可以在被调用的方法中改变,但对象引用是永远不会改变的。 下面的代码例子可以阐明这一点: 1 public class PassTest { 2 3float ptValue; 4 5// Methods to change the current values 6public void changeInt (int value) { 7value = 55; 8} 9 10public void changeStr (String value) { 11value = new String ( " different " ); 12} 13 14public void changeObjValue (PassTest ref) { 15ref.ptValue = 99.0f; 16} 17 18public static void main (String args[]) { 19 20String str; 21int val; 22 23// Create an instance of the class 24 25PassTest pt = new PassTest (); 26// Assign the int 27val = 11; 28 29// Try to change it 30pt.changeInt (val); 31 32// What is the current value? 33System.out.println ( " Int value is: " + val); 34 35// Assign the string 36str = new String ( " hello " ); 37 38// Try to change it 39pt.changeStr (str); 40 41// What is the current value? 42System.out.println ( " Str value is: " + str); 43 44// Now set the ptValue 45pt.ptValue = 101.0f; 46 47 48// Now change the value of the float 49// through the object reference 50pt.changeObjValue (pt); 51 52// What is the current value? 53System.out.println ( " Current ptValue is: " + 54 pt.ptValue); 55 } 56} 这个代码输出如下内容: c:\student\source\> java PassTest Int value is: 11 Str value is: hello Current ptValue is: 99.0 字符串对象是不会被changeStr()改变的,但是,PassTest对象的内容被改变了。

TOP

SUN JAVA标准教程

5.3.5  this引用
this引用
public class MyDate {
  private int day, month, year;
  public void tomorrow() {
    this.day = this.day + 1;
    // wrap around code...
  }
}
    关键字this是用来指向当前对象或类实例的。这里,this.day指的是当前对象的day字段。
public class MyDate {
  private int day, month, year;
  public void tomorrow() {
    this.day = this.day + 1;
    // wrap around code...
  }
}
    Java编程语言自动将所有实例变量和方法引用与this关键字联系在一起,因此,使用关键字在某些情况下是多余的。下面的代码与前面的代码是等同的。
public class MyDate {
  private int day, month, year;
  public void tomorrow() {
    day = day + 1; // no ` this. ' before ` day '
    // wrap around code...
  }
}
  也有关键字this使用不多余的情况。如,需要在某些完全分离的类中调用一个方法并将当前对象的一个引用作为参数传递时。例如:
Birthday bDay = new Birthday (this);
5.3.6  数据隐藏
数据隐藏
public class Date {
  private int day, month, year;
  pubic void tomorrow(){
    this.day = this.day +1;
    // wrap around code…
  }
}
public class DateUser {
  public static void main(String args[]) {
    Date mydate = new MyDate ();
    Mydate.day = 21; //illegal!
  }
}
在MyDate 类的day,month,year声明中使用关键字private,使从除了在MyDate类本身的方法以外的任何代码中访问这些成员成为不可能。因此,给MyDate类指定一个声明,下述代码是非法的:
public class DateUser {
  public static void main(String args[]) {
    MyDate d = new MyDate ();
    d.day = 21; // illegal!
  }
}
   防止直接访问数据变量看起来奇怪,但它实际上对使用MyDate类的程序的质量有极大的好处。既然数据的单个项是不可访问的,那么唯一的办法就是通过方法来读或写。因此,如果要求类成员的内部一致性,就可以通过类本身的方法来处理。
    思考一下允许从外部对其成员进行自由访问的MyDate类。代码做如下工作将是非常容易的:
MyDate d = new MyDate ();
d.day = 32; // invalid day
d.month = 2; d.day = 30; // plausible but wrong
d.month = d.month + 1; // omit check for wrap round
   
警告—这些和其它类似的赋值导致了在MyDate对象字段中无效的或不一致的值。这种情形是不可能马上作为问题暴露出来的,但肯定会在某个阶段终止程序。
    如果类的数据成员没有暴露(被封装在类里),那么,类的用户会被迫使用方法来修改成员变量。这些方法能进行有效性检查。考虑将下述方法作为MyDate类的一个部分。
public void setDay(int targetDay) {
  if (targetDay > this.daysInMonth()) {
    System.err.println( " invalid day " + targetDay);
  }
  else {
    this.day = targetDay;
  }
}
方法检查就是要看所要求设定的日期是否有效。如果日期无效,方法忽略了请求并打印出了信息。后面将会看到Java编程语言提供一个更有效的机制来处理溢出的方法参数。 但现在,你可以看到Date类对无效日期是受到有效保护的。
注—在上例中,daysInMonth()被指定为Date类中的一个方法。因此,它有一个this值,从这个值上,它可以提取回答询问所要求的month和 year。由于与成员变量一起使用,daysInMonth()方法的this的使用就多余了。
关于如何正确地调用方法,诸如“参数“月份”的值在对象中必须在有效范围内”的规则被叫做不变量(或前置和后置条件)。谨慎使用前置条件测试,可使类更容易再次使用,而且在重用中更可靠,因为如果任何方法被误用,使用该类的程序员能马上发现。

TOP

SUN JAVA标准教程

5.3.7  封装
封装
    隐藏类的实现细节
    迫使用户去使用一个界面去访问数据
    使代码更好维护
    除了保护对象的数据不被误修改外,在确保所要求的副作用被正确处理的情况下,迫使用户通过方法访问数据能使类的重用变得更简单。比如,在MyDate类的情形之下,考虑如何构造tomorrow()方法。
如果数据完全可以访问,那么类的每一个用户都需要增加day的值,测试当前月份的天数,并处理月终(而且可能是年终)情形。这就毫无意义而且容易出错。仅管这些要求对日期来说是很好理解的,其它数据类型可能具有鲜为人知的相似的限制。通过迫使类的用户去使用所提供的tomorrow()方法,保证每个人每次都能连续地并且正确地处理必要的副作用。
数据隐藏通常指的就是封装。它将类的外部界面与类的实现区分开来,隐藏实现细节。迫使用户去使用外部界面,即使实现细节改变,还可通过界面承担其功能而保留原样,确保调用它的代码还继续工作。这使代码维护更简单。
第四节  重载方法名称
重载方法名称
    它可如下所示使用:
           public void println(int i)
           public void println(float f)
           public void println()
参数表必须不同
返回类型可以不同
    在某些情况下,可能要在同一个类中写几种做同样工作但带有不同参数的方法。考虑一个简单方法,它试图输出参数的文本表示法。这种方法被称做println()。
    现在假设打印每个int,float,String类型需要不同的打印方法。这是合情合理的,因为各种数据类型要求不同的格式,而且可能要求不同的处理。可以分别创建三种方法,即:printInt(),printfloat()和printString()。但这是很乏味的。
    Java与其它几种编程语言一样,允许对不止一种方法重用方法名称。清楚地说,只有当某种东西能区分实际上需要哪种方法并去调用它时,它才能起作用。在三种打印方法的情况下,可能在参数的数量和类型的基础上对此进行区分。
    通过重用方法名称,可用下述方法结束:
public void println(int i)
public void println(float f)
public void println()
    当写代码来调用这些方法中的一种方法时,根据提供的参数的类型选择合适的一种方法。
    有两个规则适用于重载方法:
    调用语句的参数表必须有足够的不同,以至于允许区分出正确的方法被调用。正常的拓展晋升(如,单精度类型float到双精度类型double)可能被应用,但是这样会导致在某些条件下的混淆。
    方法的返回类型可以各不相同,但它不足以使返回类型变成唯一的差异。重载方法的参数表必须不同。
第五节  构造并初始化对象
构造并初始化对象
调用new XXX()来为新对象分配空间会产生如下结果:
    新对象的空间被分配并被初始化为0或空值
    进行了显式的初始化
    构造函数被执行
    已经看到,为了给新对象分配空间,如何必须执行调用到new XXX()。有时还会看到参数被放在括号中,如:new Button (“press me”)。使用关键字new会导致下述情况:
    首先,新对象的空间被分配并初始化到0或空值。在Java编程语言中,该阶段是不可分的,以确保在分配空间的过程中不会有大小为随机值的对象。
    其次,进行显式的初始化。
    第三,构造函数被执行,它是一种特殊方法。括号中传递给new的参数被传递给构造函数。
本节讨论这最后两个步骤。
5.5.1  显式成员初始化
显式成员初始化
public class Initialized {
  private int x = 5;
  private String name = " Fred " ;
  private Date created = new Date();
  // Methods go here
  ...
}
  如果将简单的赋值表达式放在成员声明中,在对象构造过程中会进行显式成员初始化。
public class Initialized {
  private int x = 5;
  private String name = " Fred " ;
  private Date created = new Date();
// Methods go here
...
}
5.5.2  构造函数
构造函数
方法名称必须完全与类名称相匹配
对于方法,不要声明返回类型
    刚刚描述过的显式初始化机制为在一个对象中设定字段的初始值提供了一个简单的办法。但有时确实需要执行一个方法来进行初始化。也许还需要处理可能发生的错误。或者想用循环或条件表达式来进行初始化。或者想把参数传递到构造过程中以便要求构造新对象的代码能控制它创建的对象。
    一个新对象的初始化的最终步骤是去调用一个叫做构造函数的方法。
    构造函数是由下面两个规则所确认的:
    方法名称必须与类名称完全相匹配。
    对于方法,不要声明返回类型
构造函数
public class Xyz {
  // member variables go there
public Xyz() {
  // set up the object.
    }
public Xyz(int x) {
// set up the object with a parameter
    }
}
public class Xyz {
  // member variables
  public Xyz() {               // No-arg constructor
  // set up the object.
  }
  public Xyz(int x) {           //int-arg constructor
    // set up the object using the parameter x.
  }
}
注—由于采用了方法,因此可以通过为几个构造函数提供不同的参数表的办法来重载构造函数。当发出new Xyz(argument_list)调用的时候,传递到new语句中的参数表决定采用哪个构造函数。                                          

TOP

SUN JAVA标准教程

5.5.3  调用重载构造函数
调用重载构造函数
public class Employee {
  private String name;
  private int salary;
public Employee(String n, int s) {
  name = n;
  salary = s;
}
public Employee(String n) {
  this(n, 0);
}
  public Employee() {
    this( " Unknown " );
  }
}
    如果有一个类带有几个构造函数,那么也许会想复制其中一个构造函数的某方面效果到另一个构造函数中。可以通过使用关键字this作为一个方法调用来达到这个目的。
public class Employee {
private String name;
private int salary;
public Employee(String n, int s) {
  name = n;
  salary = s;
}
public Employee(String n) {
  this(n, 0);
}
public Employee() {
  this( " Unknown " );
}
}
    在第二个构造函数中,有一个字符串参数,调用this(n,0)将控制权传递到构造函数的另一个版本,即采用了一个String参数和一个int参数的构造函数中。
在第三个构造函数中,它没有参数,调用this(“Unknownn”)将控制权传递到构造函数的另一个版本,即采用了一个String参数的构造函数中。
注—对于this的任何调用,如果出现,在任何构造函数中必须是第一个语句。
5.5.4  缺省构造函数
缺省构造函数
    每个类中都有
    能够用new Xxx()创建对象实例
如果增加一个带参数的构造函数声明,将会使缺省无效
    每个类至少有一个构造函数。如果不写一个构造函数,Java编程语言将提供一个。该构造函数没有参数,而且函数体为空。
缺省构造函数能用new Xxx()创建对象实例,反之,你会被要求为每个类提供一个构造函数。
注—如果增加一个带参数的构造函数声明到一个类中,该类以前没有显式构造函数,那么将失去该缺省构造函数。基于这一点,对new Xxx()的调用将会引起编译错误。认识到这一点很重要。

TOP

返回列表 回复 发帖