一、多态
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)不可以被成员修饰符修饰。如public、private、static等修饰符修饰。它的作用域被限定在了声明这个局部类的代码块中
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;