【问题标题】:Difference between constructors and methods构造函数和方法的区别
【发布时间】:2015-01-05 14:28:18
【问题描述】:

Bellow 是我在 Tutorials Points 上找到的一个例子,一个构造函数的例子。我得到了大部分,但我只是不明白为什么你需要一个构造函数一个方法。

public Puppy(String name){
    System.out.println("Passed Name is :" + name ); 
}

我的问题是,是什么阻止了您这样做?

public static void Puppy(String name){
    System.out.println("Passed Name is: "+name);
}

这两个调用一次不做同样的事情吗?

这是完整的程序供参考:

public class Puppy {
    int puppyAge;

    public Puppy(String name) {
        System.out.println("Passed Name is :" + name); 
    }

    public void setAge(int age) {
        puppyAge = age;
    }

    public int getAge() {
        System.out.println("Puppy's age is :" + puppyAge); 
        //what does this return do? since the puppyAge is already printed above.
        return puppyAge;
    }

    public static void main(String []args){
        Puppy myPuppy = new Puppy("tommy");

        myPuppy.setAge(2);
        myPuppy.getAge();

        System.out.println("Variable Value :" + myPuppy.puppyAge); 
    }
}

【问题讨论】:

  • 您认为哪种方法会重复构造函数的工作?只有当构造函数和另一个方法正在执行相同的任务时,它们中的一个才可能是不必要的。然而,在这种情况下,构造函数并没有做它应该做的所有事情——特别是构造函数应该设置类中所有成员变量的值(并且它没有设置puppyAge,所以实例处于未定义状态构造后立即声明)并且它没有使用小狗的名字来设置变量(那么为什么要提供小狗的名字呢?)。

标签: java methods constructor


【解决方案1】:

您的第一个示例是在您实际创建 Puppy 时调用的公共构造函数。通常,构造函数会保存名称而不是显示并丢弃它,因此您可以稍后再参考它,例如:

public class Puppy{
    private String puppyName;

    public Puppy(String name){
        puppyName = name;
    }
    public void bark(){
        System.out.println(puppyName + ": woof!");
    }
    ...
}
...
public static void main(){
    Puppy Spot = new Puppy("Spot"); // here, the constructor is called, which saves the name
                                    // "Spot" into the variable Spot.puppyName.

    Puppy Fido = new Puppy("Fido"); // on this line, the constructor is called again, but this time, the string
                                    // "Fido" is saved into Fido.puppyName.

    Spot.bark();                    // output: "Spot: woof!"
}

我认为构造函数是一种“返回” Puppy 对象的方法,这就是它没有返回类型的原因。声明为静态的方法和变量,不像小狗的名字,不“属于”任何特定的对象,而是在类的所有成员之间共享。

public class Puppy{
    ...
    private static String latinName;
    public static void setLatinName(String newLatinName){
        latinName = newLatinName;
    }
    public static void printLatinName(){
        System.out.println("I am a " + latinName + ".");
    }
};
...
static void main(){
    Puppy Spot("Spot");
    Puppy Fido("Spot");
    Spot.setLatinName("Canis Lupus");
    Fido.setLatinName("Canis Lupus Familiaris");

    Spot.printLatinName();    // output: "I am a Canus Lupus Familiaris."
    // even though we used the Fido object to change the latinName string,
    // the output of Spot's non-static method still reflects the change because
    // latinName is a static field: there is only one String latinName 
    // shared among all members of the class.
}

虽然您可以使用 Puppy 对象来调用静态方法(如果 Puppy 是多态犬,这可能特别有用),反之亦然!

public class Puppy{
    ...
    public static void bark(){
        System.out.println( puppyName + ": woof!"); // this will not compile!
    }
}

在静态函数的上下文中引用非静态数据成员是无效的(并且没有意义)!静态函数类似于静态变量:所有成员对象之间共享的函数只有一个副本。这意味着您可以调用 Puppy.setLatinName()(而不是 Spot.setLatinName())。您不需要使用特定的 Puppy 对象来调用静态函数:静态函数保证不依赖任何非静态成员变量(因为它没有任何成员变量)。调用 Puppy.bark() 没有意义,因为“Puppy”类没有puppyName;每个 Puppy 都有自己的 puppyName。

我希望这会有所帮助。面向对象编程非常强大,理解基础知识很重要!祝你好运。

【讨论】:

    【解决方案2】:

    Java Spec for Constructors中声明:

    • 构造函数不返回任何内容,因此它们没有返回类型。

      在所有其他方面,构造函数声明看起来就像一个没有结果的方法声明 (§8.4.5)。

    • 您不能覆盖构造函数。

      构造函数声明不是成员。它们永远不会被继承,因此不会被隐藏或覆盖。

    • 他们不能直接访问实例变量,而是必须使用thissuper 来委托给类的变量。

      构造函数体中的显式构造函数调用语句不能引用在该类或任何超类中声明的任何实例变量或实例方法或内部类,或在任何表达式中使用this或super;否则,会发生编译时错误。

    【讨论】:

    • 我想你最后一个读错了:你可以在构造函数中访问任何类或实例变量,但调用super(...)this(...) 可能无法访问实例成员或类成员。
    【解决方案3】:

    您没有了解实例的基本概念,这是 OOP 的基础。如果你想要一个比喻,我们来谈谈汽车。

    我很确定你知道什么是汽车;你知道它可以让你从一个地方移动到另一个地方,它有 4 个轮子等等。这是一个概念,而你车库里的实际汽车就是这个概念的实例)。

    构造函数的目标是创建一个实例,而不是打印一些文本。如果没有构造函数,您将永远无法调用类的非静态方法。您将无法驾驶汽车的概念,您需要先制造汽车。

    只需回顾这些概念;没有它,你将一事无成。

    【讨论】:

      【解决方案4】:

      我真的宁愿阅读一本关于 OOP 概念的书,在这种情况下是 java,而不是阅读这里的所有(好的)答案。您可能会得到很多答案,因为这个问题相当受欢迎,但答案不会涉及所有必要的细节。

      但我想简短地提一件事:

      public int getAge() {
          System.out.println("Puppy's age is :" + puppyAge); 
          //what does this return do? since the puppyAge is already printed above.
          return puppyAge;
      }
      

      一个方法可以返回某种答案。 那么这个方法有什么作用呢?它返回值,保存在名为puppyAge 的变量中, 这是一个整数。 所以如果你调用方法myPuppy.getAge(),这个方法中的所有东西(实际上也是打印出来的), 将被处理,然后返回。在您的情况下,您不会将返回值保存在任何地方。 但是您想要做的“正常”事情是通过该方法访问小狗的年龄 实际上用它做一些事情,比如计算你所有小狗和东西的平均年龄。 当然,您可以从类外部访问此处的变量,因为您没有设置可见性修饰符,例如 public、private、protected。

      public class Puppy {
          int puppyAge;
      

      实际上没有公共修饰符也有含义,但这暂时不相关。 因此,您要做的就是将其设置为例如私有:

      private int age;
      

      并通过getAge() 之类的方法访问它并使用它执行以下操作:

      int allAgesummed = puppy1.getAge() + puppy2.getAge() +
      puppy3.getAge();
      

      最后,如果你只是想在控制台上打印出年龄,你的方法应该只做一件事并且应该相应地命名,比如:

      public void printAgeToConsole(){
          System.out.println("Age: " + age);
      }
      

      void 表示此方法做了一些事情,但不返回任何内容。

      【讨论】:

        【解决方案5】:

        我猜你的问题是为什么构造函数是使用...创建的

        public Puppy(String name)
        

        ...而不是...

        public static void Puppy(String name)
        

        ...,对吗?

        如果是,首先你应该知道构造函数是一个类拥有的特殊方法,每当你创建该类的新实例时都会调用它。因此,例如,如果您有此类...

        public class Clazz {
            public Clazz (String s) {
                 System.out.println(s);
            }
        }
        

        ...,那么每当您创建此类的新实例时,例如...

        Clazz c = new Clazz("Hello World");
        

        ...,将调用构造函数并执行该方法。 (在本例中,它将在屏幕上打印“Hello World”。)

        也就是说,您可以看到构造函数是一个方法,但它是在类实例化时调用的特殊方法。因此,关于您的问题,构造函数以这种方式而不是常规方法标头正确的原因是向编译器指示 它是构造函数不是 常规方法。如果你写public void Clazz ()而不是public Clazz (),那么编译器将无法识别你的构造函数,并认为它只是一个常规方法。

        请注意,构造函数永远不会返回任何内容,因此没有必要使用 void 关键字。此外,将构造函数设为静态也没有任何意义,因为在实例化新对象时会调用构造函数。

        【讨论】:

        • constructors never return anything, so it would not be necessary to use the void keyword 这是一种奇怪的思考方式。我一直认为相反:它们返回一个特定类型的对象。在public Clazz () 中,我会考虑Clazz 返回类型,而不是“方法”名称。
        • @nbrooks 你说的很有道理。但在 java 文档中,构造函数 没有 有返回类型。但是现在你已经说了,我想这两种方式都是正确的
        • 我认为文档说您不使用 return 关键字,而不是没有返回类型。我们知道它会返回一些东西,否则你不能用这样的方法调用链接构造函数:new String("Hello World").substring(3).
        • @nbrooks 在我看来,使new String("Hello World") 返回字符串的是new 关键字,而不是构造函数本身。请注意,如果我们像this() 这样在类中单独调用构造函数方法,它肯定不会返回任何内容。关于 java 文档,上面写着 Constructor declarations look like method declarations—except that they use the name of the class and have no return type. 这是写在这里 docs.oracle.com/javase/tutorial/java/javaOO/constructors.html
        • 关于this() 的观点很好,new operator 上的文档实际上也支持您的观点。谢谢你,这是一个微妙的区别,但你完全就在这里。
        【解决方案6】:

        构造函数是 公共类(){ 定义需要在整个类中使用的变量 方法在构造函数下 公共无效方法名(){} 您可以将 void 更改为您希望方法返回特定类型的变量类型。

        【讨论】:

          【解决方案7】:

          构造函数是方法的类型。具体来说,当您实例化该类的对象时会调用它。所以在你的例子中,如果你写:

          Puppy pup = new Puppy("Rover");
          

          您创建一个名为pup 的对象并实例化该对象,这样您就可以使用它。通过实例化它,您调用指定的构造函数,在本例中为:public Puppy(String name),它将打印设置为"Rover",但这并不是构造函数的真正用途。更有意义的是将String puppyName; 作为字段变量,并让this.puppyName = namep.puppyName 设置为等于构造函数中传递的值。

          【讨论】:

            【解决方案8】:

            Constructor 就是这样做的,它“构造”您正在创建的对象,从技术上讲,它是Method 的特定类型。 Method 用于修改对象,主要是做单条逻辑。见:single responsibility principle

            类/方法应该简单并处理精确的信息片段,例如采用Shape 对象,如果你希望它的属性是长度、宽度、高度、体积、面积等......你的构造函数必须是:

            public Shape (double length, double width, double height, double volume, double area, ect...) {
                //code here
            } 
            

            (注意这个方法的名字应该和类的名字一样,表示一个“构造器”)

            这就是所谓的code smell,具体来说:参数太多。它很难阅读,不是很有条理,只是编写代码的一种糟糕方式。因此,我们使用方法来设置这些变量:

            public void setLength(double length) {
                //code here
            } 
            

            那么你的构造函数只会是:

            public Shape() {
                //code here
            }
            

            这只是一个简单的例子。在许多情况下,您可能希望根据具体情况将参数(或几个)传递给构造函数。

            【讨论】:

              【解决方案9】:

              当您创建对象的新实例 (Puppy doggy = new Puppy("Bobby");) 时,会立即调用构造函数。它构造新对象。它用名称“Bobby”构造它。 (小狗出生时的名字叫“鲍比”)。假设我们的小狗对它的名字不满意,你想把它改成“史努比”。然后你会调用这个方法:doggy.setName("Snoopy");

              【讨论】:

                【解决方案10】:

                已经有几个关于构造函数的好答案了。

                一个小问题;你不能有一个与你的类同名的静态方法。该名称​​为构造函数保留

                【讨论】:

                  【解决方案11】:

                  构造函数和方法之间有很大的区别。当你像在程序中那样实例化一个对象时

                  Puppy myPuppy = new Puppy( "tommy" );
                  

                  使用 new 关键字,JVM 调用构造函数来生成对象。因此,构造函数创建了不存在的对象,但方法与存在的对象相关联。您可以在此链接上阅读更多内容 http://www.javaworld.com/article/2076204/core-java/understanding-constructors.html

                  【讨论】:

                    【解决方案12】:

                    例如,您不需要构造函数。这是一个非常糟糕的例子。方法也可以。

                    (如果您不添加,所有 Java 类都有一个默认构造函数,因此您仍然可以调用 new Puppy() 来获取实例。)

                    【讨论】:

                      【解决方案13】:

                      构造函数应该用于设置对象,其中方法只是对对象执行一些操作。 Java 是面向对象的,因此整个语言都围绕对象展开。构造函数也不能返回任何东西,并且必须与 new 一起使用以返回创建的对象的实例,而 as 方法可以返回任何东西或根本不返回任何东西。

                      【讨论】:

                        【解决方案14】:

                        构造函数是方法,但是它们是特殊类型的方法。构造函数是没有返回类型的方法,必须以其封闭类命名。那么为什么我们需要构造函数呢?好吧,这就是我们创建类实例的方式。当您想创建某个类的实例时,您可以通过调用其构造函数来实现。构造函数为对象保留内存并返回对新创建对象的引用。

                        【讨论】:

                          猜你喜欢
                          • 2016-11-04
                          • 1970-01-01
                          • 2019-10-08
                          • 1970-01-01
                          • 2016-06-16
                          • 1970-01-01
                          • 2011-06-01
                          相关资源
                          最近更新 更多