六、Java多态(1)

1 多态

1.1 多态概述

多态:同一个对象(事物),在不同时刻体现出来的不同状态。
	举例:
		猫是猫,猫是动物。
		水(液体,固体,气态)。
		
	多态的前提:
		A:要有继承关系。
		B:要有方法重写。
			其实没有也是可以的,但是如果没有这个就没有意义。
				动物 d = new 猫();
				d.show();
				动物 d = new 狗();
				d.show();
		C:要有父类引用指向子类对象。
			父 f =  new 子();

1.2 多态成员访问特点

多态中的成员访问特点:
	A:成员变量
		编译看左边,运行看左边。
	B:构造方法
		创建子类对象的时候,访问父类的构造方法,对父类的数据进行初始化。
	C:成员方法
		编译看左边,运行看右边。
	D:静态方法
		编译看左边,运行看左边。
		(静态和类相关,算不上重写,所以,访问还是左边的)
		
	由于成员方法存在方法重写,所以它运行看右边。

总结:多态中,父类有的,展现到子类的不同,才可以叫多态,所以说,如果父类都没有,那么肯定编译错误

1.3 多态的优缺点

多态的好处:
		A:提高了代码的维护性(继承保证)
		B:提高了代码的扩展性(由多态保证)
多态的弊端:
		A:不能使用子类的特有功能。

如何解决这个弊端:

多态的弊端:
	不能使用子类的特有功能。
	
我就想使用子类的特有功能?行不行?
	行。
	
怎么用呢?
	A:创建子类对象调用方法即可。(可以,但是很多时候不合理。而且,太占内存了)
	B:把父类的引用强制转换为子类的引用。(向下转型)
	
对象间的转型问题:
	向上转型:
		Fu f = new Zi();
	向上转型时,父类只能调用父类方法或者子类覆写后的方法,而子类中的单独方法则是无法调用的.
	
	向下转型:
		Zi z = (Zi)f; //要求该f必须是能够转换为Zi的。
向下转型容易出现的问题:
ClassCastException:类型转换异常,一般在多态的向下转型中容易出现

2 抽象类

2.1 抽象类概述

抽象类的概述:
		动物不应该定义为具体的东西,而且动物中的吃,睡等也不应该是具体的。
		我们把一个不是具体的功能称为抽象的功能,而一个类中如果有抽象的功能,该类必须是抽象类。
		
抽象类的特点:
	A:抽象类和抽象方法必须用abstract关键字修饰
	B:抽象类中不一定有抽象方法,但是有抽象方法的类必须定义为抽象类
	C:抽象类不能实例化
		因为它不是具体的。
		抽象类有构造方法,但是不能实例化?构造方法的作用是什么呢?
			答:用于子类访问父类数据的初始化
	D:抽象的子类
		a:如果不想重写抽象方法,该子类是一个抽象类。
		b:重写所有的抽象方法,这个时候子类是一个具体的类。
		
	抽象类的实例化其实是靠具体的子类实现的。是多态的方式。
		Animal a = new Cat();
//abstract class Animal //抽象类的声明格式
abstract class Animal {
	//抽象方法
	//public abstract void eat(){} //空方法体,这个会报错。抽象方法不能有主体
	public abstract void eat();
	
	public Animal(){}
}

//子类是抽象类
abstract class Dog extends Animal {}

//子类是具体类,重写抽象方法
class Cat extends Animal {
	public void eat() {
		System.out.println("猫吃鱼");
	}
}

class AbstractDemo {
	public static void main(String[] args) {
		//创建对象
		//Animal是抽象的; 无法实例化
		//Animal a = new Animal();
		//通过多态的方式
		Animal a = new Cat();
		a.eat();
	}
}

2.2 抽象类的成员特点

抽象类的成员特点:
	成员变量:既可以是变量,也可以是常量。
	构造方法:有。
				用于子类访问父类数据的初始化。
	成员方法:既可以是抽象的,也可以是非抽象的。
	
抽象类的成员方法特性:
	A:抽象方法 强制要求子类做的事情。
	B:非抽象方法 子类继承的事情,提高代码复用性。
abstract class Animal {
	public int num = 10;
	public final int num2 = 20;

	public Animal() {}
	
	public Animal(String name,int age){}
	
	public abstract void show();
	
	public void method() {
		System.out.println("method");
	}
}

class Dog extends Animal {
	public void show() {
		System.out.println("show Dog");
	}
}

class AbstractDemo2 {
	public static void main(String[] args) {
		//创建对象
		Animal a = new Dog();
		a.num = 100;
		System.out.println(a.num);
		//a.num2 = 200;
		System.out.println(a.num2);
		System.out.println("--------------");
		a.show();
		a.method();
	}
}

2.3 抽象类的小问题

一个类如果没有抽象方法,可不可以定义为抽象类?如果可以,有什么意义?
	A:可以。
	B:不让创建对象。只能子类访问

abstract不能和哪些关键字共存?
	private	冲突 无法继承
	final	冲突 无法继承	 
	static	冲突  static修饰的方法可以通过类名调用,然而调用一个没有方法体的抽象方法必然出错。
abstract class Fu {
	//public abstract void show();
	//非法的修饰符组合: abstract和private
	//private abstract void show();
	
	//非法的修饰符组合
	//final abstract void show();	
	
	//非法的修饰符组合
	//static abstract void show();
}

3 接口

3.1 接口概述

接口的特点:
	A:接口用关键字interface表示	
		interface 接口名 {}
	B:类实现接口用implements表示
		class 类名 implements 接口名 {}
	C:接口不能实例化
		那么,接口如何实例化呢?
		按照多态的方式来实例化。
	D:接口的子类
		a:可以是抽象类。但是意义不大。
		b:可以是具体类。要重写接口中的所有抽象方法。(推荐方案)

由此可见:
	A:具体类多态(几乎没有)
	B:抽象类多态(常用)
	C:接口多态(最常用)

举个例子:

//定义动物培训接口
interface AnimalTrain {
	public abstract void jump();
}

//抽象类实现接口
abstract class Dog implements AnimalTrain {
}

//具体类实现接口
class Cat implements AnimalTrain {
	public void jump() {
		System.out.println("猫可以跳高了");
	}
}

class InterfaceDemo {
	public static void main(String[] args) {
		//AnimalTrain是抽象的; 无法实例化
		//AnimalTrain at = new AnimalTrain();
		//at.jump();
		
		AnimalTrain at = new Cat();
		at.jump();
	}
}

3.2 接口成员特点

接口成员特点
	成员变量;只能是常量,并且是静态的。
			默认修饰符:public static final
			建议:自己手动给出。
	构造方法:接口没有构造方法。
	成员方法:只能是抽象方法。
			默认修饰符:public abstract
			建议:自己手动给出。
	
所有的类都默认继承自一个类:Object。
类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。

举个例子:

interface Inter {
	public int num = 10;
	public final int num2 = 20;
	public static final int num3 = 30;
	
	//错误: 需要<标识符>
	//public Inter() {}
	
	//接口方法不能带有主体
	//public void show() {}

	//abstract void show(); //默认public
	public void show(); //默认abstract
}

//接口名+Impl这种格式是接口的实现类格式
/*
class InterImpl implements Inter {
	public InterImpl() {
		super();
	}
}
*/

class InterImpl extends Object implements Inter {
	public InterImpl() {
		super();//Object的无参构造
	}
	
	public void show() {}
}

//测试类
class InterfaceDemo2 {
	public static void main(String[] args) {
		//创建对象
		Inter i = new InterImpl();
		System.out.println(i.num);
		System.out.println(i.num2);
		//i.num = 100;
		//i.num2 = 200;
		//System.out.println(i.num); //无法为最终变量num分配值
		//System.out.println(i.num2);//无法为最终变量num2分配值
		System.out.println(Inter.num);
		System.out.println(Inter.num2);
		System.out.println("--------------");
	}
}

3.3 类与类,类与接口,接口与接口

类与类:
	继承关系,只能单继承,可以多层继承。
类与接口:
	实现关系,可以单实现,也可以多实现。
	并且还可以在继承一个类的同时实现多个接口。
接口与接口:
	继承关系,可以单继承,也可以多继承。
interface Father {
	public abstract void show();
}

interface Mother {
	public abstract void show2();
}

interface Sister extends Father,Mother {

}

//class Son implements Father,Mother //多实现
class Son extends Object implements Father,Mother {
	public void show() {
		System.out.println("show son");
	}
	
	public void show2() {
		System.out.println("show2 son");
	}
}

class InterfaceDemo3 {
	public static void main(String[] args) {
		//创建对象
		Father f = new Son();
		f.show();
		//f.show2(); //报错
	
		Mother m = new Son();
		//m.show(); //报错
		m.show2();
	}
}

3.4 抽象类和接口的区别和联系

区别:

A:成员区别
	抽象类:
		成员变量:可以变量,也可以常量
		构造方法:有
		成员方法:可以抽象,也可以非抽象
	接口:
		成员变量:只可以常量
		成员方法:只可以抽象
		
B:关系区别
	类与类
		继承,单继承
	类与接口
		实现,单实现,多实现
	接口与接口
		继承,单继承,多继承
		
C:设计理念区别
	抽象类 被继承体现的是:”is a”的关系。抽象类中定义的是该继承体系的共性功能。
	接口 被实现体现的是:”like a”的关系。接口中定义的是该继承体系的扩展功能。

抽象类可以实现接口:

当你自己写的类想用接口中个别方法的时候(注意不是所有的方法),那么你就可以用一个抽象类先实现这个接口(方法体中为空),然后再用你的类继承这个抽象类,这样就可以达到你的目的了,如果你直接用类实现接口,那是所有方法都必须实现的;

3.5 形式参数和返回值的问题

(1)形式参数:
	类名:需要该类的对象
	抽象类名:需要该类的子类对象
	接口名:需要该接口的实现类对象
(2)返回值类型:
	类名:返回的是该类的对象
	抽象类名:返回的是该类的子类对象
	接口名:返回的是该接口的实现类的对象

4 权限修饰符

权限修饰符
				本类	同一个包下	不同包下的子类	不同包下的无关类
	private		Y
	默认		Y		Y
	protected	Y		Y			Y
	public		Y		Y			Y				Y
常见的修饰符(理解)
(1)分类:
	权限修饰符:private,默认,protected,public
	状态修饰符:static,final
	抽象修饰符:abstract
(2)常见的类及其组成的修饰
	类:
		默认,public,final,abstract
		
		常用的:public
	
	成员变量:
		private,默认,protected,public,static,final
		
		常用的:private
		
	构造方法:
		private,默认,protected,public
		
		常用的:public
	
	成员方法:
		private,默认,protected,public,static,final,abstract
		
		常用的:public
(3)另外比较常见的:
	public static final int X = 10;
	public static void show() {}
	public final void show() {}
	public abstract void show();

5 内部类

5.1 内部类概述

内部类概述:
	把类定义在其他类的内部,这个类就被称为内部类。
	举例:在类A中定义了一个类B,类B就是内部类。

内部的访问特点:
	A:内部类可以直接访问外部类的成员,包括私有。
	B:外部类要访问内部类的成员,必须创建对象。

内部类位置
		成员位置:在成员位置定义的类,被称为成员内部类。	
		局部位置:在局部位置定义的类,被称为局部内部类。		

5.2 成员内部类

成员位置:在成员位置定义的类,被称为成员内部类。

class Outer {
	private int num = 10;
	//成员位置
	/*
	class Inner {
		
	}
	*/
	
	public void method() {
		//局部位置
		class Inner {
		
		}
	}
}

class InnerClassDemo2 {
	public static void main(String[] args) {
		
	}
}

5.3 成员内部类的访问

成员内部类不是静态的:
	外部类名.内部类名 对象名 = new 外部类名.new 内部类名();
成员内部类是静态的:
	外部类名.内部类名 对象名 = new 外部类名.内部类名();

案例1:private 保证安全

成员内部类的修饰符:
	private 为了保证数据的安全性
	static 为了方便访问数据
		注意:静态内部类访问的外部类数据必须用静态修饰。

案例:我有一个人(人有身体,身体内有心脏。)
	
	class Body {
		private class Heart {
			public void operator() {
				System.out.println("心脏搭桥");
			}
		}
		
		public void method() {
			if(如果你是外科医生) {
				Heart h = new Heart();
				h.operator();
			}
		}
	}
	
	按照我们刚才的讲解,来使用一下
	Body.Heart bh = new Body().new Heart();
	bh.operator();
	//加了private后,就不能被访问了,那么,怎么玩呢?
	Body b =  new Body();
	b.method();

案例2:public static 便于访问

class Outer {
	private int num = 10;
	private static int num2 = 100;
	
	//内部类用静态修饰是因为内部类可以看出是外部类的成员
	public static class Inner {
		public void show() {
			//System.out.println(num);
			System.out.println(num2);
		}

		public static void show2() {
			//System.out.println(num);
			System.out.println(num2);
		}		
	}
}

class InnerClassDemo4 {
	public static void main(String[] args) {
		//使用内部类
		// 限定的新静态类
		//Outer.Inner oi = new Outer().new Inner();
		//oi.show();
		//oi.show2();
		
		//成员内部类被静态修饰后的访问方式是:
		//格式:外部类名.内部类名 对象名 = new 外部类名.内部类名();
		Outer.Inner oi = new Outer.Inner();
		oi.show();
		oi.show2();
		
		//show2()的另一种调用方式
		Outer.Inner.show2();
	}
}

5.4 局部内部类

见5.5案例

5.5 局部内部类访问局部变量的注意事项

局部内部类
	A:可以直接访问外部类的成员
	B:在局部位置,可以创建内部类对象,通过对象调用内部类方法,来使用局部内部类功能

面试题:
	局部内部类访问局部变量的注意事项?
	A:局部内部类访问局部变量必须用final修饰
	B:为什么呢?
		局部变量是随着方法的调用而调用,随着调用完毕而消失。
		而这个时候局部对象并没有从堆内存中立即消失。为了能够继续使用这个局部变量,我们加final修饰。
		加入final修饰后,这个变量就成了常量。既然是常量。你消失了。
		我在内存中存储的是数据20,所以,我还是有数据在使用。
class Outer {
	private int num  = 10;
	
	public void method() {
		//int num2 = 20; 局部变量用完消失
		final int num2 = 20;
		class Inner {
			public void show() {
				System.out.println(num);
				//从内部类中访问本地变量num2; 需要被声明为最终类型
				System.out.println(num2);//20
			}
		}
		
		//System.out.println(num2);
		
		Inner i = new Inner();//等待垃圾回收器回收
		i.show();//还会使用到num2,所以num2必须加final
	}
}

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

6 匿名内部类

6.1 匿名内部类概述

匿名内部类
	就是内部类的简化写法。
	这里的类可以是具体类也可以是抽象类或者接口。


我们经常使用的是匿名对象
创建匿名对象的格式:
	new 类名或者接口名(){
		重写方法;
	}
	
匿名对象的本质是什么呢?
	new -> 对象
	重写方法 -> 子类对象
	最终 -> 是一个继承了该类或者实现了该接口的子类的匿名对象。

案例:

interface Inter {
	public abstract void show();
	public abstract void show2();
}

class Outer {
	public void method() {
		//匿名对象调用一个方法的时候
		/*
		new Inter() {
			public void show() {
				System.out.println("show");
			}
		}.show();
		*/
		
		//匿名对象调用二个方法的时候
		//第一种写法:创建两个匿名对象分别调用方法
		/*
		new Inter() {
			public void show() {
				System.out.println("show");
			}
			
			public void show2() {
				System.out.println("show2");
			}
		}.show();
		
		new Inter() {
			public void show() {
				System.out.println("show");
			}
			
			public void show2() {
				System.out.println("show2");
			}
		}.show2();
		*/
		
		//第二种写法,给匿名对象一个名字调用
		Inter i = new Inter() { //多态
			public void show() {
				System.out.println("show");
			}
			
			public void show2() {
				System.out.println("show2");
			}
		};
		
		i.show();
		i.show2();
	}
}

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

6.2 匿名内部类在开发中的使用

举个栗子:

interface Person {
	public abstract void study();
}

class PersonDemo {
	//接口名作为形式参数
	//其实这里需要的不是接口,而是该接口的实现类的对象
	public void method(Person p) {
		p.study();
	}
}

//实现类
class Student implements Person {
	public void study() {
		System.out.println("好好学习,天天向上");
	}
}

class InnerClassTest2 {
	public static void main(String[] args) {
		//测试
		PersonDemo pd = new PersonDemo();
		Person p = new Student();
		pd.method(p);
		System.out.println("--------------------");
		
		//匿名内部类在开发中的使用
		//匿名内部类的本质是继承类或者实现了接口的子类匿名对象
		pd.method(new Person(){
			public void study() {
				System.out.println("好好学习,天天向上");
			}
		});
	}
}

6.3 匿名内部类面试题

匿名内部类面试题:
	按照要求,补齐代码
		interface Inter { void show(); }
		class Outer { //补齐代码 }
		class OuterDemo {
			public static void main(String[] args) {
				  Outer.method().show();
			  }
		}
		要求在控制台输出”HelloWorld”
interface Inter { 
	void show(); 
	//public abstract
}

class Outer { 
	//补齐代码
	public static Inter method() {
		//子类对象 -- 子类匿名对象
		return new Inter() {
			public void show() {
				System.out.println("HelloWorld");
			}
		};
	}
}

class OuterDemo {
	public static void main(String[] args) {
		Outer.method().show();
		/*
			1:Outer.method()可以看出method()应该是Outer中的一个静态方法。
			2:Outer.method().show()可以看出method()方法的返回值是一个对象。
				又由于接口Inter中有一个show()方法,所以我认为method()方法的返回值类型是一个接口。
		*/
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值