情有千千劫,有感于程序编码的质量(二)

情有千千劫,有感于程序编码的质量()

(2)结构

 

<1> 小心数组的引用动了你的奶酪. (java)

让我们一起来看一下这一个函数.我相信很多朋友喜欢这样写.

//*****************************************************************************

public class test {

    public static void main(String[] args) {

       int[] testArray = new int[] { 0, 1, 2, 3, 4 };

       int[] returnArray = doArrayTest(testArray);

 

       int testInt = 10;

       int returnValue = doIntTest(testInt);

 

       System.out.println("Int Array = " + testArray[0]);

       System.out.println("Int Value = " + testInt);

    }

 

    private static int[] doArrayTest(int[] testArray) {

 

       int[] value = testArray;

       for (int i = 0; i < value.length; i++) {

           value[i] = testArray[i] + 1;

       }

 

       return value;

    }

 

    private static int doIntTest(int testInt) {

       int value = testInt;

       value = value + 1;

       return value;

    }

}

//*****************************************************************************

输出结果: Int Array = 1

Int Value = 10

为什么呢?int 型的原始值为什么不会改变,int[]型会改变呢?其实这个原理大家应该在学习java的前面几章就应该知道.因为Java中只有基本数据类型,比如intdoubleboolean等是值传递,请记住它是分配在栈内存上的,其他一律是引用传递是分配在堆内存上。在Java中数组(如:int [])被认为是对象,也是引用传递,即2个名称指向同一内存地址。所以在这里尽管int[] 里面是包括int,但它本身在传递的时候还是引用传递.也有许多人认为”java里面只有值传递”,大家千万不要被这句话所误导.要搞清楚这句话的真正原因.”java 引用对象里面会产生一个副本,其副本里面并没有包含被引用对象的内容,只是包含被引用对象地址的值”,才是这句话的意思, 比如说:int[] a = new int[]{0},int[] b = a;很多人认变其在内存中最终的顺序应该是这样,b->a->{0},其实是错误的,应该是a->{0}<-b,不相信的话,大家看一下这个简单的程序:

//*****************************************************************************

public static void main(String[] args) {

       int[] base = new int[] { 1 };

 

       int[] a = new int[] { 0 };

       int[] b = a;

       b[0] = 100;

 

       System.out.println("a[0] = " + a[0]);

       System.out.println("b[0] = " + b[0]);

 

       a = base;

       System.out.println("a[0] = " + a[0]);

       System.out.println("b[0] = " + b[0]);

    }

}

//*****************************************************************************

输出结果:

a[0] = 100

b[0] = 100

a[0] = 1

b[0] = 100

 

在这里,我改变a指向另一个对象,其对象b并没有发生改变.还是指向初始对象.

 

 

<2> 操作数组时,最好请不要返回空值.(java)

大家在写函数的时候,如果返回一个数组经常喜欢这样写.

//*****************************************************************************

public static void main(String[] args) {

       String[] filesName = getSubFilesName("C:/widows");

                  for (int i = 0; i < filesName.length; i++) {

              System.out.println(filesName[i]);

           }

       }

       catch (Exception ex) {

           ex.printStackTrace();

       }

    }

 

    private static String[] getSubFilesName(String path) {

       String[] filesPath = null;

       try {

           File file = new File(path);

 

           File[] files = file.listFiles();

           filesPath = new String[files.length];

           for (int i = 0; i < files.length; i++) {

              filesPath[i] = files[i].getAbsolutePath();

           }

          

       }

       catch (NullPointerException ex) {

           ex.printStackTrace();

       }

       return filesPath;

 

    }

//*****************************************************************************

假定C:/widows不存在

程序输出:

java.lang.NullPointerException

    at test.test.getSubFilesName(test.java:21)

    at test.test.main(test.java:8)

Exception in thread "main" java.lang.NullPointerException

    at test.test.main(test.java:9)

 

在这里我们把这个程序改进一下.不使用null赋值,大家可以看一下,程序的输出.

//*****************************************************************************

 

public static void main(String[] args) {

       String[] filesName = getSubFilesName("C:/widows");

       for (int i = 0; i < filesName.length; i++) {

           System.out.println(filesName[i]);

       }

 

    }

 

    private static String[] getSubFilesName(String path) {

       String[] filesPath = new String[0];

       try {

           File file = new File(path);

 

           File[] files = file.listFiles();

           filesPath = new String[files.length];

           for (int i = 0; i < files.length; i++) {

              filesPath[i] = files[i].getAbsolutePath();

           }

 

       }

       catch (NullPointerException ex) {

           ex.printStackTrace();

       }

       return filesPath;

 

    }

//*****************************************************************************

假定C:/widows不存在

程序输出:

java.lang.NullPointerException

    at test.test.getSubFilesName(test.java:21)

    at test.test.main(test.java:8)

 

程序由两个异常变为一个异常,这是因为我们没有使用null的原因.因为使异常减少了一个,很多朋友肯定会问,使用这个有什么好处?使用null的话,性能应该好于分配一个0空间的数组啊.其实不然,且不说异常的开销多了一个,我们接下来就分析一下null!

 

Null这个东西,C语言的世界里面也有.只是变为了NULL,它代表空对象,空地址.但是在java里面它的性能开销远远大于C语言里面的版本(相对而言,其实还是比较少啦J),不信的话,大家可以看一下这段程序.

//*****************************************************************************

public class TestNull {

    private void print(Object value) {

       System.out.println("Object");

    }

 

    private void print(Integer value) {

       System.out.println("Integer");

    }

 

    public static void main(String[] args) {

       TestNull test = new TestNull();

       test.print(null);

    }

}

//*****************************************************************************

输出结果:

Integer

为什么会这样?为什么不输出”Object”或是什么东西都不输出?或是全部输出呢?因为NULL既是Integer的对象,也是Object的对象,它可以代表任何对象.这一点大家应该清楚. java.lang.Objectjava.lang.Interger的父类. 在上一程序的情况下,java会沿着继承的路线从下往上搜索一直匹配.直到找到最上层的类.不信的话,大家可以换一些类型,结果肯定也是一样的,当然你要确定你在使用的时候,要确保其之间的关系是继承的关系,否则,程序运行是通不过的.

     从这一点我们就不难看出即便把一个对象赋值为null,编译器在背后仍然要做很多工作的.所以说把一个对象赋值为null,就不会引起系统的开销这种说法是站不住脚的.

 

<3>尽量使用接口来安排你程序的结构,并使用接口来接受你的实例.(C#)

先让我们一起来看一下什么接口的定义:接口是实现构件可插入性的关键,可插入构件的关键在于存在一个公用的接口,以及每个构件实现了这个接口。Java中的接口是一系列方法的声明,是一些方法特征的集合,一个接口只有方法的特征没有方法的实现,因此这些方法可以在不同的地方被不同的类实现,而这些实现可以具有不同的行为(功能)。

 

一起来看一下,下面这段代码:

//*****************************************************************************

 

class Program

    {

        static void Main (string[] args)

        {

            //在此使用接口.

            IDoor door = new Gate();

            door.Open();

            door.Close();

 

            //在此不使用接口.

            Gate door1 = new Gate();

            door1.Open();

            door1.Close();

        }

    }

   

    interface IDoor  //定义接口.

    {

        void Open();

        void Close();

    }

   

    class Gate : IDoor

    {

 

        public void Open()

        {

            Console.WriteLine("Gate has Opened");

        }

 

        public void Close()

        {

            Console.WriteLine("Gate has Closed");

        }

    }

 

    class Door : IDoor

    {

        public void Open()

        {

            Console.WriteLine("Door has Closed");

        }

 

        public void Close()

        {

            Console.WriteLine("Door has Closed");

        }

}

//*****************************************************************************

结果输出:

Gate has Opened

Gate has Closed

Gate has Opened

Gate has Closed

 

结果肯定是一样的,但使用接口来部置你的代码结构,使别人很清楚就知道你在做什么. 这一点相信大家也应该一眼就看得出来.特别是在大一些的项目中为了统一规范,则由架构师开发统一的接口.然后下面的程序员去实现其接口,它把工作细化和简化了.这也是接口一个比较重要的好处,至于为什么使用接口来接收实便呢? 假设你代码在以后需要重构,不再需要Gate 实例了,而是需要Gateway实例,你需要这样的实例化.

Gateway door1 = new Gateway ();

而使用接口,你只需要:

IDoor door = new Gateway ();

请注意里面的类的构造在左边的什么都没有改,改的只有右边,而在上一种,你需要两边都改,这个例子简单一些,假如是真实的一个工程,可能里面有上千个类的,你使用前一种来实例化的话,那将会是灾难性的.请记住这只是基中一点好处. 请相信我,使用接口来写程序不是做秀,而是在实际开发中,占据着非常重要的角色. 现在的项目实战的构建,与其说是面向对象不如说其是面向接口.

 

未完,待续…………………………………

 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值