类与对象(三)

类与对象之类的复用(继承、组合、代理)

作者:egg

微博:http://weibo.com/xtfggef

出处:http://blog.csdn.net/zhangerqing

此章我们主要谈下面向对象编程的代码复用机制。

继承

继承是OOP中最为重要的概念,达到了非常有效的代码重用效果,使得开发效率变得很高!同时也因此,造成了OOP语言执行效率低下,不免被C/C++程序员嘲笑。在Java语言中,两个类之间通过extends关键字实现继承。我们来看个继承的实例:

[java]  view plain  copy
  1. class A {  
  2.     public A() {  
  3.         System.out.println("A()!");  
  4.     }  
  5. }  
  6.   
  7. class B extends A {  
  8.     public B() {  
  9.         System.out.println("B()!");  
  10.     }  
  11. }  
  12.   
  13. public class ExtendsTest extends B {  
  14.   
  15.     public ExtendsTest() {  
  16.         System.out.println("ExtendsTest()!");  
  17.     }  
  18.   
  19.     public static void main(String[] args) {  
  20.         new ExtendsTest();  
  21.     }  
  22.   
  23. }  

ExtendsTest继承自B,B继承自A,当实例化ExtendsTest的时候,却依次打印出了A、B、ExtendsTest构造器中的内容,说明:构造器被依次调用了,这是为什么?因为当类实现继承时,默认的会将基类的一个子对象传给子类,而子类需要对这个子对象进行初始化,所以需要调用父类的构造器,但是,这一切都是隐式进行的,我们看不到,不过可以从实验中得出结论:在对子类进行初始化的时候,会先调用父类的构造器(如果有学过C++的同学,肯定知道,在C++中除了有构造函数,还有析构函数,初始化的时候先调用父类的构造函数,析构的时候,先析构子类对象,再析构父类对象,一个从外到里,再由里到外的过程)。如果父类构造器需要传递参数,则使用super关键字来实现就行了。

[java]  view plain  copy
  1. class B extends A {  
  2.     public B(int n) {  
  3.         System.out.println("B()!");  
  4.     }  
  5. }  
  6.   
  7. public class ExtendsTest extends B {  
  8.   
  9.     public ExtendsTest(int n) {  
  10.         super(n);  
  11.         System.out.println("ExtendsTest()!");  
  12.     }  
  13.   
  14.     public static void main(String[] args) {  
  15.         new ExtendsTest(1);  
  16.     }  
  17.   
  18. }  

下面我们分几种情况讨论下继承:

1、子类不能继承父类私有的域或者方法。如果想要继承父类的私有对象,只能将private改成protected,因为protected的权限控制在包内。因此一般情况,用到继承的话,最好将父类中的域声明为私有(private,因为一般情况不需要继承成员变量),将方法声明为public,方便继承。

2、当子类对象调用一个方法时,如果子类没有,则去调用父类的同名方法,但是调用者保持是子类。

[java]  view plain  copy
  1. public class A {  
  2.     int a = 10;  
  3.     void a(){  
  4.         System.out.println(a);  
  5.         System.out.println(getClass().getName());  
  6.     }  
  7. }  
  8.   
  9. class B extends A {  
  10.     int a = 20;  
  11. //  void a(){  
  12. //      System.out.println(a);  
  13. //      System.out.println(getClass().getName());  
  14. //      System.out.println(this.a);  
  15. //      System.out.println(super.a);  
  16. //  }  
  17.   
  18.     public static void main(String[] args) {  
  19.         B b = new B();  
  20.         b.a();  
  21.     }  
  22. }  

输出:

10

B

a()B中被注释掉了,则调用的是父类A中的,所以输出的值是A中的成员变量。但是调用getClass()获取的仍然是B。当我们将上述代码中的注释去掉,则输出:

20

B

20

10

B中有a()方法时,屏蔽了A中的a()super关键字调用的是父类的信息,this关键字调用的是当前类的信息。

代理

代理的思想在我们讲得设计模式里面有体现,就是在一个类中持有另一个类的实例,从而代替原类进行一个操作,我们看个例子:

[java]  view plain  copy
  1. public class ProxyTest {  
  2.     Source source = new Source();  
  3.     void p(int n){  
  4.         source.a(n);  
  5.     }  
  6.     void p2(int n){  
  7.         source.b(n);  
  8.     }  
  9.     public static void main(String[] args) {  
  10.         ProxyTest pt = new ProxyTest();  
  11.         pt.p(20);  
  12.         pt.p2(50);  
  13.     }  
  14. }  
  15.   
  16. class Source{  
  17.     void a(int n){  
  18.         System.out.println("this is : "+n);  
  19.     }  
  20.     void b(int n){  
  21.         System.out.println("this is : "+n);  
  22.     }  
  23. }  

组合

如果大家还记得设计模式里的建造者模式,那么很容易联想到组合机制,就是将一系列的对象组合在一起,组合成一个功能丰富的类,当然,这些对象包括基本数据类型,也包括引用。来看个例子:

[java]  view plain  copy
  1. class Soap{  
  2.     private String s;  
  3.     Soap(){  
  4.         System.out.println("soap");  
  5.         s = "constructor";  
  6.     }  
  7.     public String toString(){  
  8.         return s;  
  9.     }  
  10. }  
  11.   
  12. public class CompronentTest {  
  13.     private String s1 = "happy",s2="Happy",s3,s4;  
  14.     private Soap castille;  
  15.     private int i;  
  16.     public CompronentTest(){  
  17.         s3 = "joy";  
  18.         castille = new Soap();  
  19.     }  
  20.     {  
  21.         i = 88;  
  22.     }  
  23.     public String toString(){  
  24.         if(s4 == null){  
  25.             s4 = "Joy";  
  26.         }  
  27.             return "s1 = " + s1 + "\n" +  
  28.                    "s2 = " + s2 + "\n" +  
  29.                    "s3 = " + s3 + "\n" +  
  30.                    "s4 = " + s4 + "\n" +  
  31.                    "i = " + i + "\n" +  
  32.                    "castille = " + castille;  
  33.   
  34.     }  
  35.     public static void main(String[] args) {  
  36.         CompronentTest ct = new CompronentTest();  
  37.         System.out.println(ct);  
  38.     }  
  39. }  

该类就是一个普通的组合类,在组合类中我们应该注意这个对象的初始化方式,此处:1s1s2采用在声明的地方直接赋值,这样能够保证它们在构造器被调用之前被初始化(详细可见类与对象一中关于类的初始化顺序的介绍)。2s3在构造器中初始化。3s4采用的是懒加载(下面会讲)。4i在非静态初始化块中。此处我们说下toString方法,就是一个将其它对象转为String对象的方法,除了非基本类型的对象,其它都有一个toString方法,这是因为toString方法是Object类的固有方法,在Java中任何类都隐式继承Object类,也就说都隐含toString方法。所以,在上述的例子中,当最后的字符串+ castille对象时,需要将castille对象以字符串的形式表现出来,因此调用了toString()

懒加载因为涉及持有对象实例,所以会涉及到懒加载的机制,代码中的:

if(s4 == null){

         s4 = "Joy";

}

就是一种懒加载的机制,这种机制就是解决当所需的对象比较庞大的时候,只有在用的时候才去初始化,节省空间,提高效率!

总结下:

1、初始化方面,注意一些特殊对象的初始化,可以在定义的时候直接初始化,或者在构造方法中或在方法块中,或者在用的时候懒加载。

2toString方法,解决和字符串对象衔接出现的类型不匹配问题。

3、懒加载,提高效率,对于大的对象,延迟加载!

补充:

Java对中文类名、方法名的支持。

[java]  view plain  copy
  1. public class Test {  
  2.   
  3.     public static void main(String[] args) {  
  4.           
  5.         学生 我 = new 学生();  
  6.         我.说();  
  7.       
  8.     }  
  9. }  
  10.   
  11. class 学生{  
  12.     public void 说(){  
  13.         System.out.println("hello everyone!");  
  14.     }  
  15. }  

 补充一点儿东西,Java中的运算符优先级:

补充:Java中跳出循环的方法:

众所周知,在Java中,如果想跳出for循环,一般情况下有两种方法:break和continue。

break是跳出当前for循环,如下面代码所示:

[java]  view plain  copy
  1. package com.xtfggef.algo;  
  2.   
  3. public class RecTest {  
  4.   
  5.     /** 
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.         for(int i=0; i<10; i++){  
  10.             if(i==5){  
  11.                 break;  
  12.             }  
  13.             System.out.print(i+" ");  
  14.         }  
  15.     }  
  16. }  

输出:0 1 2 3 4

也就是说,break会跳出(终止)当前循环。

continue是跳出当前循环,开使下一循环,如下所示:

[java]  view plain  copy
  1. package com.xtfggef.algo;  
  2.   
  3. public class RecTest {  
  4.   
  5.     /** 
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.         for (int i = 0; i < 10; i++) {  
  10.             if (i == 5) {  
  11.                 continue;  
  12.             }  
  13.             System.out.print(i+" ");  
  14.         }  
  15.     }  
  16. }  

输出:0 1 2 3 4 6 7 8 9

以上两种方法没有办法跳出多层循环,如果需要从多层循环跳出,则需要使用标签,定义一个标签label,然后再需要跳

出的地方,用break label就行了,代码如下:

[java]  view plain  copy
  1. package com.xtfggef.algo;  
  2.   
  3. public class RecTest {  
  4.   
  5.     /** 
  6.      * @param args 
  7.      */  
  8.     public static void main(String[] args) {  
  9.   
  10.         loop: for (int i = 0; i < 10; i++) {  
  11.             for (int j = 0; j < 10; j++) {  
  12.                 for (int k = 0; k < 10; k++) {  
  13.                     for (int h = 0; h < 10; h++) {  
  14.                         if (h == 6) {  
  15.                             break loop;  
  16.                         }  
  17.                         System.out.print(h);  
  18.                     }  
  19.                 }  
  20.             }  
  21.         }  
  22.         System.out.println("\nI'm here!");  
  23.     }  
  24. }  

输出:

012345
I'm here!

意思很显然!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值