簡體   English   中英

為什么在 Java 實例化過程中要為類命名兩次?

[英]Why do you name the class twice during instantiation in Java?

實例化一個對象時,為什么要指定類兩次?

OddEven number = new OddEven();

為什么你不能只說number = new OddEven(); ? 當我聲明一個字符串時,我只說一次String

String str = "abc";

實際上,我的問題不是“你為什么要這樣做”——顯然,你這樣做是因為你必須這樣做——而是,為什么創造者選擇讓 Java 語法像這樣工作?

我的想法是:

  1. Java 在低級別運行的方式有一些基本的東西,需要輸入兩次名稱,或者
  2. 創建者自由選擇這樣做是為了保持語法的某些方面的統一——首先聲明類型? 還是它更像它的前輩?

因為你可以這樣做:

Superclass x = new Subclass();

引用的類型可以是所聲明的實際對象的超類,因此您需要同時指定兩者。 例如,您可以執行以下操作:

List<String> stringList = new ArrayList<String>();

您的程序與實現 List 的對象進行交互,而您並不關心實現。,

看似多余的類型名稱的原因是您正在執行兩個單獨的操作,每個操作都需要您指定一個類型。

在左側,您聲明了一個具有特定類型的變量(存儲位置)。 在右側,您正在創建具有特定類型的新對象。 中間的“=”會導致對您創建的新對象的引用放置在您創建的存儲位置中。

每邊的類型不必相同。 例如,這是合法代碼:

Object number = new OddEven();

關鍵字 String 在第二個示例中只出現一次的原因是右側隱含了類型 String,因為“xxx”是一個 String 常量。 它只是以下的簡寫:

String string = new String("xxx");

當你寫:

OddEven number = new OddEven();

實際上,你要做兩件事情:1)你聲明一個變量numberOddEven 2)分配給類的新實例的引用OddEven 但是因為一個變量可以保存一個類型的任何子類型,所以寫作number = new OddEven(); 編譯器知道number變量的真實類型是不夠的。 所以,你也必須聲明它。 Java 是一種強類型語言,這意味着每個變量和每個表達式都有一個在編譯時已知的類型。 您可能需要閱讀整個第 4 章Java 語言規范 (JLS) 的類型、值和變量以了解更多信息。

現在,當你寫:

String str = "abc";

事情有點不同。 用雙引號括起來的字符,這里的"abc" ,被稱為字符串文字,它已經是對String實例的引用,並且總是引用類String的同一個實例。 引用 JLS 的第3.10.5字符串文字

每個字符串文字是基准(§4.3)到一個實例( 第4.3.1節§12.5類的) String (§4.3.3) 。 String對象具有常量值。 字符串字面量——或者更一般地說,作為常量表達式值的字符串(第 15.28 節) ——被“ 嵌入”,以便使用String.intern方法共享唯一的實例。

所以, String str = "abc"; 肯定不是轉換成String str = new String("abc"); 這絕對不等同於我在一些評論和答案中讀到的。 運行以下類:

public class Test {
    public static void main(String[] args) {
        String one = "abc";
        String two = "abc";
        String abc = new String("abc");

        System.out.println(one == two);
        System.out.println(one == abc);
    }
}

產生以下輸出:

true
false

並演示了onetwo是對同一個實例的引用,而abc是對另一個實例的引用(即創建了一個額外的不必要的對象)。

實際上,使用new String(String)是一種構建新字符串的低效方式,只能用於強制將子字符串復制到新的底層字符數組,如

String tiny = new String(monster.substring(10,20))

將“奇偶數”視為定義對象和“new OddEven();” 作為填充對象。

我不打算詳細介紹超類和子類,因為其他人已經解釋過了。

當您說String name = "foo" ,Java 編譯器內部會創建一個值為 "foo" 的 String 對象,並將其引用分配給name變量。 因此,這里不是創建一個新的 String 對象,而是為另一個 String 對象分配一個引用。

順便說一句,編譯器無論如何都會為我們創建“foo”。 它首先在字符串池中查找,如果它不存在,則才創建“foo”。 否則,Compiler 從字符串池返回一個引用。 這是 Java 編譯器在內部執行的一些優化。

String name = "foo"與 OddEvenNumber OddEvenNumber oddEven = anotherOddEvenObject;

數組示例:

聲明和初始化 - 當您知道數組的長度時:

int[] numberArray = new int[10];

聲明然后初始化 - 當您還不知道數組的長度並且可能從方法或用戶輸入中獲取它時

int[] numberArray;
int length = 10; // let’s say we got this from the user
numberArray = new int[length];

僅初始化 - 當您不需要重用時:

return new int[10];

第一個OddEven是類型,第二個是實例。 它甚至不必是OddEven ,它可以是OddEven任何子類。 這並不意味着您輸入了兩次。 任何 IDE 都有代碼模板,您只需在其中鍵入一次名稱。

第一個聲明是您想要在您擁有的范圍內使用的變量類型,在本例中是 OddEven,第二個聲明是用於實例化(在本例中初始化)引用的構造函數。

您可以說 INumberInstance = new OddEven(),其中 INumberInstance 是 OddEven 可以轉換到的某個類(例如 OddEven 的超類)。

在java中創建新對象的方法是:

Class_name reference_variable = new Class_name(param_if_any);

字符串類是一個例外

您可以創建一個新的字符串對象作為

String s = "abc";

或者

String s = new String("abc");

正如 Jim 所說,Java 是一種靜態類型語言。 這意味着每個變量都有一個在編譯時已知的類型。

例如:

public class A
{
    public void foo() { }
}

public class B
{
    public void foo() { }
}

public class Main
{
    public static void main(final String[] argv)
    {
        A a = new A();
        B b = new B();

        a.foo();
        b.foo();
    }
}

編譯器查看 "a.foo()" 和 "b.foo()" 並檢查 a 是否屬於 A 類型,並且 A 有一個名為 "foo" 的方法,該方法不帶參數。 編譯器對“b.foo()”做同樣的事情。

如果你可以這樣寫 main:

public class Main
{
    public static void main(final String[] argv)
    {
        a = new A(); // in Java you would really do Object a = new A();
        b = new B(); // in Java you would really do Object b = new B();

        a.foo();
        b.foo();
    }
}

那么編譯器就無法進行該驗證,並且必須在運行時進行。

的Java的設計者並沒有做語法冗余。 Scala是另一種使用 JVM 的語言,它也是靜態類型的。 Scala 使用類型推斷來減少冗長。 例如,這里有一個名為 x 的 MyPair 類型變量的聲明。 MyPair 將兩個變量相互關聯。 它是一個泛型類,因此您可以指定第一個變量的類型為 Int,第二個變量的類型為 String:

var x: MyPair[Int, String] = new MyPair[Int, String](1, "scala")

Scala 類型推斷允許您刪除冗余類型聲明:

var x = new MyPair[Int, String](1, "scala")

Scala 甚至根據構造函數參數推斷類型,所以你可以這樣寫:

var x = new MyPair(1, "scala")

考慮下面的例子,

我們可以指定對象類型如下,

List<String> abc;

在method1()中,如果你想使用最適合它要求的數組列表,那么我們可以像下面這樣實例化,

abc = new ArrayList<String>();

在method2()中,如果你想使用最適合它要求的Linked array list,那么我們可以像下面這樣實例化,

abc = new LinkedList<String>();

因此,我們的想法是我們可以指定“SuperClass”的類型,並在適當的操作中動態地使用適合不同要求的任何子類(例如“LinkedList”和“ArrayList”)進行實例化。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM