Java 多态和抽象类
1.1 案例一 : 人类喂狗
需要完成一个功能: Dog类有一个eat函数,人类有一个feed函数 ,人类的feed函数能传递狗对象并且调用其eat函数 public class People { public void feed(Dog dog){ dog.eat(); } } public class Dog { public void eat(){ System.out.println("狗爱吃肉"); } } public class JavaTest { public static void main(String[] args) { Dog wangcai = new Dog(); People zhangsan = new People(); zhangsan.feed(wangcai); } } 问题: 狗为什么不自己吃? 因为我们这个案例 是模拟后期的一种场景,一个类要使用另一个类的对象, People 需要使用Dog对象 调用Dog对象的函数 这是后面我们学习Spring的时候 依赖注入DI的操作
函数中参数传递的时候 基本类型传参 和 对象类型传参 有什么不同? 基本类型是传值 对象类型是传址 ---- 大错特错 无论是什么 都是传递的引用地址
1.2 案例2: 人类喂猫
需要我们创建一个猫类 添加eat函数,人类也需要喂猫。 public class Cat { public void eat(){ System.out.println("猫爱吃鱼"); } } 此时让zhangsan去喂猫,发现报错,因为参数类型不匹配 feed(Dog dog) 此时传递的是Cat类型 。 public class People { public void feed(Dog dog){ dog.eat(); } public void feed11111111(Cat cat){ cat.eat(); } } 现在喂狗 用 feed 喂猫用 feed1 此时我们发现如果动物再多一点 我们需要不同的动物 使用不同的函数 很麻烦 所以我们可以使用函数的重载 public class People { public void feed(Dog dog){ dog.eat(); } public void feed(Cat cat){ cat.eat(); } } 此时调用的时候 我们不管什么动物 只需要调用feed就行 public class JavaTest { public static void main(String[] args) { Dog wangcai = new Dog(); Cat kate = new Cat(); People zhangsan = new People(); zhangsan.feed(wangcai); zhangsan.feed(kate); } }
1.3 人类喂猪
需要创建一个Pig类,也有eat函数,人类也需要喂猪。 这个操作就是创建一个Pig类 public class Pig { public void eat(){ System.out.println("猪爱吃饲料"); } } 人类要喂猪,人类需要再重载一个feed函数 参数类型是Pig类型 问题就来了 : 这样操作能行,但是有问题,后续如果还有新动物,人类就需要不断的去重载 这样的代码 我们称之为 高耦合(我们的代码联系太密切 牵一发而动全身) 高内聚低耦合,是软件工程中的概念,是判断软件设计好坏的标准,主要用于程序的面向对象的设计,主要看类的内聚性是否高,耦合度是否低。目的是使程序模块的可重用性、移植性大大增强。通常程序结构中各模块的内聚程度越高,模块间的耦合程度就越低。内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事,它描述的是模块内的功能联系;耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。 优秀的代码: 低耦合 高内聚 所以我们需要一个方案 来降低耦合,就目前我们的案例而言:添加新动物 人类不需要重载 还能正常喂养
1.4 创造了这样一个语法来解决问题
此时造了这样一个语法: public class Haha { public void eat(){ System.out.println("你好世界"); } } public class Dog extends Haha{ @Override public void eat(){ System.out.println("狗爱吃肉"); } } public class JavaTest { public static void main(String[] args) { Haha wangcai = new Dog(); wangcai.eat(); } } 形容: 父类的引用可以被赋值成不同的子类对象,赋值以后,将以子类的形式进行运作 ---- 多态 引用Charlie Calverts对多态的描述——多态性是允许你将父对象设置成为一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。 多态 字面意思:多种状态 多态的语法规则: 父类引用 名字 = new 子类构造函数(); 继承多态 5% 接口引用 名字 = new 实现类构造函数(); 接口多态 95% 多态的作用:产生多态之后可以用来解耦 使用多态的语法解决当前的问题:高耦合 public class JavaTest { public static void main(String[] args) { Haha kate = new Cat(); Haha wangcai = new Dog(); Haha peiqi = new Pig(); People zhangsan = new People(); zhangsan.feed(kate); zhangsan.feed(wangcai); zhangsan.feed(peiqi); } } 此时添加一个新动物 Tiger 有eat 人类也需要喂养Tiger public class Tiger extends Haha { @Override public void eat(){ System.out.println("老虎爱吃肉"); } } 注意 现在添加新动物 人类不需要修改代码 也能正常喂养,仅仅是降低了耦合,人类和动物不耦合了,但是人类和Haha耦合
多态: 多种状态 运行状态和编译状态 Haha d = new Dog(); d 编译的时候 是Haha 运行的时候是Dog
1.5 出现了一个新问题
此时我们将People 和 动物们解耦了,但新问题: 1 Haha能否被创建对象?合适不? 能 不合适,因为没有Haha这种动物,Haha存在是为了作为其他动物的父类,这样能产生多态,从而解耦 也就是说 Haha这个类 不是为了创建对象而存在的 并且也不能被创建对象 2 Haha的eat可以不写吗?为什么? 不能 因为虽然没有用 但是不写编译不过去。也就是说必须写 但是又不会被调用 所以 函数声明是有用的:为了让编译通过 函数的体是没有用的因为写什么都不会被调用。 3 Dog Pig Cat 不写eat能行么?合适吗? 可以 不合适,不写就去调用父类,但是需求是每个类都要有自己的eat 总结以上三点:我们需要弄一个新语法 1 某个类不能被new对象 2 函数可以只声明 不用写方法体 3 子类必须写要求写的函数 所以java的开发者 就造了一个关键字 只要添加这个关键字 就能实现我们想要的效果 并且给其命名为 : 抽象
1.6 抽象解决当前的问题
public abstract class Haha { public abstract void eat(); }
1.7 抽象的语法规则
1 abstract添加到类上面称之为抽象类 2 抽象类不能new对象 3 抽象类有构造函数 [构造函数就是与new连用用来创建对象 但是抽象类不能被new对象 构造函数存在的意义是什么呢?为了子类继承的时候 super() ] 4 抽象类中可以有抽象函数 也可以有成员变量 普通函数 静态变量 静态常量等内容 ----- 抽象类和普通类相比 在于抽象类可以添加抽象函数 但是不能new对象 5 abstract添加到方法上面称之为抽象方法 6 抽象方法一定是在抽象类中(接口) 7 抽象方法不写也不能写方法体 8 抽象方法必须由子类负责重写或者子类也是抽象的 9 抽象方法不能和无法重写的关键词连用 private final static
java面向对象特征: 封装 继承 多态 抽象 接口