Показаны сообщения с ярлыком Классы. Показать все сообщения
Показаны сообщения с ярлыком Классы. Показать все сообщения

9 июл. 2015 г.

Наследование. Часть 2 – абстрактные классы и методы.

Абстрактный класс – это класс экземпляр которого нельзя создать. Ну и зачем же он тогда может быть нужен? А вот представьте себе нужен таки :)

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

Абстрактные классы объявляются при помощи модификатора abstract. И как можно догадаться они могут содержать абстрактные методы, которые объявляются при помощи этого же модификатора.

Java позволяет определить метод без реализации, объявив его с модификатором abstract. У абстрактного метода нет тела; у него есть только заголовок, заканчивающийся точкой с запятой.

A0001

Вот правила для абстрактных методов и абстрактных классов, которые их содержат:

  • Любой класс с абстрактным методом автоматически становится абстрактным и должен быть объявлен как abstract.
  • Нельзя создать ни одного экземпляра абстрактного класса.
  • Экземпляры подклассов абстрактного класса, можно создавать только в том случае, если все методы, объявленные как abstract, замещены и реализованы (то есть имеют тело). Такие классы часто называют реальными, чтобы подчеркнуть тот факт, что они не абстрактные.
  • Если подкласс абстрактного класса, не реализует всех методов, объявленных как abstract, то этот подкласс сам является абстрактным.
  • Методы, объявленные как static, private и final, не могут быть объявлены как abstract, поскольку такие методы не могут быть замещены подклассами. Точно так же класс, объявленный как final, не может содержать методов, объявленных как abstract.
  • Класс может быть объявлен как abstract, даже если он не содержит ни одного абстрактного метода. Такое объявление означает, что реализация класса все еще не закончена и класс будет служить родителем для одного или нескольких подклассов, которые завершат реализацию. Экземпляр такого класса не может быть создан.
  • Можно создавать объектные переменные абстрактных классов, однако такие переменные должны ссылаться на объект неабстрактного подкласса.

A0002Ну а теперь немножко попрактикуемся, чтобы понять получше эту тему. Создадим абстрактный класс животного и от него унаследуем конкретных животных – корову, кошку и собаку.

В абстрактном классе объявим абстрактный метод getSound(), который будет выводить на печать звук который издает животное.

А так же, чтобы знать что за животное издает этот звук мы создадим нормальный (не абстрактный) метод getType(), который будет возвращать тип животного.

В классах наследниках мы уже сделаем для каждого свою реализацию абстрактного метода getSound().

A0003

A0004

A0005

A0006

Ну и собственно сводим теперь все в одну работающую кучу :)

Следует заметить что мы объявили массив ссылок типа Animal. Напоминаю одно из правил – можно создавать объектные ссылки абстрактных классов, но ссылаться они должны на объекты подклассов, что у нас и реализовано.

Каждому из элементов массива мы присваиваем ссылку на объект подкласса класса Animal.

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

Вывод у программы следующий:

A0007

Как видим звуки соответствуют животным :)

6 июл. 2015 г.

Модификатор final для методов и классов.

Мы уже рассматривали ключевое слово final для полей и переменных. Но модификатор final может так же применятся к классам и методам. Смысл ключевого слова final зависит от контекста, но в основном оно означает: "Это нельзя изменить".

Теперь мы рассмотри модификатор final в контексте применения к методам и классам поскольку в данном случае это связано с наследованием и полиморфизмом, которые мы сейчас изучаем.

  • Если модификатор final стоит перед определением метода, то это означает, что этот метод не может быть переопределен (overrided) в классах наследниках.
  • Если модификатор final стоит перед определением класса, то это означает, что от этого класса не возможно унаследоваться.

Стоит отметить что метод объявленный как private, косвенно является final методом. Так как вы не можете получить доступ к private методу, то не можете его и переопределить.

GrabliИ вот тут возможны грабельки. Если вы не внимательно использовали модификатор private для какого-либо метода в суперклассе, а затем решили переопределить этот метод в подклассе, то переопределения или ожидаемого эффекта полиморфизма не произойдет и что более интересно, компилятор не выдаст ни какой ошибки, так как такой синтаксис является вполне легитимным и не нарушает выполнения программы. Этот момент я думаю стоит пояснить на примере. Но сперва еще раз прочитайте материал о полиморфизме.

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

P0001

У нас есть суперкласс Root, от которого наследуется класс Branch. В классе Root есть метод prt(), а в классе наследнике Branch этот метод как бы переопределяется. Но на самом деле переопределения не происходит, так как метод prt() в классе Root имеет модификатор private. Поэтому метод prt() в классе Branch не переопределяет его, поскольку даже не может его увидеть. Метод prt() класса Branch это полностью новый метод.

Из того что мы знаем вполне естественно было бы ожидать что программа выведет как результат слово Branch, но  она выведет слово Root. А вот если в методе prt() класса Root убрать модификатор private, то выведется слово Branch.

 

Чтобы понять эту кухню надо объяснить что такое позднее и раннее связывание. Но об этом мы подробнее поговорим в одной из следующих статей.

P0002Сейчас же пока надо отметить что модификаторы final и private не совсем равнозначны. Верней их действие совсем разное. Модификатор private делает метод или поле доступным только в пределах класса и как следствие методы с модификатором private не могут быть переопределены в подклассах, поскольку эти методы даже не видны в подклассах. Это означает что подклассы не могут вызвать private методы суперклассов. Методы же объявленные как final доступны подклассам и могут быть ими вызваны, но не могут быть ими переопределены. Поэтому, как показано, на рисунке слева, при использовании модификатора final вместо private, компилятор выдаст ошибку.

2 июл. 2015 г.

Наследование. Практика – вызов конструкторов суперклассов.

Чтобы не сильно уставать от теории, опять немного попрактикуемся. И опять же вспомним и закрепим некоторый прошлый материал на практике. А практиковаться будем чтобы лучше понять вызов конструкторов суперклассов. А так же их инициализационных блоков.

И так, у нас есть три класса А, В и С. Класс С наследуется от класс В, а класс В от класса А.

C0001

C0002

C0003

C0004

C0005

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

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

Но так же надо понимать всю внутреннюю кухню этого процесса, что просто по умолчанию в конструкторах подклассов не явно присутствует вызов конструктора суперкласса – super().

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

30 июн. 2015 г.

Пакеты в Java.

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

Зачем нужны пакеты в Java и что это такое

  • Задание пространства имен, предотвращение коллизий имен классов
  • Логическая группировка связанных классов
  • Инкапсуляция - сокрытие деталей реализации за счет модификаторов доступа

Платформа Java содержит пакеты, имена которых начинаются на com, java, javax и org. Посмотрите это в сырцах, не поленитесь. Основные классы языка входят в пакет java.lang. Различные вспомогательные классы находятся в java.util. Классы для ввода и вывода входят в java.io, а классы для работы в сети – в java.net. Некоторые их этих пакетов содержат подпакеты. Например, java.lang содержит два специализированных пакета java.lang.reflect и java.lang.ref, а java.util содержит подпакет java.util.zip, который в свою очередь содержит классы для работы с ZIPархивами.

Каждый класс имеет как простое имя, данное ему в определении, так и полное имя, включающее имя пакета, в который он входит. Например, класс String является частью пакета java.lang, а его полное имя – java.lang.String.

В стандартную библиотеку Java API входят сотни классов. Каждый программист в ходе работы добавляет к ним десятки своих классов. Множество классов растет и становится необозримым. Уже давно принято отдельные классы, решающие какую-то одну определенную задачу, объединять в библиотеки классов. Но библиотеки классов, кроме стандартной библиотеки, не являются частью языка.

Разработчики Java включили в язык дополнительную конструкцию — пакеты (packages). Все классы Java распределяются по пакетам. Кроме классов пакеты могут содержать интерфейсы и вложенные подпакеты (subpackages). Образуется древовидная структура пакетов и подпакетов.

Эта структура в точности отображается на структуру файловой системы. Все файлы с расширением class (содержащие байт-коды), образующие один пакет, хранятся в одном каталоге файловой системы. Подпакеты образуют подкаталоги этого каталога.

Каждый пакет создает одно пространство имен (namespace). Это означает, что все имена классов, интерфейсов и подпакетов в пакете должны быть уникальны. Имена в разных пакетах могут совпадать, но это будут разные программные единицы. Таким образом, ни один класс, интерфейс или подпакет не может оказаться сразу в двух пакетах. Если надо в одном месте программы использовать два класса с одинаковыми именами из разных пакетов, то имя класса уточняется именем пакета: пакет.Класс. Такое уточненное имя называется полным именем класса (fully qualified name).

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

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

Определение пакета

Чтобы определить пакет для какого-либо класса, следует использовать директиву package. Если в Java-коде присутствует ключевое слово package, оно должно быть первой лексемой кода в файле Java, то есть должно следовать сразу после комментариев и пробелов. После ключевого слова должно стоять имя требуемого пакета и точка с запятой. Например:

package pro.java.pkg001;

Все файлы с исходными текстами и тем-более откомпилированные файлы с байт-кодом (расширение .calss) должны находится в соответствующих под-каталогах, как, например в данном случае в подкаталоге pro\java\pkg001. Все современные IDE при создании пакета, сразу же создают соответствующую файловую структуру в каталоге проекта, так что об этом можно особо не беспокоиться, но знать необходимо.

Поскольку строка package имя; только одна и это обязательно первая строка файла, каждый класс попадает только в один пакет или подпакет.

До сих пор мы создавали классы без директивы package. Классы, определяемые файлом Java-кода без директивы package, входят в пакет без имени, существующий по умолчанию. Как вы узнаете чуть позже, классы одного пакета получают специальный доступ друг к другу. Именно поэтому, когда мы использовали в программе несколько классов, мы могли получать доступ из одного класса к методам или полям другого.

Компилятор всегда создает для таких классов безымянный пакет (unnamed package), которому соответствует текущий каталог (current working directory) файловой системы.

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

Имя пакета может быть любым, главное чтобы оно было уникальным. Соглашение "Code Conventions" рекомендует записывать имена пакетов строчными буквами. Тогда они не будут совпадать с именами классов, которые, по соглашению, начинаются с прописной буквы. Кроме того, соглашение советует использовать в качестве имени пакета или подпакета доменное имя своего сайта, записанное в обратном порядке, например:

com.oracle.developer

Самое главное правило – это то, что имя пакета должно быть уникальным.

Одной из важных функций пакетов является разделение пространства имен Java и предотвращение конфликта имен между классами. Например, классы java.util.List и java.awt.List можно различить только по именам их пакетов. Однако важно, чтобы различались и имена самих пакетов.

Как работают пакеты

Использование класса из пакета:

  • Классы текущего пакета и пакета java.lang всегда видны
  • Классы других пакетов доступны по полному имени с пакетом, но если они имеют соответствующие модификаторы доступа
  • Можно использовать директиву import

Методы и поля классов в текущем пакете доступны другим классам из этого же пакета, если у них нет ни каких модификаторов доступа (о которых мы поговорим чуть позже).

А вот классы и их поля и методы принадлежащих другим пакетам, если у них нет ни каких модификаторов доступа, уже не доступны даже по полному имени пакета и класса.

Чтобы отдохнуть от теории и понять ее получше немного попрактикуемся и рассмотрим на примере первый пункт использование класса из пакета. Создадим пакет pro.java.pkg001 и в нем два класса:

P0001

P0002

Вывод у программы следующий:

P0003

Как видно у класса Print нет ни каких модификаторов, но все же его статические метод и переменная видны из класса Main и могут им использоваться.

Очень важно заметить, что при компиляции файлов находящихся в пакетах (за исключением пакета по умолчанию), в class-файл попадает полное имя класса, то есть имя_пакета.имя_класса. Поэтому и запускать класс содержащий метод main() надо по полному имени. Кроме того, все другие пакеты и классы, используемые вашей программой должны быть доступны через classpath. Более подробно мы поговорим об этом в конце данной статьи.

Импорт классов и пакетов

Класс пакета pro.java.pkg001 может ссылаться на любой другой класс в pro.java.pkg001 по его простому имени. А поскольку классы пакета java.lang являются базовыми для языка Java, любой Java-код может ссылаться на любой класс этого пакета по его простому имени. Значит, всегда можно набирать String вместо java.lang.String. Однако по умолчанию для всех других классов нужно указывать полные имена. Поэтому, чтобы применить класс File пакета java.io, следует набрать java.io.File.

Директива import бывает двух видов. Чтобы определить отдельный класс, на который можно ссылаться по его простому имени, следом за ключевым словом import введите имя класса и точку с запятой:

import java.io.File; // Теперь можно набирать File вместо java.io.File

Чтобы импортировать целый пакет классов, введите после import имя пакета, символы .* и точку с запятой. Например, чтобы использовать класс File вместе с несколькими другими классами пакета java.io, нужно просто импортировать целый пакет:

import java.io.*; // Теперь простые имена можно применять для всех классов из java.io

Этот синтаксис импорта пакета не относится к подпакетам. Даже если я импортирую пакет java.util, мне все еще придется обращаться к классу java.util.zip.ZipInputStream по его полному имени. Если два класса, импортированные из разных пакетов, называются одинаково, к ним нельзя обращаться по простому имени; во избежание двусмысленности к обоим классам следует обращаться по их полным именам.

Начиная с версии Java 5 в язык введена еще одна форма оператора import, предназначенная для импорта статических полей и методов класса — оператор import static. Например, можно написать оператор:

import static java.lang.Math.*;

После этого все статические поля и методы класса Math можно использовать без указания имени класса. Вместо записи:

double r = Math.cos(Math.PI * alpha);

можно записать просто

double r = cos(PI * alpha);

Теперь разгаданы и другие строчки, которые были в наших исходниках, например:

import static pro.java.util.Print.*;
import static pro.java.util.Strings.*;

Подчеркнем, что оператор import вводится только для удобства программистов и слово "импортировать" не означает никаких перемещений классов.

Директивы import позволяют компилятору получить полные имена всех используемых классов, полей и методов по их коротким именам. В class-файл попадают полные имена, подстановка содержимого не происходит. Поэтому при запуске программы все используемые классы должны присутствовать в classpath.

Структура файла Java

Теперь можно описать структуру исходного файла с текстом программы на языке Java.

  • В первой строке файла может быть необязательный оператор package.
  • В следующих строках могут быть необязательные операторы import.
  • Далее идут описания классов и интерфейсов (интерфейсы будут рассмотрены позже).

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

Директив import может быть сколько угодно.

Существует еще несколько важных ограничений по файлам Java. Во-первых, каждый файл может содержать максимум один класс, объявленный как public. Такой класс предназначен для использования другими классами в других пакетах. Более подробно public классы и связанные с ними модификаторы мы рассмотрим в следующей статье. Данное ограничение по открытым классам относится только к классам верхнего уровня; как вы скоро узнаете, класс может содержать любое количество вложенных, или внутренних, классов, объявленных как public.

Второе ограничение касается имени файла в Java. Если файл Java содержит класс public, то имя файла должно представлять собой имя public класса, к которому присоединено расширение .java. Например, если Point является public классом, его исходный код должен находиться в файле Point.java.

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

Соглашение "Code Conventions" рекомендует открытый класс, если он имеется в файле, описывать первым.

Даже если ваши классы не являются public, полезно определять только по одному классу на файл и давать файлу имя класса. Для технологии Java характерно записывать исходный текст каждого класса в отдельном файле. В конце концов, компилятор всегда создает class-файл для каждого класса.

Исполнение классов в пакетах и CLASSPATH

GrabliС запуском нашей простой программы из Eclipse, что была в самом начале, нет ни каких проблем, а вот при запуске из командной строки они могут возникнуть.

Дело в том, что как я уже говорил ранее, в откомпилированный  class файл попадает полное имя класса, то есть имя_пакета.имя_класса.

Давайте запустим и посмотрим (обращаем внимание на пути файлов):

P0004

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

javap спасет отца русской демократии! Давайте посмотрим что там у нас скомпилилось?

P0005

Как видим наш класс сейчас называется pro.java.pkg001.Main, то есть наш класс содержит полное имя, включая имя пакета. И кстати обратите внимание на то как объявлен массив String[].

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

P0006

Рассмотрим откомпилированный класс Classes001.

Как видим в данном случае, когда мы не использовали пакеты, имя класса точно такое же как мы и задавали в коде программы. Именно поэтому мы могли запустить наш класс по его короткому имени.

Давайте попробуем запустить наш класс Main по полному имени и посмотрим что получиться.

 

P0007

Как видим у нас опять ошибка.

Чтобы наша программа запустилась, должно выполняться одно из следующих двух условий. Либо программа должна выполняться из каталога, расположенного непосредственно над каталогом pro, то есть в нашем случае запуск должен происходить из каталога bin, либо переменная среды CLASSPATH должна содержать путь к каталогу pro, либо параметр –classpath должен указывать путь к каталогу pro во время выполнения программы с помощью java.

P0008

P0009

CLASSPATH должен указывать путь к каталогу, где лежит пакет (верхний каталог пакета), но не на сам каталог или подкаталоги пакета.

Ну и на последок можно глянуть хорошее видео по этой теме

Пакеты (pagkages) в Java

29 июн. 2015 г.

Классы. Практика: паттерн проектирования Builder.

Паттерн проектирования – это некий шаблон который можно использовать при решении однотипных задач. По словам Кристофера Александра, «любой паттерн описывает задачу, которая снова и снова возникает в нашей работе, а также принцип ее решения, причем таким образом, что это решение можно потом использовать миллион раз, ничего не изобретая заново». Именно поэтому уже существует великое множество паттернов проектирования в ООП. И каждому программисту хорошо бы знать хотя бы некоторые из них. И вообще иметь понятие о паттернах.

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

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

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

B0001

Допустим у нас есть класс Contact представленный слева. В нем несть несколько однотипных строковых полей.

Конечно, когда мы захотим создать объект этого класса в среде разработки, то она нам может подсказать названия полей, но даже в этом случае легко ошибиться. Например, перепутать name с surname.

Паттерн Builder как раз и предназначен для уменьшения вероятности таких ошибок.

Хотя может показаться, что не стоит ради этого городить весь тот огород, что мы собираемся сделать, но ради практики почему бы и нет. И к тому же, это хорошая практика и плюс в вашу копилку знаний.

Для этого класса создание объекта будет выглядеть так:

Contact c = new Contact("Петров", "Вася", "p@v.ru", "1234", "Уфа");

Здесь я намеренно допустил ошибку, перепутав имя и фамилию местами.

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

Потом вы можете сравнить эту строчку с тем как мы будем создавать новый контакт при использовании паттерна Builder.

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

B0002

B0003

B0004

И вывод данной программы:

B0005

Сразу обратите внимание на то, что в классе Contact все поля final, а в классе ContactBuilder все поля не имеют этого модификатора. Это является общераспространённой практикой при создании паттерна Builder, хотя это можно и изменить исходя из нужд вашего приложения.

Кроме того можете увидеть по классу Classes01, что в случае применения паттерна Bulder, можно даже пропустить задание значений для некоторых полей и тогда они получат значения по умолчанию. Или же поменять порядок полей при задании их значений.

Чтобы стало все более или менее понятно, очень рекомендую посмотреть видео ниже, по мотивам которого я и написал этот пост.

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

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

Сделаем поле surname обязательным, а остальные как и было опциональными, но присвоим им наши значения по умолчанию. Для  этого нам надо изменить класс ContactBuilder и, соответственно, его применение в  Classes014.

B0006

Как видим в классе ContactBuilder мы добавили конструктор и убрали метод установки значения surname, которое теперь устанавливается в конструкторе и является final, то есть обязательным полем.

А использование ContactBuilder выглядит теперь вот так:

B0009

И вывод у программы следующий:

B0008

На этом с паттерном Builder мы не прощаемся. Мы еще встретимся с ним на практике после изучения абстрактных и внутренних классов.

Классы. Практика: строки и массивы как аргументы.

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

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

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

Далее вывод данной программы:

CP0001

Кратенько поясню данный вывод. В строке 8 основной программы создается объект str1, который содержит строку Str1. В 9 строке создается строковая переменная str2 которая так же содержит строку Str1. НО! Хотя эти строки и создаются по разному, это один и тот же объект, что видно из вывода программы. С выводом str3 и str4 все должно уже быть более мнее вам понятно. А затем при помощи метода intern() в строке 20 мы заставляем str4 указывать на тот же объект (строку) что и str1 с str2. С массивами еще интереснее. Мы создаем класс dim1, внутри которого уже формируется массив из переданных аргументов. И далее на эти аргументы мы воздействуем из Class013 через массив strArray. А затем делаем наоборот. Это возможно поскольку мы работаем со ссылочными типами.

На эти грабли частенько наступают начинающие программисты. Если что-то не понятно, то пишите комменты.

Классы. Часть 12 – передача методов как аргументы в методы и конструкторы

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

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

  • литералы
  • переменные
  • объекты
  • методы (с соответствующим аргументу возвращаемым значением)

С первыми тремя видами передаваемых в метод аргументов мы уже познакомились. Осталось познакомиться с последним – передачей методов как аргументы в методы и конструкторы.

M0001

M0002

 

Вывод у этой программы простой:

M0003

Как видим ни чего сложного тут нет. Главное чтобы методы, передаваемые как аргументы, возвращали тот же тип данных, что и аргумент. Хотя иначе компилятор даже не скомпилирует программу, а среда разработки покажет ошибку.

Если что-то не понятно, то очень советую посмотреть видео ниже:

Основы понимания методов классов

И особенно второе видео из этой серии, где как раз и говориться о том, что мы только что обсуждали…

Передача аргументов в методы

Классы. Часть 11 – раскрываем тайну System.out.println()

Этот пост можно назвать небольшой практикой, которая может показать нам, что мы уже знаем в чем же тайна System.out.println(). Мы сейчас практически повторим эту же конструкцию сами. Тут просто приведу код двух классов, которые реализуют эту конструкцию и ее применение в третьем.

N0037

N0038

N0039

В Classes011 мы просто используем класс Syst в котором определена статическая переменная (ссылка) out ссылающаяся на класс PrtStr, в котором есть метод print().

Хотя, конечно же, в классе System все это реализовано немного по другому, но принцип остается тот же.

Поле out инициализируется в нативном коде JVM. А более подробно можно почитать тут.

26 июн. 2015 г.

Классы. Часть 10 – инициализация полей класса.

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

  • Инициализация значениями по умолчанию
  • Инициализация при объявлении поля
  • Инициализация через конструктор
  • Инициализация в инициализационном блоке

Первые три способа мы уже видели, теперь рассмотрим четвертый.

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

Ну и теперь примеры…

N0034

N0035

Вывод у программы следующий:

N0036

Инициализационные блоки выполняются в порядке их следования в объявлении класса.

В этой программе продемонстрированы все четыре способа инициализации полей.

25 июн. 2015 г.

Классы. Часть 9 – модификатор static для полей и методов.

Во всех программах, которые мы до сих пор обсуждали, при объявлении метода main() использовался модификатор static. Рассмотрим действие этого модификатора для методов и полей.

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

Как уже обсуждалось, класс – это коллекция данных, хранимых в именованных полях, и кода, организованного в именованные методы, оперирующие этими данными. Поля и методы называются членами класса (members). В Java классы могут содержать другие классы. Эти классы-члены, или внутренние классы, предоставляют дополнительную функциональность которую мы обсудим чуть позже. Сейчас мы рассмотрим только поля и методы. Члены класса делятся на два различных типа: члены класса, связанные непосредственно с классом (статические), и члены экземпляра, связанные с каждым экземпляром класса (то есть с объектом). Не принимая во внимание классы-члены, мы получаем четыре типа членов:

  • Поля класса (статические поля – static fields)
  • Методы класса (статические методы – static methods)
  • Поля экземпляра (instance fields)
  • Методы экземпляра (instance methods)

Поля класса связаны с классом, в котором они определены, а не с экземпляром класса. То же касается и методов класса, как мы это уже отмечали.

Поскольку статические методы класса могут вызываться без создания объектов класса, то они не могут работать с полями и методами экземпляров класса, так как таковых может просто не существовать. Поэтому при попытке обращения из статического метода к обычному методу или полю экземпляра, компилятор выдаст ошибку. То есть надо понять, что статические поля и методы класса могут существовать и использоваться без наличия в памяти объектов данного класса. В тоже время обычные методы класса могут обращаться к статическим полям и статическим методам класса.

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

Статические поля и методы объявляются при помощи модификатора static.

На методы, объявленные как static, накладывается ряд ограничений:

  • Они могут вызывать только другие статические методы.
  • Они могут получать доступ только к статическим переменным.
  • Они ни коим образом не могут ссылаться на члены типа this или super. (Ключевое слово super связано с наследованием и будет рассмотрено чуть позже)
  • Статические методы не могут быть абстрактными
  • Статические методы переопределяются в подклассах только как статические
  • При переопределении статических методов полиморфизм не действует, ссылки всегда направляются на методы класса, а не объекта

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

Ну и теперь немного попрактикуемся… Вернемся к нашим баранам коровам…

N0029

N0030

Как видно в классе Cow мы определили статическую переменную count, а так же статический метод getCount(), который возвращает значение статической переменной count.

В классе Herd показано как можно использовать статические поля и методы класса, а так же пример, как делать не следует.

Программа генерирует следующий вывод:

N0032

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

 

Рекомендую, так же, посмотреть хорошее видео на эту тему:

Что есть static

Классы. Часть 8 – Модификатор final для переменных и полей.

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

Так же, на заметку, можно отметить, что модификатор final можно использовать и при объявлении аргументов в методе. Например:

N0033

Если аргумент а объявлен таким образом, то его невозможно изменить в методе.

 

На эту тему, я думаю, достаточно этих двух простых примеров.

Так что идем дальше!

Классы. Часть 7 – передача аргументов в метод.

Во время вызова метода в стеке создается для него фрейм (кадр или область памяти) где сохраняются все переменные используемые в методе, в том числе и те, что объявлены как аргументы. После выполнения метода этот фрейм со всеми переменными уничтожается. Именно поэтому в Java существует только одни способ передачи аргументов в метод – передача по значению.

Передаваемые значения копируются в переменные метода объявленные как аргументы. С примитивными типами все просто – аргументы метода получают их копии, поэтому метод ни как не может влиять на переменную переданную в него как аргумент, но может работать только с её копией. А вот с передачей объектов (верней сказать ссылок на объекты) как аргументы в метод все чуть хитрее. Они тоже передаются по значению, только это значение (ссылка) копируется в переменную объявленную как аргумент метода, но поскольку значение содержит ссылку на объект, то метод может изменять состояние переданного ему таким образом объекта. На саму же изначальную ссылку метод повлиять не может, он работает только с ее локальной копией. Чтобы все стало понятно давайте рассмотрим простой пример:

N0026

Вывод у программы следующий:

N0027

Как видите при вызове метода change() со значением типа int изменение переменной в методе ни как не повлияло на переменную переданную методу в качестве аргумента.

Что же касается объекта Box, то после передачи в метод change() его состояние было изменено несмотря на то что в метод была передана копия ссылки на объект. Копия ссылки все равно ссылается на тот же объект, поэтому он и был изменен. По существу произошло следующее b=box1. Но вот повлиять на саму ссылку box1 метод change() не может, что и продемонстрировано в методе chnge(), где присвоение переменой b ссылки на новый объект ни как не повлияло на ссылку box2.

В последнем варианте произошло следующее: во время передачи аргумента произошло b=box2, а затем b=new Box(). Надеюсь что все с передачей аргументов в метод понятно.

Если нет, то пишите письма и присылайте деньги комменты.

JA01

JA02

Я решил этот пример сделать более наглядным. Стоить обратить внимание на красные стрелки. В первом случае с box1 может показаться, что это та же самая ссылка передается в метод, но на самом деле это ее копия, что подтверждает пример с box2.

Из примера box2 видно, что хотя мы изменили ссылку box2 в методе chhge(), но это ни как не повлияло на ссылку box2 вне метода. Что и следовало доказать :) И ссылки и примитивные типы передаются в методы по значению.