class StudentTest { public static void main(String[] args) { Person bob = new Student("Bob", 17, 3.7f); Person jane = new Person("Jane", 21); //Object arrays can hold anything - well anything that we can refer to. Object[] allTheStuff = new Object[10]; allTheStuff[0] = bob; //Autoboxing allows for a primitive like an int to be placed into an Object //(in this case, the Integer class) automatically without us saying: //allTheStuff[1] = new Integer(7); //These two lines are equivalent, however. allTheStuff[1] = 7; allTheStuff[2] = jane; //Auto-unboxing occurs as well, thanks to the cast to int. //This is equivalent to the code: // int x = ((Integer)allTheStuff[1]).intValue(); int x = (int)allTheStuff[1]; //Displays 7 System.out.println(x); //Calls toString on the Student, even though its compile-time type was Person System.out.println(allTheStuff[0]); //Calls toString on the Person System.out.println(allTheStuff[2]); } } class Student extends Person { private float qpa; public Student(String name, int age, float qpa) { super(name, age); this.qpa = qpa; } //Overrides Person's toString to be more specific to a Student. public String toString() { //we invoke the superclass toString method (from Person) as part of it return super.toString() + ", QPA: " + qpa; } public float getQPA() { return qpa; } public void setQPA(float qpa) { this.qpa = qpa; } } class Person //implicitly extends Object { private int age; private String name; public Person(String name, int age) { this.name = name; this.age = age; } //No-arg constructor - we have to make this if we want to create someone with // new Person(). If we don't, and we want to force them to use a parameterized // constructor, just omit this constructor. public Person() { } public int getAge() { return age; } public String getName() { return name; } public void setAge(int age) { this.age = age; } public void setName(String name) { this.name = name; } //Overrides Object's toString method public String toString() { return "Name: " + name + ", Age: " + age ; } }