Skip to content
wannacode

Behavior

Problem

Everyone withdraws their own way

The bank has an Account class with an owner and a balance. Money is withdrawn in two places in the program. The ATM checks that there is enough money. The card payment was written by someone else, and it does not check:

Account account = new Account("Maria", 100);
// The ATM checks that there is enough money.
if (account.balance >= 70) {
account.balance -= 70;
}
// Someone else wrote the card payment. It has no check.
account.balance -= 50;
System.out.println(account.balance);
Output
-20.0

The account is below zero. The rule "you cannot withdraw more than you have" is important, but it is written (or forgotten) separately in every place that withdraws.

The idea

Methods live with the data

The withdrawal logic is part of the account, so put it in the Account class. A method in the class, without static, is an instance method:

Main.java
class Account {
public String owner;
public double balance;
public Account(String owner, double balance) {
this.owner = owner;
this.balance = balance;
}
public void deposit(double amount) {
balance += amount;
}
public void withdraw(double amount) {
if (amount <= balance) {
balance -= amount;
} else {
System.out.println("Not enough money: " + owner);
}
}
}
public class Main {
public static void main(String[] args) {
Account account = new Account("Maria", 100);
account.withdraw(70);
account.withdraw(50);
account.deposit(20);
System.out.println(account.balance);
}
}
Output
Not enough money: Maria
50.0
  • You call the method on an object with a dot: account.withdraw(70), just like the fields.
  • Inside, balance is the balance of this account, the object before the dot. You can also write this.balance.
  • The check is written once. Anyone who withdraws through the method goes through it.

Account maria = new Account("Maria", 100);
Account peter = new Account("Peter", 40);
maria.withdraw(70);
peter.withdraw(70);
System.out.println(maria.balance + " " + peter.balance);
Step by step, in words
  1. Two accounts, two objects. Each one has its own balance.
  2. maria.withdraw(70): this gets an arrow to the object before the dot.
  3. In the method, the balance belongs to this: 100 - 70 = 30.
  4. Same method, different object: now this points to Peter.
  5. 70 is more than 40: the method prints a refusal, and Peter's balance stays the same.
  6. There is one copy of the method code. The fields belong to the object you call it on.

The card payment can no longer "forget" the check if it withdraws with withdraw. It can still write straight into the field, though. You will see how to stop that in the "Encapsulation" lesson.

The idea

Instance method or static method

The methods from the "Methods" module were static. They get everything as parameters and have no object of their own. An instance method works with the fields of the object it was called on:

static double applyDiscount(double price, double percent) {
return price * (100 - percent) / 100;
}
double price = applyDiscount(10, 10);
account.withdraw(70);

You cannot call an instance method through the class name. It is not clear which account to withdraw from:

Account.withdraw(70);
Compile error
error: non-static method withdraw(double) cannot be referenced from a static context

What exactly static means and when you need it comes in the lesson "static: what all objects share".

Try it

Whose account?

What will the program print?

Account maria = new Account("Maria", 100);
Account peter = new Account("Peter", 40);
maria.deposit(30);
peter.withdraw(10);
System.out.println(maria.balance + " " + peter.balance);
Answer

Try it

Find the bug

The class does not compile. Which line has the bug?

Pick the line with the bug.

The idea

A method can also answer

An instance method can return a result, just like the methods from the "Methods" module. A product knows its price and quantity, so it can tell you its own cost:

class Product {
// the fields and the constructor, as before
public double total() {
return price * quantity;
}
}
Product[] cart = {
new Product("Bread", 1.20, 2),
new Product("Milk", 2.10, 1),
new Product("Coffee", 7.50, 1)
};
double sum = 0;
for (Product p : cart) {
sum += p.total();
}
System.out.println("Total: €" + sum);
Output
Total: €12.0

Compare this with the first lesson of the module: there, the sum was computed in main from the fields. Now main only asks each product. If a bulk discount is added tomorrow, you change only total().

The idea

What does the object look like as text?

If you print a product directly, you get something useless:

Product coffee = new Product("Coffee", 7.50, 1);
System.out.println(coffee);
Output
Product@1b6d3586

Java prints the class name and a number that is different on every run. It tells you nothing about the product.

The class can say how it should be shown. The method is called toString(), and it returns text:

class Product {
// the fields and the constructor, as before
public double total() {
return price * quantity;
}
@Override
public String toString() {
return name + " x" + quantity + ": €" + total();
}
}
Product coffee = new Product("Coffee", 7.50, 1);
Product bread = new Product("Bread", 1.20, 2);
System.out.println(coffee);
System.out.println("In the cart: " + bread);
Output
Coffee x1: €7.5
In the cart: Bread x2: €2.4

You do not call it yourself. System.out.println(coffee) and "In the cart: " + bread call it for you when the object has to become text.

For now, write @Override before the method exactly like this. What it means comes in the "Inheritance and polymorphism" module. The name must be exactly toString, with a lowercase t and an uppercase S.

Try it

A book in a sentence

What will the program print?

class Book {
public String title;
public String author;
public Book(String title, String author) {
this.title = title;
this.author = author;
}
@Override
public String toString() {
return title + " by " + author;
}
}
Book book = new Book("Tobacco", "Dimitar Dimov");
System.out.println("Reading: " + book);
Answer

Exercise

Objects that do something

Solve the tasks in your editor. Take the classes from the earlier lessons and add methods to them.

  1. Person: a method introduce() that prints the name and the age.
  2. SimpleCar: a method accelerate() that increases the speed by 10. Now main does not touch the speed directly.
  3. DanceMove: a method perform() that "performs" the move.
  4. Book: a method read() that prints "Reading [title] by [author]".

Create one object of each class and call its methods:

Output
Hi, I'm Maria and I'm 34 years old.
Speed after accelerating: 10
Performing moonwalk for 5 seconds
Reading Under the Yoke by Ivan Vazov

Exercise

A car with fuel

A Car class with a brand, a speed, fuel (in liters) and a mileage in kilometers. The constructor gets the brand and the fuel. The speed and the mileage start at 0.

  • accelerate() increases the speed by 10.
  • refuel(double liters) adds fuel, but the tank holds at most 50 liters.
  • drive(int distance) uses 6 liters per 100 km and adds the distance to the mileage. If there is not enough fuel, it prints a message and the car does not move.
  • toString() returns the brand, the mileage and the fuel.
Main
Car car = new Car("Toyota", 20);
car.accelerate();
car.accelerate();
System.out.println("Speed: " + car.speed);
car.drive(100);
System.out.println(car);
car.drive(300);
car.refuel(40);
System.out.println(car);
car.drive(300);
System.out.println(car);
Output
Speed: 20
Toyota: 100 km, 14.0 L
Not enough fuel for 300 km
Toyota: 100 km, 50.0 L
Toyota: 400 km, 32.0 L
Hint: distance * 6 / 100.0. With 100 instead of 100.0, the division is integer division, and for 30 km you get 1 liter instead of 1.8.

More tasks

  • Discount. Add a method applyDiscount(double percent) to Product that lowers the price of the product. Compare it with the static method from the "Methods" module that returned a new price.
  • An account with a history. Add an operation counter to Account: every deposit and every successful withdrawal increases it by 1. A method toString() returns the owner, the balance and the number of operations.

Test

Check yourself

1. What does maria.deposit(30) do?
2. What can an instance method see that a static method cannot?
3. Why does Account.withdraw(70); not compile?
4. When is toString() called on its own?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·