`
lijun87
  • 浏览: 267870 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

java细节

阅读更多
[转贴]Java作为一门优秀的面向对象的程序设计语言,正在被越来越多的人使用。本文试图列出作者在实际开发中碰到的一些Java语言的容易被人忽视的细节,希望能给正在学习Java语言的人有所帮助。
 
1,拓宽数值类型会造成精度丢失吗?
    Java语言的8种基本数据类型中7种都可以看作是数值类型,我们知道对于数值类型的转换有一个规律:从窄范围转化成宽范围能够自动类型转换,反之则必须强制转换。请看下图:
byte-->short-->int-->long-->float-->double
char-->int
我们把顺箭头方向的转化叫做拓宽类型,逆箭头方向的转化叫做窄化类型。一般我们认为因为顺箭头方向的转化不会有数据和精度的丢失,所以Java语言允许自动转化,而逆箭头方向的转化可能会造成数据和精度的丢失,所以Java语言要求程序员在程序中明确这种转化,也就是强制转换。那么拓宽类型就一定不会造成数据和精度丢失吗?请看下面代码:
int i=2000000000;
int num=0;
for(float f=i;f<i+50;f++){
    num++;
}
System.out.println(num);

请考察以上代码输出多少?
如果你回答50 ,那么请运行一下,结果会让你大吃一惊!没错,输出结果是0,难道这个循环根本就没有执行哪怕一次?确实如此,如果你还不死心,我带你看一个更诧异的现象,运行以下代码,看输出什么?
int i=2000000000;
float f1=i;
float f2=i+50;
System.out.println(f1==f2);
    哈哈,你快要不相信你的眼睛了,结果竟然是true;难道f1和f2是相等的吗?是的,就是这样,这也就能解释为什么上一段代码输出的结果是0,而不是50了。那为什么会这样呢?关键原因在于你将int值自动提升为float时发生了数据精度的丢失,i的初始值是2000000000,这个值非常接近Integer.MAX_VALUE,因此需要用31位来精确表示,而float只能提供24位数据的精度(另外8位是存储位权,见IEEE745浮点数存储规则)。所以在这种自动转化的过程中,系统会将31位数据的前24位保留下来,而舍弃掉最右边的7位,所以不管是2000000000还是2000000050,舍弃掉最右边7位后得到的值是一样的。这就是为什么f1==f2的原因了。
    类似的这种数值拓宽类型的过程中会造成精度丢失的还有两种情况,那就是long转化成float和long转化成double,所以在使用的时候一定要小心。

2,i=i+1和i+=1完全等价吗?
    可能有很多程序员认为i+=1只是i=i+1的简写方式,其实不然,它们一个使用简单赋值运算,一个使用复合赋值运算,而简单赋值运算和复合赋值运算的最大差别就在于:复合赋值运算符会自动地将运算结果转型为其左操作数的类型。看看以下的两种写法,你就知道它们的差别在哪儿了:
  (1) byte i=5;
      i+=1;
  (2) byte i=5;
      i=i+1;
    第一种写法编译没问题,而第二种写法却编译通不过。原因就在于,当使用复合赋值运算符进行操作时,即使右边算出的结果是int类型,系统也会将其值转化为左边的byte类型,而使用简单赋值运算时没有这样的优待,系统会认为将i+1的值赋给i是将int类型赋给byte,所以要求强制转换。理解了这一点后,我们再来看一个例子:
  byte b=120;
  b+=20;
  System.out.println("b="+b);
  说到这里你应该明白了,上例中输出b的值不是140,而是-116。因为120+20的值已经超出了一个byte表示的范围,而当我们使用复合赋值运算时系统会自动作类型的转化,将140强转成byte,所以得到是-116。由此可见,在使用复合赋值运算符时还得小心,因为这种类型转换是在不知不觉中进行的,所以得到的结果就有可能和你的预想不一样。

3,位移运算越界怎么处理
    考察下面的代码输出结果是多少?
    int a=5;
    System.out.println(a<<33);
    按照常理推测,把a左移33位应该将a的所有有效位都移出去了,那剩下的都是零啊,所以输出结果应该是0才对啊,可是执行后发现输出结果是10,为什么呢?因为Java语言对位移运算作了优化处理,Java语言对a<<b转化为a<<(b%32)来处理,所以当要移位的位数b超过32时,实际上移位的位数是b%32的值,那么上面的代码中a<<33相当于a<<1,所以输出结果是10。

4,判断奇数
  以下的方法判断某个整数是否是奇数,考察是否正确:
   public boolean isOdd(int n){
       return (n%2==1);
   }
   很多人认为上面的代码没问题,但实际上这段代码隐藏着一个非常大的BUG,当n的值是正整数时,以上的代码能够得到正确结果,但当n的值是负整数时,以上方法不能做出正确判断。例如,当n=-3时,以上方法返回false。因为根据Java语言规范的定义,Java语言里的求余运算符(%)得到的结果与运算符左边的值符号相同,所以,-3%2的结果是-1,而不是1。那么上面的方法正确的写法应该是:
   public boolean isOdd(int n){
       return (n%2!=0);
   }

5,可以让i!=i吗?
在本题中,要求你声明一个i值,使得以下程序输出"No i!=i":
//在此声明i,并赋值。
if(i==i){
      System.out.println("Yes i==i");
  }else{
      System.out.println("No i!=i");
  }

    当你看到这个命题的时候一定会以为我疯了,或者Java语言疯了。这看起来是绝对不可能的,一个数怎么可能不等于它自己呢?或许就真的是Java语言疯了,不信请将i做出以下声明,再运行上面的代码。
  double i=0.0/0.0;
    上面的代码输出"No i!=i",为什么会这样呢?关键在0.0/0.0这个值,在IEEE 754浮点算术规则里保留了一个特殊的值用来表示一个不是数字的数量。这个值就是NaN("Not a Number"的缩写),对于所有没有良好定义的浮点计算都将得到这个值,比如:0.0/0.0;其实我们还可以直接使用Double.NaN来得到这个值。在IEEE 754规范里面规定NaN不等于任何值,包括它自己。所以就有了i!=i的代码。

6,2.0-1.1==0.9吗?
考察下面的代码:
 double a=2.0,b=1.1,c=0.9;
 if(a-b==c){
   System.out.println("YES!");
 }else{
   System.out.println("NO!");
 }

以上代码输出的结果是多少呢?你认为是“YES!”吗?那么,很遗憾的告诉你,不对,Java语言再一次欺骗了你,以上代码会输出“NO!”。为什么会这样呢?其实这是由实型数据的存储方式决定的。我们知道实型数据在内存空间中是近似存储的,所以2.0-1.1的结果不是0.9,而是0.88888888889。所以在做实型数据是否相等的判断时要非常的谨慎。一般来说,我们不建议在代码中直接判断两个实型数据是否相等,如果一定要比较是否相等的话我们也采用以下方式来判断:
  if(Math.abs(a-b)<1e-5){
     //相等
  }else{
    //不相等
  }
上面的代码判断a与b之差的绝对值是否小于一个足够小的数字,如果是,则认为a与b相等,否则,不相等。


分享到:
评论
2 楼 lijun87 2008-01-10  
说过了,是转来的贴子,我也不知道是哪的,老师给我们看的,我觉得还可以,就贴上来大家分享一下.
1 楼 Eastsun 2008-01-09  
JAVA PUZZLER 里面的吧?

相关推荐

    达内 CoreJava细节

    《达内 CoreJava 细节》是一份针对Java初学者和进阶者的重要学习资源,主要涵盖了CoreJava的基础部分。这份PDF文档旨在提供实用且深入的Java编程知识,帮助读者全面掌握这一强大的编程语言。 CoreJava是Java的核心...

    java基础 java细节

    Java基础,你想不到的知识,JVM等,java基础 java细节

    java细节优化代码1.0版本.7z

    "java细节优化代码1.0版本.7z"这个压缩包很可能包含了一些最佳实践和技巧,帮助开发者在Java编程中实现性能提升。 首先,让我们探讨一下Java代码优化的一些关键方面: 1. **算法优化**:选择正确的数据结构和算法...

    Java编程中须注意的细节.pdf

    在进行Java编程时,许多开发者往往会忽略一些琐碎但至关重要的细节,这些细节可能在项目的开发和维护中造成不可预见的问题。为了解决这些问题,可以参考《Java Pazzlers》(Java解惑)这本书,其中对许多常见的编程...

    java 细节代码收集 供自己查看

    在Java编程语言的世界里,对细节的掌握是提升代码质量、效率和可维护性的关键。这篇集合了关于Java编程的一些重要细节,旨在帮助开发者更好地理解和运用这个强大的后端语言。以下是一些基于提供的文件名和标签所衍生...

    java 一些细节知识

    在这个"java一些细节知识"的压缩包中,包含了一份名为"java test.doc"的文档,我们可以期待它涵盖了作者个人测试后总结的Java关键知识点。以下是对这些可能包含的内容的详细解释: 1. **变量与数据类型**:Java支持...

    Java 一些值得注意的细节

    Java编程语言以其强大的跨平台能力和丰富的库而广受开发者喜爱,但在实际开发中,很多细节问题往往会成为初学者或有经验的开发者们的困扰。本文将深入探讨Java的一些值得注意的细节,帮助你避免常见陷阱,提升代码...

    java中命名细节

    java中命名细节

    疯狂Java:突破程序员基本功

    疯狂Java:突破程序员基本功的16课 ,增加对Java细节的学习理解和掌握,提高基本知识

    java语言的26个细节

    【Java语言的26个细节】涵盖了Java编程中的一些易被忽视或误解的要点,以下是对其中几个细节的详细说明: 1. **位移运算越界处理**:Java中的位移运算符`和`&gt;&gt;`在处理整型数据时,会进行优化。对于左移运算`a,当...

    Java性能优化的45个细节.pdf

    以上是Java性能优化的一些关键点,开发者在开发过程中应当根据具体情况合理运用这些细节,以提高Java程序的性能。这些细节的掌握和运用需要对Java编程有较深的理解和实践经验,能够帮助开发者编写出更高效、更优质的...

    Java编程中影响性能的细节

    在Java编程中,优化性能是开发者必须关注的重要环节。以下是一些关键的性能优化细节: 1. **使用单例模式**:单例模式可以确保一个类只有一个实例,从而节省资源和提高效率。不过,过度使用单例可能导致设计复杂性...

    Java虚拟机规范.Java SE 8版.zip

    书中基于全新Java SE 8,完整且准确地阐述Java虚拟机规范,是深度了解Java虚拟机和Java语言实现细节的必读之作。 《Java核心技术系列:Java虚拟机规范(Java SE 8版)》共分7章。第1章从宏观的角度介绍了Java虚拟机...

    java笔记(细节问题讲的很细)

    这份"java笔记(细节问题讲的很细)"很可能包含了Java语言的核心概念、类库使用、编程技巧以及常见问题的解决方案。 一、Java基础 1. 变量与数据类型:Java提供了八种基本数据类型,包括整型、浮点型、字符型和...

    一个桌面程序,通过游戏闯关的方式来了解一些java的语言细节 .zip

    一个桌面程序,通过游戏闯关的方式来了解一些java的语言细节 一个桌面程序,通过游戏闯关的方式来了解一些java的语言细节 一个桌面程序,通过游戏闯关的方式来了解一些java的语言细节 一个桌面程序,通过游戏闯关的...

    一个桌面程序,通过游戏闯关的方式来了解一些java的语言细节.zip

    一个桌面程序,通过游戏闯关的方式来了解一些java的语言细节.zip一个桌面程序,通过游戏闯关的方式来了解一些java的语言细节.zip一个桌面程序,通过游戏闯关的方式来了解一些java的语言细节.zip一个桌面程序,通过...

    黄金档 » Java NIO 那些躲在角落的细节

    黄金档 » Java NIO 那些躲在角落的细节黄金档 » Java NIO 那些躲在角落的细节

    Java项目开源源码,共111个

    Java项目实现细节 在这些Java项目源码中,可以看到以下实现细节: * 使用requests库来发送HTTP请求,获取网页内容。 * 使用BeautifulSoup库来解析HTML网页内容。 * 使用docx库来生成Word文档。 * 使用selenium库来...

Global site tag (gtag.js) - Google Analytics