One of the most basic examples of this is the Person, Student, Teacher lesson which I will go over briefly.
To start, we'll create the Person class:
public class Person {
private final String name;
private int age;
public Person(final String name, final int age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
public void setAge(final int age) {
this.age = age;
}
@Override
public String toString() {
return "Name: " + getName() + ", Age: " + getAge();
}
}
A person has a name and an age. For the sake of this tutorial, a Person's name cannot change, but their age can. As an added foot-note (you don't have to do this), I added a toString to the Person class, so if you ever want to have access to that you can.
Now, a Person is pretty bland and very ambiguous. Anyone can be a Person, so let's create two more classes that will extend Person (they will essentially "be" Person's).
Student class:
public class Student extends Person {
private final int id;
public Student(final int id) {
super("Default", 50);
this.id = id;
}
public Student(String name, int age, final int id) {
super(name, age);
this.id = id;
}
public int getId() {
return this.id;
}
@Override
public String toString() {
return super.toString() + ", Id: " + getId();
}
}
In the class above, we have Student extend Person. This allows for the name and age fields to be inherited so we don't need to re-define them inside the Student class. Person is Students 'parent/super class', so the keyword 'super' is referring to the Person class. After we create the Teacher class, I will provide example usage of these three classes.
Teacher class:
public class Teacher extends Person {
private final String className;
public Teacher(final String className) {
super("Default", 50);
this.className = className;
}
public Teacher(String name, int age, final String className) {
super(name, age);
this.className = className;
}
public String getClassName() {
return this.className;
}
@Override
public String toString() {
return super.toString() + ", Class Name: " + getClassName();
}
}
Now that we have all three classes defined, we can use them as we please.
Example usage:
import static java.lang.System.out;
public class Test {
public static void main(String[] args) {
Person p = new Person("Bob", 20);
Student s = new Student("Bob2", 25, 128396);
Teacher t = new Teacher("Bob3", 40, "OSBot");
out.println(p);
out.println(s);
out.println(t);
/*
The following output is recorded:
Name: Bob, Age: 20
Name: Bob2, Age: 25, Id: 128396
Name: Bob3, Age: 40, Class Name: OSBot
*/
}
}
Now you know basic class hierarchy. You can have as many parent and child classes as you want so long as what you're writing makes sense for what you're trying to accomplish.
Per request:
Java is always pass-by-value. Here's a short example of what this means:
Take class Cat:
public class Cat {
private String name;
public Cat(final String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setName(final String name) {
this.name = name;
}
@Override
public String toString() {
return getName();
}
}
Now, take class ReferenceTutorial:
public class ReferenceTutorial {
public static void main(String[] args) {
Cat cat = new Cat("Bob"); //LINE 1
foo(cat);
System.out.println(cat); //LINE 3
Cat cat2 = new Cat("Bob"); //LINE 9
foo2(cat2);
System.out.println(cat2); //LINE 10
}
public static void foo(Cat c) {
c = new Cat("Bob2"); //LINE 2
System.out.println(c); //LINE 4
}
public static void foo2(Cat c) {
c.setName("Bob3"); //LINE 5
c = new Cat("Bob4"); //LINE 6
c.setName("Bob5"); //LINE 7
System.out.println(c); //LINE 8
}
}
On LINE 1, we create a pointer to a Cat called cat. This Cat has a memory address. If we pass that address into a method, such as foo() and then on LINE 2 we set that address to be a new Cat (which has a separate address), the name of the passed in Cat will not change because the two fields have different memory addresses.
LINE 3 and LINE 4 will print out "Bob" and "Bob2" respectively. Why does LINE 4 print out "Bob2"? Because when we called println on the field c, we are still in the scope of the method and it will be referencing the new memory address we created.
Now on LINE 5, we are referencing the original memory address we passed in by calling c.setName(). This will actually change the name of the Cat we created in the main method called cat2.
Now on LINE 6, we create a new pointer that points to an entirely new memory address and then set that cat's name and print it out (remember, we created an entirely new address, so the Cat (pointer) we created on LINE 9 will not have its name changed twice and it will remain as "Bob3").
LINE 8 and LINE 10 will print out "Bob5" and "Bob3" respectively. Why does LINE 10 print out "Bob3"? Because we changed the name of cat2 on LINE 5 (we were referencing the original memory address).
The recorded output will be:
/*
Bob2
Bob
Bob5
Bob3
*/
This should give you a general idea of what pass-by-value is. If you have any further questions, feel free to post them.