Символы (тип char)
Для хранения символов Java использует специальный тип char. В Java для char используется кодировка Unicode и для хранения Unicode-символов используется 16 бит или 2 байта. Диапазон допустимых значений - от 0 до 65536 (отрицательных значений не существует).
char ch1, ch2, ch3;
ch1 = 67; // код переменной
ch2 = 'a'; // сам символ
ch3 = 116; // код переменной
System.out.println(ch1 + ch2 + ch3); //Cat
Из примера выше видно, что переменной можно присвоить код символа или непосредственно сам символ, который следует окружить одинарными кавычками. Попробуйте запустить пример и посмотреть, какое слово получится из трёх указанных символов.
Не следует путать символ 'a' со строкой "a", состоящей из одного символа. На экране монитора они выглядят одинаково, но в программах ведут себя по-разному.
Стандартные символы ASCII можно выводить сразу. Если нужно вывести специальный символ из Unicode, то можно воспользоваться шестнадцатеричным представлением кода в escape-последовательности - вы указываете обратную наклонную черту и четыре цифры после u. Например:
char myChar = '\u0054';
Хотя тип char используется для хранения Unicode-символов, его можно использовать как целочисленный тип, используя сложение или вычитание.
char ch1;
ch1 = 'x';
System.out.println("ch1 содержит " + ch1);
ch1++; // увеличим на единицу
System.out.println("ch1 содержит " + ch1);
В результате получим:
ch1 содержит x
ch1 содержит y
Чтобы узнать, какой символ содержится в значении переменной, заданной как int, можно воспользоваться обычным явным приведением типов:
int a = 65;
char c = (char)a;
System.out.println(c);
Для считывания символа с консоли воспользуемся хитростью и одним из методов строк, который описан немного впереди. Сначала считаем строку (либо с помощью next(), либо с помощью nextLine()), а потом достанем с помощью функции charAt() первый символ из нашей строки:
Scanner sc = new Scanner(System.in);
char c = sc.next().charAt(0);
Ссылочные типы данных
Мы уже долгое время работаем с примитивными типами данных. Настало время двигаться дальше. Чем же ссылочные типы данных отличаются от примитивных? Давайте рассмотрим пример:
class Example{
public static void main(String[] args){
String a = "Hello";
String b = new String("Hello");
System.out.println( a == b ? "Равны": "Не равны");
}
}
Очевидно, что и в переменной а, и в переменной b лежит одна и та же строка. Но почему тогда при сравнении получаем false? Ответ простой. Если вы сравниваете ссылочные типы данных, то сравниваются не их значения, а адрес в памяти компьютера. Предполагается, что ссылочные типы данных могут содержать очень много информации, поэтому хранение информации прямо в переменной сочли неразумным. В ней теперь лежит не само значение, а только ссылка на область в памяти компьютера, где лежит это значение. Если тяжело это запомнить, то просто каждый раз вспоминайте Джека Воробья:
Мои извинения, Капитан Джек Воробей! Вспомним наш пример с коробочками: переменную можно сравнить с коробочкой, в которой лежит значение, но что если наше значение - это не просто число или символ, а товарный вагон с информацией? Очевидно, что вагон в коробочку не положить. В таком случаем мы можем просто оставить в коробочке бумажку с адресом, где находится этот вагон. Вот мы и подошли к ответу на изначально поставленный вопрос. Когда мы сравниваем строковые переменные, в них лежат не сами строки, а ссылки на место в памяти компьютера, где находятся строки. Поэтому и сравниваются именно сами адреса. Если они не совпадают, то несмотря на то, что сами строки могут совпадать, то при сравнении мы получим false.
class Example{
public static void main(String[] args){
int[] a = new int[]{1, 2, 3};
int[] b = new int[]{1, 2, 3};
int[] c = a;
System.out.println("Ссылка на а: " + a);
System.out.println("Ссылка на b: " + b);
System.out.println("Ссылка на c: " + c);
System.out.println("Массивы a и b ссылаются на " + (a == b ? "один объект": "разные объекты"));
System.out.println("Массивы a и с ссылаются на " + (a == c ? "один объект": "разные объекты"));
}
}
Пример Вывода:
Ссылка на а: [I@5ebec15
Ссылка на b: [I@21bcffb5
Ссылка на c: [I@5ebec15
Массивы a и b ссылаются на разные объекты
Массивы a и с ссылаются на один объект
Дальше Интересней! Как думаете, что выведет этот код? Попробуйте запустить и объяснить результат.
class Example{
public static void main(String[] args){
int[] a = new int[]{1, 2, 3};
int[] c = a;
c[0] = 5;
for (int i = 0; i < a.length; i++)
System.out.print(a[i] + " ");
}
}
Основы
Строка представляет собой последовательность символов (char). Для работы со строками в Java определен класс String, который предоставляет ряд методов для манипуляции строками. Физически объект String представляет собой ссылку на область в памяти, в которой размещены символы.
Для создания новой строки мы можем использовать один из конструкторов класса String, либо напрямую присвоить строку в двойных кавычках:
String str1 = "Java";
String str2 = new String(); // пустая строка
String str3 = new String(new char[] {'h', 'e', 'l', 'l', 'o'}); //hello
//Для следующей строки 3 -начальный индекс, 4 -кол-во символов
String str4 = new String(new char[]{'w', 'e', 'l', 'c', 'o', 'm', 'e'}, 3, 4); //come
При работе со строками важно понимать, что объект String является неизменяемым (immutable). То есть при любых операциях над строкой, которые изменяют эту строку, фактически будет создаваться новая строка.
Переменная String может не указывать на какой-либо объект и иметь значение null:
String s = null; // строка не указывает на объект
if(s == null)
System.out.println("String is null");
Значение null не эквивалентно пустой строке. Например, в следующем случае мы столкнемся с ошибкой выполнения:
String s = null; // строка не указывает на объект
if(s.length() == 0)
System.out.println("String is empty"); // ! Ошибка
Так как переменная не указывает ни на какой объект String, то соответственно мы не можем обращаться к методам объекта String. Чтобы избежать подобных ошибок, можно предварительно проверять строку на null: Значение null не эквивалентно пустой строке. Например, в следующем случае мы столкнемся с ошибкой выполнения:
String s = null; // строка не указывает на объект
if(s!=null && s.length()==0)
System.out.println("String is empty");
Обратите внимание, что для считывания строк необходимо пользоваться командами sc.nextLine() и sc.next() для считывания всей строки и только одного слова соответственно.
Основные методы строк
Метод
Описание
s.concat() объединяет строки
s.length() возвращает длину строки
s.valueOf() преобразует объект в строковый вид
s.join() соединяет строки с учетом разделителя
s.сompare() сравнивает две строки
s.charAt() возвращает символ строки по индексу
s.getChars() возвращает группу символов
s.equals() сравнивает строки с учетом регистра
s.equalsIgnoreCase() сравнивает строки без учета регистра
s.regionMatches() сравнивает подстроки в строках
s.indexOf() находит индекс первого вхождения подстроки в строку
s.isEmpty() возвращает true, если строка пуста, иначе - false
s.lastIndexOf() находит индекс последнего вхождения подстроки в строку
s.startsWith() определяет, начинается ли строка с подстроки
s.endsWith() определяет, заканчивается ли строка на определенную подстроку
s.replace() заменяет в строке одну подстроку на другую
s.trim() удаляет начальные и конечные пробелы
s.substring()возвращает подстроку, начиная с определенного индекса
до конца или до определенного индекса
s.toLowerCase() переводит все символы строки в нижний регистр
s.toUpperCase() переводит все символы строки в верхний регистр
s.toCharArray() преобразовывает строку в массив символов
Подробнее о методах:
Для соединения строк можно использовать операцию сложения ("+"):
String str1 = "Java";
String str2 = "Hello";
String str3 = str1 + " " + str2;
System.out.println(str3); // Hello Java
При этом если в операции сложения строк используется нестроковый объект, например, число, то этот объект преобразуется к строке:
String str3 = "Год " + 2015;
Фактически же при сложении строк с нестроковыми объектами будет вызываться метод valueOf() класса String. Данный метод преобразует практически все типы данных к строке. Метод concat() принимает строку, с которой надо объединить вызывающую строку, и возвращает соединенную строку:
String str1 = "Java";
String str2 = "Hello";
str2 = str2.concat(str1); // HelloJava
Поскольку строка рассматривается как набор символов, то мы можем применить метод length() для нахождения длины строки или длины набора символов:
String str1 = "Java";
System.out.println(str1.length()); // 4
Еще один метод объединения - метод join() позволяет объединить строки с учетом разделителя. Например, выше две строки сливались в одно слово "HelloJava", но в идеале мы бы хотели, чтобы две подстроки были разделены. И для этого используем метод join():
String str1 = "Java";
String str2 = "Hello";
String str3 = "Legasoft";
String str = String.join(" - ", str2, str1, str3); // Hello - Java - Legasoft
Первым параметром идет разделитель, которым будут разделяться подстроки в общей строке, а все последующие параметры передают через запятую произвольный набор объединяемых подстрок - в данном случае три строки, хотя их может быть и больше. Так же можно объединять и массивы строк.
String[] m = {"123", "456", "789"};
String ans = String.join(" ", m);
System.out.print(ans);
А с помощью метода toCharArray() можно обратно преобразовать строку в массив символов:
String str = new String(new char[] {'h', 'e', 'l', 'l', 'o'});
char[] helloArray = str.toCharArray();
Извлечение символов и подстрок
Для извлечения символов по индексу в классе String определен метод charcharAt(int index). Он принимает индекс, по которому надо получить символов, и возвращает извлеченный символ:
String str = "Java";
char c = str.charAt(2);
System.out.println(c); // v
Как и в массивах, индексация начинается с нуля.
Если надо извлечь сразу группу символов или подстроку, то можно использовать метод getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin). Он принимает следующие параметры:
· srcBegin: индекс в строке, с которого начинается извлечение символов
· srcEnd: индекс в строке, до которого идет извлечение символов
· dst: массив символов, в который будут извлекаться символы
· dstBegin: индекс в массиве dst, с которого надо добавлять извлеченные из строки символы
String str = "Hello world!";
int start = 6;
int end = 11;
char[] dst=new char[end - start];
str.getChars(start, end, dst, 0);
System.out.println(dst); // world
Сравнение строк
Для сравнения строк используются методы equals() (с учетом регистра) и equalsIgnoreCase() (без учета регистра). Оба метода в качестве параметра принимают строку, с которой надо сравнить:
String str1 = "Hello";
String str2 = "hello";
System.out.println(str1.equals(str2)); // false
System.out.println(str1.equalsIgnoreCase(str2)); // true
Еще один специальный метод regionMatches() сравнивает отдельные подстроки в рамках двух строк. Он имеет следующие формы:
1
2
boolean regionMatches(int toffset, String other, int oofset, int len)
boolean regionMatches(boolean ignoreCase, int toffset, String other, int oofset, int len)
Метод принимает следующие параметры:
· ignoreCase: надо ли игнорировать регистр символов при сравнении. Если значение true, регистр игнорируется
· toffset: начальный индекс в вызывающей строке, с которого начнется сравнение
· other: строка, с которой сравнивается вызывающая
· oofset: начальный индекс в сравниваемой строке, с которого начнется сравнение
· len: количество сравниваемых символов в обеих строках
Используем метод:
String str1 = "Hello world";
String str2 = "I work";
boolean result = str1.regionMatches(6, str2, 2, 3);
System.out.println(result); // true
В данном случае метод сравнивает 3 символа с 6-го индекса первой строки ("wor") и 3 символа со 2-го индекса второй строки ("wor"). Так как эти подстроки одинаковы, то возвращается true.
И еще одна пара методов int compareTo(String str) и int compareToIgnoreCase(String str) также позволяют сравнить две строки, но при этом они также позволяют узнать, больше ли одна строка, чем другая или нет. Если возвращаемое значение больше 0, то первая строка больше второй, если меньше нуля, то, наоборот, вторая больше первой. Если строки равны, то возвращается 0.
Для определения больше или меньше одна строка, чем другая, используется лексикографический порядок. То есть, например, строка "A" меньше, чем строка "B", так как символ 'A' в алфавите стоит перед символом 'B'. Если первые символы строк равны, то в расчет берутся следующие символы. Например:
String str1 = "hello";
String str2 = "world";
String str3 = "hell";
System.out.println(str1.compareTo(str2)); // -15 - str1 меньше чем strt2
System.out.println(str1.compareTo(str3)); // 1 - str1 больше чем str3
Поиск в строке
Метод indexOf() находит индекс первого вхождения подстроки в строку, а метод lastIndexOf() - индекс последнего вхождения. Если подстрока не будет найдена, то оба метода возвращают -1:
String str = "Hello world";
int index1 = str.indexOf('l'); // 2
int index2 = str.indexOf("wo"); //6
int index3 = str.lastIndexOf('l'); //9
Метод startsWith() позволяет определить начинается ли строка с определенной подстроки, а метод endsWith() позволяет определить заканчивается строка на определенную подстроку:
String str = "myfile.exe";
boolean start = str.startsWith("my"); //true
boolean end = str.endsWith("exe"); //true
Замена в строке
Метод replace() позволяет заменить в строке одну последовательность символов на другую:
String str = "Hello world";
String replStr1 = str.replace('l', 'd'); // Heddo wordd
String replStr2 = str.replace("Hello", "Bye"); // Bye world
Обрезка строки
Метод trim() позволяет удалить начальные и конечные пробелы:
String str = " hello world ";
str = str.trim(); // hello world
Метод substring() возвращает подстроку, начиная с определенного индекса до конца или до определенного индекса:
String str = "Hello world";
String substr1 = str.substring(6); // world
String substr2 = str.substring(3,5); //lo
Изменение регистра
Метод toLowerCase() переводит все символы строки в нижний регистр, а метод toUpperCase() - в верхний:
String str = "Hello World";
System.out.println(str.toLowerCase()); // hello world
System.out.println(str.toUpperCase()); // HELLO WORLD
Split
Метод split() позволяет разбить строку на подстроки по определенному разделителю. Разделитель - какой-нибудь символ или набор символов - передается в качестве параметра в метод. Например, разобьем текст на отдельные слова:
String text = "FIFA will never regret it";
String[] words = text.split(" ");
for(String word : words){
System.out.println(word);
}
Проблема со Scanner и её решение
И так, в предыдущих задачах, вы могли столкнуться с проблемой считывания строки после числа. А именно, допустим, нам нужно считать следующие значения, как число и строку соответственно:
5
Привет
Мы, по обыкновению пишем код:
Scanner sc = new Scanner(System.in);
int a = sc.nextInt();
String s = sc.next(); //или даже nextLine(), без разницы
И видим, что в строку S явно не попало наше слово "Привет". С чем это связано и как это решать. Всё связано с символом перевода строки. Для нас написаны две строки: "5" и "Привет", но компьютер воспринимает их иначе. Он видит одну строку: "5\nПривет". Всё дело в том, что символ "\n" - это символ перехода на новую строку для компьютера, поэтому, когда мы считываем число 5 с помощью команды nextInt() мы не считываем этот самый символ для перехода на новую строку. А вот потом, как раз, считываем его единственного с помощью команды next() (nextLine()). Поэтому, чтобы всё работало корректно, надо сначала просто считать его, а потом уже считать нашу строку. И Получим следующее:
Scanner sc = new Scanner(System.in);
int a = sc.nextInt();
sc.next();
String s = sc.next(); //или даже nextLine(), без разницы
Свои вопросы можете задать в телеграмм-канале
Курс по Java - разработке бесплатно(8 урок)
Курс по Java - разработке бесплатно(10 урок)