JavaSE(10):多态和特殊类

1、多态

1.1 多态的概念

  • 多态主要指同一种事物表现出来的多种形态。
  • 饮料:可乐、雪碧、红牛、脉动、…
  • 宠物:猫、狗、鸟、小强、鱼、…
  • 人:学生、教师、工人、保安、…
  • 图形:矩形、圆形、梯形、三角形、…

1.2 多态的语法格式

  • 语法格式:
父类类型 引用变量名 = new 子类类型();
  • 如:
Shape sr = new Rect();
sr.show();

1.3 多态的特点

  • 当父类类型的引用指向子类类型的对象时,父类类型的引用可以直接调用父类独有的方法。
  • 当父类类型的引用指向子类类型的对象时,父类类型的引用不可以直接调用子类独有的方法。
  • 当父类类型的引用指向子类类型的对象时,对于父子类都有的非静态方法来说,编译阶段调用父类版本,运行阶段调用子类重写的版本(动态绑定)。
  • 当父类类型的引用指向子类类型的对象时,对于父子类都有的静态方法来说,编译和运行阶段都调用父类版本。

1.4 引用数据类型之间的转换

  • 引用数据类型之间的转换方式有两种:自动类型转换 和 强制类型转换。
  • 自动类型转换主要指小类型向大类型的转换,也就是子类转为父类,也叫做向上转型。
  • 强制类型转换主要指大类型向小类型的转换,也就是父类转为子类,也叫做向下转型或显式类型转换。
/*
多态是指父类类型的引用指向子类类型的对象。例如下面的代码,
相当于从Rect类型到Shape类型的转换,也就是子类到父类的转换(小到大的转换),即自动类型转换。
*/
Shape sr = new Rect();
/*
Shape类型的引用sr不能直接调用子类Rect类中独有的方法getLen(),
若想要sr调用子类Rect中独有的方法,需要将sr从Shape类型转换为Rect类型,
也就是父类到子类的转换(大到小的转换),即强制类型转换。
*/
((Rect) sr).getLen();
  • 引用数据类型之间的转换必须发生在父子类之间,否则编译报错。
  • 若强转的目标类型并不是该引用真正指向的数据类型时,则编译通过,运行阶段发生ClassCastException(类型转换异常)。
/*
Shape类为父类,Rect类为Shape类的一个子类,
Circle类为Shape类的另一个子类,
将sr从Shape强制类型转换为Circle,编译通过,但运行时发生ClassCastException,
因为sr真正指向的是Rect类型,所以才可以从Shape类型强转为Rect类型。
*/
Shape sr = new Rect();
Circle c1 = (Circle)sr;
  • 为了避免上述错误的发生,应该在强转之前进行判断。判断引用变量指向的对象是否为后面的数据类型。格式如下:
if(引用变量 instanceof 数据类型)
// 判断sr指向堆区内存中的对象是否为Circle类型,若是则返回true,否则返回false
if(sr instanceof Circle) {
    System.out.println("可以放心地转换了!");
    Circle c1 = (Circle)sr;
} else {
    System.out.println("强转有风险,操作需谨慎!");
}

1.5 多态的实际意义

  • 多态的实际意义在于屏蔽不同子类的差异性实现通用的编程带来不同的效果。

2、抽象类

2.1 抽象方法的概念

  • 抽象方法主要指不能具体实现的方法并且使用abstract关键字修饰,也就是没有方法体。
  • 语法格式:
访问权限 abstract 返回值类型 方法名(形参列表);
  • 如:
public abstract void cry();

2.2 抽象类的概念

  • 抽象类主要指不能具体实例化的类并且使用abstract关键字修饰,也就是不能创建对象。

2.3 抽象类和抽象方法的关系

  • 抽象类中可以有成员变量、构造方法、成员方法。
  • 抽象类中可以没有抽象方法,也可以有抽象方法。
  • 拥有抽象方法的类必须是抽象类,因此真正意义上的抽象类应该是具有抽象方法并且使用abstract关键字修饰的类。

2.4 抽象类的实际意义

  • 抽象类的实际意义不在于创建对象而在于被继承。
  • 当一个类继承抽象类后必须重写抽象方法,否则该类也变成抽象类,也就是抽象类对子类具有强制性和规范性,因此叫做模板设计模式

2.5 多态的使用场合

  • 场合一:通过参数传递形成了多态
/*
Shape类为父类,Rect类和Circle类分别为Shape类的子类。
show方法是在Shape类中自定义的方法,分别在Rect类和Circle类中重写过。
*/
public class ShapeTest {

	public static void draw(Shape s) {
		// 编译阶段调用父类的版本,运行阶段调用子类重写以后的版本
		s.show();
	}

	public static void main(String[] args) {
		
		ShapeTest.draw(new Rect(1, 2, 3, 4));
		ShapeTest.draw(new Rect(5, 6, 7));
	}
}
  • 场合二:使用抽象类的引用指向子类类型的对象
// 声明抽象类AbstractTest类型的引用指向子类的对象,形成了多态
AbstractTest at = new SubAbstractTest();
// 编译阶段调用父类版本,运行阶段调用子类版本
at.show();
/* 
若要用at调用子类SubAbstractTest类中独有的test()方法,
需要将at从AbstractTest类型强转为SubAbstractTest类型。
*/
((SubAbstractTest) at).test();
  • 在以后的开发中推荐使用多态的格式,此时父类类型引用直接调用的所有方法一定是父类中拥有的方法,若以后更换子类时,只需要将new关键字后面的子类类型修改而其它地方无需改变就可以立即生效,从而提高了代码的可维护性和可扩展型。
  • 该方式的缺点就是:父类引用不能直接调用子类独有的方法,若调用则需要强制类型转换。

2.6 抽象方法的注意事项

  • private 和 abstract 关键字不能共同修饰一个方法。private修饰的私有方法不能被子类继承,而abstract修饰的抽象方法是需要在子类中重写的,所以不能共同修饰一个方法。
  • final 和 abstract 关键字不能共同修饰一个方法。final修饰的成员方法不能被重写,而abstract修饰的抽象方法是需要在子类中重写的,所以不能共同修饰一个方法。
  • static 和 abstract 关键字不能共同修饰一个方法。抽象类不能创建自己的对象,因而避免调用没有方法体的抽象方法,但static修饰的成员方法可以通过 类名. 的方式调用,使得抽象方法被调用成为可能,所以static 和 abstract 不能共同修饰一个方法。

3、接口

3.1 接口的基本概念

  • 接口就是一种比抽象类还抽象的类,体现在所有方法都为抽象方法。
  • 定义类的关键字是class,而定义接口的关键字是interface。
  • 语法格式:
public interface 接口名 {
	
}
  • 如:
public interface InterfaceTest {
	// 接口里面只能有常量,“public static final”可以省略
	public static final int CNT = 1;
	// 接口里面只能有抽象方法(新特性除外),“public abstract”可以省略
	public abstract void show();
	// 从Java9开始允许接口中出现私有方法
	private void show(){}
}
  • 在接口中定义私有方法的作用:在接口中减少重复的代码,提高代码的复用性,同时私有方法不能在接口外部调用,有很好的安全性。

3.2 类和接口之间的关系

名称关键字关系
类和类之间的关系使用extends关键字表达继承关系支持单继承
类和接口之间的关系使用implements关键字表达实现关系支持多实现
接口和接口之间的关系使用extends关键字表达继承关系支持多继承

3.3 抽象类和接口的主要区别(笔试题)

  • 定义抽象类的关键字是abstract class,而定义接口的关键字是interface。
  • 继承抽象类的关键字是extends,而实现接口的关键字是implements。
  • 继承抽象类支持单继承,而实现接口支持多实现。
  • 抽象类中可以有构造方法,而接口中不可以有构造方法。
  • 抽象类中可以有成员变量,而接口中只可以有常量。
  • 抽象类中可以有成员方法,而接口中只可以有抽象方法。
  • 抽象类中增加方法时子类可以不用重写,而接口中增加方法时实现类需要重写(Java8以前的版本)。
  • 从Java8开始增加新特性,接口中允许出现非抽象方法和静态方法,但非抽象方法需要使用default关键字修饰。
  • 从Java9开始增加新特性,接口中允许出现私有方法。

3.4 案例题目

  • 题目:编程实现Runner接口,提供一个描述奔跑行为的抽象方法。
    编程实现Hunter接口继承Runner接口,并提供一个描述捕猎行为的抽象方法。
    编程实现Man类实现Hunter接口并重写抽象方法,在main方法中使用多态方式测试。
  • Runner接口:
public interface Runner {

    // 自定义抽象方法描述奔跑的行为
    public abstract void run();
}
  • Hunter接口:
public interface Hunter extends Runner {

    // 自定义抽象方法描述捕猎的行为
    public abstract void hunt();
}
  • Man类:
public class Man implements Hunter {
	// 重写Hunter接口中的hunt方法
    @Override
    public void hunt() {
        System.out.println("正在捕猎中...");
    }

	// 重写Runner接口中的run方法
    @Override
    public void run() {
        System.out.println("正在奔跑中...");
    }

    public static void main(String[] args) {

        // 声明接口类型的引用指向实现类的对象,形成了多态
        Runner runner = new Man();
        runner.run();

        Hunter hunter = new Man();
        hunter.hunt();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值