方法能重写,属性能重写吗?

来源:互联网 发布:淘宝怎么设置秒杀价 编辑:程序博客网 时间:2024/04/27 15:58

覆写是多态的一种表现,我们平时所说的覆写一般是针对方式来说,在网上看到过有人讨论试着覆写属性,于是有点兴趣,属性真能覆写吗?回答问题之前,我们还是回忆一下方法的覆写具备哪些条件,或都说哪些方法能覆写。
先回顾一下方法覆写要注意的地方:
1、重载(也叫过载)时只与方法特征有关,但重写(覆写)是会进一步检查两个方法的返回类型是否相同、访问修饰权限是否缩小(假设public->protected方向是缩小,反之叫扩大)和抛出的异常范围是否扩大。那么什么是方式特征呢?一个方法的特征(也可叫方法签名)仅包括方法的名字、参数的个数、类型、顺序(实质上就是参数列表),而不包括方法的返回类型、访问修饰权限与所抛出的异常。

[java] view plaincopy
  1. public class A {  
  2.     public void overwrite(int i) throws IOException {}  
  3. }  
  4.   
  5. class B extends A {  
  6.     // !! 编译通不过,不能缩小访问权限  
  7.     //    void overwrite(int i) throws IOException {}  
  8.   
  9.     // !! 编译通不过,不能扩大异常范围  
  10.     //    public void overwrite(int i) throws Exception {}  
  11.   
  12.     // 正常,编译没问题,可以不抛出异常  
  13.     // public void overwrite(int i) {}  
  14.   
  15.     // 覆写父类方法  
  16.     public void overwrite(int i) throws IOException {}  
  17.   
  18.     protected void overload(int i) {}  
  19.   
  20.     //重载上面的方法  
  21.     int overload(long i) throws IOException {  
  22.         return 0;  
  23.     }  
  24. }  

另外,要补充说明的是重载一般是指在同一个类中多个方法间,但也可重父类的的方法,而重写只发生面父子与子类的方法间。

2、非私有非静态方法不能被任何静态方法覆写,如果子类中试着以静态方式(不管访问权限修饰符是什么)来覆写父类的方法,编译时会报错。

[java] view plaincopy
  1. public class A {  
  2.     void f() {}  
  3. }  
  4.   
  5. class B extends A {  
  6.     // !! 编译出错,下面错误都一样  
  7.     //    static void f(){};  
  8.     //    private static void f(){};  
  9.     //    protected static void f(){};  
  10.     //    public static void f(){};  
  11. }  

3、非私有静态方法不能被任何非静态方法覆写,如果子类中试着以非静态方式(不管访问权限修饰符是什么)来覆写父类的方法,编译时会报错。

[java] view plaincopy
  1. public class A {  
  2.     public static void f() {}  
  3. }  
  4.   
  5. class B extends A {  
  6.     // !! 编译出错,下面错误都一样  
  7.     //    public void f(){}  
  8.     //    protected void f() {}  
  9.     //    void f() {}  
  10.     //    private void f() {}  
  11. }  

4、子类与父类中有方法特征相同的静态方法时,覆写规则与非静态方法覆写规则一样,但一般我们不把静态方法的覆写叫覆写,虽然语法规则上与非静态方法相同。

[java] view plaincopy
  1. public class A {  
  2.     public static void overwrite(int i) throws IOException {  
  3.         System.out.println("父类静态方法...");  
  4.     }  
  5. }  
  6.   
  7. class B extends A {  
  8.     // !! 编译通不过,不能缩小访问权限  
  9.     //    static void overwrite(int i) throws IOException {}  
  10.   
  11.     // !! 编译通不过,不能扩大异常范围  
  12.     //    public static void overwrite(int i) throws Exception {}  
  13.   
  14.     // 正常,编译没问题,可以不抛出异常  
  15.     // public static void overwrite(int i) {}  
  16.   
  17.     // 覆写父类静态方法  
  18.     public static void overwrite(int i) throws IOException {  
  19.         System.out.println("子类静态方法...");  
  20.     }  
  21.   
  22.     public static void main(String[] args) throws IOException {  
  23.         A a = new B();  
  24.         //覆写静态在语法上是合法的,但没有多态效果  
  25.         a.overwrite(0);//print: 父类静态方法...  
  26.         A.overwrite(0);//print: 父类静态方法...  
  27.         B.overwrite(0);//print: 子类静态方法...  
  28.     }  
  29. }  

上面重写了父类的静态方法,但却没有多态作用,也就是说,静态方法不存在重写这一说,只不过在语法规则上与非静态方法一样罢了。

5、私有方法对子类同名方法不产生任何影响,也就是说私有方法不能被重写,即使试着在子类覆写了父类的私有方法,不管访问权限修饰符是什么,在编译时也不会报错。原因就是私有方法对子类也是不可见的。

[java] view plaincopy
  1. public class A {  
  2.     private void f() {  
  3.     }  
  4.   
  5.     // 加上static修饰符,其结果都一样  
  6.     // private static void f() {}  
  7. }  
  8.   
  9. class B extends A {  
  10.     // 尝试每个方式,都会编译能过  
  11.     // private void f() {}  
  12.     // void f() {}  
  13.     // protected void f() {}  
  14.     // public void f() {}  
  15.   
  16.     // private static void f() {}  
  17.     // static void f() {}  
  18.     // protected static void f() {}  
  19.     public static void f() {}  
  20. }  

好了,上面总结并理解方法覆后,我们再来看属性能不能覆写,其实,这是一个很无聊的问题,不像上面讨论方法那有点价值,可以说属性是没有覆写这一说的,这有点像静态方法一样,不过还是稍微看一下:

[java] view plaincopy
  1. public class A {  
  2.     // 这行不能去掉,否则①编译通不过  
  3.     int i = 1;  
  4.   
  5.     A() {  
  6.         System.out.println("父类构造方法 i=" + this.i);//①  
  7.         this.f();//②  
  8.         System.out.println("父类中 " + (this == getThis()));//③ print:true  
  9.     }  
  10.   
  11.     public void f() {  
  12.         System.out.println("父类方法f() i=" + this.i);  
  13.     }  
  14.   
  15.     public A getThis() {  
  16.         System.out.println("父类getThis");  
  17.         return this;  
  18.     }  
  19. }  
  20.   
  21. class B extends A {  
  22.     int i = 2;  
  23.   
  24.     B() {  
  25.         System.out.println("子类构造方法 i=" + this.i);  
  26.         System.out.println("子类中 " + (this == super.getThis()));//④  
  27.     }  
  28.   
  29.     public void f() {  
  30.         System.out.println("子类方法f() i=" + this.i);  
  31.     }  
  32.   
  33.     public A getThis() {  
  34.         System.out.println("子类getThis");  
  35.         return this;  
  36.     }  
  37.   
  38.     public static void main(String[] args) {  
  39.         /* 
  40.          * 从下面 a.i 与 b.i 的输出可以看出,某对象的属性只与声明 的类型相 
  41.          * 关,与对象本身是什么类型无关,这与方法是完全不同的 
  42.          */  
  43.         A a = new B();  
  44.         System.out.println("main方法 a.i=" + a.i);  
  45.         B b = (B) a;  
  46.         System.out.println("main方法 b.i=" + b.i);  
  47.          
  48.         /* 
  49.          * output: 
  50.         父类构造方法 i=1 
  51.         子类方法f() i=0 
  52.         子类getThis 
  53.         父类中 true 
  54.         子类构造方法 i=2 
  55.         父类getThis 
  56.         子类中 true 
  57.         main方法 a.i=1 
  58.         main方法 b.i=2 
  59.          */  
  60.     }  
  61. }  

这里一直有个疑问:上面①②处的this肯定都是同一个对象,且都指向子类对象,还并且与子类中this是同一象,由第③④行代码可以证明这一点。但为什么①处的this好像指向了父类对象,不然为什么①处输出的i是父类中的i呢?这说明属性除了只与定义他的类型有关外,如果在类的内部自身访问时,就会访问当前所在类的属性,与this无关,这进一步说明了属性是不具有多态这一特性的。