这是我第一次编写JAVA,我正尝试从正确的轨道开始。

利用我的C ++知识,我能够理解三元运算符的语法,但是我不知道在JAVA中应该如何正确处理它。我已经使用Ideone对此进行了测试,其中包括imports,该程序显然甚至不需要它,或者看起来如此。

我想对提供的所有内容进行审查,甚至如果涉及到告诉我,无论出于何种原因,从Ideone开始并不是最佳选择。

import java.util.*;
import java.lang.*;
import java.io.*;

class BeginnerFizzBuzz {
    public static void main(String[] args) {
        for (int i = 1; i <= 100; ++i) {
            System.out.println(
                (i % 15 == 0) ? "FizzBuzz" :
                (i % 3 == 0) ? "Fizz" :
                (i % 5 == 0) ? "Buzz" :
                i
            );
        }
    }
}


评论

虽然这肯定是有效的代码,但是许多嵌套的三元组通常使代码更难阅读和调试。通常,我坚持使用1,有时如果表达式较短,我会选择2。

@ user26721:我本来是考虑if / else,但是我认为那也是多余的。但是我现在看到它不太可读。

几个名词的含义:(1)语言名称是Java(大写/小写)。 (2)?:运算符是条件运算符; “三元运算符”是采用三个操作数的任何运算符(在大多数编程语言中只有一个三元运算符,因此人们在意指“条件运算符”时经常说“三元运算符”)。

@GregHewgill是“ CR事情”(请参阅​​此meta post).. JAVA是故意的;)

@ Mat'sMug:恐怕我听不懂。 Java和JAVA在什么基础上被视为两种不同的语言?据我所知,它们遵循Java语言规范中规定的相同规则。

#1 楼


即使涉及到告诉我无论出于何种原因,从Ideone开始都不是最佳选择。


作为初学者,从Ideone开始并不是IMO的最佳选择。由于多种原因:


使用import xxx.xxx.*不是一个好习惯。仅导入您实际需要的东西。在这种情况下,您甚至不需要导入任何内容(因为所需的内容是java.lang软件包的一部分。
在IdeOne中,文件名是“ Main.java”,无法更改它,这意味着您将不会知道文件中的公共类必须与文件本身具有相同的名称,即public class SomeClass必须位于名为SomeClass.java的文件中。
在IdeOne中,您无法声明包名称。包名称主要用于标识项目和代码的作者等,它们通常是您的域名倒退。例如,由于我拥有zomis.net,所以我的包名称是net.zomis.someproject。如果您不拥有域,好吧...现在弥补一下。com.jamal可以为您工作。无论哪种方式,都不鼓励使用“默认”软件包(即根本不声明软件包)。
在IdeOne中,没有直接编译器错误的反馈。您必须在代码上单击“运行”以确保它没有编译器错误。在所有现代Java IDE中,每当您完成一行代码时,盟友都会收到此类反馈(有时您必须按Ctrl + S进行保存,但在此之前它将无法正确显示错误)。乍一看,这可能并不重要,但是一旦习惯了它,您将真的会错过其他IDE的功能。对于代码本身,这还不是很糟糕。最好删除IdeOne的throws Exception声明。

Greg已经介绍了条件运算符的各个方面,因此我只能评论一件事,但这仅是个人观点而已,其他任何内容,请随时完全忽略此评论。

for (int i = 1; i <= 100; ++i) {


在Java中,在此类for循环中更常见的是使用i++而不是++i。尽管在这种情况下这毫无作用,但我确信您已经从C ++(或++ C?)的经验中了解到i++++i之间的区别。

使用时仅凭++ii++(即,未分配其他变量),就存在一个神话,即C ++和C在性能上存在差异,而事实并非如此。

在Java中,也完全相同(如果您想知道的话,也适用于C#)。


当然,在添加另一个分配时使用i++++i总是很重要。如果i = 1



a = i++将使a == 1


a = ++i将使a == 2



两种情况下的声明后均为i == 2


评论


\ $ \ begingroup \ $
我应该提到Ideone更像是Java的“潜行高峰”。我现在还没有计划进一步使用它,我将很快安装一个IDE。
\ $ \ endgroup \ $
– Jamal♦
14年7月11日在14:28

\ $ \ begingroup \ $
前/后递增的东西很不错,但是我主要是根据有关Ideone的建议接受这个答案。我不知道Java IDE之间的工作原理有很大不同。我相信这对于发现此问题的其他人也将是有用的。
\ $ \ endgroup \ $
– Jamal♦
2014年7月12日在20:23

#2 楼

起初,我不确定此代码是否可以正确编译(但显然可以)。这样做的原因是由于Java的两个独立功能。System.out.println()方法具有一个重载,该重载带有一个Object参数(然后调用String.valueOf()以获取该对象的字符串表示形式)。 Java中的每种对象类型都来自Object基类。因此,您传递的文字字符串值实际上是String类的实例,可以将其分配给Object

那在条件链末尾的i呢?那不是字符串,int也不是Object(Java中的原始类型也不是Object的子类)。但是,Java具有一个称为“装箱”的功能,该功能可以将原始值放在对象容器(在本例中为Integer类)内部。编译器会自动对传递给Object参数的原始值进行装箱。

如果要显式构造并强制转换所有代码,则代码大致等效于:

        System.out.println(
            (i % 15 == 0) ? (Object) new String("FizzBuzz") :
            (i % 3 == 0) ? (Object) new String("Fizz") :
            (i % 5 == 0) ? (Object) new String("Buzz") :
            (Object) new Integer(i)
        );


由于您在条件运算符中混合使用不同类型,因此您可能会在此处发现意外行为。例如,以下代码无法编译:

        String s = (
            (i % 15 == 0) ? "FizzBuzz" :
            (i % 3 == 0) ? "Fizz" :
            (i % 5 == 0) ? "Buzz" :
            i
        );


原因是在条件表达式a ? b : c中,bc的类型必须匹配(或者是彼此)。在上述情况下,"Buzz"i是不同的不兼容类型。特别是,不能将i分配给String

评论


\ $ \ begingroup \ $
这将(主要)适用于三元运算符,对吗?如果我要使用ifs通过单独的println()执行此操作,那是否合适?
\ $ \ endgroup \ $
– Jamal♦
2014年7月11日下午0:56

\ $ \ begingroup \ $
是的,这是正确的。编译器将为您传递的每种类型选择一个单独的println()重载。
\ $ \ endgroup \ $
– Greg Hewgill
2014年7月11日下午0:57

\ $ \ begingroup \ $
非常小的nit-pick:“ ...带有Object参数的重载(然后调用toString()方法以获取对象的字符串表示形式)。”这不是严格准确的。它不调用object.toString()方法。它实际上执行String.valueOf(object),对null对象值的处理略有不同...
\ $ \ endgroup \ $
–rolfl
2014年7月11日在2:14

\ $ \ begingroup \ $
抱歉,我没有收到此“评论”。整个问题是因为您不了解某些众所周知的Java机制,所以您不理解所发布的代码吗?即使它们未知,该代码也是可读的,并且可以正常工作。那么-这次审查的重点是什么? (免责声明:我是在认真询问,但这并不意味着要无礼-抱歉,此举听起来太令人反感-这不是我的意图)。
\ $ \ endgroup \ $
– BartoszKP
2014年7月11日15:11

\ $ \ begingroup \ $
@BartoszKP:代码显然应该正确。如果由于该语言的两个微妙的交互功能而不得不阅读两次(并且我对Java有一定的经验),那么也许可以用另一种更明显正确的方式来编写它。那就是我的评论的重点。
\ $ \ endgroup \ $
– Greg Hewgill
2014年7月12日,0:40

#3 楼

我意识到在CR中,我们鼓励我们审查代码而不是建议替代方法,但是在这种情况下,我们进行了很好的审查,因此我希望我能原谅发布替代方法。

如何?
/>
enum FizzBuzz {

    Fizz(3),
    Buzz(5);
    final int divisor;

    FizzBuzz(int divisor) {
        // Each has a divisor.
        this.divisor = divisor;
    }

    public boolean useName(int n) {
        // Use the name if the divisor is a factor.
        return n % divisor == 0;
    }
}

public void test() {
    for (int i = 1; i < 20; i++) {
        StringBuilder s = new StringBuilder();
        // Walk all the FizzBuzzes
        for (FizzBuzz fb : FizzBuzz.values()) {
            if (fb.useName(i)) {
                // Add up all possible names.
                s.append(fb.name());
            }
        }
        if (s.length() == 0) {
            // No names used - use the number instead.
            s.append(i);
        }
        System.out.println(i + " - " + s);
    }
}


该方法的最甜蜜之处在于您可以使用以下方法简单地扩展它:

    Fizz(3),
    Buzz(5),
    Pling(7);


所有这些都仍然按照您期望的方式工作。

本质上是分别对算法和数据进行编码。

评论


\ $ \ begingroup \ $
替代方法也可以,只要它们后面有一些解释即可(“尝试此”答案无效)。如果愿意,您可以进一步解释,特别是因为我是初学者。
\ $ \ endgroup \ $
– Jamal♦
2014年7月11日14:40



\ $ \ begingroup \ $
我是这样认为的。其余的将涉及单个问题,但是语法看起来很简单。
\ $ \ endgroup \ $
– Jamal♦
2014年7月11日14:44

\ $ \ begingroup \ $
这是一个好的开始,但是显然缺少动态类的加载,反射和FizzBu​​zzManager类;)
\ $ \ endgroup \ $
– Voo
2014年7月11日23:31

\ $ \ begingroup \ $
FizzBu​​zz的一个枚举,这是一个新的枚举。大约一年前,我使用Strategy Pattern做了一个略微过度设计的解决方案,但有一个枚举吗?我喜欢。
\ $ \ endgroup \ $
–西蒙·福斯伯格
14年7月13日在13:16

\ $ \ begingroup \ $
@Voo-在下一版本中,我也会尝试将数据库也放入其中。 :D
\ $ \ endgroup \ $
–OldCurmudgeon
2014年7月13日15:43