From 34f25e725b178f2c9d5e2295ad67430bf3c9adfe Mon Sep 17 00:00:00 2001 From: olekstomek Date: Tue, 14 May 2019 00:44:54 +0200 Subject: [PATCH 1/3] =?UTF-8?q?Propozycja=20rozwi=C4=85zania?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/README.md b/README.md index 010d69d..f08955d 100644 --- a/README.md +++ b/README.md @@ -3,3 +3,71 @@ Rozwiązania do zadania z Newslettera #10 - 08 maja 2019 Do Newslettera można zapisać się na stronie: https://mailtrain.javastart.pl/subscription/tc5pDEzUq + +Omawiany kod: + +``` +class StringTask { + public static void main(String[] args) { + String string1 = "Hello".concat("World"); // Linia-1 + String string2 = new String("HelloWorld"); // Linia-2 + String string3 = string1.intern(); // Linia-3 + System.out.println(string1 == string3); // Linia-4 + } +} +``` +#### Dlaczego jeżeli w poniższym kodzie usunę Linia-2 to wynik Linia-4 zmieni się z false na true? + + +Spróbuję to wyjaśnić przedstawiając swoją teorię na ten temat. Wydaje mi się, że pomocne do zrozumienia tego przypadku jest wiedza o tym, że +`String string2 = new String("HelloWorld")` +nie tworzy jednego obiektu w pamięci ale dwa obiekty w pamięci. Jeden obiekt jest tworzony w String Pool i jeden poza String Pool. `string2` to referencja do obiektu nie będącego w String Pool. + +Co robi metoda `intern()?` Metoda ta pobiera wartość Stringa ze String Pool. W jaki sposób pobiera określa to dokumentacja: +> *

+> * When the intern method is invoked, if the pool already contains a +> * string equal to this {@code String} object as determined by +> * the {@link #equals(Object)} method, then the string from the pool is +> * returned. Otherwise, this {@code String} object is added to the +> * pool and a reference to this {@code String} object is returned. +> *

+Czyli jeśli w String Pool jest już dany String (w naszym przypadku "HelloWorld") to zostanie zwrócony, jeśli go nie ma w String Pool to ten String jest dodawany do String Pool i referencja do tego dodanego obiektu jest zwracana. +Jeszcze dokładniej co zwraca `public native String intern();`: +> * @return a string that has the same contents as this string, but is +> * guaranteed to be from a pool of unique strings. + +Co ciekawe, referencje do obiektów z wykomentawaną linią 2 czy bez wykomentowanej linii 2 po kompilacji zawsze są takie same: +``` +System.out.println("Hashcode string1 " + Integer.toHexString(string1.hashCode())); +System.out.println("Hashcode string3 " + Integer.toHexString(string3.hashCode())); +``` + +> true +> Hashcode string1 1a2fa200 +> Hashcode string3 1a2fa200 + +``` +System.out.println("Hashcode string1 " + Integer.toHexString(string1.hashCode())); +System.out.println("Hashcode string2 " + Integer.toHexString(string2.hashCode())); +System.out.println("Hashcode string3 " + Integer.toHexString(string3.hashCode())); +``` + +> false +> Hashcode string1 1a2fa200 +> Hashcode string2 1a2fa200 +> Hashcode string3 1a2fa200 + +Dlatego poleceniem `javap -c StringTask.class` można wykonać disassemblację kodu i podejrzeć zachowanie w pamięci: + + + +Niestety dość ciężko jest mi zrozumieć poszczególne kody, ale przykładowo __astore_X__ (X - cyfra) oznacza przypisanie referencji utworzonej w puli do zmiennej lokalnej, jak widać w przypadku kompilacji bez Linia-2 przypisanie referencji utworzonej w puli do zmiennej lokalnej odbywa się dwa razy: astore_1 i astore_2. W przypadku gdy zwracany jest false występują astore_1, astore_2 i astore_3. + +### Moja teoria: +* z Linia-2: +Referencja string3 wskazuje na obszar w pamięci na obiekt "HelloWorld" który jest zwracany przez metodę `intern()` ze String Pool (gdzie są przechowywane unikalne elementy w celu oszczędzenia pamięci) zgodnie z tym co powyżej. Jednak w STACK (na stosie) dołożony mamy nowy obiekt "HelloWorld" z inną referencją (`new String("HelloWorld")`) i to on jest wykorzystany (LIFO). Finalnie więc referencje `string1` i `string3` są różne, wskazują różne miejsce w pamięci na "HelloWorld". + +* bez Linia-2: +W przypadku gdy nie mamy linii: +```String string2 = new String("HelloWorld"); // Linia-2``` +metoda `intern()` zwraca Stringa ze String Pool i referencje są identyczne. From b11ce3a7621ccc8c58f822367f9ac24f9b89193c Mon Sep 17 00:00:00 2001 From: olekstomek Date: Tue, 14 May 2019 00:47:40 +0200 Subject: [PATCH 2/3] Update README.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dodanie zrzutów ekranu --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index f08955d..506b1e8 100644 --- a/README.md +++ b/README.md @@ -59,6 +59,13 @@ System.out.println("Hashcode string3 " + Integer.toHexString(string3.hashCode()) Dlatego poleceniem `javap -c StringTask.class` można wykonać disassemblację kodu i podejrzeć zachowanie w pamięci: +Z Linia-2: + +![with_Linia-2](https://user-images.githubusercontent.com/26818304/57659160-9c301900-75e1-11e9-8fc9-33166610b537.PNG) + +Bez Linia-2: + +![without_Linia-2](https://user-images.githubusercontent.com/26818304/57659159-9c301900-75e1-11e9-93cf-8314a88407f5.PNG) Niestety dość ciężko jest mi zrozumieć poszczególne kody, ale przykładowo __astore_X__ (X - cyfra) oznacza przypisanie referencji utworzonej w puli do zmiennej lokalnej, jak widać w przypadku kompilacji bez Linia-2 przypisanie referencji utworzonej w puli do zmiennej lokalnej odbywa się dwa razy: astore_1 i astore_2. W przypadku gdy zwracany jest false występują astore_1, astore_2 i astore_3. From 1befe7904cf4ffa08ca35ec175d25c926cecfd49 Mon Sep 17 00:00:00 2001 From: olekstomek Date: Thu, 16 May 2019 16:22:55 +0200 Subject: [PATCH 3/3] Update README.md dodanie poprawek po informacjach z wideo na YT --- README.md | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 506b1e8..620ef73 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ Rozwiązania do zadania z Newslettera #10 - 08 maja 2019 Do Newslettera można zapisać się na stronie: https://mailtrain.javastart.pl/subscription/tc5pDEzUq +https://youtu.be/4hevGbDXowg?t=154 - omówienie mojego rozwiązania + Omawiany kod: ``` @@ -23,7 +25,7 @@ Spróbuję to wyjaśnić przedstawiając swoją teorię na ten temat. Wydaje mi `String string2 = new String("HelloWorld")` nie tworzy jednego obiektu w pamięci ale dwa obiekty w pamięci. Jeden obiekt jest tworzony w String Pool i jeden poza String Pool. `string2` to referencja do obiektu nie będącego w String Pool. -Co robi metoda `intern()?` Metoda ta pobiera wartość Stringa ze String Pool. W jaki sposób pobiera określa to dokumentacja: +Co robi metoda `intern()`? Metoda ta pobiera wartość Stringa ze String Pool. W jaki sposób pobiera określa to dokumentacja: > *

> * When the intern method is invoked, if the pool already contains a > * string equal to this {@code String} object as determined by @@ -36,28 +38,28 @@ Jeszcze dokładniej co zwraca `public native String intern();`: > * @return a string that has the same contents as this string, but is > * guaranteed to be from a pool of unique strings. -Co ciekawe, referencje do obiektów z wykomentawaną linią 2 czy bez wykomentowanej linii 2 po kompilacji zawsze są takie same: +~~Co ciekawe, referencje do obiektów z wykomentawaną linią 2 czy bez wykomentowanej linii 2 po kompilacji zawsze są takie same:~~ ``` -System.out.println("Hashcode string1 " + Integer.toHexString(string1.hashCode())); -System.out.println("Hashcode string3 " + Integer.toHexString(string3.hashCode())); +System.out.println("Hashcode string1 " + System.identityHashCode(string1)); +System.out.println("Hashcode string3 " + System.identityHashCode(string3)); ``` -> true -> Hashcode string1 1a2fa200 -> Hashcode string3 1a2fa200 +> true +> Hashcode string1 = 356573597 +> Hashcode string2 = 356573597 ``` -System.out.println("Hashcode string1 " + Integer.toHexString(string1.hashCode())); -System.out.println("Hashcode string2 " + Integer.toHexString(string2.hashCode())); -System.out.println("Hashcode string3 " + Integer.toHexString(string3.hashCode())); +System.out.println("Hashcode string1 " + System.identityHashCode(string1)); +System.out.println("Hashcode string2 " + System.identityHashCode(string2)); +System.out.println("Hashcode string3 " + System.identityHashCode(string3)); ``` -> false -> Hashcode string1 1a2fa200 -> Hashcode string2 1a2fa200 -> Hashcode string3 1a2fa200 +> false +> Hashcode string1 = 356573597 +> Hashcode string2 = 1735600054 +> Hashcode string3 = 2168566900 -Dlatego poleceniem `javap -c StringTask.class` można wykonać disassemblację kodu i podejrzeć zachowanie w pamięci: +Poleceniem `javap -c StringTask.class` można wykonać disassemblację kodu i podejrzeć zachowanie w pamięci: Z Linia-2: @@ -72,9 +74,10 @@ Niestety dość ciężko jest mi zrozumieć poszczególne kody, ale przykładowo ### Moja teoria: * z Linia-2: -Referencja string3 wskazuje na obszar w pamięci na obiekt "HelloWorld" który jest zwracany przez metodę `intern()` ze String Pool (gdzie są przechowywane unikalne elementy w celu oszczędzenia pamięci) zgodnie z tym co powyżej. Jednak w STACK (na stosie) dołożony mamy nowy obiekt "HelloWorld" z inną referencją (`new String("HelloWorld")`) i to on jest wykorzystany (LIFO). Finalnie więc referencje `string1` i `string3` są różne, wskazują różne miejsce w pamięci na "HelloWorld". +Referencja string3 wskazuje na obszar w pamięci na obiekt "HelloWorld" ~~który jest zwracany~~ którego referencja jest zwracana przez metodę `intern()` ze String Pool (gdzie są przechowywane unikalne elementy w celu oszczędzenia pamięci) zgodnie z tym co powyżej. ~~Jednak w STACK (na stosie) dołożony mamy nowy obiekt "HelloWorld" z inną referencją (`new String("HelloWorld")`) i to on jest wykorzystany (LIFO). Finalnie więc referencje `string1` i `string3` są różne, wskazują różne miejsce w pamięci na "HelloWorld".~~ Przez to zwracane są dwie referencje różne - inna referencja dla string1 i inna referencja dla string3 która jest pobrana ze String Pool. * bez Linia-2: -W przypadku gdy nie mamy linii: +~~W przypadku gdy nie mamy linii: ```String string2 = new String("HelloWorld"); // Linia-2``` -metoda `intern()` zwraca Stringa ze String Pool i referencje są identyczne. +metoda `intern()` zwraca Stringa ze String Pool i referencje są identyczne.~~ +W String Pool nie mamy "HelloWorld" więc metoda `intern()` wrzuca obiekt "HelloWorld" do String Pool na który wskazuje string1 i zwraca do niego referncje. Stąd string1 i string3 mają takie same referencje.