本文是《零基础学Java》专栏的第六篇文章。文章以通俗易懂的文字、图表和代码实战,带你从零基础走向高薪之路!本文首发于公众号【编程指南】继承创建一个Person类我们创建一个类来描述一个人。我们如何抽象一个人的类别?我们从不同的角度进行抽象,得到的属性和行为都会有些不同。在这里,我们主要从人的社会属性上进行抽象。为了表示性别,我们首先设置一个枚举类型,它有两个值,分别用来表示男性和女性。代码如下:publicenumSex{male,female}上面的代码写成一个单独的源码在文件:Sex.java中,因为上面的枚举类型需要公开使用,所以也需要定义为public,所以必须放在一个独立的源文件中。然后定义类Person,代码如下:publicclassPerson{privateStringname;//姓名privateSexsex;//性别privateDatebirthday;//出生日期privateStringID;//身份证号码//下面是对上述私有成员变量的一系列setter和getterpublicvoidsetName(Stringname){this.name=name;}publicStringgetName(){返回名称;}publicvoidsetSex(Sexsex){this.sex=sex;}publicSexgetSex(){返回性别;}publicvoidsetBirthday(Datedate){birthday=date;}publicDategetBirthday(){返回生日;}publicvoidsetID(StringID){this.ID=ID;}publicStringgetID(){返回ID;}publicPerson(Stringname,Sexsex,Datebirthday,StringID){this.name=name;this.sex=性别;this.birthday=生日;这个.ID=ID;}}我们编译上面的Person.java,它涉及到的相关类也会一起编译,比如:Sex.java文件。定义学生子类如果考虑一个班级的构成,大致分为人员、学生、教师、辅导员三类,依次定义。这三个类都具有人所具有的属性,所以我们不需要重新定义三个类中那些共同的属性,只需要继承Person类即可。当然,这些阶级还必须具有不同于常人的属性和行为。首先定义学生类。publicclassStudentextendsPerson{privateStringstudentID;//学号privateDaterollInDate;//入学时间publicvoidsetStudentID(StringstudentID){this.studentID=studentID;}publicStringgetStudentID(){返回学生ID;}publicvoidsetRollInDate(DaterollInDate){this.rollInDate=rollInDate;}publicDategetRollInDate(){返回rollInDate;}publicStudent(Stringname,Sexsex,Datebirthday,StringID,StringstudentID,DaterollInDate){super(name,sex,birthday,ID);//调用父类的构造函数//虽然Student可以继承父类的私有成员,//但是不能像使用本类中的成员变量那样直接调用,//因此,下一句不能调用//this.name=name;this.studentID=studentID;//这个类的成员变量,当然可以这样写。rollInDate=rollInDate;}}Student类继承了Person的所有成员,包括私有的,但是子类虽然有继承自父类的成员,但是不能直接使用。子类可以直接使用的继承成员是public和protected的。对于没有修饰符的成员变量,如果子类与父类不在同一个包中,则不能使用。一个例子如下:classA{privateintx=0;受保护的y=1;公共intz=2;诠释米=3;A(){System.out.println("在父类中");}A(Strings){System.out.println("在父类中"+s);}}publicclassBextendsA{//在这里添加intx;会发生什么?B(){System.out.println("在子类中");}B(Strings){超级(s);System.out.println("在子类中"+s);}publicstaticvoidmain(Strings[]){Bb=newB("对不对?");System.out.println(b.x);//否,System.out.println(b.y);System.out.println(b.z);System.out.println(b.m);}}继承有如下特点:一个子类只能继承一个父类,它继承了父类的所有成员,甚至包括父类中被private修饰的成员变量或方法。子类中可以定义新的成员变量和成员方法,也可以重新定义父类中的成员变量和成员方法。这种方法的重新定义称为覆盖。函数,而这些函数在子类中没有被重写,那么父类的函数变化会影响到子类。超类具有从它继承的所有子类的共同特征和行为。继承最基本的作用:代码复用。继承最重要的作用:方法可以被复制。super的使用我们使用了supper(name,sex,birthday,ID);在Student的构造函数中;这是对父类构造函数的调用。对于有继承关系的类,子类的构造函数总会调用父类的构造函数:如果没有显式调用,系统会自动调用父类的无参构造函数,但一旦显式调用,系统将不再调用父类中的无参构造函数。A类{A(){System.out.println("在A()");}}B类扩展A{B(){System.out.println("在B()");}}classCextendsB{C(){System.out.println("inC()");}}classTestExtends{publicstaticvoidmain(String[]args){Cc=newC();}}super的使用与此类似,但本质不同。这是一个参考,但super不是。super不指向父类对象。它表示当前子类对象中继承自父类的属性和行为。如图:什么时候用super?当子类和父类中有同名的成员时,比如子类和父类都有属性名。如果要访问子类中继承自父类的对象名属性,需要用super来区分。super可以用在什么地方?第一:super,像这样,可以用在非静态方法中,但是不能用在静态方法中。第二:super可以用在构造函数中。如果一个构造方法的第一行没有this(...);并且没有显式调用super(...);,系统会默认调用super()。因此,如果父类中没有默认的构造方法,就会报错(如果把上面代码中的super注释掉,测试一下);注意:super(...)的调用;只能放在构造方法的第一行。因此,super(....)和this(....)不能共存。极好的(...);只是调用父类中的构造函数,并没有创建父类对象。覆盖是指子类重新定义了父类中同名的方法。什么时候应该覆盖方法?如果父类中的方法已经不能满足当前子类的业务需求,就需要重新定义父类中方法的功能。如果子类重写了父类中的方法,则子类对象必须调用被重写的方法。方法覆盖的条件:发生在有继承关系的两个类之间。重写发生在继承关系中,必须具有相同的方法名、相同的返回值类型和相同的参数列表。被覆盖的方法不能比被覆盖的方法减少访问权限。被覆盖方法抛出的异常必须与被覆盖方法或其子类抛出的异常一致;私有方法不能被重写,但是子类中可以定义与父类同??名的方法(但不叫重写)构造函数不能被重写。因为子类的构造方法和父类的构造方法不一样。覆盖是指成员方法,与成员变量无关。A类{私人intx=0;受保护的y=1;公共intz=2;诠释米=3;voidt1(){System.out.println("在父类t1");}}公共类B扩展A{voidt1(){super.t1();System.out.println("在子类t1");}publicstaticvoidmain(Strings[]){Bb=newB();b.t1();System.out.println(b.m);}}final关键字final关键字可以出现在类名、方法、局部变量、成员变量、形参之前。它有以下作用:final修饰的类不能被继承。最终修改的方法不能被覆盖。不能更改最终修改的局部变量。一旦赋值,final-modified形参就不能改变,形参的值在方法体中也不能改变。final修饰的成员变量必须手动初始化。它的默认值不能取,一般与static结合使用,如:publicstaticfinalinti=100;final修饰的引用类型,引用不能重定向到其他java对象。但是,最终修改引用所指向的对象的属性是可以修改的,一定不能混淆。定义教师子类教师类除了其作为人的特征外,更多的是教师教育事务的特征和功能。因此,我们首先定义一个描述课程的类:Course,如下:publicclassCourse{privateStringcourseName;私人课程编号;公共课程(intcourseId,StringcourseName){this.courseId=courseId;this.courseName=courseName;}publicvoidsetCourseName(StringcourseName){this.courseName=courseName;}publicStringgetCourseName(){返回课程名称;}publicvoidsetCourseId(intcourseId){this.courseId=courseId;}//toString方法继承自Object类publicStringtoString(){System.out.println("课程ID:"+courseId+"\n课程名称:"+courseName);}}我们定义教师类:Teacher如下://教授publicvoidsetTeacherID(StringstudentID){this.teacherID=teacherID;}publicStringgetTeacherID(){返回t每个人的身份;}publicvoidsetRollInDate(DaterollInDate){this.rollInDate=rollInDate;}publicDategetRollInDate(){返回rollInDate;}publicvoidsetTeachingCourse(Course[]teachingCourse){this.teachingCourse]getTeachingCourse(){returnteachingCourse;}publicTeacher(Stringname,Sexsex,Datebirthday,StringID,StringteacherID,DaterollInDate,Course[]teachingCourse){super(name,sex,birthday,ID);//为父类构造的调用method//Student虽然可以继承父类的私有成员,//但是不能像使用本类中的成员变量那样直接调用,//所以下一句不能调用//this.名字=名字;this.teacherID=teacherID;//这个类的成员变量,当然可以这样写this.rollInDate=rollInDate;this.teachingCourse=teachingCourse;在功能的基础上还增加了一些管理功能,所以辅导员类要继承老师类,代码如下:publicclassCounselorextendsTeacher{}Transformation(cast)对于上面定义的类,我们可以说:astudentisaperson,ateacherisaperson,acounselorisateacher,acounselorisaperson从这些陈述中,我们可见,子类对象的类型可以看成是父类的类型。因此,下面的语句是合法的:Personp=newStudent();这种情况,我们称之为upcast,有点类似于基本类型中的自动类型转换。有时,我们还需要将某个父类对象作为子类对象使用。我们不鼓励这种使用,因为它是不自然的,就像我们不能说:一个人是一个顾问,如果我们必须这样做,我们必须使用像原始类型的转换这样的转换,我们称之为向下转换,如在下面的例子中:Teacherteacher=newTeacher();Counselor辅导员=(Counsellor)老师;在上面的例子中,我们使用了默认构造函数,但是我们并没有定义相关类的默认构造函数。我们只是为了说明转换的概念。为了使上面的例子合法,要么使用带参数的构造函数,要么修改之前定义的相关类。转换示例:classT1{inti=1;}classT2extendsT1{inti=2;}publicclassTUpcast{publicstaticvoidmain(String[]args){T2t2=newT2();系统输出。println("t2.i="+t2.i+"upcastt2.i="+((T1)t2).i);//这里说明子类可以重新定义父类中的成员变量,父类中的同名变量可以隐藏,也就是说是不同的变量}}定义a的基本组成班级里,有几个学生,几个老师,还有一个辅导员。我们定义类class如下:publicclassClasses{publicpublicintclassId;公共学生[]学生;公共教师[]教师;公共顾问顾问;publicClasses(intclassId,StringclassName,Student[]students,Teacher[]teachers,Counselorcounselor){这个。班级号;this.className=className;this.students=学生;this.teachers=教师;this.counsellor=顾问;}}一旦一个类被创建和测试,它有一些有用的功能,我们就可以创建这个类的对象,通过它来使用定义的功能。此外,我们还可以利用类的继承机制进行复用,或者将对象放入一个新的类中,称为成员对象。这个新类由其他类对象组成,称为组合(composition)。例如,上面的Classes类是由现有的类成员组成的。多态我们用下面的例子来解释什么是多态,如下:}voidchasingMouse(){System.out.println("追逐追逐太好玩了!");}}classRabbitextendsAnimal{voideat(){System.out.println("萝卜比鱼好吃!");}voidrun(){System.out.println("我跑得比乌龟还快!");}}classTest{publicstaticvoidcallEat(Animalanimal){animal.eat();}publicstaticvoidmain(String[]args){Catcat=newCat();兔子rabbit=newRabbit();//下面两个calEat方法在执行时,会根据eatin传入的实参类型选择执行不同的类型//这种情况就是多态的callEat(cat);callEat(兔子);//animal变量存放的是其子类的对象,执行子类对象中的eat方法Animalanimal=cat;动物.吃();动物=兔子;动物.吃();//animal.chasingMouse();((兔子)动物).run();//原始animal指的是Rabbit对象,downcast是可以的((Cat)animal).chasingMouse();//原始animal指的是Rabbit,rabbit不能转成cat}}多态性通过重载、重写、upcast来体现。使用多态性可以降低代码之间的耦合度。增强了项目的可扩展性。
