本篇文章是《零基础学Java》专栏的第六篇文章,文章采用通俗易懂的文字、图示及代码实战,从零基础开始带大家走上高薪之路!

本文章首发于公众号【编程攻略】

继承

创建一个Person类

我们创建一个用于描述的类。我们怎么抽象出一个这个类呢?我们以不同的角度做抽象,得到的属性和行为都会有些区别。这里,我们主要从的社会属性来抽象。为了表示性别,我们先顶一个枚举类型,该枚举类型中有两个值,用于表示,代码如下:

public enum Sex {male,female}

上面的代码写入单独的一个源代码文件中:Sex.java,因为上面的枚举类型是需要公开使用的,所以,也需要定义为public,因此,必须将之放在独立的源文件中 。

再定义Person这个类,代码如下:

public class Person{
private String name;//姓名
private Sex sex;//性别
private Date birthday;//出生日期
private String ID;//身份证号 //下面是一系列上述私有成员变量的setter和getter
public void setName(String name){
this.name = name;
} public String getName(){
return name;
} public void setSex(Sex sex){
this.sex = sex;
} public Sex getSex(){
return sex;
} public void setBirthday(Date date){
birthday = date;
} public Date getBirthday(){
return birthday;
} public void setID(String ID){
this.ID = ID;
} public String getID(){
return ID;
} public Person(String name, Sex sex, Date birthday, String ID){
this.name = name;
this.sex = sex;
this.birthday = birthday;
this.ID = ID;
}
}

我们编译上面的Person.java,它所涉及到的相关的类也会一并编译完成,如:Sex.java文件。

定义学生子类

如果我们考虑一个班级的组成,大致有三种类别的人员,学生教师辅导员,我们依次进行定义。这三类都具有所具有的属性,因此,我们没必要在三个类中重新定义那些公共属性,我们只需继承Person类即可。当然,这些类一定也具有不同于普通的属性和行为。先定义学生类。

public class Student extends Person {
private String studentID;//学号
private Date rollInDate;//入学时间 public void setStudentID(String studentID){
this.studentID = studentID;
} public String getStudentID(){
return studentID;
} public void setRollInDate(Date rollInDate){
this.rollInDate = rollInDate;
} public Date getRollInDate(){
return rollInDate;
} public Student(String name,
Sex sex,
Date birthday,
String ID,
String studentID,
Date rollInDate){
super(name,sex,birthday,ID);//对父类的构造方法的调用
//虽然Student能够继承父类中的私有成员,
//但是却不能直接像使用本类中的成员变量一样直接调用,
//因此,下句是不能调用的
//this.name = name;
this.studentID = studentID; //本类的成员变量,当然可以这么写
this.rollInDate = rollInDate;
}
}

Student类继承了Person中所有的成员,包括私有的,但是对于子类虽然拥有继承自父类的成员,却不能直接使用。子类可以直接使用的继承来的成员有public和protected修饰的,对于无修饰符的成员变量,如果子类同父类不在同一个包,也是不能使用的。示例如下:

class A{
private int x = 0;
protected int y = 1;
public int z = 2;
int m = 3; A(){
System.out.println("在父类里");
} A(String s){
System.out.println("在父类里 "+s);
}
} public class B extends A{
//在此处加上int x;会怎么样呢?
B(){
System.out.println("在子类里");
}
B(String s){
super(s);
System.out.println("在子类里 "+s); }
public static void main(String s[]){
B b=new B("对吗?");
System.out.println(b.x);//不对哦
System.out.println(b.y);
System.out.println(b.z);
System.out.println(b.m);
}
}

继承具有以下特征:

  • 子类只能继承自一个父类,它继承父类中的所有成员,甚至包括父类中private所修饰的成员变量或方法。子类中可以定义新的成员变量和成员方法,也可以对父类中的成员变量及成员方法进行重新定义,这种对方法的重新定义称为复写(override)

  • 如果改变了父类中某些功能,而这些功能在子类中未进行复写,那么修改父类的功能改变会影响到子类。

  • 父类具有所有继承自它的子类的共同的特征和行为。

继承最基本的作用:代码重用。 继承最重要的作用:方法可以复写。

super的使用

我们在Student的构造方法中使用了supper(name,sex,birthday,ID);,这是对父类的构造方法的调用。对于继承关系的类,子类的构造方法总是会调用父类的构造方法:如果没有显式调用,系统会自动调用父类的无参构造方法,但是一旦显式调用,系统就不会再调用父类中的无参构造方法。

class A{
A(){
System.out.println("in A()");
}
} class B extends A{
B(){
System.out.println("in B()");
}
} class C extends B{
C(){
System.out.println("in C()");
}
} class TestExtends{
public static void main(String[] args){
C c=new C();
}
}

super的使用同this相似,但是它们两者的本质是不同的,this是引用,而super不是,super指向的不是父类对象,它代表当前子类对象中继承自父类的属性和行为。如图:

什么时候使用super呢?当子类和父类中具有同名的成员时,例如,子类和父类中都有name这个属性,如果要在子类对象中访问继承自父类中的name属性,就需要使用 super进行区分。

super可以用在什么地方?

  • 第一:super和this一样可以用在非静态方法中,不能用在静态方法中。
  • 第二:super可以用在构造方法中。一个构造方法第一行如果没有this(...);,也没有显式的去调用super(...);,系统会默认调用super(),因此,如果父类中没有缺省构造方法,则会出错(大家如果注释掉上面代码中的super,测试一下);
  • 注意:super(...);的调用只能放在构造方法的第一行。因此,super(....)this(....)不能共存。super(...);只是调用了父类中的构造方法,并不会创建父类对象。

复写(override)

复写指子类重定义了父类中的同名方法。什么时候方法要进行复写?如果父类中的方法已经无法满足当前子类的业务需求,需要将父类中的方法功能进行重新进行定义。子类如果复写父类中的方法之后,子类对象一定调用的是复写之后的方法。

发生方法复写的条件:

  1. 发生在具有继承关系的两个类之间
  2. 复写发生在继承关系中,必须具有相同的方法名,相同的返回值类型,相同的参数列表
  3. 复写后的方法不能比被复写的方法拥有缩小的访问权限。
  4. 复写后的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
  5. 私有的方法不能被复写,但可以在子类定义和父类中同名的方法(但不称之为复写)
  6. 构造方法无法被复写。因为子类同父类的构造方法不同。
  7. 复写指的是成员方法,和成员变量无关。
class A{
private int x=0;
protected int y=1;
public int z=2;
int m=3; void t1(){
System.out.println("在父类t1里");
}
} public class B extends A{
void t1(){
super.t1();
System.out.println("在子类t1里");
}
public static void main(String s[]){
B b=new B();
b.t1();
System.out.println(b.m);
}
}

final关键字

final关键字可以出现在类名前、方法前、局部变量前、成员变量前、形参前,它具有如下作用:

  • final修饰的类无法被继承
  • final修饰的方法无法被复写
  • final修饰的局部变量,一旦赋值,不可再改变
  • final修饰形参,形参的值在方法体中也无法改变
  • final修饰的成员变量必须手动初始化,不能取它的默认值,一般和static联用,如:
public static final int i = 100;
  • final修饰的引用类型,该引用不可再重新指向其他的java对象。但是final修饰的引用指向的对象的属性是可以修改的,切不可混为一谈。

定义教师子类

教师类除了它具有作为所具有的特征之外,更多的是教师的教务特征和功能,由此,我们先定义一个描述课程的类:Course,如下:

public class Course{
private String courseName;
private int courseId; public Course(int courseId, String courseName){
this.courseId = courseId;
this.courseName = courseName;
} public void setCourseName(String courseName){
this.courseName = courseName;
} public String getCourseName(){
return courseName;
} public void setCourseId(int courseId){
this.courseId = courseId;
} //toString方法继承自Object类
public String toString(){
System.out.println("课程编号:" + courseId + "\n课程名称:" + courseName);
}
}

我们定义教师类:Teacher如下:

public class Teacher extends Person {
private String teacherID;//工号
private Date rollInDate;//入职时间
private Course[] teachingCourse; //教授的课程 public void setTeacherID(String studentID){
this.teacherID = teacherID;
} public String getTeacherID(){
return teacherID;
} public void setRollInDate(Date rollInDate){
this.rollInDate = rollInDate;
} public Date getRollInDate(){
return rollInDate;
} public void setTeachingCourse(Course[] teachingCourse){
this.teachingCourse = teachingCourse;
} public Course[] getTeachingCourse(){
return teachingCourse;
} public Teacher(String name,
Sex sex,
Date birthday,
String ID,
String teacherID,
Date rollInDate,
Course[] teachingCourse){
super(name,sex,birthday,ID);//对父类的构造方法的调用
//虽然Student能够继承父类中的私有成员,
//但是却不能直接像使用本类中的成员变量一样直接调用,
//因此,下句是不能调用的
//this.name = name;
this.teacherID = teacherID; //本类的成员变量,当然可以这么写
this.rollInDate = rollInDate;
this.teachingCourse = teachingCourse;
}
}

定义辅导员

辅导员是教师,但辅导员在教师的功能基础上又多了一些管理功能,所以辅导员类应该继承自教师类,代码如下:

public class Counsellor extends Teacher{

}

转型(cast)

对于上面所定义的类,我们可以说:a student is a persona teacher is a persona counsellor is a teachera counsellor is a person。从这些说法,我们可以看出来一个子类对象的类型是可以看作父类类型的。因此,下面的语句是合法的:

Person p = new Student();

这种情况,我们称之为向上转型(upcast),这有点类似于基本类型中的自动类型转换。

有时候,我们也需要把某个父类对象当作一个子类对象使用,我们不鼓励这么用,因为这是不自然的,就像我们不能说:a person is a counsellor一样,如果非要这么做,我们必须像基本类型的强制类型转换一样使用强制转换,我们称之为向下转型(downcast),如下例:

Teacher teacher = new Teacher();
Counsellor counsellor = (Counsellor)teacher;

上例中,我们使用了缺省构造方法,但是,我们并没有定义相关类的缺省构造方法,我们只是为了说明转型这个概念,大家为了使得上例合法,要么使用带参的构造方法,要么,修正前面定义的相关类。

转型示例:

class T1{
int i = 1;
} class T2 extends T1{
int i = 2;
} public class TUpcast{
public static void main(String[] args){
T2 t2 = new T2();
System.out.println("t2.i=" + t2.i +"upcast t2.i=" + ((T1)t2).i);
//这说明子类中可以重新定义父类中的成员变量,同名的可以隐藏父类中的同名变量,也就是说他们是不同的变量
}
}

定义班级

一个班级的基本组成,有若干学生、若干教师、一个辅导员,我们定义班级类如下:

public class Classes{
public String className;
public int classId;
public Student[] students;
public Teacher[] teachers;
public Counsellor counsellor; public Classes(int classId,
String className,
Student[] students,
Teacher[] teachers,
Counsellor counsellor){
this.classId = classId;
this.className = className;
this.students = students;
this.teachers = teachers;
this.counsellor = counsellor;
}
}

一旦某个类创建及测试后,它便具有了某种有用的功能,我们可以创建该类的对象,通过该对象,使用定义好的功能。除此之外,我们还可以通过使用类的继承机制进行复用,也可以把对象放入一个新的类中,称为成员对象,这种新类由其它类对象组成的情况,称为组合(composition)。如上面的Classes类就是由已有的类成员组合而成。

多态

我们由下面的例子来解释什么是多态,如下:

class Animal{
void eat(){ }
} class Cat extends Animal{
void eat(){
System.out.println("鱼好吃!");
} void chasingMouse(){
System.out.println("追啊追,追的好开心!");
}
} class Rabbit extends Animal{
void eat(){
System.out.println("萝卜比鱼好吃!");
} void run(){
System.out.println("我比乌龟跑得快!");
}
} class Test{ public static void callEat(Animal animal){
animal.eat();
} public static void main(String[] args) {
Cat cat = new Cat();
Rabbit rabbit = new Rabbit(); //下面的两个calEat方法在执行的时候,会根据传入的实参类型,选择执行不同类中的eat
//这种情况就是多态
callEat(cat);
callEat(rabbit); //animal变量存放的为其子类的对象,执行子类对象中的eat方法
Animal animal = cat;
animal.eat(); animal = rabbit;
animal.eat();
//animal.chasingMouse();
((Rabbit)animal).run(); //原本animal引用的就是Rabbit对象,downcast是可以的
((Cat)animal).chasingMouse();//原本animal引用的是Rabbit,rabbit是不能转换为cat的 }
}

多态性是通过重载和复写及upcast来体现的。使用多态可以使代码之间的耦合度降低。项目的扩展能力增强。

零基础学Java第六节(面向对象二)的更多相关文章

  1. 零基础学Java第五节(面向对象一)

    本篇文章是<零基础学Java>专栏的第五篇文章,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! 本文章首发于公众号[编程攻略] 类与对象 在哲学体系中,可以分为主 ...

  2. 零基础学Java第四节(字符串相关类)

    本篇文章是<零基础学Java>专栏的第四篇文章,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! String 本文章首发于公众号[编程攻略] 在Java中,我们经 ...

  3. 零基础学Java(10)面向对象-使用LocalDate类完成日历设计

    前言 在我们完成这个日历设计前,需要了解Java中的预定义类LocalDate的一些用法 语法 LocalDate.now() // 2022-07-01 会构造一个新对象,表示构造这个对象时的日期. ...

  4. 零基础学Java第二节(运算符、输入、选择流程控制)

    本篇文章是<零基础学Java>专栏的第二篇文章,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! 第一章 运算符 1.1 算术运算符的概述和用法 运算符 对常量和变 ...

  5. 零基础学Java第一节(语法格式、数据类型)

    本篇文章是<零基础学Java>专栏的第一篇文章,从本篇文章开始,将会连更本专栏,带领大家将Java基础知识彻底学懂,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! ...

  6. 零基础学Java,PayPal技术专家手把手带你入门

    在最权威的 TIOBE 编程语言排名榜单上,Java 常年稳居第一,可以说是世界上应用最为广泛的一门语言. 同时,在微服务.云计算.大数据.Android App 开发等领域,Java 也是当之无愧的 ...

  7. 零基础学Java第三节(基本输入输出)

    本篇文章是<零基础学Java>专栏的第三篇文章,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! 本文章首发于公众号[编程攻略] Java程序的命令行参数 我们可以 ...

  8. 零基础学Java(1)初识Java程序

    前言 就国内来说,Java毫无疑问是后端语言中的No.1没有之一,所以今天我们也来0基础学习Java!!! Java的好处(针对测试工程师) 面试加分->涨薪 大多数公司服务端用的都是Java, ...

  9. 零基础学Java之Java学习笔记(一):Java概述

    什么是Java? Java是一门面向对象编程语言,可以编写桌面应用程序.Web应用程序.分布式系统和嵌入式系统应用程序. Java特点有哪些? 1.Java语言吸收了C++语言的各种优点,具有功能强大 ...

随机推荐

  1. numpy计算数组中满足条件的个数

    Numpy计算数组中满足条件元素个数 需求:有一个非常大的数组比如1亿个数字,求出里面数字小于5000的数字数目 1. 使用numpy的random模块生成1亿个数字 2. 使用Python原生语法实 ...

  2. vim recording的使用方法

    使用vim时无意间触碰到q键,左下角出现"recording"这个标识,觉得好奇,遂在网上查了一下,然后这是vim的一个强大功能.他可以录 制一个宏(Macro),在开始记录后,会 ...

  3. “一键”生成HTML——Emmet插件常用语法

    Emmet是一款文本编辑器/IDE的插件,用来快速生成复杂的HTML代码,只要掌握一些常用的语法(类似于CSS选择器),就可以减少重复编码的工作(主要是懒).我个人惯用的是sublime,因此下文介绍 ...

  4. (SSM框架)实现小程序图片上传(配小程序源码)

    阅读本文约"2分钟" 又是一个开源小组件啦! 因为刚好做到这个小功能,所以就整理了一下,针对微信小程序的图片(文件)上传! 原业务是针对用户反馈的图片上传.(没错,本次还提供小程序 ...

  5. 小程序图片轮播特效swiper(纯手打)

    前言 一个月前还是用vue做微信H5,后面公司业务发展,入坑小程序,做了几款小程,跑了不少坑, 也会陆续在后面几节跟大家分享. 在这节给大家分享这个 小程序图片轮播实现方案 初步的实现思路 我要实现的 ...

  6. 解决使用 swiper 常见的问题

    使用 swiper 的过程中个人总结 1. swiper插件使用方法, 直接查看文档 swiper基础演示 swiper API文档 2.swiper近视初始化时, 其父级元素处于隐藏状态(displ ...

  7. 前端面试题整理——HTML/CSS

    如何理解语义化: 对应的内容是用相应意思的标签,增加开发者和机器爬虫对代码的可读性. 块状元素和内联元素: 块状元素有:display:block/table:有div h1 h2 table ul  ...

  8. Taro开发微信小程序遇到的问题和解决方法

    1.scroll-view 置顶, 给设置scroll-top为0无效问题? 解决方案: 不触发置顶问题,需要给scroll-top一个设置接近0的随机数,Math.random() 2.scroll ...

  9. c++对c的拓展_命名空间_简单使用

    名字的控制:c可使用static关键字使该关键字在本单元内可见,c++则使用命名空间对名字的可见性及产生进行控制 命名空间:控制标识符的作用域(本质上就是一个作用域) 使用特点:1.必须定义在全局范围 ...

  10. win10设置开机自启动程序

    问题情境:前两天刚刚给自己的win10系统美化了一下,但发现一个问题,每次开机都需要双击启动一个程序,才能达到一个我想要的效果,所以就在思考能不能将这个程序设为开机自启动项呢? 1.首先,找到启动文件 ...