Ссылочные типы. Массивы. Строки и классы-оболочки

1 Теоретическая часть

1.1 Ссылки

В Java нет типов указателей. Имена переменных непримитивных типов по сути являются именами ссылок на соответствующие объекты. Все непримитивные типы имеют название ссылочных типов. Разыменование не требуется: обращение к примитивным типам всегда осуществляется по значению, а к непримитивным – по ссылке. Ссылочные типы никогда не могут быть приведены к примитивным и наоборот.

Специальное ключевое слово null используется для указания того, что переменная ссылочного типа не ссылается ни на что. Константа null может быть присвоена переменной любого ссылочного типа.

Объекты, на которые указывают ссылки, должны быть размещены в динамической памяти с помощью операции new:

SomeType st = new SomeType();

Присваивание значения одной ссылки другой не обеспечивает копирования объектов. После присваивания две ссылки ссылаются на один объект.

SomeType a = new SomeType();
SomeType b = new SomeType();
a = b;

Объект, на который ссылалась ссылка a, потерян.

В отличие от Паскаля, C, С++, освобождение памяти от ненужных объектов не требуется. В Java нет операции удаления объекта из памяти. Для освобождения памяти используется специальный механизм, именуемый сборкой мусора. Этот механизм базируется на подсчете ссылок на объекты. Каждый объект имеет свой счетчик ссылок. Когда ссылка копируется в новую переменную ссылочного типа, счетчик увеличивается на единицу. Когда ссылка выходит из области видимости, или перестает указывать на данный объект, счетчик уменьшается на единицу.

Когда виртуальной машине Java не хватает оперативной памяти, запускается сборщик мусора. Он просматривает список объектов и удаляет из памяти все объекты, для которых количество ссылок равно 0.

Операция == применительно к переменным типа ссылок осуществляет сравнение адресов, а не содержания объектов.

Аргументы функций типа ссылки передаются в функции по ссылке. Внутри функции создается новая ссылка на тот же объект. Этот объект можно изменить в функции и после возврата из функции использовать его значение.

1.2 Массивы

1.2.1 Описание массивов

Массив представляет собой набор ячеек хранения данных, каждая из которых имеет тот же тип. Отдельная ячейка хранения данных называется элементом массива. В отличие от других составных типов данных, массивы всегда располагаются в цельном блоке памяти. Поскольку все элементы занимают одинаковые по размеру ячейки памяти, адрес конкретного элемента всегда может быть вычислен по его номеру. Номера элементов именуются индексами. Обращение к конкретным элементам осуществляют через индекс.

Массивы в Java индексируются начиная с нуля. При описании массива квадратные скобки ставятся после имени типа, а не имени переменной. Размещение квадратных скобок после имени переменной допускается, но не рекомендуется.

int[] a;

В этом примере описывается ссылка на массив, который будет создан позже. Это позволяет определить массив необходимого размера при выполнении программы.

int[] numbers;         // ссылка на массив целых чисел произвольной длины
numbers = new int[10]; // numbers состоит из 10 элементов
numbers = new int[20]; // numbers состоит из 20 элементов

В Java поддерживаются одно- и многомерные массивы. В Java многомерные массивы – это массивы массивов. Например, двумерный массив – это массив ссылок на строки.

Так можно определить ссылку на двумерный массив:

byte[][] scores;

Следующие примеры показывают, как создать различные массивы:

int[] numbers = new int[5];          // одномерный массив
String[][] names = new String[5][4]; // прямоугольный массив
byte[][] scores = new byte[5][];     // двумерный массив с различной длиной строк
for (int k = 0; k < 5; k++) {
    scores[k] = new byte[k + 4];
}

Последний пример показывает, как можно создать двумерный массив с различной длиной строк. Использование имени двумерного массива с одним индексом (например, scores[k]) позволяет получить ссылку на указанную строку массива.

Размер массива может быть определен как с помощью констант, так и с помощью переменных и выражений, дающих целый результат.

В Java массивы содержат элементы вместе с их количеством. Количество элементов массива всегда можно получить с помощью специального поля length. Это поле доступно только для чтения:

int[] a = new int[10];
System.out.println(a.length); // 10

В Java предусмотрен простой способ инициализации массива списком начальных значений:

int[] numbers = new int[] {1, 2, 3, 4, 5};

Можно опустить операцию new:

int[] numbers = {1, 2, 3, 4, 5};

Аналогично инициализируются многомерные массивы:

int[][] c = {{1, 2, 3},
             {0, 0, 1},
             {1, 1, 11},
             {0, 0, 0}};

В приведенном примере создается двумерный массив из четырех строк и трех столбцов.

Так выглядит типичный цикл для обхода массива (например, для занесения значения 0 во все элементы):

for (int i = 0; i < a.length; i++) {
    a[i] = 0;
}

Аналогично обход всех элементов двумерного массива обычно осуществляется так:

for (int i = 0; i < c.length; i++) {
    for (int j = 0; j < c[i].length; j++) {
        c[i][j] = 0;
    }
}

Как видно из последнего примера, для получения размера i-й строки двумерного массива используется конструкция c[i].length.

Начиная с версии JDK 1.5 (Java 5) синтаксис цикла for расширен для более удобной работы с массивами: добавлен так называемый цикл "for-each". При этом элементы могут быть использованы только для чтения. Старая форма:

int[] a = {1, 2, 4, 8};
int sum = 0;
for (int i = 0; i < a.length; i++) {
    sum += a[i];
}
System.out.println(sum);

Новая "for-each" форма (здесь n – текущий элемент):

int[] a = {1, 2, 4, 8};
int sum = 0;
for (int n : a) {
    sum += n;
}
System.out.println(sum);

Массивы читают с клавиатуры поэлементно. Следующий пример демонстрирует чтение количества и значений элементов с клавиатуры.

public class ArrayTest {
    public static void main(String[] args) {
        System.out.println("Введите количество элементов массива:");
        java.util.Scanner s = new java.util.Scanner(System.in);
        int size = s.nextInt();
        double[] a = new double[size];
        System.out.println("Введите элементы массива:");
        for (int i = 0; i < a.length; i++) {
            a[i] = s.nextDouble();
        }
        // Работа с массивом
        // ...
    }

}

Присваивание имени массива другому имени массива приводит только к копированию ссылки, но не самого массива. Для копирования элементов массива необходимо организовать цикл или воспользоваться стандартными средствами.

1.2.2 Массивы как параметры и результат функций

Массивы-параметры передаются в функции по ссылке. После возврата из функции элементы могут содержать измененные значения:

package ua.inf.iwanoff.fourth;

public class SwapElements {

    static void swap(int[] a) {
        int z = a[0];
        a[0] = a[1];
        a[1] = z;
    }

    public static void main(String[] args) {
        int[] b = {1, 2};
        swap(b);
        System.out.println(b[0]); // 2
        System.out.println(b[1]); // 1
    }

}

Если требуется одномерный массив, то можно в качестве параметра передать строку двумерного массива. Можно также передавать параметры типа многомерных массивов. Например

package ua.inf.iwanoff.fourth;

public class ArraysTest {

    static double sum(double[] arr1D) {
        double s = 0;
        for (double elem : arr1D) {
            s += elem;
        }
        return s;
    }

    static double sum(double[][] arr2D) {
        double s = 0;
        for (double[] line : arr2D) {
            for (double elem : line) {
                s += elem;
            }
        }
        return s;
    }

    public static void main(String[] args) {
        double[][] arr = {{1, 2},
                          {3, 4},
                          {5, 6}};
        System.out.printf("Сумма элементов строки с индексом 1: %f%n", sum(arr[1]));
        System.out.printf("Сумма всех элементов: %f%n", sum(arr));
    }
}

В Java 1.5 появилась дополнительная возможность создания функций с переменным числом параметров определенного типа. Внутри функции такие параметры интерпретируются как массив:

static void printIntegers(int... a) {
    for (int i = 0; i < a.length; i++) {
        System.out.println(a[i]);
    }
}

Вызвать такую функцию можно двумя способами: передавая список аргументов типа элемента массива, или передавая массив целиком:

public static void main(String[] args) {
    printIntegers(1, 2, 3);
    int[] arr = {4, 5};
    printIntegers(arr);
}

Параметры такого вида обязательно должны быть последними в списке.

Функция может возвращать ссылку на массив. Чаще всего такой массив создается внутри функции. Например, создаем массив целых, заполненный единицами:

static int[] arrayOfOnes(int n) {
    int[] arr = new int[n];
    for (int i = 0; i < arr.length; i++) {
        arr[i] = 1;
    }
    return arr;
}

Теперь можно создать массив с помощью нашей функции:

int[] a = arrayOfOnes(6);

Если нужен только один элемент, для него можно не создавать массив с именем и совместить создание массива с его использованием:

System.out.println((arrayOfOnes(2)[0]));

Можно также возвращать ссылки на многомерные массивы.

1.2.3 Стандартные функции для работы с массивами

Класс System предоставляет простейший путь копирования одного массива в другой – использование статического метода arraycopy():

System.arraycopy(a, a_from, b, b_from, size);

Это эквивалентно следующему циклу:

for (int i = a_from, j = b_from; i < size + a_from; i++, j++) {
    b[j] = a[i];
}

Массив, в который осуществляется копирование, должен иметь подходящие размеры. Функция arraycopy() не создает нового массива. Весь массив a можно скопировать в b следующим вызовом:

System.arraycopy(a, 0, b, 0, a.length);

Класс java.util.Arrays предоставляет набор полезных функций для заполнения, поиска, сортировки, преобразования в строку и т. д. Для заполнения массивов можно использовать статические методы класса Arrays, реализованного в пакете java.util. Для всех примитивных типов и типа Object реализованы функции fill() в двух видах:

public static void fill(тип[] a, тип val)
public static void fill(тип[] a, int fromIndex, int toIndex, тип val)

Тут тип означает один из фундаментальных (примитивных) типов или тип Object. Первый вариант используется для заполнения всего массива, второй – части, причем элемент с номером toIndex не включается в последовательность. Например:

package ua.inf.iwanoff.fourth;

public class FillArray {

    public static void main(String[] args) {
        int[] a = new int[6];
        java.util.Arrays.fill(a, 0, 4, 12); // Другие элементы равны 0
        for (int x : a) {
            System.out.print(x + " ");
        }
        System.out.println();
        java.util.Arrays.fill(a, 100);      // Все элементы равны 100
        for (int x : a) {
            System.out.print(x + " ");
        }
        System.out.println();
    }

}

В предыдущем примере для вывода элементов массива на экран был использован цикл. Альтернативный способ – использование функции toString() класса Arrays. Эта функция возвращает строковое представление массива, удобное для большинства применений:

java.util.Arrays.fill(a, 100);
System.out.println(Arrays.toString(a)) // [100, 100, 100, 100, 100, 100];

Примечание: для вывода в строку многомерных массивов следует использовать функцию deepToString().

Класс Arrays предоставляет альтернативный путь копирования массивов. Функция copyOf() создает новый массив копий элементов. Первый параметр – исходный массив, второй параметр – длина результирующего массива. Не поместившиеся элементы обрезаются, недостающие заполняются нулями. Функция copyOfRange(тип[] a, int from, int to) копирует в новый массив часть массива, включая начало интервала и не включая конца интервала:

package ua.inf.iwanoff.fourth;

import java.util.Arrays;

public class CopyOfTest {

    public static void main(String[] args) {
        int[] a = { 1, 2, 3, 4 };
        int[] b = Arrays.copyOf(a, 3); 
        System.out.println(Arrays.toString(b));// [1, 2, 3]
        int[] c = Arrays.copyOf(a, 6); 
        System.out.println(Arrays.toString(c));// [1, 2, 3, 4, 0, 0]
        int[] d = Arrays.copyOfRange(a, 1, 3);
        System.out.println(Arrays.toString(d));// [2, 3]
    }

}

Сравнить два массива или часть их можно с помощью функций группы equals():

public static boolean equals(тип[] a, тип[] a2)

Массивы сравниваются поэлементно. Два массива также считаются эквивалентными, если обе ссылки – null. Например:

package ua.inf.iwanoff.fourth;

import java.util.Arrays;

public class ArraysComparison {

    public static void main(String[] args) {
        double[] a = null, b = null;
        System.out.println(Arrays.equals(a, b)); // true
        a = new double[] { 1, 2, 3, 4 };
        b = new double[4];
        System.out.println(Arrays.equals(a, b)); // false
        System.arraycopy(a, 0, b, 0, a.length);
        System.out.println(Arrays.equals(a, b)); // true
        b[3] = 4.5;
        System.out.println(Arrays.equals(a, b)); // false
    }

}

Имеется также метод deepEquals(), использование которого аналогично. Различие проявляется при использовании многомерных массивов. Осуществляется более "глубокая" проверка:

int[][] a1 = { { 1, 2 } , { 3, 4 } };
int[][] a2 = { { 1, 2 } , { 3, 4 } };
System.out.println(Arrays.equals(a1, a2));    // false
System.out.println(Arrays.deepEquals(a1, a2));// true    

Функция sort() обеспечивает сортировку массива вещественных чисел по возрастанию. Например:

package ua.inf.iwanoff.fourth;

import java.util.Arrays;

public class ArraySort {

    public static void main(String[] args) {
        int[] a = new int[] { 11, 2, 10, 1 };
        Arrays.sort(a);            // 1 2 10 11
        for (int x : a) {
            System.out.print(x + " ");
        }
        System.out.println();
    }

}

Функция sort() реализована для массивов всех примитивных типов и строк. Можно сортировать часть массива. Как и для функции fill(), указывается начальный и конечный индексы последовательности, которую следует отсортировать. Конечный индекс не включается в последовательность. Например:

int[] a = {7, 8, 3, 4, -10, 0};
java.util.Arrays.sort(a, 1, 4); // 7 3 4 8 -10 0

В отсортированных массивах можно выполнить поиск с помощью методов класса Arrays. Группа функций binarySearch(), реализованная для всех примитивных типов и типа Object, возвращает индекс найденного элемента или отрицательное значение, если элемент отсутствует.

public static int binarySearch(тип[] a, тип key)

1.3 Использование стандартных классов

Класс – это структурированный тип данных, набор элементов данных различных типов и функций для работы с этими данными. Ранее уже использовались статические средства стандартных классов System (поля-потоки in и out), Math (стандартные математические функции, реализованные в виде статических методов) и Arrays. Кроме того, создавался объект класса java.util.Scanner с вызовом его функций, а также объект класса java.util.Random для получения псевдослучайных чисел.

Практически ни одна программа на Java не может обойтись без объектов класса String: функция main() описывается с параметром типа массива строк, данные читаются из потоков в строки и записываются из строк в потоки, строки используются для представления данных в визуальных компонентах графического интерфейса пользователя и т. д. Для модификации содержимого строк используются стандартные классы StringBuffer и StringBuilder. Для разделения строки на лексемы используется класс StringTokenizer.

Классы-оболочки Integer, Double, Boolean, Character, Float, Byte, Short и Long используются для хранения данных примитивных типов в объектах, доступных через ссылки. Кроме того, эти классы предоставляют ряд полезных методов для преобразования данных.

1.4 Работа со случайными величинами

Иногда при тестировании возникает необходимость в заполнении массивов случайными (псевдослучайными) значениями. Это можно осуществить с помощью функции random() класса Math и с помощью специального класса java.util.Random. Первый вариант дает псевдослучайное число в диапазоне от 0 до 1. Например:

package ua.inf.iwanoff.fourth;

import java.util.Arrays;

public class MathRandomTest {

    public static void main(String[] args) {
        double[] a = new double[5];
        for (int i = 0; i < a.length; i++) {
            a[i] = Math.random() * 10; // случайные значения от 0 до 10
        }
        System.out.println(Arrays.toString(a));
    }

}

Использование класса Random позволяет получить более разнообразные результаты. Конструктор класса Random без параметров инициализирует датчик псевдослучайных чисел так, что последовательности случайных значений практически не повторяются. Если для отладки нам необходимо каждый раз получать одни и те же случайные значения, следует воспользоваться конструктором с целым параметром. Параметром может быть любое целое, которое инициализирует собой датчик случайных чисел.

В следующей таблице представлены функции класса java.util.Random, позволяющие получить различные псевдослучайные значения.

Функция
Описание
nextBoolean() возвращает следующее равномерно распределенное значение типа boolean
nextDouble() возвращает следующее значение типа double, равномерно распределенное на интервале от 0 до 1
nextFloat() возвращает следующее значение типа float, равномерно распределенное на интервале от 0 до 1
nextInt() возвращает следующее равномерно распределенное значение типа int
nextInt(int n) возвращает следующее значение типа int , равномерно распределенное от 0 до n (не включая n)
nextLong() возвращает следующее равномерно распределенное значение типа long
nextBytes(byte[] bytes) заполняет массив целых типа byte случайными значениями
nextGaussian() возвращает следующее значение типа double, распределенное на интервале от 0 до 1 по нормальному закону

В следующем примере мы получаем псевдослучайные целые значения в диапазоне от 0 (включительно) до 10:

package ua.inf.iwanoff.fourth;

import java.util.*;

public class UtilRandomTest {

    public static void main(String[] args) {
        int[] a = new int[15];
        Random rand = new Random(100);
        for (int i = 0; i < a.length; i++) {
            a[i] = rand.nextInt(10); // случайные значения от 0 до 10
        }
        System.out.println(Arrays.toString(a));
    }

}

Каждый раз при запуске программы мы будем получать одни и те же псевдослучайные значения. Если мы хотим, чтобы значения были действительно случайные, нужно воспользоваться конструктором Random() без параметров.

1.5 Строки

1.5.1 Использование класса String

Строки в Java – это экземпляры класса java.lang.String. Объекты этого класса содержат символы Unicode.

Объект-строка может быть создан при описании ссылки путем присваивания ей строкового литерала:

String s = "Первая строка";

Строку можно также создать с помощью различных конструкторов. Класс String в Java предоставляет 15 конструкторов, которые позволяют определить начальное значение строки. Например, можно получить строку из массива символов:

char[] chars = { 'Т', 'е', 'к', 'с', 'т'};
String s1 = new String(chars); // Текст

Можно создать массив байтов и получить из него строку. При этом преобразование байтов в символы осуществляется согласно таблице кодирования, принятой в системе по умолчанию.

byte[] bytes = { 49, 50, 51, 52 };
String s2 = new String(bytes);
System.out.println(s2); // 1234

Можно создать строку с другой строки. Следует отличать создание новой ссылки от создания новой строки:

String s = "текст";
String s1 = s;             // s и s1 ссылаются на одну строку 
String s2 = new String(s); // s2 ссылается на новую строку - копию s

Если один из операндов – строка, а другой – нет, то этот операнд приводится к стрковому представлению. Альтернативный способ преобразования числовых данных в строки – применение статических функций ValueOf(). Соответствующие функции реализованы для аргументов числовых типов, а также типов Object, char, boolean и массивов символов. Например:

double d = 1.1;
String sd = String.valueOf(d); // "1.1"

Ниже приведены некоторые наиболее часто используемые методы класса String:

Метод

Аргументы

Возвращает

Описание

length

()

int

Возвращает количество символов в строке.

concat (String str) String Добавляет указанную строку в конец текущей строки
contains (String substring) boolean Возвращает true, если подстроки входит в текущую строку

charAt

(int index)

char

Возвращает символ, соответствующий заданному индексу.

compareTo

(String value)

int

Сравнивает текущую строку с аргументом-строкой. Результат отрицателен, если текущая строка лексикографически (по алфавиту) предшествует строке-аргументу. Результат равен 0, если строки совпадают и положителен в противном случае.

compareToIgnoreCase (String str) int Работает как compareTo(), но игнорирует регистр
equals (String value) boolean Возвращает true, если строки совпадают посимвольно.
equalsIgnoreCase (String str) boolean Сравнивает текущую строку с аргументом-строкой с игнорированием регистров. Возвращает true, если строки совпадают
indexOf (int ch) int Возвращает индекс, соответствующий первому вхождению символа в строку. Если символ не входит в строку, возвращает -1

indexOf

(String substring)

int

Возвращает индекс, соответствующий первому вхождению подстроки в строку. Если подстрока не входит в строку, возвращает -1

lastIndexOf

(char ch)

int Возвращает индекс, соответствующий последнему вхождению символа в строку. Если символ отсутствует, возвращает -1
lastIndexOf

(String substring)

int Возвращает индекс, соответствующий последнему вхождению подстроки в строку. Если подстрока не входит в строку, возвращает -1

substring

(int beginIndex, int endIndex)

String

Возвращает новую строку, являющуюся подстрокой исходной.

toLowerCase

()

String

Возвращает строку, все символы которой переведены в нижний регистр.

toUpperCase

()

String

Возвращает строку, все символы которой переведены в верхний регистр.

regionMatches (int toffset, String other, int ooffset, int len) boolean Проверяет, совпадают две последовательности символов в двух строках
regionMatches (boolean ignoreCase, int toffset, String other, int ooffset, int len) boolean Проверяет, совпадают две последовательности символов в двух строках. Дополнительно можно устанавливать возможность игнорирования регистра при проверке
toCharArray () char[] Преобразует текущую строку в новый массив символов
getChars (int srcBegin, int srcEnd, char[] dst, int dstBegin) void Копирует символы текущей строки в результирующий символьный массив
getBytes () byte[] Возвращает массив байтов, содержащих коды символов с учетом таблицы кодов платформы (операционной системы)
trim

()

String

Возвращает копию строки, в которой начальные и конечные пробелы опущены
startsWith (String prefix) boolean Проверяет, начинается ли строка с указанного префикса
endsWith (String suffix) boolean Проверяет, заканчивается ли строка указанным суффиксом

Дополнительные возможности работы со строками будут изучены при рассмотрении возможностей Java SE.

Следующие примеры демонстрируют использование некоторых методов обработки строк.

String s1 = new String("Hello World.");
int i = s1.length();                 // i = 12
char c = s1.charAt(6);               // c = 'W'
i = s1.indexOf('e');                 // i = 1 (index of 'e' in "Hello World.")
String s2 = "abcdef".substring(2, 5);// s2 = "cde"
int k = "AA".compareTo("AB");        // k = -1
s2 = "abc".toUpperCase();            // s2 = ABC

Одна из наиболее типичных операций со строками – сшивание. Для сшивания двух строк можно применить функцию concat():

String s3 = s1.concat(s2);
s3 = s3.concat("добавляем текст");

Но чаще всего вместо вызова функции concat() применяют операцию +:

String s1 = "first";
String s2 = s1 + " and second";

Если один из операндов – строка, а другой – нет, то этот операнд приводится к строковому представлению.

int n = 1;
String sn = "n равно " + n; // "n равно 1"
double d = 1.1;
String sd = d + ""; // "1.1"

Можно также использовать операцию "+=" для дошивания в конец строки.

Можно создавать массивы строк. Как и для других типов-ссылок, массив хранит не строки непосредственно, а ссылки на них. Функция sort() класса java.util.Arrays реализована также для строк. Строки упорядочиваются по алфавиту:

String[] a = { "dd", "ab", "aaa", "aa" };
java.util.Arrays.sort(a); // aa aaa ab dd

При чтении стоки из потока с использованием класса java.util.Scanner строку можно прочитать с помощью метода next() (до разделителя) или nextLine() (до конца строки). Примеры чтения строк будут приведены ниже.

Экземпляр класса String не может быть изменен после создания. Работа некоторых методов и операций внешне напоминает модификацию объекта, однако на самом деле создается новая строка.

String s = "ab"; // В памяти одна строка
s = s += "c";    // В памяти три строки: "ab", "c" и "abc". На "abc" ссылается s
// Лишние строки затем будут удалены сборщиком мусора

1.5.2 Использование классов StringBuffer и StringBuilder

Существует специальный класс StringBuffer, позволяющий модифицировать содержимое строкового объекта. Создать объект типа StringBuffer можно из существующей строки. После модификации можно создать новый объект класса String, используя объект класса StringBuffer. Например:

String s = "abc";
StringBuffer sb1 = new StringBuffer(s);    // Вызов конструктора
StringBuffer sb2 = new StringBuffer("cd"); // Вызов конструктора
// модификация sb1 и sb2
// ...
String s1 = new String(sb1); // Вызов конструктора
String s2 = sb2 + "";        // Преобразование типов

Кроме некоторых типичных для класса String функций, таких как length(), charAt(), indexOf(), substring(), класс StringBuffer предоставляет ряд методов для модификации содержимого. Это такие методы, как append(), delete(), deleteCharAt(), insert(), replace(), reverse() и setCharAt(). Рассмотрим использование этих функций на следующем примере:

public class StringBufferTest {

    public static void main(String[] args) {
        String s = "abc";
        StringBuffer sb = new StringBuffer(s);
        sb.append("d");         // abcd
        sb.setCharAt(0, 'f');   // fbcd
        sb.delete(1, 3);        // fd
        sb.insert(1, "gh");     // fghd
        sb.replace(2, 3, "mn"); // fgmnd
        sb.reverse();           // dnmgf
        System.out.println(sb);
    }

}

Использование StringBuffer может повысить эффективность работы программы в случаях, когда определенную строку необходимо многократно модифицировать в течение работы программы. Но важно помнить, что несколько ссылок указывают на один объект типа StringBuffer. Поэтому, когда мы его меняем, все ссылки будут указывать на измененную строку.

Начиная с Java 5, вместо StringBuffer можно использовать StringBuilder. В программах, которые не создают отдельных потоков управления, функции этого класса выполняются более эффективно.

1.5.3 Разделение строки на лексемы

Существует несколько способов разделения строки на лексемы. Простейший способ – использование класса java.util.StringTokenizer. Объект этого класса создается с помощью конструктора с параметром типа String, который определяет строку, подлежащую разделению на лексемы:

StringTokenizer st = new StringTokenizer(someString);

После создания объекта можно получить общее количество лексем с помощью метода countTokens(). Класс реализует внутренний "текущий указатель", который указывает на следующее слово. Функция nextToken() возвращает следующую лексему из строки. Функции nextToken() можно задать альтернативный разделитель лексем в качестве параметра. С помощью функции hasMoreTokens() можно проверить, есть ли еще лексемы. В следующем примере все слова строки выводятся в отдельных строках:

package ua.inf.iwanoff.fourth;

import java.util.*;

public class AllWords {

    public static void main(String[] args) {
        String s = new Scanner(System.in).nextLine();
        StringTokenizer st = new StringTokenizer(s);
        while (st.hasMoreTokens()) {
            System.out.println(st.nextToken());
        }
    }

}

Более современный способ разбиения на лексемы – использование метода split() класса String. Параметр данного метода так называемое регулярное выражение, определяющие разделительные символы. Регулярные выражения будут рассмотрены при изучении Java SE. Однако в простейшем случае можно использовать сам разделительный символ – пробел. Например:

String s = "aa bb ccc";
String[] a = s.split(" ");
System.out.println(Arrays.toString(a)); // [aa, bb, ccc]

1.6 Классы Integer, Double, Boolean, Character, Float, Byte, Short и Long

Классы Integer, Double, Boolean, Character, Float, Byte, Short и Long позволяют представить числовые и логические значения в объектах. Эти классы также называются классами-обертками. Дополнительно эти классы предоставляют набор методов для преобразования арифметических значений в строковое представление и наоборот и другие средства для удобной работы с целыми и вещественными числами.

Статический метод Double.parseDouble() возвращает вещественное число по заданному строковому представлению:

String s = "1.2";
double d = Double.parseDouble(s);

Аналогично работают функции Integer.parseInt(), Boolean.parseBoolean(), Long.parseLong(), Float.parseFloat(), Byte.parseByte(), Short.parseShort().

В версии Java 5 (JDK 1.5) объекты типа Integer можно инициализировать выражениями целого типа, использовать в выражениях для получения значений (автоматическая упаковка / распаковка). Целые значения (константы) можно заносить в списки и другие контейнеры. Автоматически будут создаваться и заноситься в контейнер объекты типа Integer. В ранних версиях Java (JDK 1.4 и раньше) необходимо было писать:

Integer m = new Integer(10); // инициализируем объект типа Integer
int k = m.intValue() + 1;    // используем значение в выражении
// создаем массив:  
Integer[] a = {new Integer(1), new Integer(2), new Integer(3)};
a[2] = new Integer(4);       // заносим объект с новым целым значением
// получаем объект типа Integer из массива и используем значение:
int i = a[1].intValue() + 2;

Теперь все проще:

Integer m = 10; // инициализируем объект типа Integer
int k = m + 1;  // используем значение в выражении
// создаем массив:  
Integer[] a = {1, 2, 3};
a[2] = 4;       // заносим объект с новым целым значением
// получаем объект типа Integer из массива и используем значение:
int i = a[1] + 2;

То же самое касается и других типов-оберток. Фактически объекты этих типов могут использоваться вместо переменных сответсвующих примитивных типов. Недостатком использования оберток вместо примитивных типов является уменьшение эффективности за счет добавления операций размещения в динамической памяти. Но преимуществом является возможность использования значения null. Например, если значение функции не может быть вычислено, функция может вернуть null:

package ua.inf.iwanoff.fourth;

import java.util.Scanner;

public class Reciprocal {
    // Обратная величина:
    static Double reciprocal(double x) {
        if (x == 0) {
            return null;
        }
        return 1 / x;
    }
  
    public static void main(String[] args) {
        Scanner s = new Scanner(System.in);
        double x = s.nextDouble();
        Double y = reciprocal(x);
        if (y == null) {
            System.out.println("Ошибка");
        }
        else {
            System.out.println(y);
        }
    }
}

Класс Character – это, в первую очередь, оболочка для хранения значения примитивного типа char (символ) в объекте. Объект типа Character содержит одно поле типа char. Кроме того, этот класс предоставляет несколько методов для определения категории символа (строчные буквы, цифры и т.д.) и для преобразования символов из верхнего регистра в нижний и наоборот.

Есть возможность переводить отдельный символ в верхний (или нижний) регистр:

char c1 = 'a';
char c2 = Character.toUpperCase(c1); // 'A'
char c3 = Character.toLowerCase(c2); // 'a'

Имеются функции, позволяющие проверять свойства символов. Например, метод Character.isLetter() возвращает true, если символ буквенный в украинском, русском, китайском, немецком, арабском или другом языке. Ниже приведены некоторые из наиболее полезных методов сравнения символов:

  • isDigit() возвращает true, если символ является цифрой
  • isLetter() возвращает true, если символ является буквой
  • isLetterOrDigit() возвращает true, если символ является буквой или цифрой
  • isLowerCase() возвращает true, если символ – буквенный в нижнем регистре
  • isUpperCase() возвращает true, если символ – буквенный в верхнем регистре
  • isSpaceChar() возвращает true, если символ является разделителем – символом пробела, новой строки или абзаца.

Дополнительные возможности работы с классом Character будут рассмотрены при изучении возможностей Java SE.

2 Примеры программ

2.1 Сумма элементов массива

Следующая программа находит сумму элементов массива вещественных чисел.

package ua.inf.iwanoff.fourth;

public class SumOfElements {

    public static void main(String[] args) {
        double[] a = {1, 2, 1, 2.5, 1};
        double sum = 0;
        for (int i = 0; i < a.length; i++) {
            sum += a[i];
        }
        System.out.println("Sum is " + sum);
    }

}

Второй вариант той же программы:

package ua.inf.iwanoff.fourth;

public class SumOfElements {

    public static void main(String[] args) {
        double a[] = {1, 2, 1, 2.5, 1};
        double sum = 0;
        for (double x : a) {
            sum += x;
        }
        System.out.println("Sum is " + sum);
    }

}

Третий вариант реализуем с использованием рекурсии:

package ua.inf.iwanoff.fourth;

public class SumWithRecursion {

    static double sum(double[] a, int n) {
        if (n <= 0) {
            return 0;
        }
        n--;
        return a[n] + sum(a, n);
    }

    static double sum(double[] a) {
        return sum(a, a.length);
    }
  
    public static void main(String[] args) {
        double[] a = { 1, 2, 1, 2.5, 1 };
        System.out.println("Sum is " + sum(a));
    }

}

2.2 Индекс максимального элемента

Следующая программа находит номер максимального элемента в массиве целых чисел.

package ua.inf.iwanoff.fourth;

public class MaxElement {

    public static void main(String[] args) {
        int[] a = {1, 2, 14, 8};
        int indexOfMax = 0;
        for (int i = 1; i < a.length; i++) {
            if (a[i] > a[indexOfMax]) {
                indexOfMax = i;
            }
        }
        System.out.println(indexOfMax + " " + a[indexOfMax]);
    }

}

2.3 Сумма произведений строк

В следующей программе определяется сумма произведений строк двумерного массива.

package ua.inf.iwanoff.fourth;

public class SumOfProducts {

    public static void main(String[] args) {
        int[][] a = {{1, 2, 3},
                     {2, 3, 4},
                     {0, 1, 2},
                     {1, 1, 12}};
        int sum = 0;
        for (int i = 0; i < a.length; i++) {
            int product = 1;
            for (int j = 0; j < a[i].length; j++) {
                product *= a[i][j];
            }
            sum += product;
        }
        System.out.println(sum);
    }

}

2.4 Замена элементов

В следующей программе отрицательные элементы двумерного массива заменяются нулями.

package ua.inf.iwanoff.fourth;

public class ReplaceZeros {

    public static void main(String[] args) {
        double[][] a = {{1,  -2,  3},
                        {2.1, 3, -4},
                        {0,-0.5, 11}};
        for (int i = 0; i < a.length; i++) {
            for (int j = 0; j < a[i].length; j++) {
                if (a[i][j] < 0) {
                    a[i][j] = 0;
                }
            }
        }
        for (int i = 0; i < a.length; i++) {
            for (int j = 0; j < a[i].length; j++) {
                System.out.print("\t" + a[i][j]);
            }
            System.out.println();
        }
    }

}

Примечание: для вывода двумерного массива не следует использовать Arrays.toString(), так как вместо элементов мы получим адреса строк. Для вывода всех элементов одной строкой следует использовать

System.out.println(Arrays.deepToString(a));

2.5 Сортировка

Допустим, необходимо рассортировать элементы массива по возрастанию. Следующая программа реализует алгоритм пузырьковой сортировки.

package ua.inf.iwanoff.fourth;

import java.util.Arrays;

public class SortClass {

    public static void main(String[] args) {
        double[] a = { 11, 2.5, 4, 3, 5 };
        boolean mustSort;// Повторяем до тех пор,
                         // пока mustSort равно true
        do {
            mustSort = false;
            for (int i = 0; i < a.length - 1; i++) {
                if (a[i] > a[i + 1]) {
                // Меняем элементы местами:
                    double temp = a[i];
                    a[i] = a[i + 1];
                    a[i + 1] = temp;
                    mustSort = true;
                }
            }
        }
        while (mustSort);
        System.out.println(Arrays.toString(a));
    }

}

Следующая программа использует стандартную функцию sort():

package ua.inf.iwanoff.fourth;

import java.util.Arrays;

public class SortClass {

    public static void main(String[] args) {
        double[] a = { 11, 2.5, 4, 3, 5 };
        Arrays.sort(a);
        System.out.println(Arrays.toString(a));
    }

}

2.6 Вычисление факториала

Допустим, необходимо разработать функцию вычисления факториалов (от 0 до 20! включительно) с использованием вспомогательного массива (статического поля). При первом вызове функции массив заполняется до необходимого числа. При последующих вызовах число либо возвращается из массива, либо вычисляется с использованием последнего числа, хранящегося в массиве, с дальнейшим заполнением массива.

Необходимо также осуществить тестирование функции для различных чисел, вводимых в произвольном порядке. Программа будет иметь следующий вид:

package ua.inf.iwanoff.fourth;

public class Factorial {
    private static long[] f = new long[30];
    private static int last = 0;
  
    public static long factorial(int n) {
        f[0] = 1;
        if (n > last) {
            for (int i = last + 1; i <= n; i++) {
                f[i] = i * f[i - 1];
            }
            last = n;
        }
        return f[n];
    }
  
    public static void main(String[] args) {
        System.out.println(factorial(5));
        System.out.println(factorial(1));
        System.out.println(factorial(3));
        System.out.println(factorial(6));
        System.out.println(factorial(20));
    }

}

2.7 Решение квадратного уравнения

Функция решения квадратного уравнения может возвращать массив из двух элементов – корней, из нуля элементов, если корней нет, из одного элемента, если получилось линейное уравнение и null, если корней бесчисленное множество:

package ua.inf.iwanoff.fourth;

import java.util.Arrays;

public class QuadraticTest {

    public static double[] quadratic(double a, double b, double c) {
        if (a == 0) {
            if (b == 0) {
                if (c == 0) {
                    return null; // бесчисленное множество корней
                }
                else {
                    return new double[0]; // нет корней
                }
            }
            else {
                double[] x = new double[1];
                x[0] = -c / b;
                return x; // один корень
            }
        }
        else {
            double d = b * b - 4 * a * c;
            if (d < 0) {
                return new double[0]; // нет корней
            }
            else {
                double sqrd = Math.sqrt(d);
                double[] x = new double[2];
                x[0] = (-b - sqrd) / (2 * a);
                x[1] = (-b + sqrd) / (2 * a);
                return x; // два корня
            }
        }
    }

    public static void main(String[] args) {
        double[] x = quadratic(1, 1, -2); // два корня
        System.out.println(Arrays.toString(x)); // [-1.0, 2.0]
        x = quadratic(1, 1, 2); // нет корней
        System.out.println(Arrays.toString(x)); // []
        x = quadratic(0, 0, 1); // нет корней
        System.out.println(Arrays.toString(x)); // []
        x = quadratic(0, 0, 0);  // бесчисленное множество корней
        System.out.println(Arrays.toString(x)); // null
        // Можно без именования массива (один корень):
        System.out.println(quadratic(0, 1, -2)[0]);  // [2.0]
    }

}

Теперь по количеству элементов массива или значению null для ссылки на массив можно судить о результате решения уравнения.

2.8 Сложение матриц

Допустим, необходимо разработать функцию для сложения двух матриц с проверкой размерностей и осуществить тестирование этой функции с проверкой возможных ошибок. Программа будет иметь следующий вид:

package ua.inf.iwanoff.fourth;

import java.util.Arrays;

public class SumOfMatrices {

    static double[][] sum(double[][] a, double[][] b) {
        if (a == null || b == null || a.length != b.length) {
            return null;
        }
        double[][] c = new double[a.length][a[0].length];
        for (int i = 0; i < c.length; i++) {
            if (a[i].length != c[i].length || b[i].length != c[i].length) {
                return null;
            }
            for (int j = 0; j < c[i].length; j++) {
                c[i][j] = a[i][j] + b[i][j];
            }
        }
        return c;
    }
  
    static void printC(double[][] c) {
        if (c == null) {
            System.out.println("Неправильные исходные данные!");
        }
        else {
            System.out.println(Arrays.deepToString(c));
        }
    }
  
    public static void main(String[] args) {
        double[][] a = { { 1, 2 }, { 3, 4 } };
        double[][] b = { { 5, 6 }, { 7, 8 } };
        printC(sum(a, b));  // [[6.0, 8.0], [10.0, 12.0]]
        b = new double[][] { { 5, 6 }, { 7 } };
        printC(sum(a, b));  // Неправильные исходные данные!
        b = null;
        printC(sum(a, b));  // Неправильные исходные данные!
    }

}

2.9 Сумма цифр

Допустим, необходимо определить сумму цифр числа, прочитанного из командной строки. Для этого можно предложить вариант с использованием строк.

package ua.inf.iwanoff.fourth;

public class SumOfDigids {
    public static void main(String[] args) {
        String n = args[0];
        int sum = 0;
        for (int i = 0; i < n.length(); i++) {
            sum += Integer.parseInt(n.charAt(i) + "");
        }
        System.out.println(sum);
    }
}

2.10 Удаление лишних пробелов

В следующем примере из строки, прочитанной из первого аргумента, удаляются лишние пробелы (оставляем по одному).

package ua.inf.iwanoff.fourth;

public class SpaceRemover {
    public static void main(String[] args) {
        System.out.println(args[0]);
        String s = args[0];
        while (s.indexOf("  ") >= 0) {
            s = s.replaceAll("  ", " ");
        }
        System.out.println(s); 
    }
}

Если строка содержит несколько слов, ее необходимо брать в кавычки. Если в качестве аргумента задана, например строка

"To    be  or not to                be"

получим строку

To be or not to be

2.11 Сортировка слов строки

В следующем примере из строки, прочитанной с клавиатуры, формируется строка, в которой слова упорядочены по алфавиту.

package ua.inf.iwanoff.fourth;

import java.util.*;

public class SortWords {

    public static void main(String[] args) {
        String s = new Scanner(System.in).nextLine();
        StringTokenizer st = new StringTokenizer(s);
        int count = 0;
        while (st.hasMoreTokens()) {
            st.nextToken();
            count++;
        }
        String[] words = new String[count];
        st = new StringTokenizer(s);
        for (int i = 0; i < words.length; i++) {
            words[i] = st.nextToken();
        }
        Arrays.sort(words);
        String result = "";
        for (String word : words) {
            result += word + " ";
        }
        System.out.println(result);
    }

}

3 Задания на самостоятельную работу

3.1 Сумма положительных элементов*

Проинициализировать одномерный массив вещественных чисел списком начальных значений. Найти сумму положительных элементов. Реализовать два варианта – с циклом и с рекурсией.

3.2 Суммы элементов

В функции main() проинициализировать одномерный массив целых чисел случайными значениями. Создать функцию, которая принимает в качестве параметра массив и выводит на экран сумму элементов массива от первого элемента до элемента с номером k, а также сумму элементов от элемента с номером k+1 до последнего. Функция возвращает true, если первая сумма больше, и false в противном случае. Результат вывести на экран.

3.3 Отрицательные элементы

В функции main() проинициализировать одномерный массив вещественных чисел случайными значениями в диапазоне от -100 до 100. Создать функцию, которая принимает в качестве параметра массив, находит количество отрицательных элементов этого массива, создает новый массив необходимой размерности, заполняет его отрицательными элементами и возвращает новый массив. Результат вывести на экран.

3.4 Упорядочивание элементов*

В функции main() проинициализировать одномерный массив из 10 целых чисел. Создать функцию, которая принимает в качестве параметра массив, упорядочивает первые 4 элемента этого массива по возрастанию, последние 4 – по убыванию. Результат вывести на экран.

3.5 Среднее арифметическое

В функции main() проинициализировать одномерный массив целых чисел. Создать функцию, которая принимает в качестве параметра массив и находит среднее арифметическое всех чётных элементов массива, стоящих на нечётных местах (с нечетными индексами). Результат вывести на экран.

3.6 Сдвиг элементов

В функции main() проинициализировать одномерный массив целых чисел. Создать функцию, которая принимает в качестве параметра массив, находит нулевой элемент массива (элемент с индексом 0), переносит его на последнее место, сдвинув влево все стоящие за ним элементы. Вывести полученный массив на экран.

3.7 Максимальный элемент

В функции main() проинициализировать двумерный массив размерностью m x n вещественных чисел. Создать функцию, которая принимает в качестве параметра массив, находит максимальный по модулю элемент массива и возвращает найденное значение. Результат вывести на экран.

3.8 Количество нулевых элементов

В функции main() проинициализировать двумерный массив размерностью m x n вещественных чисел. Создать функцию, которая принимает в качестве параметра массив, определяет, сколько раз в этом массиве встречается элемент со значением 0 и возвращает это количество. Результат вывести на экран.

3.9 Максимальный из минимальных

В функции main() проинициализировать двумерный массив размерностью m x n целых чисел. Создать функцию, которая принимает в качестве параметра массив, находит максимальный элемент из минимальных элементов строк массива и возвращает его. Результат вывести на экран.

3.10 Эратосфеново решето*

Заполнить массив из трехсот целых чисел последовательными положительными значениями. Заменить все значения, не являющиеся простыми числами, некоторым отрицательным значением. Для этого последовательно исключать все числа – делители других чисел (для 2 – 4, 6, 8..., для 3 – 9, 15, 21... и т.д.). Вывести на экран оставшиеся положительные значения (простые числа).

3.11 Сортировка выбором*

Проинициализировать одномерный массив целых чисел случайными значениями. Осуществить упорядочение массива методом сортировки выбором. Алгоритм заключается в следующем:

  • находим номер минимального значения в текущем списке;
  • производим обмен этого значения со значением первой неотсортированной позиции (обмен не нужен, если минимальный элемент уже находится на данной позиции);
  • сортируем хвост списка, исключив из рассмотрения уже отсортированные элементы.

Результат вывести на экран.

3.12 Сортировка вставками*

В функции main() проинициализировать одномерный массив целых чисел. Создать функцию, которая осуществляет упорядочение массива методом сортировки вставками. Алгоритм заключается в следующем:

  • перебираем все элементы массива по очереди;
  • выбираем очередной элемент массива, сдвигаем, если нужно, элемент влево, пока он не окажется на нужной позиции (при этом элементы, стоящие между первоначальной и окончательной позициями данного элемента, сдвигаются вправо);
  • продолжаем до тех пор, пока не переберем все элементы массива.

Результат вывести на экран.

3.13 Вычисление чисел Фибоначчи*

Реализовать функцию вычисления чисел Фибоначчи (до 92-го числа включительно) с использованием вспомогательного массива (статического поля). Параметр функции – номер числа Фибоначчи. При первом вызове функции массив заполняется до необходимого числа. При последующих вызовах число либо возвращается из массива, либо вычисляется с использованием последних двух чисел, хранящихся в массиве с дальнейшим заполнением массива. Использовать тип long для представления чисел.

Осуществить тестирование функции для различных значений номеров, вводимых в произвольном порядке.

3.14 Перемножение матриц*

Разработать функцию для перемножения двух матриц с проверкой размерностей и осуществить тестирование этой функции с проверкой возможных ошибок.

3.15 Сумма цифр

Дополнить пример 2.9 проверкой, все ли числа во введенной строке представляют собой цифры.

3.16 Сортировка строк

Проинициализировать одномерный массив строк. Отсортировать массив по алфавиту. Сшить все слова в новую строку.

3.17 Удаление слов*

Ввести с клавиатуры строку из нескольких слов, а также некоторую последовательность символов. Удалить из строки слова, содержащие введенную последовательность символов в виде подстроки.

3.18 Символы по алфавиту

Ввести с клавиатуры строку. Удалить из строки все цифры. Рассортировать все ее символы по алфавиту. Вывести полученные символы в виде новой строки.

3.19 Аббревиатура*

Ввести с клавиатуры строку из нескольких слов. Составить строку из первых букв слов с переводом этих букв в верхний регистр.

3.20 Корректор*

Ввести с клавиатуры строку. Удалить лишние пробелы, в том числе начальные и конечные. Внутри слов большие буквы заменить маленькими. Если перед словами, начинающимися с большой буквы, нет точки, добавить точку в конце предшествующих слов. Сформировать и вывести на экран слова, начинающиеся на мягкий знак.

3.21 Форматирование текста*

Осуществить форматирование текста с выравниванием по ширине. Текст разбить на строки с длиной, не превосходящей заданного количества символов. Если очередное слово не помещается в текущей строке, его необходимо переносить на следующую.

3.22 Транслятор*

Ввести с клавиатуры строку программного кода. Проверить соответствие фигурных скобок Заменить открывающиеся фигурные скобки словом begin, а закрывающиеся – словом end. Использовать класс StringBuilder.

3.23 Квадратный корень*

Реализовать функцию вычисления квадратного корня с параметром типа Double и возвращающую Double. Алгоритм вычисления квадратного корня заключается в определении начального приближения (например, 1) и последовательного получения новых приближений как среднего арифметического предыдущего числа и аргумента, деленного на это приближение. Алгоритм завершается, когда два последовательных приближения будут отличаться меньше заданной точности. Функция должна возвращать null, если аргумент – null или отрицательный.

4 Контрольные вопросы

  1. Чем отличается ссылка Java от указателя?
  2. Чем типы ссылки отличаются от типов-значений?
  3. Что является результатом присвоения одной ссылки другой?
  4. Всегда ли переменным типов-ссылок можно присваивать null?
  5. Как в Java удалить объект, созданный с помощью new?
  6. В чем заключается "сборка мусора"?
  7. Можно ли изменить размеры массива после его создания?
  8. Чем отличаются старая и новая формы цикла for?
  9. Можно ли с помощью новой формы цикла for менять значения элементов?
  10. Как определить количество столбцов двумерного массива?
  11. Можно использовать переменные для определения длины массива?
  12. Как добавить новый элемент в конец массива?
  13. Можно ли создать двумерный массив со строками различной длины?
  14. Чем отличается применение двух различных конструкций for для обхода элементов массива?
  15. Чем определяется размер массива, который создается функцией arraycopy()?
  16. Можно ли с помощью arraycopy() скопировать часть массива?
  17. Чем отличается работа функции System.arraycopy() от Arrays.copyOf()?
  18. Как осуществить чтение массива с клавиатуры?
  19. Каким образом можно заполнить элементы массива без цикла?
  20. Можно ли без цикла вывести значения элементов на консоль?
  21. Как без цикла установить, что элементы массивов совпадают?
  22. Можно ли без цикла отсортировать часть массива?
  23. Позволяет ли функция binarySearch() найти элемент в неотсортированном массиве?
  24. Можно ли изменить значения элементов массива с помощью функции?
  25. Как в Java создать функцию с переменным числом аргументов?
  26. Можно ли использовать рекурсию при работе с массивами?
  27. Как заполнить массив случайными значениями?
  28. Чем отличается использование функции Math.random() от средств класса java.util.Random?
  29. Какими способами можно создать новую строку?
  30. Как по умолчанию осуществляется сортировка массива строк?
  31. Можно ли изменить содержимое ранее созданной строки?
  32. Для чего используются классы StringBuffer и StringBuilder?
  33. Как осуществляется разделение строки на лексемы?
  34. Как перевести число в его строковое представление и наоборот?
  35. В чем преимущества и недостатки классов-оболочек по сравнению с соответствующими примитивными типами?
  36. Как создать объект типа Integer?
  37. Когда при работе с целыми использовать значение null?
  38. Как изменить регистр отдельных символов строки?

 

up