5.8构造函数(Constructor?copy;
每创建一个类的实例都去初始化它的所有变量是乏味的。如果一个对象在被创建时就完成了所有的初始工作,将是简单的和简洁的。因此,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;}booleansamecity(Universityu){returnsamecity(u.city);}}
classUniversityCity{publicstaticvoidmain(Stringargs[]){Stringcity="上海";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;}booleansamecity(Universityu){returnsamecity(u.city);}}
classUniversityWorldextendsUniversity{Stringcountry;UniversityWorld(Stringname,Stringcity,Stringcountry){super(name,city);this.country=country;}booleansamecity(Stringcity,Stringcountry){if(city.equals(u.city)&&country.equals(u.country))returntrue;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);}}
一?copy;这个类被调用,所有的静态变量都被初始化,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;staticvoidcallme(){System.out.println("a="+a);}}
classStaticByName{publicstaticvoidmain(Stringargs[]){StaticClass.callme();System.out.println("b="+staticClass.b);}}
下面是运行结果:
C:\>javastaticByNamea=42b=99
5.16抽象
有时你需要定义一个给出抽象结构、但不给出每个成员函数的完整实现的类。如果某个成员函数没有完整实现,必须要由子类来覆盖,你可把它声明为抽象(abstract?copy;型。含有抽象型成员函数的类必须声明为抽象的。为了把一个类声明为抽象的,你只需在类定义的class关键词前放置关键词abstract。这?copy;类不能直接用new操作符生成实例,因为它们的完整实现还没有定义。你不能定义抽象的构造函数或抽象的静态成员函数。抽象类的子类或者实现了它的超类的所有抽象的成员函数,或者也被声明为抽象的。下面例子是一个带有抽象成员函数的类,其后是一个实现了该成员函数的类。
abstractclassA{abstractvoidcallme();voidmetoo(){system.out.println("在A的metoo成员函数里");}}
classBextendsA{voidcallme(){System.out.println("在B的callme成员函数里");}}
classAbstract{publicstaticvoidmain(Stringargs[]){Aa=newB();a.callme();a.metoo();}}
下面是运行结果:
C:\>javaAbstract在B的callme成员函数里在A的metoo成员函数里
总结:
1.类是Java语言面向对象编程的基本元素,它定义了一个对象的结构和功能。2.Java通过在类定义的大括号里声明变量来把数据封装在一个类里,这里的变量称为实例变量。3.成员函数,是类的功能接口,是类定义里的一个子程序,在类的定义里和实例变量处于同一级别。 |
|