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

来源:互联网 发布:ubuntu安装到u盘上 编辑:程序博客网 时间:2024/06/02 21:21

 

一、多态

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;/*接口的出现提高了功能扩展性,接口的引用指向子类对象也提高了程序的扩展性;接口降低了耦合,提高了功能扩展性,提供了规则;*/// 接口PCIinterface 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
原创粉丝点击