Dlaczego Klasa String jest deklarowana jako ostateczna w Javie?

Od kiedy dowiedziałem się, że klasa java.lang.String jest zadeklarowana jako ostateczna w Javie, zastanawiałem się dlaczego tak jest? Nie znalazłem wtedy żadnej odpowiedzi, ale ten post: Jak stworzyć replikę klasy String w Javie?Przypomniało mi się moje zapytanie.

Jasne, String zapewnia wszystkie funkcje, jakich kiedykolwiek potrzebowałem, i nigdy nie myślałem o żadnej operacji, która wymagałaby rozszerzenia klasy String, ale nadal nigdy nie dowiesz się, czego ktoś może potrzebować!

Czy ktoś wie, co było intencje projektantów, kiedy zdecydowali się na ostateczność?
Author: Community, 2010-01-15

16 answers

Bardzo przydatne jest, aby łańcuchy znaków były zaimplementowane jako obiekty niezmienne . Powinieneś przeczytać o niezmienności , aby zrozumieć więcej na ten temat.

Jedną z zalet obiektów niezmiennych jest to, że

Można udostępniać duplikaty, wskazując je na pojedynczą instancję.

(z tutaj ).

Jeśli String nie był ostateczny, można utworzyć podklasę i mieć dwa ciągi, które wyglądają podobnie, gdy "postrzegane jako ciągi", ale które są w rzeczywistości inaczej.

 84
Author: Bruno Reis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-15 01:31:25

Jest to ładny artykuł , który przedstawia dwa powody już wymienione w powyższych odpowiedziach:

  1. bezpieczeństwo : system może rozdawać czułe bity tylko do odczytu informacji bez obawy, że zostaną zmienione
  2. wydajność : Dane niezmienne są bardzo przydatny w tworzeniu rzeczy bezpiecznych.

I to chyba najbardziej szczegółowy komentarz w tym artykule. Ma to związek z pulą ciągów w Javie i zabezpieczeniami problemy. Chodzi o to, jak zdecydować, co idzie do puli ciągów. Zakładając, że oba ciągi znaków są równe, jeśli ich sekwencja znaków jest taka sama, to mamy warunek rasy, kto dostanie się tam pierwszy i wraz z problemami bezpieczeństwa it. Jeśli nie, to Pula ciągów będzie zawierać nadmiarowe ciągi, tracąc tym samym przewagę posiadania go w pierwszej kolejności. Po prostu przeczytaj to dla siebie, dobrze?


Rozszerzanie łańcucha zagrałoby spustoszenie z równymi i intern. JavaDoc says równa się:

Porównuje ten łańcuch z podanym obiektem. Wynik jest prawdziwy wtedy i tylko wtedy, gdy argument nie jest null i jest obiektem typu String, który reprezentuje ten sam ciąg znaków co ten obiekt.

Zakładając, że java.lang.String nie jest ostateczne, a SafeString może być równe a String i odwrotnie, ponieważ reprezentowałyby ten sam ciąg znaków.

Co by się stało, gdybyś zastosował intern do SafeString -- Czy SafeString wejdzie do puli ciągów JVM? ClassLoader i wszystkie obiekty w przeciwieństwie do JVM-a, JVM-a nie JVM-a, JVM-a nie JVM-a, JVM-a nie JVM-a. Otrzymałbyś warunek race o tym, kto może być pierwszym, który internuje sekwencję znaków - może twój SafeString wygrałby, może String, a może SafeString załadowany przez inny classloader (a więc inna klasa).

Jeśli wygrasz wyścig do basenu, będzie to prawdziwy singleton, a ludzie będą mogli uzyskać dostęp do całego Twojego środowiska (piaskownicy) poprzez refleksję i secretKey.intern().getClass().getClassLoader().

Lub JVM może zablokować ten otwór, upewniając się, że do puli dodano tylko betonowe obiekty ciągów (i nie podklasy).

If equals został zaimplementowany tak, że SafeString != String then SafeString.intern != String.intern, i SafeString musiałyby być dodane do puli. Pula stanie się wtedy pulą <Class, String> zamiast <String>, a wszystko, czego potrzebujesz, aby wejść do puli, będzie nowym classloaderem.

 59
Author: Anurag,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-30 14:05:58

Absolutnie najważniejszym powodem, dla którego String jest niezmienny lub ostateczny jest to, że jest używany przez mechanizm ładowania klas, a tym samym ma głębokie i fundamentalne aspekty bezpieczeństwa.

Gdyby String był zmienny lub nie końcowy, żądanie załadowania " java. io. Writer "mogło zostać zmienione na" mil.vogoon.DiskErasingWriter "

Reference: Why String is immutable in Java

 24
Author: Jackob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-05-21 03:55:31

String jest bardzo podstawową klasą w Javie, wiele rzeczy polega na tym, że działa w określony sposób, na przykład jest niezmienna.

Tworzenie klasy final zapobiega podklasom, które mogłyby złamać te założenia.

Zauważ, że nawet teraz, jeśli używasz reflection, możesz łamać Łańcuchy (zmienić ich wartość lub hashcode). Odbicie można zatrzymać za pomocą Menedżera ochrony. Gdyby String nie było final, każdy mógłby to zrobić.

Inne klasy, które nie są zadeklarowane final pozwalają na zdefiniuj nieco zepsute podklasy (możesz mieć List, który dodaje do niewłaściwej pozycji, na przykład), ale przynajmniej JVM nie zależy od tych dla swoich podstawowych operacji.

 15
Author: Thilo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-15 01:45:16

Jak powiedział Bruno, chodzi o niezmienność. Nie chodzi tylko o ciągi znaków, ale także o dowolne wrappery, np. Double, Integer, Character, itp. Powodów jest wiele:

  • zabezpieczenie gwintu
  • Bezpieczeństwo
  • sterta, która jest zarządzana przez samą Javę (inaczej niż zwykła sterta, która jest śmieciami zbieranymi w inny sposób)
  • Zarządzanie pamięcią

W Zasadzie to Ty, jako programista, możesz być pewien, że Twój ciąg znaków nigdy nie zostanie zmieniony. To również, jeśli wiesz, jak to działa, może poprawić zarządzanie pamięcią. Spróbuj utworzyć dwa identyczne ciąg znaków jeden po drugim, na przykład "hello". Po debugowaniu zauważysz, że mają identyczne identyfikatory, co oznacza, że są dokładnie tymi samymi obiektami. Wynika to z faktu, że Java pozwala ci to zrobić. To nie byłoby możliwe, gdyby struny były mutowalne. Mogą mieć to samo i ' D itp., bo nigdy się nie zmienią. Więc jeśli kiedykolwiek zdecydujesz się stworzyć 1,000,000 string "hello" to co naprawdę zrobisz to Utwórz 1 000 000 wskaźników do "hello". Podobnie jak Dowolna funkcja na łańcuchu, lub dowolne wrappery z tego powodu, spowodowałoby utworzenie innego obiektu(ponownie spójrz na ID obiektu - to się zmieni).

Aditional final w Javie Nie koniecznie oznacza, że obiekt nie może się zmieniać (jest inny niż na przykład C++). Oznacza to, że adres, na który wskazuje, nie może się zmienić, ale nadal można zmienić jego właściwości i / lub atrybuty. Więc zrozumienie różnicy między niezmienność i ostateczna w niektórych przypadkach może być naprawdę ważna.

HTH

Bibliografia:

 6
Author: Artur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-15 02:00:18

Być może chodziło o uproszczenie implementacji. Jeśli zaprojektujesz klasę, która będzie dziedziczona przez użytkowników tej klasy, masz do rozważenia zupełnie nowy zestaw przypadków użycia. Co się stanie, jeśli zrobią to lub tamto z X podpartym polem? Dzięki temu mogą skupić się na poprawnym działaniu publicznego interfejsu i upewnić się, że jest solidny.

 2
Author: AaronLS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-01-15 01:19:51

Mając już wiele dobrych punktów na uwadze chciałbym dodać jeszcze jeden-jednym z powodów, dla których String jest niezmienny w Javie jest umożliwienie String buforować swój hashcode , będąc niezmienny String w Javie buforuje swój hashcode, i nie obliczać za każdym razem, gdy wywołujemy metodę hashcode String, co sprawia, że bardzo szybko jak hashmap klucz być używany w hashmap w Javie.

W skrócie, ponieważ String jest niezmienny, nikt nie może zmienić jego zawartości po utworzeniu, co gwarantuje, że hashCode łańcucha znaków będzie taki sam przy wielu wywołaniach.

Jeśli widzisz String klasa has jest zadeklarowana jako

/** Cache the hash code for the string */
private int hash; // Default to 0

I hashcode() funkcja jest następująca -

public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;

        for (int i = 0; i < value.length; i++) {
            h = 31 * h + val[i];
        }
        hash = h;
    }
    return h;
}

Jeśli jest już komputerem po prostu zwróć wartość.

 2
Author: Aniket Thakur,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-01-10 12:06:53

Oprócz powodów wymienionych w innych odpowiedziach (bezpieczeństwo, niezmienność, wydajność) należy zauważyć, że String posiada specjalne Wsparcie językowe. Możesz pisać literały String i jest wsparcie dla operatora +. Pozwalanie programistom na podklasę String, zachęciłoby do takich hacków jak:

class MyComplex extends String { ... }

MyComplex a = new MyComplex("5+3i");
MyComplex b = new MyComplex("7+4i");
MyComplex c = new MyComplex(a + b);   // would work since a and b are strings,
                                      // and a string + a string is a string.
 2
Author: aioobe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-22 12:31:36

Cóż, mam inne zdanie nie jestem pewien, czy mam rację, czy nie, ale w Javie String jest jedynym obiektem, który może być traktowany jako prymitywny typ danych, czyli możemy utworzyć obiekt String jako String name= "java" . Teraz, podobnie jak inne prymitywne typy danych, które są copy by value a nie copy by reference oczekuje się, że String będzie miał takie samo zachowanie, więc dlatego String jest ostateczny. Tak myślałem. Proszę zignorować, jeśli to zupełnie nielogiczne.

 1
Author: webdev,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-08 20:08:45

Skończoność łańcuchów również broni ich jako standardu. W C++ można tworzyć podklasy string, więc każdy sklep programistyczny może mieć własną wersję string. Prowadziłoby to do braku solidnych standardów.

 1
Author: ncmathsadist,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-10 18:05:50

Aby upewnić się, że nie otrzymamy lepszej implementacji. To powinien być oczywiście interfejs.

[edytuj] coraz więcej bezmyślnych głosów w dół. Odpowiedź jest zupełnie poważna. Kilka razy musiałem zaprogramować implementację głupiego ciągu, co doprowadziło do poważnej utraty wydajności i produktywności

 1
Author: Stephan Eggermont,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-19 18:13:31

Oprócz oczywistych powodów sugerowanych w innych odpowiedziach, jedna myśl o zakończeniu klasy String może być również związana z kosztami wydajności metod wirtualnych. Pamiętaj, że String jest klasą ciężką, co czyni ją ostateczną, oznacza, że nie ma pod-implementacji na pewno, oznacza, że nie ma indrection wywołującej overhead kiedykolwiek. Oczywiście teraz mamy takie rzeczy jak virtual invoke i inne, które zawsze robią tego rodzaju optymalizację dla Ciebie.

 1
Author: Abhishek Singh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-20 13:03:21

Czy JVM wie co jest niezmienne? Odpowiedź brzmi nie, stała Pula zawiera wszystkie niezmienne pola, ale wszystkie niezmienne pola / obiekty nie są przechowywane tylko w stałej puli. Tylko my wdrażamy je w taki sposób, aby osiągały niezmienność i jej cechy. CustomString może być zaimplementowany bez kończenia go za pomocą MarkerInterface, który zapewni java specjalne zachowanie dla jego poolingu, funkcja jest nadal oczekiwana!

 0
Author: hi.nitish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-09 13:49:26

Większość odpowiedzi jest związana z niezmiennością -- dlaczego obiekt typu String nie może być aktualizowany w miejscu. Jest tu wiele dobrych dyskusji, a społeczność Javy zrobiłaby dobrze, aby przyjąć niezmienność jako zasadę główną. (Nie wstrzymuję oddechu.)

Jednak pytanie OP jest o to, dlaczego jest ostateczna -- dlaczego nie może być przedłużona. Niektórzy z nas wzięli to na siebie, ale Zgadzam się z OP, że jest tu prawdziwa luka. Inny język pozwala deweloperom tworzyć nowe typy nominalne dla Typ. Na przykład w Haskell mogę utworzyć następujące nowe typy, które są identyczne w czasie wykonywania tekstu, ale zapewniają bind-safety w czasie kompilacji.

newtype AccountCode = AccountCode Text
newtype FundCode = FundCode Text

Więc chciałbym przedstawić następującą sugestię jako ulepszenie języka Java:

newtype AccountCode of String;
newtype FundCode of String;

AccountCode acctCode = "099876";
FundCode fundCode = "099876";

acctCode.equals(fundCode);  // evaluates to false;
acctCode.toString().equals(fundCode.toString());  // evaluates to true;

acctCode=fundCode;  // compile error
getAccount(fundCode);  // compile error

(a może moglibyśmy zacząć odrywać się od Javy)

 0
Author: dsmith,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-10 23:31:15

Powiedzmy, że masz Employee klasę, która ma metodę greet. Po wywołaniu metody greet po prostu wyświetla Hello everyone!. Więc to jest oczekiwane zachowanie Z metody greet

public class Employee {

    void greet() {
        System.out.println("Hello everyone!");
    }
}

Teraz pozwól GrumpyEmployee podklasę Employee i nadpisać greet metodę, jak pokazano poniżej.

public class GrumpyEmployee extends Employee {

    @Override
    void greet() {
        System.out.println("Get lost!");
    }
}

Teraz w poniższym kodzie spójrz na metodę sayHello. Pobiera instancję Employee jako parametr i wywołuje metodę greet mając nadzieję, żeHello everyone! ale dostajemy to Get lost!. Ta zmiana w zachowaniu jest spowodowana Employee grumpyEmployee = new GrumpyEmployee();

public class TestFinal {
    static Employee grumpyEmployee = new GrumpyEmployee();

    public static void main(String[] args) {
        TestFinal testFinal = new TestFinal();
        testFinal.sayHello(grumpyEmployee);
    }

    private void sayHello(Employee employee) {
        employee.greet(); //Here you would expect a warm greeting, but what you get is "Get lost!"
    }
}

Taka sytuacja może być unika się Jeśli Employee klasa została wykonana final. Teraz od twojej wyobraźni zależy, ile chaosu może spowodować bezczelny programista, jeśli klasa String nie zostanie zadeklarowana jako final.

 0
Author: Andy,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-06 21:33:25

Jeśli utworzysz ciąg znaków, to będzie to obiekt, jeśli chcesz go zmodyfikować, nie jest to możliwe,utworzy on nowy obiekt.

 -1
Author: Suresh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-24 14:00:21