面向对象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 + “]”;
 }
