#Niezbędnik Juniora. String i StringBuilder

By 8 lipca 2015Niezbędnik Juniora

Są takie klasy w Javie, o których przeczytać po prostu trzeba. Jedną z nich jest String i dlatego w dzisiejszym niezbędniku postaramy się szczegółowo opisać jej metody i właściwości.

String to naprawdę podstawa Twojego codziennego programowania, bez niego nie napiszesz nawet metody main :) W jednym zdaniu String to sekwencja znaków np.

String przedmiot = "matematyka";

Można też stworzyć Stringa poprzez inny zapis:

String przedmiot = new String("biologia");

Oba te sposoby są poprawne jednak różnią się one w subtelny sposób. Aby go wyjaśnić musimy wprowadzić pojęcie puli (ang. pool). Java stara się dobrze zarządzać swoją pamięcią dlatego używa wewnętrznych  puli do przechowywania powtarzających się literałów (czyli wpisanych przez ciebie łańcuchów znaków) i wykorzystuje je zamiast tworzyć nowe (tak samo robi np. z liczbami od -128 do 127, które ma w specjalnym integer pool i zamiast tworzyć nowe wielokrotnie używa tych samych z dostępnego zasobu). Wracając do naszego przykładu tworzenia nowego obiektu typu String, tworząc obiekt pierwszym sposobem mówimy wirtualnej maszynie, że chcemy by wykorzystała string pool. W drugim przypadku, nakazujemy jej by utworzyła nowy obiekt (czego w normalnej programistycznej pracy naprawdę nie chcemy robić :))

Dla zainteresowanych: w praktyce należy stosować pierwszą metodę (chyba, że naprawdę chcemy utworzyć nowy obiekt z ważnego powodu) z kilku powodów – pierwszy to wspomniana wyżej pula Stringów, ale istotna jest też kwestia wykorzystania pamięci (użycie konstruktora wspomnianego w drugim przypadku powoduje skopiowanie całego obszaru pamięci, co może być kosztowne, jeśli operację tę przeprowadzamy na długich ciągach znaków). Nie znaczy to jednak, że konstruktor ten jest całkowicie bezuzyteczny, są sytuacje, kiedy jego użycie jest wskazane. Zainteresowanych szczegółami odsyłamy na stronę http://kjetilod.blogspot.ie/2008/09/string-constructor-considered-useless.html .

Konkatenacja (Concatenation)

To kolejna właściwość obiektów typu String. Na pewno wiesz, że dodanie dwóch primitywów do siebie to normalna matematyka i 3+5=8. Jak zmienić wynik tego działania na 35? Wystarczy wziąć 3 i 5 w cudzysłowy. To właśnie konkatenacja. Java potrafi połączyć dwa Stringi w nowy String.

System.out.println(1+3 );// 4
System.out.println("1"+"3" );//13
System.out.println(2+"3");//23

 

Z konkatenacją związane są 3 dość proste reguły. Jeśli mamy tylko liczby to mamy do czynienia z działaniem matematycznym. Jeśli jednym z czynników jest String to mamy do czynienia z konkatenacją (patrz 3 przykład). A wyrażenie jest wykonywane od lewej do prawej.

System.out.println(1+2+"5"); //35

Warto dodać, że do konkatenacji możemy również używać +=. Przykładowo s+=”2″ znaczy tyle co s=s+”2″, czyli:

String s = "1";
s+="2";
s+="3";
System.out.println (s);//123

Niezmienność (Immutability)

Nie można zmienić raz stworzonego obiektu typu String. Skracać, wydłużać, zmieniać czegoś w środku. Możesz pomyśleć o nim jak o np. odcisku przedmiotu w gipsie. Przedmiot będzie do niego idealnie pasował, jednak jakakolwiek zmiana kształtu tak otrzymanej formy nie jest możliwa bez jej zniszczenia. Żeby to zrobić bezinwazyjne potrzebujesz kolejnego zestawu do odcisku w gipsie.

Jeśli klasa jest niezmienna  oznacza to w Javie dwie rzeczy. Po pierwsze, nie ma w sobie settera (ma tylko getter), a po drugie jest final i podklasy nie mogą umożliwić jej zmienności. Co to oznacza w praktyce?

String opis ="śliczny";
String dokladnyOpis = opis.concat(" mały");
dokladnyOpis.concat("kotek");
System.out.println(dokladnyOpis); // śliczny mały

W pierwszej linijce tworzymy obiekt typu string, w drugiej tworzymy drugi obiekt typu String, ale 3 linijka nie zadziała tak jak się można tego spodziewać (w rzeczywistości utworzy nowy obiekt typu String, ale nie przypisze go do żadnej zmiennej; obiekt ten może być usuniety następnym razem kiedy odpalimy GarbageCollector).

Przegląd najważniejszych metod

Poniżej opiszemy krótko wszystkie najważniejsze metody Stringa. To o czym musisz pamiętać, to że Java liczy od 0 i w ten sam sposób podawane są miejsca(indeksy) kolejnych liter w ramach obiektu typu String.

length()

Zwraca ilość znaków.
Przykładowo:

String przyklad ="kotek";
System.out.println(przyklad.length());//5

charAt()

Podaje jaka litera jest pod danym indeksem (pierwsza litera to indeks 0).
Przykładowo:

String przyklad ="kotek";
System.out.println(przyklad.charAt(2));//t

Podanie indeksu, który nie istnieje spowoduje wyrzucenie IndexOutOfBoundsException.

indexOf()

Wyszukuje pierwszy indeks wstępowania dla danego znaku lub ciągu znaków. Można w niej podać dodatkowo, od którego miejsca ma zacząć wyszukiwać.
Przykładowo: 

String miejsce = "Las Vegas";
System.out.println(miejsce.indexOf("a"));//1
System.out.println(miejsce.indexOf("a", 4));//7
System.out.println(miejsce.indexOf("ga"));//6
System.out.println(miejsce.indexOf("as", 4));//7

W przypadku, gdy poszukiwany ciąg znaków nie został znaleziony metoda ta zwraca -1 (a nie wyjątek!).

substring()

Zwraca podciąg tego ciągu znaku, który zaczyna się w określonym miejscu i może mieć określoną długość.

Przykładowo: 

String miejsce = "przefantastycznie";
System.out.println(miejsce.substring(4));//fantastycznie
System.out.println(miejsce.substring(4, 7));//fan
System.out.println(miejsce.substring(4, 4));//pusty string
System.out.println(miejsce.substring(4, 3));//IndexOutOfBoundsException
System.out.println(miejsce.substring(4, 15));//IndexOutOfBoundsException

toLowerCase(), toUpperCase()

Dwie metody, które konwertują cały ciąg znaków na małe/duże litery.

equals(), equalsIgnoreCase()

Metoda equals() sprawdza, czy dwa obiekty typu String składają się dokładnie z takich samych ciągów znaków, a equalsIgnoreCase() ignoruje wielkość znaków, czyli:

String male ="abc";
String duze = "ABC";
System.out.println(male.equals(duze));//false
System.out.println(male.equalsIgnoreCase(duze));//true

Należy pamiętać, że Stringi to też obiekty i do ich porównywania nie możemy używać ==. 

Więcej informacji o metodzie equals oraz związanej z nią metodzie hashCode znajdziesz we wpisie z cyklu Niezbędnik Juniora.

startWith(), endsWith()

Sprawdzają, czy dana wartość odpowiada odpowiedniej części obiektu typu String (a więc czy nasz obiekt się na nią zaczyna czy kończy).

contains()

Ta metoda również sprawdza czy  nasz string zawiera dany ciąg znaków (zarówno strartWith(),endWith() jak i contains() przyjmują Stringa, a także rozróżniają wielkość znaków).

replace()

Metoda ta wyszukuje i zamienia odpowiednią część naszego obiektu typu String. Metoda ta przyjmuje zarówno char jak i CharSequence – które jest interfejsem zarówno dla  Stringa jak i StringBuildera.

Przykładowo:

System.out.println("kot".replace('o','i'));//kit
System.out.println("kot".replace("t","p"));//kop

 

W pierwszym przykładzie operujemy na typie char (apostrof ”), w drugim na String.

trim()

Usuwa białe znaki, które znajdują się na początku i końcu obiektu typu String (i tylko tam!).

Łączenie metod (method chaining)

Jeśli chcesz wykorzystać kilka metod do stworzenia nowego obiektu typu String z innego, możesz oczywiście zrobić to „po kolei”, przypisując kolejne wyniki operacji do nowych zmiennych.

Przykładowo:

String baza = " anna";
String bezSpacji = baza.trim();
String wielkaLitera = bezSpacji.toUpperCase();
Char inicjal = wielkaLitera.charAt(0);

W wyniku tych operacji otrzymamy A. Możemy też uprościć nieco zapis i przywołać kolejne metody w łańcuchu.

Char inicjal = " anna".trim().toUpperCase().charAt(0);

Zapisując w ten sposób, musimy pamiętać o tym, że metody wykonują się od lewej do prawej, oraz, że String jest niezmienny, więc wynik naszych operacji musimy przypisać do nowej zmiennej.

String Builder

Niezmienność Stringa jest niepraktyczna, np. jeśli chcemy połączyć dużo informacji w jeden ciąg znaków w sposób efektywny. Odpowiedzią na ten problem jest String Builder (od Javy 5, wcześniej podobne zadanie spełniał StringBuffer, z którego nadal możesz korzystać – istnieją drobne różnice, zainteresowanych odsyłamy do dokumentacji Javy lub Stacka).

String Builder pozwala łączyć ze sobą różne informacje za pomocą metody append() i nie niesie to za sobą tworzenia nowego obiektu.

Przykład:

StringBuilder sb = new StringBuilder().append("wiekKota").append(":").append(1).append('r');
System.out.println(sb);//wiekKota:1r

StringBuilder posiada również kilka ciekawych metod:

  • reverse() – odwraca kolejność znaków w naszym obiekcie typu StringBuider
  • toString() – konwertuje nasz obiekt do obiektu typu String

Dodatkowe Materiały

Dokumentacja Oracle: String, StringBuilder

OCA StudyGuide

  •  
  •  
  •  
  •  
  •  
  • Agata

    Hej, znalazłam trochę błędów :) Chyba ktoś pisał bez IDE :)
    1. Konkatenacja, jest String zamiast System: String.out.println(1+2+”5″); //35
    2. charAt(), wynikiem tego przykładu jest znak ‚t’: String przyklad =”kotek”; System.out.println(przyklad.charAt(2));//o
    3. substring(), są pogubione nawiasy zamykające oraz wkradł się indexOf zamiast substring
    4. Lączenie method:
    – String baza = ” anna”; String bezSpacji = anna.trim(); //brak zmiennej anna
    – String wielkaLitera = bezSpacji.toUpperCase(); String inicjal = wilekaLitera.charAt(0);// literówka w wielkaLitera, metoda charAt(x) zwraca chara :)

    • Cześć,
      faktycznie zakradło się kilka problemów, których nie wyłapaliśmy przed publikacją. Dziękujemy bardzo za uważne czytanie, błędy już poprawione! :)

      Pozdrawiamy

      • Asia

        Można jeszcze podomykać nawiasy w następujących liniach:

        System.out.println(miejsce.substring(4);//fantastycznie
        System.out.println(miejsce.substring(4, 4);//pusty string :)

  • lukasz

    Kiedy na początku napisałaś że można tworzyć string na dwa sposoby i „Oba te sposoby są poprawne” to już chciałem napisać że się mylisz :D Ale na końcu tego samego akapitu przeczytałem że drugi sposób tworzy nową instancję obiektu „czego w nor­mal­nej pro­gramisty­cznej pracy naprawdę nie chcemy robić :))” i się ucieszyłem ;) To prawda, drugi sposób jest poprawny ale raczej nie bardzo polecany, warto napisać dlaczego tak się dzieje i jaki to ma wpływ. Poza tym wszystko oki, ciekawy tekst.

    Pozdrawiam ;)

    • Cześć,
      uzupełniliśmy wpis o krótki opis wyjaśniający dlaczego jest to (najczęściej) niewskazana praktyka :)

      Pozdrawiamy!