java基础---多态(多态思想、多态的方法回调、引用类型转换、组合关系、字段无多态)

一、理解多态思想

1.多态的产生

学了继承关系,我们知道继承关系是一种”is A”的关系,也就说子类是父类的一种特殊情况;既然子类是一种特殊的父类,那么我们可不可以认为狗对象/猫对象就是动物类型的对象.

Animal  d = new Dog();  //创建一只狗对象

Animal  c  =new Cat();    //创建一只猫对象

-------------------------------------------------------

当我的代码变成以下的样子的时候,多态就产生了:

Animal    a  =  new    Dog();

对象(a)具有两种类型:

 1)编译类型:  声明对象变量的类型,Animal,表示把对象看出什么类型.

 2)运行类型:  对象的真实类型,Dog.运行类型--->对象的真实类型.

编译类型必须是运行类型的父类/或相同.

当编译类型和运行类型不同的时候,多态就出现了.

2.多态定义

对象具有多种形态,对象可以存在不同的形式。
Animal a = null;
            a = new Dog();   //a此时表示Dog类型的形态
            a = new Cat();    //a此时表示Cat类型的形态

3.多态的前提

1)继承关系(类和类);
2)实现关系(接口和实现类)
在开发中一般都是第二种

4.多态的特点

把子类对象赋给父类变量,在运行时期会表现出具体的子类特征(调用子类的方法)。
Animal a = new Dog();

5.多态的好处

需求: 给饲养员提供一个喂养动物的方法, 用于喂养动物.
1)没有多态是代码如下
//Super类  动物
class Animal
{
	public void eat()
	{
		System.out.println("吃一般食物");
	}
}

//狗类  
class Dog extends Animal
{
	public void eat()
	{
		System.out.println("吃骨头");
	}
}

//猫类
class Cat extends Animal
{
	public void eat()
	{
		System.out.println("吃猫粮");
	}
}

//饲养员
class Feeder
{
	//喂食方法--狗
	public void feed(Dog d)
	{
		System.out.println("feeding");
		d.eat();
	}
	
	//喂食方法--猫-----重载
	public void feed(Cat c)
	{
		System.out.println("feeding");
		c.eat();
	}
}


//演示多态
class PolymorphismDemo
{
	public static void main(String[] args) 
	{
		//创建饲养员、狗和猫对象
		Feeder f = new Feeder();
		Dog d = new Dog();
		Cat c = new Cat();

		//调用饲养员的喂食方法
		f.feed(d);
		f.feed(c);
	}
}

由代码可知,没有多态是针对不同类型的动物需要提供不同的feed方法喂食。

2)存在多态时,代码如下
//Super类  动物
class Animal
{
	public void eat()
	{
		System.out.println("吃一般食物");
	}
}

//狗类  
class Dog extends Animal
{
	public void eat()
	{
		System.out.println("吃骨头");
	}
}

//猫类
class Cat extends Animal
{
	public void eat()
	{
		System.out.println("吃猫粮");
	}
}

//饲养员
class Feeder
{
	//喂食 对于不同类型的动物,在运行时体现子类独有的特性
	public void feed(Animal a)
	{
		System.out.println("feeding");
		a.eat();
	}
}


//演示多态
class PolymorphismDemo
{
	public static void main(String[] args) 
	{
		//创建饲养员、狗和猫对象
		Feeder f = new Feeder();
		Dog d = new Dog();
		Cat c = new Cat();

		//调用饲养员的喂食方法
		f.feed(d);
		f.feed(c);
	}
}


统一了喂养动物的行为;
从上述例子,可以看出多态的好处:当把不同的子类对象都当作父类类型来看待,可以屏蔽不同子类对象之间的实现差异,从而写出通用的代码达到通用代码

二、多态时方法调用问题


前提:必须先存在多态情况:

存在父类:SuperClass,子类:SubClass,方法:doWork.

--------------------------------------------------------------------

测试代码:

 SuperClass clz =  new   SubClass();//多态

 clz.doWork();//???输出什么结果


1)doWork方法存在于SuperClass中,不存在于SubClass中.



此时执行结果:编译通过,执行SuperClassdoWork方法.

      编译时:去编译类型中寻找doWork()方法;存在--->编译通过;不存在--->编译失败

      运行时:先去运行类型中寻找doWork()放法,找不到去父类中找


2) doWork方法存在于 SubClass中 ,不存在于 SuperClass .

此时执行结果: 编译错误.

     编译时期,会去编译类型(SuperClass)中找是否有doWork方法:

                  找   到:编译通过.

                  找不到:编译报错.

3)doWork方法存在于SuperClassSubClass中.


此时执行结果: 编译通过,执行SubClassdoWork方法.

      在运行时期,调用运行类型(SubClass)中的方法.

4) doWork方法存在于 SuperClass SubClass中 ,但是doWork是静态方法.

      此时这种情况,我们称之为隐藏,而不叫方法覆盖.

此时执行结果: 编译通过,执行SuperClassdoWork方法.

    静态方法的调用只需要类即可.

    如果使用对象来调用静态方法,其实使用的是对象的编译类型来调用静态方法.

    和对象没有关系.



三、引用类型转换

1.基本数据类型转换:

 1) 自动类型转换:  把小类型的数据 赋给  大类型的变量.(此时的大和小表示的容量范围)

        byte b = 12; byte是1个字节

        int   i   = b;   int4个字节

 2)强制类型转换: 把大类型的数据赋给  小类型的变量.

        short s =  (short)i ;short2个字节

2.引用类型的转换:

               引用类型的大和小,指的是父类 和子类的关系.

           1) 自动类型转换:  把子类对象赋给父类变量(多态).

                 Animal a = new Dog();

                            Object是所有类的根类:

                            Object obj = new Dog();

                       2)强制类型转换: 把父类类型对象赋给子类类型变量(当时该父类类型变量的真实类型应该是子类类型).

                            Animal a = new Dog();

                             Dog   d = (Dog ) a;

-----------------------------------------------------------------------------------------------------
instanceof运算符:判断该对象是否是某个类的实例

语法格式:booleanb = 对象A   instanceof 类B;  //判断A对象是否是B类的实例,如果是,返回true.

另:如果对象A是类B的实例,那么他也是类B的父类的实例。

在开发中 ,有时候 ,我们只想判断是真实类型的实例, 而不想判断为编译类型的实例.
此时需要用到getClass()方法,返回该对象的运行类型。
Object obj = "ABC";//自动装箱:把String对象赋给Object类型
		System.out.println(obj instanceof Object);//true
		System.out.println(obj instanceof String);//true

		System.out.println(obj.getClass());//获取对象的运行类型
		System.out.println(obj.getClass() ==String.class);//true
		System.out.println(obj.getClass() ==Object.class);//false

四、组合关系


   如果A类为了得到B的功能行为:

   如果A类是B类的一种特殊情况,我们就应该采用继承来实现;否则使用组合方式.


  

继承关系: 子类可以继承到父类中部分的成员,那么此时子类是可以修改到父类的信息的.

继承关系破坏封装,为了复用代码可能会让子类具有不该具有的功能.

-----------------------------------------------------------------------------------

为什么引入继承: 为了代码复用问题.

解决代码复用问题,不一定非要使用继承,也可以使用包含关系”(has A).

我没钱,但是我想开豪车,我想吃火锅:

    方式1: 任一个富豪干爹.                   继承关系:

    方式2: 把一个富豪绑架在我家里,挟天子以令诸侯!    组合关系/包含.




1)组合关系




2)继承关系


class ArrayUtil
{
	//排序
	public void sort(int[] arr)
	{
		for (int times = 1; times <= arr.length - 1 ; times ++ )
		{
			for (int i = times; i <= arr.length - 1 ; i ++ )
			{
				if(arr[times - 1] > arr[i])
				{
					swap(arr,times - 1, i);
				}
			}
		}
	}
	
	//交换
	static void swap(int[] arr, int index1, int index2)
	{
		int temp = arr[index1];
		arr[index1] = arr[index2];
		arr[index2] = temp;
	}
	
	//打印
	public void print(int[] arr)
	{
		for (int i = 0; i <arr.length  ; i ++ )
		{
			System.out.print(arr[i] +" ");
		}
	}
}

//演示组合关系
class CombinationDemo extends ArrayUtil
{
	/*
	//继承关系
	public void test()
	{
		int[] arr = {-2, 9, 1, 0, 5};
		sort(arr);
		print(arr);
	}
	public static void main(String[] args) 
	{
		new CombinationDemo().test();
	}
	*/
	



	
	//组合关系
	//创建一个ArrayUtil对象,调用ArrayUtil中的方法
	private ArrayUtil util = new ArrayUtil();

	public void test()
	{
		int[] arr = {-2, 9, 1, 8, 15};
		util.sort(arr);
		print(arr);
	}

	public static void main(String[] args) 
	{
		new CombinationDemo().test();
	}	
}

五、字段不存在多态特征

//父类
class SuperClass
{
	public String name = "Super.name";

	public void doWork()
	{
		System.out.println("Super.World!");
	}
}

//子类
class SubClass extends SuperClass
{
	public String name = "Sub.name";

	public void doWork()
	{
		System.out.println("Sub.World!");
	}
}

//演示:字段不存在多态特征
class FieldDemo 
{
	public static void main(String[] args) 
	{
		SuperClass clz = new SubClass();
		clz.doWork();//结果:Sub.World
		System.out.println(clz.name);//结果:Sub.name
	}
}


通过对象调用字段,编译时期就已经决定了调用那一块内存空间的数据.

--------->字段不存在覆盖的概念,在多态时,不能有多态特征(在运行时期体现子类特征).

只有方法才有覆盖的概念.

当子类和父类存在相同的字段的时候,无论修饰符是什么(即使private),都会在各自的内存空间中存储数据.





























  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值