- 浏览: 117004 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
playboy0305:
正解。
geeksun 写道在执行操作前,session.cl ...
a different object with the same identifier value -
geeksun:
在执行操作前,session.clear()就行了。
a different object with the same identifier value -
qianqingfu:
文笔太烂!!!!!
a different object with the same identifier value -
oscarswing:
也就是说我写不写都行,反正仅在debug的时候有效,是吧?
ASSERT的用法 -
boreas_baosj:
恩 ~~~
a different object with the same identifier value
5,String类和对象池
我们知道得到String对象有两种办法: String str1="hello"; String str2=new String("hello"); 这两种创建String对象的方法有什么差异吗?当然有差异,差异就在于第一种方法在对象池中拿对象,第二种方法直接生成新的对象。在JDK5.0里面,Java虚拟机在启动的时候会实例化9个对象池,这9个对象池分别用来存储8种基本类型的包装类对象和String对象。当我们在程序中直接用双引号括起来一个字符串时,JVM就到String的对象池里面去找看是否有一个值相同的对象,如果有,就拿现成的对象,如果没有就在对象池里面创建一个对象,并返回。所以我们发现下面的代码输出true: String str1="hello"; String str2="hello"; System.out.println(str1==str2); 这说明str1和str2指向同一个对象,因为它们都是在对象池中拿到的,而下面的代码输出为false: String str3="hello" String str4=new String("hello"); System.out.println(str3==str4); 因为在任何情况下,只要你去new一个String对象那都是创建了新的对象。与此类似的,在JDK5.0里面8种基本类型的包装类也有这样的差异: Integer i1=5;//在对象池中拿 Integer i2 =5;//所以i1==i2 Integer i3=new Integer(5);//重新创建新对象,所以i2!=i3 对象池的存在是为了避免频繁的创建和销毁对象而影响系统性能,那我们自己写的类是否也可以使用对象池呢?当然可以,考察以下代码: import java.util.*;
public class Student {
private String name;
private int age;
private static HashSet<Student> pool = new HashSet<Student>();
// 对象池
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 使用对象池来得到对象的方法
public static Student newInstance(String name, int age) {
// 循环遍历对象池
for (Student stu : pool) {
if (stu.name.equals(name) && stu.age == age) {
return stu;
}
}
// 如果找不到值相同的Student对象,则创建一个Student对象
//并把它加到对象池中然后返回该对象。
Student stu = new Student(name, age);
pool.add(stu);
return stu;
}
}
class Test {
public static void main(String[] args) {
Student stu1 = Student.newInstance("tangliang", 30);
// 对象池中拿
Student stu2 = Student.newInstance("tangliang", 30);
// 所以stu1==stu2
Student stu3 = new Student("tangliang", 30);
// 重新创建,所以stu1!=stu3
System.out.println(stu1 == stu2);
System.out.println(stu1 == stu3);
}
}
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语言再一次cheat了你,以上代码会输出“NO!”。为什么会这样呢?其实这是由实型数据的存储方式决定的。我们知道实型数据在内存空间中是近似存储的,所以2.0-1.1的结果不是0.9,而是0.88888888889。所以在做实型数据是否相等的判断时要非常的谨慎。一般来说,我们不建议在代码中直接判断两个实型数据是否相等,如果一定要比较是否相等的话我们也采用以下方式来判断: if(Math.abs(a-b)<1e-5){ //相等 }else{ //不相等 } 上面的代码判断a与b之差的绝对值是否小于一个足够小的数字,如果是,则认为a与b相等,否则,不相等。
7,判断奇数 以下的方法判断某个整数是否是奇数,考察是否正确: 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); }
8,拓宽数值类型会造成精度丢失吗? Java语言的8种基本数据类型中7种都可以看作是数值类型,我们知道对于数值类型的转换有一个规律:从窄范围转化成宽范围能够自动类型转换,反之则必须强制转换。请看下图: byte-->short-->int-->long-->float-->double char-->int 我们把顺箭头方向的转化叫做拓宽类型,逆箭头方向的转化叫做窄化类型。一般我们认为因为顺箭头方向的转化不会有数据和精度的丢失,所以Java语言允许自动转化,而逆箭头方向的转化可能会造成数据和精度的丢失,所以Java语言要求程序员在程序中明确这种转化,也就是强制转换。那么拓宽类型就一定不会造成数据和精度丢失吗?请看下面代码: int i=2000000000; int num=0; for(float f=i;f< 哈哈,你快要不相信你的眼睛了,结果竟然是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的原因了。" System.out.println(f1="=f2);" f2="i+50;" float f1="i;" i="2000000000;" int ,那么请运行一下,结果会让你大吃一惊!没错,输出结果是0,难道这个循环根本就没有执行哪怕一次?确实如此,如果你还不死心,我带你你看一个更诧异的现象,运行以下代码,看输出什么? 如果你回答50 请考察以上代码输出多少? System.out.println(num); } num++;>
9,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。由此可见,在使用复合赋值运算符时还得小心,因为这种类型转换是在不知不觉中进行的,所以得到的结果就有可能和你的预想不一样。
我们知道得到String对象有两种办法: String str1="hello"; String str2=new String("hello"); 这两种创建String对象的方法有什么差异吗?当然有差异,差异就在于第一种方法在对象池中拿对象,第二种方法直接生成新的对象。在JDK5.0里面,Java虚拟机在启动的时候会实例化9个对象池,这9个对象池分别用来存储8种基本类型的包装类对象和String对象。当我们在程序中直接用双引号括起来一个字符串时,JVM就到String的对象池里面去找看是否有一个值相同的对象,如果有,就拿现成的对象,如果没有就在对象池里面创建一个对象,并返回。所以我们发现下面的代码输出true: String str1="hello"; String str2="hello"; System.out.println(str1==str2); 这说明str1和str2指向同一个对象,因为它们都是在对象池中拿到的,而下面的代码输出为false: String str3="hello" String str4=new String("hello"); System.out.println(str3==str4); 因为在任何情况下,只要你去new一个String对象那都是创建了新的对象。与此类似的,在JDK5.0里面8种基本类型的包装类也有这样的差异: Integer i1=5;//在对象池中拿 Integer i2 =5;//所以i1==i2 Integer i3=new Integer(5);//重新创建新对象,所以i2!=i3 对象池的存在是为了避免频繁的创建和销毁对象而影响系统性能,那我们自己写的类是否也可以使用对象池呢?当然可以,考察以下代码: import java.util.*;
public class Student {
private String name;
private int age;
private static HashSet<Student> pool = new HashSet<Student>();
// 对象池
public Student(String name, int age) {
this.name = name;
this.age = age;
}
// 使用对象池来得到对象的方法
public static Student newInstance(String name, int age) {
// 循环遍历对象池
for (Student stu : pool) {
if (stu.name.equals(name) && stu.age == age) {
return stu;
}
}
// 如果找不到值相同的Student对象,则创建一个Student对象
//并把它加到对象池中然后返回该对象。
Student stu = new Student(name, age);
pool.add(stu);
return stu;
}
}
class Test {
public static void main(String[] args) {
Student stu1 = Student.newInstance("tangliang", 30);
// 对象池中拿
Student stu2 = Student.newInstance("tangliang", 30);
// 所以stu1==stu2
Student stu3 = new Student("tangliang", 30);
// 重新创建,所以stu1!=stu3
System.out.println(stu1 == stu2);
System.out.println(stu1 == stu3);
}
}
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语言再一次cheat了你,以上代码会输出“NO!”。为什么会这样呢?其实这是由实型数据的存储方式决定的。我们知道实型数据在内存空间中是近似存储的,所以2.0-1.1的结果不是0.9,而是0.88888888889。所以在做实型数据是否相等的判断时要非常的谨慎。一般来说,我们不建议在代码中直接判断两个实型数据是否相等,如果一定要比较是否相等的话我们也采用以下方式来判断: if(Math.abs(a-b)<1e-5){ //相等 }else{ //不相等 } 上面的代码判断a与b之差的绝对值是否小于一个足够小的数字,如果是,则认为a与b相等,否则,不相等。
7,判断奇数 以下的方法判断某个整数是否是奇数,考察是否正确: 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); }
8,拓宽数值类型会造成精度丢失吗? Java语言的8种基本数据类型中7种都可以看作是数值类型,我们知道对于数值类型的转换有一个规律:从窄范围转化成宽范围能够自动类型转换,反之则必须强制转换。请看下图: byte-->short-->int-->long-->float-->double char-->int 我们把顺箭头方向的转化叫做拓宽类型,逆箭头方向的转化叫做窄化类型。一般我们认为因为顺箭头方向的转化不会有数据和精度的丢失,所以Java语言允许自动转化,而逆箭头方向的转化可能会造成数据和精度的丢失,所以Java语言要求程序员在程序中明确这种转化,也就是强制转换。那么拓宽类型就一定不会造成数据和精度丢失吗?请看下面代码: int i=2000000000; int num=0; for(float f=i;f< 哈哈,你快要不相信你的眼睛了,结果竟然是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的原因了。" System.out.println(f1="=f2);" f2="i+50;" float f1="i;" i="2000000000;" int ,那么请运行一下,结果会让你大吃一惊!没错,输出结果是0,难道这个循环根本就没有执行哪怕一次?确实如此,如果你还不死心,我带你你看一个更诧异的现象,运行以下代码,看输出什么? 如果你回答50 请考察以上代码输出多少? System.out.println(num); } num++;>
9,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。由此可见,在使用复合赋值运算符时还得小心,因为这种类型转换是在不知不觉中进行的,所以得到的结果就有可能和你的预想不一样。
发表评论
-
JAVA最全的编码规范
2008-08-01 14:43 1255(1) 类名首字母应该大写。字段、方法以及对象(句柄)的首字母 ... -
java读写xml操作
2008-07-30 13:14 17445先定义一个简单的input.xml结构,让java进行inpu ... -
Java初学者都必须理解的六大问题
2008-07-29 18:14 1162Java初学者都必须理解的 ... -
HW interview2
2008-07-29 17:33 11881、有哪些数据类型 Java定义了8种简单类型:byte、 ... -
HW interview
2008-07-29 17:32 10271、有哪些数据类型 Java定义了8种简单类型:byte、 ... -
ORA-12516
2008-07-28 13:41 3995ORA-12516, TNS:listener could n ... -
Algorithmic Efficiency02
2008-07-28 10:51 1104The ability to analyze a piece ... -
Algorithmic Efficiency01
2008-07-28 10:50 1143Algorithmic Efficiency -- Beati ... -
Java语言的26个细节02-浅复制与深复制
2008-07-27 10:16 11394,浅复制与深复制 1)浅复制与深复制概念 ⑴浅复制(浅克隆) ... -
Java语言的26个细节01
2008-07-27 10:15 1377Java作为一门优秀的面向 ... -
java对象equals方法的重写
2008-07-26 19:07 13620什么时候需要重写equals( ... -
Java面试题-答案
2008-07-25 21:20 1034Java面试题(答案) 第 ... -
Java陷阱
2008-07-25 21:14 992面试是没什么道理可讲的,它的题目有的不合情理、脱离实际。有在纸 ... -
ant使用简介
2008-07-25 09:19 993ant使用简介 原地址:http://www ... -
理解“回调函数”
2008-07-25 00:01 1797java理解“回调函数” ... -
使用回调和线程处理一个耗时响应过程
2008-07-24 23:55 1121使用回调和线程处理一个耗时响应过程 现在程序中有许多涉及长耗 ... -
对象的串行化
2008-07-24 21:32 945转载自http://gardenlee.bokee ... -
Java虚拟机类装载:原理、实现与应用
2008-07-24 09:28 1049By jorren 发表于 2005-12-21 15:13: ... -
ASSERT的用法
2008-07-23 14:50 3704ASSERT的用法 ASSERT( booleanExpres ... -
cookie机制和session机制
2008-07-22 09:06 1898一、cookie机制和session机 ...
相关推荐
【Java语言的26个细节】涵盖了Java编程中的一些易被忽视或误解的要点,以下是对其中几个细节的详细说明: 1. **位移运算越界处理**:Java中的位移运算符`和`>>`在处理整型数据时,会进行优化。对于左移运算`a,当...
Java语言的26个细节是每一个Java开发者都应掌握的核心概念和最佳实践。这26个细节涵盖了语言特性、编程规范、性能优化以及错误处理等多个方面。以下是对这些细节的详细解析: 1. **自动装箱与拆箱**:Java 5引入了...
本书由“java之父”Jame Gosling 以及... 本书侧重于java技术细节和内幕,全面,准确,详尽地介绍了java语言及其语法,论述了java编译器所要检查的语法和java运行模式的各个方面,同时还描述了java语言最重要的新特征。
Java语言的26个细节[原创。。。。。。。。。。。。。。。
一个桌面程序,通过游戏闯关的方式来了解一些java的语言细节 一个桌面程序,通过游戏闯关的方式来了解一些java的语言细节 一个桌面程序,通过游戏闯关的方式来了解一些java的语言细节 一个桌面程序,通过游戏闯关的...
如果你想知道Java语言构造的精确含义以及各种技术细节,本书是好的资谭。 本书全面、准确而详细地讨论TJava编程语言,是Java语言新版本的规范。全书从Java基础的文法、类型、变量到高级特性lambda表达式、线程与锁等...
一个桌面程序,通过游戏闯关的方式来了解一些java的语言细节.zip一个桌面程序,通过游戏闯关的方式来了解一些java的语言细节.zip一个桌面程序,通过游戏闯关的方式来了解一些java的语言细节.zip一个桌面程序,通过...
Oracle官方发布,Java编程语言及规范创建人撰写,国内资深Java技术专家翻译,Amazon畅销书!, 基于最新Java SE 8,完整且准确地阐述Java编程语言..., 对每一个使用Java语言进行企业级开发的程序员都是不可或缺的资源。
《Java语言程序设计教程》是张席编著的一本针对初学者和进阶者的学习资料,这本书深入浅出地介绍了Java编程语言的核心概念和技术。Java作为一种广泛应用的编程语言,其强大的跨平台能力和丰富的类库使其在软件开发...
总的来说,Java语言规范和JVM规范是每个Java开发者深入学习的基石。通过研究这些文档,开发者不仅能理解语言的细节,还能掌握JVM的运行机制,从而编写出更高效、更稳定的代码。无论是初学者还是经验丰富的开发者,都...
Java语言是目前世界上最流行的编程语言之一,尤其在企业级应用开发中占据主导地位。国家开放大学的"Java语言程序设计"课程旨在让学生掌握Java的基本概念、语法和编程技巧。形考任务4是这个课程的一个重要组成部分,...
Java语言重要概述 Java是一种广泛使用的面向对象的编程语言,由Sun Microsystems(现已被Oracle公司收购)于1995年发布。它的设计目标是具有跨平台性、可移植性、安全性和高效性,使得Java成为开发桌面应用、Web...
### Java语言的详细入门教程 #### 1.1 Java语言的发展 ##### 1.1.1 Java语言的产生 Java语言起源于1990年代初的Sun Microsystems,由James Gosling...接下来,我们将在第二章中进一步探讨Java语言的开发环境和工具。
Java语言说明书是一个全面涵盖Java编程语言的资源,旨在帮助开发者深入理解并熟练运用Java进行软件开发。Java,作为一种广泛使用的面向对象的编程语言,由Sun Microsystems(现为Oracle Corporation的一部分)于1995...
Java语言作为一种广泛使用的编程语言,深受开发者喜爱,尤其对于初学者来说,它是进入计算机编程世界的理想选择。本书《JAVA语言入门》旨在为新手提供全面、易懂的Java学习指南。.chm文件是一种Windows帮助文档格式...