与其他平台一样,跟进这个问题似乎合乎逻辑:Java中常见的非明显错误是什么?似乎他们应该工作的事情,但不是.
我不会给出如何构建答案的指导,或者什么"太容易"被认为是一个问题,因为这就是投票的目的.
也可以看看:
Perl - 常见问题
.NET - 常见陷阱
butterchicke.. 36
"a,b,c,d,,,".split(",").length
返回4,而不是 7,因为你可能(我当然这样做)期望.split
忽略返回的所有尾随空字符串.这意味着:
",,,a,b,c,d".split(",").length
返回7!为了得到我认为的"最不惊人"的行为,你需要做一些非常惊人的事情:
"a,b,c,d,,,".split(",",-1).length
得到7.
"a,b,c,d,,,".split(",").length
返回4,而不是 7,因为你可能(我当然这样做)期望.split
忽略返回的所有尾随空字符串.这意味着:
",,,a,b,c,d".split(",").length
返回7!为了得到我认为的"最不惊人"的行为,你需要做一些非常惊人的事情:
"a,b,c,d,,,".split(",",-1).length
得到7.
使用==
而不是.equals()
- 比较对象的相等性- 对于基元,其行为完全不同.
这种疑难杂症,确保新人都昏昏沉沉的时候"foo" == "foo"
,但是new String("foo") != new String("foo")
.
覆盖equals()但不覆盖hashCode()
使用地图,集合或列表时,它可能会产生意想不到的结果.
我觉得这个String.substring
方法非常狡猾.这将重新使用与char[]
原始字符串相同的底层数组,并使用不同的offset
和length
.
这可能导致非常难以看到的内存问题.例如,您可能正在解析一些极小的文件(XML
可能).如果您已将整个文件转换为String
(而不是用于Reader
"遍历"文件)并使用substring
抓取您想要的位,您仍然char[]
在幕后携带完整的文件大小的数组.我已经看过很多次了,很难发现.
实际上,这是为什么界面永远不能与实现完全分离的完美示例.几年前,这是一个完美的介绍(对我来说),为什么你应该怀疑第三方代码的质量.
SimpleDateFormat不是线程安全的.
尝试阅读Java Puzzlers,它充满了可怕的东西,即使它不是你每天都碰到的东西.但它会破坏你对语言的大部分信心.
有两个让我很烦恼.
首先,Java Date和Calendar类非常混乱.我知道有修复它们的建议,我只希望它们成功.
Calendar.get(Calendar.DAY_OF_MONTH)是从1开始的
Calendar.get(Calendar.MONTH)是从0开始的
另一个是Integer vs int(这适用于对象的任何原始版本).这特别是由于没有将Integer视为与int不同而导致的烦恼(因为你可以在很多时候因为自动装箱而对待它们).
int x = 5; int y = 5; Integer z = new Integer(5); Integer t = new Integer(5); System.out.println(5 == x); // Prints true System.out.println(x == y); // Prints true System.out.println(x == z); // Prints true (auto-boxing can be so nice) System.out.println(5 == z); // Prints true System.out.println(z == t); // Prints SOMETHING
由于z和t是对象,即使它们保持相同的值,它们(很可能)也是不同的对象.你真正的意思是:
System.out.println(z.equals(t)); // Prints true
追踪这个可能是一种痛苦.你去调试一切,一切看起来都很好,你终于发现你的问题是5!= 5当两者都是对象时.
能够说
Liststuff = new ArrayList (); stuff.add(5);
是这么好看.它使Java变得更加可用,不必将所有那些"new Integer(5)"和"((Integer)list.get(3)).intValue()"放在所有地方.但这些好处伴随着这个问题.
Listlist = new java.util.ArrayList (); list.add(1); list.remove(1); // throws...
旧的API并没有考虑到拳击的设计,因此与原语和对象重载.
这个我刚刚遇到的:
double[] aList = new double[400]; List l = Arrays.asList(aList); //do intense stuff with l
有谁看到了问题?
会发生什么,Arrays.asList()
期望一个对象类型数组(例如,Double []).如果它只是为前一个ocde抛出一个错误,那就太好了.但是,asList()
也可以采用这样的参数:
Arrays.asList(1, 9, 4, 4, 20);
那么代码所做的就是List
用一个元素创建一个- a double[]
.
我应该想一下,当需要0ms才能对750000元素数组进行排序时......
这个曾经打败过我几次,我听说有不少经验丰富的java开发人员浪费了很多时间.
ClassNotFoundException ---你知道类在类路径中但是你不确定为什么类没有被加载.
实际上,这个类有一个静态块.静态块中有一个异常,有人吃了异常.他们不应该.他们应该抛出ExceptionInInitializerError.所以,总是寻找静止的块来绊倒你.它还有助于移动静态块中的任何代码进入静态方法,以便使用调试器更容易调试方法.
花车
我不知道很多次见过我
floata == floatb
应该进行"正确"的测试
Math.abs(floata - floatb) < 0.001
我真的希望BigDecimal的文字语法是默认的十进制类型...
不是特定于Java,因为许多(但不是全部)语言以这种方式实现它,但是%
运算符不是真正的模运算符,因为它与负数一起使用.这使得它成为余数运算符,如果您不了解它,可能会带来一些惊喜.
以下代码似乎打印"偶数"或"奇数",但事实并非如此.
public static void main(String[] args) { String a = null; int n = "number".hashCode(); switch( n % 2 ) { case 0: a = "even"; break; case 1: a = "odd"; break; } System.out.println( a ); }
问题是"数字"的哈希码是负数,因此n % 2
交换机中的操作也是负数.由于交换机中没有处理否定结果的情况,因此变量a
永远不会被设置.该程序打印出来null
.
%
无论您使用何种语言,请确保您知道操作员如何处理负数.
从事件派发线程外部操作Swing组件可能会导致很难找到的错误.这甚至是我们(经验丰富的程序员,拥有3年6年的java经验)经常忘记!有时这些错误在编写正确的代码并随后不经意地重构之后潜入......
请参阅本教程为什么必须.
不可变字符串,这意味着某些方法不会更改原始对象,而是返回修改后的对象副本.从Java开始我曾经一直忘记这一点,并想知道为什么替换方法似乎不适用于我的字符串对象.
String text = "foobar"; text.replace("foo", "super"); System.out.print(text); // still prints "foobar" instead of "superbar"
我认为,当我还是一名年轻的程序员时,我总是会感到困惑,从你正在迭代的数组中删除时是并发修改异常:
List list = new ArrayList(); Iterator it = list.iterator(); while(it.hasNext()){ //some code that does some stuff list.remove(0); //BOOM! }
如果你有一个与构造函数同名的方法,那么BUT有一个返回类型.虽然这个方法看起来像构造函数(对于菜鸟),但它不是.
将参数传递给main方法 - 新手习惯需要一些时间.
过世.作为在当前目录中执行程序的类路径的参数.
意识到字符串数组的名称并不明显
hashCode和equals:很多具有5年以上经验的java开发人员都不太了解它.
设置与列表
直到JDK 6,Java没有NavigableSets来让你轻松地遍历Set和Map.
整数除法
1/2 == 0 not 0.5
使用?
泛型通配符.
人们看到它并认为他们必须,例如List>
在他们想要的时候使用List
他们可以添加任何东西,而不必停下来认为List
已经做到了.然后他们想知道为什么编译器不会让它们使用add()
,因为它List>
实际上意味着"我不知道某种特定类型的列表",所以你唯一可以做的List
就是从中获取Object
实例.