第2章MiniJOOL语言 、 static void main()! Various Variables vv=new VariousVariables(2) 图2-3 MiniJOOL程序示例:变量的种类 1、变量的种类 MiniJOOL语言中的变量有以下六种: ·类变量(class variable):指在类声明中用static声明的域(即类中的数据成员),如 图2-3中的m和。当类初始化时,会为这个类创建类变量并将类变量初始化为缺 省值(注意:对于fial类变量,必须被显式赋值),类变量的生命期直到程序运行 结束为止。 ·实例变量(instance variable):指在类声明中未用static声明的域,如图2-3中的x y和W。在创建类的每一新实例(即对象)时,都要为类中的各实例变量创建一个 新变量并按缺省值对变量进行初始化。实例变量的生命期随类实例的消亡而结束。 。局部变量:由局部变量声明语句来声明,如图2-3中的ol和w。当控制流进入 个语句块时,为在该语句块中声明的每一局部变量创建一个新变量:当退出该语句 块时,局部变量不再存在。 ●方法的形参:用来命名传递给方法的实参,如图23中的X。形参变量在方法被调 用时创建,在方法体执行结束时不复存在。 ·构造器的形参:用来命名传递给构造器的实参,如图2-3中的ml。形参变量在用 n©w表达式创建类实例或者显式调用构造器时被创建,在构造器的体执行结束时不 再存在。 ·数组元素:是未命名的变量,程序中可以通过数组引用表达式(类型为数组类型) 加下标表达式米使用数组元素,如图2-3中的wx%10]。数组元素变量随数组变量 的创建而被创建并被初始化,随数组变量的消亡而结束生命期。 2、final变量 只有类变量和实例变量可以声明为final,.final变量只能被赋值一次。如果final变量被赋 值多次,将产生编译时错误。声明为al的类变量必须在声明时带初始化表达式,如图2-3 中的“n=5”,否则产生编译时错误。 3、变量的初值 MiniJOOL程序中的每一变量在使用前必须有值。 ●类变量、实例变量或数组元素在创建时按缺省值初始化。it型变量的缺省值是0, boolean型变量的缺省值是false,类类型和String型变量的缺省值是null。例如,图 2-3中m、x以及w的10个元素均缺省为0。 30
第 2 章 MiniJOOL 语言 30 } static void main( ){ VariousVariables vv = new VariousVariables(2); …… } } 图 2-3 MiniJOOL 程序示例:变量的种类 1、变量的种类 MiniJOOL 语言中的变量有以下六种: z 类变量(class variable):指在类声明中用static声明的域(即类中的数据成员),如 图 2-3 中的m和n。当类初始化时,会为这个类创建类变量并将类变量初始化为缺 省值(注意:对于final类变量,必须被显式赋值),类变量的生命期直到程序运行 结束为止。 z 实例变量(instance variable):指在类声明中未用static声明的域,如 图 2-3 中的x、 y和w。在创建类的每一新实例(即对象)时,都要为类中的各实例变量创建一个 新变量并按缺省值对变量进行初始化。实例变量的生命期随类实例的消亡而结束。 z 局部变量:由局部变量声明语句来声明,如 图 2-3 中的oldx和vv。当控制流进入一 个语句块时,为在该语句块中声明的每一局部变量创建一个新变量;当退出该语句 块时,局部变量不再存在。 z 方法的形参:用来命名传递给方法的实参,如 图 2-3 中的x。形参变量在方法被调 用时创建,在方法体执行结束时不复存在。 z 构造器的形参:用来命名传递给构造器的实参,如 图 2-3 中的m1。形参变量在用 new表达式创建类实例或者显式调用构造器时被创建,在构造器的体执行结束时不 再存在。 z 数组元素:是未命名的变量,程序中可以通过数组引用表达式(类型为数组类型) 加下标表达式来使用数组元素,如 图 2-3 中的w[x%10]。数组元素变量随数组变量 的创建而被创建并被初始化,随数组变量的消亡而结束生命期。 2、final 变量 只有类变量和实例变量可以声明为final,final变量只能被赋值一次。如果final变量被赋 值多次,将产生编译时错误。声明为final的类变量必须在声明时带初始化表达式,如 图 2-3 中的“n = 5”,否则产生编译时错误。 3、变量的初值 MiniJOOL 程序中的每一变量在使用前必须有值。 z 类变量、实例变量或数组元素在创建时按缺省值初始化。int型变量的缺省值是 0, boolean型变量的缺省值是false,类类型和String型变量的缺省值是null。例如,图 2-3 中m、x以及w的 10 个元素均缺省为 0
第2章MiniJOOL语言 ·方法或构造器的形参被初始化为由调用者提供的对应的实参值 ·局部变量必须在使用前被显式赋值,否则产生编译时错误。 ·类变量和实例变量在声明时可以带初始化表达式,如“static inta4,int2]b={a,2 String[]ss={ycar”,“month”,“day”,”.类变量的初始化表达式在类初始化时求值并 被赋给类变量,而实例变量的初始化表达式则在实例创建后、构造器执行前被求值 并赋给变最。局部变最在声明时也可以带初始化表达式,它在执行这个声明语句时 被求值并威给相应的局部变量。 数组声明中的初喵化表达式 针对数组变量在声明时的初始化,MiniJOOL语言中的规定与Java语言不太一样。在 Java语言中,当数组变量在声明时含有初始化表达式(即包含在花括号内、由逗号分隔的表 达式序列)时,不能指定数组的长度,数组变最的长度由初始化表达式中的表达式个数决定 此外,花括号中的表达式序列允许以逗号结尾。例如“[2]b={1,2:”将产生编译错误, 而可以使用“in0b-{l,2:”或“intb0F{L,2,:”。在MiniJOOL语言中,数组的初始化表 达式同样是包含在花括号内、由逗号分隔的表达式序列,但是它不允许以逗号结尾:当数组 变量在声明时含有初始化表达式时,可以同时指定数组的长度,并且要求指定的长度必须与 初始化表达式中的表达式个数一样。例如“mt2b={1,2:”和“it0b=1,2:”是正确的 但是“int3)b=1,2:”将产生编译时错误。 4、变量所允许的类型和使用特征 类变量、实例变量、局部变量、方法或构造器的形参都可以声明为int、boolean、String 数组类型或类类型。但是,当类变最、实例变量和局部变最声明为数组类型时,必须指定数 组的长度:而当方法或构造器的形参声明为数组类型时则不必指定数组的长度,这时形参将 引用传来的实参数组。在MiniJOOL语言中,方法或构造器的参数传递方式是单向的值传递。 下面简述各种类型变量的使用特征: ·it型变量只能被赋予it型值:一旦它被赋值,则可以参加比较运算和算术运算 也可以作为实参传递给int型形参,还可以传给prit语句输出到标准外设。 boolean型变量只能被赋予boolean型值:一日它被赋值,则可以参加关系运算和 逻辑运算,可以作为if和while语句的条件,也可以作为实参传递给boolean型形 参,还可以传给prit语句输出到标准外设(注意:输出的是字符串tue或false)。 ·String型变量只能被赋予字符串常最或已被赋值的String型变量:如前所述,String 型变量保存的是对字符串的引用值,这样,一个字符串常量就可以被多个字符串变 量所引用。此外,String型变量在被赋值(注意:不是nul,而是引用一个实际的 字符串)之后可以作为实参传递给String型形参,还可以传给print语句将所引用 的字符串输出到标准外设。 数组变量只能作为实参传递给具有相同元素类型的、数组类型的形参,这时数组形 参将引用实参数组。数组类型的形参可以接收不同长度的、但元素类型相同的数细 类型的实参。不能将一个数组变量赋值给另一个数组变量,否则产生编译时错误(如 图2-3中的w1=w是错误的) 31
第 2 章 MiniJOOL 语言 31 z 方法或构造器的形参被初始化为由调用者提供的对应的实参值。 z 局部变量必须在使用前被显式赋值,否则产生编译时错误。 z 类变量和实例变量在声明时可以带初始化表达式,如“static int a=4; int[2] b={a, 2}; String[] ss={“year”, “month”, “day”};”。类变量的初始化表达式在类初始化时求值并 被赋给类变量,而实例变量的初始化表达式则在实例创建后、构造器执行前被求值 并赋给变量。局部变量在声明时也可以带初始化表达式,它在执行这个声明语句时 被求值并赋给相应的局部变量。 数组声明中的初始化表达式 针对数组变量在声明时的初始化,MiniJOOL 语言中的规定与 Java 语言不太一样。在 Java 语言中,当数组变量在声明时含有初始化表达式(即包含在花括号内、由逗号分隔的表 达式序列)时,不能指定数组的长度,数组变量的长度由初始化表达式中的表达式个数决定; 此外,花括号中的表达式序列允许以逗号结尾。例如“int[2] b={1, 2};”将产生编译错误, 而可以使用“int[] b={1, 2};”或“int b[]={1, 2,};”。在 MiniJOOL 语言中,数组的初始化表 达式同样是包含在花括号内、由逗号分隔的表达式序列,但是它不允许以逗号结尾;当数组 变量在声明时含有初始化表达式时,可以同时指定数组的长度,并且要求指定的长度必须与 初始化表达式中的表达式个数一样。例如“int[2] b={1, 2};”和“int[] b={1, 2};”是正确的, 但是“int[3] b={1, 2};”将产生编译时错误。 4、变量所允许的类型和使用特征 类变量、实例变量、局部变量、方法或构造器的形参都可以声明为 int、boolean、String、 数组类型或类类型。但是,当类变量、实例变量和局部变量声明为数组类型时,必须指定数 组的长度;而当方法或构造器的形参声明为数组类型时则不必指定数组的长度,这时形参将 引用传来的实参数组。在 MiniJOOL 语言中,方法或构造器的参数传递方式是单向的值传递。 下面简述各种类型变量的使用特征: z int 型变量只能被赋予 int 型值;一旦它被赋值,则可以参加比较运算和算术运算, 也可以作为实参传递给 int 型形参,还可以传给 print 语句输出到标准外设。 z boolean 型变量只能被赋予 boolean 型值;一旦它被赋值,则可以参加关系运算和 逻辑运算,可以作为 if 和 while 语句的条件,也可以作为实参传递给 boolean 型形 参,还可以传给 print 语句输出到标准外设(注意:输出的是字符串 true 或 false)。 z String 型变量只能被赋予字符串常量或已被赋值的 String 型变量;如前所述,String 型变量保存的是对字符串的引用值,这样,一个字符串常量就可以被多个字符串变 量所引用。此外,String 型变量在被赋值(注意:不是 null,而是引用一个实际的 字符串)之后可以作为实参传递给 String 型形参,还可以传给 print 语句将所引用 的字符串输出到标准外设。 z 数组变量只能作为实参传递给具有相同元素类型的、数组类型的形参,这时数组形 参将引用实参数组。数组类型的形参可以接收不同长度的、但元素类型相同的数组 类型的实参。不能将一个数组变量赋值给另一个数组变量,否则产生编译时错误(如 图 2-3 中的w1=w是错误的)
第2章MiniJOOL语言 ●数组元素在使用前时需要做下标是否域界的检查,这种域界检查要求尽量在馆译时 完成,对于编译时无法判断的,则应在运行时检查。当数组元素越界时,应产生综 译或运行时错误。除此之外,数组元素在使用上与类型和它相同的变量没有差异。 ·类类型的变量缺省为u,它可以通过new表达式被实例化,也可以引用一个由类 型为该类或其子类的变量传来的实例:一旦类类型的变量被赋值,则可以进行域访 问、方法调用、检测其运行时类型、判断引用是否相等,还可以传递给具有相同类 类型或超类类型的的形参。 23.1.3类型兼容原则 。一个类型与其自身相兼容: ·可以将一个类型为(r为int、bool或String,N为正整数)的值赋值给类型为 ]的变量: ·若A是B的子类,则声明为B的变量可以引用类型为A的实例。 2.3.2类 在MiniJOOL语言中没有public、.protected以及private这组访问控制修饰符,所有的类 及其数据成员(域)和方法成员都是对外可访问的。 1、类声明 类声明中的DENTIFIER为类名,如果一个MiniJOOL程序内声明了多个同名的类,则 产生编译时错误。类声明中可选的extends子句用来指定该类的直接超类(即父类)。需要 指出的是,主类没有父类,如果主类有extends子句,则产生编译时错误。 program class_declaration {class_declaration) class declaration class"IDENTIFIER ["extends"IDENTIFIER class_body 2、类成员 一个类的体由若干个成员声明组成,包括域、方法和构造器。对一个在类C中或被类C 继承的成员m来说,其作用域是类C的整个体。一个类的成员包括从其父类继承而来的所 有域成员和方法成员,以及在这个类的体中声明的成员。 class body=“"class body declaration class body declaration?“l” class_body_declaration=field declarationmethod_declarationconstructor declaration 3、域声明 field declaration 【""static”]["final"]type variable_“," variable declarators variable declarator""variable declarator variable_declarator =variable_declarator_id [variable_initializer] variable_declarator_id=IDENTIFIER variable initializer=expression array_initializer =“"[expression{",expression]"y 32
第 2 章 MiniJOOL 语言 32 z 数组元素在使用前时需要做下标是否越界的检查,这种越界检查要求尽量在编译时 完成,对于编译时无法判断的,则应在运行时检查。当数组元素越界时,应产生编 译或运行时错误。除此之外,数组元素在使用上与类型和它相同的变量没有差异。 z 类类型的变量缺省为 null,它可以通过 new 表达式被实例化,也可以引用一个由类 型为该类或其子类的变量传来的实例;一旦类类型的变量被赋值,则可以进行域访 问、方法调用、检测其运行时类型、判断引用是否相等,还可以传递给具有相同类 类型或超类类型的的形参。 2.3.1.3 类型兼容原则 z 一个类型与其自身相兼容; z 可以将一个类型为τ[N](τ 为 int、bool 或 String,N 为正整数)的值赋值给类型为 τ[ ]的变量; z 若 A 是 B 的子类,则声明为 B 的变量可以引用类型为 A 的实例。 2.3.2 类 在 MiniJOOL 语言中没有 public、protected 以及 private 这组访问控制修饰符,所有的类 及其数据成员(域)和方法成员都是对外可访问的。 1、类声明 类声明中的 IDENTIFIER 为类名,如果一个 MiniJOOL 程序内声明了多个同名的类,则 产生编译时错误。类声明中可选的 extends 子句用来指定该类的直接超类(即父类)。需要 指出的是,主类没有父类,如果主类有 extends 子句,则产生编译时错误。 program = class_declaration { class_declaration } class_declaration = “class” IDENTIFIER [ “extends” IDENTIFIER ] class_body 2、类成员 一个类的体由若干个成员声明组成,包括域、方法和构造器。对一个在类 C 中或被类 C 继承的成员 m 来说,其作用域是类 C 的整个体。一个类的成员包括从其父类继承而来的所 有域成员和方法成员,以及在这个类的体中声明的成员。 class_body = “{” class_body_declaration { class_body_declaration} “}” class_body_declaration= field_declaration | method_declaration | constructor_declaration 3、域声明 field_declaration = [ “static” ] [ “final” ] type variable_declarators “;” variable_declarators = variable_declarator { “,” variable_declarator } variable_declarator = variable_declarator_id [ “=” variable_initializer ] variable_declarator_id = IDENTIFIER variable_initializer = expression | array_initializer array_initializer = “{” [ expression { “,” expression } ] “}
第2章MiniJOOL语言 一个类的变量由域声明来引入。如果同一个类体中声明了两个同名的域,则产生编译时 错误。方法名、类名和域名可以相互同名,因为它们用在不同的上下文中。 域修饰符有static和final两种。如前所述,static域为类变量,而非static的域为实例变 量。前者在类被初始化时创建,一个类只有一个与该域对应的变量:该类的所有实共享这 个变量:而后者在创建类的任一新实例(即对象)时被创建,每个实例各有一个与该域对应 的变量。 类变量和实例变量都可以声明为fal,这些变量一旦被显式赋值,则禁止对其进行修 改。声明为final的类变量必须在类初始化中被显式赋值,而声明为final的实例变量必须在 每一构造器运行结束前被显式赋值,否则都将产生编译时错误。 一个类中的域声明可以隐藏其超类中的同名域声明,这两个域的类型(包括修饰符)允 许不相同。被隐藏的域可以通过受限名来访问,例如,static域可以通过“<类名>.<域名>” 来访问,而非static域可以通过“super.<域名>”来访问。与受限名相区分,可称非受限名为 简单名。图2-4中的代码示意了子类对超类中域声明的隐藏,以及访问子类和超类中域的方 法。 class Parent int v2=4 static int y3 =8: static int v4=16 int v5 =32: static v6=64: class Child extends Parent imtv1=128: ∥隐藏了类Parent中的实例变量vl String v2="256";∥隐藏了类Parent中的实例变量v2,即使它们的类型不同 static int v3.=5l2,∥隐藏了类Parent中的类变量v3 imtv4=1024 ∥类Child中的实例变量隐藏了类Parent中的类变量v4 void fo ∥以下3行代码引用类Child中的域 int locall =vl; String local2 v2: int local3=v3; ∥以下两行代码通过受限名访问超类中被覆盖的域 int superl=super.v1;∥通过super访问超类中的实例变量 int super3=Parent.v33,∥通过超类名访向超类中的类变量 ∥超类中没有被隐藏的域在子类中可以直接访问 int super5=v5; 3
第 2 章 MiniJOOL 语言 33 一个类的变量由域声明来引入。如果同一个类体中声明了两个同名的域,则产生编译时 错误。方法名、类名和域名可以相互同名,因为它们用在不同的上下文中。 域修饰符有 static 和 final 两种。如前所述,static 域为类变量,而非 static 的域为实例变 量。前者在类被初始化时创建,一个类只有一个与该域对应的变量;该类的所有实例共享这 个变量;而后者在创建类的任一新实例(即对象)时被创建,每个实例各有一个与该域对应 的变量。 类变量和实例变量都可以声明为 final,这些变量一旦被显式赋值,则禁止对其进行修 改。声明为 final 的类变量必须在类初始化中被显式赋值,而声明为 final 的实例变量必须在 每一构造器运行结束前被显式赋值,否则都将产生编译时错误。 一个类中的域声明可以隐藏其超类中的同名域声明,这两个域的类型(包括修饰符)允 许不相同。被隐藏的域可以通过受限名来访问,例如,static域可以通过“<类名>.<域名>” 来访问,而非static域可以通过“super.<域名>”来访问。与受限名相区分,可称非受限名为 简单名。图 2-4 中的代码示意了子类对超类中域声明的隐藏,以及访问子类和超类中域的方 法。 class Parent { int v1 = 2; int v2 = 4 static int v3 = 8; static int v4 = 16; int v5 = 32; static v6 = 64; } class Child extends Parent { int v1 = 128; // 隐藏了类 Parent 中的实例变量 v1 String v2 = "256"; // 隐藏了类 Parent 中的实例变量 v2,即使它们的类型不同 static int v3 = 512; // 隐藏了类 Parent 中的类变量 v3 int v4 = 1024; // 类 Child 中的实例变量隐藏了类 Parent 中的类变量 v4 void f() { // 以下 3 行代码引用类 Child 中的域 int local1 = v1; String local2 = v2; int local3 = v3; // 以下两行代码通过受限名访问超类中被覆盖的域 int super1 = super.v1; // 通过 super 访问超类中的实例变量 int super3 = Parent.v3; // 通过超类名访问超类中的类变量 // 超类中没有被隐藏的域在子类中可以直接访问 int super5 = v5;
第2章MiniJOOL语言 ∥也可以通过受限名显式访问超类中没有被隐藏的域 int super6=super.v5; int super7=Parent.v6 图2-4 MiniJOOL程序示例:隐藏域及其访问 一个域声明如果含有初始化表达式,如“ita=2:”,则该初始化表达式对类变量来说 会在类初始化时被计算赋值:而对实例变量来说会在类实例创建时被计算赋值。类变量的初 始化表达式中不允许引用任何实例变量名、his以及sup©r关健字,否则产生编译时错误 实例变量的初始化表达式可以使用在该类及其超类中声明的任意类变量的简单名,即便这个 类变量声明在后:实例变量的初始化表达式也可以通过受限名使用在其他类中声明的类变 最,即便这些类声明在后。只有当类C的一个实例变量1出现在类C的另一个实例变量2 的初始化表达式中时,1的声明才必须要求出现在其使用之前。 注意:当执行类实例创建表达式(见23.4节)时,将首先为新的类实例(对象)分配 空间,当对象空间分配成功之后,先按缺省值对对象中的各个实例变量赋值,然后再对实例 变量声明中的初始化表达式进行计算赋值,最后再查找确定要执行的构造器并执行之.因此, 对于图2-5中的两段代码来说,当分别执行“Aa=nwA(方”后,它们所创建的对象中实 例变量i的值均为3。 classA class A inti=2. A){ i=3 i=3 inti=2; 图2-5 MiniJOOL程序示例:实例变量的初始化 4、方法声明 method_declaration= method header method body method_header =[“static”](typevoid”)method_declarato method_declarator IDENTIFIER""[formal_parameter_list ]") formal_parameter_list=formal_parameter"formal_parameter formal_parameter type variable_declarator_id method_body =block 方法用来声明可以被调用执行的代码。一个方法的形参由一组逗号分隔的参数说明列表 组成,每个参数说明由类型及表示参数名的标识符组成。方法的形参的作用域是该方法的整 个体,在方法体中不能声明与形参同名的局部变量。形参只可以通过简单名来使用,而不能 是受限名。方法的形参可以与类中的域名同名,这时在方法体中,外层的域声明将被隐藏
第 2 章 MiniJOOL 语言 34 // 也可以通过受限名显式访问超类中没有被隐藏的域 int super6 = super.v5; int super7 = Parent.v6; } } 图 2-4 MiniJOOL 程序示例:隐藏域及其访问 一个域声明如果含有初始化表达式,如“int a = 2;”,则该初始化表达式对类变量来说 会在类初始化时被计算赋值;而对实例变量来说会在类实例创建时被计算赋值。类变量的初 始化表达式中不允许引用任何实例变量名、this 以及 super 关键字,否则产生编译时错误。 实例变量的初始化表达式可以使用在该类及其超类中声明的任意类变量的简单名,即便这个 类变量声明在后;实例变量的初始化表达式也可以通过受限名使用在其他类中声明的类变 量,即便这些类声明在后。只有当类 C 的一个实例变量 v1 出现在类 C 的另一个实例变量 v2 的初始化表达式中时,v1 的声明才必须要求出现在其使用之前。 注意:当执行类实例创建表达式(见 2.3.4 节)时,将首先为新的类实例(对象)分配 空间,当对象空间分配成功之后,先按缺省值对对象中的各个实例变量赋值,然后再对实例 变量声明中的初始化表达式进行计算赋值,最后再查找确定要执行的构造器并执行之。因此, 对于 图 2-5 中的两段代码来说,当分别执行“A a=new A( );”后,它们所创建的对象中实 例变量i的值均为 3。 class A { int i = 2; A( ) { i = 3; } } class A { A( ) { i = 3; } int i = 2; } 图 2-5 MiniJOOL 程序示例:实例变量的初始化 4、方法声明 method_declaration = method_header method_body method_header = [ “static” ] ( type | “void” ) method_declarator method_declarator = IDENTIFIER “(” [ formal_parameter_list ] “)” formal_parameter_list = formal_parameter { “,” formal_parameter } formal_parameter = type variable_declarator_id method_body = block 方法用来声明可以被调用执行的代码。一个方法的形参由一组逗号分隔的参数说明列表 组成,每个参数说明由类型及表示参数名的标识符组成。方法的形参的作用域是该方法的整 个体,在方法体中不能声明与形参同名的局部变量。形参只可以通过简单名来使用,而不能 是受限名。方法的形参可以与类中的域名同名,这时在方法体中,外层的域声明将被隐藏