〃北?copy;”);这个例子创建了University的一个实例,存放在u中。通过点操作符来调用这个实例的init成员函数,把”北?copy;大学”和”北?copy;”分别传递给参数a和b。在init成员函数内部,name和city直接指向u所指向的对象的实例变量。把name赋值为”北?copy;大学”,city赋值为”北?copy;”,然后返回。在这个例子里,init被定义为无值(void?copy;返回类型。在进行这个成员函数调用后,u指向这个name值和city值改变了的University对象。
5。7thisJava有一个特殊的实例值叫this,它用来在一个成员函数内部指向当前的对象。在前面的例子里,我们调用u。init,一?copy;进入init成员函数内部,this就会指向u所指向的对象。在Java里,在同一个范围定义两个相同名字的局部变
量是不可以的。有趣的是,局部变量、成员函数的参数可以和实例变量的名字相同。前面我们没有用name和city作为成员函数init的参数名字,因为这样它们在成员函数的范围里就把实例变量name和city隐藏了,即name指向参数name,隐藏了实例变量name。this让我们可以直接指向对象本身。下面是另一个版本的init,用name和city作为参数名字,用this来接收当前对象的实例变量。
voidinit(Stringname;Stringcity){this。name=name;this。city=city;}下面是带有新的init初始成员函数的TwoUniversity例子。classUniversity{Stringname;city;voidinit(Stringname;Stringcity){this。name=name;this。city=city;}}classTwoUniversityInit{publicstaticvoidmain(Stringargs''){Universityu1=newUniversity();Universityu2=newUniversity();u1。init(”北?copy;大学”;”北?copy;”);u2。init(”清华大学”;”北?copy;”);System。out。println(”大学:”+u1。name+”城市:”+u1。city);system。out。println(”大学:”+u2。name+”城市:”+u2。city);}}
5。8构造函数(Constructor)
每创建一个类的实例都去初始化它的所有变量是乏味的。如果一个对象在被创建时就完成了所有的初始工作,将是简单的和简洁的。因此,Java在类里提?copy了一个特殊的成员函数,叫做构造函数(Constructor?copy;。一个构造函数是对象被创建时初始对象的成员函数。它具有和它所在的类完全一样的名字。一?copy;定义好一个构造函数,创建对象时就会自动调用它。构造函数没有返回类型,即使是void类型也没有。这是因为一个类的构造函数的返回值的类型就是这个类本身。构造函数的任务是初始一个对象的内部状态,所以用new操作符创建一个实例后,立刻就会得到一个清楚、可用的对象。下面这个例子里,用构造函数取代了成员函数init。
classUniversity{Stringname;city;University(Stringname;Stringcity)
{this。name=name;this。city=city;}}classUniversityCreate{publicstaticvoidmain(Stringargs''){Universityu=newUniversity(”北?copy;大学”;”北?copy;”);System。out。println(”大学:”+u。name+”城市:”+u。city);}}new语句中类名后的参数是传给构造函数的。
5。9成员函数重载
对于几个意义相近的成员函数,有时使用相同的名字便于理解。因此,Java语言实现了成员函数重载,即可以创建几个名字相同、参数不同的成员函数。成员函数重载提?copy;了Java的多态行为。下面的例子用到了重载。classUniversity{Stringname;city;University(Stringname;Stringcity){this。name=name;this。city=city;}University(){name=”北?copy;大学”;city=”北?copy;”;}}
classUniversityCreateAlt{publicstaticvoidmain(Stringargs''){Universityu=newUniversity();System。out。println(”大学:”+u。name+”城市:”+u。city);}}
这个例子创建了一个University对象,调用了第二个构造函数。下面是它的运行结果。
C:》javaUniversityCreateAlt大学:北?copy;大学城市:北?copy;一个构造函数可以调用另一个构造函数来创建实例。例如:
classUniversity{Stringname;city;University(Stringname;Stringcity)
{this。name=name;this。city=city;}University(){this(”北?copy;大学”;”北?copy;”);}}
第二个构造函数调用了第一个构造函数来完成实例的初始化。你也可以用重载来创建一般的成员函数。下面这个例子里有University类的两个版本的samecity成员函数。samecity判断一个大学是否在一个城市里或一个大学和另一个大学是否在同一个城市里。一个成员函数用city作参数,另一个用University对象作参数。
classUniversity{Stringname;city;University(Stringname;Stringcity)
{this。name=name;this。city=city;}booleansamecity(Stringcity){if
(city。equals(this。city))returntrue;elsereturnfalse;}boolean
samecity(Universityu){returnsamecity(u。city);}}
classUniversityCity{publicstaticvoidmain(Stringargs''){String
city=”上海”;Universityu1=newUniversity(”北?copy;大学”;”
北?copy;”);Universityu2=newUniversity(”清华大学”;”北?copy;”);
System。out。println(”u1=”+u1。name+”;”+u1。city);
System。out。println(”u2=”+u2。name+”;”+u2。city);
System。out。println(”city=”+city);
System。out。println(”u1。samecity(u2)=”+u1。samecity(u2));
System。out。println(”u1。samecity(city)=”+u1。samecity(city));}}
下面是该程序的运行结果。
C:》javaUniversityCityu1=北?copy;大学;北?copy;u2=清华大学;北?copy;city=上海u1。samecity(u2)=trueu1。samecity(city)=false
5。10继承
第二个基本的面向对象机制是继承。继承是关于有层次关系的类?reg;间的概念。一个类的后代可以继承它的祖先的所有变量和成员函数,就象创建自己的一样。一个类的直接父亲叫做它的超类(superclass?copy;。一?copy;你创建了一个象University这样的类,创建它的子类是很简单的。一个类的子类是它的继承了实例变量和成员函数的特殊的版本。在这个例子里,我们把University类派生为含有叫做country的第三个元素的子类。
classUniversityWorldextendsUniversity{Stringcountry;UniversityWorld(Stringname;Stringcity;Stringcountry){this。name=name;this。city=city;this。country=country;}UniversityWorld(){this(”北?copy;大学”;”北?copy;”;”中国”);}}
关键词extends用来表示我们要创建University的子类。name和city不需再在UniversityWorld中进行声明,因为它们是从University中继承的。Java允许在UniversityWorld中声明变量name和city,但这会隐藏University中的name和city,是与使用子类的目的相矛盾的,应当避免。在UniversityWorld的实例中name、city和country的地位是一样的。
5。11super在UniversityWorld的例子里,有一段代码和它的超类University的重复,这段代码是初始化name和city的,this。name=name;this。city=city;就象在University例子中用this指向第一个构造函数一样,在Java里有另一个变量叫做super,它直接指向超类的构造函数。下面这个例子用super来初始化变量name和city,然后打印出这个对象的内容。
classUniversityWorldextendsUniversity{Stringcountry;UniversityWorld(Stringname;Stringcity;Stringcountry){super(name;city);//调用了构造函数University(name;city)this。country=country;}publicstaticvoidmain(Stringargs''){UniversityWorldu=newUniversityWorld(”北?copy;大学”;”北?copy;”;”中国”);System。out。println(”大学:”+u。name+”城市:”+u。city+”国家:”+u。country);}}
下面是运行结果。C:》javaUniversityWorld大学:北?copy;大学城市:北?copy;国
家:中国
5。12成员函数的覆盖
这个University的新的子类继承了它的超类的成员函数samecity。但这个成员函数samecity判断的是两个城市的名字,这是不够的,因为有可能两个两个名字一样的城市属于不同的国家,我们要用同时判断城市和国家的成员函数来覆盖它。下面就是实现覆盖的例子。
classUniversity{Stringname;city;University(Stringname;Stringcity)
{this。name=name;this。city=city;}booleansamecity(Stringcity){if
(city。equals(this。city))returntrue;elsereturnfalse;}boolean
samecity(Universityu){returnsamecity(u。city);}}
classUniversityWorldextendsUniversity{Stringcountry;
UniversityWorld(Stringname;Stringcity;Stringcountry){super(name;
city);this。country=country;}booleansamecity(Stringcity;String
country){if(city。equals(u。city)&&country。equals(u。country))return
true;elsereturnfalse;}booleansamecity(UniversityWorldother)
{returndistance(other。city;other。country);}}
classUniversityWorldCity{publicstaticvoidmain(Stringargs'')
{Stringcity=”上海”;Stringcountry=”中国”;UniversityWorldu1=
newUniversityWorld(”北?copy;大学”;”北?copy;”;”中国”);
UniversityWorldu2=newUniversityWorld(”清华大学”;”北?copy;”;”
中国”);System。out。println(”u1=”+u1。name+”;”+u1。city+”;”+
u1。country);System。out。println(”u2=”+u2。name+”;”+u2。city+”;
”+u2。country);System。out。println(”city=”+city+”;country=”+
country);System。out。println(”u1。samecity(u2)=”+u1。samecity(u2));
System。out。println(”u1。samecity(city;country)=”+u1。samecity(city;
country));}}
下面是输出结果。
C:》javaUniversityWorldCityu1=北?copy;大学;北?copy;;中国u2=清华大学;北?copy;;中国city=上海;country=中国u1。samecity(u2)=trueu1。samecity(city;country)=false
5。13动态成员函数发送
当你用点操作符调用一个对象实例的成员函数时,对象实例所属的类在编译时要被检查,以确保调用的成员函数在该类中是存在的。在运行时,对象实例可以指向所声明类型的子类的实例。在这?copy;情况下,如果子类覆盖了要调用的成员函数,Java就用实例来决定调用哪一个成员函数。如下面的例子,两个类是子类和超类的关系,子类覆盖了超类的成员函数。
classA{voidcallme(){System。out。println(”在A的callme成员函数里”);}}
classBextendsA{voidcallme(){System。out。println(”在B的callme成员函数里”);}}
classDispatch{publicstaticvoidmain(Stringargs''){Aa=newB();a。callme();}}
有趣的是,在成员函数main里,我们把变量a声明为类型A,然后把类B的一个实例存放到它上面。我们在a上调用成员函数callme,Java编译器确定在类A确实有成员函数callme,但是在运行时,由于a事实上是B的实例,所以调用B的callme,而不调用A的。下面是运行结果:C:》javaDispatch在B的callme成员函数里
5。14final
在缺省情况下,所有的成员函数和实例变量都可以被覆盖。如果你希望你的变量或成员函数不再被子类覆盖,可以把它们声明为final。这意味着将来的实例都依赖这个定义。例如:finalintFILE_NEW=1;finalintFILE_OPEN=2;finalintFILE_SAVE=3;fianlintFILE_SAVEAS=4;finalintFILE_QUIT=5;final变量用大写标识符是一个一般的约定。
5。15静态
如果你想要创建一个可以在实例的外部调用的成员函数,那么你只需声明它为静态的(static?copy;,它就会正常运行。静态成员函数只能直接调用其他静态成员函数,而不能以任何方式使用this或super。你也可以把变量声明为静态的。如果你想初始化一个静态变量,你可以用static声明一个恰好在类调用时执行一次的程序块。下面的例子是一个带有一个静态成员函数,几个静态变
量,和一个静态初始块的类。
classStatic{staticinta=3;staticintb;staticvoidmethod(intx){System。out。println(”x=”+x);System。out。println(”a=”+a);System。out。println(”b=”+b);}static{System。out。println(”静态初始块”);b=a*4;}publicstaticvoidmain(Stringargs''){method(42);}}这个类被调用,所有的静态变量都被初始化,a被赋为3,然后运行static块,这将打印出一段消息,并且把b赋为a*4,即12。然后解释器调用main成员函数,它调用了成员函数method,参数x为42。这三个println语句打印了两个静态变量a、b和局部变量x。下面是运行结果:C:》javaStatic静态初始块x=42a=3b=12一个静态成员函数可以通过它所属的类名来调用。象调用实例变量一样,你可以用点操作符通过类名来调用静态成员函数和静态变量。Java就是这样实现了全局函数和全局变量。下面的例子里,我们创建了带有一个静态成员函数和两个静态变量的类。第二个类可以通过名字直接来调用第一个类的静态成员函数和静态变量。
classstaticClass{staticinta=42;staticintb=99;staticvoid
callme(){System。out。println(”a=”+a);}}
classStaticByName{publicstaticvoidmain(Stringargs'')
{StaticClass。callme();System。out。println(”b=”+staticClass。b);}}
下面是运行结果:C:》javastaticByNamea=42b=99
5。16抽象