我昨天刚开始学习一些Java。我写了这篇文章,并从@SimonAndréForsberg获得了一些帮助以对其进行完善。此后,我添加了一些内容。它非常简单,但是在继续进行更复杂的事情之前,我只想确保我有一些概念。感谢任何建设性的批评!

import java.lang.*;

public class HelloWorld {
    private final String output;

    public HelloWorld(String name) {
        output = "Hello World, " + name + "!";
    }

    public String getMessage() {
        return output;
    }

    public static void main (String args[]) {
        HelloWorld helloObj;
        helloObj = new HelloWorld("Phrancis");
        System.out.println(helloObj.getMessage());
    }
}


控制台输出:


Hello World, Phrancis!



>

#1 楼

在Java中,几乎不需要创建void变量“声明”。代码:


HelloWorld helloObj;
helloObj = new HelloWorld("Phrancis");



应该是:

HelloWorld helloObj = new HelloWorld("Phrancis");


然后,对于实际的HelloWorld类,有一些提示:


所有Java对象都有一个toString()方法,在大多数情况下,应重写此方法以提供有用的诊断信息或其他信息。在这种情况下,请使用toString()而不是getMessage()

,应该尽可能以“原始”形式存储数据。您的HelloWorld类应该存储“名称”而不是“输出”。
您应该为内部属性提供吸气剂。

我将类编写为:

public class HelloWorld {
    private final String name;

    public HelloWorld(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public String toString() {
        return "Hello World, " + name + "!";
    }

}


那么您的主要方法可能是:

public static void main (String args[]) {
    HelloWorld helloObj = new HelloWorld("Phrancis");
    System.out.println(helloObj);
}


评论


\ $ \ begingroup \ $
我个人不喜欢toString()如何用作业务逻辑而不是调试目的。就像两个世界交织在一起。我偏爱使用单独的getMessage()方法,因为与突然打印出一个对象相比,它还具有更多的自记录性。
\ $ \ endgroup \ $
– Jeroen Vannevel
2014年12月3日在18:25

\ $ \ begingroup \ $
“您应该为内部属性提供吸气剂。” - 不必要。这取决于对象的目的。
\ $ \ endgroup \ $
–oconnor0
2014年12月3日19:27

\ $ \ begingroup \ $
rolfl,您自己不是吗,在今年早些时候的回答中说toString()不应用于用户输出,而只能用作调试信息?
\ $ \ endgroup \ $
–西蒙·福斯伯格
2014年12月3日22:22

\ $ \ begingroup \ $
@SimonAndréForsberg-我说的话不太清楚。我说过应该记录一下toString是否具有比调试更多的合同,但是您应该始终有toString...。这就是您所指的答案
\ $ \ endgroup \ $
–rolfl
2014年12月3日22:35



\ $ \ begingroup \ $
@JeroenVannevel我认为该规则的例外是当类表示类似字符串的东西(例如StringBuilder或某种字符数组)时。在这种情况下,toString()是获取结果字符串的逻辑选择。
\ $ \ endgroup \ $
–anaximander
2014年12月4日在9:36

#2 楼

首先,


import java.lang.*;



此导入是多余的。 Java默认情况下会导入java.lang程序包,您无需指定任何其他内容。您似乎在最后一个}和下一个左括号之间使用了一致的两个换行符。我个人认为这会占用很多空间,我会保留“较大的”垂直距离以表示切片或类似内容,这是一致的;)



public static void main (String args[]) {



虽然这是对main的完全有效的声明,但为保持一致性,我宁愿在类型后面而不是在参数名称后面使用数组声明:

public static void main (String[] args) {


评论


\ $ \ begingroup \ $
+1准确地说出我说的话!
\ $ \ endgroup \ $
–西蒙·福斯伯格
2014年12月3日22:23在

#3 楼

除了字符串连接外,另一种方法是使用String.format(...)格式化消息。结合其他答案的建议,我将采用以下实现方式:

public class HelloWorld {
    private final String message;

    public HelloWorld(String name) {
        this.message = String.format("Hello World, %s!", name);
    }

    @Override
    public String toString() {
        return message;
    }
}


由于name不变,因此在构造时预先格式化消息是一件好事,
,以便toString可以直接直接返回它。

如果要直接访问该消息,可以为其添加一个吸气剂。

如果要访问名称,您可以为其添加字段和获取器。
但是“ HelloWorld”对象可能真的不需要任何访问器,对吗?

UPDATE

我之所以建议String.format是因为其他答案未提及它,它通常是一个有用的替代方法。可能是个问题,此方法的好处是带有占位符+参数列表的模板字符串比串联更易读。

根据我在@vaxquis中的评论,请注意String.format的输出可能取决于语言环境,例如在格式化日期和数字时。 String.format(Locale, String, ...)的另一个版本采用Locale作为第一个参数,以确保可预测的输出。 (没有此参数,将使用默认语言环境。)这对于打算在多个语言环境中运行的应用程序(例如Android应用程序)尤其重要。因此,Android linters会为没有区域设置参数的使用发出警告。

在此特定示例中,没有这样的担忧,因为我们没有打印任何对区域设置敏感的内容(没有日期,数字)。但是,最好在可能会有所作为的情况下意识到这种对语言环境敏感的行为。

评论


\ $ \ begingroup \ $
@vaxquis因为在这个非常简单的示例中我们仅格式化字符串参数,所以我真的不同意有关格式化数字和日期以及对语言环境敏感的情况(如Android)的详细讨论就在这里。但是无论如何,我添加了一个更新来解释这些问题。
\ $ \ endgroup \ $
– janos
2014年12月4日20:16

\ $ \ begingroup \ $
因为我们仅格式化String参数,所以我认为不需要使用String.format()与simple +并置。不过,您对format()是有用的替代方法有一点了解;非常感谢您的更新,因为您的回答现在很冗长,涵盖了警告,因此IMO有用,您从我这里当之无愧。
\ $ \ endgroup \ $
–user20300
2014年12月4日21:55

\ $ \ begingroup \ $
@vaxquis很高兴您喜欢此更新! (不过,我希望您没有删除最后的几条评论。您已经提出了一些要点,并且这些链接特别有用。)感谢您的开放态度,事实上,我对String也会更加小心。将来格式化!
\ $ \ endgroup \ $
– janos
2014-12-04 22:03



#4 楼


private final String output;



我喜欢这个。从构造函数参数初始化私有字段,并将其设置为final听起来与The Right Thing™完全一样。


public static void main (String args[]) {
    HelloWorld helloObj;
    helloObj = new HelloWorld("Phrancis");
    System.out.println(helloObj.getMessage());
}



这有点尴尬:您已经在main类中编写了(静态)HelloWorld方法,然后让该main方法创建了相同类的实例-我不喜欢那样。

您好正在使用对象-创建类的实例可以做到这一点。但是我会在单独的类中实现参数化构造函数和getMessage方法。

评论


\ $ \ begingroup \ $
“ main方法创建该类的实例” –这就是我一直在做的事情。尽快离开静态上下文,使我可以使用实例成员和方法,这比静态方法对重构更友好。
\ $ \ endgroup \ $
– maaartinus
2014年12月3日于20:07

\ $ \ begingroup \ $
@maaartinus IMO包含main方法的类应仅用作入口点,并使用其依赖关系实例化实际的应用程序对象;实例化具有静态上下文和实例上下文的同一类的主要方法听起来像是破坏了我的SRP。
\ $ \ endgroup \ $
– Mathieu Guindon♦
2014年12月3日20:22



\ $ \ begingroup \ $
我的主要工具是像new ThisClass()。go()之类的单层(或接近)或使用DI的类似物。所以我服从SRP,因为实际上没有静态上下文-如果Java可以直接调用我的非静态go,我会使用它。
\ $ \ endgroup \ $
– maaartinus
2014年12月3日22:19在

\ $ \ begingroup \ $
仅回答提到这种奇怪之处? +1
\ $ \ endgroup \ $
– nhgrif
2014年12月5日12:34

#5 楼

为了便于阅读,属性名称和相应的方法名称应该是相对的。

private final String message; 

public String getMessage() {
    return message;
}


评论


\ $ \ begingroup \ $
这样的字符串生成器难道不是杀死了吗? Java有字符串格式类型的东西,不是吗?
\ $ \ endgroup \ $
–RubberDuck
2014年12月3日17:56

\ $ \ begingroup \ $
@RubberDuck你是对的。在这种情况下,String.format是最佳选择。
\ $ \ endgroup \ $
–UğurGüneri
2014年12月3日在18:13

\ $ \ begingroup \ $
用+连接字符串绝对没有错。当您不知道循环中的串联数量时,建议使用StringBuilders。在这里使用StringBuilder会使代码混乱,并且不增加任何价值。格式化程序可能会很有趣,以介绍该概念。
\ $ \ endgroup \ $
– Jeroen Vannevel
2014年12月3日在18:19

\ $ \ begingroup \ $
@RubberDuck String.format不错,但速度较慢。在这里,显然没有关系。 StringBuilder在这里是完全没有意义的,因为+的作用完全相同(仅消除中间字符串的创建是有意义的,这里没有。)
\ $ \ endgroup \ $
– maaartinus
2014年12月3日20:05

\ $ \ begingroup \ $
属性名和对应的方法名的相对含义是什么? (编辑:您是说这个名字,对吗?在这种情况下,我同意)。
\ $ \ endgroup \ $
–西蒙·福斯伯格
2014年12月3日22:25