面向对象_多态、内部类、其它

来源:互联网 发布:mac怎么从u盘启动 编辑:程序博客网 时间:2024/05/22 14:02

------- android培训java培训、期待与您交流! ----------


1, 多态(Polymorphism)

        Java引用变量有两个类型:一个是编译时的类型,一个是运行时的类型,编译时的类型由声明该变量时使用的类型决定,运行时的类型由实际赋给该变量的对象决定。如果编译时类型和运行时类型不一致,就会出现所谓的多态。

2, 多态的演示:

[java] view plaincopyprint?
  1. class BaseClass 
  2.     public int book =6
  3.     public void base() 
  4.     { 
  5.         System.out.println("父类的普通方法"); 
  6.     } 
  7.     public void test() 
  8.     { 
  9.         System.out.println("父类的被覆盖的方法"); 
  10.     } 
  11. class  SubClass extends BaseClass 
  12.     public String book ="Java核心技术";             //重新定义一个book实例属性覆盖父类的book实例属性 
  13.     public void test() 
  14.     { 
  15.         System.out.println("子类的覆盖父类的方法"); 
  16.     } 
  17.     public void sub() 
  18.     { 
  19.         System.out.println("子类的普通方法"); 
  20.     } 
  21.     public staticvoid main(String[] args)  
  22.     { 
  23.         BaseClass bc = new BaseClass();      //编译时类型和运行时类型完全一样,不存在多态 
  24.         System.out.println(bc.book);          //输出6 
  25.         bc.base();                            //两次都执行BaseClass的方法 
  26.         bc.test(); 
  27.         System.out.println(); 
  28.         SubClass sc = new SubClass();        //编译时类型和运行时类型完全一样,不存在多态 
  29.         System.out.println(sc.book);          //输出“Java核心技术” 
  30.         sc.base();                            //调用从父类继承到的base方法 
  31.         sc.test();                            //调用当前类的test方法 
  32.         System.out.println(); 
  33.         BaseClass ploymorphicBc = new SubClass(); //编译时和运行时的类型不同,多态 
  34.         System.out.println(ploymorphicBc.book);    //输出6,表明访问的是父类属性 
  35.         ploymorphicBc.base();                      //调用父类继承到的base方法 
  36.         ploymorphicBc.test();                      //调用当前类的test方法 
  37.         //ploymorphicBc.sub();       //编译出错,因为变量编译时是BaseClass类型,BaseClass类没有sub方法 
  38.     } 
运行结果:

2.1,说明:子类其实是一种特殊的父类,因此Java允许把一个子类对象直接赋给一个父类引用变量,无需任何类型转换,或者被称为向上转型(upcasting),

向上转型由系统自动完成。

        当把一个子类对象直接赋给父类引用变量,例如上面的BaseClassploymorphicBc = new SubClass();这个ploymorphicBc引用变量的编译时类型是BaseClass,而运行时类型是SubClass,当运行时调用该引用变量的方法时,其方法行为总是像子类方法的行为,而不是像父类的方法行为,这将出现相同类型的变量、执行同一个方法时呈现出不同的行为特征,这就是多态。

2.2,注意:

        引用变量在编译阶段只能调用其编译时类型所具有的方法,但运行时则执行它运行时类型所具有的方法。因此,编写Java代码时,引用变量只能调用声明该变量时所用类里包含的方法。例如我们通过Object p = new Person()代码定义一个变量p,则这个p只能调用Object类的方法,而不能调用Person类里定义的方法。

        属性不具备多态性。通过引用变量来访问其包含的实例属性时,系统总是试图访问它编译时所定义的属性,而不是它运行时类所定义的属性。

3, 多态的总结

3.1,多态的体现:

  父类引用指向自己的子类对象;

  父类引用也可以接收自己子类的对象;

3.2,多态的前提:

   必须是类与类之间有关系,要么继承,要么实现;

   通常还有一个前提:覆盖;

3.3,多态的好处:

   多态的出现大大提高了程序的扩展性;

3.4,多态的弊端:

   提高了扩展性,但是只能使用父类的引用访问父类中的成员。

3.5,在多态中成员函数的特点:

在编译时期:参阅引用变量所属的类中是否有调用的方法,如果有,编译通过,如果没有则编译失败。

在运行时期:参阅对象所属类中是否有调用的方法。

简单总结就是:在多态中调用成员函数时,编译看左边,运行看右边。

在多态中,成员变量的特点:

无论编译还是运行,都参考左边(引用型变量所属的类)。

在多态中,静态成员函数的特点:

无论编译还是运行,都参考左边。

示例代码:

[java] view plaincopyprint?
  1. class Fu 
  2.     void method1() 
  3.     { 
  4.         System.out.println("fu method_1"); 
  5.     } 
  6.     void method2() 
  7.     { 
  8.         System.out.println("fu method_2"); 
  9.     } 
  10.     static void method4() 
  11.     { 
  12.         System.out.println("fu method_4"); 
  13.     } 
  14. class Zi extends Fu 
  15.     void method1() 
  16.     { 
  17.         System.out.println("zi method_1"); 
  18.     } 
  19.     void method3() 
  20.     { 
  21.         System.out.println("zi method_3"); 
  22.     } 
  23.     static void method4() 
  24.     { 
  25.         System.out.println("zi method_4"); 
  26.     } 
  27. class DuoTaiDemo2  
  28.     public staticvoid main(String[] args)  
  29.     { 
  30.         Fu f = new Zi(); 
  31.  
  32.         f.method1(); 
  33.         f.method2(); 
  34.         //f.method3();  //编译不通过 
  35.  
  36.         f.method4();    //打印“fu method_4” 
  37.         Zi z = new Zi(); 
  38.         z.method4(); 
  39.     } 

4, 引用变量的强制类型转换

        编写Java程序时,引用变量只能调用它编译时类型的方法,而不能调用它运行时类型的方法,即使它实际所引用对象确实包含该方法。

如果需要让这个引用变量来调用它运行时类型的方法,则必须把它强制类型转换成运行时类型,强制类型转换需要借助类型转换运算符。

类型转换运算符是(),类型转换运算符的用法如下:(type)variable,这种用法可以将variable变量转换成一个type类型的变量。

        注意:引用类型之间的转换只能把一个父类变量转换成子类类型,如果是两个没有任何继承关系的类型,则无法进行类型转换,否则编译时就会出错。如果试图把一个父类实例转换子类类型,则必须这个对象实际上是子类实例才行(即编译时类型为父类类型,而运行时类型是子类类型),否则将在运行时引发ClassCastException异常。

        当把子类对象赋给父类引用变量时,被称为向上转型(upcasting),这种转型总是可以成功的,这也是从另一个侧面证实了子类是一种特殊的父类,这种转型只是表明这个引用变量的编译类型是父类,但实际执行它的方法时,依然表现出子类对象的行为方式,但把一个父类对象赋给子类引用变量时,就需要进行强制类型转换,而且还可能在运行时产生ClassCastException异常,使用instanceof运算符可以让强制类型转换更安全。

5, instanceof运算符:

        instanceof运算符的前一个操作数通常是一个引用类型的变量,后一个操作数是一个类(也可以是接口,可以把接口理解成一种特殊的类),它用于判断前面的对象是否是后面的类,或者其子类、实现类的实例。如果是,则返回true,否则返回false。

注意:instanceof运算符前面的操作数的编译类型要么与后面的类相同,要么是后面类的父类,否则会引起编译错误。

示例代码:

[java] view plaincopyprint?
  1. class InstanceofDemo 
  2.     public staticvoid main(String[] args) 
  3.     { 
  4.         Object hello = "Hello"
  5.         //String是Object的子类,所以返回true 
  6.         System.out.println("字符串是否是Object类的实例:"+(helloinstanceof Object)); 
  7.         System.out.println("字符串是否是String类的实例:"+(helloinstanceof String)); 
  8.         System.out.println("字符串是否是Math类的实例:"+(helloinstanceof Math)); 
  9.         System.out.println("字符串是否是Comparable接口的实例:"+(helloinstanceof Comparable)); 
  10.         String a = "Hello"
  11.         //string类既不是Math类,也不是Math类的父类,所以下面的编译出错,报错:不可转换的类型 
  12.         //System.out.println("字符串是否是Math类的实例:"+(a instanceof Math)); 
  13.     } 
运行结果:

instanceof运算符的作用是:在执行强制类型转换之前,首先判断前一个对象是否是后一个类的实例,是否可以成功转换,从而保证代码更加健。

        instanceof和(type)是Java提供的两个相关的运算符,通常先用instanceof判断一个对象是否可以强制类型转换,然后再使用(type)运算符进行强制类型转换,

从而保证程序不会出现错误。

6, 内部类

        大部分时候,我们把类定义在一个独立的程序单元。在某些情况下,我们把一个类放在另一个类的内部定义,这个定义在其他类内部的类就被称为内部类(有时也叫嵌套类),包含内部类的类也被称为外部类(有时也叫宿主类)。内部类主要有如下作用:

        》》内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许用一个包中的其他类访问该类。

       》》内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员之间可以相互访问。但外部类不能访问内部类的实现细节,例如内部类的属性。

      》》匿名内部类适合用于创建那些仅需要一次使用的类。

6.1,非静态内部类和静态内部类

先看示例代码:

[java] view plaincopyprint?
  1. class Outer 
  2.     private int Num=3
  3.     //定义一个内部类 
  4.     class Inner 
  5.     { 
  6.         void function() 
  7.         { 
  8.             System.out.println("Inner:"+Num); //内部类中可以直接访问外部类的内容(Num) 
  9.         } 
  10.     } 
  11.     /*
  12.     class Inner
  13.     {
  14.         int Num=4;
  15.         void function()
  16.         {
  17.             int Num=6;
  18.             System.out.println("Inner:"+Num);  //输出6,以局部变量为主
  19.             System.out.println("Inner:"+this.Num);  //输出4,以当前类成员为主
  20.             System.out.println("Inner:"+Outer.this.Num);  //输出6,此种方式为默认方式
  21.         }
  22.     }
  23.     */ 
  24.     void method() 
  25.     { 
  26.         Inner in=new Inner(); 
  27.         in.function(); 
  28.     } 
  29. class Outer2 
  30.     private staticint Num=3
  31.     static class Inner2    //静态内部类 
  32.     { 
  33.         void function2() 
  34.         { 
  35.             System.out.println("Inner2:"+Num); 
  36.         } 
  37.     } 
  38. class Outer3 
  39.     private staticint Num=3
  40.     static class Inner3    //静态内部类 
  41.     { 
  42.         static void function() 
  43.         { 
  44.             System.out.println("Inner2:"+Num); 
  45.         } 
  46.     } 
  47. class InnerClassDemo  
  48.     public staticvoid main(String[] args)  
  49.     { 
  50.         //第一种访问方式,通过外部类访问内部类的成员 
  51.         Outer ou=new Outer(); 
  52.         ou.method(); 
  53.         //第二种访问方式,直接访问内部类成员 
  54.         Outer.Inner in=new Outer().new Inner(); 
  55.         in.function(); 
  56.         /*第二种访问方式仅出现在面试中,实际开发很少使用,因为内部类作为外部类的成员,经常会被私有化(private)*/ 
  57.      
  58.         //在外部类中访问静态内部类的非静态成员(实例看Outer2) 
  59.         new Outer2.Inner2().function2(); 
  60.         //在外部类中访问静态内部类的静态成员(实例看Outer3) 
  61.         Outer3.Inner3.function(); 
  62.     } 
运行结果:

6.1.1,内部类访问规则:

6.1.1.1,内部类可以直接访问外部类的成员,包括私有;

      内部类可以访问外部类的成员,是因为内部类中持有一个外部类的引用,格式:外部类名.this.

6.1.1.2,外部类要访问内部类,必须建立内部类的对象;

6.1.2,访问格式:

6.1.2.1,当内部类定义在外部类的成员位置上,而且非私有,可以在外部类中直接定义内部类对象,格式:

    外部类名.内部类名变量名=外部类对象.内部类对象

       Outer.Innerin=new Outer().new Inner();

6.1.2.2,当内部类在成员位置上,就可以被成员修饰符所修饰

   比如,private:将内部类在外部类中封装

               static:内部类就具有static特性

               当内部类被static修饰后,只能直接访问外部类中的static成员,出现访问局限

              在外部其他类中,如何直接访问static内部类的非静态成员呢?

               new Outer2.Inner2().function2();


               在外部其他类中,如何直接访问static内部类的静态成员呢?

               Outer3.Inner3.function();

       注意:当内部类中定义了静态成员,该内部类必须是静态的。

                   当外部类中的静态方法访问内部类时,内部类也必须是static的

6.1.3,内部类的应用:

当描述事物时,事物的内部还有事物,该事物用内部类来描述,因为内部事物在使用外包园事物的内容。

6.2,局部内部类

内部类定义在局部时

6.2.1,不可以被成员修饰符修饰

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

   但是不可以访问它所在局部中的变量,只能访问被final修饰的局部变量

示例代码:

[java] view plaincopyprint?
  1. class Outer 
  2.     int x=3
  3.     void method() 
  4.     { 
  5.         final int y=4;    //在内部类中访问局部变量,需要被声明为最终类型 
  6.         class Inner 
  7.         { 
  8.             void function() 
  9.             { 
  10.                 System.out.println(Outer.this.x); 
  11.                 System.out.println(y); 
  12.             } 
  13.         } 
  14.         new Inner().function(); 
  15.     } 
  16.  
  17.     void method2(finalint a) 
  18.     { 
  19.         final int y=4;    //在内部类中访问局部变量,需要被声明为最终类型 
  20.         class Inner 
  21.         { 
  22.             void function() 
  23.             { 
  24.                 System.out.println(a); 
  25.             } 
  26.         } 
  27.         new Inner().function(); 
  28.     } 
  29. class InnerClassDemo2  
  30.     public staticvoid main(String[] args)  
  31.     { 
  32.         new Outer().method(); 
  33.         Outer out=new Outer(); 
  34.         out.method2(7);  //这是method2方法进栈,运算完以后出栈,虽a被final修饰,但对下一次调用method2无影响 
  35.         out.method2(8);  //method2再一次进栈,此时的a与上一次的a不同 
  36.     } 
运行结果:

6.3,匿名内部类

6.3.1,匿名内部类其实就是内部类的简写格式;

6.3.2,定义匿名内部类的前提:内部类必须是继承一个类或者实现一个接口

6.3.3,匿名内部类的格式:new 父类或者接口(){定义子类的内容};

6.3.4,其实匿名内部类就是一个匿名子类对象,而且这个对象有点胖,可以理解为带内容的对象

6.3.5,匿名内部类中定义的方法最好不超过3个

示例代码:

[java] view plaincopyprint?
  1. abstract class AbsDemo 
  2.     abstract void show(); 
  3. /*
  4. //一般内部类
  5. class Outer
  6. {
  7.     int x=3;
  8.     class Inner extends AbsDemo
  9.     {
  10.         void show()
  11.         {
  12.             System.out.println("show:"+x);
  13.         }
  14.     }
  15.     public void function()
  16.     {
  17.         new Inner().show();
  18.     }
  19. }
  20. */ 
  21. //匿名内部类 
  22. class Outer 
  23.     int x=3
  24.  
  25.     public void function() 
  26.     { 
  27.         new AbsDemo() 
  28.         { 
  29.             void show() 
  30.             { 
  31.                 System.out.println("x==="+x); 
  32.             } 
  33.             void abc() 
  34.             { 
  35.                 System.out.println("haha"); 
  36.             } 
  37.         }.show(); 
  38.  
  39.         new AbsDemo() 
  40.         { 
  41.             void show() 
  42.             { 
  43.                 System.out.println("x==="+x); 
  44.             } 
  45.             void abc() 
  46.             { 
  47.                 System.out.println("haha"); 
  48.             } 
  49.         }.abc(); 
  50.         /*
  51.         AbsDemo a=new AbsDemo()
  52.         {
  53.         void show()
  54.         {
  55.             System.out.println("x==="+x);
  56.         }
  57.         void abc()
  58.         {
  59.             System.out.println("haha");
  60.         }
  61.         };
  62.         a.show();    //可以
  63.         //a.abc();   //编译失败,AbsDemo类中没有定义abc()
  64.         */ 
  65.     } 
  66.  
  67. class InnerClassDemo3  
  68.     public staticvoid main(String[] args)  
  69.     { 
  70.         new Outer().function();  
  71.     } 
运行结果:

总结:今天自学的内容在上面列出,知识点不是很多,代码量稍多,看代码区理解知识点比单纯的看知识点清单效果要好的多。

        1, 理解好多态,用自己的话总结起来,就是“父类引用指向子类对象,编译时看左边(父类),运行时看右边(子类对象)”,

这样就好理解为什么调用在子类有在父类没有的方法时,编译出现错误了。另外注意,属性不具备多态性!

         2, 虽然内部类不常用,但是作为Java的一部分,还是要掌握好,例如静态内部类,还是那句话,静态不能访问非静态。

还有各种内部类的访问方式,这个在面试中出现率很高,掌握是必须的。

        3, 理解匿名内部类,以及匿名内部类的用法,匿名内部类适合用于创建那些仅需要一次使用的类。

还有匿名内部类必须继承一个类或者实现一个接口,

实现多个接口不可以。

        4, 想到了一个问题,关于Object类中的equals方法。这个问题在黑马官方论坛上被多人多次提到,在这里我得加强一下理解,看示例代码:

[java] view plaincopyprint?
  1. class TestEqual  
  2.     public staticvoid main(String[] args)  
  3.     { 
  4.         int it=65
  5.         float ft=65.0f; 
  6.         System.out.println("65和65.0f是否相等? "+(it==ft));  //此处返回true 
  7.         char ch='A'
  8.         System.out.println("65和A是否相等? "+(it==ch));     //此处返回true 
  9.         String str1=new String("hello"); 
  10.         String str2=new String("hello"); 
  11.         System.out.println("str1和str2是否相等? "+(str1==str2));   //此处返回false 
  12.         System.out.println("str1和str2是否相等? "+str1.equals(str2)); //此处返回true 
  13.     } 

              总结:

                         当两个基本类型的变量的数值相等,使用==,结果返回true

                         但对于引用类型变量,它们必须指向同一个对象时,结果才返回true

                         String类的equal方法只要求两个字符串的字符序列相同,就返回true

             

         5, Object类提供的toString方法总是返回“该对象实现类的类名+@+hashCode值”。

这个返回值不能真正实现“自我描述”的功能,因此如果想要自定义类能实现“自我描述”的功能,必须重写Object类的toString方法。

原创粉丝点击