If same attributes - use extends (inherit the attributes and methods of the base class) - avoid having lots of repeated code

protected - can't be affect by outside modifiers

A constructor is what initializes the values of the attributes of a class when a new object of the class is created

for subclass? (same attributes) just use the constructor for the super class

Hack 1

In your own notebook, make any class with 2 attributes and 0 methods. Create a 2 argument constructor for that class. This will be your superclass. Now, create a subclass that extends from the superclass you made. Create 1 additional attribute in your subclass that was not present in the superclass.

Then, create a constructor for the subclass that uses the superclass constructor with the super keyword, and then adds an additional assignment for the third attribute.

public class Artist {
    protected String name;
    protected double age;
    
    // Constructor for the attributes present in the superclass
    public Artist(String name, double age) {
        this.name = name;
        this.age = age;
    }
    
    public void sing () {
        System.out.println("Singing...");
    }
    
    public void dance () {
        System.out.println("Dancing...");
    }
    
    public void rap () {
        System.out.println("Rapping...");
    }
    
}
public class KpopArtist extends Artist {
    // Additional attribute not present in the superclass
    protected String rapName; 
    
    // Constructor for Subclass
    public KpopArtist(String name, double age, String rapName) {
        // use the Superclass constructor for the shared attributes through the keyword "super"
        super(name, age);
        //not in Superclass, add this separately in the constructor
        this.rapName = rapName;
    }
    
    // We use override to change the functionality in the subclass of an existing method in the superclass
    @Override
    public void sing () {
        System.out.println("Singing in Korean... Good boy gone bad...");
    }
    
    // take all of the functionality of the superclass method, and then add on to it
    public void rap () {
        super.rap();
        System.out.println(rapName);
    }
    
    public static void main(String[] args) {
        // 5 argument constructor
        KpopArtist yeonjun = new KpopArtist("Yeonjun", 23, "Lonely Boy"); 
        // Example of late binding
        Artist artist = new KpopArtist("Yeonjun", 23, "Puma");
        // can still use the methods from the child class, even though we didn't mention them in the subclass!
        yeonjun.dance();
        // Using the overridden method
        yeonjun.sing();
        // Using the method we added on to
        yeonjun.rap();
        artist.rap();
    }
    
    
}
KpopArtist.main(null);
Dancing...
Singing in Korean... Good boy gone bad...
Rapping...
Lonely Boy
Rapping...
Puma

When a method in a subclass has the same name, same parameters or signature, and same return type (or sub-type) as a method in its super-class, then the method in the subclass will override the method in the super-class.

Hack 2

Add a method to the superclass you created before. This method should be very general; it should only have functionality that you know for sure will be needed in almost every single subclass. In your subclass, override this method. Remember that overriding the method will give your subclass the specific functionality it needs from that method.

//existing method in superclass
    
    public void sing () {
        System.out.println("Singing...");
    }
    
    public void dance () {
        System.out.println("Dancing...");
    }
    
    public void rap () {
        System.out.println("Rapping...");
    }
    
// We use override to change the functionality in the subclass of an existing method in the superclass
    @Override
    public void sing () {
        System.out.println("Singing in Korean... Good boy gone bad..."); //title of song of their album
    }
|       @Override
static methods cannot be annotated with @Override

super keyword - to use constructors and methods in the superclass in a child class

Polymorphism means "many forms". It means that we do one thing in many ways through inheritance.

Early binding has to do with when the compiler decides the method to be called. Relate this to Static Polymorphism or method overloading.

Late binding has to do with when the method is decided at runtime. Relate this to Runtime Polymorphism or method overriding.

Hack 3

Create another subclass from your original superclass. Now, implement method overloading in this subclass. Remember that this means having two methods with the same name, but with different arguments. The method you are using for method overloading doesn't have to exist in the superclass. This will implement Static Polymorphism.

Next, override the method in your superclass in your new subclass. Make sure it has different functionality than your method in the other subclass. This will implement Runtime Polymorphism.

public class CpopArtist extends Artist {
    
    public CpopArtist (String name, double age) {
        super(name, age);
    }
    
    @Override
    public void sing () {
        System.out.println("Singing in Chinese... there's a hundred ways...");
    }
    
    public void compose (boolean a) {  // input a boolean value instead of int
        System.out.println("Composing melody? " + a);
    }
    
    public void compose (boolean a, boolean b) {
        System.out.println("Composing melody? " + a + " Writing lyrics? " + b);
    }
    
        
    public static void main(String[] args) {
        // 4 superclass argument constructor
        CpopArtist jackson = new CpopArtist("Jackson Wang", 28);
        // Using the overridden method
        jackson.sing();
        // Using the overloaded method
        jackson.compose(true);
        jackson.compose(true, true);

    }
}
CpopArtist.main(null);
Singing in Chinese... there's a hundred ways...
Composing melody? true
Composing melody? true Writing lyrics? true