Java_Day16(多态、抽象、接口)
多态
- 什么是多态:
- 同一个对象,在不同时刻表现出来的不同形态.
- 我们可以说猫是猫:猫 cat = new 猫();
- 我们也可以说猫是动物:动物 animal = new 猫();
- 这里猫在不同的时刻表现出来了不同的形态,这就是多态
- 多态的前提:
- 要有继承/实现关系
- 要有方法的重写
- 要有父类引用指向子类对象
- 多态的特点:
- 需要有继承关系
- 方法重写
- 父类引用执行子类对象
public static void main(String[] args) {
// Animal a = new Animal();
// Cat c = new Cat();
// 父类引用指向子类对象
Animal cat = new Cat();
Animal dog = new Dog();
System.out.println(cat.leg);//2
System.out.println(dog.leg);//2
cat.eat();//猫吃鱼
dog.eat();// 狗吃骨头
}
- 多态访问成员变量的特点:
- 在多态中,如果在子类和父类中出现同名的成员变量 则使用的是父类的成员变量
- 成员变量的访问: 编译看左边,运行看左边
- 多态访问成员方法的特点:
- 在多态中,如果子类重写了父类的方法,则使用的是子类的方法
- 成员方法的访问:编译看左边,运行看右边
若编译时类型和运行时类型不一致, 就出现了对象的多态性(Polymorphism)
多态情况下成员方法, “看左边” : 看的是父类的引用(父类中不具备子类特有的方法)
“看右边” : 看的是子类的对象(实际运行的是子类重写父类的方法)
成员变量: 不具备多态性,只看引用变量所声明的类。
使用多态来调用方法只能调用子类和父类都有的方法(必须存在方法的重写)。而不能调用子类 特有的方法
多态的好处和弊端
好处
- 提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作
弊端
- 不能使用子类的特有成员
public class Person {
// 人要饲养一只动物 使用父类作为参数 当引用的时候 传入具体所指的真实 子类
public void siyang(Animal animal){
animal.eat();
}
}
public static void main(String[] args) {
// Animal a = new Animal();
// Cat c = new Cat();
// 父类引用指向子类对象
Animal cat = new Cat();
Animal dog = new Dog();
System.out.println(cat.leg);//2
System.out.println(dog.leg);//2
cat.eat();//猫吃鱼
dog.eat();// 狗吃骨头
Person person = new Person();
person.siyang(cat);// 传入真实的所引用的对象
person.siyang(dog);//传入真实 的所引用的对象
}
对于子类特有成员的访问
// 只有 真实的类型对象 才能调用当前子类中特有的 方法
Cat c = new Cat();
Dog d = new Dog();
c.play();
d.work();
// 进行类型转换
Cat cc = (Cat) cat;
cc.play();
Dog dd = (Dog) dog;
dd.work();
- 类型转换:
- 类型提升:将父类型的引用执行子类对象,由小到大,进行自动转换(多态)
- 造型 :将父类型的引用对象转换为子类型的对象由大到小,强制转换
- 从子类到父类的类型转换可以自动进行
- 从父类到子类的类型转换必须通过造型(强制类型转换)实现
- 无继承关系的引用类型间的转换是非法的
在类型转换中 (造型)
Dog dog1 = (Dog) cat;
dog1.work();
类型转换异常
解决方法:
使用instanceof
判断一个引用是否是某一个类的实例对象
if(cat instanceof Dog){
Dog dog1 = (Dog) cat;
dog1.work();
}else{
System.out.println("类型不匹配");
}
方法的重载与重写
- 重载,是指允许存在多个同名方法,而这些方法的参数不同。 编译器根据方法不同的参数表, 对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。 它们的调用地址在编译期就绑定了。 Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。所以: 对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定” ;
- 而对于多态,只有等到方法调用的那一刻, 解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”
抽象 abstract
Abstract 可以修饰类 修饰类的时候 类就称为抽象类
修饰方法的时候 就称为抽象方法
package cn.lanqiao.abs;
// 抽象类
public abstract class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
// 抽象方法
public abstract void work();
}
组成
- 成员变量
- 成员方法
- 构造方法
- 构造代码块
- 抽象方法
- 常量
- 类变量
- 静态代码块
特点
- 抽象类不能被实例化(不能创建对象)
- 抽象类是用来被继承的
- 抽象类的子类必须去重写抽象类中的抽象方法
- 要么全部重写
- 只是重写部分则子类也必须是抽象的
- 抽象类中不一定有抽象方法
- 含有抽象方法的必须是抽象类
package cn.lanqiao.abs;
import java.util.zip.ZipError;
public class Student extends Person {
@Override
public void work() {
System.out.println("努力敲代码,学习Java技术,冲刺年薪百万....");
}
}
package cn.lanqiao.abs;
public class Worker extends Person{
@Override
public void work() {
System.out.println("努力工作,赚钱还房贷.....");
}
}
package cn.lanqiao.abs;
public class PersonTest {
public static void main(String[] args) {
// 抽象类不能被实例化
// Person p = new Person();
Person person1 = new Student();
person1.work();
Person person2 =new Worker();
person2.work();
}
}
意义
抽象类中构造方法存在的意义:
- 对于抽象类我们自己不能在外部使用他的构造方法创建对象,但是jvm可以。
- 在创建子类对象的时候 jvm会创建一个父类的隐含对象,便于我们对于抽象类的成员的访问
模板方法设计模式(TemplateMethod)
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
解决的问题:
- 当功能内部一部分实现是确定的, 一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
- 换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。
package cn.lanqiao.tempalte;
public abstract class TemplateMethod {
//计算代码的执行时间
// public static long currentTimeMillis()
public long getCodeTime(){
// 获取代码开始执行的时间
long begin = System.currentTimeMillis();
//开始执行代码
code();
//代码执行结束 获取结束之后的 时间
long end = System.currentTimeMillis();
return end - begin;//毫秒
}
public abstract void code();
}
package cn.lanqiao.tempalte;
public class TemplateTest extends TemplateMethod {
@Override
public void code() {
int sum = 0;
for(int i = 0 ; i < 10000;i++){
sum += i;
}
System.out.println(sum);
}
public static void main(String[] args) {
TemplateMethod tm = new TemplateTest();
long time = tm.getCodeTime();
System.out.println(time);
}
}
public static long currentTimeMillis()
在1970年1月1日UTC之间的当前时间和午夜之间的差异,以毫秒为单位
接口
• 接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用
• Java中的接口更多的体现在对行为的抽象
• 接口的本质是契约,规范,就像我们人间的法律一样。制定好后大家都遵守。
接口中的定义:
- 常量 public static final (可不写) 默认就是这样的
- 方法 必须是抽象方法 public abstract(修饰符可不写)方法默认就是 抽象的
- 没有构造方法
- 不能存在代码块
- 不能存在变量
接口的特点:
- 接口不能被实例化
- 接口是用来被实现的 实现接口的类 必须实现接口的抽象方法
- 一个类可以实现多个接口
- 一个类在继承的同时也可以实现就接口
- 接口可以继承接口且只能继承接口
- 接口可以多继承
实现类要么实现所有的方法 要么是抽象类(实现接口的部分抽象方法)
public interface JDBCDao {
int num = 10;
void insert();
void delete();
void update();
void select();
}
package cn.lanqiao.inter;
public class JDBCDaoImpl implements JDBCDao{
@Override
public void insert() {
System.out.println("插入一条数据");
}
@Override
public void delete() {
System.out.println("删除一条数据");
}
@Override
public void update() {
System.out.println("修改一条数据");
}
@Override
public void select() {
System.out.println("查询数据");
}
}
public class JDBCTest {
public static void main(String[] args) {
JDBCDao dao = new JDBCDaoImpl();// 这种就称为接口的多态
dao.insert();
dao.delete();
dao.update();
dao.select();
}
}
所以在实际的使用中,接口往往只是方法的规范,对于接口中的常量基本不用
类、接口、多态
多态的分类
- 具体的多态
- 抽象类的多态
- 接口的多态
关系
- 类和类的关系
- 继承关系,只能单继承,但是可以多层继承
- 类和接口的关系
- 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
- 接口和接口的关系
- 继承关系,可以单继承,也可以多继承
抽象类和接口的区别
No. | 区别点 | 抽象类 | 接口 |
---|---|---|---|
1 | 定义 | 包含抽象方法的类 | 主要是抽象方法和全局常量的集合 |
2 | 组成 | 构造方法、抽象方法、普通方法、常量、变量 | 常量、抽象方法、 |
3 | 使用 | 子类继承抽象类(extends) | 子类实现接口(implements) |
4 | 关系 | 抽象类可以实现多个接口 | 接口不能继承抽象类,但允许继承多个接口 |
5 | 常见设计模式 | 模板方法 简单工厂、工厂方法、代理模式 | |
6 | 对象 | 都通过对象的多态性产生实例化对象 | |
7 | 局限 | 抽象类有单继承的局限 | 接口没有此局限 |
8 | 实际 | 作为一个模板 | 是作为一个标准或是表示一种能力 |
9 | 选择 | 如果抽象类和接口都可以使用的话,优先使用接口,因为避免单继承的局限 |