Chapter 8 Class Design Notes Flashcards
Inheritance
Inheritance
is the process by which a subclass
automatically includes any public
or protected
members
of the class, including primitives
, objects
, or methods
, defined in the parent class
.
When one class inherits from a parent class, all public
and protected members
are automatically available as part of the child class.
Package-private members
are available if the child class is in the same package
as the parent class.
private members
are restricted to the class they are defined in and are never available via inheritance
. This doesn’t mean the parent class doesn’t have private members that can hold data or modify an object; it just means the child class has no direct reference to them.
- Subclass includes any
public
orprotected members
defined in the parent class Package-private members
are available if the child class is in thesame package
as the parent class.private members
are restricted to the class they are defined in and arenever available via inheritance
.
inheritance is transitive
If child class X inherits from parent class Y, which in turn inherits from a parent class Z, then class X would be considered a subclass, or descendant, of class Z. By comparison, X is a direct descendant only of class Y, and Y is a direct descendant only of class Z.
SINGLE VS. MULTIPLE INHERITANCE
Java supports single inheritance, by which a class may inherit from only one direct parent class. Java also supports multiple levels of inheritance, by which one class may extend another class, which in turn extends another class. You can have any number of levels of inheritance, allowing each descendant to gain access to its ancestor’s members.
Java does allow one exception to the single inheritance rule that you’ll see in Chapter 9—a class may implement multiple interfaces.
- Java support single inheritance
- Java also support multiple levels of inheritance.
- A class may implement multiple interfaces
How to prevent a class from being extended?
It is possible in Java to prevent a class from being extended by marking the class with the final
modifier.
If you try to define a class that inherits from a final class, then the class will fail to compile.
INHERITING OBJECT
- In Java, all classes inherit from a single class:
java.lang.Object, or Object
for short. - The key is that when Java sees you define a class that doesn’t extend another class, it automatically adds the syntax
extends java.lang.Object to the class definition.
- Object is alway no top of the inhert tree.
Defining and extending a class
public abstract class ElephantSeal extends Seal { // Methods and Variables defined here }
public
ordefault (package-private)
access modifierabstract
orfinal
keyword (optional)class
keyword (required)- Class name (required)
extends
parent class (optional)
EXTENDING A CLASS
public class Animal { private int age; protected String name; public int getAge() { return age; } public void setAge(int newAge) { age = newAge; } } public class Lion extends Animal { public void setProperties(int age, String n) { setAge(age); name = n; } public void roar() { System.out.print(name + ", age " + getAge() + ", says: Roar!"); } public static void main(String[] args) { var lion = new Lion(); lion.setProperties(3, "kion"); lion.roar(); } }
the Lion program prints the following: kion, age 3, says: Roar!
Cannot access private variable
public class Lion extends Animal { ... public void roar() { System.out.print("Lions age: "+age); // DOES NOT COMPILE } ... }
APPLYING CLASS ACCESS MODIFIERS
Top-level class
can only have public
or default (package-private)
access modifier
> [!NOTE:]
An inner class is a class defined inside of another class and is the opposite of a top-level class. In addition to public and package-private access, inner classes can also have protected and private access. We will discuss inner classes in Chapter 9.
As you might recall, a Java file can have many top-level classes but at most one public top-level class. In fact, it may have no public class at all. There’s also no requirement that the single public class be the first class in the file. One benefit of using the package-private access is that you can define many classes within the same Java file.
Inner class
can havepublic, protected, default (package-private) and private
access modifier.- java file can have many
top-level
classes but at most onepublic top-level class
. - One benefit of using the
package-private
access is that you can define many classes within the same java file.
> [!NOTE:]
For simplicity, any time you see multiple public classes or interfaces defined in the same code sample in this book, assume each class is defined in its own Java file.
test
ACCESSING THE THIS
REFERENCE
- The
this
reference refers to the current instance of the class and can be used to access any member of the class, including inherited members. - It can be used in any
instance method, constructor, and instance initializer block
. - It cannot be used when there is no implicit instance of the class, such as in a
static method or static initializer block
.
What do you think the following program prints?
public class Flamingo { private String color; public void setColor(String color) { color = color; } public static void main(String... unused) { Flamingo f = new Flamingo(); f.setColor("PINK"); System.out.println(f.color); } }
Output:
null
The assignment completes successfully within the method, but the value of the instance variable color is never modified and is null when printed in the main() method.
public void setColor(String color) { this.color = color; }
The this
reference refers to the current instance of the class and can be used to access any member of the class, including inherited members. It can be used in any instance method, constructor, and instance initializer block. It cannot be used when there is no implicit instance of the class, such as in a static method or static initializer block.
1: public class Duck { 2: private String color; 3: private int height; 4: private int length; 5: 6: public void setData(int length, int theHeight) { 7: length = this.length; // Backwards – no good! 8: height = theHeight; // Fine because a different name 9: this.color = "white"; // Fine, but this. not necessary 10: } 11: 12: public static void main(String[] args) { 13: Duck b = new Duck(); 14: b.setData(1,2); 15: System.out.print(b.length + " " + b.height + " " + b.color); 16: } }
This code compiles and prints the following:0 2 white
How do we reference the version in the parent class instead of the current class?
To achieve this, you can use the super
reference or keyword.
ex:
class Mammal { String type = "mammal"; } public class Bat extends Mammal { String type = "bat"; public String getType() { return super.type + ":" + this.type; } public static void main(String... zoo) { System.out.print(new Bat().getType()); } }
The program prints mammal:bat
What does the following program output?
1: class Insect { 2: protected int numberOfLegs = 4; 3: String label = "buggy"; 4: } 5: 6: public class Beetle extends Insect { 7: protected int numberOfLegs = 6; 8: short age = 3; 9: public void printData() { 10: System.out.print(this.label); 11: System.out.print(super.label); 12: System.out.print(this.age); 13: System.out.print(super.age); 14: System.out.print(numberOfLegs); 15: } 16: public static void main(String []n) { 17: new Beetle().printData(); 18: } 19: }
That was a trick question—this program code would not compile!
line 13 does not complie.
while this
includes current and inherited members, super
only includes inherited members.
Constructors Rules
- a constructor is a special method that
matches the name of the class
and hasno return type
. - Like method parameters, constructor
parameters
can be any valid class, array, or primitive type, including generics,but may not include var
. - A class can have multiple constructors, so long as each
constructor has a unique signature.
(constructor parameters must be distinct
.) - During compilation, java generate default no-argument constructor if a class is declared without any constructors.
- default constructor has an empty parameter list and an empty body.
Can you tell why these two are not valid constructors for the Bunny class?
public class Bunny { public bunny() { } // DOES NOT COMPILE public void Bunny() { } }
The first one doesn’t match the class name because Java is case sensitive.
The second method is a perfectly good method but is not a constructor because it has a return type.
The following does not compile:
class Bonobo { public Bonobo(var food) { // DOES NOT COMPILE } }
Like method parameters, constructor parameters can be any valid class, array, or primitive type, including generics, but may not include var
.
Constructor Overloading
A class can have multiple constructors, so long as each constructor has a unique signature
. In this case, that means the constructor parameters must be distinct. Like methods with the same name but different signatures,
declaring multiple constructors with different signatures is referred to as constructor overloading.
ex:
public class Turtle { private String name; public Turtle() { name = "John Doe"; } public Turtle(int age) {} public Turtle(long age) {} public Turtle(String newName, String... favoriteFoods) { name = newName; } }
DEFAULT CONSTRUCTOR
Every class in Java has a constructor
whether you code one or not. If you don’t include any constructors in the class, Java will create one for you without any parameters. This Java-created constructor is called the default constructor and is added anytime a class is declared without any constructors. We often refer to it as the default no-argument constructor for clarity.
It is only in the compiled file with the .class extension that it makes an appearance.
> [!NOTE:]
Having only private constructors in a class tells the compiler not to provide a default no-argument constructor.
It also prevents other classes from instantiating the class.
This is useful when a class has only static methods
or the developer wants to have full control of all calls to create new instances of the class.
Remember, static methods in the class, including a main() method, may access private members, including private constructors.
test
CALLING OVERLOADED CONSTRUCTORS WITH THIS()
public class Hamster { private String color; private int weight; public Hamster(int weight) { // First constructor this.weight = weight; color = "brown"; } public Hamster(int weight, String color) { // Second constructor this.weight = weight; this.color = color; } }
Constructors can be called only by writing new
before the name of the constructor.
Cannot call constructor like normal method.
public Hamster(int weight) { Hamster(weight, "brown"); // DOES NOT COMPILE }
When this()
is used with parentheses, Java calls another constructor on the same instance of the class.
public Hamster(int weight) { this(weight, "brown"); }
this() rule
Calling this() has one special rule you need to know. If you choose to call it, the this()
call must be the first statement in the constructor
. The side effect of this is that there can be only one call to this() in any constructor.
ex:
3: public Hamster(int weight) { 4: System.out.println("in constructor"); 5: // Set weight and default color 6: this(weight, "brown"); // DOES NOT COMPILE 7: }
Consider the following definition of the Gopher class:
public class Gopher { public Gopher(int dugHoles) { this(5); // DOES NOT COMPILE } }
The compiler is capable of detecting that this constructor is calling itself infinitely
.
Since this code can never terminate, the compiler stops and reports this as an error
.
public class Gopher { public Gopher() { this(5); // DOES NOT COMPILE } public Gopher(int dugHoles) { this(); // DOES NOT COMPILE } }
the constructors call each other, and the process continues infinitely
. Since the compiler can detect this, it reports this as an error.
THIS VS. THIS()
Despite using the same keyword, this and this() are very different.
The first, this
, refers to an instance
of the class, while the second, this()
, refers to a constructor
call within the class.
The exam may try to trick you by using both together, so make sure you know which one to use and why.
CALLING PARENT CONSTRUCTORS WITH SUPER()
public class Animal { private int age; public Animal(int age) { super(); // Refers to constructor in java.lang.Object this.age = age; } } public class Zebra extends Animal { public Zebra(int age) { super(age); // Refers to constructor in Animal } public Zebra() { this(4); // Refers to constructor in Zebra with int argument } }
super() rule
calling super()
can only be used as the first statement of the constructor
.
SUPER VS. SUPER()
- Like
this
andthis()
,super
andsuper()
are unrelated in Java. - The first,
super
, is used to reference members of the parent class, - while the second,
super()
, calls a parent constructor. - Anytime you see the super keyword on the exam, make sure it is being used properly.
Understanding Compiler Enhancements
the first line of every constructor is a call to either this()
or super()
How did these classes compile?
public class Donkey {} public class Donkey { public Donkey() {} } public class Donkey { public Donkey() { super(); } }
Java compiler automatically inserts a call to the no-argument constructor super()
if you do not explicitly call this()
or super()
as the first line of a constructor.
For example, the following three class and constructor definitions are equivalent, because the compiler will automatically convert them all to the last example:
public class Donkey {} public class Donkey { public Donkey() {} } public class Donkey { public Donkey() { super(); } }
ARE CLASSES WITH ONLY PRIVATE CONSTRUCTORS CONSIDERED FINAL?
Remember, a final class cannot be extended.
What happens if you have a class that is not marked final but only contains private constructors—can you extend the class?
The answer is “yes,” but only an inner class defined in the class itself can extend it. An inner class
is the only one that would have access to a private constructor and be able to call super(). Other top-level classes cannot extend such a class.
Don’t worry—knowing this fact is not required for the exam. We include it here for those who were curious about declaring only private constructors.
Missing a Default No-Argument Constructor
What happens if the parent class doesn’t have a no-argument constructor?
public class Mammal { public Mammal(int age) {} } public class Elephant extends Mammal { // DOES NOT COMPILE }
Since Elephant does not define any constructors, the Java compiler will attempt to insert a default no-argument constructor. As a second compile-time enhancement, it will also auto-insert a call to super() as the first line of the default no-argument constructor.
public class Elephant extends Mammal { public Elephant() { super(); // DOES NOT COMPILE } }
Fix :
public class Elephant extends Mammal { public Elephant() { super(10); } }
For example, the following class compiles because Elephant now has a no-argument constructor, albeit one defined explicitly:
public class AfricanElephant extends Elephant {}
SUPER()
ALWAYS REFERS TO THE MOST DIRECT PARENT
- A
class
may havemultiple ancestors
via inheritance. - In our previous example, AfricanElephant is a subclass of Elephant, which in turn is a subclass of Mammal.
- For constructors, though,
super()
always refers to the most direct parent. - In this example, calling super() inside the AfricanElephant class always refers to the Elephant class, and never the Mammal class.
CONSTRUCTORS AND FINAL FIELDS
public class MouseHouse { private final int volume; private final String name = "The Mouse House"; { volume = 10; } }
final static variables
must be assigned a value exactly once.- You saw this happen in the line of the declaration and in a static initializer.
-
Instance variables
markedfinal
follow similar rules. They can be assigned values in the line in which they aredeclared
or in aninstance initializer.
public class MouseHouse { private final int volume; private final String type; public MouseHouse() { this.volume = 10; type = "happy"; } }
In our MouseHouse implementation, the values for volume and type are assigned in the constructor.
Remember that the this
keyword is optional
since the instance variables are part of the class declaration, and there are no constructor parameters with the same name.
Unlike local final variables
, which are not required to have a value unless they are actually used, final instance variables
must be assigned a value.
Default values
are not used for these variables.
If they are not assigned a value in the line where they are declared or in an instance initializer
, then they must be assigned a value in the constructor
declaration.
Failure to do so will result in a compiler error on the line that declares the constructor.
- final instance variable
- must be assigned a value
- assinged a value in the line where they are declared
- instance initializer
- constructor
public class MouseHouse { private final int volume; private final String type; { this.volume = 10; } public MouseHouse(String type) { this.type = type; } public MouseHouse() { // DOES NOT COMPILE this.volume = 2; // DOES NOT COMPILE } }
- In this example, the
first constructor
that takes a String argument compiles. Although afinal instance variable
can be assigned a value only once, each constructor is considered independently in terms of assignment. - The
second constructor
does not compile for two reasons.- First, the constructor fails to set a value for the
type variable
. The compiler detects that a value is never set for type and reports an error on the line where the constructor is declared. - Second, the constructor sets a value for the volume variable, even though it was already assigned a value by the instance initializer. The compiler reports this error on the line where volume is set.
- First, the constructor fails to set a value for the
> [!NOTE]
On the exam, be wary of any instance variables
marked final
.
Make sure they are assigned a value in the line where they are declared
, in an instance initializer
, or in a constructor
.
They should be assigned a value only once, and failure to assign a value is considered a compiler error in the constructor.
Instance variables
markedfinal
- Assigned a value only once
- declaration line
- instance initializer
- constructor
- should be assigned a value only once, and failure to assign a value is considered a compiler error in the constructor
How to fix MouseHouse() constructor?
public class MouseHouse { private final int volume; private final String type; { this.volume = 10; } public MouseHouse(String type) { this.type = type; } public MouseHouse() { // DOES NOT COMPILE this.volume = 2; // DOES NOT COMPILE } }
What about final instance variables when a constructor calls another constructor in the same class? In that case, you have to follow the constructor logic pathway carefully, making sure every final instance variable is assigned a value exactly once. We can replace our previous bad constructor with the following one that does compile:
public MouseHouse() { this(null); }
This constructor does not perform any assignments to any final instance variables, but it calls the MouseHouse(String) constructor, which we observed compiles without issue. We use null here to demonstrate that the variable does not need to be an object value. We can assign a null value to final instance variables, so long as they are explicitly set.
REVIEWING CONSTRUCTOR
RULES
- The
first statement of every constructor
is a call to an overloaded constructor viathis()
, or a direct parent constructor viasuper()
. - If the first statement of a constructor is not a call to
this()
orsuper()
, then the compiler will insert ano-argument super()
as the first statement of the constructor. - Calling
this() and super()
after the first statement of a constructor results in a compiler error. - If the parent class doesn’t have a no-argument constructor, then every constructor in the child class must start with an explicit this() or super() constructor call.
- If the parent class doesn’t have a no-argument constructor and the child doesn’t define any constructors, then the child class will not compile.
- If a class only defines private constructors, then it cannot be extended by a top-level class.
- All final instance variables must be assigned a value exactly once by the end of the constructor. Any final instance variables not assigned a value will be reported as a compiler error on the line the constructor is declared.