#Niezbędnik Juniora. String i StringBuilder

By 8 July 2015 October 2nd, 2016 Niezbędnik Juniora

Są takie klasy w Javie, o których przeczy­tać po pros­tu trze­ba. Jed­ną z nich jest String i dlat­ego w dzisiejszym niezbęd­niku postaramy się szczegółowo opisać jej metody i właściwości.

String to naprawdę pod­stawa Two­jego codzi­en­nego pro­gramowa­nia, bez niego nie napiszesz nawet metody main :) W jed­nym zda­niu String to sek­wenc­ja znaków np.

String przedmiot = "matematyka";

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

String przedmiot = new String("biologia");

Oba te sposo­by są poprawne jed­nak różnią się one w sub­tel­ny sposób. Aby go wyjaśnić musimy wprowadz­ić poję­cie puli (ang. pool). Java stara się dobrze zarządzać swo­ją pamię­cią dlat­ego uży­wa wewnętrznych  puli do prze­chowywa­nia pow­tarza­ją­cych się lit­er­ałów (czyli wpisanych przez ciebie łańcuchów znaków) i wyko­rzys­tu­je je zami­ast tworzyć nowe (tak samo robi np. z liczba­mi od ‑128 do 127, które ma w spec­jal­nym inte­ger pool i zami­ast tworzyć nowe wielokrot­nie uży­wa tych samych z dostęp­nego zasobu). Wraca­jąc do naszego przykładu tworzenia nowego obiek­tu typu String, tworząc obiekt pier­wszym sposobem mówimy wirtu­al­nej maszynie, że chce­my by wyko­rzys­tała string pool. W drugim przy­pad­ku, nakazu­je­my jej by utworzyła nowy obiekt (czego w nor­mal­nej pro­gramisty­cznej pra­cy naprawdę nie chce­my robić :))

Dla zain­tere­sowanych: w prak­tyce należy stosować pier­wszą metodę (chy­ba, że naprawdę chce­my utworzyć nowy obiekt z ważnego powodu) z kilku powodów — pier­wszy to wspom­ni­ana wyżej pula Stringów, ale istot­na jest też kwes­t­ia wyko­rzys­ta­nia pamię­ci (uży­cie kon­struk­to­ra wspom­ni­anego w drugim przy­pad­ku powodu­je sko­pi­owanie całego obszaru pamię­ci, co może być kosz­towne, jeśli oper­ację tę przeprowadza­my na długich cią­gach znaków). Nie znaczy to jed­nak, że kon­struk­tor ten jest całkowicie bezuzyteczny, są sytu­acje, kiedy jego uży­cie jest wskazane. Zain­tere­sowanych szczegóła­mi odsyłamy na stronę http://kjetilod.blogspot.ie/2008/09/string-constructor-considered-useless.html .

Konkatenacja (Concatenation)

To kole­j­na właś­ci­wość obiek­tów typu String. Na pewno wiesz, że dodanie dwóch prim­i­ty­wów do siebie to nor­mal­na matem­aty­ka i 3+5=8. Jak zmienić wynik tego dzi­ała­nia na 35? Wystar­czy wziąć 3 i 5 w cud­zysłowy. To właśnie konkate­nac­ja. Java potrafi połączyć dwa Strin­gi w nowy String.

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

 

Z konkate­nacją związane są 3 dość proste reguły. Jeśli mamy tylko licz­by to mamy do czynienia z dzi­ałaniem matem­aty­cznym. Jeśli jed­nym z czyn­ników jest String to mamy do czynienia z konkate­nacją (patrz 3 przykład). A wyraże­nie jest wykony­wane od lewej do prawej.

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

Warto dodać, że do konkate­nacji może­my również uży­wać +=. Przykład­owo 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 stwor­zonego obiek­tu typu String. Skra­cać, wydłużać, zmieni­ać czegoś w środ­ku. Możesz pomyśleć o nim jak o np. odcisku przed­mio­tu w gip­sie. Przed­miot będzie do niego ide­al­nie pasował, jed­nak jakakol­wiek zmi­ana ksz­tał­tu tak otrzy­manej formy nie jest możli­wa bez jej zniszczenia. Żeby to zro­bić bez­in­wazyjne potrze­bu­jesz kole­jnego zestawu do odcisku w gipsie.

Jeśli klasa jest niezmi­en­na  oznacza to w Javie dwie rzeczy. Po pier­wsze, nie ma w sobie set­tera (ma tylko get­ter), a po drugie jest final i pod­klasy nie mogą umożli­wić jej zmi­en­noś­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 pier­wszej lin­i­jce tworzymy obiekt typu string, w drugiej tworzymy dru­gi obiekt typu String, ale 3 lin­ij­ka nie zadzi­ała tak jak się moż­na tego spodziewać (w rzeczy­wis­toś­ci utworzy nowy obiekt typu String, ale nie przyp­isze go do żad­nej zmi­en­nej; obiekt ten może być usuni­ety następ­nym razem kiedy odpal­imy GarbageCollector).

Przegląd najważniejszych metod

Poniżej opisze­my krótko wszys­tkie 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) kole­jnych liter w ramach obiek­tu typu String.

length()

Zwraca ilość znaków.
Przykład­owo:

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

charAt()

Poda­je jaka lit­era jest pod danym indek­sem (pier­wsza lit­era to indeks 0).
Przykład­owo:

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

Podanie indek­su, który nie ist­nieje spowodu­je wyrzuce­nie Index­Out­Of­Bound­sEx­cep­tion.

indexOf()

Wyszuku­je pier­wszy indeks wstępowa­nia dla danego znaku lub ciągu znaków. Moż­na w niej podać dodatkowo, od którego miejs­ca ma zacząć wyszukiwać.
Przykład­owo: 

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 przy­pad­ku, gdy poszuki­wany ciąg znaków nie został znaleziony meto­da ta zwraca ‑1 (a nie wyjątek!).

substring()

Zwraca pod­ciąg tego ciągu znaku, który zaczy­na się w określonym miejs­cu i może mieć określoną długość.

Przykład­owo: 

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 kon­wer­tu­ją cały ciąg znaków na małe/duże litery.

equals(), equalsIgnoreCase()

Meto­da equals() sprawdza, czy dwa obiek­ty typu String składa­ją się dokład­nie z takich samych ciągów znaków, a equal­sIg­nore­Case() ignoru­je 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 Strin­gi to też obiek­ty i do ich porówny­wa­nia nie może­my używać ==. 

Więcej infor­ma­cji o metodzie equals oraz związanej z nią metodzie hash­Code zna­jdziesz we wpisie z cyk­lu Niezbęd­nik Junio­ra.

startWith(), endsWith()

Sprawdza­ją, czy dana wartość odpowia­da odpowied­niej częś­ci obiek­tu typu String (a więc czy nasz obiekt się na nią zaczy­na czy kończy).

contains()

Ta meto­da również sprawdza czy  nasz string zaw­iera dany ciąg znaków (zarówno strartWith(),endWith() jak i con­tains() przyj­mu­ją Stringa, a także rozróż­ni­a­ją wielkość znaków).

replace()

Meto­da ta wyszuku­je i zamienia odpowied­nią część naszego obiek­tu typu String. Meto­da ta przyj­mu­je zarówno char jak i CharSe­quence — które jest inter­fe­jsem zarówno dla  Stringa jak i StringBuildera.

Przykład­owo:

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

 

W pier­wszym przykładzie ope­ru­je­my na typ­ie char (apos­trof ”), w drugim na String.

trim()

Usuwa białe zna­ki, które zna­j­du­ją się na początku i końcu obiek­tu typu String (i tylko tam!).

Łączenie metod (method chaining)

Jeśli chcesz wyko­rzys­tać kil­ka metod do stworzenia nowego obiek­tu typu String z innego, możesz oczy­wiś­cie zro­bić to “po kolei”, przyp­isu­jąc kole­jne wyni­ki oper­acji do nowych zmiennych.

Przykład­owo:

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

W wyniku tych oper­acji otrzy­mamy A. Może­my też uproś­cić nieco zapis i przy­wołać kole­jne metody w łańcuchu.

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

Zapisu­jąc w ten sposób, musimy pamię­tać o tym, że metody wykonu­ją się od lewej do prawej, oraz, że String jest niezmi­en­ny, więc wynik naszych oper­acji musimy przyp­isać do nowej zmiennej.

String Builder

Niezmi­en­ność Stringa jest nieprak­ty­cz­na, np. jeśli chce­my połączyć dużo infor­ma­cji w jeden ciąg znaków w sposób efek­ty­wny. Odpowiedz­ią na ten prob­lem jest String Builder (od Javy 5, wcześniej podob­ne zadanie speł­ni­ał String­Buffer, z którego nadal możesz korzys­tać — ist­nieją drob­ne różnice, zain­tere­sowanych odsyłamy do doku­men­tacji Javy lub Stac­ka).

String Builder pozwala łączyć ze sobą różne infor­ma­c­je 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

String­Builder posi­a­da również kil­ka ciekawych metod:

  • reverse() — odwraca kole­jność znaków w naszym obiek­cie typu StringBuider
  • toString() — kon­wer­tu­je nasz obiekt do obiek­tu typu String

Dodatkowe Materiały

Doku­men­tac­ja Ora­cle: String, String­Builder

OCA StudyGuide