我一直想知道在Java中处理多个构造函数的最佳(即最干净/最安全/最有效)方法是什么?特别是在一个或多个构造函数中,并非所有字段都指定:
public class Book { private String title; private String isbn; public Book() { //nothing specified! } public Book(String title) { //only title! } ... }
如果未指定字段,我该怎么办?到目前为止,我一直在使用类中的默认值,以便字段永远不为空,但这是一种"好"的做事方式吗?
一个稍微简化的答案:
public class Book { private final String title; public Book(String title) { this.title = title; } public Book() { this("Default Title"); } ... }
考虑使用Builder模式.它允许您在参数上设置默认值,并以简洁明了的方式初始化.例如:
Book b = new Book.Builder("Catcher in the Rye").Isbn("12345") .Weight("5 pounds").build();
编辑:它还消除了对具有不同签名的多个构造函数的需求,并且更具可读性.
您需要指定什么是类不变量,即对于类的实例始终为true的属性(例如,书的标题永远不会为null,或者狗的大小将始终> 0).
这些不变量应该在构造过程中建立,并在对象的生命周期内保留,这意味着方法不应该破坏不变量.构造函数可以通过使用强制参数或通过设置默认值来设置这些不变量:
class Book { private String title; // not nullable private String isbn; // nullable // Here we provide a default value, but we could also skip the // parameterless constructor entirely, to force users of the class to // provide a title public Book() { this("Untitled"); } public Book(String title) throws IllegalArgumentException { if (title == null) throw new IllegalArgumentException("Book title can't be null"); this.title = title; // leave isbn without value } // Constructor with title and isbn }
但是,这些不变量的选择在很大程度上取决于你所写的课程,你将如何使用它等等,所以对你的问题没有明确的答案.
你应该总是构造一个有效的合法对象; 如果您不能使用构造函数参数,则应使用构建器对象创建一个构建器对象,仅在对象完成时从构建器中释放对象.
关于构造函数使用的问题:我总是尝试使用一个所有其他人都遵循的基本构造函数,将"省略"参数链接到下一个逻辑构造函数并以基本构造函数结束.所以:
class SomeClass { SomeClass() { this("DefaultA"); } SomeClass(String a) { this(a,"DefaultB"); } SomeClass(String a, String b) { myA=a; myB=b; } ... }
如果这是不可能的,那么我尝试使用所有构造函数遵循的私有init()方法.
并保持构造函数和参数的数量很少 - 每个最多5个作为指导.
一些一般的构造函数提示:
尝试将所有初始化集中在单个构造函数中,并从其他构造函数中调用它
如果存在多个构造函数来模拟默认参数,则此方法很有效
永远不要从构造函数中调用非final方法
根据定义,私有方法是最终的
多态可以杀死你; 您可以在初始化子类之前调用子类实现
如果您需要"帮助"方法,请确保将它们设为私有或最终方法
在你对super()的调用中要明确
你会惊讶于有多少Java程序员没有意识到即使你没有明确地编写它也会调用super()(假设你没有调用它(...))
了解构造函数的初始化规则的顺序.它基本上是:
这个(...)如果存在(只是移动到另一个构造函数)
调用super(...)[如果不显式,则隐式调用super()]
(递归地使用这些规则构造超类)
通过声明初始化字段
运行当前构造函数的主体
返回前面的构造函数(如果遇到过这个(...)调用)
整体流程最终是:
从超类层次结构一直向上移动到Object
虽然没有完成
init字段
运行构造函数体
下拉到子类
对于邪恶的一个很好的例子,尝试弄清楚下面将打印什么,然后运行它
package com.javadude.sample; /** THIS IS REALLY EVIL CODE! BEWARE!!! */ class A { private int x = 10; public A() { init(); } protected void init() { x = 20; } public int getX() { return x; } } class B extends A { private int y = 42; protected void init() { y = getX(); } public int getY() { return y; } } public class Test { public static void main(String[] args) { B b = new B(); System.out.println("x=" + b.getX()); System.out.println("y=" + b.getY()); } }
我将添加评论,说明为什么上面的工作原理......有些可能是显而易见的; 有些不是......