- •Основы классов
- •Общая форма класса
- •Простой класс
- •Объявление объектов
- •Операция new
- •Представление методов
- •Добавление метода к классу Box
- •Возврат значений
- •Добавление метода с параметрами
- •Конструкторы
- •Параметризованные конструкторы
- •Ключевое слово this
- •Скрытие переменной экземпляра
- •Сборка "мусора"
- •Метод finalize()
- •Класс Stack
- •Перегрузка конструкторов
- •Использование объектов в качестве параметров
- •Передача аргументов
- •Возврат объектов
- •Рекурсия
- •Управление доступом
- •Статические элементы
- •Спецификатор final
- •Ревизия массивов
- •Вложенные и внутренние классы
- •Класс String
- •Использование аргументов командной строки
- •Наследование
- •Основы наследования
- •11 Тело класса }
- •Доступ к элементам и наследование
- •Практический пример
- •Переменная суперкласса может ссылаться на объект подкласса
- •Использование ключевого слова super
- •Вызов конструктора суперкласса с помощью первой формы super
- •Создание многоуровневой иерархии
- •II построить клон объекта
- •Когда вызываются конструкторы
- •Переопределение методов
- •Динамическая диспетчеризация методов
- •Зачем нужны переопределенные методы?
- •Применение переопределения методов
- •Использование абстрактных классов
- •Void meth() { // ошибка! Нельзя переопределять.
- •Класс Object
Возврат объектов
Метод может возвращать любой тип данных, включая типы классов, которые вы создаете. Например, в следующей программе метод incrByTen() возвращает объект, в котором значение а на десять больше, чем в вызывающем объекте.
// Возврат объекта,
class Test {
int a ;
Test(int i) {
a = i;
}
Test incrByTen() {
Test temp = new Test(a+10);
return temp;
}
}
class RetOb {
public static void main(String args[]) {
Test ob1 = new Test (2);
Test ob2;
ob2 = ob1.incrByTen() ;
System.out.println("obi.a: " + obi.a);
System.out.println("ob2.a: " + ob2.a);
ob2 = ob2.incrByTen();
System.out.println("ob2.а после повторного увеличения: " + ob2.a);
}
}
Вывод, сгенерированный этой программой:
obl.a: 2
оЬ2.а: 12
оЬ2.а после 'повторного увеличения: 22
Каждый раз, когда incrByTen о вызывается, создается новый объект, и ссылка на него возвращается вызывающей подпрограмме.
Из предыдущей программы можно сделать следующий важный вывод: т. к. все объекты распределяются динамически с помощью операции new, вас не должен беспокоить выход объекта из области его видимости, потому что метод, в котором он был создан, завершается. Объект продолжает существовать, пока где-то в вашей программе присутствует ссылка на него. Когда же ссылок нет, то в следующем сеансе сборки "мусора" объект будет утилизирован.
Рекурсия
Java поддерживает рекурсию. Рекурсия – это процесс определения чего-то в терминах самого себя. Что касается Java-программирования, то рекурсия – это атрибут, который позволяет методу вызвать самого себя. Такой метод называют рекурсивным.
Классический пример рекурсии – вычисление факториала числа. Факториал числа N есть произведение всех целых чисел между 1 и N. Например, факториал 3 равен 1*2*3 или 6. Ниже показано, как факториал может быть вычислен при помощи рекурсивного метода:
// Простой пример рекурсии,
class Factorial { // это рекурсивная функция
int fact (int n) {
int result ;
if(n==l) return 1 ;
result = fact(n-l) * n ;
return result ;
}
}
class Recursion { . .
public static void main(String args[]) {
Factorial f = new Factorial();
System.out.println("Факториал 3 равен " + f.fact(3)) ;
System.out.println("Факториал 4 равен " + f.fact(4)) ;
System.out.println("Факториал 5 равен " + f.fact(5)) ;
}
}
Вывод этой программы:
Факториал 3 равен 6
Факториал 4 равен 24
Факториал 5 равен 120
Если вы не знакомы с рекурсивными методами, то операция fact () может показаться немного запутанной. Вот как она работает. Когда метод fact () вызывается с параметром 1, функция возвращает I, иначе она возвращает произведение fact(n-i)*n. Чтобы оценить это выражение, fact () вызывается с параметром п-i. Этот процесс повторяется до тех пор, пока п не станет равным 1, и вызов метода не начнет возврат.
Чтобы лучше понять, как работает метод facto, прокрутим короткий пример. Когда вы вычисляете факториал 3, первый вызов fact () приведет ко второму вызову – с аргументом 2. Это обращение, в свою очередь, вызовет fact () в третий раз – с аргументом 1, а затем возвратит значение 1, которое потом умножается на 2 (значение п во втором обращении). Этот результат (который равен 2) затем возвращается первоначальному вызову fact () и умножается на 3 (первоначальное значение п). Вся это процедура приводит к окончательному результату 6. Было бы интересно вставить в fact () утверждения с println (), которые покажут, на каком уровне каждый вызов находится и каковы промежуточные ответы.
Когда метод вызывает сам себя, новым локальным переменным и параметрам выделяется память в стеке, и код метода выполняется с этими новыми переменными от начала стека. Рекурсивный вызов не делает новой копии метода. Обновляются только параметры. Когда каждый рекурсивный вызов выполняет возврат, старые локальные переменные и параметры удаляются из стека, и выполнение возобновляется в точке вызова внутри метода.
Рекурсивные версии многих подпрограмм могут выполняться немного медленнее, чем итерационный эквивалент, из-за добавления дополнительных вызовов функций. Частые рекурсивные обращения к методу могут вызывать переполнение стека. Поскольку память для параметров и локальных переменных находится в стеке, и каждый новый вызов создает новую копию этих переменных, возможно, что стек может быть исчерпан. Если это происходит, исполнительная система Java вызовет исключение.
Главное преимущество рекурсивных методов состоит в том, что их можно использовать для создания более ясных и простых версий некоторых алгоритмов (по сравнению с их итерационными "родственниками"). Например, алгоритм быстрой сортировки весьма трудно реализовать итерационным способом. Некоторые проблемы, особенно имеющие отношение к искусственному интеллекту, кажется, удобно решать рекурсивно. Наконец, некоторым людям кажется, что рекурсивное мышление легче, чем итеративное.
При записи рекурсивных методов следует где-то использовать оператор if, чтобы вынудить метод осуществить возврат без выполнения рекурсивного вызова. Если вы этого не сделаете, то после вызова метода, он никогда не будет возвращать управления. Это самая распространенная ошибка в работе с рекурсией. Свободно используйте утверждение println о во время разработки для наблюдения за происходящим и выполняйте аварийное завершение, если видите, что сделали ошибку.
Ниже приведен еще один пример рекурсии. Рекурсивный метод printArray () печатает 10 первых элементов массива values.
// Другой пример использования рекурсии.
class RecTest {
int values[] ;
RecTest(int i) {
values = new intfi] ;
}
// отобразить массив рекурсивно
void printArray (int i) {
if(i==0) return ;
else printArray(i-1) ;
System.out.println("[" + (i-1) + "]" + values[i-1] ; } } . .
class Recursion2 {
public static void main (String ' args []) {
RecTest ob = new RecTest (10) ;
int I ;
for(i=0; i<10; i++) ob.values[i] = I ;
ob. printArray (10) ;
}
}
Эта программа генерирует следующий вывод:
[0] 0
[1] 1 . .
[2] 2 :
[3] 3
[4] 4
[5] 5 .
[6] 6
[7] 7
[8] 8
[9] 9
