Към съдържанието
wannacode

Свой тип

Проблем

Цените се разбъркаха

Пишеш количката на онлайн магазин. Всеки продукт има име и цена, затова ги пазиш в два масива: на индекс 0 са името и цената на първия продукт, на индекс 1 на втория.

Искаш най-скъпото най-отпред и разменяш цените:

String[] names = { "Хляб", "Мляко", "Кафе" };
double[] prices = { 1.20, 2.10, 7.50 };
// Най-скъпото отпред: разменяме първата и третата цена.
double temp = prices[0];
prices[0] = prices[2];
prices[2] = temp;
for (int i = 0; i < names.length; i++) {
System.out.println(names[i] + ": " + prices[i] + " €");
}
Изход
Хляб: 7.5 €
Мляко: 2.1 €
Кафе: 1.2 €

Хлябът вече струва 7,50 €. Цените са разменени, а имената не са. Двата масива трябва да се пипат винаги заедно, но нищо не те задължава. Добавиш ли трети масив с бройките, ще трябва да помниш три.

Идеята

Един продукт, едно място

Името и цената не са отделни неща. Те са части от един продукт. Искаме тип „продукт“, както има тип int за число и String за текст.

Такъв тип си правиш сам. Той се казва клас:

class Product {
public String name;
public double price;
public int quantity;
}

Променливите в класа са неговите полета. Всеки продукт ще има свое име, своя цена и своя бройка.

Класът е само описание: „всеки продукт има име, цена и бройка“. Продукт още няма.

public значи, че полетата се виждат и отвън, от main. Защо пишем точно това и какво е обратното, ще видим в урока „Капсулация“.

Идеята

new: от описанието към продукт

Конкретен продукт по класа се създава с new. Той се казва обект. До полетата му стигаш с точка:

Main.java
class Product {
public String name;
public double price;
public int quantity;
}
public class Main {
public static void main(String[] args) {
Product bread = new Product();
bread.name = "Хляб";
bread.price = 1.20;
bread.quantity = 2;
Product coffee = new Product();
coffee.name = "Кафе";
coffee.price = 7.50;
coffee.quantity = 1;
System.out.println(bread.name + ": " + bread.price * bread.quantity + " €");
System.out.println(coffee.name + ": " + coffee.price * coffee.quantity + " €");
}
}
Изход
Хляб: 2.4 €
Кафе: 7.5 €
  • Класът стои извън public class Main, над него. Във файла може да има няколко класа.
  • Product bread е променлива от тип Product, както int count е от тип int.
  • new Product() създава нов обект. Всеки обект има свои полета: хлябът и кафето не си пречат.
  • bread.price е „цената на хляба“. Чете се и се записва като обикновена променлива.

Сега името и цената на продукта са на едно място. Разместиш ли продукта, те вървят заедно.

Пробвай

Нищо не е попълнено

Ако създадеш продукт и не попълниш нищо, какво има в полетата?

Какво ще отпечата програмата?

Product item = new Product();
System.out.println(item.name);
System.out.println(item.price);
System.out.println(item.quantity);
Отговор

Пробвай

Кое липсва?

Какво трябва да пише на празното място, за да има нов продукт?

Product milk =  Product();
milk.name = "Мляко";
milk.price = 2.10;

Виж го

Променливата сочи към обекта

Искаш промоция на кафето и правиш „копие“, за да му смениш цената:

Product coffee = new Product();
coffee.price = 10;
Product sale = coffee;
sale.price = 8;
System.out.println(coffee.price); // 8.0
Стъпка по стъпка, с думи
  1. new прави обекта: полетата му са с начални стойности. В coffee отива стрелка към него.
  2. По стрелката от coffee: цената на обекта става 10.
  3. sale = coffee копира стрелката, не обекта. Две стрелки, един обект.
  4. По стрелката от sale: цената в същия обект става 8.
  5. coffee сочи същия обект, затова печата 8.0.

Цената на кафето също стана 8. Причината е, че променливата coffee не е самият обект. Обектът стои отделно в паметта, а coffee пази къде е той: стрелка към обекта. Такава променлива се казва референция.

sale = coffee копира стрелката, а не обекта. Сега две стрелки сочат един обект, затова промяната през sale се вижда и през coffee.

Същото видя в урока за масивите: sale = prices даде второ име на същия масив. Масивът също е обект.

Идеята

Отстъпката вече работи

В модул „Методи“ отстъпката не се получи: методът получи копие на цената, промени копието и то изчезна. С обект е друго:

static void discount(Product p, double percent) {
p.price = p.price * (100 - percent) / 100;
}
Product coffee = new Product();
coffee.price = 10;
discount(coffee, 10);
System.out.println(coffee.price);
Изход
9.0

Параметърът пак е копие, но копие на стрелката. Тя сочи същия обект, затова промяната на p.price остава и след метода.

По-точно, за любопитните

Ако методът сложи в параметъра нов обект, той мести само своето копие на стрелката. Обектът отвън не се променя:

static void reset(Product p) {
p = new Product();
p.price = 0;
}
Product coffee = new Product();
coffee.price = 10;
reset(coffee);
System.out.println(coffee.price);
Изход
10.0
Затова се казва, че Java предава всичко по стойност, а при обектите стойността е референцията.

Идеята

null: стрелка за никъде

Променлива от тип клас може да не сочи към нищо. Тогава в нея е null. Ако опиташ да прочетеш поле през нея, няма обект, от който да го прочетеш:

Product gift = null;
System.out.println(gift.name);

Програмата се компилира, но спира с грешка, когато стигне до реда:

Грешка
Exception in thread "main" java.lang.NullPointerException: Cannot read field "name" because "gift" is null

Това е най-честата грешка с обекти. Какво е „exception“ и как се хваща, ще видим в модул „Грешки и изключения“.

Внимавай с масивите от обекти. new Product[3] прави три места за стрелки, а не три продукта. Всяко е null, докато не сложиш там new Product():

Product[] cart = new Product[3];
System.out.println(cart[0] == null);
cart[0] = new Product();
cart[0].name = "Хляб";
System.out.println(cart[0].name);
Изход
true
Хляб

Пробвай

Намери грешката

Програмата трябва да отпечата „Хляб“ и „Мляко“. Вместо това печата „Мляко“ два пъти. На кой ред е грешката?

Избери реда с грешката.

Упражнение

Твоите класове

Реши задачите в редактора си. Класът стои над public class Main, а обектите се създават в main.

1. Книга. Клас Book с полета за заглавие и автор. Създай две книги и ги отпечатай.

Изход
Под игото от Иван Вазов
Тютюн от Димитър Димов

2. Кола. Клас SimpleCar с поле за скоростта. Създай кола (скоростта ѝ е 0), увеличи скоростта с 10 и я отпечатай. В урока „Поведение“ това ще стане метод на колата.

Изход
Скорост след ускоряване: 10

3. Количката. Количката от първата карта, този път с масив от три обекта Product: хляб 1,20 € (2 броя), мляко 2,10 € (1 брой), кафе 7,50 € (1 брой). Отпечатай сумата за всеки ред и общата сума. Не забравяй, че след new Product[3] в масива още няма продукти.

Изход
Хляб x2: 2.4 €
Мляко x1: 2.1 €
Кафе x1: 7.5 €
Общо: 12.0 €

4. Най-скъпото отпред. Подреди същата количка по цена, от най-скъпото към най-евтиното, с метода на мехурчето от урока за масивите. Този път разменяш цели продукти, затова името и цената вървят заедно.

Изход
Кафе: 7.5 €
Мляко: 2.1 €
Хляб: 1.2 €

Още задачи

  • Ученик. Клас Student с име и три оценки (три полета). Създай двама ученици и отпечатай средния успех на всеки.
  • Една книга, две имена. Създай книга, запиши я в две променливи и смени заглавието през втората. Отпечатай заглавието през първата. Обясни с думи защо е така.

Тест

Провери се

1. Каква е разликата между клас и обект?
2. Product sale = coffee; sale.price = 8; Какво става с цената на coffee?
3. Какво има в масива веднага след Product[] cart = new Product[3];?
4. Метод получава продукт като параметър и му сменя цената. Вижда ли се промяната след извикването?
5. Product gift = null; Какво става при gift.name?

Създаден от Иво Гергов като част от кампанията на Evo Tech за достъпно обучение по програмиране ·