2.16.
Рекурсивные функции
Рекурсивная функция отличаются от обычного метода тем, что может вызывать
саму себя. В качестве примера рассмотрим функцию, которая вычисляет факториал
числа:
1 static int factorial(int x){
2 if (x == 1){
3 return 1;
4 }
5 return x * factorial(x - 1);
6 }
Здесь, вначале проверяется условие: если вводимое число не равно 1, то
умножаем данное число на результат этой же функции, в которую в качестве параметра
передается число x-1 (строка 5), т.е. происходит рекурсивный спуск. И так дальше, пока
значение параметра не будет равно единице.
Рекурсивная функция обязательно должна иметь некоторый базовый вариант,
который использует оператор return и который помещается в начале функции. В случае с
факториалом это if (x == 1) return 1;. И все рекурсивные вызовы должны обращаться к
подфункциям, которые в конечном счете сходятся к базовому варианту. Так, при
передаче в функцию положительного числа при дальнейших рекурсивных вызовах
подфункций в них будет передаваться каждый раз число, меньшее на единицу. И в конце
концов мы дойдем до ситуации, когда число будет равно 1, и будет использован базовый
вариант.
Здесь нужно помнить, что для определения факториала есть другие, более
оптимальные решения на основе циклов. Например:
1 static int factorial(int x){
2 int result=1;
3 for (int i = 1; i <= x; i++)
4 {
5 result *= i;
6 }
7 return result;
8 }
Другим примером рекурсивной функции служит функция, вычисляющая числа
Фибоначчи (каждое последующее число является суммой двух предыдущих, кроме 0 и 1).
В теории n-й член последовательности Фибоначчи определяется по формуле: f(n)=f(n-1) +
f(n-2), причем f(0)=0, а f(1)=1.
1 static int fibonachi(int n){
2 if (n == 0){
3 return 0;
4 }
5 if (n == 1){
6 return 1;
7 }
8 else{
9 return fibonachi(n - 1) + fibonachi(n - 2);
10 }
11 }
2.17. Обработка исключений
В процессе выполнения программы иногда могут возникать ошибки не по вине
разработчика. Их трудно предусмотреть или предвидеть, а иногда и вовсе невозможно.
Так, например, может неожиданно оборваться сетевое подключение при передаче
файла. Подобные ситуации называются исключениями.
2.17.1. Обработка одиночного исключения
В языке Java предусмотрены специальные средства для обработки таких
нештатных ситуаций. Одним из них является конструкция try...catch...finally.
При возникновении одиночной нештатной ситуации в блоке try управление
переходит в блок catch, который может обработать данное исключение. Если такой блок
не найден, то пользователю отображается сообщение о необработанном исключении, а
дальнейшее выполнение программы останавливается. Например:
1 int num[]= new int[3];
2 num [4]=45;
3 [Link](num [4]);
Что здесь не так?
Массив num может содержать только 3 элемента. Поэтому, при выполнении
инструкции num[4]=45 (строка 2) консоль отобразит исключение, и выполнение
программы будет завершено. Это исключение можно обработать так:
1 try{
2 int num[]= new int[3];
3 num [4]=45;
4 [Link](num [4]);
5 }
6 catch(Exception ex){ //начала блока обработки исключения
7 [Link](); // вывод стека трассировки ошибки
8 }
9 [Link]("Программа завершена");
При использовании блока try...catch вначале выполняются все инструкции между
операторами try и catch. Если в блоке try возникает нештатная ситуация (исключение), то
обычный порядок выполнения останавливается и переходит к инструкции сatch. Поэтому
когда выполнение программы дойдет до строки numbers[4]=45, программа остановится и
перейдет к блоку catch
Выражение catch имеет синтаксис: catch (тип_исключения имя_переменной).
В данном случае объявляется переменная ex, которая имеет тип Exception. Если
возникшее исключение не является исключением типа, указанного в инструкции сatch, то
оно не обрабатывается, а программа просто зависает или выбрасывает сообщение об
ошибке.
Но, тип Exception является базовым классом для всех исключений. Поэтому,
выражение catch (Exception ex) будет обрабатывать практически все исключения.
Обработка исключения сводится к выводу на консоль стека трассировки ошибки с
помощью метода printStackTrace(), определенного в классе Exception.
После завершения выполнения блока catch программа продолжает свою работу,
выполняя все остальные инструкции после блока catch.
Конструкция try..catch также может иметь блок finally. Но, этот блок
необязательный, и его можно при обработке исключений опускать.
Блок finally выполняется в любом случае - возникло исключение в блоке try или нет:
1 try{
2 int num[]= new int[3];
3 num [4]=45;
4 [Link](num [4]);
5 }
6 catch(Exception ex){
7 [Link]();
8 }
9 finally{
10 [Link]("Блок finally");
11 }
12 [Link]("Программа завершена");
2.17.2. Обработка нескольких исключений
В Java имеется множество различных типов исключений. Поэтому, можно
разграничить их обработку, включив дополнительные блоки catch:
1 int num[] = new int[3];
2 try{
3 num [6]=45;
4 num [6]=[Link]("gfd");
5 }
6 catch(ArrayIndexOutOfBoundsException ex){
7 [Link]("Выход за пределы массива");
8 }
9 catch(NumberFormatException ex){
10 [Link]("Ошибка преобразования из строки в число");
11 }
Если возникает исключение определенного типа, то оно переходит к
соответствующему блоку catch.
Оператор throw
Чтобы сообщить о выполнении исключительных ситуаций в программе, можно
использовать оператор throw, с помощью которого можем создать исключение и вызвать
его в процессе выполнения. Например, в нашей программе происходит ввод числа, и мы
хотим, чтобы, если число больше 30, то возникало исключение:
1 package firstapp;
2 import [Link];
3 public class FirstApp {
4 public static void main(String[] args) {
5 try{
6 Scanner in = new Scanner([Link]);
7 int x = [Link]();
8 if(x>=30){
9 throw new Exception("Число х должно быть меньше 30");
10 }
11 }
12 catch(Exception ex){
13 [Link]([Link]());
14 }
15 [Link]("Программа завершена");
16 }
17 }
Здесь для создания объекта исключения используется конструктор класса
Exception, в который передается сообщение об исключении. Если число х окажется
больше 29, то будет выдано исключение и управление перейдет к блоку catch.
В блоке catch мы можем получить сообщение об исключении с помощью
метода getMessage().
2.18. Примеры решения задач
Задача №1: Найти корни квадратного уравнения ax2 + bx + c = 0
1 import [Link];
2 public class Koren {
3 public static void main(String args[]) {
4 double a, b, c, D, x, x1, x2;
5 [Link]("Прог. решает кв. уравнение вида: ");
6 [Link]("ax^2 + bx + c = 0");
7 Scanner in = new Scanner([Link]);//Новый объект
8 [Link]("Введите a = ");
9 a = [Link]();
10 [Link]("Введите b = ");
11 b = [Link]();
12 [Link]("Введите c = ");
13 c = [Link]();
14 D = b * b - 4 * a * c;
15 if (D > 0) {
16 x1 = (-b - [Link](D)) / (2 * a);
17 x2 = (-b + [Link](D)) / (2 * a);
18 [Link]("x1 = " + x1 + ", x2 = " + x2);
19 } else if (D == 0) {
20 x = -b / (2 * a);
21 [Link]("Корень: x = " + x);
22 } else {
23 [Link]("Уравн. не имеет корней!");
24 }
25 }
26 }
Задача №2: Осуществить ввод и вывод числа через консоль
1 import [Link];
2 public class ConsInput {
3 public static void main(String args[]) {
4 Scanner in = new Scanner([Link]);
5 [Link]("Введите число = ");
6 int num = [Link]();
7 [Link]("Вы ввели число = %d \n", num);
8 [Link]();
9 }
10 }
Задача №3: Найти сумму всех элементов массива m[] = {3,5,7,12,11,17,23}
1 public class SummMassiv {
2 public static void main(String args[]) {
3 int m[] = {3,5,7,12,11,17,23};
4 int n=[Link];
5 [Link]("Количество элементов в массиве = "+ n);
6 int s=0;
7 for (int i = 0; i <n; i++) {
8 s = s+m[i];
9 }
10 [Link]("Сумма элементов массива = " +s);
11 }
12 }
Задача №4: Найти сумму всех чисел от 1 до N
1 import [Link];
2 public class Sum_from_1_to_N {
3 public static void main(String args[]){
4 Scanner in = new Scanner([Link]);
5 [Link]("Введите число = ");
6 int n = [Link](); //Считывание числа n
7 int s = 0; // Обнуление сумматора s
8 int i;
9 for (i=1;i<=n;i=i+1) // {
10 s = s + i;
11 }
12 [Link]("Сумма = " + s); // Вывод суммы
13 }
14 }
Задача №5: Найти произведение всех чисел от 1 до N (Факториал)
1 import [Link];
2 public class Sum_from_1_to_N {
3 public static void main(String args[]){
4 Scanner in = new Scanner([Link]);
5 [Link]("Введите число = ");
6 int n = [Link](); //Считывание числа n
7 int p = 1; // Начальное значение p
8 int i;
9 for (i=1;i<=n;i=i+1) // {
10 p = p * i;
11 }
12 [Link]("Произведение = " + p);
13 }
14 }
Задача №6: Сортировка элементов массива mas = {15, 2, 17, 80, 1} по возрастанию
1) Классический способ
1 import [Link];
2 public class SortMasVozr {
3 public static void main(String[] args) {
4 int mas[] = {15, 2, 17, 80, 1};
5 int n=[Link];
6 [Link]("Число элем. массива N = " + n);
7 int i,j,c;
8 for (i=0;i<n;i++) {
9 for (j=i;j<n;j++) {
10 if (mas[i] > mas[j]) {
11 c = mas[i];
12 mas[i] = mas[j];
13 mas[j] = c;
14 }
15 }
16 }
17 for (i=0;i<n;i++) {
18 [Link](mas[i]);
19 }
20 }
21 }
2) с помощью библиотеки Arrays
1 import [Link];
2 public class SortMasVozr {
3 public static void main(String[] args) {
4 int m[] = {15, 2, 17, 80, 1};
5 [Link](m);
6 for (int values : m) {
7 [Link](values + ", ");
8 }
9 }
10 }
Задача №7: Сортировка элементов массива mas = { 5, 2, 1, 8, 10 } по убыванию
1) Использование библиотек Arrays и Collections
1 import [Link];
2 import [Link];
3 class ArraySort {
4 public static void main(String[] args) {
5 Integer[] mas = { 5, 2, 1, 8, 10 };
6 [Link](mas, [Link]());
7 for (int values : mas) {
8 [Link](values + ", "); // 10, 8, 5, 2, 1,
9 }
10 }
11 }
Задача №8: Ввод и вывод Логин и Пароль через консоль
1 import [Link];
2 public class LoginPassword {
3 public static void main(String args[]) {
4 String log, pass;
5 Scanner in = new Scanner([Link]);
6 [Link]("Введите логин: ");
7 log = [Link]();
8 [Link]("Введите пароль: ");
9 pass = [Link]();
10 [Link]("Ваш логин = "+log);
11 [Link]("Ваш пароль = "+pass);
12 }
13 }
Задача №9: Найти максимальный элемент массива m = {3,5,8,12,11,45,21,34};
1 public class MaxElement {
2 public static void main(String args[]) {
3 int m[] = {3,5,8,12,11,45,21,34};
4 int n=[Link];
5 [Link]("Кол. элем. в массиве = "+ n);
6 int max=m[0];
7 for (int i = 0; i <n; i++) {
8 if(max<m[i]) {
9 max=m[i];
10 }
11 }
12 [Link]("Макс. элем. массива = " +max);
13 }
14 }
Задача №10: Задан массив m[] = {3, 5, 8, 12, 11, 45, 21, 34}.
Найти произведение чисел от 1 до m[i], если m[i] – четный и сумму чисел от 1 до
m[i], если m[i] – нечетный.
1 public class NechetSumChetPro {
2 public static void main(String args[]) {
3 int m[] = {3, 5, 8, 12, 11, 45, 21, 34}; // Ввод элементов массива
4 int n = [Link]; // Определение длины массива
5 [Link]("Количество элементов в массиве = " + n);
6 [Link]("----------------------------------");
7 for (int i = 0; i < n; i++) {
8 int l = m[i] % 2; // Определение остатка
9 [Link](i+" элемент = "+m[i]+". Остаток l = " + l);
10 if (l == 0) {
11 int p = 1;
12 for (int k = 1; k <= m[i]; k++) {
13 p = p * k;
14 }
15 [Link](": Произведение до " + m[i]+" = " + p);
16 }
17 else {
18 int s = 0;
19 for (int t = 1; t <=m[i]; t++) {
20 s = s + t;
21 }
22 [Link](": Сумма до " + m[i] + " = " + s);
23 }
24 }
25 }
Задача №11: Задан двумерный массив
mas [5, 4] = {{5,7,3,17}, {7,0,1,12}, {8,1,2,3},{1,2,3,4},{12,7,3,8}};
5 7 3 17
7 0 1 12
mas = 8 1 2 3
1 2 3 4
12 7 3 8
Найти сумма всех элементов двумерного массива
1 public class SumOfTwoSizeMassiv {
2 public static void main(String args[]) {
3 int [][] mas = {{5,7,3,17}, {7,0,1,12}, {8,1,2,3},
4 {1,2,3,4},{12,7,3,8}};
5 // Опр. длины двумерного массива, т.е. кол. строк в массиве
6 int n = [Link];
7 [Link]("Длина массива = " + n);
8 int s=0;
9 // Вывод элементов двумерного массива
10 for (int i = 0; i < n; i++) { //Цикл по строкам
11 for (int j = 0; j < 4; j++) { //Цикл по столбцам
12 [Link](" " + mas[i][j] + " ");
13 // Вычисление суммы элементов двумерного массива
14 s=s+mas[i][j];
15 }
16 [Link]();
17 }
18 // Вывод элементов двумерного массива
19 [Link]();
20 [Link]("Сумма = "+s);
21 }
22 }
Задача №12: Дано слово "lomonosov". Найти количество символа “o” в нем.
1 public class SymbolStroka {
2 public static void main(String args[]) {
3 String slovo = "lomonosov";
4 char sym='o'; // Искомый символ
5 int n=[Link](); // Кол. символов в строке
6 [Link]("Кол. символов = "+n);
7 int s=0;
8 for(int i=0;i<n;i++) {
9 char sim = [Link](i); //Выд. i-того символа в строке
10 [Link](" "+sim);
11 if (sim==sym) {
12 s=s+1;
13 }
14 }
15 [Link](" ");
16 [Link](" Количество = "+s);
17 }
18 }
3. РАБОТА СО СТРОКАМИ
3.1. Введение в строки. Класс String
Строка представляет собой последовательность символов. Для работы со
строками в Java определен класс String, который предоставляет ряд методов для
манипуляции строками. Физически объект String представляет собой ссылку на область в
памяти, в которой размещены символы.
Для создания новой строки можно использовать один из конструкторов класса
String, либо напрямую присвоить строку в двойных кавычках:
1 public static void main(String[] args) {
2
3 String str1 ="Java";
4 String str2 =new String(); // пустая строка
5 String str3 =new String(new char[] {'h','e','l','l','o'});
6 //после Welcome 3 -начальный индекс, 4 - кол-во символов
7 String str4=new String(new char[]{'w','e','l','c','o','m','e'},3, 4);
8
9 [Link](str1); // Java
10 [Link](str2); //
11 [Link](str3); // hello
12 [Link](str4); // come}
Объект String является неизменяемым (immutable), т.е. при любых операциях над
строкой, которые изменяют эту строку, фактически будет создаваться новая строка.
3.1.1. Длина строки
Поскольку строка рассматривается как набор символов, то мы можем применить
метод length() для нахождения длины строки или длины набора символов:
1 String w = "Fantomas";
2 [Link]([Link]()); // 8
3.1.2. Преобразование строки в массив символов
С помощью метода toCharArray() можно преобразовать строку в массив символов:
1 String str = new String(new char[] {'h','e','l','l','o'});
2 char[] helloArray = [Link]();
Строка может быть пустой. Для этого ей нужно присвоить пустые кавычки или
удалить из стоки все символы:
1 String s = ""; // пустая строка
2 if([Link]() == 0) [Link]("Строка пустая");
В этом случае длина строки, возвращаемая методом length(), равна 0.
3.1.3. Проверка строки на пустоту
Класс String имеет специальный метод, который позволяет проверить строку на
пустоту - isEmpty(). Если строка пуста, он возвращает true:
1 String s = ""; // пустая строка
2 if([Link]()) [Link]("Строка пустая");
Переменная String может не указывать на какой-либо объект и иметь значение null:
1 String s = null; // строка не указывает на объект
2 if(s == null) [Link]("Строка null");
Значение null не эквивалентно пустой строке. Например, в следующем случае мы
столкнемся с ошибкой выполнения:
1 String s = null; // строка не указывает на объект
2 if([Link]()==0) [Link]("Строка пустая "); //!Ошибка
Так как переменная не указывает ни на какой объект String, то соответственно
нельзя обращаться к методам объекта String.
Чтобы избежать подобных ошибок, можно предварительно проверять строку на
null:
1 String s = null; // строка не указывает на объект
2 if(s==null || [Link]()==0) [Link]("String is empty");
3.2. Основные методы класса String
Основные операции со строками выполняются через методы класса String:
concat(): объединяет строки
valueOf(): преобразует объект в строковый вид
join(): соединяет строки с учетом разделителя
сompareTo(): сравнивает две строки
charAt(): возвращает символ строки по индексу
getChars(): возвращает группу символов
equals(): сравнивает строки с учетом регистра
equalsIgnoreCase(): сравнивает строки без учета регистра
regionMatches(): сравнивает подстроки в строках
indexOf(): находит индекс первого вхождения подстроки в строку
lastIndexOf(): находит индекс последнего вхождения подстроки в
строку
startsWith(): определяет, начинается ли строка с подстроки
endsWith(): определяет, заканчивается ли строка на определенную подстроку
replace(): заменяет в строке одну подстроку на другую
trim(): удаляет начальные и конечные пробелы
substring(): возвращает подстроку, начиная с определенного
индекса
до конца или до определенного индекса
toLowerCase(): переводит все символы строки в нижний регистр
toUpperCase(): переводит все символы строки в верхний регистр
Рассмотрим более подробно работу этих методов.
3.2.1. Соединение строк
Для соединения строк можно использовать операцию сложения ("+"):
1 String str1 = "Java";
2 String str2 = "Hello";
3 String str3 = str1 + " " + str2;
4
5 [Link](str3); // Hello Java
При этом если в операции сложения строк используется нестроковый объект,
например, число, то этот объект преобразуется к строке:
1 String str3 = "Год " + 2015;
Фактически же при сложении строк с нестроковыми объектами будет вызываться
метод valueOf() класса String. Данный метод имеет множество перегрузок и преобразует
практически все типы данных к строке. Для преобразования объектов различных классов
метод valueOf вызывает метод toString() этих классов.
Другой способ объединения строк представляет метод concat():
1 String str1 = "Java";
2 String str2 = "Hello";
3 str2 = [Link](str1); // HelloJava
Метод concat() принимает строку, с которой надо объединить вызывающую строку,
и возвращает соединенную строку.
Еще один метод объединения - метод join() позволяет объединить строки с
учетом разделителя. Например, выше две строки сливались в одно слово "HelloJava", но
в идеале мы бы хотели, чтобы две подстроки были разделены пробелом. И для этого
используем метод join():
1 String str1 = "Java";
2 String str2 = "Hello";
3 String str3 = [Link](" ", str2, str1); // Hello Java
Метод join является статическим. Первым параметром идет разделитель, которым
будут разделяться подстроки в общей строке, а все последующие параметры передают
через запятую произвольный набор объединяемых подстрок - в данном случае две
строки, хотя их может быть и больше
3.2.2. Извлечение символов и подстрок
Для извлечения символов по индексу в классе String определен метод char
charAt(int index). Он принимает индекс, по которому надо получить символов, и
возвращает извлеченный символ:
1 String str = "Java";
2 char c = [Link](2);
3 [Link](c); // v
Как и в массивах индексация начинается с нуля.
Если надо извлечь сразу группу символов или подстроку, то можно использовать
метод getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin). Он принимает следующие
параметры:
srcBegin: индекс в строке, с которого начинается извлечение символов
srcEnd: индекс в строке, до которого идет извлечение символов
dst: массив символов, в который будут извлекаться символы
dstBegin: индекс в массиве dst, с которого надо добавлять извлеченные из
строки
символы
1 String str = "Hello world!";
2 int start = 6;
3 int end = 11;
4 char[] dst=new char[end - start];
5 [Link](start, end, dst, 0);
6 [Link](dst); // world
3.2.3. Сравнение строк
Для сравнения строк используются методы:
equals() - с учетом регистра
equalsIgnoreCase() - без учета регистра.
Оба метода в качестве параметра принимают строку, с которой надо сравнить:
1 String str1 = "Hello";
2 String str2 = "hello";
3
4 [Link]([Link](str2)); // false
5 [Link]([Link](str2)); // true
В отличие от сравнения числовых и других данных примитивных типов для строк
не применяется знак равенства ==. Вместо него надо использовать метод equals().
Еще один специальный метод regionMatches() сравнивает отдельные подстроки в
рамках двух строк. Он имеет следующие формы:
1 boolean regionMatches(int toffset, String other, int oofset,
2 int len)
3
4 boolean regionMatches(boolean ignoreCase, int toffset,
5 String other, int oofset, int len)
Метод принимает следующие параметры:
ignoreCase: надо ли игнорировать регистр символов при сравнении. Если
значение true, регистр игнорируется
toffset: начальный индекс в вызывающей строке, с которого начнется сравнение
other: строка, с которой сравнивается вызывающая
oofset: начальный индекс в сравниваемой строке, с которого начнется сравнение
len: количество сравниваемых символов в обеих строках
Используем метод:
1 String str1 = "Hello world";
2 String str2 = "I work";
3 boolean result = [Link](6, str2, 2, 3);
4 [Link](result); // true
В данном случае метод сравнивает 3 символа с 6-го индекса первой строки ("wor")
и 3 символа со 2-го индекса второй строки ("wor"). Так как эти подстроки одинаковы, то
возвращается true.
И еще одна пара методов int compareTo(String str) и int compareToIgnoreCase(String
str) также позволяют сравнить две строки, но при этом они также позволяют узнать
больше ли одна строка, чем другая или нет. Если возвращаемое значение больше 0, то
первая строка больше второй, если меньше нуля, то, наоборот, вторая больше первой.
Если строки равны, то возвращается 0.
Для определения больше или меньше одна строка, чем другая, используется
лексикографический порядок. То есть, например, строка "A" меньше, чем строка "B", так
как символ 'A' в алфавите стоит перед символом 'B'. Если первые символы строк равны,
то в расчет берутся следующие символы. Например:
1 String str1 = "hello";
2 String str2 = "world";
3 String str3 = "hell";
4
5 [Link]([Link](str2)); // -15 - str1 меньше чем strt2
6 [Link]([Link](str3)); // 1 - str1 больше чем str3