我记得读过一本名为:
的书描述了Java代码中的奇怪行为。看起来完全无辜的东西,但实际上执行的东西与显而易见的完全不同。一个例子是:
(编辑:这篇文章不是对这个特定例子的讨论。这是我提到的书中的第一个例子。我要求你可能遇到过其他奇怪的事情。)
此代码是否可用于识别数字是否为奇数?
public static boolean isOdd(int i) {
return i % 2 == 1;
}
答案当然不是。如果你插入一个负数,当数字是奇数时,你会得到错误的答案。正确的答案是:
public static boolean isOdd(int i) {
return i % 2 != 0;
}
现在我的问题是您遇到的最奇怪,最直观的Java代码是什么?(我知道这不是一个真正的问题,也许我应该将其发布为社区维基,请对此的建议)
答案 0 :(得分:3)
最近有一个我blogged about,给出了以下两个类:
public class Base
{
Base() {
preProcess();
}
void preProcess() {}
}
public class Derived extends Base
{
public String whenAmISet = "set when declared";
@Override void preProcess()
{
whenAmISet = "set in preProcess()";
}
}
当您创建新的whenAmISet
对象时,您认为Derived
的价值是什么?
给出以下简单的主要类:
public class Main
{
public static void main(String[] args)
{
Derived d = new Derived();
System.out.println( d.whenAmISet );
}
}
大多数人说看起来输出应该是“在preProcess()中设置”,因为 Creation of New Class Instances的JLS部分详细说明了创建对象时发生的事件序列。Base
构造函数调用该方法,但事实并非如此。 <{1}}类成员在调用Derived
构造函数中的preProcess()
方法后初始化,该方法将覆盖Base
中设置的值。< / p>
答案 1 :(得分:2)
事实上,Java Puzzlers还有94个谜题,这些谜题有时会出现奇怪的,有时甚至是欺骗性的行为。
答案 2 :(得分:2)
我遇到的最违反直觉的概念是来自Josh Bloch的PECS(Producer Extends,Consumer Super)。这个概念非常好,但你认为消费者/生产者在某种情况下是什么 - 我最初会想到的方法本身。但不,参数集合是这个概念中的P / C:
public <T> void consumeTs(Collection<? extends T> producer);
public <T> void produceTs(Collection<? super T> consumer);
有时很困惑。
答案 3 :(得分:2)
我们曾经在遗留代码库(最初被5级继承结构和几个间接伪装伪装)中偶然发现了类似的东西:
public abstract class A {
public A() {
create();
}
protected abstract void create();
}
public class B extends A {
private Object bMember=null;
protected void create() {
bMember=getNewObject();
}
}
当您致电B
构造函数时,它会调用A
默认构造函数,调用B
的{{1}}方法,create()
初始化。
或者我们天真地想。因为在调用bMember
之后,初始化过程的下一步是将明确定义的默认值分配给super()
成员,实际上将B
重置为bMember
。
程序实际上工作正常,因为null
稍后再次通过另一条路线分配。
在某些时候,我们删除了bMember
显然无用的null
默认值,突然程序行为发生了变化。
答案 4 :(得分:1)
我最近发现Math.abs(i)并不总是产生正数。
Math.abs(Integer.MIN_VALUE)产生-2 ^ 31
为什么呢?因为有一个正整数而不是负数。 2 ^ 31比Integer.MAX_VALUE多一个,因此溢出到-2 ^ 31
我猜大多数其他语言的情况类似,但我在java中遇到过这个
答案 5 :(得分:0)
我倾向于同意你的意见,但我已经阅读(并希望有人可以提供一个或两个链接)的文章,这些文章解释了Java的'%'与其'/'一致,这对任何人都足够了。根据我自己的经验:
Java的'%'运算符在处理负输入时与其他语言略有不同。我个人更喜欢返回非负数的“模数”运算符,例如
-5 % 2 == 1
这将使您的示例有效。我认为这个操作有一个正式名称,但我现在想不到它所以我会坚持使用“模数”。两种形式之间的区别在于,'a%b'的Java变体执行'a / b'并向零舍入(并减去由'a'产生的结果),而首选操作则向下舍入。
如果结果'r'为'0&lt; = r&lt;&lt; = r&lt; b'(一个示例是在将点映射到可以延伸'&lt; 0'的平面上的瓦片上时找到从瓦片的最左边缘的偏移。这种体验的一个例外是在大学任务中对Java程序中的整数算术进行静态分析。正是在这个任务期间,Java的'%'的微妙之处曝光,我不顾一切地用“固定”版本替换它。这一切都适得其反,因为关键在于模拟Java如何算术,而不是实现我自己喜欢的那种。
答案 6 :(得分:0)
查看 java.util.concurrent.SynchronousQueue 中定义的方法: http://java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent/SynchronousQueue.html
一半的方法总是返回 null / true / false / 零:
当你第一次开始使用它时,并不是很明显,而不是先阅读文档。
答案 7 :(得分:-1)
奇数和偶数通常被认为是正数,所以我认为不要认为将负数看作奇数或偶数是有用的。通常要测试是否设置了最低位。
这些对于负数也是好的。
if ((i & 1) == 0) // lowest bit not set.
if ((i & 1) == 1) // lowest bit set.
或
if ((i & 1) != 0) // lowest bit set.