与其他平台一样,跟进这个问题似乎合乎逻辑:Java中常见的非显而易见的错误是什么?似乎他们应该工作的事情,但不要。
我不会就如何构建答案给出指导,或者什么“太容易”被认为是问题,因为这就是投票的目的。
另见:
答案 0 :(得分: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。
答案 1 :(得分:35)
使用==
而不是.equals()
比较对象的相等性 - 它们对于基元的行为完全不同。
这个问题确保新移民在"foo" == "foo"
但new String("foo") != new String("foo")
时会被迷惑。
答案 2 :(得分:30)
覆盖equals()但不覆盖hashCode()
使用地图,集合或列表时,可能会产生意想不到的结果。
答案 3 :(得分:30)
我认为一个非常狡猾的方法是String.substring
方法。这会将相同的基础char[]
数组重新用作具有不同offset
和length
的原始字符串。
这可能会导致很难看到内存问题。例如,您可能正在解析一些极小的文件(可能是XML
)。如果您已将整个文件转换为String
(而不是使用Reader
来“遍历”文件)并使用substring
来抓取您想要的位,那么您仍然需要在幕后完整的文件大小的char[]
数组周围。我已经看过很多次这种情况,很难发现。
事实上,这是接口永远无法与实现完全分离的完美示例。几年前,对于为什么你应该怀疑第三方代码的质量,这是一个完美的介绍(对我而言)。
答案 4 :(得分:25)
SimpleDateFormat不是线程安全的。
答案 5 :(得分:21)
尝试阅读充满可怕内容的Java Puzzlers,即使这些东西不是你每天都碰到的东西。但它会破坏你对这种语言的大部分信心。
答案 6 :(得分:21)
有两个让我烦恼不已。
首先,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,当它们都是对象时。
能够说出
List<Integer> stuff = new ArrayList<Integer>();
stuff.add(5);
所以很好。它使Java变得更加可用,不必将所有那些“new Integer(5)”和“((Integer)list.get(3))。intValue()”放在所有地方。但这些好处伴随着这个问题。
答案 7 :(得分:20)
List<Integer> list = new java.util.ArrayList<Integer>();
list.add(1);
list.remove(1); // throws...
旧的API并没有考虑到拳击的设计,所以用原语和对象重载。
答案 8 :(得分:17)
我刚刚遇到过这个:
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
- double[]
。
我应该想一下,当需要0ms来对750000个元素数组进行排序时......
答案 9 :(得分:16)
这个曾经嘲笑我几次,我听说有不少经验丰富的java开发人员浪费了很多时间。
ClassNotFoundException ---你知道这个类在classpath中但是你不确定为什么这个类没有被加载。
实际上,这个类有一个静态块。静态块中有一个异常,有人吃了异常。他们不应该。他们应该抛出ExceptionInInitializerError。所以,总是寻找静止的块来绊倒你。它还有助于移动静态块中的任何代码进入静态方法,以便使用调试器更容易调试方法。
答案 10 :(得分:16)
浮筒
我不知道很多次见过
floata == floatb
“正确”测试应该
Math.abs(floata - floatb) < 0.001
我真的希望BigDecimal的文字语法是默认的十进制类型......
答案 11 :(得分:13)
并不是特定于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
。
确保您知道%
运算符如何处理负数,无论您使用的语言是什么。
答案 12 :(得分:8)
我认为,当我还是一名年轻的程序员时,我总是会感到困惑,当你从正在迭代的数组中删除时,是并发修改异常:
List list = new ArrayList();
Iterator it = list.iterator();
while(it.hasNext()){
//some code that does some stuff
list.remove(0); //BOOM!
}
答案 13 :(得分:8)
从事件派发线程外部操作Swing组件可能会导致极难找到的错误。这甚至是我们(经验丰富的程序员,拥有3年6年的java经验)经常忘记!有时这些错误会在编写正确的代码并随后不经意地重构之后潜入......
请参阅tutorial为什么必须。
答案 14 :(得分:8)
不可变字符串,这意味着某些方法不会更改原始对象,而是返回修改后的对象副本。从Java开始我曾经一直忘记这一点,并想知道为什么替换方法似乎不适用于我的字符串对象。
String text = "foobar";
text.replace("foo", "super");
System.out.print(text); // still prints "foobar" instead of "superbar"
答案 15 :(得分:7)
如果你有一个与构造函数同名的方法,那么BUT有一个返回类型。虽然这个方法看起来像构造函数(对于菜鸟),但它不是。
将参数传递给main方法 - 这需要一些时间来让新手习惯。
传递。作为在当前目录中执行程序的类路径的参数。意识到字符串数组的名称不明显
hashCode和equals:很多具有5年以上经验的java开发人员都不太明白。
设置与列表
直到JDK 6,Java没有NavigableSets让你轻松迭代Set和Map。
答案 16 :(得分:7)
整数分部
1/2 == 0 not 0.5
答案 17 :(得分:5)
使用?
泛型通配符。
人们看到它并认为他们必须,例如如果他们想要List<?>
他们可以添加任何内容,请使用List
,而不必停下来认为List<Object>
已经这样做了。然后他们想知道为什么编译器不会让他们使用add()
,因为List<?>
实际上意味着“我不知道某种特定类型的列表”,所以你唯一可以做的就是List
从中获取Object
个实例。
答案 18 :(得分:4)
(联合国)拳击和长/长混淆。与Java 5之前的经验相反,您可以在下面的第二行获得NullPointerException。
Long msec = getSleepMsec();
Thread.sleep(msec);
如果getSleepTime()返回null,则取消装箱将抛出。
答案 19 :(得分:4)
默认哈希是非确定性的,因此如果用于HashMap中的对象,该地图中条目的顺序可能会在不同运行之间发生变化。
作为一个简单的演示,以下程序可以根据其运行方式给出不同的结果:
public static void main(String[] args) {
System.out.println(new Object().hashCode());
}
为堆分配了多少内存,或者是否在调试器中运行它都可以改变结果。
答案 20 :(得分:4)
当您从父缓冲区创建duplicate
或slice
ByteBuffer
时,它does not inherit the value of the order
property,因此这样的代码将无法满足您的期望:
ByteBuffer buffer1 = ByteBuffer.allocate(8);
buffer1.order(ByteOrder.LITTLE_ENDIAN);
buffer1.putInt(2, 1234);
ByteBuffer buffer2 = buffer1.duplicate();
System.out.println(buffer2.getInt(2));
// Output is "-771489792", not "1234" as expected
答案 21 :(得分:3)
在常见的陷阱中,众所周知但仍偶尔咬人的程序员,在所有类C语言中都有经典的if (a = b)
。
在Java中,当然,只有当a和b是布尔值时,它才能工作。但我经常看到像if (a == true)
这样的新手测试(虽然if (a)
更短,更具可读性和更安全......)并偶尔写错误if (a = true)
,想知道为什么测试不起作用。
对于那些没有得到它的人:最后一个语句首先将true
分配给a
,然后进行测试,这总是成功!
-
一个咬了很多新手的人,甚至一些分心的更有经验的程序员(在我们的代码中找到它),if (str == "foo")
。请注意,我总是想知道为什么Sun覆盖字符串的+符号但不覆盖== 1,至少对于简单的情况(区分大小写)。
对于新手:==
比较引用,而不是字符串的内容。您可以有两个相同内容的字符串,存储在不同的对象(不同的引用)中,因此==
将为false。
简单示例:
final String F = "Foo";
String a = F;
String b = F;
assert a == b; // Works! They refer to the same object
String c = "F" + F.substring(1); // Still "Foo"
assert c.equals(a); // Works
assert c == a; // Fails
-
我也看到if (a == b & c == d)
或类似的东西。它(奇怪地)工作但我们丢失了逻辑运算符快捷方式(不要试着写:if (r != null & r.isSomething())
!)。
对于新手:在评估a && b
时,如果a为false,则Java不会评估b。在a & b
中,Java评估两个部分然后执行操作;但第二部分可能会失败。
[编辑] J Coombs的好建议,我更新了我的答案。
答案 22 :(得分:2)
非统一型系统与对象取向思想相矛盾。即使所有内容都不是堆分配的对象,程序员仍应允许通过调用它们上的方法来处理原始类型。
带有类型擦除的泛型类型系统实现非常糟糕,并且当他们在Java中学习第一个泛型时会抛弃大多数学生:为什么如果已经提供了类型参数,我们仍然需要进行类型转换?是的,他们确保了向后兼容性,但费用相当愚蠢。
答案 23 :(得分:1)
我想指出的另一个问题是使API变得通用的(过于普遍)驱动。使用精心设计的通用代码很好。设计自己的很复杂。 非常复杂!
只需查看新Swing JTable
中的排序/过滤功能。这是一场彻头彻尾的噩梦。很明显,你可能想要在现实生活中链接过滤器,但我发现如果不使用所提供的类的原始类型版本就不可能这样做。
答案 24 :(得分:1)
首先,这是我今天抓到的一个。这与Long
/ long
混淆有关。
public void foo(Object obj) {
if (grass.isGreen()) {
Long id = grass.getId();
foo(id);
}
}
private void foo(long id) {
Lawn lawn = bar.getLawn(id);
if (lawn == null) {
throw new IllegalStateException("grass should be associated with a lawn");
}
}
显然,这些名字已经改变以保护无辜者:)
答案 25 :(得分:1)
System.out.println(Calendar.getInstance(TimeZone.getTimeZone("Asia/Hong_Kong")).getTime());
System.out.println(Calendar.getInstance(TimeZone.getTimeZone("America/Jamaica")).getTime());
输出相同。
答案 26 :(得分:0)
IMHO 1.使用vector.add(Collection)代替vector.addall(Collection)。第一个将集合对象添加到vector,第二个添加集合的内容。 2.尽管与编程无关,但是使用来自xerces,jdom等多个源的xml解析器。依靠不同的解析器并将其罐子放在类路径中是一场噩梦。
答案 27 :(得分:0)
我有一些调试TreeSet的乐趣,因为我不知道来自API的这些信息:
请注意,如果要正确实现Set接口,则由set维护的排序(无论是否提供显式比较器)必须与equals一致。 (请参阅Comparable或Comparator以获得与equals一致的精确定义。)这是因为Set接口是根据equals操作定义的,但TreeSet实例使用compareTo(或compare)方法执行所有键比较,因此从集合的角度来看,通过这种方法被认为相等的密钥是相等的。集合的行为即使其排序与equals不一致也是明确定义的;它只是不遵守Set接口的一般合同。 http://download.oracle.com/javase/1.4.2/docs/api/java/util/TreeSet.html
正在添加具有正确的equals / hashcode实现的对象,并且再也没有看到过,因为compareTo实现与equals不一致。