《大话Java性能优化》面向对象及基础类型相关部分

3.1 面向对象及基础类型

3.1.1 采用Clone()方式创建对象

Java语言里面的所有类都默认继承自java.lang.Object类,在java.lang.Object类里面有一个clone()方法,JDK API的说明文档里面解释了这个方法会返回Object对象的一个拷贝。我们需要说明两点:一是拷贝对象返回的是一个新对象,而不是一个对象的引用地址;二是拷贝对象与用new关键字操作符返回的新对象的区别是,这个拷贝已经包含了一些原来对象的信息,而不是对象的初始信息,即每次拷贝动作不是一个针对全新对象的创建。

当我们使用new关键字创建类的一个实例时,构造函数中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,那么我们可以通过调用它的clone()方法,注意,clone()方法不会调用任何构造函数。

代码3-1所示是工厂模式的一个典型实现,工厂模式是采用工厂方法代替new操作的一种模式,所以工厂模式的作用就相当于创建实例对象的new操作符。

代码清单3-1 创建新对象

public static CreditgetNewCredit()

{

    return new Credit();//创建一个新的Credit对象

}

如果我们采用clone()方法的方式创建对象,那么原有的信息可以被保留,因此创建速度会加快。如清单3-2所示,改进后的代码使用了clone()方法。

代码清单3-2 使用了clone()方法

private static CreditBaseCredit = new Credit();

public static CreditgetNewCredit()

{

    return (Credit)BaseCredit.clone();

}

 

3.1.2 避免对boolean判断

Java里的boolean数据类型被定义为存储8位(1个字节)的数值形式,但只能是true或是false。

有些时候我们出于写代码的习惯,经常容易导致习惯性思维,这里指的习惯性思维是想要对生成的数据进行判别,这样感觉可以在该变量进入业务逻辑之前有一层检查、判定。对于大多数的数据类型来说,这是正确的做法,但是对于boolean变量,我们应该尽量避免不必要的等于判定。如果尝试去掉boolean与true的比较判断代码,大体上来说,我们会有2个好处。

n  代码执行的更快(生成的字节码少了5个字节);

n  代码整体显得更加干净。

例如代码清单3-3和3-4所示,我们针对这个判定进行了代码解释,这两个类只有一个差距,即是否调用了等号表达式进行了一致性判定,如代码string.endswith ("a") == true。

代码清单3-3 boolean示例1

boolean method (stringstring) {

    return string.endswith ("a") ==true;//判断是否以a结尾

}

代码清单3-4 boolean示例2

boolean method (stringstring) {

  return string.endswith ("a");

}

 

3.1.3 多用条件操作符

我们在编写代码的过程中很喜欢使用if-else用于判定,这种思维来源于C语言学习的经历。大多数中国学生都是从谭老师的C语言书籍[1]了解计算机领域知识的,我们在高级语言程序设计过程中,如果有可能,尽量使用条件操作符"if (cond) return; else return;"这样的顺序判断结构,主要原因还是因为条件操作符更加简捷,代码看起来会少一点。其实JVM会帮助我们优化代码,但是个人感觉能省就省吧,代码过多让人看着不爽。代码清单3-5和3-6所示是示例代码,对比了两者的区别。

代码清单3-5 if示例1

//采用if-else的方式

public intmethod(boolean isdone){

        if (isdone) {

            return 0;

        } else {

            return 1;

        }

}

代码清单3-6 if示例

public intmethod(boolean isdone) {

   return (isdone ? 0 : 1);

}

上面两个例子,我们可以看到有一定差距,代码行数缩短了50%。其实现代JVM已经在编译时做了类似的处理,但是从代码整洁度考虑,作者觉得还是推荐多采用代码清单3-6的方式实现。

3.1.4 静态方法替代实例方法

在Java中,使用static关键字描述的方法是静态方法。与静态方法相比,实例方法的调用需要消耗更多的系统资源,这是因为实例方法需要维护一张类似虚拟函数导向表的结构,这样可以方便地实现对多态的支持。对于一些常用的工具类方法,我们没有必要对其进行重载,那么我们可以尝试将它们声明为static,即静态方法,这样有利于加速方法的调用。

如代码清单3-7所示,我们分别定义了两个方法,一个是静态方法,一个是实例方法,然后在main函数进程里分别调用10亿次两个方法,计算两个方法的调用总计时间。

代码清单3-7 静态方法示例

public static voidstaticMethod(){

}

//实例方法

public voidinstanceMethod(){

 

}

 

@Test

public static voidmain(String[] args){

  long start = System.currentTimeMillis();

   //循环10亿次,创建静态方法

for(inti=0;i<1000000000;i++){

 staticVSinstance.staticMethod();

}

System.out.println(System.currentTimeMillis()- start);

 

 start = System.currentTimeMillis();

staticVSinstance si1 =new staticVSinstance();

        //循环10亿次,创建实例方法

for(intj=0;j<1000000000;j++){

si1.instanceMethod();

 }

System.out.println(System.currentTimeMillis()- start);

}

清单3-7代码中申明了一个静态方法staticMethod()和一个实例方法instanceMethod(),运行程序,统计了两个方法调用若干次后的耗时,程序输出如下,单位是毫秒,方法内部没有实现任何代码。请读者注意,由于机器差别,所以运行的结果可能也会有所不同。

代码清单3-8 程序运行输出

733   764

总的来说,静态方法和实例方法的区别主要体现在两个方面:

n 在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。

n 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。

从上面的例子我们可以这么总结,如果你没有必要去访问对象的外部,那么就让你的方法成为静态方法。静态方法会被更快地调用,因为它不需要一个虚拟函数导向表,该表用来告诉你如何区分方法的性质,调用这个方法不会改变对象的状态。

3.1.5 有条件地使用final关键字

在Java中,final关键字可以被用来修饰类、方法和变量(包括成员变量和局部变量)。我们在使用匿名内部类的时候可能会经常用到final关键字,例如Java中的String类就是一个final类。

如代码清单3-9所示,由于final关键字会告诉编译器,这个方法不会被重载,所以我们可以让访问实例内变量的getter/setter方法变成“final”。

代码清单3-9 非final类

public void setsize(int size) {

  _size = size;

}

private int _size;

代码清单3-10 final类

//告诉编译器该方法不会被重载

final public voidsetsize (int size) {

    _size = size;

}

private int _size;

总的来说,使用final方法的原因有两个[2]。第一个原因是把方法锁定,以防任何继承类修改它的含义。第二个原因是提高效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。JDK6以后的Java版本已经不再需要使用final方法进行这些优化了。

3.1.6 避免不需要的instanceof操作

instanceof关键字是Java的一个二元操作符,和==、>、<是属于同一类表达式。由于instanceof是由字母组成的,所以它也是Java的保留关键字。instanceof的作用是测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据,即如果左边的对象的静态类型等于右边的,我们使用的instanceof表达式的返回值会返回true。

代码清单3-11 instanceof示例

void method (dog dog,faClass faclass) {

    dog d = dog;

   if (d instanceof faClass) // 这里永远都返回true.

      system.out.println("dog is a faClass");

    faClass faclass = faclass;

    if (faclass instanceof object) // alwaystrue.

      system.out.println("uiso is anobject");

}

上述代码里面对dog类型的变量都做了判定,由于已经确定类继承自基类,所以我们可以删除不需要的instanceof操作。当然,这样的操作修改还是需要基于实际的业务逻辑,有些时候为了保证数据准确性、安全性,还是需要层层检查的。如代码清单3-12所示,代码可以被精简成这样。

代码清单3-12 instanceof示例

void method () {

   dog d;

    system.out.println ("dog is an faclass");

    system.out.println ("uiso is an faclass");

}

另外,绝大多数情况下都不推荐使用instanceof方法,还是好好利用多态特性吧,这是面向对象的基本功能。

3.1.7 避免子类中存在父类转换

我们知道在Java语言里所有的类都是直接或者间接继承自Object类。我们可以说,Object类是所有Java类的祖先,因此每个类都使用Object作为超类,所有对象(包括数组)都实现这个类的方法。在不明确是否提供了超类的情况下,Java会自动把Object作为被定义类的超类。

我们可以使用类型为Object的变量指向任意类型的对象。同样,所有的子类也都隐含的“等于”其父类。那么,程序代码中就没有必要再把子类对象转换为它的父类了。

代码清单3-13 避免父类转换示例

class oriClass {

  string _id = "unc";

}

class dog extendsoriClass{

    void method () {

     dog dog = new dog();

     oriClass animal = (oriClass)dog;  //已经确定继承自oriClass类了,因此没有必要再转对象类型

     object o = (object)dog;

  }

}

代码清单3-14 避免父类转换示例

class dog extendsoriClass {

  //去掉了转换父类操作

  void method () {

     dog dog = new dog();

     unc animal = dog;

    object o = dog;

}

}

 

3.1.8 建议多使用局部变量

调用方法时传递的参数以及在调用中创建的临时变量都被保存在栈(

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值