An abstract class is a class that is declared abstract
. Abstract classes cannot be instantiated, only subclassed.
abstract class Animal {
public void eat() {}
}
An abstract method is a method that is declared without an implementation, like this:
public abstract void method(int arg);
If a class includes an abstract method, it has to be declared abstract. When an abstract class is subclassed, if the subclass doesn't provide an implementation for the inherited abstract methods, it has to be declared abstract as well.
abstract class Animal {
public void eat() {
/** Do something */
}
public abstract makeSound();
}
class Dog extends Animal {
public makeSound() {
System.out.println("Bark");
}
}
If an abstract class has static members, you can use them with a class reference (AbstractClass.staticMethod()
) as you would with any other class.
Abstract classes are similar to interfaces. However, with abstract classes, you can declare fields that are not static and final, and define public, protected, and private concrete methods. With interfaces, all fields are automatically public, static, and final, and all methods that you declare or define are public. In addition, you can extend only one class, whether or not it is abstract, whereas you can implement any number of interfaces.
Use abstract classes when:
Use interfaces when:
The final keyword can be used in variables, methods, and classes.
If you make a variable final, you cannot change its value after is initialized. It can only be initialized in the constructor or when it's declared. If it's static, it will be initialized in a static block or when it's declared.
If the variable holds an object, it cannot be assigned to other objects, but the attributes of that object can be changed.
class Car {
final int speed = 70;
void speedUp(){
speed = 90; //Compile-time error
}
If you make a method of a class final, a subclass cannot override that method when inherited.
class Animal {
final public void eat() {
System.out.println("eating");
}
}
class Dog extends Animal {
void eat() { //Compile-time error
System.out.println("eating");
}
}
If you make any class final, you cannot extend it.
final class Animal {
public void eat() {
System.out.println("eating");
}
}
class Dog extends Animal { //Compile-time error
}
An inner class is a class declared inside another class.
An inner class declared with the static keyword becomes a static inner class, for example:
class OuterClass {
// Static nested class
public static class NestedStaticClass {
public void print() {
System.out.println("Message from nested static class");
}
}
}
public class Test {
public static void main(String args[]){
// create instance of nested Static class
OuterClass.NestedStaticClass sc = new OuterClass.NestedStaticClass();
// call nonstatic method of nested static class
sc.print();
}
}
The rules of a static nested class are:
A class created inside a method or a block is called local inner class. If you want to use a local inner class, you must instantiate it inside the method or block.
public class LocalClass{
private int n = 5;
void result(){
// Local inner class
class Cube {
int calc() {
return n*n*n;
}
}
Cube c = new Cube();
System.out.println(c.calc());
}
public static void main(String args[]) {
LocalClass lc = new LocalClass();
lc.result();
}
}
The rules of a local inner class are:
A nested class or member inner class is a non-static class that is created inside a class but outside a method.
class OuterClass {
// Nested class
public class NestedClass {
public void print() {
System.out.println("Message from nested class");
}
}
}
public class Test {
public static void main(String args[]){
// create instance of outer class
OuterClass oc = new OuterClass();
// create instance of nested Static class
OuterClass.NestedClass nc = oc.new NestedClass();
// call nonstatic method of nested static class
sc.print();
}
}
The rules of a nested class are:
OuterClass.NestedClass nc = this.new NestedClass();
)There's also a concept named shadowing. If a declaration of a type (such as a member variable or a parameter name) in a particular scope (such as an inner class or a method definition) has the same name as another declaration in the enclosing scope, then the declaration shadows the declaration of the enclosing scope, for example:
public class Test {
public int x = 10;
class Inner {
public int x = 1;
void m(int x) {
System.out.println("x = " + x);
System.out.println("this.x = " + this.x);
System.out.println("Test.this.x = " + Test.this.x);
}
}
public static void main(String... args) {
Test t = new Test();
Test.Inner i = t.new Ineer();
i.m(5);
}
}
This example defines three variables named x, the member variable of the class Test, the member variable of the inner class Inner, and the parameter in the method m. The variable x defined as a parameter of the method shadows the variable of the inner class. So, when you use the variable x in the method m, it refers to the method parameter. To refer to the member variable of the inner class Inner, use the keyword this to represent the enclosing scope. To refer to member variables that enclose larger scopes, use the class name to which they belong. The output of the example is:
x = 5
this.x = 1
Test.this.x = 10
Anonymous Inner classes are classes without a name (but not without type) used to override a method of class or interface. They can't have constructors.
interface Animal {
void eat();
}
public class Test {
public static void main(String args[]){
Animal a = new Animal(){
public void eat() { System.out.println("eat"); }
};
a.eat();
}
}
The code:
Animal a = new Animal() { //This is the only case where you can use the keyword 'new' with an interface
public void eat() { System.out.println("eat"); }
};
Represents:
The rules to access variables in anonymous classes are:
Enumerated types (or enums) are classes that can be used to define a set of constants. They are type-safe, meaning that you cannot assign anything else other than the predefined constants to an enum variable.
Here's an example:
public enum Colors {
RED,
BLUE,
BLACK
}
You can refer to the constants in the enum like this:
Colors color = Colors.BLUE;
Enums extend from java.lang.Enum
implicitly, so they cannot extend another class. But they can implement an interface and override any method like a normal class.
You can specify values of enum constants at the creation time, but you need to define a constructor for this and, optionally, a method to get these values, for example:
public enum Colors {
RED("#ff0000"),
BLUE("#3366cc"),
BLACK("#000000");
private String hexValue;
private Colors(String hexValue) {
this.hexValue = hexValue;
}
public String getHexValue() {
return value;
}
}
And the method is used like this:
String value = Colors.BLUE.getHexValue();
If an enum contains attributes and methods, their definition must always come after the list of constants in the enum and the list of constants must be terminated by a semicolon.
The constructor of an enum must be private, any other access modifier will result in compilation error. For this reason, you cannot create an instance of an enum by using the new operator.
Enums can be used in if statements in this way:
Colors color = Colors.BLUE;
if( color == Colors.RED) {
/** Do something */
} else if( color == Colors.BLUE) {
/** Do something */
} else if( color == Colors.BLACK) {
/** Do something */
}
And in switch statements like this:
Colors color = Colors.BLUE;
switch (color) {
case RED: /** Do something */; break;
case BLUE: /** Do something */; break;
case BLACK: /** Do something */; break;
}
You can also get all the possible values of an enum type by calling the static values()
method:
for (Colors c : Colors.values()) {
System.out.println(c);
}
The order of the values is exactly the same in which they were defined.
An interface is like an abstract class, except that it cannot contain an implementation of the methods, only their signature (return type, name, parameters, and exceptions).
An interface is declared using the interface keyword. Just like classes, an interface can be declared public or with package scope (no access modifier).
public interface Vehicle {
public String serie = "XXX";
public void start();
}
The variables declared in an interface are public, static and final by default. The methods are public and abstract by default.
Before you can use an interface, it must be implemented by some class:
public class Truck implements Vehicle {
public void start() {
System.out.println("Starting truck...");
}
}
A class that implements an interface must implement all the methods declared in the interface. The only exception is default methods. Java 8 introduces this feature, which provides the flexibility to allow an interface to define an implementation which will be used as default in the situation where a class fails to provide an implementation for that method.
This is made by adding the keyword default
before the method's access modifier and adding its implementation inside the interface itself:
interface Vehicle {
default public void start() {
System.out.println("Default start");
}
}
class Car implements Vehicle {
// valid in Java 8
}
Once a class implements an interface, an instance of that class can be assigned to a reference of the interface type:
Vehicle truck = new Truck();
A class can implement multiple interfaces. In that case, the class must implement all the methods declared in all the interfaces implemented:
interface Run {
public void run();
}
interface Sleep {
public void sleep();
}
public class Man
implements Run, Sleep {
public void run() {
System.out.println("run");
}
public void sleep() {
System.out.println("sleep");
}
}
An interface cannot extend from another class or implement another interface. It can only extend another interface(s):
interface Run {
public void run();
}
interface Sleep {
public void sleep();
}
interface Behavior extends Run, Sleep {
}
public class Man
implements Behavior {
public void run() {
System.out.println("run");
}
public void sleep() {
System.out.println("sleep");
}
}
If a class implements Behavior, it has to implement all methods defined in both Run and Sleep interfaces.
When you use the @Override
annotation, the compiler checks to make sure you're actually overriding a method. For example, if you misspell the method name or not match the parameters correctly, you will be warned that you're not actually overriding a method of the superclass.
The most common use case for @Override
is with Object methods:
public class Test {
@Override
public String toString() {
/** Do something */
}
@Override
public boolean equals(Object) {
/** Do something */
}
@Override
public int hashCode() {
/** Do something */
}
}
Think about an interface with one method, like the one used to create threads:
public interface Runnable() {
public void run();
}
You can use an anonymous class to implement that interface:
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("run");
}
}).start();
Lambda expressions can be used in cases like this, where you have what is called a functional interface, an interface with just one method declared in it.
A lambda expression has the following syntax:
parameter -> expression body
Here are some examples of Lambda expressions.
(int a, int b) -> { return a * b; }
() -> System.out.println("Hi");
String s -> { System.out.println(s); }
(a) -> a
() -> return 100;
Using a lambda expression, a thread can be started in this way:
new Thread(
() -> System.out.println("run");
).start();
In this case, the lambda expression replaces the method run()
. Since this method has no parameters, the parentheses have no content in between. That is to signal that the lambda takes no parameters. Also, with lambda expressions, the type can be inferred from the surrounding code, so there is no need to reference the Runnable interface.
Lambda expressions are objects. You can assign a lambda expression to a variable and pass it around like this:
Runnable r = () -> System.out.println("run");
new Thread(r).start();
So lambda expressions only work with one-method interfaces (called functional interfaces). There's even an annotation to make the compiler check if an interface has more than one method:
@FunctionalInterface
public interface FuncInterface { //Generates a compile-time error
public void doSomething();
public void doMoreSomething();
}
Here's another example of the use of a lambda expression:
@FunctionalInterface
interface MathFunction {
public int operation(int a, int b);
}
public class Test {
public static void main(String args[]) {
MathFunction multiply = (a, b) -> a * b;
MathFunction divide = (a, b) -> a / b;
System.out.println("4 * 2 = " + multiply.operation(4, 2));
System.out.println("4 / 2 = " + divide.operation(4, 2));
}
}
In summary, lambda expressions are basically used to define an implementation of a functional interface instead of using an anonymous class.