在java

时间:2015-11-07 12:43:44

标签: java constructor

我的类中有两个构造函数,它们扩展了另一个类。 第一个采用两个参数。第二个没有参数,并从调用第一个构造函数的这个(param1,param2)开始。 在第一个带有两个参数的构造函数中,我有一个if语句来检查一些条件。如果条件满足,那就没问题,程序可以为两个变量赋值。否则,我希望再次调用第一个构造函数,直到满足这些条件。可以吗? 我的目标是将一名学生添加到一个阵列中,打印出10名18岁以上的学生,并在不早于1980年且不迟于2015年开始上学。

这是子类

public class Student extends Human{
    private int startYear;
    private int currentYear;

    public Student(int startYear, int currentYear){
        super(); // the constructor with no argument is called (i.e. the constructor that slumps age and name)
        if(Integer.parseInt(this.getAge())>18 && this.startYear<=2015 && this.startYear>=1980){
            this.startYear = startYear;
            this.currentYear = currentYear;
        } else {
            // What do I put here to call this constructor again?
        }
    }

    public Student(){
        this((int)(Math.random()*2011),(int)(Math.random()*4));
    }

这是超类

public class Human {
    private int age;
    private String name;

    /**
     * creates a list of names
     */
    private static String[] nameArray = {"Name1", "Name2", "Name3"};

    /**
     * This is a constructor
     * @param ageIn
     * @param nameIn
     */
    public Human(int ageIn, String nameIn){
        this.age = ageIn;
        this.name = nameIn;
    }

    /**
     * constructor that does not take any parameters
     * this() refers to the "head constructor" in this class.
     */
    public Human(){
        this((int)(Math.random()*100), nameArray[(int)(Math.random()*nameArray.length)]);
}
/**
 * create setter
 * @param ageIn
 */
public void setAge(int ageIn){
    this.age = ageIn;
}

/**
 * create setter
 * @param nameIn
 */
public void setName(String nameIn){
    this.name = nameIn;
}

/**
 * returns a string with name and age
 * @return
 */
public String toString(){
    return "Name: " + this.name + ", " + "Age: " + this.age;
}

4 个答案:

答案 0 :(得分:2)

我相信你有设计问题。

您似乎正在尝试为HumanStudents创建构造函数,这些构造函数将为各个字段设置一个具有随机值的对象。

首先,这个目标本身就值得怀疑。您应该设计对象而不仅仅考虑一个任务。应该将对象设计为以通常有用的方式表示现实世界实体。构造函数通常应该具有可预测的结果。如果未提供值,他们可能会设置合理的默认值。

因此,设计HumanStudent没有随机性 - 用简单的构造函数接受调用者给出的值 - 然后设计一个单独的方法来生成随机值会更好用于为特定任务填充数组。

这意味着您的构造函数应仅验证HumanStudent值是否可接受。通常,如果将参数传递给对该构造函数无效的构造函数,则构造函数会抛出异常。常用的异常是IllegalArgumentException,它是RuntimeException,不需要在构造函数的throws子句中声明。

让我们从Human开始。

public class Human {
    private int age;
    private String name;

    /**
     * This is a constructor
     * @param ageIn
     * @param nameIn
     */
    public Human(int ageIn, String nameIn){
        if ( ageIn < 0 ) {
            throw new IllegalArgumentException( "Negative age is not acceptable for human: " + ageIn );
        }
        if ( ageIn > 150 ) {
            throw new IllegalArgumentException( "Age is limited to 150 years for a human, supplied value was: " + ageIn );
        }
        if ( nameIn == null ) {
            throw new IllegalArgumentException( "A non-null string is expected for human" );
        }
        age = ageIn;
        name = nameIn;
    }

    // Getters, setters
}

现在,这个类甚至没有no-args构造函数,因为暂时没有Human的“合理”默认值。无论何时创建一个,都必须使用年龄和名称。

正如您所看到的,我正在检查年龄是否在人类的合理范围内,并且该名称不为空。如果参数不合理,将抛出异常并且对象创建将失败。

接下来,转到学生。 Student似乎是一个年龄超过18岁的人,并且从1980年到2015年开始在某个地方学习。

public class Student extends Human{
    private int startYear;
    private int currentYear;

    public Student( int age, String name, int startYear, int currentYear ) {

        super( age, name );
        if ( age < 18 ) {
            throw new IllegalArgumentException( "A student cannot be less than 18 years old. Supplied age is: " + age );
        }
        if ( startYear < 1980 || startYear > 2015 ) {
            throw new IllegalArgumentException( "A Student must have started studying between 1980 and 2015. Supplied start year is: " + startYear );
        }
        if ( currentYear < 1 || currentYear > 4 ) {
            throw new IllegalArgumentException( "A student must currently be in his 1st to 4th year. Supplied curret year is: " + currentYear );
        }
        this.startYear = startYear;
        this.currentYear = currentYear;
    }

    // Getters, setters, etc

}

现在您可能会注意到我们将年龄原样传递给super(age,name)并仅在之后测试约束。这是因为super(...)必须是构造函数中的第一个指令。但这并不重要。并不是为super分配了新对象。分配已经完成,因为super是当前对象的一部分。因此,如果年龄小于18岁,那么超级构造者可能会完成一些不会被使用的任务,因为它会被销毁,因为事实证明这个年龄对学生来说是非法的 - 但这并不是很多浪费,无论如何都无法避免。

其他检查适用于Student本身的字段,因此它们在分配完成之前就已到来。

现在我们如何为您的作业生成一名合适的“随机”学生?

如果您认为它对其他作业有用,或者在您为当前作业编写的某个类中创建静态方法,则可以在Student中创建静态方法,也许是Main类,或者可能是Utility类等。

无论我们在哪里添加它,我们都希望在该类中拥有可能名称的静态数组:

private static final String[] namesForRandomStudents = { "John Smith",
                                                         "George Robinson",
                                                         "Sarah Carpenter",
                                                         "Judy Thatcher"
                                                         // etc
                                                       };

并且可能会添加一个Random对象,该对象将在内部用于学生名称生成:

private static final Random studentRandomGenerator = new Random();

现在您可以声明静态工厂方法(用于生成特定类的实例的静态方法称为静态工厂方法)。

天真地,您可能认为最好的方法是生成随机年龄和年份,将它们传递给构造函数并捕获异常,直到您成功生成“好”结果:

/**
 * NAIVE APPROACH, NOT RECOMMENDED
 */
public static Student getRandomStudentInstance() {

    boolean failed = true;
    Student result = null;
    do {
        try {
            result = new Student(
                            studentRandomGenerator.nextInt(),
                            namesForRandomStudents[studentRandomGenerator.nextInt(namesForRandomStudents.length)],
                            studentRandomGenerator.nextInt(),
                            studentRandomGenerator.nextInt());
            failed = false;
        } catch ( IllegalArgumentException e ) {
            // Nothing to do here, failed will be true so
            // we'll attempt to generate again
        }
    } while ( failed );

    return result;
}

但这很糟糕。在成功之前,您可能会创建大量失败的对象。这将花费大量时间,也因为捕获机制本身比循环等更重。

通常情况下,如果您获得RuntimeException(在这种情况下为IllegalArgumentException),则表示您应该在传递之前检查值。在这种情况下也是如此。最好确保发送不会导致异常的合理值:

/**
 * Improved version
 */
public static Student getRandomStudentInstance() {

    // Generate an age between 18 and 40
    int age = studentRandomGenerator.nextInt(23) + 18;

    // Generate a name from the array:
    String name = namesForRandomStudents[studentRandomGenerator.nextInt(namesForRandomStudents.length)];

    // Generate a start year between 2000 and 2015 inclusive
    int startYear = studentRandomGenerator.nextInt(16) + 2000;

    // Generate student's current year between 1 and 4 inclusive
    int currentYear = studentRandomGenerator.nextInt(4) + 1;

    return new Student(age,name,startYear,currentYear);
}

现在您知道值是合理的。你可以将选择的范围扩展到整个法律范围,我只选择了“看起来不错”的价值观,没有百岁学生或在里根时代开始学习的学生。

您可以根据需要多次调用此工厂方法,填写随机学生列表等,并且因为您将“生成”方法与构造函数分开,您将能够使用{未来任务中的{1}}和Student类不依赖于随机生成,而是例如从数据库中填充值。

答案 1 :(得分:0)

没有参数的构造函数应首先调用super();如果没有给出,编译器将成功。如果你有第二个构造函数,并且它有参数,那么第一个调用应该是this();也就是说,调用另一个构造函数。 (来源,OCA)

构造函数只调用一次:创建类实例时。你可以在那里放一个检查某个条件的循环。在该循环中,您可以根据需要多次调用方法。

所以,让工作在方法中完成,让构造函数多次调用方法,如果需要,可以使用循环。

答案 2 :(得分:0)

这是不可能的。 当您拨打super()时,您正在使用一些随机参数创建对象Human

因此,如果Student课程中的条件不满足,则需要抛出异常。

我认为您应该将代码更改为:

super(); // the constructor with no argument is called (i.e. the constructor that slumps age and name)
if(Integer.parseInt(this.getAge())>18 && this.startYear<=2015 && this.startYear>=1980){
    this.startYear = startYear;
    this.currentYear = currentYear;
} else {
   throw new IllegalArgumentException();
}

答案 3 :(得分:0)

那么,你想生成随机学生吗?

构造函数应该保留构造函数,并且只分配变量。

public class Student extends Human{
    private int startYear;
    private int currentYear;

    /*
     * Just a classic Student constructor with parameters
     */
    public Student(int startYear, int currentYear){
        this.startYear = startYear;
        this.currentYear = currentYear;
    }


    /*
     * Generate a random student aged over 18 and started school between 1980 and 2015.
     */
    public static Student generateRandomStudent(){
       // random age>18
       int age = 18 + Math.random()*(100-18);
       // random name chosen in the Human array
       String name = Human.nameArray[(int)(Math.random()*nameArray.length)]
       // random startYear between 1980 and 2015
       int startYear = 1980 + Math.random()*(2015-1980);
       // What the purpose of currentYear ?
       int currentYear = 2015;

       // Create the desired student
       Student randomStudent = new Student(startYear, currentYear)
       randomStudent.setAge(age);
       randomStudent.setName(name);

       return randomStudent;
    }
}