零基础学Java第六节(面向对象二)
本篇文章是《零基础学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)
复写指子类重定义了父类中的同名方法。什么时候方法要进行复写?如果父类中的方法已经无法满足当前子类的业务需求,需要将父类中的方法功能进行重新进行定义。子类如果复写父类中的方法之后,子类对象一定调用的是复写之后的方法。
发生方法复写的条件:
- 发生在具有继承关系的两个类之间
- 复写发生在继承关系中,必须具有
相同的方法名,相同的返回值类型,相同的参数列表
。 - 复写后的方法不能比被复写的方法拥有缩小的访问权限。
- 复写后的方法所抛出的异常必须和被覆盖方法的所抛出的异常一致,或者是其子类;
- 私有的方法不能被复写,但可以在子类定义和父类中同名的方法(但不称之为复写)
- 构造方法无法被复写。因为子类同父类的构造方法不同。
- 复写指的是成员方法,和成员变量无关。
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 person
,a teacher is a person
,a counsellor is a teacher
,a 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第六节(面向对象二)的更多相关文章
- 零基础学Java第五节(面向对象一)
本篇文章是<零基础学Java>专栏的第五篇文章,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! 本文章首发于公众号[编程攻略] 类与对象 在哲学体系中,可以分为主 ...
- 零基础学Java第四节(字符串相关类)
本篇文章是<零基础学Java>专栏的第四篇文章,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! String 本文章首发于公众号[编程攻略] 在Java中,我们经 ...
- 零基础学Java(10)面向对象-使用LocalDate类完成日历设计
前言 在我们完成这个日历设计前,需要了解Java中的预定义类LocalDate的一些用法 语法 LocalDate.now() // 2022-07-01 会构造一个新对象,表示构造这个对象时的日期. ...
- 零基础学Java第二节(运算符、输入、选择流程控制)
本篇文章是<零基础学Java>专栏的第二篇文章,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! 第一章 运算符 1.1 算术运算符的概述和用法 运算符 对常量和变 ...
- 零基础学Java第一节(语法格式、数据类型)
本篇文章是<零基础学Java>专栏的第一篇文章,从本篇文章开始,将会连更本专栏,带领大家将Java基础知识彻底学懂,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! ...
- 零基础学Java,PayPal技术专家手把手带你入门
在最权威的 TIOBE 编程语言排名榜单上,Java 常年稳居第一,可以说是世界上应用最为广泛的一门语言. 同时,在微服务.云计算.大数据.Android App 开发等领域,Java 也是当之无愧的 ...
- 零基础学Java第三节(基本输入输出)
本篇文章是<零基础学Java>专栏的第三篇文章,文章采用通俗易懂的文字.图示及代码实战,从零基础开始带大家走上高薪之路! 本文章首发于公众号[编程攻略] Java程序的命令行参数 我们可以 ...
- 零基础学Java(1)初识Java程序
前言 就国内来说,Java毫无疑问是后端语言中的No.1没有之一,所以今天我们也来0基础学习Java!!! Java的好处(针对测试工程师) 面试加分->涨薪 大多数公司服务端用的都是Java, ...
- 零基础学Java之Java学习笔记(一):Java概述
什么是Java? Java是一门面向对象编程语言,可以编写桌面应用程序.Web应用程序.分布式系统和嵌入式系统应用程序. Java特点有哪些? 1.Java语言吸收了C++语言的各种优点,具有功能强大 ...
随机推荐
- 判断1~n有多少个1
判断1~n有多少个1 例如:1~12中 1 2 3 4 5 6 7 8 9 10 11 12 有1,10,11,12中含有1,则共有5个1: 算法如下: #include<stdio.h> ...
- 浅谈JavaScript原型与原型链
对于很多前端开发者而言,JavaScript的原型实在是很让人头疼,所以我这边就整理了一下自己对应原型的一点理解,分享给大家,供交流使用 原型 说起原型,那就不得不说prototype.__proto ...
- HTML5 Canvas绘制效率如何?
js运行效率在提升 编程语言的效率是前提,js自然比不上native的C语言效率,所以Canvas效率无疑比不上原生的2D图形绘制,但是js效率的提升是有目共睹的,以js与as为例,基本操作(运算操作 ...
- vue点击按钮复制文本框内容
1.npm进行安装 npm install clipboard --save 2.在需要使用的组件中import 引用方法:import Clipboard from 'clipboard'; 3.添 ...
- python---冒泡排序的实现
冒泡排序 思想 列表中有n个数, 每两个相邻的数, 如果前边的数比后边的数大, 就交换. 关键点: 趟: 总共执行 n-1趟 无序区: 第 i 趟时, 索引 0~ n-1-i 为无序区 ...
- redis的基础命令操作
文章目录 前言 一.字符串类型 二.哈希类型 三.列表类型 四.集合类型 五.有序集合类型 六.通过命令 前言 redis的数据结构 redis存储的是key,value格式的数据,其中的key是字符 ...
- SpringBoot-总结
SpringBoot一站式开发 官网:https://spring.io/projects/spring-boot Spring Boot可以轻松创建独立的.基于Spring的生产级应用程序,它可以让 ...
- VsCode 常用插件清单
插件离线安装说明 在一些内网开发环境中,无法做到在线安装,这个时候就需要对插件进行离线安装 了 打开 VSCode 插件市场网址 Extensions for the Visual Studio fa ...
- Linux内核--链表结构(一)
一.前言 Linux内核链表结构是一种双向循环链表结构,与传统的链表结构不同,Linux内核链表结构仅包含前驱和后继指针,不包含数据域.使用链表结构,仅需在结构体成员中包含list_head*成员就行 ...
- 企业DevOps之路:Jenkins 流水线
1. Pipeline 概述 Pipeline 即流水线,是 jenkins2.X 的新特性,是 jenkins 官方推荐使用的持续集成方案.与传统的自由风格项目不同,它是通过 jenkins DSL ...