Java基础系列三之多态

多态

引言:

什么叫作多态呢?

用通俗的话来说就是在同一时刻事物所呈现的不同状态

例如   水:

多态 固态     液态     气态

打个比方 父亲person有行为这个方法,里面包括几个动作:吃饭,睡觉,走路 父亲有三个儿子,三个儿子都继承了父亲的行为方法,所以三个儿子都有吃饭,睡觉,走路这些动作,但是三个儿子又分别有自己的方法---大儿子A会弹吉他,二儿子B会唱歌,三儿子C会打鼓 ...
1.Person person = new A();是父类引用指向子类对象而不是父类对象指向子类引用。
2.如果仅是这么去写程序则父类引用不能去调用子类A特有的弹吉他方法--person.guitar(); X
3此时还不是多态,记住实现多态的三要素:继承 重写 父类引用指向子类对象
4.要想使用父类引用去调用子类的特有方法guitar(),则需要在父类中同样写一个guitar()方法,子类的guitar()是对父类guitar()方法的重写,此时就可以使用person.guitar()。
注意:此时在代码的编译阶段,person调用的仍然是自己的guitar()不是子类的,而当程序运行时person调用的却是子类的guitar()。这个动态的过程(编译和运行是不同的状态)才是多态
多态的前提:
 
1)必须有继承关系
这是多态的入口,必经之地
 
2)必须有方法重写
使用父类的引用去调用子类中的方法,则这个被调用的方法在子类中必须对其进行重写。
 
3)必须有父类的引用指向子类对象(向上转型)
 
父类名  fu = new  子类名() ;
 
父类引用的创建是通过子类堆内存中新建了(new)一个对象,也就是说父类的引用是通过子类新建对象进行的。

重载是一种静态的多态。在程序编译时能够确定的方法调用,称为早绑定,而在多态变量和重写相结合之后在运行时才能确定,是动态的多态性,称为晚绑定。
多态中成员访问特点:
 
1)成员变量: 编译看左,运行看左
 
2)
非静态员方法: 编译看左,运行看右(存在方法重写)
  3)构造方法:无论是父类还是子类,都是对对象进行初始化
 
4)静态成员方法:    编译看左,运行看左(静态跟类有关,算不上方法重写) 

代码示例

package org.westos;

class Fu{
	int num = 20 ;//
	public void show() {
		System.out.println("show Fu....");
	}
	public Fu(){
		
	}
	
	public static void function() {
		System.out.println("function Fu...");
	}
}
//子类
class Zi extends Fu{
	int num = 30 ;
	
	public void show() {
		System.out.println("show Zi....");
	}
	
	public static void function() {
		System.out.println("function Zi...");
	}
	
	           public void method() {
		System.out.println("----4");
	}
}
public class ExtendsDemo2{
	public static void main(String[] args) {
        Fu f = new Zi() ;	
		System.out.println(f.num);
		f.show();
		f.function();
	}
}

多态的好处

1)可以提高代码的复用性:由继承保证

2)可以提高代码的扩展性:由多态保证

代码示例

package org.westos;

class Animal {
	public void eat() {
		System.out.println("eat");
	}

	public void sleep() {
		System.out.println("sleep");
	}
}

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

	public void sleep() {
		System.out.println("猫趴着睡觉...");
	}
}

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

	public void sleep() {
		System.out.println("狗卧着睡觉...");
	}
}

// pig类
class Pig extends Animal {
	public void eat() {
		System.out.println("猪吃白菜...");
	}

	public void sleep() {
		System.out.println("猪躺着睡觉...");
	}
}

// 可以定义一个动物的工具类AnimalTool
class AnimalTool {

	private AnimalTool() {
	}
	// 静态功能
	// 调用猫的功能
	/*
	 * public static void useCat(Cat c) { c.eat(); c.sleep();
	 * 
	 * } //调用猪的功能 public static void usePig(Pig p) { p.eat(); p.sleep(); }
	 * 
	 * //调用狗的功能 public static void useDog(Dog d) { d.eat(); d.sleep(); }
	 */

	public static void useAnimal(Animal a) { // 需要他的子类对象 Animal a = new Cat()/new Dog()/new Pig();
		a.eat();//eat()非静态的,编译看左,运行看右,即调用子类的eat()
		a.sleep();//sleep()非静态的,编译看左,运行看右,即调用子类的sleep()
	}
}

public class ExtendsDemo2 {
	public static void main(String[] args) {
		// //我喜欢猫,要养一只猫
		// Cat c = new Cat() ;
		// c.eat();
		// c.sleep();
		// System.out.println("-------------");
		//
		// //还喜欢猫,又养一只
		// Cat c2 = new Cat() ;
		// c2.eat();
		// c2.sleep();
		//
		// Dog d = new Dog() ;
		//
		// System.out.println("-------------");
		// //我很喜欢猫
		// Cat c3 = new Cat() ;
		// c3.eat();
		// c3.sleep();
		//
		// System.out.println("-----------------");
		//
		//
		// //上述的方法非常麻烦..,可以通过调用方法进行改进
		// useCat(c);
		// useCat(c2);
		// useCat(c3);
		//
		// System.out.println("----------");
		//// //定义了动物工具类的改进...
		//// AnimalTool.useCat(c);
		//// AnimalTool.useCat(c2);
		//// AnimalTool.useCat(c3);
		//
		// 改进之后
		Animal c = new Cat();  //向上引用
		Animal d = new Dog();  //向上引用 
		Animal p = new Pig();  //向上引用
		AnimalTool.useAnimal(c);//因为useAnimal()为静态的(static),所以可以由类名AnimalTools直接去调用
		AnimalTool.useAnimal(d);//因为useAnimal()为静态的(static),所以可以由类名AnimalTools直接去调用
		AnimalTool.useAnimal(p);//因为useAnimal()为静态的(static),所以可以由类名AnimalTools直接去调用

	}
}

万物有利则必有弊,此乃真理也!!!下面来说说多态的弊端

多态的弊端

不能通过父类的引用去调用子类中特有的功能

代码实例

package org.westos;

class Father2{
	
	public void show() {
		System.out.println("show father2...");
	}
}

class Son2  extends Father2{
	public void show() {
		System.out.println("show son2...");
	}
	
	//子类的特有关功能
	public void method() {
		System.out.println("method son2...");
	}
}

public class ExtendsDemo2 {
	public static void main(String[] args) {
		Father2 f  = new Son2() ;
		f.show();//编译看左边,运行看右边
		f.method() ; 编译通过了,但是Father2这个类中没有method()方法,则程序报错
	}
}

可不可以将子类的引用指向父类的引用呢? (向下转型)

        格式:    子类名  s=(子类名) f;

        将父类的引用强制转换子类的引用

代码示例

package org.westos;

class Father3 {

	public void show() {
		System.out.println("show father2...");
	}
}

class Son3 extends Father3 {
	public void show() {
		System.out.println("show son2...");
	}

	// 子类的特有关功能
	public void method() {
		System.out.println("method son2...");
	}
}

public class ExtendsDemo2 {
	public static void main(String[] args) {
		Father3 f = new Son3();// 向上转型
		f.show();
		Son3 s = (Son3) f; // 前提是必须要有父类的引用
		// 将父类的引用强制转换子类的引用 ,向下转型使用不当,会出现一个异常:属于运行时期异常:ClassCastException
		s.method();
	}
}
向下转型使用不当,会出现一个异常:属于运行时期异常:ClassCastException

图解如下


代码示例

package org.westos.多态;
/***
 * 将父类的引用强制转换子类的引用  ,向下转型使用不当,会出现一个异常:属于运行时期异常:ClassCastException
 * @author Administrator
 *
 */
class Animal2{
	public void eat() {	
		
	}
}

//猫类
class Cat2 extends Animal2 {
	public void eat() {
		
	}
	public void playGame() {
		
	}
}

//狗类:
class Dog2 extends Animal2{
	public void eat() {}
	
	public void lookDoor() {}
}

//测试类
public class DuoTaiDemo5 {

	public static void main(String[] args) {
		
		//内存中是猫
		Animal a = new Cat() ;//向上转型
		Cat c = (Cat)a;//向下转型,还原成猫
		//内存中变成了Dog
		a = new Dog() ;
		//还原成狗
		Dog d = (Dog)a ;
		
		Cat cc = (Cat)a ;
	}
}

多态Test

需求:南北方的饮食文化不同
南方人和北方人(使用多态可以测试)

自己分析功能

代码示例

package org.westos;

class Person {
	public void eat() {
		System.out.println("吃饭...");
	}
}

// 南方人呢
class SourthPeople extends Person {
	public void eat() {
		System.out.println("南方人吃米饭...");
	}

	// 特有功能
	public void business() {
		System.out.println("南方人爱经商...");
	}
}

// 北方人
class NorthPeople extends Person {
	public void eat() {
		System.out.println("北方人爱吃面...");
	}

	// 特有功能
	public void yanJiu() {
		System.out.println("北方人爱钻研...");
	}
}

public class ExtendsDemo2 {
	public static void main(String[] args) {
		Person p = new SourthPeople();//向上转型
		p.eat();//调用子类的eat()方法
		SourthPeople sp = (SourthPeople) p;//向下转型
		sp.business();
		System.out.println("-------");
		NorthPeople nop = new NorthPeople();
		nop.eat();
		nop.yanJiu();
	}
}
 面试题:
      final,finally,finalize的区别?

 final:表示最终,终态(不能被更改的)

它可以修饰类,那么该类不能继承

它可以修饰成员方法,成员方法不能被重写

它可以修饰变量,此时这个变量是一个常量 

常量的分类:

  字面值常量:

   字符串常量,字符常量,,,,

   自定义常量(final修饰的)

  pubic final int num = 100 ;
 

final

final不仅可以修饰基本数据类型

   还可以修饰引用类型

   如果final修饰的是一个基本类型数据,则基本数据类型的值不能再改变了

   如果final修饰的是一个引用类型数据,则引用类型的地址值不能再改变了,但是堆内存中的成员变量的值可以改变 

final的初始化时机:

   1)被final只能赋值一次(final int a = 10 )

   2)final int a ;

   //在使用之前进行初始化,在构造方法之前进行赋值(非静态的)

代码示例

package org.westos;

class Father{
	
	int num = 10; 
	
	final int num2  = 20 ;//num2被final修饰,则为常量
	public void method() {
		System.out.println("method father...");
	}
//	public final void show() {   //show()方法被final修饰,则不能再被重写
//		System.out.println("show father....");
//	}
	public void function() {
		num = 200 ;
//		num2 = 30 ;//num2为常量,不能再被赋值
		System.out.println(num);
		System.out.println(num2);
		
	}
}
//子类
class Son extends Father{
	public void method() {
		System.out.println("method son....");
	}
	/*public void show() {
		
	}*/
}
public class ExtendsDemo2 {
	public static void main(String[] args) {
		Son s = new Son() ;
		s.function(); 
		s.method();
	}
}

package org.westos.finaldemo;

class Student{
	int age = 20 ;
}
public class FinalTest {

	public static void main(String[] args) {
		//定一个变量
		int x = 10 ;
		x = 100 ;
		System.out.println(x);
		System.out.println("------------");
		final int y = 20 ;
		//y = 200 ;// 不能被赋值了,变量y此时常量...
		System.out.println("-------------");
		
		//创建Student类对象
		Student s = new Student() ;
		s.age = 56 ;
		System.out.println(s.age);
		System.out.println("-----------------");
		//另一种情况:
		final Student ss = new Student() ;
		ss.age = 70 ;
		System.out.println(ss.age);
		
		
		
		//给堆内存开辟一个新的空间
//		ss = new Student() ;//the final local variable ss cannot be assigned
	}
}

代码块

用{}括起来的代码,统称为代码块;

根据其所处位置的不同分为以下几类:

   1)局部代码块: 在main()里面,作用是限定变量的生命周期

  2)构造代码块:在一个类的成员位置上,用{}括起来。作用:将多个构造方法中相同的代码放到构造代码块中,对对象

进行初始化.在每次执行构造方法之前,先执行构造代码块.

                 3)静态代码块:在一个类的成员位置上,也是用{}包起来,但是被static修饰。作用:一般情况下给类进行初始化

  面试题:

   构造代码块,构造方法,静态代码块的优先级?

   静态代码块>构造代码块>构造方法

 静态代码块:只能执行一次

 构造代码块在每次执行构造方法之前都会被执行.

代码示例

package org.westos;

class Code{
	
	//静态代码块
	static{
		int x = 1000 ;
		System.out.println(x);
	}
	
	//构造代码块
	{
		int x = 100 ;
		System.out.println(x);
	}
	//构造方法
	public Code() {
		System.out.println("code11");
	}
	
	//构造代码块
	{
		int y = 200 ;
		System.out.println(y);
	}
	
	//有参构造
	public Code(int a) {
		System.out.println("code2");
	}
	
	//静态代码块
	static {
		int y = 2000 ;
		System.out.println(y);
	}
}
public class AbstractDemo11 {
	public static void main(String[] args) {
		Code code = new Code() ;
		System.out.println("------不再执行静态代码块-----");
		Code code2 = new Code() ;
		System.out.println("--------------------");
		Code code3 = new Code(100) ;
	}
}






阅读更多
个人分类: 知识点总结
上一篇Java基础系列三之继承
下一篇Java基础系列三之抽象
想对作者说点什么? 我来说一句

Java 面向对象三大特性之多态

2017年05月20日 6KB 下载

没有更多推荐了,返回首页

关闭
关闭