Особенностью объекта класса String является то, что его значение не может быть изменено после создания объекта при помощи какого-либо метода, так как любое изменение приводит к созданию нового объекта. При этом ссылку на объект класса String можно изменить так, чтобы она указывала на другой объект. Класс String поддерживает несколько конструкторов, например:
Эти конструкторы используются для инициализации объектов класса. Например, при вызове конструктора String(str.getBytes(),"Cp1251"), где str – строка в формате Unicode, можно установить необходимый алфавит, в данном случае кириллицу. Когда Java встречает литерал, заключенный в двойные кавычки, автоматически создается объект типа String, на который можно установить ссылку. Таким образом, объект класса String можно создать, присвоив ссылке на класс значение существующего литерала, или с помощью оператора new и конструктора, например:
String s1 = ”sun.com”;
String s2 = new String(”sun.com”);
Класс String содержит следующие методы для работы со строками:
concat(String s) или “+” – слияние строк;
equals(Object ob) и equalsIgnoreCase(String s) – сравнение строк с учетом и без учета регистра соответственно;
compareTo(String s) и compareToIgnoreCase (String s) – лексикографическое сравнение строк с учетом и без учета регистра;
contentEquals(StringBuffer ob) – сравнение строки и содержимого объекта типа StringBuffer;
substring(int n, int m) – извлечение из строки подстроки длины m-n, начиная с позиции n;
substring(int n) – извлечение из строки подстроки, начиная с позиции n;
length() – определение длины строки;
valueOf(значение) – преобразование переменной базового типа к строке;
toUpperCase()/toLowerCase() – преобразование всех символов вызывающей строки в верхний/нижний регистр;
replace(char с1, char с2) – замена в строке всех вхождений первого символа вторым символом;
intern(String str) – занесение строки в “пул” литералов;
trim()– удаление всех пробелов в начале и конце строки;
charAt(int position) – возвращение символа из указанной позиции (нумерация с нуля);
getBytes(параметры), getChars(параметры) – извлечение символов строки в виде массива байт или символов.
Во всех случаях вызова методов изменяющих строку создается новый объект типа String. В следующем примере массив символов и целое число преобразуются в объекты типа String с использованием методов этого класса.
/* пример # 1 : использование методов : DemoString.java */
public class DemoString {
static int i;
public static void main(String[] args) {
char s[] = { 'J', 'a', 'v', 'a' };
//комментарий содержит результат выполнения кода
String str = new String(s); //str="Java"
i = str.length(); //i=4
String num = String.valueOf(2); //num="2"
str = str.toUpperCase(); //str="JAVA"
num = str.concat(num); //num="JAVA2"
str = str + "C";//str="JAVAC";
char ch = str.charAt(2); //ch='V'
i = str.lastIndexOf('A'); //i=3 (-1 если нет)
num = num.replace('2', 'H'); //num="JAVAH"
i = num.compareTo(str); //i=5 между символами H и С
str.substring(0, 4).toLowerCase(); //java
}
}
Сохранить изменения в объекте класса String можно только с применением оператора присваивания, т.е. установкой ссылки на новый объект.
/* пример # 2 : передача строки по ссылке :
RefString.java */
public class RefString {
static String changeStr(String st) {
st = st.concat(" Microsystems");
return st;
}
public static void main(String[] args) {
String str = new String("Sun");
changeStr(str);
// str = changeStr(str);//сравнить результат!
System.out.println(str);
}
}
В результате будет выведена строка:
Sun
Так как объект был передан по ссылке, то любое изменение объекта в методе должно сохраняться и для исходного объекта, так как обе ссылки равноправны. Этого не происходит по той причине, что вызов метода concat() приводит к созданию нового объекта, на который ссылается локальная ссылка. Этот же объект возвращается оператором return, но возвращаемое значение ничему не присваивается, поэтому все изменения теряются. Если изменить код, как показано в комментарии, то все изменения объекта, произведенные в методе changeStr(), будут сохранены в объекте, объявленном в main().
Далее рассмотрены особенности способов хранения и идентификации объектов на примере вызова метода equals(), сравнивающего строку String с указанным объектом и метода hashCode(), который вычисляет хэш-код объекта.
/* пример # 3 : сравнение ссылок и объектов :
EqualStrings.java */
public class EqualStrings {
public static void main(String[] args) {
String s1 = "Java";
String s2 = "Java";
String s3 = new String(s1);
System.out.println(s1 + "==" + s2
+ " : " + (s1==s2));//true
System.out.println(s1 + "==" + s3
+ " : " + (s1==s3));//false
System.out.println(s1 + " equals " + s2
+ " : " + s1.equals(s2));//true
System.out.println(s1 + " equals " + s3
+ " : " + s1.equals(s3));//true
System.out.println(s1.hashCode());
System.out.println(s2.hashCode());
System.out.println(s3.hashCode());
}
}
В результате, например, будет выведено:
Java==Java : true
Java==Java : false
Java equals Java : true
Java equals Java : true
Несмотря на то, что одинаковые по значению объекты расположены в различных участках памяти, значения их хэш-кодов совпадают. В Java все ссылки хранятся в стеке, а объекты – в куче. При создании s2 сначала создается ссылка, а затем этой ссылке устанавливается в соответствие объект. В данной ситуации s2 ассоциируется с уже существующим литералом, так как объект s1 уже сделал ссылку на этот литерал. При создании s3 происходит вызов конструктора, то есть выделение памяти происходит раньше инициализации, и в этом случае в куче создается новый объект. Существует возможность сэкономить память и переопределить ссылку с объекта на литерал при помощи вызова метода intern().
// пример # 4 : применение intern() : DemoIntern.java
public class DemoIntern {
public static void main(String[] args) {
String s1 = "Java";//литерал и ссылка на него
String s2 = new String("Java");
System.out.println(s1 == s2); //false
s2 = s2.intern();
System.out.println(s1 == s2); //true
}
}
В данной ситуации ссылка s1 инициализируется литералом, обладающим всеми свойствами объекта вплоть до вызова методов. Вызов метода intern() организует поиск соответствующего значению объекта s2 литерала (канонического представления строки) и при положительном результате возвращает ссылку на найденный литерал, а при отрицательном – заносит значение в пул и возвращает ссылку на него. В следующем примере рассмотрена сортировка массива строк методом выбора.
Вызов метода trim() обеспечивает удаление всех начальных и конечных символов пробелов. Метод compareTo() выполняет лексикографическое сравнение строк между собой по правилам Unicode.