目录
一、课程概述
二、继承的应用
2-1 什么是多态
2-2 继承基本语法和使用场景
三、final和super关键字
3-1 final关键字
3-2 super关键字
四、方法重载与重写
4-1 方法重载和方法重写
重载
五、接口和抽象类
5-1 抽象类和抽象方法
5-2 抽象类银行存款
5-3 接口基本语法操作
5-4 接口的应用
六、多态
6-1 多态概述
6-2 多态操作案例
本课程主要学习Java中的重要概念继承和多态,通过案例的方式进行讲解,让你对继承和多态在企业中的应用更加了解。
Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程.
Java的继承和多态
前面我们了解了课程大纲,给大家介绍一下关于动态的概念,首先我们要认识到所有的软件都是为了解决生活中的问题而存在的,解决生活中的问题,首先要做到的是还原我们生活中的场景,而在我们生活中就存在着各种动态变化的事物以及操作行为,这些变化关系反映到程序代码中,我们就称呼为多态,多态呢,他能解决一些看上去相对比较复杂的关系以及流程业务,主要的通过两个方面来体现,第1个方面呢就是程序运行过程中,对象角色的一个切换,另外一个过程中,对象行为的一个扩展变化
- 多态性:多态性是指允许不同子类型的对象对同一消息作出不同的响应。简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。
- 多态:程序运行过程中,对象角色的切换:如老师,在家是丈夫或妻子,在校是老师
- 程序运行过程中,对象行为的扩展:如孵蛋器:把鸡蛋/鸽子蛋->孵出小鸡和鸽子
1.继承
2.重写
3.父类引用指向子类对象。
final变量有成员变量或者是本地变量(方法内的局部变量),在类成员中final经常和static一起使用,作为类常量使用。其中类常量必须在声明时初始化,final成员常量可以在构造函数初始化。
- final成员变量必须在声明的时候初始化或者在构造器中初始化,否则就会报编译错误。final变量一旦被初始化后不能再次赋值。
- 本地变量必须在声明时赋值。 因为没有初始化的过程
- 在匿名类中所有变量都必须是final变量。
- final方法不能被重写, final类不能被继承
- 接口中声明的所有变量本身是final的。类似于匿名类
- final和abstract这两个关键字是反相关的,final类就不可能是abstract的。
- final方法在编译阶段绑定,称为静态绑定(static binding)。
- 将类、方法、变量声明为final能够提高性能,这样JVM就有机会进行估计,然后优化。
代码
public class Father { //父类
public Integer regNo; //定义一个Integer类型的变量
public String name; //定义一个字符串
public String gender; //定义一个字符串
public Integer age; //定义一个Integer类型的变量
public String job; //定义一个字符串
public void working(){ //定义一个方法
System.out.println(this.name+"在工作中...."); //输出信息李白在工作中....
}
}
public class Son extends Father { //子类继承父类
public void playGame(){//定义一个方法
System.out.println(this.name+"在游戏中....");//输出信息
}
public static void main(String[] args) {//主方法
Son son=new Son();//创建一个对象
//操作属性
son.name="李白";//继承
//操作方法
son.working();//继承
son.playGame();//自有
}
}
结果
- super.父类属性名:调用父类中的属性
- super.父类方法名:调用父类中的方法
- super():调用父类的无参构造方法
- super(参数):调用父类的有参构造方法
代码
/**
* 系统用户:父类
* @author 86157
*
*/
public class SystemUser {
public String username;//账号
public String password;//密码
public String name;//姓名
public String gender;//性别
public Integer age;//年龄
public String regNo;//身份证号码
public String phone;//手机
public String email;//邮箱
public String address;//地址
public SystemUser(){}//一个方法
public SystemUser(String username,String password,String name){//定义一个方法并传入三个字符串类型的参数
this.username=username;//调用成员变量并赋值
this.password=password;//调用成员变量并赋值
this.name=name;//调用成员变量并赋值
}
}
/**
* 系统管理员
* @author 86157
*
*/
public class Manager extends SystemUser {//类名
}
/**
* 医师
* @author 86157
*
*/
public class Doctor extends SystemUser {//类名
public String department;//部门
public String offices;//科室
}
/**
* 医护
* @author 86157
*
*/
public class Nurse extends SystemUser{//定义一个类并继承父类
//部门
public String department;//定义自己的属性
}
结果
概念:
重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
好处:
重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为private和final的方法。
子类和父类不在同一个包中,那么子类只能够重写父类的声明为public和protected的非final方法。
代码
import java.util.Date;//导入Data方法
/**
* 医疗项目中的采购清单
* 采购方/联系方式/邮箱/传真/地址
*提供方/联系方式/邮箱/传真/地址
*采购单编号/采购类型/采购单位/采购数量/采购单价/交易时间/总计金额/备注/注意事项1~n
*
*/
public class Purchase {//购买类
public String demanName;//采购方
public String demanPhone;//联系方式
public String demandEmail;//邮箱
public String demandFax;//传真
public String demandAddress;//地址
public String supplyName;//提供方
public String supplyPhone;//联系方式
public String supplyEmail;//邮箱
public String supplyFax;//传真
public String supplyAddress;//地址
public Integer purchaseId;//采购单编号
public String purchaseType;//采购类型[枚举]
public String purchaseBrand;//采购规格
public String purchaseUnits;//采购单位
public String purchasePrice;//采购单价
public Date purchaseDeal;//交易时间
public Double purchaseTotal;//总结金额
public String purchaseRemark;//注意事项
public String purchaseRemark1;//注意事项
public String purchaseRemark2;//注意事项
public String purchaseRemark3;//注意事项
public String purchaseRemark4;//注意事项
public String purchaseRemark5;//注意事项
}
代码
/**
* 基础类型
* @author 86157
*
*/
public class SystemUser {//类名
public int id;//定义一个公用的整型变量id
public String name;//定义一个字符串类型变量name
public int age;//定义一个公用的整型变量age
public String gender;//定义一个字符串变量gender
public boolean equals(Object obj){//定义一个方法传入一个参数
if(!(obj instanceof SystemUser )){//判断obj是否属于User
return false;//返回布尔类型
}
SystemUser user=(SystemUser )obj;//进行类型转换
//if(this.age>=user.age){
//return true;
//}else if(this.age=user.age?true:false;
return this.age>=user.age;//返回布尔类型
}
}
/**
* 医师类型
* @author 86157
*
*/
public class Doc extends Users{//定义一个类并继承Users类
public String job;//定义一个字符串job
public int level;//定义一个整型变量level
public boolean equals(Object obj){//定义一个方法并传入一个参数
if(!(obj instanceof Doc)){//判断obj是否属于Doc
return false;//返回false
}
Doc dt=(Doc)obj;//进行强制转换
if(this.level>dt.level){//判断成员变量level与dt引用里的level是否相等
return false;//返回布尔类型
}else if(this.level
概念:
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
被重载的方法可以改变返回类型;
被重载的方法可以改变访问修饰符;方法能够在同一个类中或者在一个子类中被重载。
无法以返回值类型作为重载函数的区分标准。
代码
/**
* 基础类型
* @author 86157
*
*/
public class SystemUser {//类名
public int id;//定义一个公用的整型变量id
public String name;//定义一个字符串类型变量name
public int age;//定义一个公用的整型变量age
public String gender;//定义一个字符串变量gender
public boolean equals(Object obj){//定义一个方法传入一个参数
if(!(obj instanceof SystemUser )){//判断obj是否属于User
return false;//返回布尔类型
}
SystemUser user=(SystemUser )obj;//进行类型转换
//if(this.age>=user.age){
//return true;
//}else if(this.age=user.age?true:false;
return this.age>=user.age;//返回布尔类型
}
}
抽象类是用来捕捉子类的通用特性的 。它不能被实例化,只能被用作子类的超类。抽象类是被用来创建继承层级里子类的模板。
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。也是因为这个原因,通常在设计阶段决定要不要设计抽象类。
父类包含了子类集合的常见的方法,但是由于父类本身是抽象的,所以不能使用这些方法。
在Java中抽象类表示的是一种继承关系,一个类只能继承一个抽象类,而一个类却可以实现多个接口。
接口
代码
/**
* DeviceService
* @author 86157
*@Reamrk:设备服务类型
*/
public class DeviceService {//定义一个类
/**
* 医师领取的设备
* @return返回领取到的设备
*/
public String getDevice(){//定义一个方法
return "医师领取的设备";//返回一个字符串
}
/**
* 医护领取设备
* @param notes 领取记录
* @return返回领取到的设备
*/
public String getDevice(String notes){//定义一个方法并传入一个参数
return"医护领取的设备";//返回一个字符串
}
/**
* 病人领取的设备
* @param offices治疗科室
* @param notes领取记录
* @return返回领取到的设备
*/
public String getDevice(String offices,String notes){//定义一个方法并传入两个参数
return "病人领取到的设备";//返回一个字符串
}
public static void main(String[]args){//主方法
DeviceService ds=new DeviceService();//创建设备服务对象
String res=ds.getDevice();//调用方法并将该值赋予定义好的字符串
System.out.println(res);//输出信息
String res2=ds.getDevice("手术用品");//调用方法并传入参数的值并赋予给定义好的参数
System.out.println(res2);//输出信息
String res3=ds.getDevice("骨科", "固定带");//调用方法并给参数赋值,之后赋予给定义的字符串变量
System.out.println(res3);//输出信息
}
}
结果
特点
- 接口是一种特殊的抽象类,只包含常量和方法的定义,而没有方法的实现。
- 通过接口可以指明多个需要实现的方法,而不需考虑这些类之间的层次关系。
- 在类体中可以使用接口中定义的常量,必须实现接口中定义的所有方法。
- 一个类可以实现多个接口,在implements字句中用逗号隔开。
Java里面由于不允许多重继承,所以如果要实现多个类的功能,则可以通过实现多个接口来实现。Java接口和Java抽象类代表的就是抽象类型,就是我们需要提出的抽象层的具体表现。
接口(英文:Interface),在JAVA编程语言中是一个抽象类型,是抽象方法的集合,接口通常以interface来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。另外,在 Java 中,接口类型可用来声明一个变量,他们可以成为一个空指针,或是被绑定在一个以此接口实现的对象。
代码
/**
* Hosipital
* @author 86157
*@Remark:医院的类型
*/
public class Hospital { //创建类
public void register(Patient patient,Department dept){//定义一个方法并传入两个参数
System.out.println("开始挂号到对应的科室:"+dept.name);//输出信息
dept.treatment(patient);//调用方法并传入参数
}
public static void main(String[]args){//主方法
Hospital hs=new Hospital();//创建医院对象
Orthopaedics op=new Orthopaedics();//骨科
op.name="骨科";//给变量赋值
Surgery sg=new Surgery();//外科
sg.name="外科";//给变量赋值
Patient patient=new Patient();//病人
hs.register(patient, sg);//调用方法并传入参数
}
}
class Patient{//类名
public int id;//编号
public String name;//姓名
public String gender;//性别
public int age;//年龄
public float health;//健康状态
}
class Department{//类名
public int id;//编号
public String name;//名称
public String intro;//介绍
public void treatment(Patient patient){//定义一个方法并传入一个参数
System.out.println(this.name+"接受病人,开始治疗");//输出信息
}
}
class Orthopaedics extends Department{//定义一个类并继承Department类
public void treatment(Patient patient){//定义一个方法并传入参数
System.out.println(this.name+"骨科接受到病人,开始给病人进行治疗....");//输出信息
}
}
class Surgery extends Department{//定义一个方法并继承Department类
public void treatment(Patient patient){//定义一个方法并传入参数
System.out.println(this.name+"外科接受到病人,准备给病人手术......");//输出信息
}
结果
代码
public class InheritSytax {//类名
/**
* 驱动器类型
* 类型使用abstract声明:抽象类
* 使用过程中不能被实例化
*/
abstract static class Device{//方法使用abstract声明:抽象方法,子类继承父类时必须重写的方法
public abstract void working();//抽象方法
public void showInformation(){//方法名
System.out.println("这是一个机械硬盘...");/输出信息
}
}
static class DeviceSystem extends Device{//定义一个静态类并继承父类
public void working() {//重写方法
System.out.println("系统盘正在工作中...");//输出信息
}
}
static class DeviceBackup extends Device{//定义一个静态类并继承父类
public void working() {//重写方法
System.out.println("备份盘正在工作中...");//输出信息
}
}
public static void main(String[]args){//主方法
//父类实例化测试:抽象类型不能被实例化
//Device device=new Device();
//子类实例化
DeviceSystem ds=new DeviceSystem();//创建对象
ds.working();//直接调用重写的方法完成具体的业务处理
DeviceBackup db=new DeviceBackup();//创建对象
db.working();//直接调用重写的方法完成具体的业务处理
}
}
结果
区别
- 1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
- 2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
- 3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
- 4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
代码
public class Bank {//类名
static abstract class Service{//定义一个静态的抽象类
public abstract String deposit(double money);//定义一个公用的抽象类方法并传入参数
}
static class CashDesk extends Service{//定义一个静态的类并继承抽象类
public String deposit(double money) {//重写抽象类方法
System.out.println("柜台存款流程");//输出信息
return "柜台存款收据";//返回一个字符串
}
}
static class ATM extends Service{//定义一个静态类并继承抽象类
public String deposit(double money) {//重写抽象类方法
System.out.println("ATM存款");//输出信息
return "ATM存款小票";//返回一个字符串
}
}
public static void main(String[] args) {//主方法
Service service=new Bank.CashDesk();//创建对象
Service service2=new Bank.ATM();//创建另一个对象
service.deposit(100);//调用方法
service2.deposit(200);//调用方法
}
}
结果
final关键字的功能概述
final关键字可以用来修饰引用、方法和类。
给final关键字修饰的成员变量赋值有两种方式:
1.直接赋值。
2.在构造方法中赋初值。
代码
public interface IDataType {
String TYPE="JSON";//定义一个字符串
public static final String TYPE2="JSON";//定义一个全局常量并赋初值
String format_msg(String msg);//定义一个抽象类方法
public abstract String format_msg2(String msg);//定义一个抽象方法
static String getTypes(){//定义一个静态方法
return IDataType.TYPE;//返回调用的字符串
}
default String transfer_msg(String msg){//定义一个默认的方法
String res=this.format_msg(msg);//调用抽象方法并将值赋予定义的字符串
System.out.println("数据开始传输...");//输出信息
return "数据传输结果";//返回一个字符串
}
public static void main(String[]args){//主方法
String res=IDataType.getTypes();//静态方法
System.out.println(res);//输出信息
JsonData jd=new JsonData();//创建对象
jd.transfer_msg("hello tom!");//调用默认方法
String res2=jd.format_msg("hello jerry!");//调用普通方法
System.out.println(res2);//输出信息
}
}
class JsonData implements IDataType{//定义一个类并继承接口
public String format_msg(String msg) {//重写方法
return "json 类型的数据 序列化操作";//返回一个字符串
}
public String format_msg2(String msg) {//重写方法
// TODO Auto-generated method stub
return null;//返回null值
}
}
结果
Interface
1.接口定义通常类名以I开头
2.接口属性默认以public static final 修饰
3,方法默认以public abstract 修饰
Jdk7及以前的接口只允许存在抽象方法
4.接口使用方法
一个接口可以实现另外一个接口-单继承机制
一个类型(可实例化)可以实现多个接口_多实现机制
代码
public class Produce {//类名
static interface IDataType{//定义一个静态接口
String transfer();//定义一个抽象的方法
}
static class JsonData implements IDataType{//定义一个静态的类并继承接口
public String transfer() {//重写方法
System.out.println("传输JSON格式的数据");//输出信息
return this.prepareDate();//方法调用的字符串
}
public String prepareDate(){//自己的方法
return "JSON格式的数据";//返回一个字符串
}
}
static class XmlData implements IDataType{//定义一个静态的类并继承接口
public String transfer() {//重写方法
System.out.println("准备传输XML格式的数据");//输出信息
return this.prepareData();//返回调用的字符串
}
public String prepareData(){//定义一个方法
return "XML格式的数据";//返回一个字符串
}
}
static class Consumer{//定义一个静态的类
public String getData(IDataType dt){//定义一个方法并传入一个参数
System.out.println("客户端调用接口,获取数据");//输出信息
return dt.transfer();//返回调用的字符串
}
}
public static void main(String[]args){//主方法
IDataType dt=new JsonData();//创建对象
IDataType dt2=new XmlData();//创建对象
Consumer consumer=new Consumer();//创建对象
String res=consumer.getData(dt2);//将调用的值赋予给定义的字符串
System.out.println(res);//输出信息
}
}
结果
代码
/**
* 标记接口
* @author 86157
*
*/
public class DataApp {//定义一个类
static interface IDataFormat{}//定义一个静态的接口
public String transfer(Object obj){//定义一个方法并传入一个参数
if(obj instanceof IDataFormat){//判断该参数是否属于这个类
System.out.println("复杂数据。需要进行序列化处理和类型转换");//输出信息
}
System.out.println("数据监测完成,数据开始传输...");//输出信息
System.out.println("数据传输完成");//输出信息
return"ok";//返回一个字符串
}
static class Address{//定义一个静态的类
public int id;//编号
public String nation;//国家
public String province;//省份
public String city;//市区
}
static class Person implements IDataFormat{//定义一个静态的类并继承父类
public int id;//编号
public String name;//姓名
public String gender;//性别
public int age;//年龄
public Address address;//地址
}
public static void main(String[]args){//主方法
DataApp da=new DataApp();//创建对象
Address addr=new Address();//创建对象
Person person=new Person();//创建对象
da.transfer("hello");//调用方法并传入参数
da.transfer(12);//调用方法并传入参数
da.transfer(addr);//调用方法并传入参数
da.transfer(person);//调用方法并传入参数
}
}
结果
多态是继封装、继承之后,面向对象的第三大特性。
多态现实意义理解:
1、现实事物经常会体现出多种形态,如学生,学生是人的一种,则一个具体的同学张三既是学生也是人,即出现两种形态。
2、Java作为面向对象的语言,同样可以描述一个事物的多种形态。如Student类继承了Person类,一个Student的对象便既是Student,又是Person。
3、多态体现为父类引用变量可以指向子类对象。
4、前提条件:必须有子父类关系。
多态的转型分为向上转型和向下转型两种:
向上转型:多态本身就是向上转型过的过程
使用格式:父类类型 变量名=new 子类类型();
适用场景:当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作。
向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用类型转为子类引用各类型
使用格式:子类类型 变量名=(子类类型) 父类类型的变量;
代码
public class Shopping {//定义一个类
public void seckill(String name,int deviceType,String deviceInfo){//定义一个方法并传入三个参数
System.out.println("正在秒杀商品:"+name);//输出信息
if(deviceType==1){//判断是否等于1
this.record(name, Long.parseLong(deviceInfo)); //调用方法并传入参数
}else if(deviceType==2){//判断是否等于2
this.record(name, deviceInfo);//调用方法并传入参数
}
}
private void record(String name,Long i){//定义一个私人的方法并传入两个参数
System.out.println("记录--秒杀商品:"+name+";移动终端信息:"+i);//输出信息
}
private void record(String name,String url){//定义一个私人的方法并传入两个参数
System.out.println("记录--秒杀商品:"+name+";PC终端的信息:"+url);//输出信息
}
public static void main(String[]args){//主方法
Shopping shopping=new Shopping();//创建对象
shopping.seckill("alienware",1, "15688889999");//调用方法并传入值
shopping.seckill("asua",2,"10.10.68.92");//调用方法并传入值
}
}
结果
代码
public class Shopping2 {//定义一个方法
public void seckill(String name,Device device){//定义一个方法并传入两个参数
System.out.println("正在秒杀商品:"+name);//输出信息
System.out.println("记录秒杀信息");//输出信息
device.record(name);//调用方法
}
public static void main(String[]args){//主方法
Shopping2 shopping=new Shopping2();//创建对象
Device dc=new PC();//创建对象
shopping.seckill("玩家国度", dc);//调用方法并传入值
Device dc2=new Phone();//创建对象
shopping.seckill("飞行堡垒", dc2);//调用方法并传入值
}
}
abstract class Device{//定义一个抽象类
public abstract void record(String name);//定义一个抽象方法
}
class Phone extends Device{//定义一个类并继承父类
public void record(String name) {//重写方法
System.out.println("移动端发起的秒杀:商品名称:"+name);//输出信息
}
}
class PC extends Device{//定义一个类并继承父类
public void record(String name) {//重写方法
System.out.println("PC端发起的秒杀:商品名称:"+name);//输出信息
}}
结果