
Класс Object
На вершине иерархии классов находится класс Object. Ссылочная переменная типа Object может обращаться к объекту любого другого класса, кроме того, переменная типа Object может указывать на любой массив, так как массивы реализуются как классы. В классе Object определен набор методов, который наследуется всеми классами. Следует отметить два метода: equals() и toString(). Метод equals() при сравнении двух объектов возвращает истину, если объекты эквивалентны, и ложь – в противном случае. Если требуется сравнивать объекты класса, созданного программистом, то этот метод необходимо переопределять в этом классе. Метод toString() возвращает строку с описанием объекта в виде:
getClass().getName() + '@' +
Integer.toHexString(hashCode())
Метод вызывается автоматически, когда объект выводится методами println(), print() и некоторыми другими.
Метод hashCode() переопределен, как правило, в каждом классе Java и возвращает число, являющееся уникальным идентификатором объекта, зависящем в большинстве случаев только от значения объекта. Метод hashCode() возвращает хэш-код объекта, который вычисляется по принципу – различные по содержанию объекты одного и того же типа имеют различные хэш-коды, с другой стороны, принято соглашение “все равные по значению объекты одного типа имеют одинаковые хэш-коды”. При создании классов также рекомендуется переопределять методы hashCode() и toString(), чтобы адаптировать их действия для создаваемого типа.
/* пример # 9 : переопределение методов equals() и
toString() : Point.java */
class Point {
protected byte b;
protected String str;
public Point(byte n, String s) {
b = n;
str = s;
}
public Point() {
this((byte)0, "NoName");
}
public boolean equals(Object obj) {
if (obj instanceof Point)
return (this.b == ((Point) obj).b) &&
(str.equals(((Point) obj).str));
return false;
}
public String toString() {
return getClass().getName() + "@"
+ " name=" + str + " b=" + b;
}
}
class PointZ extends Point{
short s = 100;
}
Метод equals() переопределяется для класса Point таким образом, чтобы убедиться в том, что полученный объект является объектом типа Point или одним из его наследников, а также сравнить содержимое полей b и str, соответственно у вызывающего и передаваемого объектов. Метод toString() переопределен таким образом, что кроме стандартной информации о пакете, в котором находится класс Point, и самого имени класса, выводит значения полей объекта, вызвавшего этот метод, вместо хэш-кода, как это делается в классе Object. Следует обратить внимание на вызов одного конструктора из другого с передачей ему параметров, а также на преобразование значения типа int к типу byte, так как данное преобразование не выполняется по умолчанию из-за возможной потери информации.
/* пример # 10 : иллюстрация работы методов equals()
и toSt ing() r : PointDemo.java */
public class PointDemo {
public static void ain(Stri args) { m ng[]
Point p1 = new Point((byte) 1, "Петров");
Point p2 = new Point((byte) 1, "Петров");
PointZ p3 = new PointZ();
Point p4 = new Point();
System.out.println(p1.equals(p2));
System.out.println(p1.equals(p3));
System.out.println(p4.equals(p3));
System.out.println(p3.equals(p4));
System.out.println(p1.toString());
}
}
В результате выполнения данного кода будет выведено следующее:
true
false
true
true
com.mypack.Point@ name=Петров b=1
Переопределенный таким образом метод equals() позволяет сравнивать объекты суперкласса с объектами подклассов, но только по тем полям, которые являются общими.
СТРОКИ
Системная библиотека Java содержит классы String и StringBuffer, поддерживающие работу со строками и определенные в пакете java.lang. Эти классы объявлены как final, что означает невозможность создания собственных порожденных классов со свойствами строки.
Класс String
Особенностью объекта класса String является то, что его значение не может быть изменено после создания объекта при помощи какого-либо метода, так как любое изменение приводит к созданию нового объекта. При этом ссылку на объект класса String можно изменить так, чтобы она указывала на другой объект. Класс String поддерживает несколько конструкторов, например:
String(), String(String str), String(byte asciichar[]),
String(char[] c), String(StringBuffer sbuf) и др.
Эти конструкторы используются для инициализации объектов класса. Например, при вызове конструктора 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
2301506
2301506
2301506
Несмотря на то, что одинаковые по значению объекты расположены в различных участках памяти, значения их хэш-кодов совпадают. В 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 литерала (канонического представления строки) и при положительном результате возвращает ссылку на найденный литерал, а при отрицательном – заносит значение в пул и возвращает ссылку на него. В следующем примере рассмотрена сортировка массива строк методом выбора.
// пример # 5 : сортировка : SortArray.java
public class r ay { So tArr
public static void main(String[] args) {
String a[] = {" Vika", "Natasha ", " Alina",
" Dim a ", "Denis "};
for(int j = 0; j < a.length; j++) a[j] = a[j].trim();
for int = 0; j < a.length; j++) ( j
for int i = j + 1; i < a.length; i++) (
if(a[i].compareTo(a[j]) < 0) {
String t = a[j];
a[j] = a[i];
a[i] = t;
}
int i = -1;
while(++i < a.length) System.out.print(a[i] + " ");
}
}
Вызов метода trim() обеспечивает удаление всех начальных и конечных символов пробелов. Метод compareTo() выполняет лексикографическое сравнение строк между собой по правилам Unicode.