您的位置:首页 > 其它

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

2007-05-28 09:47 176 查看
情有千千劫,有感于程序编码的质量(二)
(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中只有基本数据类型,比如int、double、boolean等是值传递,请记住它是分配在栈内存上的,其他一律是引用传递是分配在堆内存上。在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.Object是java.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 ();
请注意里面的类的构造在左边的什么都没有改,改的只有右边,而在上一种,你需要两边都改,这个例子简单一些,假如是真实的一个工程,可能里面有上千个类的,你使用前一种来实例化的话,那将会是灾难性的.请记住这只是基中一点好处. 请相信我,使用接口来写程序不是做秀,而是在实际开发中,占据着非常重要的角色. 现在的项目实战的构建,与其说是面向对象不如说其是面向接口.

未完,待续…………………………………
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: