What Is an Object?
Objects are key to understanding object-oriented technology. Look around right now and you’ll find many examples of real-world objects: your dog, your desk, your television set, your bicycle.
Real-world objects share two characteristics: They all have state and behavior. Dogs have state (name, color, breed, hungry) and behavior (barking, fetching, wagging tail). Bicycles also have state (current gear, current pedal cadence, current speed) and behavior (changing gear, changing pedal cadence, applying brakes). Identifying the state and behavior for real-world objects is a great way to begin thinking in terms of object-oriented programming.
Take a minute right now to observe the real-world objects that are in your immediate area. For each object that you see, ask yourself two questions: «What possible states can this object be in?» and «What possible behavior can this object perform?». Make sure to write down your observations. As you do, you’ll notice that real-world objects vary in complexity; your desktop lamp may have only two possible states (on and off) and two possible behaviors (turn on, turn off), but your desktop radio might have additional states (on, off, current volume, current station) and behavior (turn on, turn off, increase volume, decrease volume, seek, scan, and tune). You may also notice that some objects, in turn, will also contain other objects. These real-world observations all translate into the world of object-oriented programming.
Software objects are conceptually similar to real-world objects: they too consist of state and related behavior. An object stores its state in fields (variables in some programming languages) and exposes its behavior through methods (functions in some programming languages). Methods operate on an object’s internal state and serve as the primary mechanism for object-to-object communication. Hiding internal state and requiring all interaction to be performed through an object’s methods is known as data encapsulation a fundamental principle of object-oriented programming.
Consider a bicycle, for example:
A bicycle modeled as a software object.
By attributing state (current speed, current pedal cadence, and current gear) and providing methods for changing that state, the object remains in control of how the outside world is allowed to use it. For example, if the bicycle only has 6 gears, a method to change gears could reject any value that is less than 1 or greater than 6.
Bundling code into individual software objects provides a number of benefits, including:
- Modularity: The source code for an object can be written and maintained independently of the source code for other objects. Once created, an object can be easily passed around inside the system.
- Information-hiding: By interacting only with an object’s methods, the details of its internal implementation remain hidden from the outside world.
- Code re-use: If an object already exists (perhaps written by another software developer), you can use that object in your program. This allows specialists to implement/test/debug complex, task-specific objects, which you can then trust to run in your own code.
- Pluggability and debugging ease: If a particular object turns out to be problematic, you can simply remove it from your application and plug in a different object as its replacement. This is analogous to fixing mechanical problems in the real world. If a bolt breaks, you replace it, not the entire machine.
Классы. Объектно-ориентированное программирование
Java является объектно-ориентированным языком, поэтому такие понятия как «класс» и «объект» играют в нем ключевую роль. Любую программу на Java можно представить как набор взаимодействующих между собой объектов.
Шаблоном или описанием объекта является класс , а объект представляет экземпляр этого класса. Можно еще провести следующую аналогию. У нас у всех есть некоторое представление о человеке — наличие двух рук, двух ног, головы, туловища и т.д. Есть некоторый шаблон — этот шаблон можно назвать классом. Реально же существующий человек (фактически экземпляр данного класса) является объектом этого класса.
Класс определяется с помощью ключевого слова сlass :
В данном случае класс называется Person. После названия класса идут фигурные скобки, между которыми помещается тело класса — то есть его поля и методы.
Любой объект может обладать двумя основными характеристиками: состояние — некоторые данные, которые хранит объект, и поведение — действия, которые может совершать объект.
Для хранения состояния объекта в классе применяются поля или переменные класса. Для определения поведения объекта в классе применяются методы. Например, класс Person, который представляет человека, мог бы иметь следующее определение:
В классе Person определены два поля: name представляет имя человека, а age — его возраст. И также определен метод displayInfo, который ничего не возвращает и просто выводит эти данные на консоль.
Теперь используем данный класс. Для этого определим следующую программу:
public class Program < public static void main(String[] args) < Person tom; >> class Person < String name; // имя int age; // возраст void displayInfo()< System.out.printf("Name: %s \tAge: %d\n", name, age); >>
Как правило, классы определяются в разных файлах. В данном случае для простоты мы определяем два класса в одном файле. Стоит отметить, что в этом случае только один класс может иметь модификатор public (в данном случае это класс Program), а сам файл кода должен называться по имени этого класса, то есть в данном случае файл должен называться Program.java.
Класс представляет новый тип, поэтому мы можем определять переменные, которые представляют данный тип. Так, здесь в методе main определена переменная tom , которая представляет класс Person. Но пока эта переменная не указывает ни на какой объект и по умолчанию она имеет значение null . По большому счету мы ее пока не можем использовать, поэтому вначале необходимо создать объект класса Person.
Конструкторы
Кроме обычных методов классы могут определять специальные методы, которые называются конструкторами . Конструкторы вызываются при создании нового объекта данного класса. Конструкторы выполняют инициализацию объекта.
Если в классе не определено ни одного конструктора, то для этого класса автоматически создается конструктор без параметров.
Выше определенный класс Person не имеет никаких конструкторов. Поэтому для него автоматически создается конструктор по умолчанию, который мы можем использовать для создания объекта Person. В частности, создадим один объект:
public class Program < public static void main(String[] args) < Person tom = new Person(); // создание объекта tom.displayInfo(); // изменяем имя и возраст tom.name = "Tom"; tom.age = 34; tom.displayInfo(); >> class Person < String name; // имя int age; // возраст void displayInfo()< System.out.printf("Name: %s \tAge: %d\n", name, age); >>
Для создания объекта Person используется выражение new Person() . Оператор new выделяет память для объекта Person. И затем вызывается конструктор по умолчанию, который не принимает никаких параметров. В итоге после выполнения данного выражения в памяти будет выделен участок, где будут храниться все данные объекта Person. А переменная tom получит ссылку на созданный объект.
Если конструктор не инициализирует значения переменных объекта, то они получают значения по умолчанию. Для переменных числовых типов это число 0, а для типа string и классов — это значение null (то есть фактически отсутствие значения).
После создания объекта мы можем обратиться к переменным объекта Person через переменную tom и установить или получить их значения, например, tom.name = «Tom» .
В итоге мы увидим на консоли:
Name: null Age: 0 Name: Tom Age: 34
Если необходимо, чтобы при создании объекта производилась какая-то логика, например, чтобы поля класса получали какие-то определенные значения, то можно определить в классе свои конструкторы. Например:
public class Program < public static void main(String[] args) < Person bob = new Person(); // вызов первого конструктора без параметров bob.displayInfo(); Person tom = new Person("Tom"); // вызов второго конструктора с одним параметром tom.displayInfo(); Person sam = new Person("Sam", 25); // вызов третьего конструктора с двумя параметрами sam.displayInfo(); >> class Person < String name; // имя int age; // возраст Person() < name = "Undefined"; age = 18; >Person(String n) < name = n; age = 18; >Person(String n, int a) < name = n; age = a; >void displayInfo() < System.out.printf("Name: %s \tAge: %d\n", name, age); >>
Теперь в классе определено три коструктора, каждый из которых принимает различное количество параметров и устанавливает значения полей класса.
Консольный вывод программы:
Name: Undefined Age: 18 Name: Tom Age: 18 Name: Sam Age: 25
Ключевое слово this
Ключевое слово this представляет ссылку на текущий экземпляр класса. Через это ключевое слово мы можем обращаться к переменным, методам объекта, а также вызывать его конструкторы. Например:
public class Program < public static void main(String[] args) < Person undef = new Person(); undef.displayInfo(); Person tom = new Person("Tom"); tom.displayInfo(); Person sam = new Person("Sam", 25); sam.displayInfo(); >> class Person < String name; // имя int age; // возраст Person() < this("Undefined", 18); >Person(String name) < this(name, 18); >Person(String name, int age) < this.name = name; this.age = age; >void displayInfo() < System.out.printf("Name: %s \tAge: %d\n", name, age); >>
В третьем конструкторе параметры называются так же, как и поля класса. И чтобы разграничить поля и параметры, применяется ключевое слово this:
Так, в данном случае указываем, что значение параметра name присваивается полю name.
Кроме того, у нас три конструктора, которые выполняют идентичные действия: устанавливают поля name и age. Чтобы избежать повторов, с помощью this можно вызвать один из конструкторов класса и передать для его параметров необходимые значения:
В итоге результат программы будет тот же, что и в предыдущем примере.
Инициализаторы
Кроме конструктора начальную инициализацию объекта вполне можно было проводить с помощью инициализатора объекта. Инициализатор выполняется до любого конструктора. То есть в инициализатор мы можем поместить код, общий для всех конструкторов:
public class Program < public static void main(String[] args) < Person undef = new Person(); undef.displayInfo(); Person tom = new Person("Tom"); tom.displayInfo(); >> class Person < String name; // имя int age; // возраст /*начало блока инициализатора*/ < name = "Undefined"; age = 18; >/*конец блока инициализатора*/ Person() < >Person(String name) < this.name = name; >Person(String name, int age) < this.name = name; this.age = age; >void displayInfo() < System.out.printf("Name: %s \tAge: %d\n", name, age); >>
Name: Undefined Age: 18 Name: Tom Age: 18