面向对象4
1.抽象类
什么时候使用抽象类??
抽象类适用于被继承的,抽象的关键字为abstract, abstract只能修饰类和方法
从子类中提取共同的方法,放在父类中,但是每个子类对于该方法的实现不一致,这个时候可以将该方法在父类中生成抽象方法。包含该抽象方法的类为抽象类
例如:
形状:矩形 圆形 三角形 菱形等等
每一个形状 都有一个功能:求面积
把求面积的方法提取出来 放在父类中 但是在父类中到底该如何实现这个方法?? - 不确定 这个时候可以把该方法设置为抽象方法 - 由子类去重写
抽象类中的特点
1.有抽象方法的类必定是抽象类。抽象类中不一定有抽象方法
2.如果一个普通类继承自抽象类,必须重写实现抽象类中的所有抽象方法【原因是在普通类中不允许出现抽象方法】
3.如果抽象子类继承抽象父类,可以不用全部实现父类中的抽象方法【原因抽象类中可以存在抽象方法】
4.普通类继承自抽象子类,抽象子类继承自抽象父类,普通类必须重写并实现抽象子类以及抽象父类中的所有抽象方法
5.抽象类不能进行实例化,原因是如果可以进行实例化就可以调用抽象方法,但是抽象方法不能让被直接调用,只能被子类重写。所以抽象类不能被实例化
6.抽象方法只有方法签名 没有方法的实现 - 没有{}的方法体
2.abstract关键字的注意事项
1.不能与final共存
原因:final修饰的类是不能被继承 修饰的方法不能被重写
但是abstract修饰的类是用于被继承的 修饰的方法必须被子类重写
2.与private不能共存
原因:private修饰的方法只能在当前类中被使用 子类中无法获取
但是abstract修饰的方法必须得被子类获取并重写
3.不能与static共存
原因:static修饰的方法 可以直接被类调用
但是abstract修饰的方法不能被直接调用
3.抽象类和一般类的区别
- 抽象类中可以有抽象方法,也可以有普通方法,但是一般类中只有普通方法
2.抽象类和一般类都是来描述事物的,但是抽象类描述事物行为时有不确定性,所以会使用抽象方法来表示
3.抽象类是必须被继承的 但是一般类可以不能被继承
4.抽象类不能直接实例化,但是一般类可以
4.练习1:
为一个州立大学建立账单系统,州内和州外学生的收费标准不同,州内每学分收费$75,州外每学分$200。每个学生账单上都显示学校的名字,学生自己的名字,学分和账单总额。
分析:学生抽象类
属性:学校名 学生名 学分。
方法:计算学费的抽象方法和打印账单的方法
州内学生类 继承自 学生类
实现计算学费的方法
打印账单的方法
州外学生类 继承自学生类
实现计算学费的方法
打印账单的方法
测试类
方法:显示学生账单
练习2
定义一个抽象(abstract)类,类名为Employee。 Employee的子类有YearWorker、MonthWorker和WeekWorker。YearWorker对象按年领取薪水(每年10万),MonthWorker对象按月领取薪水(每月1万),WeekWorker对象按周领取薪水(每周0.5万,设定一个月4周)。Employee类有一个抽象(abstract)方法: public abstract double earnings(); 子类必须重写父类的earnings()方法,给出各自领取每年报酬的具体方式。
5.接口
抽象类是从多个类中提取出来的抽象模板,如果将这种抽象更彻底化,可以提炼出一种特殊的“抽象类” - 接口(Interface)
接口中的方法都是抽象方法 属性都是常量 在接口中没有构造代码块 也没有构造方法
确切的说:接口是一种规范
程序中如何声明接口?
声明接口的关键字:interface
格式:访问权限修饰符[public] interface 接口名{
0-多个属性常量
0-多个抽象方法
}
注意:
如果声明接口前访问权限为public,那么该接口所在的源程序文件名应该与接口名一致。
接口编译之后也会生成对应的class文件。所以可以把接口看做为"类"
接口的声明和类的声明是平级的
总结:
在接口中声明的属性默认为常量,前面直接默认添加修饰符:public static final
在接口中声明的方法默认就为抽象方法,前面默认添加修饰符:public abstract
注意:重写接口中的方法时 注意访问权限的问题
接口和接口的关系:
类和类之间的关系是继承的关系,而且是单继承。
接口和接口的关系是继承关系,多继承
多继承格式:
[public] interface A extends B, C{
}
接口和类的关系:
类实现接口,而且支持多实现
格式
[public] class 类名 extends 父类 implements 父接口1,父接口2,父接口3…{
类体
}
【注意:
1.当一个普通类实现接口时,要将接口中的抽象方法全部重写
2.抽象类实现类接口,可以不用将所有方法都重写
3.普通继承自抽象类,又实现接口,那么抽象类中方法和接口中的抽象方法都要重写
】
可以把实现接口看做是"继承类",一个类实现接口时,也可以拥有接口中的特性
类实现多个接口,其实是间接实现了"类的多继承"
标记接口:
如果一个接口中没有任何内容,这种接口称之为标记接口。起一个标记性的作用
例如:序列化接口
interface Serializable{
}
练习:
创建一个名称为Vehicle的接口,在接口中添加两个带有一个参数的方法start()和stop()。在两个名称分别为Bike和Bus的类中实现Vehicle接口。创建一个名称为interfaceDemo的类,在interfaceDemo的main()方法中创建Bike和Bus对象,并访问start()和stop()方法。
练习2:设计一个家政服务接口,在接口中定义一个方法:洗衣,做饭
设计一个测试类,在测试类实现接口; 在main方法中实现场景:
小明请了一个保姆,回去做饭
6.面向对象特性之一 - 多态
继承是多态的前提。有继承/实现存在的情况下才会有多态
什么叫多态??
可以理解为事务的多种形态。
java中引用变量有两种类型:编译时类型和运行时类型
编译时类型由声明该变量的类型来决定的
运行时类型实际给该变量赋值的对象的类型决定的
Person p = new Person();
当编译时类型和运行时类型不一致时 就会产生所谓的多态
多态的格式:
父类或者父接口的引用变量 指向子类或者实现类的对象 这个就是多态
父类/父接口 变量 = new 子类();
多态在成员中的特点
代码只有编译通过才有运行的可能
成员变量的特点:
不具备多态性
成员方法的特点:
具备多态性
静态成员方法的特点:
不具备多态性的 与对象无关
在多态中 编译的时候调用子类特有的属性或者方法 此时此刻编译报错,错误原因:父类中没有声明对应的变量或者方法。
引用类型的类型转换
自动类型转换(向上转型,隐式类型转换)
父类或者父接口的引用变量指向子类的对象,在编译时将子类对象自动向上提升,提升为父类
强制类型转换(向下转型, 显式类型转换)
多态的前提下,将父类/父接口的引用变量 转换成子类类型
编译时父类变量调用子类特有的内容 需要进行强转
格式:
子类类型 变量 = (子类类型)父类/父接口的引用变量;
注意:在强制转换时 最好做一下转换判断 判断该变量是否可以转换为该类型
判断格式:
if(父类变量 instanceof 子类类型){
// 进行强转
}else{
s.o.p(“这两者不能进行类型转换”)
}
判断格式中:
父类变量 instanceof 子类类型 什么情况下判断为true 什么情况下为false
当父类变量运行时与子类类型一致时 结果判断为true 反之为false
练习:
菜菜家有宠物狗和宠物猫
宠物狗:
昵称 年龄 性别
行为: 看家 吃骨头
宠物猫:
昵称 年龄 性别
行为:抓老鼠 吃鱼
菜菜向自己的好朋友介绍他的宠物们
宠物的共性:
昵称 年龄 性别
行为:吃
练习:
定义一个抽象(abstract)类,类名为Employee。 Employee的子类有YearWorker、MonthWorker和WeekWorker。YearWorker对象按年领取薪水(每年10万),MonthWorker对象按月领取薪水(每月1万),WeekWorker对象按周领取薪水(每周0.5万,设定一个月4周)。Employee类有一个抽象(abstract)方法: public abstract double earnings(); 子类必须重写父类的earnings()方法,给出各自领取每年报酬的具体方式。
有一个Company类,该类用Employee数组作为成员,Employee数组的单元可以YearWorker对象、MonthWorker对象、WeekWorker对象。程序能输出Company对象一年需要支付的薪水总额。
class Company{
Employees[] employees;
}
class Test{
public static void main(String[] args){
Company com = new Company();
YearWorker year =new YearWorker(10);
MonthWorker month=new MonthWorker(1);
// 这个父类类型的数组中存放子类对象 数组中每个父类变量都指向子类对象
com.employees = new Employees[]{year,month};
Employees em = new YearWorker(10);
}
}
7.Object类
Java中所有类的父类或者是间接父类,Object位于食物链的最顶端。Object被称为根类
声明的类如果没有使用extends显式继承某个类 他就继承了Object [隐式继承]
class Person{}
class Student extends Person{}
所以:Object中的方法 所有类都可以使用
常用方法:
1.public boolean equals(Object obj)
这个方法是用来判断对象伪相等的。一般都得重写
判断两个对象是否相等,比较两个对象地址即可,使用==来比较
对象真实相等 是比较地址 地址的比较是 ==
equals在Object中实现的时候 默认比较的是地址,但是一般情况下需要equals进行伪相等判断,所以在对应的类中进行方法的重写,根据某些属性来判断是否相等
例如 如果两个人id一致 判定为"相等"
class Person {
String name;
int id;
Person(){}
Person(String name, int id){
this.name = name;
this.id = id;
}
// 重写Object中equals方法
@Override
public boolean equals(Object obj){ //Object obj= new Person("张三", 28);
// Object没有id的属性 想获得的话 需要进行强转
Person person = (Person)obj;
if(this.id == person.id){
return true;
}
return false;
}
}
2.hashCode()方法
public int hashCode() 默认返回的是一个对象的地址的整数格式
对象的具体地址 不由 hashCode来决定
即使hashCode一致 对象也有可能不是一个地址
3.toString方法
public String toString()
默认返回的是:
getClass().getName() + ‘@’ + Integer.toHexString(hashCode())
getClass().getName(): 类名
@ -
hashCode() 地址的整数形式
Integer.toHexString(hashCode()) 将地址的整数形式转化为十六进制
打印变量时 默认调用toString方法。一般情况下打印变量时 是想打印该变量对应的对象信息,所以经常来重写该方法 - 以对象信息的形式重写该方法
public String toString(){
return “[姓名:” + name + “,id为:” + id + “]”;
}