Lesson 11Phase 2: Objects
Creating classes and objects
Writing your first class with fields, making objects with new, and reading and writing their state.
By BasithUpdated 60 min
- class definition
- fields
- new
- instance
- dot notation
- default values
So far objects have been an idea: a blueprint, and the cookies pressed from it. Now you build them. You will write a class of your own from a blank file, make objects from it, and look inside them in the memory view as their fields change.
- Write a class of your own with fields, from nothing.
- Create objects, instances of your class, with
new, and read and write their fields with the dot. - Predict what a field holds before you have set it, using the default-value rules.
- Explain why a variable holds a reference to an object, and what happens when you copy that variable.
- Keep many objects in one array and walk them with a loop.
new. You also have variables, types and arrays. That is all you need. Nothing on this page does more than read and set fields; methods that give objects behavior come in the next lesson.Defining a class with fields#
Pick something concrete: a student. A student hasa name, an age and a grade-point average. Those three things are the student's state, the data that belongs to it. In a class, each piece of state is written as a field: a variable that lives inside the object instead of inside a method.
public class Main { public static void main(String[] args) { System.out.println("The Student class is defined below."); }} class Student { String name; int age; double gpa;}Read the Student block carefully, because the shape is the whole point. The keyword class says “I am about to describe a kind of thing”. Then comes a name, Student, with a capital letter by convention. Then, inside the braces, three field declarations. Each one is a type followed by a name, just like declaring a variable, but with no value attached and no method around it. That is a field.
The program prints only its one line. Defining a class builds no students at all: main never asks for one, so none exists. The class just sits there, ready to be used.
Key ideaA field is a variable that belongs to the object. Every
Studentobject you build carries its ownname, its ownageand its owngpa.
main. A variable declared inside a method is called a local variable: it exists only while that method runs. A field is different. It lives as long as its object lives: when the object is built, its fields come into being, and while the object exists, the fields hold its state. The declaration looks the same; the home and the lifetime are different.__init__, and you can add a new one to a single object later. A Java object has exactly the fields its class declares, no more and no fewer, and each field holds one type.Making objects and setting their fields#
The class on its own builds nothing. To get a real student you use new, exactly as in why objects. Then you reach into the fresh object with the dot and set each field.
public class Main { public static void main(String[] args) { Student s1 = new Student(); s1.name = "Mara"; s1.age = 19; s1.gpa = 3.6; Student s2 = new Student(); s2.name = "Theo"; s2.age = 21; s2.gpa = 3.9; System.out.println(s1.name + ", age " + s1.age + ", gpa " + s1.gpa); System.out.println(s2.name + ", age " + s2.age + ", gpa " + s2.gpa); }} class Student { String name; int age; double gpa;}Press Run and step through it slowly, watching the memory view under the code. The first new Student() adds a Student object there, with every field at a starting value, and the three lines after it fill those fields in. The second new Student() adds a completely separate object, and filling its fields never touches the first one. Each of them is an instance of Student: two cookies from one cutter.
The dot does two different jobs depending on which side of the = it sits on. On the left, s1.age = 19 writes into the object: it sets the age field belonging to s1. On the right, inside the println, s1.age reads the same field back out. Underneath, the dot always means the same thing: reach into this one object and point at one field inside it.
new Student()Student object and hand back a reference to it.s1.name = "Mara"name field of the object s1 points at.s1.name (in an expression)name field of that object.s2 after its own news1's fields never changes it.Student starts with a capital and s1 does not. Java does not force this, but everyone follows it: class names are Capitalized, and variables, including those holding objects, start in lowercase. When you read code later, the capital letter is your instant signal “this is a type, a blueprint” rather than “this is one thing”.What a field holds before you set it#
Here is a question that catches people. The moment new Student() runs, before you have assigned anything, what is in name? What is in age? They are not garbage, and they are not missing. Java gives every field a fixed default value the moment the object is built. Run this and read each line of output.
public class Main { public static void main(String[] args) { Student s = new Student(); System.out.println("name: " + s.name); System.out.println("age: " + s.age); System.out.println("gpa: " + s.gpa); System.out.println("enrolled: " + s.enrolled); }} class Student { String name; int age; double gpa; boolean enrolled;}Step through and look at the memory view before any printing happens: the fields already hold values. The whole-number field age starts at 0. The decimal field gpa starts at 0.0. The boolean enrolled starts at false. And name, which is a String, starts at null.
That last one deserves a moment. nullis a special value that means “points at no object at all”. A Stringfield is not a number or a true-or-false value; it refers to a text object somewhere, and until you give it one, it points at nothing. So its default is the “nothing here yet” value, null, and printing it shows the word null.
The rule is for fields (and array slots). A local variable inside main gets no default at all. Declare int count; and print it before giving it a value, and the program does not even compile: javac stops with variable count might not have been initialized.
Student s1 = new Student();, then Student s2 = new Student();, then s1.age = 19;. What does System.out.println(s2.age); print?name that is null looks harmless until you use it as if it were real text. Asking a null for its length or its letters stops the program with a NullPointerException, Java's complaint that there was no object to ask. The cure is simple: set your fields to real values before you lean on them.s.name, s.age and s.gpa one line at a time works, but it is easy to forget one and leave a null or a 0 behind. Constructors let you fill every field at the moment of new, in one step, so an object is never born half-built. And encapsulation will later stop outside code from writing nonsense, such as a negative age, into your fields at all. You do not need either yet.The variable does not hold the object#
Now the idea that quietly runs underneath everything above. When you write Student s1 = new Student(), it is tempting to picture the whole student living inside s1, the way a number lives inside an int variable. That picture is wrong, and getting it right now saves you real confusion later.
The object, with its name and age and gpa, lives in memory on its own. The variable s1 does not contain it. s1 holds a referenceto it, which you can think of as the object's home address. The variable holds the address; the object lives at that address. When you write s1.age, Java follows the address to the object and reads the field there.
This sounds like hair-splitting until you copy one variable into another. Watch what happens when a program does exactly that.
public class Main { public static void main(String[] args) { Student a = new Student(); a.name = "Mara"; a.age = 19; Student b = a; b.age = 20; System.out.println(a.name + " is " + a.age); System.out.println(b.name + " is " + b.age); }} class Student { String name; int age;}Step through it and keep your eye on the memory view. new runs once, so there is only ever one Student object there. The line Student b = a does not build a second student. It copies the address in a into b, so now a and b both point at the same object: two names for one thing. So when b.age = 20 runs and you then read a.age, you get 20 as well. Both lines print Mara is 20, because both variables are looking at the very same student.
Key ideaA variable of a class type holds a reference, an address, not the object. Copy the variable and you copy the address, so both names now reach the one object behind it.
public class Main { public static void main(String[] args) { Student a = new Student(); a.age = 19; Student b = new Student(); b.age = 19; Student c = a; c.age = 30; b.age = b.age + 1; System.out.println(a.age + " " + b.age + " " + c.age); }} class Student { String name; int age;}Two objects are built here, and one of them gets a second name. What does the last line print?
It prints 30 20 30. new ran twice, so there are two students. c = a copied an address, so c is a second name for the first student, and setting c.age to 30 changes the age a sees too. b is the other student, untouched by all of that, and its own age goes from 19 to 20. Count the news to count the objects; every other assignment of an object variable only copies an address.
int x = 19; int y = x; y = 20; leaves x at 19, because y got its own copy of the number. Objects work differently: the value being copied is the address, not the student, so both variables end up at the same student. This is the single biggest surprise for newcomers, and now you have seen it before it bit you.Student object?Many objects in one array#
An object can go anywhere a value can, including into an array. A Student[] is an array whose slots hold references to Student objects. This is the clean answer to the parallel arrays in why objects: one array, where each slot leads to a whole student with all of its fields together.
public class Main { public static void main(String[] args) { Student[] roster = new Student[3]; System.out.println(roster[0]); roster[0] = new Student(); roster[0].name = "Mara"; roster[0].age = 19; roster[1] = new Student(); roster[1].name = "Theo"; roster[1].age = 21; roster[2] = new Student(); roster[2].name = "Ines"; roster[2].age = 20; int total = 0; for (Student s : roster) { System.out.println(s.name + " is " + s.age); total = total + s.age; } System.out.println("Average age: " + total / roster.length); }} class Student { String name; int age;}new Student[3] makes the array, not the students. Its three slots are references, and like any reference they start as null, which is what the first line prints. Each roster[i] = new Student() puts a real student in a slot, and roster[0].name reaches through the slot into that student. The for-each loop then hands you each student in turn as s. Step through it and watch three separate Student objects appear in the memory view, one for each slot.
This fixes the trouble with parallel arrays. A student's name and age travel together, so sorting or removing students can never pair a name with the wrong age. And a field added to Student once, such as gpa, is there for every student in the array.
new Student[3] does not run new Student() for you. Use a slot before you fill it, as in roster[0].name = "Mara"; on a fresh array, and the program stops with a NullPointerException, because there is no student in that slot to reach into.Biscuit and then Mochi. Careful: a slot must hold a pet before you can set its name.Tap a line, then tap where it should go. Or use the arrows.
Book class yourself, with three fields: title, author and pages. Then, in main, build the two books described in the comments, print one line for each in the format shown, and print their total number of pages.Try it
Answer each one in your head or on paper first, then open the answer.
Book class that has a title, a number of pages and a price. Use sensible Java types.Student class with String name; int age; double gpa;, a fresh object is made with Student s = new Student(); and nothing else. What do s.name, s.age and s.gpa hold right now?s1.gpa = 3.6; and then later System.out.println(s1.gpa);, the same text s1.gpa appears twice. Is it doing the same job both times?Student b = a; and b.age = 20;, why does a.age also read 20?Student[] group = new Student[4];, how many Student objects exist, and what does group[2] hold?Practice
Work through these on your own. Each one builds on this page.
Car class with the fields String make;, String model; and int year;. In main, build two different cars with new, set all three fields on each, and print a line describing each car. Confirm the two cars are independent.boolean electric; field to your Car, but do not set it on one of the cars. Print that car's electric field. Before you run it, write down what you expect, then check whether you were right and say why.a, set its fields, then write Car b = a; and change b.year. Print a.year and b.year. Then, separately, build a truly second car with its own new Car() and show that changing one of those does not affect the other. In one or two sentences, explain the difference between the two situations.Car[] array. Then write a loop that finds the oldest car (the smallest year) and prints its make and model. Your loop should still work if you add a fourth car to the array.Further reading#
- Creating Classes, the dev.java tutorial on declaring a class and its fields
- Creating and Using Objects, the dev.java tutorial on new, references and what happens to objects nothing points at
- Initial values of variables in the Java Language Specification, section 4.12.5, the official list of default values
Spotted a mistake, or a part that did not make sense? Report it by email