为什么Java不使用某些类的封装?


25

我的问题与System.inSystem.out类有关(可能还有其他类似标准库中的类)。这是为什么?在OOP中这不是不好的做法吗?它不应该被使用,如:System.getIn()System.getOut()?我一直有这个问题,希望在这里能找到一个好的答案。

Answers:


36

该类中inout字段的定义为System

public final static PrintStream out;
public final static InputStream in;

这些是常数。它们也碰巧也是对象,但它们是常量。它与Math类几乎相同:

public static final double E = 2.7182818284590452354;
public static final double PI = 3.14159265358979323846;

或在布尔类中:

public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);

或在Color类中:

public final static Color white     = new Color(255, 255, 255);
public final static Color black     = new Color(0, 0, 0);
public final static Color red       = new Color(255, 0, 0);

访问不变的公共常量时,对其进行封装并没有明显的优势-从概念上或基于性能。在那。它不会改变。

Color.white和之间没有真正的区别System.out


好吧,我还没有那样看,它们是不变的对象。这是一个很好的解释。
Clawdidr

1
在当今的Java中,@ Clawdidr 可能会考虑使用enum...来容纳它们……尽管enumJava 1.5新增了该功能(1.0天内不是一个选项)。

出于好奇(我自己不是Java开发人员):final static和之间有区别static final吗?如果是这样,那是什么?
Marjan Venema

1
@MarjanVenema没什么区别,只是修饰符的首选顺序。 checkstyle修饰符check显示首选顺序。显然,无论谁以jdk版本编写了这两个源文件,我都不同意该命令。它仅仅是约定。

:)并感谢Michael抽出宝贵的时间进行回复并提供了链接。
Marjan Venema

5

真正的原因是这是一个遗留问题。该System.in,out,err常数是Java 1.0的一部分......,可能很多更靠后。到了很显然,设计出现问题时,现在修复该工具为时已晚。他们所能做的最好的事情就是添加System.setIn,setOut,setErrJava 1.1中的方法,然后处理语言规范问题1

这类似于为什么静态System.arraycopy方法的名称违反Java命名约定的问题。


至于是否是“不良设计”,我认为是。在某些情况下,当前的非OO处理非常严重。(想想…… 当它们的“标准IO”流需求冲突时,如何另一个Java程序中运行它。想想……需要更改流的单元测试代码。)

但是,我也可以提出这样的论点,即在许多情况下,当前的处理方式更方便


1-有趣的是,System.in,out,err变量在JLS中被特别提及为具有“特殊语义”。JLS表示,如果更改final字段的值,则行为是不确定的...除非是这些字段。


2

我认为out对象是不可变的,因此可以以某种方式安全地保存在公共最终静态字段中(这值得商bat)。

JDK中的许多类都不遵循最佳的面向对象设计原则。造成这种情况的原因之一是,它们大约是20年前编写的,当时面向对象只是新兴的主流范式,许多程序员根本不像现在那样熟悉它们。错误的API设计的一个很好的例子是Date&Time API,它花了19年的时间进行更改...


3
我将使该语句更加强大,一个公共的最终变量(无论是否静态)都与吸气剂一样“安全”,并且我会认为在一个setter / getter对上具有不变性。Setter几乎总是一个坏主意(Immutable是个好主意,并且如果不能将“ Sets”的方法不变,那么它也应该值得一点商业逻辑),如果您需要一个getter,您也可以将其公开最终,但两者都不可取-不要向类要求它的数据,而要类对它的数据做些事情。
Bill K

@BillK:是的,也称为Demeter法(尽管这不是法律,而是指导原则)。
sleske

2

这个答案是真实的。

我想补充一点,在某些情况下,出于可用性的考虑而做出了让步

当String不是基本类型时,可以实例化String类型的对象而无需new事件:

String s = "Hello";

字符串是非基元的,应像这样实例化:

String s = new String("Hello");          // this also works 

但是编译器允许使用较短,较少的OO选项,因为String是API中使用最广泛的类。

数组也可以通过非OO方式初始化:

int i[] = {1,2,3};

奇怪的是,对象要么是类的实例,要么是数组。含义数组是完全独立的类类型。

数组具有一个非常量的length公共字段。同样,关于类数组的实例也没有文档。(不要与Arrays类或java.reflect.Array混淆)。

int a = myArray.length;    // not length()

6
您的情况有所不同- new String("Hello")始终会创建一个新的String对象。虽然String s = "Hello";会使用被拘留对象。比较:"Hello" == "Hello"可能为true,而new String("Hello") == new String("Hello")始终为false。首先,发生了编译时优化魔术,而这是无法实现的new String("Hello")。参见en.wikipedia.org/wiki/String_interning

@MichaelT我只是为了完整性而在数组上添加了一些额外的复杂性。
图兰斯·科尔多瓦

2
@Tarik我反对“字符串是非原始的,应像这样实例化:String s = new String("Hello");”,这是不正确的。由于字符串实习,较短的选项(String s = "Hello";正确。

2
使用String,没有“更正确”的选项。两者都是正确的。但是,正如MichaelT所说,由于String实习,较短的是首选。
Andres F.

1
@AndresF。我将“较少正确”更改为“较少OO”。
图兰斯·科尔多瓦
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.