黑马程序员--java基础复习之多态与Object及匿名内部类

------ Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------



多态

定义:某一类事物的多种存在形态
例如:动物中的猫、狗。猫这个对象对应的类型是猫类型,如:猫 x = new(); 同时猫也是动物中的一种,也可以把猫称为动物。
动物  y = new (); 那么动物就是猫和狗具体事物中抽取出来的父类型。父类型引用指向了子类对象。

多态:可以理解为事物存在的多种体现形态
1、多态的体现
父类的引用指向了自己的子类对象。
父类的引用也可以接收自己的子类对象。
2、多态的前提
必须是类与类之间有关系。要么继承,多么实现
通常还有一个前提:存在覆盖

3、多态的好处
多态的出现大大的提高程序的扩展性
4、多态的弊端
提高了扩展性,但是只能使用父类的引用访问父类中的成员

如下代码:
abstract class Animal
{
	abstract void eat();  //定义一个eat()的抽象方法,所以这个类也是一个抽象类
	//其子类调用此类的时候,要对其抽象方法全部重写
}

interface Catch
{
	abstract void catchMou();
}

//继承动物这个类
class Cat extends Animal  implements Catch   
{
	void eat()
	{
		System.out.println("我是猫,我吃鱼");
	}	
	public void catchMou() //重写(覆盖)接口中的抽象类
	{
	  System.out.println("接口:抓老鼠抓老鼠抓老鼠抓老鼠");	
	}
	
	void CatchMouse()  //子类的独有方法
	{
		System.out.println("我是猫,这是我的独有功能:抓老鼠");
	}
}

class Dog extends Animal  //继承动物这个类
{
	void eat() //重写eat()这个方法
	{
		System.out.println("我是狗,吃骨头");
	}
	void kanJia()  //Dog类的独有方法
	{
		System.out.println("我是狗,这是我的独有功能:看家");
	}
}

class Pig extends Animal  //继承动物这个类
{
	void eat()
	{
		System.out.println("我是猪,吃饲料");
	}
	void GongDi()
	{
		System.out.println("我是猪,这是我的独有功能:拱地");
	}
}


class DuoTaiDemo
{
	 public static void main(String[] args)
	 {
		 System.out.println("*****************使用多态前******************");
		 /*****************前期做法******************/
		 function(new Cat());  //调用猫的eat()方法
		 function(new Dog());  //调用狗的eat()方法
		 function(new Pig());  //调用猪的eat()方法
		 /*******************************************/
		 System.out.println("*****************使用多态******************");
		 /*****************使用多态******************/
		 functionDuoTai(new Cat());
		 functionDuoTai(new Dog());
		 functionDuoTai(new Pig());
		 /*******************************************/
		 
		
		 /*******************************************/
		 
	 }
	 
	 /*****************前期做法******************/
	 //以下为以往做法,代码重复冗余
	 public static  void function(Cat c)
	 {
		 c.eat();	
		c.catchMou();
	 }
	 public static  void function(Dog d)
	 {
		 d.eat();		 
	 }
	 public static  void function(Pig p)
	 {
		 p.eat();		 
	 }
	 /*******************************************/
	 
	 /*****************使用多态******************/
	 public static void functionDuoTai(Animal a)
	 {
		
		 
		 //运行动物的独有功能
		 //多态的类型转换
		
		 if
		 (a instanceof Cat)  //如果Animal是猫
		 {
			 Cat c=(Cat)a;  //将动物向下转换成猫
			 c.CatchMouse(); //猫的特有功能
		 }
		 else if(a instanceof Dog)
		 {
			 Dog d=(Dog)a; //将动物向下转换成狗
			 d.kanJia();
		 }
		 else if(a instanceof Pig)
		 {
			 Pig p=(Pig)a;
			 p.GongDi();
		 }
		 
	 }
	/*******************************************/
 }
结果:

 这代码就是多态的体现,假设子类Cat中有特有的抓老鼠功能,父类型的 a就不能直接调用。这上面的代码中,可以理解为Cat类型提升了,向上转型。如:
	Animal a=new Cat();//类型提升,向上转型
	//如果想要调用猫的特有方法时,得强制将父类的引用,转成子类类型		
	Cat c=(Cat)a;
        如果此时父类的引用想要调用Cat中特有的方法,就需要强制将父类的引用,转成子类类型,向下转型。如:Catc = (Cat)a;
注:如果父类可以创建对象,如:Animal a = new Animal(); 此时,就不能向下转型了,Cat c = (Cat)a; 这样的代码就变得不容许,编译时会报错。所以千万不能出现这样的操作,就是将父类对象转成子类类型。
        我们能转换的是父类引用指向了自己的子类对象时,该引用可以被提升,也可以被强制转换。多态至始至终都是子类对象在做着变化。

看一个例子:
/*
基础班学生:学习,睡觉
高级班学生:学习,睡觉
可以将这两类事物进行抽取
*/
abstract class Student
{
	//定义一个抽象方法
   public abstract	void study();
   
   //这个方法可以继承(子类对象可以直接使用),也可以复写
   void sleep()
   {
	   System.out.println("躺着睡");
   }
}

//基础班学生
class BaseStudent extends Student
{
	//复写父类的study方法
	public void study()
	{
		System.out.println("基础学习");
	}
	//复写父类的sleep方法
	void sleep()
	{
		System.out.println("坐着睡");
	}
}
class AdvStudent extends Student
{
	//复写父类的study方法
	public void study()
	{
		System.out.println("高级学习");
	}
}

class DoStudent
{
	//多态的使用
	 void DoSome(Student s)
	{
		s.study();
		s.sleep();
	}
}

class DuoTaiDemo
{
	public static void main(String[] args)
	{
		DoStudent ds=new DoStudent();
		
		ds.DoSome(new BaseStudent());
		ds.DoSome(new AdvStudent());
	}	
}
上述代码是定义了一个中间的类,将基础班学生和高级班学生的共同行为封装在一个类中,使用其父类作为参数传递,也是用到了多态。
下面也是一个多态的小应用,其分为三个部分

1、定义好工具类,即将共同行为封装在一个类中。

2、对类型进行抽取,---->多态的产生。

3、操作同一父类型,对其中的子类型均可操作


如下:
/*
多态的应用

需求:
电脑运行实例,电脑运行是基本于主板的。
假设主板只是提供电脑运行,但是没有上网,听歌等功能。
而上网、听歌需要硬件的支持。而现在主板上没有网卡和声卡,这时可以定义一个规则,叫PCI,
只要符合这个规则的网卡和声卡都可以在主板上使用,这样就降低了主板和网卡、声卡之间的耦合性。
用程序体现。
*/

//创建一个主板类
class MainBoard
{
	public void run()
	{
		System.out.println("主板运行");
	}
	
	public void usePCI(PCI p)  //PCI p=new NetCard();接口型引用指向自己的子类对象
	{
		if(p!=null)  //当使用设备时
		{
			p.open();
			p.close();
		}			
		
	}
}

//创建一个接口 PCI
interface PCI
{
	void open();
	void close();
}

//网卡实现PCI接口
class NetCard implements PCI
{
	public void open()
	{
		System.out.println("网卡运行");
	}
	public void close()
	{
		System.out.println("网卡停止");
	}
}

//声卡实现PCI接口
class SoundCard implements PCI
{
	public void open()
	{
		System.out.println("声卡运行");
	}
	public void close()
	{
		System.out.println("声卡停止");
	}
}

class DuoTaiDemo5
{
	public static void main(String[] args)
	{
		//运行主板
		MainBoard mb=new MainBoard();
		mb.run();
		//主板通过PCI接口操作设备
		mb.usePCI(null);
		mb.usePCI(new SoundCard());
		mb.usePCI(new NetCard());
	}
}

1、在多态中成员函数(非静态)的特点:

在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。

在运行时期:参阅对象所属的类中是否有调用的方法。

如下:

class Fu
{
	void method1()
	{
		System.out.println("Fu Method1");
	}
	void method2()
	{
		System.out.println("Fu Method2");
	}
}

class Zi extends Fu
{
	void method1()
	{
		System.out.println("Zi Method1");
	}
	void method3()
	{
		System.out.println("Zi Method2");
	}
}

class DuoTaiDemo3
{
		public static void main(String[] args)
		{
			Fu z=new Zi();
			z.method1();
			z.method2();
			//z.method3();  //编译失败
		}
}
结果:

简单总结:成员函数在多态调用时,编译看左边,运行看右边


2、在多态中成员变量的特点:无论编译或运行,都参考左边(引用型变量所属的类)

如下面代码:

class Fu
{
	int num=5;
}

class Zi extends Fu
{
	int num=8;
}

class DuoTaiDemo4
{
		public static void main(String[] args)
		{
			Fu f=new Zi();
			System.out.println(f.num);
			Zi z=new Zi();
			System.out.println(z.num);
		}
}
运行结果:



3、在多态中,静态成员函数的特点(在开发中很少见,因为很少会去复写静态方法)
无论编译和运行,都参考左边    如下:
class Fu
{
	int num=5;
	static void method()
	{
		System.out.println("Fu  method");
	}
}

class Zi extends Fu
{
	int num=8;
	static void method()
	{
		System.out.println("Zi  method");
	}
}

class DuoTaiDemo4
{
		public static void main(String[] args)
		{
			Fu f=new Zi();			
			f.method();
			Zi z=new Zi();			
			z.method();			
		}
}
结果:









Object类

类Object是类层次结构的根类。每个类都使用Object作为超
类。
Object :是所有对象的直接或者间接父类,传说中的上帝
该类中定义的肯定是所有对象都具备的功能。

下面讲的就是Object中的常用的方法

equals  
boolean equals(Object obj)           指示其他某个对象是否与此对象“相等”。(其实比较的就是对象在内存中的地址值)
参数类型是Object ,这里也是用到了多态。

如下代码:
class Demo
{
	
}	
	 
class ObjectDemo
{
	public static void main(String[] args)
	{
		Demo  d1=new Demo();
		Demo d2=new Demo();
		Demo d3=d1;
		
		System.out.println(d1.equals(d2));  //d1和 d2分别指向不同的对象,地址值自然不同 false
		System.out.println(d1==d2);  //在比较对象时,==和equals 效果一样  false
		
		System.out.println(d1.equals(d3)); //d1和d3均指向同一个对象,所以地址值自然一样  true
		System.out.println(d1==d3);  //同上
		
	}
}
结果:





Object类中已经提供了对对象是否相同的比较方法。
如果自定义类中也有比较相同的功能,没有必要重新定义,只要沿袭父类Object中的功能,建立自己特有的比较内容即可。这就是覆
盖。如下:

class Demo
{
	private int num;
	Demo(int num)
	{
		this.num=num;
	}
	//复写父类Object中的equals方法
	public boolean equals(Object obj)
	{
		if(obj instanceof Demo) //判断是否是Demo类型的对象
		{
			Demo d=(Demo)obj;
			return this.num==d.num;
		}
		return false;
	}
}	
	 
class ObjectDemo
{
	public static void main(String[] args)
	{
		Demo  d1=new Demo(5);
		Demo d2=new Demo(5);
		
		System.out.println(d1.equals(d2));
	}
}

结果:


内部类

内部类:将一个类定义在另一个类的里面,对里面那个类就称为内部类(内置类,嵌套类)

访问特点:
A、内部类可以直接访问外部类中的成员,包括私有成员
之所以可以直接访问外部类中的成员,是因为外部类中持有了一个外部内的引用,格式 外部类名.this
B、而外部类要访问内部类中的成员必须要建立内部类的对象


如下代码:
//外部类
class Outer
{
	private int x=3;
	
	//内部类
	class Inner
	{
		void function()
		{
			//访问外部类中的成员,包括私有成员
			System.out.println("Inner:"+x);
		}
	}
	
	void method()
	{
		//外部类要访问内部类中的成员必须要建立内部类的对象
		Inner in=new Inner();
		in.function();
	}
}

class InnerClassDemo
{
	public static void main(String[] args)
	{
		Outer ou=new Outer();
		ou.method();
		
		/*********在其他类中直接访问内部类中的成员************/
		//当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中,直接建立内部类对象。
		Outer.Inner in=new Outer().new Inner();		
		in.function();
	}
}

再看下面一段代码:
class Outer
{
	private int x=3;
	private int y=6;
	class Inner
	{
		int x=4;
		void function()
		{
			int x=5;
			System.out.println("Inner:"+x); //访问本类中的局部变量x
			System.out.println("Inner:"+this.x);  //访问本类中的x,所以用this
			System.out.println("Inner:"+Outer.this.x);//访问外部类中的本类变量x,所以加上Outer.this
			System.out.println("Inner:"+y); //这里因为没有同名变量,所以省略了Outer.this
		}
	}
	
	void method()
	{
		Inner in=new Inner();
		in.function();
	}
}

class InnerClassDemo0
{
	public static void main(String[] args)
	{
		Outer ou=new Outer();
		ou.method();			
	}
}

结果:
从上述代码中可以得出:之所以可以直接访问外部类中的成员,是因为外部类中持有了一个外部内的引用,格式 外部类名.this

从上面的代码中可以看到内部类的访问格式
访问格式:
1、当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中,直接建立内部类对象。
格式:外部类名.内部类名  变量名=外部类对象.内部类对象
                                           如 Outer.Inner  in=new Outer().new Inner();
2、当内部类在成员位置上,就可以被成员修饰符所修饰。
比如,private:将内部类在外部类中进行封装


静态内部类
 static:内部类就具备static 的特性。
当内部类被static 修饰后,只能直接访问外部类中的static成员。出现了访问局限。

如下代码片段:
class Outer
{
	 private int x=3;
	
	//静态内部类
	static class Inner
	{
		void function()
		{
			System.out.println("Inner:"+x);
		}
	}
}
结果:

因为内部类用static修饰后,就具备了static的特性。所以静态不能直接访问非静态

注意:当内部类中定义了静态成员,该内部类必须是静态的n
 当外部类中的静态方法访问内部类时,内部类也必须是static的


如下代码段:
class Outer
{
	 private int x=3;
	
	//静态内部类(当内部类中有静态成员时,该内部类必须是静态的)
	static class Inner
	{
	  static void function()
		{
			System.out.println("Inner:"+x);
		}
	}
}


3、内部类定义在局部时:

1、不可以被成员修饰符修饰(static  private)
2、可以直接访问外部类中的成员,因为还持有外部类中的引用。
但是不可以访问它所在的局部中的变量。只能访问被final修饰的局部变量


用如下代码说明:
<span style="color:#666666;">class Outer
{
	int x=3;
	void method()
	{
		final int y=4;
		//局部内部类
		class Inner
		{			
			void function()
			{
				System.out.println("局部内部类:"+x);
				//如果y没有用final修饰,则访问y时编译器会提示:从内部类中访问局部变量y;
				//需要被声明为最终类型
				System.out.println(y);
			}
		}
		
		//要想访问非静态,必须通过对象
		
		new Inner().function();
	}
}

class InnerClassDemo1
{
	public static void main(String[] args)
	{
		new Outer().method();
	}
}</span>






匿名内部类

1、匿名内部类其实就是内部类的简写格式。
2、定义匿名内部类的前提:内部类必须是继承一个类或者实现接口
3、匿名内部类的格式:new 父类或者接口(){定义子类的内容}
4、其实匿名内部类就是一个匿名子类对象。而且这个对象有点胖。
5、匿名内部类中定义的方法最好不要等于或超过3个


用一个例子来说明:
//定义一个抽象类,作为匿名内部类的父类
abstract class AbsDemo
{
	abstract void show();
}

//外部类
class Outer
{
	int x=8;
	void method()
	{
		//new Inner().function();
		/**********使用匿名内部类1***********/
	
		new AbsDemo()
		{
			//复写父类的抽象方法
			void show()
			{
				System.out.println("我是匿名内部类:"+x);
			}
		}.show();
		/**********使用匿名内部类2***********/
		new AbsDemo()
		{
			//复写父类的抽象方法
			void show()
			{
				System.out.println("我是匿名内部类:"+x);
			}
			void haha()
			{
				System.out.println("haha");
			}
		}.haha();
		
		/**********使用匿名内部类2***********/
		
		//这个是加载中的向上提升
	AbsDemo ad=	new AbsDemo()
		{
			//复写父类的抽象方法
			void show()
			{
				System.out.println("我是匿名内部类:"+x);
			}
			//这个方法其实没什么意义
			void haha()
			{
				System.out.println("haha");
			}
		};
		
		ad.show();
		//ad.haha();//这句会编译失败,因为父类中根本没有haha这个方法
	}
	
	
}

class InnerClassDemo3
{
	public static void main(String[] args)
	{
		new Outer().method();
	}
}

下面来看一个匿名内部类的题目:

interface Inter
{
	void method();
}
class Test
{
	//补足代码。通过匿名内部类
}

class InnerClassTest
{
	public static void main(String[] args)
	{
		Test.function().method();
	}
}
代码及思路如下:
//这个接口是作为内部类的父类存在的(注:接口中全部都是public 类型的抽象方法)
interface Inter
{
	void method();
}
class Test
{
	//补足代码。通过匿名内部类
	static Inter function()
	{
		return new Inter()
		{
			public void method()
			{
				System.out.println("这是一个匿名内部类的小题目");
			}
		};
	}
}

class InnerClassTest
{
	public static void main(String[] args)
	{
		Test.function().method();
		//思路:
		//Test.function():Test类中有一个静态的方法 function.
		//.method():function这个方法运算后的结果是一个对象。
		//因为只有是Inter类型的对象,才可以调用method方法。
	}
}

再看另一个匿名内部类的题目:
/*补全下面的代码,想使用一个function方法。要求使用匿名内部类的方式完成*/

class InnerTest
{
	public static void main(String[] args)
	{
		//补足代码。通过匿名内部类
	}
}

完成:因为没有定义父类或接口,所以想到Object是所有类的直接或间接父类。
class InnerTest
{
	public static void main(String[] args)
	{
		//补足代码。通过匿名内部类
		new Object()
		{
			void function()
			{
				System.out.println("Hello world");
			}
		}.function();
	}
}
















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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值