JAVA语言之Thinking in Java-复用类
小标 2018-07-24 来源 : 阅读 1035 评论 0

摘要:本文主要向大家介绍了JAVA语言之Thinking in Java-复用类,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

本文主要向大家介绍了JAVA语言之Thinking in Java-复用类,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助。

复用代码,即使用已经开发并调试好的类。组合和继承是两种实现方法。

组合语法:

  在新类中创建现有类的对象。该方法只是复用了现有代码的功能,而非它的形式。

  组合的例子随处可见,这里不举例说明。但书中特意强调了toString方法。

  每一个非基本类型的对象都有一个toString方法,因为每一个类都是继承Object类而来的,而Object类中包含这个方法。具体需要注意的是,当你要将一个对象和字符串连接的时候,编译器会自动调用toString方法,当Object类中的toString方法不能满足要求时,则需要重写这个方法。

继承语法:

  继承是所有OOP语言不可缺少的组成部分。当创建一个类时,总是在继承,因为若没有明确指出要从其他类中继承,就默认从Java的标准根类Object进行继承。

  为了继承,一般的规则是将所有的数据成员指定为private,所有的方法指定为public。虽然在特殊的情况下必须做出调整,但是这的确是一个很有用的规则。

  当继承类中有对基类中定义的方法修改时,欲调用基类的方法,必须加上super关键字,否则程序将产生递归。当然,继承类同样可以定义属于自己的方法。

 初始化基类:

  当创建了一个导出类的对象时,该对象包含了一个基类的子对象。这个子对象与直接创建的基类对象时一样的。二者的却别在于后者来自于外部,而前者来自于导出类对象的内部。

  1)无参构造器的初始化

   

class Art {

    Art() {

        System.out.println("Art");

    }

}

class Drawing extends Art () {

    Drawing() {

        System.out.println("Drawing");

    }

}

public class Cartoon extends Drawing {

    public Cartoon() {

        System.out.println("Cartoon");

    }

}

/*

Output:

    Art

    Drawing

    Cartoon

*/

   

   2)有参构造器的初始化

    对于无参构造器,当然默认的构造器也会无参的,编译器可以轻松地调用而不需要考虑传递参数的问题。但是想调用带参数的基类构造器,就必须用super显示地编写调用基类构造器的语句,而且调用基类构造器必须是你在导出类构造器中要做的第一件事,否则编译器将报错。

class Game {

    Game(int i) {

        System.out.println("Game Constructor");

    }

}

class BoardGame extends Game {

    BoardGame(int i) {

        super(i);

        System.out.println("BoardGame Constructor");

    }

}

public class Chess extends BoardGame {

    public Chess() {

        super(11);

        System.out.println("Chess Constructor");

//        super(11); // 报错

    }

    public static void main(String[] args) {

        new Chess();

    }

}

/*

output:

    Game Constructor

    BoardGame Constructor

    Chess Constructor

*/

   

代理:

  代理是继承与组合之间的中庸之道,但是Java并没有提供对它的直接支持。代理可控制需要哪些被代理类中的方法,而组合和继承则拥有了所有方法。

  代理的具体过程,先创建被代理类的对象引用,然后创建与被代理类中方法同名的方法,并通过这个对象引用来调用被代理类的方法,这里有点像重载(注意重载是建立在继承的基础上的)。

class SpaceShipControls {

    void up(int velocity) {}

    void down(int velocity) {}

}

public class SpaceShipDelegation {

    private String name;

    private SpaceShipControls controls = new SpaceShipControls();//创建被代理类的对象

    public SpaceShipDelegation(String name){

        this.name = name;

    }

    public void up(int velocity){//选择需要代理的方法,注意名字需一样

        controls.up(velocity);//通过对象引用,调用被代理类的方法。实现代理

    }

    public static void main (String[] args) {

        SpaceShipDelegation protector = new SpaceShipDelegation("lalala");

        protector.up(100);

    }

}

   

确保正确清理:

  Java中没有析构函数的概念。虽然Java中有垃圾回收机制,但是你永远不知道它什么时候才会被调用。因此,如果想要某个类清理一些东西,就必须显示地编写一个特殊方法来实现。清理的首要任务是,将这一清理动作置于finally子句之中,以防异常的出现。finally子句表示无论发生什么事,一定要执行这个动作。清理动作的顺序和生成顺序相反,因为可能存在子对象依赖于另一个子对象的情况。

名称屏蔽:

  如果导出类中有对基类的方法进行重载,不会对名称进行屏蔽,即所有重载方法都是可用的。@override注解重写基类方法,避免方法名称写错。

向上转型:

  导出类转型为基类,在继承图上是向上的,因此得名。由于向上转型是从一个较专用类型向较通用类型转换,所以总是安全的,而且在向上转型的过程中,类接口中唯一发生的事情是丢失方法,而不是获取他们,所以在安全的考虑上是可以接受的,编译器也是允许的。

组合与继承之间选择:

  尽管面向对象的过程中,一直强调继承的概念,但并不是尽可能的使用它。相反,应当慎用这一技术。一个清晰的方法是问问自己到底是否需要从新类向基类向上转型。如果需要,那就用继承吧,如果不需要,那应当好好考虑下了。所以向上转型是判断组合和继承选择的重要依据。

final数据:

  对于基本类型,final使数值恒定不变,而用于对象引用,final引用恒定不变。一旦引用被初始化指向一个对象,就无法再把改为指向另一个对象。但是对象本身是可以修改的。既是static又是final的域将用大写表示,并使用下划线分隔各个单词。

  有一点需要注意的是,空白final是指指定了final但又没有赋初值的域。无论什么情况,编译器都要确保空白final在被使用前都必须要被初始化。一般情况下,空白final是在其他类的构造器中初始化,即某个类的final域可以根据创建不同的对象具有不同的初始值,而且还能保持其恒定不变的特性。空白final大大提高了灵活性。

class Poppet {

    private int i;

    Poppet(int ii) {

        i = ii;

    }

}

 

public class BlankFinal {

    private final int i = 0;

    private final int j;

    private final Poppet p;

    public BlankFinal() {

        j = 1;

        p = new Poppet(1);

    }

    public BlankFinal(int x) {

        j = x;

        p = new Poppet(x);

    }

    public static void main(String[] args) {

        /*通过调用不同的构造器,创建不同的对象,使空白final域P有不同的初始值*/

        new BlankFinal();

        new BlankFinal(1);

    }

}

   

final参数:

  Java允许在参数列表中以声明的方式将参数指明为final,这意味着你不能在方法中更改参数引用所指向的对象。  

class Gizmo {

    public void spin() {

    }

}

 

public class FinalArguments {

    void with(final Gizmo g) {

        //g = new Gizmo();  不能更改

    }

    void without(Gizmo g) {

        g = new Gizmo();

        g.spin();

    }

    //void f(final int i){i++} 不能更改i的值

    int f(final int i){ return i + 1; } //这里并没有更改i的值

    public static void main (String[] args){

        FinalArguments bf = new FinalArguments();

        bf.with(null);

        bf.without(null);

    }

}

   

final方法:

  使用final方法的原因有两个,第一个原因是效率,这是使用初衷。但是这仅仅是在代码块不是很大的情况下才能显示出其作用,后来Java找到了其他的方式进行提高效率,所以在final方法的使用上不再考虑效率问题。第二个原因是为了防止在继承中对方法进行覆盖。此外,定义为private访问权限的方法,其隐式指定为final。

  特别注意,覆盖只有在某方法是基类接口中的一部分时才会出现,基类中的private方法不是接口的一部分,所以如果再基类的导出类中定义一个和基类中private方法同名的public或者protect方法,不是重载,而是定义了一个新的方法,切记!

final类:

  当定义某个类为final时,就表示你不想继承这个类,而且也决不允许别人继承这个类,或者说这个类完全没有被继承的必要,又或者出于安全的考虑。总之,它被限制了。由于final类无法被继承,所以类下的所有方法都是final的,无论是否指定为final。

对于使用final的忠告:

  将一个方法或者一个类指定为final,大部分可能是明智的。但是你必须注意到,这些所谓的不能重载、不能被继承,都是你自己的想象,总有你意想不到的运用它的情况。所以,使用final请慎重!

初始化及类的加载:

  类只有在创建类的第一个对象或者访问static域和static方法时才会发生加载。其实创建类的对象也是在访问static方法,因为创建时调用的构造器是隐式的static。所以,类的加载之处也是static初始化之处,而所有的static只会被初始化一次,按照定义的顺序初始化。

   

class Insect { 

  private int i = 9; 

  protected int j; 

  Insect() { 

    print("i = " + i + ", j = " + j); 

    j = 39; 

  } 

  private static int x1 = 

    printInit("static Insect.x1 initialized"); 

  static int printInit(String s) { 

    print(s); 

    return 47; 

  } 

   

public class Beetle extends Insect { 

  private int k = printInit("Beetle.k initialized"); 

  public Beetle() { 

    print("k = " + k); 

    print("j = " + j); 

  } 

  private static int x2 = 

    printInit("static Beetle.x2 initialized"); 

  public static void main(String[] args) { 

    print("Beetle constructor"); 

    Beetle b = new Beetle(); 

  } 

/**

  * static Insect.x1 initialized

  * static Beetle.x2 initialized

  * Beetle constructor

  * i = 9, j = 0

  * Beetle.k initialized

  * k = 47

  * j = 39

*/

   

总结:

  在开始一个设计时,一般优先选择使用组合,或者可能是代理,只有确实必要时才使用继承,因为组合更具灵活性。

  在设计一个系统时,目标应该是创建某些类,其中每个类都有具体的用途,而且既不会太大,也不会太小。太大则复杂难以复用,太小则可能不添加其他功能就无法使用。所以,太大的情况下,就适当的细分。在系统的设计阶段,必须意识到这是一种增量过程,是不断累积的过程,并不是一蹴而就的。

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注编程语言JAVA频道!

本文由 @小标 发布于职坐标。未经许可,禁止转载。
喜欢 | 1 不喜欢 | 0
看完这篇文章有何感觉?已经有1人表态,100%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程