Core Java Основы ООП Viacheslav Kolbasin, <EPAM>
Объектная архитектура Java Классы p Интерфейсы p Пакеты p
Классы p Класс представляет собой совокупность полей данных и процедур - операций над этими данными. p Структурно класс представляет собой «черный ящик» с разъемами, в качестве которых выступают методы класса p Основные свойства классов n n n Инкапсуляция Полиморфизм Наследование
Объявление класса [мд] class Имя. Класса [extends Имя. Предка] [implements интерфейс1, …, инт. N ] { [мд] тип поле 1; . . . . [мд] тип поле. N; } p p [мд] тип имя_метода 1(список_параметров){ тело метода; } мд – модификаторы управления доступом (public, protected, …) и др. Переменная типа класс – ссылка! n p Статически, как в С++, создать объект класса нельзя Создание объекта выполняется оператором new: Имя. Класса ссылка = new Имя. Класса([параметры конструктора]);
Поля p p При создании экземпляра класса (объекта класса) поля инициализируются нулевым значением или null, если поле – ссылка. Принято поля делать скрытыми (private или protected), а для доступа к ним создавать методы чтения и записи (getter/setter). 1. class Figure{ private int pos; public void set. X(int x){ pos = pos – (pos % 1000) + x; } public int get. X(){ return pos % 1000; } …… Можно менять тип } поля данных и способ хранения данных, не затрагивая код, который Figure fig; использует этот класс fig. set. X(10);
Поля p p При создании экземпляра класса (объекта класса) поля инициализируются нулевым значением или null, если поле – ссылка. Принято поля делать скрытыми (private или protected), а для доступа к ним создавать методы чтения и записи (getter/setter). 1. class Figure{ private int x, y; public void set. X(int x){ this. x = x; } public int get. X(){ return x; } …… Можно менять тип } поля данных и способ хранения данных, не затрагивая код, который Figure fig; использует этот класс fig. set. X(10);
Поля p p При создании экземпляра класса (объекта класса) поля инициализируются нулевым значением или null, если поле – ссылка. class Figure{ private int x, y; public void set. X(int x){ if (x < 0) throw new Exception(“X”); Принято поля делать скрытыми (private или protected), а для доступа к ним создавать методы чтения и записи (getter/setter). 2. Можно выполнять обработку (проверку, преобразование) считываемых и записываемых данных this. x = x; } public int get. X(){ return x; } } Figure fig; fig. set. X(10);
Константы p Финальное поле в Java – это поле, значение которого задается лишь единожды n n n p В объявлении поля или в конструкторе Объявляется с использованием модификатора final Значение такого поля может меняться для каждого объекта и для каждого объекта под нее будет выделяться память Классическая константа в Java объявляется как static final поле: n Имя константы принято писать большими буквами p p n n final static int MAX_VALUE = 200; final static int OBJECT_ID = Math. random(); Одна область памяти для всех объектов Компилятор может подставлять значение в выражения
Методы p p Тело метода пишется сразу после его объявления Java поддерживает полиморфизм методов – можно задавать методы с одинаковым именем, но разной сигнатурой. n Какие методы будут считаться одинаковыми? p p p 1. public void print(int i, byte b) 2. public void print(int i) 3. public int print(int i) Конструктор объявляется как и в С++ Деструктора в понимании С++ нет. n Есть метод void finalize(); n ! который вызывается перед тем, как сборщик мусора будет удалять объект НО: Время после потери последней ссылки на объект и до его утилизации может быть любым! ! p В finalize нельзя помещать штатное освобождение ресурсов Перегрузки операторов тоже нет.
Наследование p Наследование есть детализация свойств некоторого общего класса n n Товар -> Продукт -> Молоко Животное -> Грызун -> Хомячок p Смысл наследования в возможности добавления новых полей и методов к существующему классу p Java поддерживает одиночное наследование классов В Java используются следующие названия: p n n Класс-предок – суперкласс Класс-потомок – подкласс
Наследование (2) class Figure{ int color; Figure (){ color = 0 x. FFFFFF; } Figure (int color){ this. color = color; } void move(int dx, int dy){} } class Rect extends Figure{ int x 1, y 1, x 2, y 2; Rect(){ super(); x 1=x 2=y 1=y 2=0; } Rect(int _x 1, int _y 1, int _x 2, int _y 2, int _color){ super(_color); x 1=_x 1; x 2=_x 2; y 1=_y 1; y 2=_y 2; } } void move(int dx, int dy){ x 1 +=dx; x 2+=dx; y 1+=dy; y 2+=dy; }; class Circle extends Figure{ int x, y, r; Circle (){super(); r = 10; } Circle (int _x, int _y, int _r, int _color){ super(_color); x=_x; y=_y; r= _r } void move(int dx, int dy){ x+=dx; y+=dy; Можно ли так }; присваивать? }; Figure f[] = new Figure[3]; f[0] = new Circle(); f[1] = new Rect(); … Предку присвоить for(int i=0; i<3; i++) потомка? f[i]. move(-5, 10);
Приведение типов (1) p Как может выполняться присвоение (пример на С++)? Figure f (); Circle c (); f=c; c=f; Правило автоматического приведения типов: Объекту суперкласса можно присвоить объект подкласса, а обратное присвоение (объекту подкласса присвоить объект суперкласса) недопустимо.
Приведение типов (2) p Как может выполняться присвоение (пример на Java)? Figure f = new Figure(); Circle c = new Circle(); f=c; память c=f; память
Приведение типов (3) p Явное приведение типов возможно и в обратном направлении n n n Так как все объекты – ссылки, ссылка типа «предок» может указывать на объект «потомок» Выполняется проверка реального типа объекта и при ошибке генерируется исключение Желательно, перед явным приведением типа проверить реальный тип объекта оператором instanceof Figure fig = new Rect(); Rect rect; // rect = null if (fig instanceof Rect) rect =(Rect) fig; else System. out. println("Ошибочка вышла!");
Наследование (4) class Figure{ int color; Figure (){ color = 0 x. FFFFFF; } Figure (int color){ this. color = color; } void move(int dx, int dy){. . . } } class Rect{ int x 1, y 1, x 2, y 2; Rect(){ super(); x 1=x 2=y 1=y 2=0; } Rect(int _x 1, int _y 1, int _x 2, int _y 2, int _color){ super(_color); x 1=_x 1; x 2=_x 2; y 1=_y 1; y 2=_y 2; } } void move(int dx, int dy){ x 1 +=dx; x 2+=dx; y 1+=dy; y 2+=dy; }; class Circle{ int x, y, r; Circle (){ r = 10; super(); } Circle (int _x, int _y, int _r, int _color){ super(_color); x=_x; y=_y; r= _r } void move(int dx, int dy){ x+=dx; y+=dy; Метод какого }; }; класса будет вызван ? Figure f[] = new Figure[3]; f[0] = new Circle(); f[1] = new Rect(); … for(int i=0; i<3; i++) Figure vs Circle ? f[i]. move(-5, 10);
Раннее и позднее связывание p Раннее связывание - адрес вызова будет вычислен на этапе компиляции в зависимости от типа ссылки n n p Позднее связывание - адрес будет вычислен в процессе выполнения программы в зависимости от реального типа объекта. n n p Будет вызван метод класса Figure. Метод никак не называется Будет вызван метод класса Circle. Метод называется виртуальным В Java все не final методы являются виртулаьными n В примере будут вызваны методы move классов Rect и Circle
Позднее связывание f[0] = new Circle(); f[1] = new Rect(); f[2] = new Circle();
Модификаторы методов p Для вызова метода суперкласса используется ключевое слово super n n Можно вызвать только метод непосредственного предка Вызов конструктора предка должен быть первым в конструкторе потомка p n Вызов метода: p p super(параметры_констр_предка) super. метод(параметры); Завершенные классы и методы n n Объявляются с указанием ключевого слова final Завершенный метод. p p n Нельзя перекрыть в наследнике Используется раннее связывание Завершенный класс p Нельзя создать наследник
Модификаторы методов p Абстрактные классы и методы n n Абстрактный метод не имеет реализации Объявляются с использованием ключевого слова abstract Нужен для описания действия, которое должно быть, но на данном этапе не может быть определено Класс, в котором есть хотя бы один абстрактный метод также должен быть абстрактным abstract class CFigure{ int color; CFigure(){ color = 0 x. FFFFFF; } abstract void move(int dx, int dy); . . . };
Поля и методы класса (1) p p p Также называются статическими полями и методами Объявляются с указанием ключевого слова static Поля класса n n Обращаться можно как имя. Класс. поле разделяются среди всех экземпляров класса изменив поле из одного объекта получим изменим поле во всех объектах может быть инициализировано в специальном блоке p static { …} class A { public static int var; static { var = 0; } } ……… A a=new A(), b = new A(); a. var = 10; b. var = 20; System. out. println(a. var); ? Выведет 20
Поля и методы класса (2) p p p Также называются статическими полями и методами Объявляются с указанием ключевого слова static Методы класса n n Обращаться можно как имя. Класс. метод(…) разделяются среди всех экземпляров класса в методах класса недоступен указатель this часто используются там, где нужны обычные процедуры p Например методами класса Math сделаны все математические функции class Figure { static int сount; static{ сount = 0 }; Figure{ сount++; } . . . static int get. Count(){ return Count; } }; …………. System. out. println(Figure. get. Count());
Области видимости p Классы n n p Имя public класса должно совпадать с именем файла ! 1 public класс в 1 файле исходного текста Поля и методы Доступ из методов public Нет protected модификатора (default) данного класса Да Да класса-наследника в том же пакете Да Да Да независимого класса в том же пакете Да Да Да класса-наследника в другом пакете Да Да независимого класса в другом пакете Да private
Интерфейсы
Предпосылки интерфейсов p Проблема множественного наследования: n n Если объект принадлежит к нескольким категориям, то он должен быть наследником базовых классов этих категорий А если эти категории тоже наследуют одной базовой ? Что, если к МФУ обратиться по типу предка? Электроприбор p; p. мощность = 10; Какая из мощностей правильная ?
Предпосылки интерфейсов p Проблема множественного наследования: n n Если объект принадлежит к нескольким категориям, то он должен быть наследником базовых классов этих категорий А если эти категории тоже наследуют одной базовой ? В С++ проблема решается виртуальным наследованием, но … его очень сложно корректно использовать
Интерфейсы (1) p В Java при множественном наследовании n запретили наследование p p n разрешили наследование p p p полей обычных методов абстрактных методов Для этого ввели новый тип – интерфейс Интерфейс n n n это дальнейшее развитие концепции абстрактного класса технически интерфейс - набор абстрактных методов и статических констант архитектурно интерфейс – способ задания общности поведения группы классов, не требуя общности их реализации p так разъем DVI задает общность поведения видеокарт, не требуя, чтобы они имели одинаковые детали
Интерфейсы (2) p объявление интерфейса [мд] interface имя. Интерфейса [extends имя. Инт. Предка 1 [, имя. Инт. Предка 2 […], имя_предка. N] { [поля констант; ] [объявления методов; ] }; p Поля n Все поля неявно объявлены как public static final p p То есть поля – статические константы Методы n Все методы неявно объявлены как public abstract p p То есть методы не имеют реализации При их определении в классах область видимость должна быть public
Пакеты
Пакеты (1) p Пакеты облегчают повторное использование кода и «крупноузловую сборку» программы из бибилиотек p Пакеты обеспечивают n Разделение пространства имен программы p n Подобно namespace в С++ Группировку классов, предназначенных для решения определенных задач p Наподобие библиотек в С++
Пакеты (2) p Имя пакета может состоять из нескольких частей, каждая из которых детализирует назначение пакета n “java. net” p p n “com. mysql. jdbc” p p “java” – пакет является частью библиотеки языка Java “net” – содержит классы для работы с сетью “com” – пакет стороннего коммерческого разработчика “mysql” – пакет относится к СУБД My. SQL “jdbc” – пакет содержит классы интерфейса с СУБД Имя пакета связано с расположением файлов классов n Каждой части имени пакета соответствует каталог n Список корневых каталогов задается переменной окружения CLASSPATH или настройками JVM n Корневым каталогом могут быть zip- и jar-архивы
Пакеты (2) p Имя пакета может состоять из нескольких частей, каждая из которых детализирует назначение пакета n “java. net” p p n “com. mysql. jdbc” p p “java” – пакет является частью библиотеки языка Java “net” – содержит классы для работы с сетью “com” – пакет стороннего коммерческого разработчика “mysql” – пакет относится к СУБД My. SQL “jdbc” – пакет содержит классы интерфейса с СУБД Если CLASSPATH = c: javalibrt. jar; c: mysqllib n то файлы пакетов будут располагаться в каталогах: “com. mysql. jdbc” “java. net” c: mysqllibcommysqljdbc c: javalibrt. jar@javanet
Пакеты (3) p Чтобы отнести классы и интерфейсы к пакету нужно n Указать в начале файла исходного кода строку package имя_пакета; n Разместить файл в каталоге, соответствующему имени пакета p p NB: при создании класса среда разработки это делает сама Чтобы подключить в программу классы из пакета надо n Использовать директиву import имя_пакета. имя_класса; import имя_пакета. *; n А что если имя класса повторяется в разных пакетах? p p Использовать полное имя класса Полное имя класса - имя. пакета. Имя. Класса n Для класса Driver из пакета com. mysql. jdbc полное имя будет p n com. mysql. jdbc. Driver Классу будет соответствовать файл: c: mysqllibcommysqljdbcDriver. class
Исключения
Принципы обработки ошибок p Код возврата h = open(filename, ”rw”); if (h > 0){ if (seek(h, pos) == pos){ cnt = read(h, &len, 4); if (cnt == 4){ c 2 = read(h, buf, len); if (c 2 == len){ close(h); return 1; } }else { close(h); return -1; } } else { // do something close(h); return -2; } }else return -3; p Исключения try{ h = open(filename, ”rw”); seek(h, pos); read(h, &len, 4); read(h, buf, len); } catch(File. Seek. Error *er){ // do something } catch(File. Error *er){ close(h); er->add. Stack. Pos(__LINE__); throw er; }
Принципы обработки ошибок p Код возврата n Ошибочность результата проверяется каждый раз p Исключения n Правильность выполнения проверяется только один раз n Функциональный код и код, обрабатывающий ошибки разделены n Функциональный код и код, обрабатывающий ошибки смешаны n Можно «потерять» ошибку n Нельзя «потерять» ошибку n Быстрее выполнение Переносимость n Медленнее выполнение Проблемы переносимости n n
Объекты ИС p Объектом исключительной ситуации может быть только наследник от класса Throwable n Error – ошибка JVM, которую прикладная программа не может обработать p n Exception – ошибка, которую прикладная программа может обработать p n Unchecked exception Checked exception Runtime. Exception – частая ошибка, которую не надо декларировать p Unchecked exception
Объекты ИС p Объектом исключительной ситуации может быть только наследник от класса Throwable n Unchecked exception – ситуации, которые прикладная программа не может обработать p p Error – ошибка системы (JVM) Обрабатывать нет смысла, т. к. обычно после ее возникновения JVM не может продолжить выполнение программы Runtime. Exception – ошибка программирования Тоже нет смысла обрабатывать, ибо иначе почему сразу не исправить ошибку ? ? ?
Объекты ИС p Объектом исключительной ситуации может быть только наследник от класса Throwable n Checked exception – ситуации, которые прикладная программа может обработать p p Exception Внешняя ошибка или ошибка в данных, после которой можно восстановить адекватное состояние программы p Ошибка чтения файла p Ошибка сетевого соединения p Ошибочный формат данных пользователя
Синтаксис p Блок обработки ИС try { // защищенный блок } catch (Тип. Исключения 1 е) { // обработчик ИС типа Тип. Исключения 1 } catch (Тип. Исключения 2 е) { // обработчик ИС типа Тип. Исключения 2 throw(e) // повторное возбуждение исключения } finally { // блок завершения } p Если при выполнении кода в защищенном блоке возникла ИС, то для нее ищется обработчик среди блоков «catch» n n n Если он найден, управление передается ему Если не найден – выполняется возврат в вызвавший метод и поиск обработчика там Всегда после выхода из защищенного блока выполняется блок завершения
Поиск обработчика (1) p Обработчики последовательно опрашиваются, могут ли они обработать ИС. n p Обработчик может обработать ИС, если тип ИС можно привести к типу, указанному в параметрах catch. Если защищенный блок может вызывать несколько ИС, то n порядок обработки зависит от взаимного расположения обработчиков p n Но это неправильно! В Java обработчики исключений следует располагать в порядке уменьшения детализированности ИС. p p То есть, сначала располагать исключения-потомки, а только после них - исключения-предки. Причем это закреплено на уровне правил языка
Поиск обработчика (2) class Ex 9_1{ public static void main(String args[]) { try { int a = args. length; if ((int)(Math. random()*2) == 1){ Ex 9_1 a; a. to. String(); }; int b = 42 / a; int c[] = { 1 }; c[42] = 99; } catch(Exception e){ System. out. println("Общая ошибка "); } catch (Arithmetic. Exception e) { System. out. println("деление на ноль: " + e); } catch(Array. Index. Out. Of. Bounds. Exception e) { System. out. println("выход за границы массива: " + e); } }}
Поиск обработчика (2) class Ex 9_1{ public static void main(String args[]) { try { int a = args. length; if ((int)(Math. random()*2) == 1){ Ex 9_1 a; a. to. String(); }; int b = 42 / a; int c[] = { 1 }; c[42] = 99; } catch (Arithmetic. Exception e) { System. out. println("деление на ноль: " + e); } catch(Array. Index. Out. Of. Bounds. Exception e) { System. out. println("выход за границы массива: " + e); } catch(Exception e){ System. out. println("Общая ошибка "); } }}
Поиск обработчика (3) p Если в обработчике возникает ИС, она, независимо от ее типа, может быть обработана только внешним, по отношению к данному, обработчиком. n p Ни один из обработчиков, связанных с текущим защищенным блоком эту ИС не обработает. ИС можно передать внешнему обработчику, для чего используется оператор throw e, где e - имя переменной ИС.
Пример 2 class Ex 9_2{ public static void gen_ex(){ int a=10, b=2; try{ a = a/(b-2); }catch(Aritmetic. Exception e){ e. set. Message("Деление на ноль"); throw e; }; } public static void main(String args[]) { try { gen_ex(); } catch (Arithmetic. Exception e) { System. out. println(e); } } }
Блок завершения Используется для освобождения ресурсов, выделенных в защищенном блоке. p Код, написанный внутри этого блока, всегда будет выполнен перед выходом из защищенного блока, независимо от того, p n n нормально ли завершился блок сгенерирована ли ИС
Пример 3 class Ex 9_3{ static void proc. A() { try { println("inside proc. A"); int a=0, b = 99/a; } finally { println("proc. A's finally"); } } static void proc. B() { try { println("inside proc. B"); return; } finally { println("proc. B's finally"); } } static void println(String s) { System. out. println(s); } } public static void main(String args[]){ try { proc. A(); }catch (Exception e) { } proc. B(); } Что будет отпечатано? inside proc. A's finally inside proc. B's finally
Пример 3 + class Test{ public static void main(String args[]){ try { System. exit(0); }finally { System. out. println(“fin”); } } } А что здесь напечатает ? Это один из случаев, когда finally не выполняется. А какой второй случай?
Список исключений метода p Обработчик ИС можно забыть написать n p Тогда системный обработчик сработает в самый неподходящий момент В Java генерируемые методом ИС должны быть задекларированы тип имя. Метода(…) throws ИС 1, …, ИСN{ тело метода } p Метод должен либо n n n p полностью обработать ИС внутри себя, либо объявить ее в списке исключений метода. Иначе - ошибка компиляции Это правило не распространяется на подклассы Runtime. Exception и Error n Они могут возникнуть всегда и после них нельзя восстановиться – смысл их декларировать?
Генерация исключительных ситуаций p Для генерации ИС используется оператор throw n n throw ссылка_на_наследника_Throwable Например: p p throw new Arithmetic. Exception("/ by zero"); Для создания собственной ИС следует объявить класс-наследник от Exception. n Классы Error и Runtime. Excetion лучше не использовать p p Error - отвечает за системные исключения, а не ИС прикладных программ Runtime. Excetion - приводит к "забыванию" обработчиков.
Пример class Ex 9_4{ static void demoproc() throws Illegal. Access. Exception{ try { throw new Illegal. Access. Exception ("demo"); } catch (Illegal. Access. Exception e) { System. out. println("caught inside demoproc"); throw e; } } public static void main(String args[]) { try { demoproc(); } catch(Illegal. Access. Exception e) { System. out. println("recaught: " + e); } } } caught inside demoproc recaught: java. lang. Null. Pointer. Exception: demo
Пример class My. Exception extends Exception { public static void main(String a[]) { private int detail; try { My. Exception(int a) { compute(1); detail = a: compute(20); } } catch (My. Exception e) { public String to. String() { S. o. println("caught" + e); return "My. Exception[" + detail + "]"; } } } class Exception. Demo { static void compute(int a) throws My. Exception{ called compute(1). S. o. println("called computer + a + "); if (a > 10) normal exit. throw new My. Exception(a); System. out. println("normal exit. "); called compute(20). } caught My. Exception[20]
Try with resources (JDK 1. 7+) try (Buffered. Reader br = new Buffered. Reader( new Input. Stream. Reader(System. in)); ){ // Какой-то код } Buffered. Reader br = null; try { br = new Buffered. Reader(new IStreader(System. in)); // Какой-то код }finally{ try { if (br != null) br. close(); } catch(Throwable t) {…. } }
Try with resources (JDK 1. 7+) void func 1() throws IOException{ try (Buffered. Reader br = new Buffered. Reader( new Input. Stream. Reader(System. in)); ){ // Какой-то код } } public static void main(String args[]){ try { obj. func 1(); }catch(IOException ex){ ex. print. Stack. Trace(); for (Throwable t : ex. get. Suppressed()) t. print. Stack. Trace();
class A { public void meth() { } } class B extends A { public void meth() throws IOException{ try{ System. in. read(); }catch(Exception ex){ throw new My. Runtime. Ex(ex); } } }