多态、内部类、匿名内部类

 

一、多态

1.定义:可以理解为事物存在的多种体现形态.

人:男人,女人;
动物:猫,狗;
猫 x= new 猫();类与对象的关系;
动物 x = new 猫();类与类产生关系以后,原来实体还可以具备其他类型,对象的另一种形态;即多态性;
在java中我们强调对象的多态性,事实上,函数也有多态性,比如重载和覆盖就是函数的多态性体现;同一个函数名称在一个类中有多个,也是多态;

简单说:就是一个对象对应着不同类型

2.多态的体现;
    父类的引用指向了自己子类的对象;
    父类的引用也可以接受自己的子类对象;
3.多态的前提;
    必须是类与类之间有关系,要么继承,要么实现;
    存在覆盖;
4.多态的好处;
    (1)多态的出现大大的提高了程序的扩展性;

    (2)前期的代码可以使用后期的内容
5.多态的应用;
6.多态的弊端:
    (1) 提高了扩展性,但是只能使用父类的引用访问父类中的成员

    (2)前期定义的内容不能使用(调用)后期子类的特有内容,因为自始至终只能是子类在变化,父类只是引用指向了子类

举例:

package day02;
abstract class Animal
{
	// 所有动物都吃,强迫子类都是实现这个方法
abstract void eat();
}
class Cat extends Animal
{
	//复写父类的抽象方法
public void eat()
{
System.out.println("吃鱼");
}
//子类特有方法 捉老鼠
public void catchMouse()
{
System.out.println("抓老鼠");
}
}

class Dog extends Animal
{
	复写父类的抽象方法
public void eat()
{
System.out.println("吃骨头");
}
//子类特有方法  看家
public void kanjia()
{
System.out.println("看家");
}

}

class Pig extends Animal
{
public void eat()
{
System.out.println("饲料");
}
public void gongDi()
{
System.out.println("拱地");
}

}

public class DuotaiDemo
{
public static void main(String[] args)
{
/*Cat c = new eat();
c.eat();

function(new Cat());
functin(new Dog());

Animal c = new Cat();
c.eat();
*/

Animal a = new Cat();
/*自动类型提升,猫对象提升了动物类型。但是特有功能无法访问。
        作用就是限制对特有功能的访问。
        专业讲:向上转型。将子类型隐藏。就不用使用子类的特有方法。
*/
a.eat();
//如果想要调用猫的特有方法时,如何操作?
//强制将父类的引用,转成子类类型;向下转型。
Cat c = (Cat)a;
c.catchMouse();

/*加入动物可以实例化对象,不允许出现下面的情况,将父类对象转成子类类型;我们能转换的是父类引用指向了自己的子类对象时,该引用可以被提升;多条自始至终都是子类对象在作者变化;*/
/*Animal a = new Animal();
Cat c = (Cat)a*/


/*
毕姥爷 x = new 毕老师();
x.讲课();

毕老师 y =  (毕老师)x;
y.看电影();

*/
function(new Cat());
function(new Dog());







}
//定义一个方法专门对子类对象类型进行判断,进而访问子类特有功能
public static void function(Animal a)//Animal a = new Cat();
{
a.eat();
if(a instanceof Cat)
{
Cat c = (Cat)a;
c.catchMouse();
}
else if(a instanceof Dog)
{
Dog c = (Dog)a;
c.kanjia();
}

}
//这样定义比较麻烦,抽象成一个特有函数就可以了
/*public static void function(Cat c)//Cat c = new Cat();
{
c.eat();
}

public static void function(Dog d)
{
d.eat();
}
public static void function(Pig p)
{
p.eat();
}*/

}


运行结果:

7.多态时各组成的变化

(1)成员变量

无论编译和运行,都参考左边  原因:参考的是引用型变量所属的类
(2)成员函数(非静态)。
编译时:参考引用型变量所属的类中的是否有调用的函数。有,编译通过,没有,编译失败。
运行时:参考的是对象所属的类中是否有调用的函数。
简单说:编译看左边,运行看右边。
原因:成员函数存在覆盖特性。
(3)静态函数。
无论编译和运行,都参考左边;

原因:当类一被加载,静态函数就随类绑定在了内存中。此时,不需要创建对象,就可以使用类名直接调用。同时,父类中的静态成员函数一般是不被复写的。

package day02;

class Fu
{
int num = 3;
void show()
{
System.out.println("fu show");
}
static void method()
{
System.out.println("fu static method");
}
}
class Zi extends Fu
{
int num = 4;
void show()
{
System.out.println("zi show");
}
static void method()
{
System.out.println("zi static method");
}
}
public class DuoTaiDemo2
{
public static void main(String[] args)
{
	System.out.println("调用静态方法时");
Fu.method();
Zi.method();
System.out.println("调用非静态方法时");
Fu f = new Zi();//
f.show();
System.out.println("调用成员变量时时");
System.out.println("多态时:"+f.num);
Zi z = new Zi();
System.out.println("子类对象调用时:"+z.num);
}
}

运行结果:

8.多态应用

需求:
电脑运行实例:
电脑运行基于主板。

接口的出现提高了功能扩展性,接口的引用指向子类对象也提高了程序的扩展性;接口降低了耦合,提高了功能扩展性,提供了规则;

package day02;

/*
接口的出现提高了功能扩展性,接口的引用指向子类对象也提高了程序的扩展性;接口降低了耦合,提高了功能扩展性,提供了规则;
*/
// 接口PCI
interface PCI
{
	void open();
	void close();
}

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

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

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

class Mainboard
{
	//电脑运行
	public static void run()
	{
		System.out.println("Mainboard_run");
	}
	
	//使用扩展功能
	public static void usePCI(PCI p)//PCI p = new NetCard()//接口型引用指向自己的子类对象。
	{
		if(!(p==null))
		{
			p.open();
			p.close();
		}
	}
}

public class DuoTaiDemo3
{
	public static void main(String[] args) 
	{
		Mainboard m =new Mainboard();
		//电脑运行
		m.run();
		//电脑上网
		m.usePCI(new NetCard());

		//电脑听歌
		m.usePCI(new SoundCard());
	}
}

运行结果:


 

二、内部类

内部类的访问规则:
1.内部类可以直接访问外部类的成员,包括私有;
    之所以可以直接访问外部类中的成员 ,是因为内部类中有一个  外部类的引用;格式:外部类名.this
2.外部类要访问内部类,必须建立内部类对象;

3.访问格式:
(1).当内部类定义在外部类的成员位置上,而且非私有,可以在外部其他类中。可以直接建立内部类对象。
格式
    外部类名.内部类名 变量名 = 外部类对象.内部类对象;
    Outer.Inner in   =   new outer().new Inner();

(2).当内部类在成员位置上,就可以被成员修饰符所修饰。
  比如:private:将内部类在外部类中进行封装。
        static:内部类就具备static的特性。
        当内部类被static修饰后,只能直接访问外部类中static的成员,出现了访问局限;

        在外部其他类中,如何访问内部类非静态成员呢?
          new Outer.Inner().function();

        在外部其他类中,如何访问内部类静态成员呢?
          Outer.Inner().function();
 注意:当内部类中定义了静态成员,该内部类必须是静态的;
            当外部类中的静态成员访问内部类时,内部类也必须是static的

举例:

package day02;

class Outer
{
int x=2;

/*
* 当内部类中定义了静态成员时,该内部类必须是static的。
* 静态方法中不可以出现this,因为静态优先于对象存在
* static class Inner
{
	int x=3;
 static void method(){
	int x=1;
	System.out.println(x);
}	
}
*/
 class Inner
{
	int x=3;
 void method(){
	int x=1;
	System.out.println(x);
}	
}
/*
* 当外部类中的静态方法访问内部类时,内部类也必须是static的。
* 静态方法中不可以出现this,因为静态优先于对象存在
*/
static class Inner2
{
void show()
{
System.out.println("inner2 show");	
}
}

public static void function()
{
new Inner2().show();	
}
}

public class InnerDemo {

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

}

运行结果:1
                   inner2 show

2、内部类定义在局部

        内部类定义在外部类中的某个方法中,创建了这个类型的对象时,且仅使用了一次,那么可在这个方法中定义局部类。

        1)不可以被成员修饰符修饰。如publicprivatestatic等修饰符修饰。它的作用域被限定在了声明这个局部类的代码块中

        2)可以直接访问外部类中的成员,因为还持有外部类中的引用。

注意:内部类不可以访问它所在的局部中非最终变量。只能访问被final修饰的局部变量。

 举例:

package day02;

class Outer3 {
	int x = 3;

	void method(final int a) {
		final int i=2;
		 //内部类定义在局部
		class Inner {
			int x = 4;

			void show() {
				System.out.println(a);
			}
		}
		//要想访问非静态show必须有对象;
		new Inner().show();
	}
}

public class InnerDemo3 {

	public static void main(String[] args) {
		Outer3 out = new Outer3();
		out.method(7);
		out.method(9);
		//方法的参数是局部变量,局部变量存放在栈中,out.method(7)结束时,局部变量出栈,在执行下一句;但是当final局部变量入栈后,是不能操作的,

	}

}

运行结果: 7

                   9
注意:

方法中的内部类能不能访问方法中的局部变量,为什么?

 

内部类的生命周期和方法中的局部变量是不一样的,内部类是也是一个类,是存储在堆中,也只有当对该类的引用消失时,内部类才会消亡。而方法的局部变量是存储在堆栈中的,当调用结束时就会退栈,即在内存中这个属性就消失了。也就是说,内部类的生命周期超过了方法中局部变量的生命周期,内部类可能会调用到已经消失的属性,因此内部类不能访问方法中的局部变量。

解决方法就是在局部变量前加修饰符final

编译程序的实现方法:将所有的局部内部类对象要访问的final型局部变量,都拷贝成为该内部类对象中的一个数据成员。这样,即使栈中局部变量(含final)已死亡,但由于它是final,其值永不变,因而局部内部类对象在变量死亡后,照样可以访问final型局部变量。


 3.内部类什么么时候定义?
当描述事物时,事物的内部事物还有事物,该事物用内部类描述。
因为内部类事物在使用外部类事物的内容;

4.匿名内部类:
(1).匿名内部类其实就是内部类的简写格式。

如把下面的代码改成匿名内部类

package day02;
abstract class AbsDemo
{
abstract void show();
}
class Outer4
{
int x=3;
class Inner extends AbsDemo
{
void show() {
	System.out.println("show:"+x);
	
}
}
public void function()
{
new Inner().show();	
}
}
public class InnerDemo5 {

	
	public static void main(String[] args) {
		
		new Outer4().function();
	}

}

更改成匿名内部类的形式:

package day02;
abstract class AbsDemo
{
abstract void show();
}
class Outer4
{
int x=3;

public void function()
{
//new Inner().show();
	new AbsDemo(){
		void show(){
			System.out.println("show:"+x);
		}
	}.show();
}
}
public class InnerDemo5 {

	
	public static void main(String[] args) {
		
		new Outer4().function();
	}

}


(2).定义匿名内部类的前提:
      内部类必须是继承一个类或者接口;

内部类还可以抽象,比如人有心脏,心脏想当于内部类,其他动物也有心脏,就把心脏抽象出来
(3).匿名内部类的格式: new 父类或者接口(){定义子类的内容}
(4).其实匿名内部类(简化书写,覆盖方法)就是一个匿名子类对象;而且这个对象有点胖
  (就是内部类带创建子类对象)
(5).匿名内部类中定义的方法最好不要超过三个;

面试题

1.补足代码

 

package day02;

interface Inter {
	void method();
}

class Test {
	//补足代码
	/*分析:Test是类名,类名调用函数,说明function是static,
	 * 调用function的结果又调用一个非静态方法,说明方法返回的是一个
	 * 对象,而且是Inter类型的对象,因为只有他可以调用method方法

*/
	
	static Inter function()
	{
		return new Inter(){
			public void method()
			{
				System.out.println("method  hahha" );
			}
		};
	}
}

public class InnerDemo4 {

	public static void main(String[] args) {
		Test.function().method();

	}

}


2.如果没有写父类或者接口类,能够用匿名内部类吗?
  可以,父类可以Object;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值