Java中构造函数的作用是什么?

23

构造函数的目的是什么?我在学校学习Java,到目前为止似乎构造函数在我们所做的事情中大多是多余的。尽管目的尚未明确,但到目前为止,它对我来说似乎没有意义。例如,下面两个代码片段之间有什么区别?

public class Program {    
    public constructor () {
        function();
    }        
    private void function () {
        //do stuff
    }    
    public static void main(String[] args) { 
        constructor a = new constructor(); 
    }
}

这是我们在作业中学习的做事方法,但是下面的方法不是可以达到同样的效果吗?

public class Program {    
    public static void main(String[] args) {
        function();
    }        
    private void function() {
        //do stuff
    }
}

构造函数的目的让我感到困惑,但是迄今为止我们所做的一切都非常基础。


10
这不是一个构造函数。实际上,它根本没有构造类的功能。一个构造函数看起来像public Program(){\\...,并且会被调用new Program() - AJMansfield
12个回答

50
构造函数用于初始化类的实例。您可以使用构造函数创建新对象,通常使用参数指定对象的初始状态或其他重要信息。
来自官方Java教程

A class contains constructors that are invoked to create objects from the class blueprint. Constructor declarations look like method declarations—except that they use the name of the class and have no return type. For example, Bicycle has one constructor:

public Bicycle(int startCadence, int startSpeed, int startGear) {
    gear = startGear;
    cadence = startCadence;
    speed = startSpeed;
}

To create a new Bicycle object called myBike, a constructor is called by the new operator:

Bicycle myBike = new Bicycle(30, 0, 8);

new Bicycle(30, 0, 8) creates space in memory for the object and initializes its fields.

Although Bicycle only has one constructor, it could have others, including a no-argument constructor:

public Bicycle() {
   gear = 1;
   cadence = 10;
   speed = 0;
}

Bicycle yourBike = new Bicycle(); invokes the no-argument constructor to create a new Bicycle object called yourBike.


13
要么你没有理解他的意思,要么他不理解构造函数的用处。假设你的程序是一个纸牌游戏:你想要52个名为 Card 的类的实例,每个实例都有一个数字和一种颜色。构造函数用于创建 Card 类的实例,你需要调用它52次以获得 52 张卡牌:new Card(1, "hearts") 等等。现在每个 Player 的实例(你也需要一个构造函数)可以拥有一个由构造函数构造的卡牌列表。阅读一本入门 Java 书籍或官方 Java 教程。 - JB Nizet
3
感谢,这解决了我的困惑。针对设定默认或初始参数而言,这是有道理的,但在我们迄今所做的一切中,并不需要这样做。我猜我们是为维护一致性在需要时做好了准备。 - gator
2
谁给我点踩了,能解释一下为什么吗?从我的回答来看,我绝对没有以任何方式损害 OP 对构造函数的知识。 :/ - LotusUNSW
2
@LotusUNSW 我有一种感觉这里有个恶意点踩 troll。你的回答很棒。 - Paul Richter
2
@AJMansfield - 不是这样的。你可以在超类中放置额外的构造函数,仍然可以在子类中使用默认构造函数,只要超类包含一个没有参数和throws子句的明确可访问的构造函数即可。一个类可以包含多个构造函数。提供哪些构造函数的选择可以基于可以用于初始化实例的参数集。有时,这些参数必须在构造函数中指定,因为它们是实例中的不变量。 - Andy Thomas
显示剩余3条评论

32

构造函数基本上是一种方法,您可以使用它来确保您的类对象始终有效。这是构造函数的主要动机。

假设你想让你的类有一个整数字段,该字段应始终大于零。如何以可靠的方式实现呢?

public class C {
     private int number;

     public C(int number) {
        setNumber(number);
     }

     public void setNumber(int number) {
        if (number < 1) {
            throws IllegalArgumentException("C cannot store anything smaller than 1");
        }
        this.number = number;
     }
}
在上面的代码中,看起来似乎有些多余的操作,但实际上它确保了该数字无论如何都是有效的。
“初始化一个类的实例”是构造函数所做的内容,但不是我们拥有构造函数的原因。问题在于构造函数的目的。您也可以使用上面示例中的c.setNumber(10)从外部初始化类的实例。因此,构造函数并不是初始化实例的唯一方法。
构造函数以一种安全的方式来执行这个任务。换句话说,一个类单独解决了确保其对象始终处于有效状态的整个问题。不使用构造函数将把这种验证留给外部世界,这是不良的设计。
以下是另一个例子:
public class Interval {
    private long start;
    private long end;

    public Interval(long start, long end) {
        changeInterval(start, end);
    }

    public void changeInterval(long start, long end) {
        if (start >= end) {
            throw new IllegalArgumentException("Invalid interval.");
        }
        this.start = start;
        this.end = end;
    }
    public long duration() {
        return end - start;
    }
}

Interval类表示时间间隔。时间使用long存储。结束时间早于开始时间是没有意义的。通过使用上述构造函数之一,系统中任何给定时刻都不可能存在一个Interval实例,该实例包含不合理的时间间隔。


1
通常情况下,构造函数(非final类的构造函数)应该只调用final或private方法。 https://dev59.com/u2445IYBdhLWcg3wXZO9 - shiggity
5
感謝您的選擇,以下是翻譯結果:對於這個務實的答案我要點個讚。這個答案不僅符合定義,還回答了我的疑問。 "誕生有效" 是我在理解必須使用構造函數時所缺少的「有用性」小片段。 - Arthur Zennig
我知道这个问题非常老了,但这是一个好的思考方式吗?假设你制作了一个联系人列表应用程序。您保存100个联系人的地址,并使用构造函数来保存“有效”的电话号码。每个成员都被初始化为一个有效的电话号码? - someguy76
1
如果您将电话号码表示为类或地址类的字段,则可能希望在创建保存此类信息的对象之前验证电话号码。将对象视为在创建后不一定受到严格控制的事物。例如,您的对象最终可能被您未编写的代码使用。因此,您的对象应该具有意义。构造函数是一种表明不允许以违反其语义的方式存在对象的方法。 - Akira

10

如LotusUNSW的回答中所述,构造函数用于初始化类的实例。

例如:

假设你有一个类似于 Animal 的类:

class Animal{
   private String name;
   private String type;
}

让我们看看当你尝试创建一个Animal类的实例,比如一个名为Puppy的Dog时会发生什么。现在你需要初始化name = Puppytype = Dog。那么,你该怎么做呢?一种方法是有一个构造函数,就像这样:

    Animal(String nameProvided, String typeProvided){
         this.name = nameProvided;
         this.type = typeProvided;
     }

现在当你创建一个Animal类的对象时,例如Animal dog = new Animal("Puppy", "Dog");,你的构造函数将被调用并初始化名称和类型为你提供的值,如"Puppy"和"Dog"。

现在你可能会问,如果我没有向构造函数提供参数,会发生什么,例如:

Animal xyz = new Animal();

这是一个默认构造函数,它使用默认值初始化对象,在我们的Animal类中,与xyz对象对应的nametype的值将为name = nulltype = null


4

构造函数在创建对象时进行初始化。它与其类具有相同的名称,并且在语法上与方法类似,但构造函数没有明确的返回类型。通常,我们使用构造函数为类定义的实例变量赋初始值,或执行任何其他启动程序以生成完整形式的对象。

下面是一个构造函数的示例:

class queen(){
   int beauty;

   queen(){ 
     beauty = 98;
   }
 }


class constructor demo{
    public static void main(String[] args){
       queen arth = new queen();
       queen y = new queen();

       System.out.println(arth.beauty+" "+y.beauty);

    }
} 

输出结果为:

98 98

这里是构造函数:

queen(){
beauty =98;
}

现在轮到参数化构造函数了。
  class queen(){
   int beauty;

   queen(int x){ 
     beauty = x;
   }
 }


class constructor demo{
    public static void main(String[] args){
       queen arth = new queen(100);
       queen y = new queen(98);

       System.out.println(arth.beauty+" "+y.beauty);

    }
} 

输出结果为:

100 98

3
  • 通过构造函数(带参数),您可以“请求”该类的用户所需的依赖项。
  • 它用于初始化实例变量
  • 并向超类的构造函数传递参数 (super(...)),基本上是相同的
  • 它可以使用代码初始化(final)实例变量,该代码可能会抛出异常,与实例初始化程序作用域不同
  • 应该不要盲目调用构造函数内部的方法,因为在本地或派生类中可能尚未完成/足够初始化。

实例初始化器作用域可以抛出异常,只要所有构造函数都声明抛出相同的异常。 - user207421

2

Java构造函数与其所属类的名称相同。

构造函数的语法不包括返回类型,因为构造函数从不返回值。

创建对象时总是调用构造函数。 例如:默认构造函数

class Student3{  
    int id;  
    String name;  
    void display(){System.out.println(id+" "+name);}  
    public static void main(String args[]){  
        Student3 s1=new Student3();  
        Student3 s2=new Student3();  
        s1.display();  
        s2.display();  
    }  
} 

输出:

0 null
0 null

解释: 在上面的类中,您没有创建任何构造函数,因此编译器会为您提供默认构造函数。这里的0和null值是由默认构造函数提供的。

参数化构造函数示例

在此示例中,我们已经创建了Student类的构造函数,该构造函数具有两个参数。我们可以在构造函数中拥有任意数量的参数。

class Student4{  
    int id;  
    String name;  
    Student4(int i,String n){  
        id = i;  
        name = n;  
    }  
    void display(){System.out.println(id+" "+name);}  
    public static void main(String args[]){  
        Student4 s1 = new Student4(111,"Karan");  
        Student4 s2 = new Student4(222,"Aryan");  
        s1.display();  
        s2.display();  
    }  
}  

输出:

111 Karan
222 Aryan

2
这篇文章存在严重的格式问题,请编辑并使用代码块。 - Halvor Holsten Strand

1
我认为构造函数可能像数据库一样工作,仅定义变量和控制它们的方法。
然后我看到对象使用未在其构造函数中定义的变量和方法。因此,对我来说最有意义的讨论是测试变量值的有效性,但类可以创建未在构造函数中定义的变量和方法 - 不太像数据库,更像蒸汽机车上的过压阀。它不控制车轮等部件。
比我想象中的定义要少得多。

这并没有回答问题。 - adao7000

1
它用于设置您的类的内容和状态。虽然您可以使用主方法来制作更简单的示例,但每个应用程序只有一个主方法,因此这不是明智的方法。
将主方法视为仅启动程序,并且不应知道如何执行其他操作。请注意,main()是静态的,因此无法调用需要类实例和相关状态的函数。主方法应调用new Program().function(),而Program构造函数不应调用function(),除非需要设置类。

1
类定义定义了你的类的API。换句话说,它是一个蓝图,定义了类与其客户端之间存在的契约-使用此类的所有其他代码。合同指示哪些方法可用,如何调用它们以及期望得到什么回报。
但是类定义是一种规范。除非你有这个类的实际对象,否则合同只是“一张纸”。这就是构造函数的作用。
构造函数是通过在内存中创建一个对象并返回对其的引用来创建类的实例的手段。构造函数应该发生的事情之一是使对象处于适当的初始状态,以便随后对对象进行的操作具有意义。
从构造函数返回的对象现在将遵守类定义中指定的合同,您可以使用此对象进行实际工作。
想象一下。如果你曾经看过保时捷网站,你会看到它能做什么-马力,扭矩等等。但是,直到你真正拥有一辆保时捷,它才有趣。
希望这有所帮助。

1

构造函数基本上用于在对象创建时初始化变量


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接