1、多态的概念
- 多态主要指向一种事物表现出来的多种形态
=》饮料:可乐,雪碧,红牛,脉动
=》图形:矩形,圆形,梯形,三角形。
2、多态的语法格式
- 父类类型 引用变量名 = new 子类类型();
Shape s =new Rect();
s.show();
/**
* @author XiceSberg
* @date 2020/7/26 16:36
* @description 编程实现Shape类的封装,特征有:横纵坐标,要求打印提供打印所有特征方法
*/
public class Shape {
private int x;
private int y;
public Shape(int x, int y) {
setX(x);
setY(y);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public void show(){
System.out.println("x="+x+",y="+y);
}
}
/**
* @author XiceSberg
* @date 2020/7/26 16:39
* @description 编程实现Rect类的封装并继承自Shape类,特征有:长度和宽度
*/
public class Rect extends Shape{
private int len;
private int wid;
public Rect(int x, int y, int len, int wid) {
super(x, y);
setLen(len);
setWid(wid);
}
public int getLen() {
return len;
}
public void setLen(int len) {
if(len > 0)
{
this.len = len;
}else
{
System.out.println("数据不合理");
}
}
public int getWid() {
return wid;
}
public void setWid(int wid) {
if(wid > 0)
{
this.wid = wid;
}else
{
System.out.println("数据不合理");
}
}
@Override
public void show() {
super.show();
System.out.println("len="+len+",wid="+wid);
}
}
/**
* @author XiceSberg
* @date 2020/7/26 16:45
* @description
*/
public class ShapeRectTest {
public static void main(String[] args) {
// 1.声明Shape类型的引用指向Shape类型的对象并打印特征
// 当Rect类中没有重写方法时,下面调用的时Shape中的show方法
// 当Rect类中有重写方法时,下面调用的是Shape中的show方法
Shape s=new Shape(3,4);
s.show(); //x=3,y=4
System.out.println("===============");
// 2.声明Rect类型的引用指向Rect类型的对象并打印特征
// 当Rect类中没有重写方法时,下面调用的时Shape中的show方法
// 当Rect类中有重写方法时,下面调用的是Rect中的show方法
Rect r=new Rect(5,6,7,8);
r.show(); //x=5,y=6
//len=7,wid=8
System.out.println("===============");
// 3.声明Shape类型的引用指向Rect类型的对象并打印特征
// 当Rect类中没有重写方法时,下面调用的时Shape中的show方法
// 当Rect类中有重写方法时,下面调用的方法是,在编译阶段调用的是Shape中的方法,在运行阶段调用的是Rect中的方法
// 父类的引用指向子类的对象
Shape sr=new Rect(9,10,11,12);
sr.show(); //x=9,y=10
//len=11,wid=12
}
}
- 上面的代码中的注释,诠释解释的很清楚,一定要理解熟悉
3、多态的特点
- 当父类类型的引用指向子类类型的对象时,父类类型的引用可以直接调用父类独有的方法
sr.getX();
- 当父类类型的引用指向子类类型的对象时,父类类型的引用不可以直接调用子类独有的方法
- 对于父子类都有的非静态方法来说,编译阶段调用父类版本,运行阶段调用子类重写的版本(动态绑定)
//Shape类中的方法
public static void test(){
System.out.println("Shape静态方法");
}
//Rect类中的方法
//@Override Error:历史原因,不是真正意义上的重写
//静态方法不能用@Override来重写,报错
public static void test(){
System.out.println("Rect静态方法");
}
//ShapeRectTest类中的方法
//Static member 'com.task11.Shape.test()' accessed via instance reference
//提示:不建议使用引用.的方法访问
//这种方法的时候在IDEA中是斜体,同时还有背景色。
sr.test();
//推荐使用类名.的方法访问
//调用的是父类的版本
//只和声明的类型有关,和指向的对象无关
Shape.test();
- 上面的代码中的注释,诠释解释的很清楚,一定要理解熟悉
- 对于父子类都有的静态方法来说,编译与运行阶段都调用父类版本
4、引用数据类型之间的转换
//使用父类类型的引用调用子类独有的方法的方式
((Rect) sr).getLen();
- 引用数据类型之间的转换方式有两种:自动类型转换和强制类型转换
- 自动类型转换主要指小类型转向大类型的转换,也就是子类转为父类,也叫做向上转型
- 强制类型转换主要指大类型向小类型的转换,也就是父类转子类,也叫做向下转型或显示类型转换
- 引用数据类型之间的转换必须发生在父子类之间,否则编译报错
String str1 = (String)sr;//error
5、引用数据类型之间的转换
- 若强转的目标类型并不是该引用真正指向的数据类型时则编译通过,运行阶段发生类型转换异常
Circle c1=(Circle)sr;//编译ok,但运行阶段发生 ClassCastException类型转换异常
- 为了避免上述错误的发生,应该在强转之前进行判断,格式如下:
if (引用变量 instanceof 数据类型)
判断引用变量指向的对象是否为后面的数据类型
//在强制类型转换之前应该使用instanceof进行类型的判断
//判断sr指向堆区内存中的对象是否为Circle类型,
if(sr instanceof Circle)
{
System.out.println("可以放心转换");
Circle c1=(Circle)sr;
}else
{
System.out.println("强转有风险");
}
//自定义成员方法实现将参数指定矩形对象特征打印出来的行为
public static void draw(Rect r)
{
r.show();
}
//自定义成员方法实现将参数指定圆形对象特征打印出来的行为
public static void draw(Circle c)
{
c.show();
}
//多态的运用
ShapeRectTest.draw(new Rect(1,2,3,4));
ShapeRectTest.draw(new Circle(2,3,4));
//上面的两个draw就不需要写了。
//自定义成员方法实现既能打印矩形对象又能打印圆形对象的特征,对象传入参数 子类 is a 父类
//Shape s=new Rect(1,2,3,4); //父类类型的引用指向子类类型的对象,形成多态
//Shape s=new Circle(2,3,4); //多态
public static void draw(Shape s){
//编译阶段调用父类的版本,运行阶段调用子类重写以后的版本
s.show();
}
6、多态的实际意义
- 多态的实际意义在于屏蔽不同子类的差异性实现通用的编程带来不同的效果
7、抽象方法的概念
- 抽象方法主要指不能具体实现的方法并且使用abstract关键字修饰,也就是没有方法体
访问权限 abstract 返回值类型 方法名(形参列表);
private abstract void cry();
8、抽象类的概念
- 抽象类主要指不能具体实例化的类并且使用abstract关键字修饰,也就是不能创建对象。
/**
* @author XiceSberg
* @date 2020/7/26 18:39
* @description
*/
public abstract class Abstract {
private int cnt;
public Abstract() {
}
public Abstract(int cnt) {
setCnt(cnt);
}
public int getCnt() {
return cnt;
}
public void setCnt(int cnt) {
this.cnt = cnt;
}
public static void main(String[] args) {
//抽象类与普通类的区别,除了不能new对象,其他都和普通类一样,get,set,构造,成员变量,成员方法,等
//抽象类不能new对象的原因是因为抽象类中可能有抽象方法,而抽象方法是没有方法体的,所以不能new对象就防止程序员误调用没有方法体的抽象方法。
//Abstract ab=new Abstract();
//System.out.println("ab.cnt="+ab.getCnt());
}
}
- 抽象类中可以有抽象方法,也可以没有抽象方法
- 拥有抽象方法的类必须是抽象类,因此真正意义上的抽象类应该是具有抽象方法并且使用abstract关键字修饰的类
9、抽象类的实际意义
- 抽象类的实际意义不在于创建对象而在于被继承
/**
* @author XiceSberg
* @date 2020/7/26 18:39
* @description
*/
public abstract class Abstract {
private int cnt;
public Abstract() {
}
public Abstract(int cnt) {
setCnt(cnt);
}
public int getCnt() {
return cnt;
}
public void setCnt(int cnt) {
this.cnt = cnt;
}
public static void main(String[] args) {
//抽象类与普通类的区别,除了不能new对象,其他都和普通类一样,get,set,构造,等
//Abstract ab=new Abstract();
//System.out.println("ab.cnt="+ab.getCnt());
}
public abstract void show();
}
/**
* @author XiceSberg
* @date 2020/7/26 18:51
* @description
*/
//Class 'SubAbstractTest' must either be declared abstract or implement abstract method 'show()' in 'Abstract'
//提示:要不重写实现内部抽象方法,或者自己也变成抽象方法类
public class SubAbstractTest extends Abstract {
@Override
public void show() {
System.out.println("其实我是被迫重写的,否则我也要变成抽象方法");
}
public static void main(String[] args) {
SubAbstractTest sa=new SubAbstractTest();
sa.show();
}
}
- 当一个类继承抽象类后必须重写抽象方法,否则该类也变成抽象类,也就是抽象类对子类具有强制性和规范性,因此叫做模板设计模式
//声明Abstract类型的引用指向子类的对象,形成了多态
//多态的使用场合之二:直接在方法体中使用抽象类的引用指向子类类型的对象
Abstract at=new SubAbstractTest();
//编译阶段调用父类版本,运行阶段调用子类版本
at.show();
//在以后开发中推荐使用此方法,方便代码的可维护性
//更换了子类2后的内容后,所有的代码不需要改动,只需要改动这一个子类
Abstract at=new SubAbstractTest2();
at.show();
- 在以后的开发中推荐使用多态的格式,此时父类类型的引用直接调用的所有方法一定时父类中拥有的方法,若以后更换子类时,只需要将new关键字后面的子类类型修改而其他地方无需改变就可以立即生效,从而提高了代码的可维护性和可扩展性
- 该方法的缺点就是:父类引用不能直接调用子类独有的方法,若调用则需要强制转换类型
/**
* @author XiceSberg
* @date 2020/7/26 19:12
* @description
*/
public abstract class Account {
private double money;
public Account() {
}
public Account(double money) {
setMoney(money);
}
public double getMoney() {
return money;
}
public void setMoney(double money) {
if(money > 0)
{
this.money = money;
}else
{
System.out.println("数据不合理");
}
}
public abstract double getLixi();
}
/**
* @author XiceSberg
* @date 2020/7/26 19:14
* @description
*/
public class FixedAccount extends Account {
@Override
public double getLixi() {
return getMoney()*0.03*1;
}
public FixedAccount(double money) {
super(money);//调用父类的有参构造方法
}
public static void main(String[] args) {
Account ac=new FixedAccount(1000);
System.out.println(ac.getLixi());
}
//abstract 渴望继承被重写
//private 和 abstract 关键字不能共同修饰一个方法
//private 不能继承
// private abstract double getLixi();
//final 和 abstract 关键字不能共同修饰一个方法
//final 不能重写,可以继承
// public final Abstract double getLixi();
//static 和 abstract 关键字不能共同修饰一个方法
//static 是通过类名.调用方法,类层级,这两在一起会把abstract提升为类层级,
// 而abstract不能直接new方法,所以这两货冲突
// public static abstract double getLixi();
}
- 上面的注释为重点,需要理解记住,笔试中会考
10、接口的基本概念
- 接口就是一种比抽象类还抽象的类,体现在所有方法都为抽象方法
- 定义类的关键字是class ,而定义接口的关键字是interface
- 接口不能new对象,public static final int CNT = 1; (public static final)此不写,默认JVM会自动添加
- 接口不支持多继承,但是可以多接口而多实现(implements)
- 类与接口之间得关系
名称 | 关键字 | 关系 |
类和类之间得关系 | 使用extends关键字表达继承关系 | 支持单继承 |
类和接口之间得关系 | 使用implements关键字表达实现关系 | 支持多实现 |
接口和接口之间得关系 | 使用extends关键字表达继承关系 | 支持多继承 |
private void show(){} //jdk9开始允许接口中出现私有方法
//接口中得方法不能有身体
public void show();
//接口中只能由抽象方法(新特性除外)
public abstract void show();
11、接口和抽象类得主要区别
- 定义抽象类得关键字是abstract class ,而定义接口得关键字是interface
- 继承抽象类得关键字是extends,而实现接口得关键字是implements
- 继承抽象类支持单继承,而实现接口支持多实现
- 抽象类中可以有成员方法,而接口中只可以有抽象方法
- 抽象类中增加方法时子类可以不用重写,而接口中增加方法时实现类需要重写(JAVA8以前得版本)
- 从JAVA8开始增加新特性,接口中允许出现非抽象方法和静态方法,但非抽象方法需要使用default关键字
//接口中可以增加非抽象方法
public default void show(){}
//main中重写
@Override
public void show(){}
//接口中得静态方法
public static void test(){}
//main中得调用
接口名称.test();
- 从JAVA9开始增加新特性,接口中允许出现私有方法(主要用来处理代码得冗余,让代码再内部进行使用)
//接口
private void show(){
sout("共同得代码提取");
}
public default void show1(){}
public default void show2(){}