澳门新葡亰信誉平台游戏小 汇总

by admin on 2020年1月28日

       
这篇跟昨天的那篇《构造方法的调用顺序》有联系。我们知道,构造方法的调用顺序以及类的初始化顺序,是先父类的构造方法,然后初始化子类的成员变量,最后才是子类的构造方法。不过里面其中会有些特殊情况让你的程序看起来发生错误。先看下面的一段程序:

今天的主要内容是构造方法,static用法,继承,多态,抽象类,接口。由于从今天开始JAVA的学习基本上都是面向对象的内容,虽然这些内容底层的实现是由C++写的编译器实现的,在用法上与C语言已经大相径庭,故不再比较二者的差异。开始阐释JAVA的知识点为主。

对于JAVA中类的初始化是一个很基础的问题,其中的一些问题也是易被学习者所忽略。当在编写代码的时候碰到时,常被这些问题引发的错误,感觉莫名其妙。而且现在许多大公司的面试题,对于这方面的考查也是屡试不爽。不管基于什么原因,我认为,对于java类中的初始化问题,有必要深入的了解。Java类的初始化,其实就是它在JVM的初始化问题(类加载的问题),对于它在JVM中的初始化是一个相当复杂的问题,是给专家们来探讨的,所以在这里我只是对一些容易忽略的问题,发表一下个人观点: 
1,在一个类的内部(不考虑它是另一个类的派生类):很多人认为,类的成员变量是在构造方法调用之后再初始化的,先不考虑这种观点的正确性,先看一下下面的代码:

public

构造方法

[java] view
plaincopy

class A extends Father {

所谓构造方法,就是在创建对象时自动执行的方法,主要用于对成员变量的初始化,当然也可以用于对运行环境的初始化,如加载驱动等。

  1. class Test01…{  
  2.     public Test01(int i)…{  
  3.         System.out.println(“Test01 of constractor : ” + i);  
  4.     }  
  5. }  
  6. public class Test02 …{  
  7.     private Test01 t1 = new Test01(1);  
  8.     private int n = 10;  
  9.       
  10.     public Test02()…{  
  11.         System.out.println(“Test02 of constructor : ” + n);  
  12.     }  
  13.     private Test01 t2 = new Test01(2);  
  14.     public static void main(String[] args) …{  
  15.         Test02 test = new Test02();  
  16.     }  
  17.   
  18. }  
  19. 输出的结果为:  
  20. Test01 of constractor : 1  
  21. Test01 of constractor : 2  
  22. Test02 of constructor : 10  

public int i = 1;

空参构造

通过输出,可见当生成Test02的实例test时,它并不是首先调用其构造方法而是先是成员变量的初始化,而且成员的初始化的顺序以成员变量的定义顺序有关,先定义的先初始化,初始化后再调用构造方法。其实成员变量的初始化,在类的所有方法调用之前进行,包括构造方法 当类中有Static 修饰的成员呢?测试下面一段代码:

A() {

空参构造即以不传递参数的方式下创建对象。如Demo d = new Demo();
就是用空参构造的方式创建对象,Demo后面的括号,就是用来传递参数的,当其为空时,便是空参构造。

[java] view
plaincopy

//i = 5;

空参构造方法

  1. public class Test03 …{  
  2.     private int i1 = printCommon();  
  3.     private static int i2 = printStatic();  
  4.       
  5.     public Test03()…{  
  6.           
  7.     }  
  8.     public static int printCommon()…{  
  9.         System.out.println(“i1 is init!”);  
  10.         return 1;  
  11.     }  
  12.     public static int printStatic()…{  
  13.         System.out.println(“i2 is init!”);  
  14.         return 2;  
  15.     }  
  16.     public static void main(String[] args) …{  
  17.         Test03 t = new Test03();  
  18.     }  
  19. }  
  20.   
  21. 输出结果为:  
  22. i2 is init!  
  23. i1 is init!  

System.

在类中编写的通过无参构造创建对象系统自动执行的方法。

可见static的成员比普通的成员变量先初始化。 我们都知道,如果一个类的成员变量没有在定义时,系统会给予系统默认的值,有=号的就直接给予右值,系统在给予初值和=号给予值这2中方式,在执行时间上有先后吗?为了测试,我编写了如下代码:

out.println(“i=” + i);

空参构造方法没有返回值,连void也没有

[java] view
plaincopy

}

空参构造方法与类同名

  1. public class Test04 …{  
  2.     private static Test04 t1 = new Test04();  
  3.     private static int i1;  
  4.     private static int i2 = 2;  
  5.       
  6.     public Test04()…{  
  7.         i1++;  
  8.         i2++;  
  9.     }  
  10.       
  11.     public static void main(String[] args) …{  
  12.         Test04 t2 = new Test04();  
  13.         System.out.println(“t2.i1 = ” + t2.i1);  
  14.         System.out.println(“t2.i2 = ” + t2.i2);  
  15.     }  
  16. }  
  17. 我们先预计一下输出,可能有几种答案:2和3,3和3,2和2  
  18. 执行代码后:  
  19. t2.i1 = 2  
  20. t2.i2 = 3  

public void p() {

class Demo_constructor {

为什么是2和3呢?其实代码的执行顺序是这样的:首先执行给t1,i1,i2分别给予初始值null,0,0,再执行
Test04 t1 =new
Test04(),这样i1++,i2++被执行,i1,i2都变为1,执行完毕后接着执行int i1;
i1,i2的值仍然是1,1,当执行int i2 = 2时i2被赋予了值,即i1 =
1,i2=2;再执行Test04 t2 = new Test04(),i1,i2再执行++,此时i1 =2,i2
=3,输出i1,i2,结果就是:t2.i1 = 2,t2.i2 = 3。
通过上面的代码我们可以认为系统默认值的给予比通过等号的赋予先执行。
2,一个类还有上层的类,即父类:
     
当生成一个子类时,大家到知道会调用父类的构造方法。如果子类和父类中都有Static的成员变量呢,其实我们在深入分析一个类的内部初始化后,对于存在父类的类的初始化其实原理都一样,具体以下面的代码为例:

System.

public static void main(String[] args) {

**[java] view
plaincopy**

out.println(“i=” + i);

    Person p = new Person();

  1. class SuperClass …{  
  2.     static…{  
  3.         System.out.println(“SuperClass of static block”);  
  4.     }  
  5.       
  6.     public SuperClass()…{  
  7.         System.out.println(“SuperClass of constracutor”);  
  8.     }  
  9. }  
  10.   
  11. public class SubClass extends SuperClass…{  
  12.     static…{  
  13.         System.out.println(“SubClass of static block”);  
  14.     }  
  15.       
  16.     public SubClass()…{  
  17.         System.out.println(“SubClass of constracutor”);  
  18.     }  
  19.       
  20.     public static void main(String[] args)…{  
  21.         SuperClass t = new SubClass();  
  22.     }  
  23. }  
  24. 输出结果:  
  25. SuperClass of static block  
  26. SubClass of static block  
  27. SuperClass of constracutor  
  28. SubClass of constracutor  

}

    System.out.println(p.name+”…”+p.age);        //张三…18

可见当父类,和子类有Static时,先初始化Static,再初始化子类的Static,再初始化父类的其他成员变量->父类构造方法->子类其他成员变量->子类的构造方法。
父类上层还有父类时,总是先执行最顶层父类的Static-->派生类Static-->派生类Static-->…….-->子类Static-->顶层父类的其他成员变量-->父类构造方法-->
派生类的其他成员变量 --> 派生类构造方法-->
……………-->子类其他成员变量-->子类构造方法
讨论到继承,就不得提一下多态:
如果父类构造方法的代码中有子类中被重写得方法,当执行这样的语句
SuperClass super = new SubClass();
初始化时调用父类的构造方法,是执行父类的原方法,还是执行子类中被重写的方法呢?

public static void main(String args[]) {

    }

[java] view
plaincopy

new A();

}

  1. class SuperClass…{  
  2.     public SuperClass()…{  
  3.         System.out.println(“SuperClass of constructor”);  
  4.         m();  
  5.     }  
  6.     public void m()…{  
  7.         System.out.println(“SuperClass.m()”);  
  8.     }  
  9. }  
  10. public class SubClassTest extends SuperClass …{  
  11.     private int i = 10;  
  12.     public SubClassTest()…{  
  13.         System.out.println(“SubClass of constructor”);  
  14.         super.m();  
  15.         m();  
  16.     }  
  17.     public void m()…{  
  18.         System.out.println(“SubClass.m(): i = ” + i);  
  19.     }  
  20.     public static void main(String[] args)…{  
  21.         SuperClass t = new SubClassTest();  
  22.     }  
  23. }  
  24. 可能很多人会认为输出为:  
  25. SuperClass of constructor  
  26. SubClass.m(): i = 10  
  27. SubClass of constructor  
  28. SuperClass.m()  
  29. SubClass.m(): i = 10  
  30. 其实不然!  
  31. 正确输出为:  
  32. SuperClass of constructor  
  33. SubClass.m(): i = 0  
  34. SubClass of constructor  
  35. SuperClass.m()  
  36. SubClass.m(): i = 10  
  37. 在生成对象时,父类调用的M()方法,不是父类的 M()方法,而时子类中被重写了的M()方法!!并且还出现一个怪异的现象,子类的privte  int i 也被父类访问到,这不是和我们说private的成员只能在本类使用的原则相违背了吗?其实我们说的这条原则是编译期间所遵守的,在JAVA程序的编译期间,它只检查语法的合法性,在JAVA的JVM中,即运行期间,不管你声明的什么,对于JVM来说都是透明的,而多态是在运行期间执行的,所以能拿到SubClass的private成员,一点都不奇怪,只是此时还没执行 i = 10,所以在父类的构造方法中调用m()时,系统只能将i赋予系统初值0。  
  38. 下面是我设计的一道完整的初始化例子,可测试你对类的初始化问题是否完整掌握:  
  39. 写出程序运行的结果:  
  40. class A…{  
  41.     private int i = 9;  
  42.     protected static int j;  
  43.     static…{  
  44.         System.out.println(“– Load First SuperClass of static block start!– “);  
  45.         System.out.println(“j = ” + j);  
  46.         System.out.println(“– Load First SuperClass of static block End  — “);  
  47.     }  
  48.       
  49.     public A()…{  
  50.         System.out.println(“——- Load SuperClass of structor start ——–“);  
  51.         System.out.println(“Frist print j = ” + j);  
  52.         j = 10;  
  53.         m();  
  54.         System.out.println(“k = ” + k);  
  55.         System.out.println(“Second print j = ” + j);  
  56.         System.out.println(“———–  Load  SuperClass End    ———– “);  
  57.     }  
  58.       
  59.     private static int k = getInt();  
  60.           
  61.     public static int getInt()…{  
  62.         System.out.println(“Load SuperClass.getInt() “);  
  63.         return 11;  
  64.     }   
  65.     static…{  
  66.         System.out.println(“— Load Second SuperClass of static block!——-“);  
  67.         System.out.println(“j = ” + j);  
  68.         System.out.println(“k = ” + k);  
  69.         System.out.println(“– Load Second SuperClass of static block End — “);  
  70.     }  
  71.       
  72.     public void m()…{  
  73.         System.out.println(“SuperClass.m() , ” + “j = ” +j);  
  74.           
  75.     }  
  76. }  
  77.   
  78. class B extends A …{  
  79.     private  int a = 10;  
  80.       
  81.     static…{  
  82.         System.out.println(“—- Load SubClass of static block!——“);  
  83.         System.out.println(“– Load SubClass of static block End — “);  
  84.     }  
  85.       
  86.     public B()…{  
  87.         System.out.println(“Load SubClass of structor”);  
  88.         m();  
  89.         System.out.println(“—   Load SubClass End  —- “);  
  90.     }  
  91.       
  92.     public void m()…{  
  93.         System.out.println(“SubClass.m() ,” + “a = ” + a );  
  94.     }  
  95. }  
  96.   
  97. public class Test1…{  
  98.     public static void main(String[] args)…{  
  99.         A a = new B();  
  100.     }  
  101. }  
  102. 正确的答案为:  
  103. — Load First SuperClass of static block start!–   
  104. j = 0  
  105. — Load First SuperClass of static block End  —  
  106. Load SuperClass.getInt()  
  107. — Load Second SuperClass of static block!——-  
  108. j = 0  
  109. k = 11  
  110. — Load Second SuperClass of static block End —  
  111. —- Load SubClass of static block!——  
  112. — Load SubClass of static block End —  
  113. ——- Load SuperClass of structor start ——–  
  114. Frist print j = 0  
  115. SubClass.m() ,a = 0  
  116. k = 11  
  117. Second print j = 10  
  118. ———–  Load  SuperClass End    ———–  
  119. Load SubClass of structor  
  120. SubClass.m() ,a = 10  
  121. —   Load SubClass End  —-  

}

class Person{

 

}

    String name;

下面需要说明的一点也是至关重要的一点:那就是成员变量的初始化和非static初始化块之间的执行顺序是按照他们出现的先后顺序来执行的

class

    int age;


Father {

    public Person(){

**[java] view
plaincopy**

public int i;

          name = “张三”;

  1. public class Test04  
  2. {      
  3.     //下面的这两行代码放置的顺序,跟执行结果是有关系的  
  4.     private  String t1 = test();  
  5.       
  6.     {  
  7.        System.out.println(“初始化快!”);  
  8.     }  
  9.     //上面的这两行代码放置的顺序,跟执行结果是有关系的  
  10.       
  11.     private String test(){  
  12.         System.out.println(“实例变量的执行过程”);  
  13.         return “test”;  
  14.     }  
  15.   
  16.     public Test04()  
  17.     {  
  18.         System.out.println(“构造方法!”);  
  19.     }  
  20.   
  21.     public static void main(String[] args)  
  22.     {  
  23.         Test04 t2 = new Test04();  
  24.     }  
  25.       
  26. }  

public void p() { }

          age = 18;

 

Father() {

    }

System.

}

out.println(“Before p()”);

代码中通过空参构造方法Person(),对类中的name和age赋值为张三和18,在主方法中new一个Person对象的时候没有传递参数,系统自动执行空参构造方法,对象中的name和age便有了初值张三和18;

p();

有参构造方法

System.

当创建对象时传递参数,便自动执行有参构造方法。

out.println(“After p()”);

有参构造方法与无参大致相同,只需在编写时写上参数列表即可。

}

class Demo_constructor {

}

  public static void main(String[] args) {

        打印结果是:

      Person p = new Person(“张三”,18);

Before p()

      System.out.println(p.name+”…”+p.age);        //张三…18

i=0

  }

After p()

}

i=1

class Person{

       
首先,Father是A的父类。在Father中,有一个成员变量i,一个方法p(),以及Father的构造方法。A中也有一个成员变量i,并且在定义的时候被初始化为1,同样也有一个打印出i的方法p(),以及A的构造方法。
       
程序刚开始运行的时候,从main进入,创建一个A对象。那么,按照昨天讲的,将会先执行Father的构造方法。在Father的构造方法中,先打印出Before
p(),然后调用了子类的方法p()。注意,这个时候,我们看到打印出来的是i=0而不是i=1!我们在写子类代码的时候,明明将i初始化为1了,为什么会出现i=0的情况呢?因为,这个时候父类的构造方法还没有执行完毕,子类中的成员变量并没有执行i=1这个初始化过程,而只有系统给出的默认值(int型的是0,boolean型的是false,对象引用是null等等)。所以我们可以看到结果中是i=0而不是i=1。继续往下执行,打印出After
p()。这时,父类的构造方法执行完毕。然后就是初始化子类的成员变量,即执行i=1。然后再执行子类A的构造方法,打印出了i=1。
        另外,如果我们去掉类A构造方法的注释,我们会看到如下的结果:

  String name;

Before p()

  int age;

i=0

  public Person(String name,int age){

After p()

      this.name = name;

i=5

      this.age = age;

       
与有注释的结果不同的是最后i=5而不是i=1。这是因为在i被初始化为1之后,我们才执行A的构造方法。而此时构造方法使i=5,然后再打印出i。因此我们看到了i=5。
       
所以,我们可以进一步确定类的初始化与构造方法的调用顺序的关系。这里我们要注意的是,尽量不要在父类的构造方法中调用其他的方法,否则会出现一些意想不到的后果。如果的确需要的话,就调用那些是private或者final的方法,因为这些方法不能被子类继承,不是动态调用的方法。

  }

}

当局部变量名与成员变量名的相同时,局部变量会隐藏成员变量,但在开发中常要求见名知意,对于这种情况,可以用this.来引用成员变量,与局部变量区分开来。

在写类的时候,倘若没有写构造方法,系统会自动加上空参构造,如果写了有参构造方法,没写空参构造方法,系统不再加上空参构造,在创建对象时便不可以用空参的方式创建,因此,在写完有参构造方法后尽量将空参构造方法也写上。

static

static是静态的意思,在类中定义static成员变量和static成员方法,这些方法是存放在内存的方法区中,在创建对象的时候,不需对这些变量和方法分配空间,可以达到资源共享的作用。

静态成员变量

静态成员变量由所有对象共享,在一个对象该变量修改后,所有的对象静态变量都会修改,因为他们其实都在引用同一个变量。

class Demo_static {

  public static void main(String[] args) {

      Person p = new Person();

      p.name = “张三”;

p.country =”中国”;

p.speak();                        //张三…中国

      Person p2 = new Person();

      p2.speak();                        //null…中国

      p2.country = “America”;

      p.speak();                        //张三…America

  }

}

class Person{

  String name;

  static String country;       
//在c语言的解释中就是将country声明周期延长,实现共享,节省空间资源

  public void speak(){

      System.out.println(name+”…”+country);

  }

}

从代码中可以看到,创建对象p之后,将p的name和country修改为张三和中国,然后创建对象p2,虽然没有对p2的属性进行修改,但由于country是static变量,所以,p2的country也为中国,而非静态变量的name为null。然后p2将其变量country修改为America,p的变量country也变为了America。

静态成员方法

将类中的成员方法修饰为static,可以在不创建对象的情况下对方法进行引用,节省空间。其实主方法public
static void main(String[]
arg){}中,main就是JVM识别的入口,声明为static就可以在不创建该类的情况下调用主函数。

class ArrayTool {

    private ArrayTool(){};

    //若类只有静态方法,要设置私有构造方法,防止创建对象,浪费内存

    public static int getMax(int[] arr){

        int max = arr[0];

        for (int i = 1;i < arr.length ;i++ ){

            if (arr[i] > max){

                max = arr[i];

            }

        }

        return max;

    }

}

上面定义的类中含有static方法,在主函数中可以不创建该类的对象而直接调用方法

class Array_Test {

    public static void main(String[] args) {

        int[] arr = {11,22,33,44,55};

        int max = ArrayTool.getMax(arr);

        }

}

下面的内存分配图可以帮助我们更好的理解static的机制,了解static各个特点的原理

类中静态变量与静态方法内存分配

注意事项

共性用static,特性用auto

静态变量又叫类变量,非静态变量又叫对象变量

静态变量随类产生和消失,非静态变量随对象产生和消失

静态只能访问静态

继承

有时候类与类之间有很多相同的成员变量和方法,倘若全部都要重写,代码的复用性太低,不利于开发。怎么通过类与类之间的联系,减少代码量?继承由此诞生。

JAVA的继承通过extends实现,实现继承的类成为子类,被继承的类成为父类。子类继承了父类,相同的成员变量和方法不需重写便可使用,大大提高了代码的复用性。

class Demo_extends {

    //让类与类之间产生关系,提高代码的复用性,维护性,也是多态的前提

    public static void main(String[] args) {

        Cat c = new Cat();

        c.color = “白”;

        c.legs = 4;

        System.out.println(c.color+”…”+c.legs);

    }

}

class Animal{

    String color;

    int legs;

    public void sleep(){

        System.out.println(“sleep”);

    }

}

class Cat extends Animal{

}

通过extends关键字,Cat类继承了Animal类,即使Cat中的代码块为空,仍然可以使用Animal中的成员变量和方法。

JAVA不支持多继承,只支持单继承,即一个子类只有一个父类,因为当两个父类中有同名的方式时会造成冲突,造成安全隐患。

JAVA在创建子类对象的时候,会先访问父类的构造方法,在访问子类的构造方法。从继承的设计理念来不难理解为什么要这么做,因为子类继承父类是为了使用父类的资源,所以在构造子类时要访问父类。

class Demo_extends3 {

  public static void main(String[] args) {

      Son s = new Son();            //这是父类构造,这是子类构造

  }                                //在构造子类时会动访问父类构造

}

class Father{

  int num1;

  int num2;

  public Father(){

      System.out.println(“这是父类构造”);

  }

}

class Son extends Father{

  public Son(){                           
//所有的子类构造方法会默认访问父类的无参构造方法

      //super();                           
//这是一条语句,没写会自动加上,用来访问父类

      System.out.println(“这是子类构造”);

  }

}

这种机制在父类对象没有空参构造的时候会出现错误,因为系统默认的super();语句是访问父类的空参构造方法,在这种情况下,要在子类的构造方法中进行处理,即用super语句访问父类的有参构造方法。

class Demo_extends4 {//关于父类没有无参构造的解决方法

  public static void main(String[] args) {

      Son s = new Son(1);

      System.out.println(“—————–“);

      Son s2 = new Son();

  }

}

class Father{

  int num;

  public Father(int num){

      System.out.println(“父类有参构造”);

  }

}

class Son extends Father{

  public Son(){

      super(1);

      System.out.println(“子类无参构造”);

  }

  public Son(int num){

      super(num);

      System.out.println(“子类有参构造”);

  }

}

对于在创建的时候,是否会创建父类对象,因为要调用父类构造方法,需要一个实例对象。网上的答案鱼龙混杂,有说要的,有说不要的。但从我查阅的资料看,在创建子类对象的时候不会创建父类对象。

从内存的使用来看,倘若在创建子类对象的时候创建父类对象,所有的对象都是Object的子类对象,那么内存必然会被各种Object对象充斥,可用内存少之又少,几乎不可能有可用内存。这与我们的显示显然是冲突的。

但是要使用父类构造方法,必须有一个实例,这个实例在哪?在知乎一个回答中看到,父类,子类的实例其实是同一个,也就是说在访问父类方法的时候,将该实例作为父类对象,在访问子类构造方法的时候,又把这个实例当做子类对象。这个实例中有一部分是地址用super表示,是作为父类的储存区,而其余部分是子类的储存区域,地址用this表示。

子类对象内存图

多态

编译和运行的时候产生不同的形态成为多态。

多态前提:要有继承,要有方法重写,要有父类引用子类对象

对于多态编译与运行时类中元素的规则

成员变量: 编译看左边(父类),运行看左边(父类);

成员方法: 编译看左边(父类),运行看右边(子类);  动态绑定

静态方法: 编译看左边(父类),运行看左边(父类);

class Demo_Polymorthic {

  public static void main(String[] args) {

      Father f = new Son();

      System.out.println(f.num);            //10

      f.print();                            //Son

      f.speak();                            //fat

  }

}

class Demo_Polymorthic {

  public static void main(String[] args) {

      Father f = new Son();

      System.out.println(f.num);            //10

      f.print();                            //Son

      f.speak();                            //fat

  }

}

class Father{

  int num = 10;

  public void print(){

      System.out.println(“Father”);

  }

  public static void speak(){

      System.out.println(“fat”);

  }

}

class Son extends Father  {

  int num = 5;

  public void print(){

      System.out.println(“Son”);

  }

  public static void speak(){

      System.out.println(“so”);

  }

}

多态的好处与应用场景

在方法中,父类对象用作参数,子类方法重写,使得代码的复用性提高

class Demo_Polymorthic3 {

  public static void main(String[] args) {

      make(new Dog());

      make(new Cat());

  }

  public static void make(Animal a){       
//多态常应用于父类作参数,子类方法重写的应用场景

      a.eat();

  }

}

class Animal{

  void eat(){

      System.out.println(“动物吃饭”);

  }

}

class Cat extends Animal{

  void eat (){

      System.out.println(“猫吃鱼”);

  }

  void catchmouse(){

      System.out.println(“捉老鼠”);

  }

}

在多态的情况下,当父类引用要使用子类特有的方法,要将父类引用强制转换向下转型。以下例子也许可以方便理解和记忆。

class Demo_Polymorthic2 {

  public static void main(String[] args) {

      Person p = new SuperMan();

      System.out.println(p.name);                //man

      p.谈生意();                                //谈几个亿的生意

      //p.fly();                               
//p实际上就是子类向上提升

      SuperMan sm = (SuperMan) p;                //向下转型

      sm.fly();

  }

}

class Person{

  String name=”man”;

  public void 谈生意(){

      System.out.println(“谈生意”);

  }

}

class SuperMan extends Person{

  String name = “sup”;

  public void 谈生意(){

      System.out.println(“谈几个亿的生意”);

  }

  public void fly(){

      System.out.println(“去救人”);

  }

}

在上面的代码中,父类引用p
指向子类对象superman,所以在正常引用下,superman就伪装成普通人person,调用成员变量,当然是用伪装的名字man,而当调用成员方法使,实际上就是superman在“”谈生意“,而不是普通的person,故表现其真实的行为,即superman重写的谈生意()这一方法,调用结果是“谈几个亿的大生意”。当要用到superman特有的方法fly()时,必须向下转型成superman,无法再伪装了。

多态内存图解

抽象类

抽象,即无法具体的,所谓抽象类,即用abstract修饰符修饰的类。

abstract 修饰的方法,没有方法体,如 abstarct eat();

抽象类的成员变量不能抽象

抽象类也有构造方法

抽象类的抽象方法使子类必须重写该方法

抽象类可以有非抽象方法,子类可以不重写该方法

抽象类无法创造对象,无法创造出实例,但可以通过多态引用其子类

class Demo_Abstract {

    public static void main(String[] args) {

        Animal a = new Cat();               
//通过多态来引用抽象类,抽象类本身无法创造实例

    }

}

abstract class Animal{

    int num1;                                       
//抽象类成员变量不能抽象

    final int num2 = 10;

    Animal(){

        System.out.println(“抽象类的有参构造”);    //抽象类也有构造方法

    }

    abstract void eat();

    public void method(){                       
//抽象类可以有非抽象方法,子类继承可以不重写

        System.out.println(“抽象类的非抽象方法”);

    }

}

class Cat extends Animal{

    void eat(){                               
//抽象类使得子类必须重写其抽象方法

        System.out.println(“猫吃鱼”);

    }

}

抽象类规定了子类必须重写的方法,即规定了子类都必须有的方法,且要求其根据实际重写。

接口

广义上的接口,就是向外提供或者说制定的一种规则,如usb接口,规定了生产接口的厂商应该使用的规格,线路的电压等规则。

在JAVA中接口可以说是抽象类进一步抽象,对实现接口的类制定了特定的规则。

接口没有构造方法

接口所有的都是抽象方法,所以实现接口,就是要重写接口所规定的方法

接口所有的变量和方法都是public,因为接口的目的就是表露其规则,让类去实现,所以权限要足够大。

类实现接口用implements,值得注意的是一个类可以实现多个接口,因为接口的方法都是抽象的,没有方法体,不会产生冲突,这弥补了单继承的缺陷。

接口与接口之间可以继承

class Demo_Interface {

  public static void main(String[] args) {

      InterA a = new Demo();

  }

}

//设计理念,抽象类是为了规定共性的东西,接口是为了实现扩展性

interface InterA{

  public static final int num = 1;    //接口没有构造方法

  public abstract void printA();        //接口的方法都是抽象方法

}

interface InterB{

  void printB();

}

interface InterC extends InterA,InterB{   
//接口可以继承接口,并且是多继承

}

class Demo implements InterC {    //类可以实现多个接口

  public void printA(){;}

  public void printB(){;}

}

关于抽象类和接口的设计理念,抽象类规定了子类共同需要重写的方法,是规定了子类的共性,而接口的多实现,可以使类具有扩展性,如下面的例子

class Demo_Interface2 {

    public static void main(String[] args) {

        Cat c = new Cat();

        c.eat();

        JumpCat j = new JumpCat();

        j.eat();

        j.jump();

    }

}

abstract class Animal{

    String name;

    abstract void eat();

}

interface Jump{

    void jump();

}

class Cat extends Animal{

    public void eat(){

        System.out.println(“猫吃鱼”);

    }

}

class JumpCat extends Cat implements Jump{

    public void jump(){

        System.out.println(“跳高”);

    }

}

在抽象类Animal中定义了抽象方法eat();因为所有继承他的子类都具有这一行为,必须根据自身重写该方法。而接口Jump定义了jump();这一抽象方法,不同的类根据自己的需求,是否去实现这一接口,去扩展该类的内容。而且由于接口是多实现的,同一个类可以实现不同的接口,展现于其他类不同的行为。

以上便是今天学习的内容中的要点及最应该注意的地方,其中有什么错误的地方,特别是关于内存的分配方面,由于是自己的理解,可能与实际有偏差,希望读者指出并谅解。

               

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图