[英]A constructor calling itself in java
我的课堂上有两个构造函数,它们扩展了另一个课堂。 第一个有两个参数。 第二个不带参数,并以this(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;
}
我相信您有设计上的问题。
您似乎正在尝试为“ Human
和“ Students
创建构造函数,这些构造函数将为各个字段设置具有随机值的对象。
首先,这个目标本身值得怀疑。 设计对象时,不仅要考虑一项任务。 对象应设计为以通常有用的方式表示真实世界的实体。 构造函数通常应具有可预测的结果。 如果未提供值,他们可以设置合理的默认值。
因此,这将是更好的设计Human
及Student
没有随机性-与接受来电者给定的值平原构造-然后设计一个单独的方法,将产生,这将是用于填充数组特定任务有用的随机值。
这意味着您的构造函数应仅验证Human
和Student
可接受的值。 通常,如果将参数传递给对该构造函数无效的构造函数,则该构造函数将引发异常。 常用的异常是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
没有“合理的”默认值。 每当您创建一个时,年龄和名称都是必填项。
如您所见,我正在检查年龄是否在合理范围内,并且该名称不为null。 如果参数不合理,将引发异常,并且对象创建将失败。
接下来,转到学生。 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);
}
现在您知道这些值是合理的。 您可以将选择范围扩展到整个法律范围,我只是选择了“看起来不错”的值,没有百岁老人或在里根时代就开始学习的学生。
您可以根据需要多次调用此工厂方法,以填充随机学生等的列表,并且由于将“生成”方法与构造函数分开,因此可以使用Student
和Human
类在将来的任务中,这些任务将不依赖于随机生成,而是例如从数据库中填充值。
没有参数的构造函数应该首先调用super(); 如果没有给出,编译器将完成。 如果您有第二个构造函数,并且有参数,则第一个调用应该是this(); 也就是说,对另一个构造函数的调用。 (来源,OCA)
构造函数仅被调用一次:创建类实例时。 您可以在其中放置一个检查特定条件的循环。 在该循环中,您可以根据需要多次调用一个方法。
因此,让工作在方法内完成,并让构造函数调用方法,如果需要的话,可以使用循环多次。
这是不可能的。 调用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();
}
那么,您想生成随机学生吗?
构造函数应保留为构造函数,并且仅分配变量。
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;
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.