Java基础10--多态--内部类

10-1,接口的应用

小例子:电脑USB接口的实现

interface USB { //暴露的规则
	public void open();
	public void close();
}
class BookPC {
	public static void main(String[] args) {
		useUSB(new UPan());
		useUSB(new UMouse());
	}
	public static void useUSB(USB u) {
		if(u != null) {
			u.open();
			u.close();
		}
	}
}
//实现规则
//这些设备和电脑的耦合性降低了。
class UPan implements USB {
	public void open() {
		System.out.println("UPan open...");
	}
	public void close() {
		System.out.println("UPan close...");
	}
}
class UMouse implements USB {
	public void open() {
		System.out.println("mouse open...");
	}
	public void close() {
		System.out.println("mouse close...");
	}
}

 

10-2,多态-概述

1,对象的多态性(多种形态):

class 动物{...}
class 猫 extends 动物 {...}
class 狗 extends 动物 {...}

之前我们创建 猫 对象的时候,用的方法是:

猫 mao = new 猫();

有了多态之后,创建 猫 对象可以这么创建:

动物 dw = new 猫();

多态的创建方式,可以看出,一个对象有两种形态。即猫这个事物既具备着猫的形态,又具备着动物的形态,这就是对象的多态性。

简单的说,就是一个对象对应着不同的类型。

 

多态在代码中的体现:父类或者接口的引用指向其子类的对象。

 

10-3,多态-好处

好处是:提高了代码的扩展性,前期定义的代码可以使用后期的内容。

举例说明:

abstract class Animal {
	abstract void eat();
}
class Dog extends Animal {
	//实现父类中的抽象方法,实现动物的共性功能:吃饭
	void eat() {
		System.out.println("啃骨头");
	}
	//定义自己的方法,只有狗才具备的功能:看家
	void lookHome() {
		System.out.println("看家");
	}
}
class Cat extends Animal {
	void eat() {
		System.out.println("吃鱼");
	}
	void catchMouse() {
		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) {
		Cat c = new Cat();
		Dog d = new Dog();
		method(new Pig()); //Animal a = new Pig();
		method(c);
		method(d);
	}
	public static void method(Animal a) {
		a.eat();
	}
}

Cat,Dog,Pig都继承了Animal,是Animal的子类,Animal的变量a指向了创建的Pig对象,调用Pig中的eat()方法,实现了多态性。

 

10-4,多态-弊端&前提

1,多态的弊端:

    前期定义的内容不能使用(调用)后期子类的特有内容。

2,多态的前提:

(1)必须有关系,继承或实现,Animal a = new Cat();Cat要继承Animal。

(2)要有覆盖,不然方法不能用。Cat中的方法要重写Animal中的方法。

弊端:参考10-3的代码:

如:

Cat c = new Cat();
method(c);
...
public static void method(Animal a) {
	a.eat();
	a.catchMouse();
}

创建Cat对象,把c传给method方法,Animal的a指向c,因为在Animal中没有定义catchMouse方法,所以会报错,只能调用Animal中定义过的并且被覆盖实现的方法。也就是不能调用Cat类中的特有内容catchMouse方法。

 

10-5,多态-转型:

1,Animal a = new Cat();

a.eat();

这是自动类型提升,猫对象被提升为动物类型。但是特点功能无法访问。

其作用就是限制对特有功能的访问。

专业讲:向上转型,向上造型,上溯造型。

2,Cat c = (cat)a;

如果还想用具体动物猫的特有功能,可以将该对象进行向下转型。

向下转型的目的是为了使用子类中的特有方法。

Cat c = (Cat)a;//强制转换为Cat类型

c.eat();

c.catchMouse();

3,注意:对子转型,自始至终都是子类对象在做着类型的变化。

Animal a1 = new Pig();

Cat c1 = (Cat)a1;//ClassCastException

类型转换失败,子类间不能转换。

 

10-6,多态-类型判断-instanceof

public static void method(Animal a) {
	a.eat();
	if(a instanceof Cat) { //判断a是不是Cat类型
		Cat c = (Cat)a;
		c.catchMouse();
	} else if (a instanceof Dog) {
		Dog d = (Dog)a;
		d.lookHome();
	}
}

instanceof:用于判断对象的具体类型。只能用于引用数据类型判断。

通常在向下转型前用于健壮性的判断。

向下造型时通常用instanceof判断一下,提高健壮性,若传入的不是相同类型,在转型时会报错。若把Cat改为Animal,则下面些什么都没有用,因为所有的对象都继承于Animal,都属于Animal类型。

 

10-7,多态-成员变量

1,多态时,成员变量的特点:

编译时,参考引用类型变量所属的类中是否有调用的成员变量,有则编译通过,没有则编译失败。

运行时,参考引用型变量所属的类中是否有调用的成员变量,并运行该所属类中的成员变量。

简单说:编译和运行都参考等号左边。

class Fu {
	int num = 3;
}
class Zi extends Fu {
	int num = 4;
}
class DuoTaiDemo {
	public static void main(String[] args) {
		Fu f = new Zi();
		System.out.println(f.num);
	}
}

结果:3

左边为父类,就看父类中的num,若没有,则编译失败,若为Zi f = new Zi();则打印Zi中的num,此时Zi类中不定义num打印为3,因为继承了父类的num。

开发是不会出现此类情况,因为父类中定义的东西直接拿过来用就可以,不用再在子类中定义。

 

10-8,多态-成员函数

1,成员函数(非静态)

编译时,参考引用型变量所属的类中是否有调用的函数,有则编译通过,没有则编译失败。

运行时,参考的是对象所属的类中是否有调用的函数。

简单说:编译看等号左边,运行看等号右边。

class Fu {
	void show() {
		System.out.println("fu show...");
	}
}
class Zi extends Fu {
	void show() {
		System.out.println("zi show...");
	}
}
class DuoTaiDemo {
	public static void main(String[] args) {
		Fu f = new Zi();
		f.show();
	}
}

f为引用变量,Zi为对象,在堆中开辟空间,引用变量f指向Zi对象的地址,调用show方法时,show方法进栈,带有this引用,指向Zi对象的地址,在Zi对象中找有没有show方法,若有,则执行Zi中的show方法,若没有,则在super指向的父类中找show方法,有则执行父类中的show方法,若没有则编译失败。

 

10-9,多态-静态函数

静态函数:

编译时,参考引用变量所属的类中是否有调用的静态方法。

运行时,参考引用变量所属的类中是否有调用的静态方法。

简单说:编译和运行都看等号左边。

其实对于静态方法是不需要有对象的,直接用类名调用即可。

class Fu {
	static void method() {
		System.out.println("fu static method");
	}
}
class Zi extends Fu {
	static void method() {
		System.out.println("zi static method");
	}
}
class DuoTaiDemo {
	public static void main(String[] args) {
		Fu f = new Zi();
		f.method(); //打印fu static method
		Fu.method();
		Zi.method();
	}
}

静态方法加载进静态方法区。Static修饰的函数不受对象控制,不需要创建对象直接用类名调用即可。

Fu f是创建了一个Fu类对象的引用,若用f调用静态方法,则直接执行Fu类静态方法区中的method函数,静态方法中没有this。


10-10,内部类-概述

1,内部类也生成.class文件,文件名格式为:外部类名$内部类名.class。

2,内部类访问特点:

(1)内部类可以直接访问外部类中的成员,包括私有的。

(2)外部类要访问内部类,必须建立内部的对象。

内部类一般用于类的设计。

3,何时使用内部类?

分析事物时,发现该事物描述中还有事物,而且这个事物还在访问被描述事物的内容,这时就将这个内部的事物定义成内部类来描述。

例如:

class Outer {
	private int num = 3;
	class Inner { //内部类
		void show() {
			System.out.println("show run..." + num);
		}
	}
	public void method() {
		Inner in = new Inner(); //在外部类中定义内部类的对象,访问内部类中的内容
		in.show();
	}
}
class InnerClassDemo {
	public static void main(String[] args) {
		Outer out = new Outer();
		out.method();
	}
}

 

10-11,内部类-修饰符

1,内部类可以被修饰符修饰,如private ,public ,static等。

2,例如:

class Outer {
	private static int num = 31; //静态函数访问静态成员
	class Inner { //内部类
		void show() {
			System.out.println("show run ... " + num);
		}
		/*
		static void function() {
			System.out.println("function run ... " + num);
		}
		*/
	}
	public void method() {
		Inner in = new Inner();
		in.show();
	}
}
class InnerClassDemo {
	public static void main(String[] args) {
		Outer out = new Outer();
		out.method();
		//直接访问外部类中的内部类中的成员。
		Outer.Inner in = new Outer().new Inner();
		in.show();
		//如果内部类是静态的,相当于一个外部类
		Outer.Inner in = new Outer.Inner(); //外部类一加载,该静态内部类就存在了。
		in.show();
		//如果内部类是静态的,成员是静态的
		Outer.Inner.function();//静态直接用类名调用
	}
}

10-12,内部类-细节

细节1:

class Outer {
	int num = 3;
	class Inner {
		int num = 4;
		void show() {
			int num = 5;
			System.out.println(num);//打印5
		}
	}
	void method() {
		new Inner.show();
	}
}
class InnerClassDemo {
	public static void main(String[] args) {
		new Outer().method();
	}
}

若内部类的show方法打印this.num,则这个this指代的是Inner类的对象,打印4;

若内部类的show方法打印Outer.this.num,则指代了Outer类的对象,打印3。

 

细节2:

为什么内部类能直接访问外部类中的成员呢?

因为内部类持有了外部类的引用。格式为:外部类名.this,如:Outer.this.num,指的是Outer中的num。

 

10-13,内部类-局部内部类

内部类可以存放在局部位置上。

内部类在局部位置上只能访问局部中被final修饰的局部变量。

例如:

class Outer {
	int num = 3;
	Object method(final int y) {
		final int x = 9;
		class Inner { //局部内部类,在method方法中
			void show() {
				//局部内部类访问局部变量,局部变量必须被final修饰。
				System.out.println("show..." + y);
			}
		}
		Object in = new Inner();
		return in;
	}
}
class InnerClassDemo {
	public static void main(String[] args) {
		Outer out = new Outer();
		Object obj = out.method();
	}
}

10-14,匿名内部类-概述

匿名内部类就是内部类的简写格式。

必须有的前提是:

    内部类必须继承或实现一个外部类或接口。

匿名内部类:

    其实就是一个匿名子类对象。

格式:

    new父类 or 接口() { 子类内容 }

 

例如:

abstract class Demo {
	abstract void show();
}
class Outer {
	int num = 4;
	/* 非匿名方法
	class Inner extends Demo {
		void show() {
			System.out.println("show ... " + num);
		}
	}
	*/
	public void method() {
		//new Inner().show(); //对应上面非匿名方法,
		new Demo { //匿名内部类,继承了外部类Demo
		//new了一个匿名子类对象并调用show方法,子类中重写Demo的抽象方法
			void show() {
				System.out.println("show ..." + num);
			}
		}.show();
	}
}
class InnerClassDemo {
	public static void main(String[] args) {
		new Outer().method();//new 一个Outer对象并调用其method方法。
	}
}

10-15,匿名内部类-应用

使用场景:

当函数参数是接口类型时,而且接口中的方法不超过三个。

可以用匿名内部类作为实际参数进行传递。

class InnerClassDemo {
	class Inner {}
	public static void main(String[] args) {
		/* 接口类型参数传递,在传递时直接实现方法
		show(new Inter(){
			public void show1() {
				System.out.println("show1...");
			}
			public void show2() {
				System.out.println("show2...");
			}
		});
		*/
		new Inner();
	}
	public void method() {
		new Inner();
	}
	public static void show(Inter in) {//接口形参
		in.show1();
		in.show2();
	}
}
interface Inter {
	void show1();
	void show2();
}
class Outer {
	/* 非匿名方式
	class Inner implements Inter {
		public void show1() {
			System.out.println("show1...");
		}
		public void show2() {
			System.out.println("show2...");
		}
	}
	*/
	public void method() {
		Inner in = new Inner();
		in.show1();
		in.show2();
		//给匿名内部类起个名字,用该名字调用里面的show1,show2方法。
		Inter in = new Inter() {
			public void show1() {
				System.out.println("show1...");
			}
			public void show2() {
				System.out.println("show2...");
			}
		};
		in.show1();
		in.show2();
	}
}

若匿名内部类中只有一个方法,可以这么调用:

new Inter() {
	public void show() {
		System.out.println("show run ... ");
	}
}.show();

 

10-16,匿名内部类-细节

class Outer {
	void method() {
		//前面的Object表示父类对象,后面的Object表示子类对象。
		//这里是多态,把new Object向上转型为Object型,编译时看等号左边,
		//因为Object类中没有show方法,所以编译失败。
		Object obj = new Object() {
			public void show() {
				System.out.println("show run ... ");
			}
		};
		obj.show(); // 报错,找不到show方法。
	}
}
class InnerClassDemo {
	public static void main(String[] args) {
		new Outer().method();
	}
}

报错原因:因为匿名内部类这个子类对象被向上转型为了Object类型,这样就不能再使用子类的特有方法了。

 

10-17,对象的初始化过程:

代码示例:

class Fu {
	int num = 9;
	{ // 构造代码块
		System.out.println("Fu"); //第一步:打印 Fu
	}
	Fu() {
		super();
		//显示初始化
		//构造代码块初始化
		show();
	}
	void show() {
		//这个show会被子类的show覆盖
		System.out.println("fu show " + num); //第二步:打印Zi类中的show:Zi show 0
	}
}
class Zi extends Fu {
	int num = 8;
	{
		System.out.println("Zi"); //第三步:打印 Zi
	}
	Zi() {
		super();
		//显示初始化
		//构造代码块初始化
		show();
	}
	void show() {
		System.out.println("Zi show " + num); //第四步:打印Zi show 8
	}
}
public class Demo {
	public static void main(String[] args) {
		new Zi();
	}
}

步骤:

(1)Demo类加载进方法区,Demo的构造函数加载进方法区。

(2)main方法加载进静态方法区,main进栈。

(3)new Zi()在堆中创建子类对象,在这个内存块中开辟两块空间,分别为Zi的num = 0,Fu的num = 0。

(4)Fu类先加载进方法区,Zi类后加载进方法区。

(5)new Zi();时调用Zi的构造函数,Zi中的super调用Fu的构造函数,Fu()先执行super这里super调用的是Object,然后执行成员变量的显示初始化,然后执行本类中构造代码块的初始化,这时输出Fu,然后再执行show方法,由于Zi继承了Fu,并且重写了Fu中的show方法,所以这类输出Zi类中的show方法,输出Zi show 0。因为Zi类还未进行显示初始化,所以输出默认初始值0。

(6)Zi()中的super()运行完毕,进行Zi类成员变量的显示初始化,这时Zi中的num=8,再进行Zi类构造代码块的初始化,输出Zi,再执行show方法,这时因为已经进行了显示初始化,所以输出Zi show 8。

(7)main方法弹栈,运行结束。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值