| NUMBER | QUESTION |
|---|---|
| Java Platform | |
| 1.1 | Why is Java so popular? |
| 1.2 | What is platform independence? |
| 1.3 | What is bytecode? |
| 1.4 | Compare JDK vs JVM vs JRE |
| 1.5 | What are the important differences between C++ and Java? |
| 1.6 | What is the role for a classloader in Java? |
| Wrapper Classes | |
| 2.1 | What are Wrapper classes? |
| 2.2 | Why do we need Wrapper classes in Java? |
| 2.3 | What are the different ways of creating Wrapper class instances? |
| 2.4 | What are differences in the two ways of creating Wrapper classes? |
| 2.5 | What is auto boxing? |
| 2.6 | What are the advantages of auto boxing? |
| 2.7 | What is casting? |
| 2.8 | What is implicit casting? |
| 2.9 | What is explicit casting? |
| Strings | |
| 3.1 | Are all String’s immutable? |
| 3.2 | Where are String values stored in memory? |
| 3.3 | Why should you be careful about String concatenation(+) operator in loops? |
| 3.4 | How do you solve above problem? |
| 3.5 | What are differences between String and StringBuffer? |
| 3.6 | What are differences between StringBuilder and StringBuffer? |
| 3.7 | Can you give examples of different utility methods in String class? |
| Object oriented programming basics | |
| 4.1 | What is a class? |
| 4.2 | What is an object? |
| 4.3 | What is state of an object? |
| 4.4 | What is behavior of an object? |
| 4.5 | What is the super class of every class in Java? |
| 4.6 | Explain about toString method ? |
| 4.7 | What is the use of equals method in Java? |
| 4.8 | What are the important things to consider when implementing equals method? |
| 4.9 | What is the Hashcode method used for in Java? |
| 4.10 | Explain inheritance with examples. |
| 4.11 | What is method overloading? |
| 4.12 | What is method overriding? |
| 4.13 | Can super class reference variable can hold an object of sub class? |
| 4.14 | Is multiple inheritance allowed in Java? |
| 4.15 | What is an interface? |
| 4.16 | How do you define an interface? |
| 4.17 | How do you implement an interface? |
| 4.18 | Can you explain a few tricky things about interfaces? |
| 4.19 | Can you extend an interface? |
| 4.20 | Can a class implement multiple interfaces? |
| 4.21 | What is an abstract class? |
| 4.22 | When do you use an abstract class? |
| 4.23 | How do you define an abstract method? |
| 4.24 | Compare abstract class vs interface? |
| 4.25 | What is a constructor? |
| 4.26 | What is a default constructor? |
| 4.27 | How do you call a super class constructor from a constructor? |
| 4.28 | What is the use of this()? |
| 4.29 | Can a constructor be called directly from a method? |
| 4.30 | Is a super class constructor called even when there is no explicit call from a sub class constructor? |
| 4.31 | Is it possible to call one constructor from another in Java? |
| Advanced object oriented concepts | |
| 5.1 | What is polymorphism? |
| 5.2 | What is the use of instanceof operator in Java? |
| 5.3 | What is coupling? |
| 5.4 | What is cohesion? |
| 5.5 | What is encapsulation? |
| 5.6 | What is an inner class? |
| 5.7 | What is a static inner class? |
| 5.8 | Can you create an inner class inside a method? |
| 5.9 | What is an anonymous class? |
| Modifiers | |
| 6.1 | What is default class modifier? |
| 6.2 | What is private access modifier? |
| 6.3 | What is default or package access modifier? |
| 6.4 | What is protected access modifier? |
| 6.5 | What is public access modifier? |
| 6.6 | What access types of variables can be accessed from a class in same package? |
| 6.7 | What access types of variables can be accessed from a class in different package? |
| 6.8 | What access types of variables can be accessed from a sub class in same package? |
| 6.9 | What access types of variables can be accessed from a sub class in different package? |
| 6.10 | What is the use of a final modifier on a class? |
| 6.11 | What is the use of a final modifier on a method? |
| 6.12 | What is a final variable? |
| 6.13 | What is a final argument? |
| 6.14 | What happens when a variable is marked as volatile? |
| 6.15 | What is a static variable? |
| 6.16 | What is the difference between volatile and synchronized? |
| Conditions & loops | |
| 7.1 | Why should you always use blocks around if statement? |
| 7.2 | Should default be the last case in a switch statement? |
| 7.3 | Can a switch statement be used around a String |
| 7.4 | What is an enhanced for loop? |
| 7.5 | Is it possible to break out of nested loops in Java? |
| Exception handling | |
| 8.1 | Why is exception handling important? |
| 8.2 | What design pattern is used to implement exception handling features in most languages? |
| 8.3 | What is the need for finally block? |
| 8.4 | Does finally always execute? |
| 8.6 | Is try without a catch is allowed? |
| 8.7 | Is try without catch and finally allowed? |
| 8.8 | Can you explain the hierarchy of exception handling classes? |
| 8.9 | What is the difference between error and exception? |
| 8.10 | What is the difference between checked exceptions and unchecked exceptions? |
| 8.11 | How do you throw an exception from a method? |
| 8.12 | What happens when you throw a checked exception from a method? |
| 8.13 | What are the options you have to eliminate compilation errors when handling checked exceptions? |
| 8.14 | How do you create a custom exception? |
| 8.15 | How do you handle multiple exception types with same exception handling block? |
| 8.16 | Can you explain about try with resources? |
| 8.17 | How does try with resources work? |
| 8.18 | Can you explain a few exception handling best practices? |
| Miscellaneous topics | |
| 9.1 | What are the default values in an array? |
| 9.2 | How do you loop around an array using enhanced for loop? |
| 9.3 | How do you print the content of an array? |
| 9.4 | How do you compare two arrays? |
| 9.5 | What is an enum? |
| 9.6 | Can you use a switch statement around an enum? |
| 9.7 | What are variable arguments or varargs? |
| 9.8 | What are asserts used for? |
| 9.9 | When should asserts be used? |
| 9.10 | What is garbage collection? |
| 9.11 | Can you explain garbage collection with an example? |
| 9.12 | When is garbage collection run? |
| 9.13 | What are best practices on garbage collection? |
| 9.14 | What are initialization blocks? |
| 9.15 | What is a static initializer? |
| 9.16 | What is an instance initializer block? |
| 9.17 | What is tokenizing? |
| 9.18 | Can you give an example of tokenizing? |
| 9.19 | What is serialization? |
| 9.20 | How do you serialize an object using serializable interface? |
| 9.21 | How do you de-serialize in Java? |
| 9.22 | What do you do if only parts of the object have to be serialized? |
| 9.23 | How do you serialize a hierarchy of objects? |
| 9.24 | Are the constructors in an object invoked when it is de-serialized? |
| 9.25 | Are the values of static variables stored when an object is serialized? |
| 9.26 | Is Java "pass-by-reference" or "pass-by-value"? |
| 9.27 | How can you create a memory leak in Java? |
| 9.28 | What is reflection and why is it useful? |
| Collections | |
| 10.1 | Why do we need collections in Java? |
| 10.2 | What are the important interfaces in the collection hierarchy? |
| 10.3 | What are the important methods that are declared in the collection interface? |
| 10.4 | Can you explain briefly about the List interface? |
| 10.5 | Explain about ArrayList with an example? |
| 10.6 | Can an ArrayList have duplicate elements? |
| 10.7 | How do you iterate around an ArrayList using iterator? |
| 10.8 | How do you sort an ArrayList? |
| 10.9 | How do you sort elements in an ArrayList using comparable interface? |
| 10.10 | How do you sort elements in an ArrayList using comparator interface? |
| 10.11 | What is vector class? How is it different from an ArrayList? |
| 10.12 | What is linkedList? What interfaces does it implement? How is it different from an ArrayList? |
| 10.13 | Can you briefly explain about the Set interface? |
| 10.14 | What are the important interfaces related to the Set interface? |
| 10.15 | What is the difference between Set and sortedSet interfaces? |
| 10.16 | Can you give examples of classes that implement the Set interface? |
| 10.17 | What is a HashSet? |
| 10.18 | What is a linkedHashSet? How is different from a HashSet? |
| 10.19 | What is a TreeSet? How is different from a HashSet? |
| 10.20 | Explain briefly about Queue interface? |
| 10.21 | What are the important interfaces related to the Queue interface? |
| 10.22 | Explain about the Deque interface? |
| 10.23 | Explain the BlockingQueue interface? |
| 10.24 | What is a priorityQueue? |
| 10.25 | Can you give example implementations of the BlockingQueue interface? |
| 10.26 | Can you briefly explain about the Map interface? |
| 10.27 | What is difference between Map and sortedMap? |
| 10.28 | What is a HashMap? |
| 10.29 | What are the different methods in a Hash Map? |
| 10.30 | What is a TreeMap? How is different from a HashMap? |
| 10.31 | Can you give an example of implementation of navigableMap interface? |
| 10.32 | What are the static methods present in the collections class? |
| 10.33 | What are two differences between a HashMap and a Hashtable? |
| Advanced collections | |
| 11.1 | What is the difference between synchronized and concurrent collections in Java? |
| 11.2 | Explain about the new concurrent collections in Java? |
| 11.3 | Explain about copyonwrite concurrent collections approach? |
| 11.4 | What is compareandswap approach? |
| 11.5 | What is a lock? How is it different from using synchronized approach? |
| 11.6 | What is initial capacity of a Java collection? |
| 11.7 | What is load factor? |
| 11.8 | When does a Java collection throw UnsupportedOperationException? |
| 11.9 | What is difference between fail-safe and fail-fast iterators? |
| 11.10 | What are atomic operations in Java? |
| 11.11 | What is BlockingQueue in Java? |
| Generics | |
| 12.1 | What are Generics? |
| 12.2 | Why do we need Generics? Can you give an example of how Generics make a program more flexible? |
| 12.3 | How do you declare a generic class? |
| 12.4 | What are the restrictions in using generic type that is declared in a class declaration? |
| 12.5 | How can we restrict Generics to a subclass of particular class? |
| 12.6 | How can we restrict Generics to a super class of particular class? |
| 12.7 | Can you give an example of a Generic Method? |
| Multi threading | |
| 13.1 | What is the need for threads in Java? |
| 13.2 | How do you create a thread? |
| 13.3 | How do you create a thread by extending thread class? |
| 13.4 | How do you create a thread by implementing runnable interface? |
| 13.5 | How do you run a thread in Java? |
| 13.6 | What are the different states of a thread? |
| 13.7 | What is priority of a thread? How do you change the priority of a thread? |
| 13.8 | What is executorservice? |
| 13.9 | Can you give an example for executorservice? |
| 13.10 | Explain different ways of creating executor services . |
| 13.11 | How do you check whether an executionservice task executed successfully? |
| 13.12 | What is callable? How do you execute a callable from executionservice? |
| 13.13 | What is synchronization of threads? |
| 13.14 | Can you give an example of a synchronized block? |
| 13.15 | Can a static method be synchronized? |
| 13.16 | What is the use of join method in threads? |
| 13.17 | Describe a few other important methods in threads? |
| 13.18 | What is a deadlock? |
| 13.19 | What are the important methods in Java for inter-thread communication? |
| 13.20 | What is the use of wait method? |
| 13.21 | What is the use of notify method? |
| 13.22 | What is the use of notifyall method? |
| Functional Programming - Lamdba expressions and Streams | |
| 14.1 | What is functional programming? |
| 14.2 | What is a stream? |
| 14.3 | Explain about streams with an example? |
| 14.4 | What are intermediate operations in streams? |
| 14.5 | What are terminal operations in streams? |
| 14.6 | What are method references? |
| 14.7 | What are lambda expressions? |
| 14.8 | Can you give an example of lambda expression? |
| 14.9 | Can you explain the relationship between lambda expression and functional interfaces? |
| 14.10 | What is a predicate? |
| 14.11 | What is the functional interface - function? |
| 14.12 | What is a consumer? |
| 14.13 | Can you give examples of functional interfaces with multiple arguments? |
| New Features | |
| 15.1 | What are the new features in Java 4/5? |
| 15.2 | What are the new features in Java 6? |
| 15.3 | What are the new features in Java 7? |
| 15.4 | What are the new features in Java 8? |
| Best Practices | |
| 16.1 | What are different techniques for avoiding != null statements (Not Null Check)? |
| 16.2 | How to avoid NullPointerException when comparing Strings? |
| 16.3 | How to concatenate Strings faster and more efficient? |
| 16.4 | Which one should be used, “implements Runnable” vs. “extends Thread”? |
An important reason that contributes to the immense popularity enjoyed by Java is its platform independence or multiplatform support. Java programs are able to execute on different machines as long as there is a JRE (Java Runtime Environment) in place. Since it has been around for so long, some of the biggest organisations in the world are built using the language. Many banks, retailers, insurance companies, utilities, and manufacturers all use Java. Steve Zara, a programmer for more than 40 years, describes how there is no sign of Java declining in use. Instead, it is an evolving language which almost uniquely combines stability with innovation.
Platform independence is a term that describes a technology (usually a ProgrammingLanguage or a FrameWork) that you can use to implement things on one machine and use them on another machine without (or with minimal) changes.
There are two basic types of PlatformIndependence:
- Binary Platform Independence:
Languages like Java (JavaLanguage) or Python (PythonLanguage) use a VirtualMachine to run and therefore can be transported from one machine to another in their compiled, binary format. CsharpLanguage seems to be moving in that direction as well, by way of the MonoProject. - Source Platform Independence:
ANSI C (CeeLanguage) and ANSI C++ (CeePlusPlus) could be considered platform independent to the extent that the source needs no change (or almost no change) to be moved from one type of machine to another. The source needs to be recompiled for each platform. If the programmer adheres to strict ANSI standards, programs in both languages should compile and run nicely on all platforms.
ScriptingLanguage / InterpretedLanguage platform independence PerlLanguage can also be listed here - it doesn't compile into a binary distributable and most Perl programs (especially text based ones) can run on many platforms. There needs to be an interpreter for each platform.
Bytecode, also termed portable code or p-code, is a form of instruction set designed for efficient execution by a software interpreter. Unlike human-readable source code, bytecodes are compact numeric codes, constants, and references (normally numeric addresses) that encode the result of compiler parsing and performing semantic analysis of things like type, scope, and nesting depths of program objects.
- JDK:
JDK (Java Development Kit) is a software development kit to develop applications in Java. When you download JDK, JRE is also downloaded, and don't need to download it separately. In addition to JRE, JDK also contains number of development tools (compilers, JavaDoc, Java Debugger etc). - JVM:
JVM (Java Virtual Machine) is an abstract machine that enables your computer to run a Java program.
When you run the Java program, Java compiler first compiles your Java code to bytecode. Then, the JVM translates bytecode into native machine code (set of instructions that a computer's CPU executes directly).
Java is a platform-independent language. It's because when you write Java code, it's ultimately written for JVM but not your physical machine (computer). Since, JVM executes the Java bytecode which is platform independent, Java is platform-independent. - JRE:
JRE (Java Runtime Environment) is a software package that provides Java class libraries, along with Java Virtual Machine (JVM), and other components to run applications written in Java programming. JRE is the superset of JVM.
| Topic | Java | C++ |
|---|---|---|
| Memory Management | Conrolled by system, does not use pointers. Supports Threads and Interfaces. | Managed by developers using pointers. Supports structures and union. |
| Inheritance | Does not support multiple inheritance. Uses the concept of Interface to achieve. | Provides single and multiple inheritance. |
| Runtime error detection mechanism | System's responsoility. | Programmer's responsobility. |
| Libraries | Provides wide range of classes for various high-level services. | Comparatively available with low-level functionalities. |
| Program Handling | All meethods and data reside in class itself. Concept of Package is used. | Methods and data can reside outside classes. Concept of global file, namespace scopes available. |
| Type Semantics | Differend for primitive and object types. | Supports consistent support between primitive and object types. |
| Portability | Uses concept of bytecode which is platform independent and can be used with platform specific JVM. | Platform dependent as source code must be recompiled for different platform. |
| Polymorphism | Automatic, uses static and dynamic binding. | Explicit for methods, supports mied hierarchies. |
Class loaders are responsible for loading Java classes during runtime dynamically to the JVM (Java Virtual Machine). Also, they are part of the JRE (Java Runtime Environment). Hence, the JVM doesn’t need to know about the underlying files or file systems in order to run Java programs thanks to class loaders.
Also, these Java classes aren’t loaded into memory all at once, but when required by an context. This is where class loaders come into the picture. They are responsible for loading classes into memory.
A Wrapper class is a class whose object wraps or contains a primitive data types.
- They convert primitive data types into objects. Objects are needed if we wish to modify the arguments passed into a method (because primitive types are passed by value).
- The classes in java.util package handles only objects and hence wrapper classes help in this case also.
- Data structures in the Collection framework, such as ArrayList and Vector, store only objects (reference types) and not primitive types.
- An object is needed to support synchronization in multithreading.
- Autoboxing
You just set a value to the Wrapper instance:
char ch = 'a';
Character a = ch;
//Or:
int i = 5;
Integer o = Integer.valueOf(i);- Constructor
When instantiating a Wrapper you give the primitive type as Parameter in the constructor:
int i = 5;
Integer o = new Integer(i);The difference is that using the Constructor you will always create a new object, while using valueOf() static method, it may return you a cached value with-in a range.
Autoboxing is the automatic conversion that the Java compiler makes between the primitive types and their corresponding object wrapper classes. For example, converting an int to an Integer, a double to a Double, and so on. If the conversion goes the other way, this is called unboxing.
Example:
Integer test = 9;
/** OR */
Integer test2 = new Integer(10);
test2++; Auto Boxing helps in saving memory by reusing already created Wrapper objects. Auto Boxing uses the static valueOf methods. However wrapper classes created using new are not reused.
Example:
/** Two wrapper objects created using new are not same object. */
Integer testA = new Integer(9);
Integer testB = new Integer(9);
System.out.println(testA == testB); //false
System.out.println(testA.equals(testB)); //true
/** Two wrapper objects created using boxing are same object. */
Integer testC = 9;
Integer testD = 9;
System.out.println(testC == testD); //true
System.out.println(testC.equals(testD)); //trueType Casting in Java is nothing but converting a primitive or interface or class in Java into other type. There is a rule in Java Language that classes or interface which shares the same type hierrachy only can be typecasted. If there is no relationship between then Java will throw ClassCastException.
Type casting are of two types they are:
- Implicit Casting (Widening)
- Explicit Casting (Narrowing)
No Explicit casting required if you are widenting the datatype from smaller to larger or child to parent.
Example:
public class Implicit_Casting_Example {
public static void main(String args[]){
byte i = 50;
// No casting needed for below conversion
short j = i;
int k = j;
long l = k;
float m = l;
double n = m;
}
}When you are assigning a larger type to a smaller type, then Explicit Casting is required. Also if you are narrowing from a parent class to a child class
Example:
public class Explicit_Casting_Example {
public static void main(String args[]){
double d = 75.0;
// Explicit casting is needed for below conversion
float f = (float) d;
long l = (long) f;
int i = (int) l;
short s = (short) i;
byte b = (byte) s;
}
}All String are immutable in Java. An immutable class is simply a class whose instances cannot be modified. All information in an instance is initialized when the instance is created and the information can not be modified. There are many advantages of immutable classes.
All the String objects will be stored in the heap.
Whenever you concat two strings two String objects are created in the memory consider this example:
String s1 = "value1";
String s2 = "value2";
for(int i = 0; i < 10000; i++){
s2 = s1 + s2;
}With the above code 10000 String objects will be created. This will have a huge impact on the performance.
You can easily solve this problem by using a StringBuilder or StringBuffer.
| Basis | String | StringBuffer |
|---|---|---|
| Basic | The length of the String is fixed. | The length of the StringBuffer can be increased. |
| Modification | String object is immutable. | StringBuffer object is mutable. |
| Performance | It is slower during concatenation. | It is faster during concatenation. |
| Memory | Consumes more memory. | Consumes less memory. |
| Storage | String constant pool. (Inside of the Heap) | Heap memory. |
StringBuilder is faster than StringBuffer because it's not synchronized.
So if you are in a single threaded environment or don’' care about thread safety, you should use StringBuilder else use StringBuffer.
Some important String methods:
isEmpty(): Checks if the String is empty.length(): Returns the length of the String.charAt(index): Returns the char at a specific index.contains(sample): Checks if the String contains the sample.toLowerCase(): Put's all characters of the String to lower case.toUpperCase(): Put's all characters of the String to upper case.
In object-oriented programming, a class is an extensible program-code-template for creating objects, providing initial values for state (member variables) and implementations of behavior (member functions or methods). In many languages, the class name is used as the name for the class (the template itself), the name for the default constructor of the class (a subroutine that creates objects), and as the type of objects generated by instantiating the class; these distinct concepts are easily conflated.
In the class-based object-oriented programming paradigm, object refers to a particular instance of a class, where the object can be a combination of variables, functions, and data structures.
The state of an object are the attributes.
The behaviour of an object are the methods.
Every class in Java inherits from the java Object.
The toString() Methods gives a String representation of an object.
the equals() Method compares two objects if they are the same,
Any equals implementation should satisfy these properties:
- Reflexive. For any reference value x, x.equals(x) returns true.
- Symmetric. For any reference values x and y, x.equals(y) should return true if and only if y.equals(x) returns true.
- Transitive. For any reference values x, y, and z, if x.equals(y) returns true and y.equals(z) returns true, then x.equals(z) must return true.
- Consistent. For any reference values x and y, multiple invocations of x.equals(y) consistently return true or consistently return false, if no information used in equals is modified.
- For any non-null reference value x, x.equals(null) should return false.
Example:
//Client class
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Client other = (Client) obj;
if (id != other.id)
return false;
return true;
}HashCode's are used in hashing to decide which group (or bucket) an object should be placed into. A group of object's might share the same hashcode. The implementation of hash code decides effectiveness of Hashing. A good hashing function evenly distributes object's into different groups (or buckets). A good hashCode method should have the following properties:
- If obj1.equals(obj2) is true, then obj1.hashCode() should be equal to obj2.hashCode()
- obj.hashCode() should return the same value when run multiple times, if values of obj used in equals() have not changed.
- If obj1.equals(obj2) is false, it is NOT required that obj1.hashCode() is not equal to obj2.hashCode(). Two unequal objects MIGHT have the same hashCode.
A sample hashcode implementation of Client class which meets above constraints is given below:
//Client class
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
return result;
}In Java, it is possible to inherit attributes and methods from one class to another. We group the "inheritance concept" into two categories:
- subclass (child) - the class that inherits from another class
- superclass (parent) - the class being inherited from
To inherit from a class, use the
extendskeyword.
Method overloading is when you code a method multiple times with different types and numbers of parameters.
Example:
public calculate(int a, int b){
calculate((double)a, (double)b);
}
public calculate(double a, double b){
//do some calculation
}Method overriding is when you write a method given from a parent class again in the child class with different functionality, mostly a more child specific functionality. It is considered good coding when you tag the method then with @Override.
Example:
public class parent{
public void doSomeStuff(int a){
//Do something
}
}
public class child extends parent{
@Override
public void soSomeStuff(int a){
//Do other more child specific stuff
}
}Yes.
Since object is super class of all classes, an Object reference variable can also hold an instance of any
class for example.
No multiple inheritance is not allowed in Java. Therefore you can make use of Interfaces.
An interface is a completely "abstract class" that is used to group related methods with empty bodies.
You can define an interface like a cluss but instead of the class keywod class you use the keword interface and without any modifier.
Example:
interface MyInterface{
//...
}You can implement an interface using the implements keword behind the class and the name of the Interface. If you want to implement multiple interfaces just write the names comma seperated. Then implement all the methods form the interface.
Example:
public class test implements MyInterface{
//....
}Some tricky stuff:
- Variables in an interface are always public, static, final.
- Variables in an interface cannot be declared private.
- Interface methods are by default public and abstract.
Yes an interface can extend another interface in Java. Therefore just use the extends keyword.
Yes a class can implement multiple interfaces therefore you just name the interfaces comma separated. You then have to implement all the methods of both interfaces in your class.
A abstract class is a special class that can't be instantiated.
If you want to group multiple classes but dont want to get an instance of this group element. For exmaple you got the classes Dog and Cat and wanna group them with a class Animal but the single instance of an animal would make less sense so you make the class Animal abstract.
An abstract method needs to have an empty body and the subclasses have to implement and override this method.
Difference between interface and abstract class:
- Main difference is methods of a Java interface are implicitly abstract and cannot have implementations. A Java abstract class can have instance methods that implements a default behavior.
- Variables declared in a Java interface is by default final. An abstract class may contain non-final variables.
- Members of a Java interface are public by default. A Java abstract class can have the usual flavors of class members like private, protected, etc..
- Java interface should be implemented using keyword “implements”; A Java abstract class should be extended using keyword “extends”.
- An interface can extend another Java interface only, an abstract class can extend another Java class and implement multiple Java interfaces.
- A Java class can implement multiple interfaces but it can extend only one abstract class.
- Interface is absolutely abstract and cannot be instantiated; A Java abstract class also cannot be instantiated, but can be invoked if a main() exists.
- In comparison with java abstract classes, java interfaces are slow as it requires extra indirection.
The constructor is a special method that, by calling, creates a instance(object) of the class. The constructor also has always the same name as the class itself. A constructor can also be overriden by child classes or overloaded.
The default cunstructor is called when you want to instantiate an object without any parameter. Every instantiable class has one defaut constructor.
Example:
public class Test{
//This is the default constructor: (Normally you don't have to code this)
public Test(){}
}To call the constructor of the parent class you siply call super().
Example:
public class parent{
public parent(){
//Initialize object
}
}
public class child extends parent{
public child(){
super();
//Initialize object
}
}With this() you can call another constructor of the class.
A constructor cannot be explicitly called from any method except another constructor.
4.30 Is a super class constructor called even when there is no explicit call from a sub class constructor?
Yes, if a constructor does not explicitly invoke a superclass constructor, the Java compiler automatically inserts a call to the no-argument constructor of the superclass. If the super class does not have a no-argument constructor, you will get a compile-time error. Object does have such a constructor, so if Object is the only superclass, there is no problem.
Yes, but one can only chain to one constructor - and it has to be the first statement in your constructor body. Keyword to call another constructor of the same class is this().
Example:
public class Foo {
private int x;
public Foo() {
this(1); //Call other Constructor; is first statement of the Constructor itself
}
public Foo(int x) {
this.x = x;
}
}Polymorphism is the ability of an object to take on many forms. The most common use of polymorphism in OOP occurs when a parent class reference is used to refer to a child class object.
The instanceof operator checks if an object is an instance of a specific class.
In object oriented design, Coupling refers to the degree of direct knowledge that one element has of another. In other words, how often do changes in class A force related changes in class B.
In object oriented design, cohesion refers all about how a single class is designed. Cohesion is the Object Oriented principle most closely associated with making sure that a class is designed with a single, well-focused purpose. The more focused a class is, the cohesiveness of that class is more. The advantages of high cohesion is that such classes are much easier to maintain (and less frequently changed) than classes with low cohesion. Another benefit of high cohesion is that classes with a well-focused purpose tend to be more reusable than other classes.
Encapsulation is “hiding the implementation of a Class behind a well defined interface”. Encapsulation helps us to change implementation of a class without breaking other code
An inner class is a class that is defined within another class and can only be used inside of this class.
A class declared directly inside another class and declared as static. In the example above, class name StaticNestedClass is a static inner class.
Yes. An inner class can be declared directly inside a method.
Example:
class OuterClass{
public void exampleMethod(){
class MethodInnerClass{
};
}
}An anonymous class is a class that doesn't have a name.
- A class is called a Default Class is when there is no access modifier specified on a class.
- Default classes are visible inside the same package only.
- Default access is also called Package access.
- Private variables and methods can be accessed only in the class they are declared.
- Private variables and methods from SuperClass are NOT available in SubClass.
- Default variables and methods can be accessed in the same package Classes.
- Default variables and methods from SuperClass are available only to SubClasses in same package.
- Protected variables and methods can be accessed in the same package Classes.
- Protected variables and methods from SuperClass are available to SubClass in any package
- Public variables and methods can be accessed from every other Java classes.
- Public variables and methods from SuperClass are all available directly in the SubClass
Package and public attributes and methods of the other classes and all the attributes and methods of itseelf.
Only the public attributes and methods and all the attributes and methods of itself.
The same as the classes in the same package. But also all the attributes and methods of its super classes except private attributes and methods from superclass.
The same as the classes in different packages. But also all the attributes and methods of its super classes except private attributes and methods from superclass.
The final keyword on a class prevents the class from inheritance.
The final keyword on a method prevents the method from being overridden.
A final variable is a variable that can't be 'changed' while runtime. So a final variable is a constant. If a variable is declared final that holds a object the object can be modified but the reference of the variable to the object alway keeps the same.
A final parameter ensures that these won't accidentally be changed by the method.
Using volatile is yet another way (like synchronized, atomic wrapper) of making class thread safe. Thread safe means that a method or class instance can be used by multiple threads at the same time without any problem.
Static variables and methods are class level variables and methods. There is only one copy of the static variable for the entire Class. Each instance of the Class (object) will not have a unique copy of a static variable.
First two important definitions:
- Mutual Exclusion:
It means that only one thread or process can execute a block of code (critical section) at a time. - Visibility:
It means that changes made by one thread to shared data are visible to other threads. Java’s synchronized keyword guarantees both mutual exclusion and visibility. If we make the blocks of threads that modifies the value of shared variable synchronized only one thread can enter the block and changes made by it will be reflected in the main memory. All other thread trying to enter the block at the same time will be blocked and put to sleep.
In some cases we may only desire the visibility and not atomicity. Use of synchronized in such situation is an overkill and may cause scalability problems. Here volatile comes to the rescue. Volatile variables have the visibility features of synchronized but not the atomicity features. The values of volatile variable will never be cached and all writes and reads will be done to and from the main memory. However, use of volatile is limited to very restricted set of cases as most of the times atomicity is desired. For example a simple increment statement such as x = x + 1; or x++ seems to be a single operation but is s really a compound read-modify-write sequence of operations that must execute atomically.
For clearer reading purposes its considered good practice to use brackets with an if Statement if the statement is longer than one line.
The default case doesn't need to be the last case it also can be the first or any other case inside a switch Statement. But it's considered good practice to put the default case always as last case.
Yes since Java 7 it's possible to use String in a switch Statement.
Enhanced for loop can be used to loop around array’s or List’s.
Example:
int[] test = {1, 2, 3, 4 ,5, 6};
for(int number : test){
System.out.println(number);
}If you get to this point I'd definitely prefer to put the loops in a different method, at which point you can just return to stop iterating completely. This answer just shows how the requirements in the question can be met.
You can use break with a label for the outer loop. For example:
public class Test {
public static void main(String[] args) {
outerloop:
for (int i=0; i < 5; i++) {
for (int j=0; j < 5; j++) {
if (i * j > 6) {
System.out.println("Breaking");
break outerloop;
}
System.out.println(i + " " + j);
}
}
System.out.println("Done");
}
}The output is:
0 0
0 1
0 2
0 3
0 4
1 0
1 1
1 2
1 3
1 4
2 0
2 1
2 2
2 3
Breaking
Done
Most applications are large and complex. I’ve not seen an application without defects. It is not that bad programmers create defects. Even good programmers write code that has defects and throws exceptions. There are two things that are important when exceptions are thrown.
- A friendly message to the user:
You do not want a windows blue screen. When something goes wrong and an exception occurs, it would be great to let the user know that something went wrong and tech support has been notified. Additional thing we can do is to give the user a unique exception identifier and information on how to reach the tech support. - Enough Information for the Support Team/Support Developer to debug the problem:
When writing code, always think about what information would I need to debug a problem in this piece of code. Make sure that information is made available, mostly in the logs, if there are exceptions. It would be great to tie the information with the unique exception identifier given to the user.
When an exception is thrown from a method with no exception handling, it is thrown to the calling method. If there is no exception handling in that method too, it is further thrown up to its calling method and so on. This happens until an appropriate exception handler is found.This is an example of Chain of Responsibility Pattern defined as “a way of passing a request between a chain of objects”.
A finally block is needed if you use resources that needs to be closed even is an error or exception occurs. Or to prevent a dead lock in a exception or error state.
Yes, finally will be called after the execution of the try or catch code blocks.
The only times finally won't be called are:
- If you invoke
System.exit() - If the JVM crashes first
- If the JVM reaches an infinite loop (or some other non-interruptable, non-terminating statement) in the
tryorcatchblock - If the OS forcibly terminates the JVM process; e.g.,
kill -9 <pid>on UNIX - If the host system dies; e.g., power failure, hardware error, OS panic, et cetera
- If the
finally block is going to be executed by a daemon thread and all other non-daemon threads exit beforefinallyis called
Yes, we can have try without catch block by using finally block. You can use try with finally. As you know finally block always executes even if you have exception or return statement in try block.
Yes, you can make use of the try-with-ressources block.
Example:
static String readFirstLineFromFile(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}Throwable is the highest level of Error Handling classes.
Below are Error then Exception and then all the more specific exceptions like RuntimeException.
Underneath these exceptions you can create your own exceptions.
- Error:
An Error “indicates serious problems that a reasonable context should not try to catch.” Both Errors and Exceptions are the subclasses of java.lang.Throwable class. Errors are the conditions which cannot get recovered by any handling techniques. It surely cause termination of the program abnormally. Errors belong to unchecked type and mostly occur at runtime. Some of the examples of errors are Out of memory error or a System crash error. - Exceptions:
An Exception “indicates conditions that a reasonable context might want to catch.” Exceptions are the conditions that occur at runtime and may cause the termination of program. But they are recoverable using try, catch and throw keywords. Exceptions are divided into two catagories : checked and unchecked exceptions. Checked exceptions like IOException known to the compiler at compile time while unchecked exceptions like ArrayIndexOutOfBoundException known to the compiler at runtime. It is mostly caused by the program written by the programmer.
- Un-Checked Exception
RuntimeException and classes that extend RuntimeException are called unchecked exceptions. - Checked Exception
Other Exception Classes (which don’t fit the earlier definition). These are also called Checked Exceptions. They are subclasses of Exception which are not subclasses of RuntimeException.
Example:
private void test(int i){
if(i < 0){
//Thorws an exception:
throw new IllegalArgumentException("Value must be positive");
}
//...
}You will get a compilation error, because you try to throw a unhandled exception type.
8.13 What are the options you have to eliminate compilation errors when handling checked exceptions?
All classes that are not RuntimeException or subclasses of RuntimeException but extend Exception are called CheckedExceptions. The rule for CheckedExceptions is that they should either be handled or thrown. Handled means it should be completed handled - i.e. not throw out of the method. Thrown means the method should declare that it throws the exception
- Declaring that a method would throw an exception:
public int test(int input) throws Exception{
if(input == null)
throw new Exception("Input invalid")
return input++;
}- Handling the checked exception with a try catch block
You simply create an own class that extends the class Exception.
Example:
public class MyOwnException extends Exception{
//Constructor, methods ...
}Separate the Exceptions wit the pipe character |.
Example:
private void doSomething(){
try{
checkConnection();
} catch(RemoteException | IllegalArgumentException | RuntimeException e){
e.printStackTrace();
}
}Consider the example below. When the try block ends the resources are automatically released. We do not need to create a separate finally block.
try (BufferedReader br = new BufferedReader(new FileReader("FILE_PATH"))) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}try-with-resources is available to any class that implements the AutoCloseable interface. In the above example BufferedReader implements AutoCloseable interface.
Only catch specific Exceptions and no Errors or Throwable, because Errors need to be thrown until the top to show that the context has some serious problems and shouldn't be caught so that the context stops running.
Everything in a Java program not explicitly set to something by the programmer, is initialized to a zero value.
- For references (anything that holds an object) that is null.
- For int/short/byte/long that is a 0.
- For float/double that is a 0.0
- For booleans that is a false.
- For char that is the null character '\u0000' (whose decimal equivalent is 0).
When you create an array of something, all entries are also zeroed.
Name of the variable is unit and the array we want to loop around is units.
for (int unit : units) {
System.out.println(unit);
}- 1D Array:
Example:
int array[] = { 25, 30, 50, 10, 5 };
System.out.println(array); //[I@6db3f829
System.out.println(Arrays.toString(array));//[25, 30, 50, 10, 5]- 2D Array:
Example:
int[][] array = { { 1, 2, 3 }, { 4, 5, 6 } };
System.out.println(array); //[[I@1d5a0305
System.out.println(Arrays.toString(array)); //[[I@6db3f829, [I@42698403]
System.out.println(Arrays.deepToString(array)); //[[1, 2, 3], [4, 5, 6]]Arrays can be compared using static method equals defined in Arrays class. Two arrays are equal only if they have the same numbers in all positions and have the same size. Example:
int[] array1 = { 1, 2, 3 };
int[] array2 = { 4, 5, 6 };
System.out.println(Arrays.equals(array1, array2)); //false
int[] array3 = { 1, 2, 3 };
System.out.println(Arrays.equals(array1, array3)); //trueAn enum(short: Enumeration) is a special "class" that represents a group of constants (unchangeable variables, like final variables).
To create an enum, use the enum keyword (instead of class or interface), and separate the constants with a comma. Note that they should be in uppercase letters.
Example:
enum Level{
LOW,
MEDIUM,
HIGH
}
Level l = Level.MEDIUM;With enums you can also make some pretty advanced stuff like gibe them differend funktions etc. seen in some design patterns.
Yes you can and should be used for good coding style.
Example:
enum Level{
LOW,
MEDIUM,
HIGH
}
Level myVar = Level.MEDIUM;
switch(myVar) {
case LOW:
System.out.println("Low level");
break;
case MEDIUM:
System.out.println("Medium level");
break;
case HIGH:
System.out.println("High level");
break;
}Variable Arguments allow calling a method with different number of parameters. Consider the example method sum below. This sum method can be called with 1 int parameter or 2 int parameters or more int parameters.
//int(type) followed ... (three dot's) is syntax of a variable argument.
public int sum(int... numbers) {
//inside the method a variable argument is similar to an array.
//number can be treated as if it is declared as int[] numbers;
int sum = 0;
for (int number: numbers) {
sum += number;
}
return sum;
}Now you can call the method any number of int parameter you want:
System.out.println(sum(1,2,3,4));
System.out.println(sum(1,2,3,4,5,6,7));Assertions are introduced in Java 1.4. They enable you to validate assumptions. If an assert fails (i.e. returns false), AssertionError is thrown (if assertions are enabled). Basic assert is shown in the example below:
private int test(int number){
assert(number>0);
return 100;
}Assertions should not be used to validate input data to a public method or command line argument. IllegalArgumentException would be a better option. In public method, only use assertions to check for cases which are never supposed to happen.
In computer science, garbage collection (GC) is a form of automatic memory management. The garbage collector, or just collector, attempts to reclaim garbage, or memory occupied by objects that are no longer in use by the program. Garbage collection was invented by John McCarthy around 1959 to simplify manual memory management in Lisp.
Garbage collection is essentially the opposite of manual memory management, which requires the programmer to specify which objects to deallocate and return to the memory system.
Let’s say the below method is called from a function.
void method(){
Calendar calendar = new GregorianCalendar(2000,10,30);
System.out.println(calendar);
}An object of the class GregorianCalendar is created on the heap by the first line of the function with one reference variable calendar. After the function ends execution, the reference variable calendar is no longer valid. Hence, there are no references to the object created in the method. JVM recognizes this and removes the object from the heap. This is called Garbage Collection.
Garbage Collection runs at the whims and fancies of the JVM (it isn't as bad as that). Possible situations when Garbage Collection might run are
- when available memory on the heap is low
- when cpu is free
Programmatically, we can request (remember it’s just a request - Not an order) JVM to run Garbage Collection by calling System.gc() method. JVM might throw an OutOfMemoryException when memory is full and no objects on the heap are eligible for garbage collection. finalize() method on the objected is run before the object is removed from the heap from the garbage collector. It's recommended not to write any code in finalize().
Initialization Blocks - Code which runs when an object is created or a class is loaded There are two types of Initialization Blocks:
- Static Initializer: Code that runs when a class is loaded.
- Instance Initializer: Code that runs when a new object is created.
Example:
public class test{
static{
//This is a static initializer
System.out.println("init");
}
}Code within static{} is called a static initializer. This is run only when class is first loaded. Only static
variables can be accessed in a static initializer.
Example:
public class test{
{
//This is an instance initializer
System.out.println("init");
}
}Code within instance initializer is run every time an instance of the class is created.
Tokenizing means splitting a string into several sub strings based on delimiters. For example, delimiter ; splits the string ac;bd;def;e into four sub strings ac, bd, def and e. Delimiter can in itself be any of the regular expression(s) we looked at earlier. String.split(regex) function takes regex as an argument.
Example:
private static void tokenize(String string,String regex) {
String[] tokens = string.split(regex);
System.out.println(Arrays.toString(tokens));
}
tokenize("ac;bd;def;e",";");//[ac, bd, def, e]Serialization helps us to save and retrieve the state of an object.
- Serialization => Convert object state to some internal object representation.
- De-Serialization => The reverse. Convert internal representation to object.
Two important methods
- ObjectOutputStream.writeObject() // serialize and write to file
- ObjectInputStream.readObject() // read from file and deserialize
To serialize an object it should implement Serializable interface.
Example:
class Rectangle implements Serializable {
int length;
int breadth;
int area;
public Rectangle(int length, int breadth) {
this.length = length;
this.breadth = breadth;
area = length * breadth;
}
}
FileOutputStream fileStream = new FileOutputStream("Rectangle.ser");
ObjectOutputStream objectStream = new ObjectOutputStream(fileStream);
objectStream.writeObject(new Rectangle(5, 6)); //Here happens the serialization
objectStream.close();Example with the same object as from the question before:
FileInputStream fileInputStream = new FileInputStream("Rectangle.ser");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
Rectangle rectangle = (Rectangle) objectInputStream.readObject(); //Here happens the deserialization
objectInputStream.close();
System.out.println(rectangle.length);// 5
System.out.println(rectangle.breadth);// 6
System.out.println(rectangle.area);// 30We mark all the properties of the object which should not be serialized as transient. Transient attributes in
an object are not serialized.
Example:
public class Rectangle implements Serializable{
transient int nonSerializedInt;
}Objects of one class might contain objects of other classes. When serializing and de-serializing, we might
need to serialize and de-serialize entire object chain. All classes that need to be serialized have to
implement the Serializable interface. Otherwise, an exception is thrown. Otherwise you can also mark the dependent inner objects as transient.
No. When a class is De-serialized, initialization (constructor’s, initializer’s) does not take place. The state of the object is retained as it is.
Static Variables are not part of the object. They are not serialized.
Java is always "pass-by-value" but if you pass an object, java is passing the value of the address(reference).
A good way to create a true memory leak (objects inaccessible by running code but still stored in memory) in pure Java:
- The context creates a long-running thread (or use a thread pool to leak even faster).
- The thread loads a
classvia an (optionally custom)ClassLoader. - The
classallocates a large chunk of memory (e.g.new byte[1000000]), stores a strong reference to it in a static field, and then stores a reference to itself in a ThreadLocal. Allocating the extra memory is optional (leaking the Class instance is enough), but it will make the leak work that much faster. - The thread clears all references to the custom class or the ClassLoader it was loaded from.
- Repeat.
This works because the ThreadLocal keeps a reference to the object, which keeps a reference to its Class, which in turn keeps a reference to its ClassLoader. The ClassLoader, in turn, keeps a reference to all the Classes it has loaded.
(It was worse in many JVM implementations, especially prior to Java 7, because Classes and ClassLoaders were allocated straight into permgen and were never GC'd at all. However, regardless of how the JVM handles class unloading, a ThreadLocal will still prevent a Class object from being reclaimed.)
A variation on this pattern is why context containers (like Tomcat) can leak memory like a sieve if you frequently redeploy applications that happen to use ThreadLocals in any way. (Since the context container uses Threads as described, and each time you redeploy the context a new ClassLoader is used.)
The name reflection is used to describe code which is able to inspect other code in the same system (or itself).
For example, say you have an object of an unknown type in Java, and you would like to call a 'doSomething' method on it if one exists. Java's static typing system isn't really designed to support this unless the object conforms to a known interface, but using reflection, your code can look at the object and find out if it has a method called 'doSomething' and then call it if you want to.
So, to give you a code example of this in Java (imagine the object in question is foo):
Method method = foo.getClass().getMethod("doSomething", null);
method.invoke(foo, null);One very common use case in Java is the usage with annotations. JUnit 4, for example, will use reflection to look through your classes for methods tagged with the @Test annotation, and will then call them when running the unit test.
There are some good reflection examples to get started at http://docs.oracle.com/javase/tutorial/reflect/index.html. The usecase of looking for methods which are marked or work in a certain way is still common.
Note:
The ability to inspect the code in the system and see object types is not reflection, but rather Type Introspection. Reflection is then the ability to make modifications at runtime by making use of introspection. The distinction is necessary here as some languages support introspection, but do not support reflection. One such example is C++
Arrays are not dynamic. Once an array of a particular size is declared, the size cannot be modified. To add a new element to the array, a new array has to be created with bigger size and all the elements from the old array copied to new array.
Collections are used in situations where data is dynamic. Collections allow adding an element, deleting an element and host of other operations. There are a number of Collections in Java allowing to choose the right Collection for the right context.
Example:
interface Collection<E> extends Iterable<E> {
}
// Unique things only - Does not allow duplication.
// If obj1.equals(obj2) then only one of them can be in the Set.
interface Set<E> extends Collection<E> {
}
// LIST OF THINGS
// Cares about which position each object is in
// Elements can be added in by specifying position - where should it be added in
// If element is added without specifying position - it is added at the end
interface List<E> extends Collection<E> {
}
// Arranged in order of processing - A to-do list for example
// Queue interface extends Collection. So, it supports all Collection Methods.
interface Queue<E> extends Collection<E> {
}
// A,C,A,C,E,C,M,D,H,A => {("A",5),("C",2)}
// Key - Value Pair {["key1",value1],["key2",value2],["key3",value3]}
// Things with unique identifier
interface Map<K, V> {
}Some of the most important methods:
- add: Add an element to the collection
- remove: Remove an element from the collection
- size: Return the size of the collection (number of objects stored in the collection)
- isEmpty: Returns if nothing is stored in the collection
- clear: Removes all the elements from the collection
- contains: Checks if a element is already stored inside of a collection
List interface extends Collection interface. So, it contains all methods defined in the Collection interface.
In addition, List interface allows operation specifying the position of the element in the Collection.
Most important thing to remember about a List interface - any implementation of the List interface would maintain the insertion order. When an element A is inserted into a List (without specifying position) and then another element B is inserted, A is stored before B in the List. When a new element is inserted without specifying a position, it is inserted at the end of the list of elements.
However, We can also use the void add(int position, E paramE); method to insert an element at a specific position.
ArrayList implements the list interface. So, ArrayList stores the elements in insertion order (by default).
Element’s can be inserted into and removed from ArrayList based on their position.
Example:
List<Integer> integers = new ArrayList<Integer>();ArrayList can have duplicates (since List can have duplicates).
Example:
Iterator<String> arraylistIterator = arraylist.iterator();
while (arraylistIterator.hasNext()) {
String str = arraylistIterator.next();
System.out.println(str);
}You can make use of the Collections.sort([LIST]) method to sort a list.
First you have to implement the Compareable interface and override the compareTo() method this method returns -1, 0 or 1 depending on the comparison to the other object.
With this comparison the Collections.sort([LIST]) method will sort the list.
With the implementation of the Comparator interface to a sorting class you have to override the compare method that gets 2 objects and returns like the compareTo method 1, -1 or 0.
This allows you now to call Collections.sort([LIST], new [YOUR_SORT_CLASS]) and sort your list by the sorting class you created.
Vector has the same operations as an ArrayList. However, all methods in Vector are synchronized. So, we can use Vector if we share a list between two threads and we would want to them synchronized.
Linked List extends List and Queue. Other than operations exposed by the Queue interface, LinkedList has the same operations as an ArrayList. However, the underlying implementation of Linked List is different from that of an ArrayList.
ArrayList uses an Array kind of structure to store elements. So, inserting and deleting from an ArrayList are expensive operations. However, search of an ArrayList is faster than LinkedList.
LinkedList uses a linked representation. Each object holds a link to the next element. Hence, insertion and deletion are faster than ArrayList. But searching is slower.
There are hardly any new methods in the Set interface other than those in the Collection interface. The major difference is that Set interface does not allow duplication. Set interface represents a collection that contains no duplicate elements.
Some important sets:
// Unique things only - Does not allow duplication.
// If obj1.equals(obj2) then only one of them can be in the Set.
interface Set<E> extends Collection<E> {}
//Main difference between Set and SortedSet is - an implementation of
//SortedSet interface maintains its elements in a sorted order. Set
//interface does not guarantee any Order.
interface SortedSet<E> extends Set<E> {
SortedSet<E> subSet(E fromElement, E toElement);
SortedSet<E> headSet(E toElement);
SortedSet<E> tailSet(E fromElement);
E first();
E last();
}
//A SortedSet extended with navigation methods reporting closest matches for
//given search targets.
interface NavigableSet<E> extends SortedSet<E> {
E lower(E e);
E floor(E e);
E ceiling(E e);
E higher(E e);
E pollFirst();
E pollLast();
}SortedSet Interface extends the Set Interface. Both Set and SortedSet do not allow duplicate elements.
Main difference between Set and SortedSet is - an implementation of SortedSet interface maintains its elements in a sorted order. Set interface does not guarantee any Order. For example, If elements 4,5,3 are inserted into an implementation of Set interface, it might store the elements in any order. However, if we use SortedSet, the elements are sorted. The SortedSet implementation would give an output 3,4,5.
Some examples might be:
HashSet: unordered, unsorted - iterates in random order; uses hashCode()LinkedHashSet: ordered - iterates in order of insertion; unsorted; uses hashCode()TreeSet: sorted - natural order; implements NavigableSet
HashSet implements set interface. So, HashSet does not allow duplicates. However, HashSet does not support ordering. The order in which elements are inserted is not maintained.
LinkedHashSet implements set interface and exposes similar operations to a HashSet. Difference is that LinkedHashSet maintains insertion order. When we iterate a LinkedHashSet, we would get the elements back in the order in which they were inserted.
TreeSet implements Set, SortedSet and NavigableSet interfaces.TreeSet is similar to HashSet except that it stores element’s in Sorted Order.
Queue Interface extends Collection interface. Queue Interface is typically used for implementation holding elements in order for some processing.
Queue interface offers methods peek() and poll() which get the element at head of the queue. The
difference is that poll() method removes the head from queue also. peek() would keep head of the
queue unchanged.
Two important interfaces are Deque and BlockingQueue.
Example:
//A linear collection that supports element insertion and removal at both ends
interface Deque<E> extends Queue<E> {
void addFirst(E e);
void addLast(E e);
boolean offerFirst(E e);
boolean offerLast(E e);
E removeFirst();
E removeLast();
E pollFirst();
E pollLast();
E getFirst();
E getLast();
E peekFirst();
E peekLast();
boolean removeFirstOccurrence(Object o);
boolean removeLastOccurrence(Object o);
}Example:
//A Queue that additionally supports operations that wait for
//the queue to become non-empty when retrieving an
//element, and wait for space to become available in the queue when
//storing an element.
interface BlockingQueue<E> extends Queue<E> {
//Same as in Queue Interface
//Inserts the specified element into queue IMMEDIATELY
//Throws exception in case of failure
boolean add(E e);
//Same as in Queue Interface
//Inserts the specified element into queue IMMEDIATELY
//Returns false in case of failure
boolean offer(E e); //Same as in Queue Interface
//Inserts the specified element into this queue, waiting
//if necessary for space to become available.
void put(E e) throws InterruptedException;
//waiting up to the specified wait time
boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException;
//waits until element becomes available
E take() throws InterruptedException;
//waits for specified time and returns null if time expires
E poll(long timeout, TimeUnit unit) throws InterruptedException;
int remainingCapacity();
boolean remove(Object o);
public boolean contains(Object o);
int drainTo(Collection<? super E> c);
int drainTo(Collection<? super E> c, int maxElements);
}PriorityQueue implements the Queue interface.
The elements of the priority queue are ordered according to their natural ordering.
Two examples:
ArrayBlockingQueue: uses Array - optionally-boundedLinkedBlockingQueue: uses Linked List - optionally-bounded; Linked queues typically have higher throughput than array-based queues but less predictable performance in most concurrent applications.
First and foremost, Map interface does not extend Collection interface. So, it does not inherit any of the methods from the Collection interface.
A Map interface supports Collections that use a key value pair. A key-value pair is a set of linked data items: a key, which is a unique identifier for some item of data, and the value, which is either the data or a pointer to the data. Key-value pairs are used in lookup tables, hash tables and configuration files. A key value pair in a Map interface is called an Entry.
Put method allows to add a key, value pair to the Map.
V put(K paramK, V paramV);
Get method allows to get a value from the Map based on the key.
V get(Object paramObject);
Other important methods in Map Inteface are shown below:
interface Map<K, V>
{
int size();
boolean isEmpty();
boolean containsKey(Object paramObject);
boolean containsValue(Object paramObject);
V get(Object paramObject);
V put(K paramK, V paramV);
V remove(Object paramObject);
void putAll(Map<? extends K, ? extends V> paramMap);
void clear();
Set<K> keySet();
Collection<V> values();
Set<Entry<K, V>> entrySet();
boolean equals(Object paramObject);
int hashCode();
public static abstract interface Entry<K, V>
{
K getKey();
V getValue();
V setValue(V paramV);
boolean equals(Object paramObject);
int hashCode();
}
}SortedMap interface extends the Map interface. In addition, an implementation of SortedMap interface maintains keys in a sorted order.
HashMap implements Map interface – there by supporting key value pairs.
TreeMap is similar to HashMap except that it stores keys in sorted order. It implements NavigableMap interface and SortedMap interfaces along with the Map interface.
TreeMap.
- static int binarySearch(List, key)
-
- Can be used only on sorted list
- static int binarySearch(List, key, Comparator)
- static void reverse(List)
-
- Reverse the order of elements in a List.
- static Comparator reverseOrder();
-
- Return a Comparator that sorts the reverse of the collection current sort sequence.
- static void sort(List)
- static void sort(List, Comparator)
There are several differences between HashMap and Hashtable in Java:
Hashtableis synchronized, whereasHashMapis not. This makesHashMap better for non-threaded applications, as unsynchronized Objects typically perform better than synchronized ones.Hashtabledoes not allownullkeys or values.HashMapallows onenullkey and any number ofnullvalues.- One of HashMap's subclasses is
LinkedHashMap, so in the event that you'd want predictable iteration order (which is insertion order by default), you could easily swap out theHashMapfor aLinkedHashMap. This wouldn't be as easy if you were usingHashtable.
Synchronize means acquiring a reentrant lock on an object. The lock is released when either end of synchronized block is reached or thread goes into waiting state. The lock is reentrant in the sense that the same thread can acquire the lock again and again but a different thread cannot. So synchronized keyword essentially guards a piece of code to be accessed by multiple threads simultaneously
Concurrent collections are exactly opposite. They are called so because they allow multiple threads to concurrently access the same data. For example concurrent HashMap allows multiple threads to perform write operations in parallel as long as the writes are happening on different segments.
Also concurrent collections doesn't allow null keys and null values, whereas synchronized collections may allow null keys and null values based on the original collectionclass being passed inside it.
Post Java 5, collections using new approaches to synchronization are available in Java. These are called concurrent collections. Examples of new approaches are :
- Copy on Write
- Compare and Swap
- Locks These new approaches to concurrency provide better performance in specific context’s.
Important points about Copy on Write approach
- All values in collection are stored in an internal immutable (not-changeable) array. A new array is created if there is any modification to the collection.
- Read operations are not synchronized. Only write operations are synchronized. Copy on Write approach is used in scenarios where reads greatly out number write’s on a collection. CopyOnWriteArrayList & CopyOnWriteArraySet are implementations of this approach. Copy on Write collections are typically used in Subject – Observer scenarios, where the observers very rarely change. Most frequent operations would be iterating around the observers and notifying them.
Compare and Swap is one of the new approaches (Java 5) introduced in java to handle synchronization. In traditional approach, a method which modifies a member variable used by multiple threads is completely synchronized – to prevent other threads accessing stale value.
In compare and swap approach, instead of synchronizing entire method, the value of the member
variable before calculation is cached. After the calculation, the cached value is compared with the current value of member variable.
If the value is not modified, the calculated result is stored into the
member variable. If another thread has modified the value, then the calculation can be performed
again. Or skipped – as the need might be.
ConcurrentLinkedQueue uses this approach.
CopyOnWriteArrayList : final ReentrantLock lock = this.lock;
When 10 methods are declared as synchronized, only one of them is executed by any of the threads at any point in time. This has severe performance impact.
Another new approach introduced in Java 5 is to use lock and unlock methods. Lock and unlock methods are used to divide methods into different blocks and help enhance concurrency. The 10 methods can be divided into different blocks, which can be synchronized based on different variables.
Extract from the reference : http://docs.oracle.com/javase/6/docs/api/java/util/HashMap.html. An instance of HashMap has two parameters that affect its performance: initial capacity and load factor. The capacity is the number of buckets in the hash table, and the initial capacity is simply the capacity at the time the hash table is created. The load factor is a measure of how full the hash table is allowed to get before its capacity is automatically increased.
When the number of entries in the hash table exceeds the product of the load factor and the current capacity, the hash table is rehashed (that is, internal data structures are rebuilt) so that the hash table has approximately twice the number of buckets.
As a general rule, the default load factor (.75) offers a good tradeoff between time and space costs. Higher values decrease the space overhead but increase the lookup cost (reflected in most of the operations of the HashMap class, including get and put).
The expected number of entries in the map and its load factor should be taken into account when setting its initial capacity, so as to minimize the number of rehash operations.
Refer answer to Initial Capacity above.
All Java Collections extend Collection interface. So, they have to implement all the methods in the Collection interface. However, certain Java collections are optimized to be used in specific conditions and do not support all the Collection operations (methods). When an unsupported operation is called on a Collection, the Collection Implementation would throw an UnsupportedOperationException.
Fail Fast Iterators throw a ConcurrentModificationException if there is a modification to the underlying collection is modified. This was the default behavior of the synchronized collections of pre Java 5 age.
Fail Safe Iterators do not throw exceptions even when there are changes in the collection. This is the default behavior of the concurrent collections, introduced since Java 5.
Fail Safe Iterator makes copy of the internal data structure (object array) and iterates over the copied data structure.
Fail Safe is efficient when traversal operations vastly outnumber mutations
Atomic Access Java Tutorial states “In programming, an atomic action is one that effectively happens all at once. An atomic action cannot stop in the middle: it either happens completely, or it doesn't happen at all. No side effects of an atomic action are visible until the action is complete”.
Let’s assume we are writing a multi threaded program. Let’s create an int variable i. Even a small operation, like i++ (increment), is not thread safe. i++ operation involves three steps.
- Read the value which is currently stored in i
- Add one to it (atomic operation).
- Store it in i In a multi-threaded environment, there can be unexpected results. For example, if thread1 is reading the value (step 1) and immediately after thread2 stores the value (step 3).
To prevent these, Java provides atomic operations. Atomic operations are performed as a single unit without interference from other threads ensuring data consistency.
A good example is AtomicInteger. To increment a value of AtomicInteger, we use the incrementAndGet() method. Java ensures this operation is Atomic.
BlockingQueue interface is introduced in Java specifically to address specific needs of some Producer Consumer scenarios. BlockedQueue allows the consumer to wait (for a specified time or infinitely) for an element to become available.
Generics are a facility of generic programming that were added to the Java programming language in 2004 within version J2SE 5.0. They were designed to extend Java's type system to allow "a type or method to operate on objects of various types while providing compile-time type safety". The aspect compile-time type safety was not fully achieved, since it was shown in 2016 that it is not guaranteed in all cases.
Generics help to keep a class as flexible as possible. The example below shows a very flexible class that can store any Object as a self coded list structure:
class MyListGeneric<T> {
private List<T> values;
void add(T value) {
values.add(value);
}
void remove(T value) {
values.remove(value);
}
T get(int index) {
return values.get(index);
}
}Example:
public class Entry<T, U>{
private final T key;
private final U value;
public Entry(T key, U value){
this.key = key;
this.value = value;
}
//Getter, Stter, ToString...
}If a generic is declared as part of class declaration, it can be used any where a type can be used in a class
- method (return type or argument), member variable etc. For Example: See how T is used as a parameter and return type in the class MyListGeneric.
Example:
public class Entry<? extends ParentClass>{
private final ParendClass key;
public Entry(ParentClass key){
this.key = key;
}
//Getter, Stter, ToString...
}Example:
public class Entry<? super ChildClass>{
private final ParendClass key;
public Entry(ParentClass key){
this.key = key;
}
//Getter, Stter, ToString...
}A generic type can be declared as part of method declaration as well. Then the generic type can be used
anywhere in the method (return type, parameter type, local or block variable type).
Example:
static <X extends Number> X doSomething(X number){
X result = number;
//do something with result
return result;
}The method can now be called with any Class type extend Number.
Threads allow Java code to run in parallel. So we can do multiple independent things at once. This allows us to get a better performance.
Creating a Thread class in Java can be done in two ways. Extending Thread class and implementing Runnable interface.
Thread class can be created by extending Thread class and implementing the public void run() method.
Example:
class Test extends Thread {
//run method without parameters
public void run() {
for (int i = 0; i < 1000; i++)
System.out.println("Running Batting Statistics Thread " + i);
}
}Thread class can also be created by implementing Runnable interface and implementing the method
declared in Runnable interface public void run().
Example:
class Test implements Runnable {
//run method without parameters
public void run() {
for (int i = 0; i < 1000; i++)
System.out.println("Running Bowling Statistics Thread " + i);
}
}Running a Thread in Java is slightly different based on the approach used to create the thread.
- Thread created Extending Thread class
When using inheritance, An object of the thread needs be created andstart()method on the thread needs to be called. Remember that the method that needs to be called is notrun()but it isstart(). Example:
Test test = new Test();
test.start();- Thread created implementing RunnableInterface.
Three steps involved.- Create an object of the BowlingStatisticsThread(class implementing Runnable).
- Create a Thread object with the earlier object as constructor argument.
- Call the start method on the thread.
Example:
Test test = new Test();
Thread thread = nw Thread(test);
thread.start();Different states that a thread can be in are defined the class State.
- NEW
- RUNNABLE
- RUNNING
- BLOCKED / WAITING
- TERMINATED / DEAD
Scheduler can be requested to allot more CPU to a thread by increasing the threads priority. Each thread in Java is assigned a default Priority 5. This priority can be increased or decreased (Range 1 to 10).
If two threads are waiting, the scheduler picks the thread with highest priority to be run. If all threads have equal priority, the scheduler then picks one of them randomly. Design programs so that they don't depend on priority.
You can set the priority of a thread by using the [THREAD_OBJECT].setPriority([NUMBER_1-10]) method.
Java also provides predefined constants Thread.MAX_PRIORITY(10), Thread.MIN_PRIORITY(1),
Thread.NORM_PRIORITY(5) which can be used to assign priority to a thread.
The java.util.concurrent.ExecutorService interface is a new way of executing tasks asynchronously in the background. An ExecutorService is very similar to a thread pool.
Example:
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.execute(new Runnable() {
public void run() {
System.out .println("From ExecutorService");
}
});
System.out .println("End of Main");
executorService.shutdown();There are three ways of creating executor services. Below example shows the three different ways. executorService1 can execute one task at a time. executorService2 can execute 10 tasks at a time. executorService3 can execute tasks after certain delay or periodically.
// Creates an Executor that uses a single worker thread operating off an
// unbounded queue.
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
// Creates a thread pool that reuses a fixed number of threads
// operating off a shared unbounded queue. At any point, the parameter
// specifies the most threads that will be active processing tasks.
ExecutorService executorService2 = Executors.newFixedThreadPool(10);
// Creates a thread pool that can schedule commands to run after a
// given delay, or to execute periodically.
ExecutorService executorService3 = Executors.newScheduledThreadPool(10);We can use a Future to check the return value. Future get method would return null if the task finished successfully. Example:
Future future = executorService1.submit(new Runnable() {
public void run() {
System.out .println("From executorService1");
}
});
future.get(); // returns null if the task has finished correctly.Runnable interface's run method has a return type void. So, it cannot return any result from executing a
task. However, a Callable interface's call method has a return type. If you have multiple return values
possible from a task, we can use the Callable interface.
Example:
Future futureFromCallable = executorService1.submit(new Callable() {
public String call() throws Exception {
return "RESULT";
}
});
System.out .println("futureFromCallable.get() = " + futureFromCallable.get());Since Threads run in parallel, a new problem arises. What if thread1 modifies data which is being accessed by thread2? How do we ensure that different threads don’t leave the system in an inconsistent state? This problem is usually called synchronization problem.
The way you can prevent multiple threads from executing the same method is by using the synchronized
keyword on the method. If a method is marked synchronized, a different thread gets access to the
method only when there is no other thread currently executing the method.
Example:
public synchronized void calculate(){
//Do thread critical stuff
}All code which goes into the block is synchronized on the current object.
Example:
void test() {
synchronized (this){
//Thread safe code
}
}Yes.
Join method is an instance method on the Thread class. Let's see a small example to understand what join method does. Let’s consider the thread's declared below: thread2, thread3, thread4
ThreadExample thread2 = new ThreadExample();
ThreadExample thread3 = new ThreadExample();
ThreadExample thread4 = new ThreadExample();Let’s say we would want to run thread2 and thread3 in parallel but thread4 can only run when thread3 is finished. This can be achieved using join method.
- Join method example:
thread3.start();
thread2.start();
thread3.join();//wait for thread 3 to complete
System.out.println("Thread3 is completed.");
thread4.start();thread3.join() method call force the execution of main method to stop until thread3 completes execution. After that, thread4.start() method is invoked, putting thread4 into a Runnable State.
- Overloaded Join Method:
Join method also has an overloaded method accepting time in milliseconds as a parameter.
thread4.join(2000);In above example, main method thread would wait for 2000 ms or the end of execution of thread4, whichever is minimum.
- Thread yield method:
Yield is a static method in the Thread class. It is like a thread saying " I have enough time in the limelight. Can some other thread run next?".
A call to yield method changes the state of thread from RUNNING to RUNNABLE. However, the scheduler might pick up the same thread to run again, especially if it is the thread with highest priority.
Summary is yield method is a request from a thread to go to Runnable state. However, the scheduler can immediately put the thread back to RUNNING state. - Thread sleep method:
sleep is a static method in Thread class. sleep method can throw a InterruptedException. sleep method causes the thread in execution to go to sleep for specified number of milliseconds.
Let’s consider a situation where thread1 is waiting for thread2 ( thread1 needs an object whose synchronized code is being executed by thread1) and thread2 is waiting for thread1. This situation is called a Deadlock. In a Deadlock situation, both these threads would wait for one another for ever.
Important methods are:
- wait
- notify
- notifyAll
wait method is defined in the Object class. This causes the thread to wait until it is notified.
Example:
synchronized(thread){
thread.start();
thread.wait();
}notify method is defined in the Object class. This causes the object to notify other waiting threads.
Example:
synchronized (this) {0
doSomeStuff();
notify();
}If more than one thread is waiting for an object, we can notify all the threads by using notifyAll method.
Example:
synchronized (this) {0
doSomeStuff();
notifyAll();
}Functional programming is a programming paradigm — a style of building the structure and elements of computer programs — that treats computation as the evaluation of mathematical functions and avoids changing-state and mutable data.
A Stream is a source of objects. In the above example, we created a stream from List.
Streams have Intermediate Operations and Terminal Operations. In the example above, we used filter as intermediate operation and reduce as a terminal operation.
Streams are introduced in Java 8. In combination with Lambda expressions, they attempt to bring some of the important functional programming concepts to Java.
A stream is a sequence of elements supporting sequential and parallel aggregate operations. Consider the example code below. Following steps are done:
- Step I : Creating an array as a stream
- Step II : Use Lambda Expression to create a filter
- Step III : Use map function to invoke a String function
- Step IV : Use sorted function to sort the array
- Step V : Print the array using forEach
Example:
Arrays.stream(new String[] {"Ram", "Robert", "Rahim"})
.filter(s - > s.startsWith("Ro"))
.map(String::toLowerCase)
.sorted()
.forEach(System.out::println);In general any use of streams involves
- Source - Creation or use of existing stream : Step I above
- Intermediate Operations - Step II, III and IV above. Intermediate Operations return a new stream
- Terminal Operation – Step V. Consume the stream. Print it to output or produce a result (sum,min,max etc).
Intermediate Operations are of two kinds
- Stateful : Elements need to be compared against one another (sort, distinct etc)
- Stateless : No need for comparing with other elements (map, filter etc)
Intermediate operations return another Stream which allows you to call multiple operations in a form of a query. Intermediate operations do not get executed until a terminal operation is invoked as there is a possibility they could be processed together when a terminal operation is executed.
Operations:
filtermapflatMappeekdistinctsortedlimit
Terminal operations produces a non-stream, result such as primitive value, a collection or no value at all. Terminal operations are typically preceded by intermediate operations which return another Stream which allows operations to be connected in a form of a query.
Operations:
forEachtoArrayreducecollectminmaxcountanyMatchallMatchnoneMatchfindFirstfindAny
Integer::sum, System.out::print in the above examples are method references. These two are simple static methods which are used instead of Lambda Expressions.
One issue with anonymous classes is that if the implementation of your anonymous class is very simple, such as an interface that contains only one method, then the syntax of anonymous classes may seem unwieldy and unclear. In these cases, you're usually trying to pass functionality as an argument to another method, such as what action should be taken when someone clicks a button. Lambda expressions enable you to do this, to treat functionality as method argument, or code as data.
Syntax : Parameters -> Executed code
Example:
List<String> upper = new ArrayList<>();
List<String> list = new ArrayList<>();
list.forEach(item -> upper.add(item.toUpperCase()));When ever we create a Lambda Expression, we are defining a function which implements a predefined/ custom defined Functional Interface.
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
public void lambdaExpression_predicate() {
List<Integer> numbers = Arrays.asList(1, 3, 4, 6, 2, 7);
numbers.stream().filter((number) -> (number % 2 != 0)).forEach(
number -> System.out.print(number));
}(number) -> (number % 2 != 0) is a Predicate. Takes an argument and returns true of false.
Signature of filter function : Stream java.util.stream.Stream.filter(Predicate<? super T> predicate). filter returns a stream consisting of the elements of this stream that match the given predicate.
@FunctionalInterface
public interface Function<T, R> {
R apply(T t);
}public interface Consumer<T> {
void accept(T t);
}@FunctionalInterface
public interface BiFunction<T, U, R> {
R apply(T t, U u);
}Features in Java SE 4/5
- Assertions.
- For-each loop.
- Varargs.
- Static import.
- Autoboxing and Unboxing.
- Enum Type.
- Annotations.
- Custom Annotation.
- Generics.
Features in Java SE 6
- Scripting Language Support
- JDBC 4.0 API
- Java Compiler API
- Pluggable Annotations
- Native PKI, Java GSS, Kerberos and LDAP support.
- Integrated Web Services.
- Lot more enhancements.
Features in Java SE 7
- Strings in switch Statement
- Type Inference for Generic Instance Creation
- Multiple Exception Handling
- Support for Dynamic Languages
- Try with Resources
- Java nio Package
- Binary Literals, underscore in literals
- Diamond Syntax
- Automatic null Handling
Features in Java SE 8
- Lambda Expressions
- Pipelines and Streams
- Date and Time API
- Default Methods
- Type Annotations
- Nashhorn JavaScript Engine
- Concurrent Accumulators
- Parallel operations
- PermGen Error Removed
- TLS SNI
This a quite common problem that junior to intermediate developers tend to face at some point: they either don't know or don't trust the contracts they are participating in and defensively overcheck for nulls. Additionally, when writing their own code, they tend to rely on returning nulls to indicate something thus requiring the caller to check for nulls.
To put this another way, there are two instances where null checking comes up:
- Where
nullis a valid response in terms of the contract; and - Where it isn't a valid response.
(2) is easy. Either use assert statements (assertions) or allow failure (for example, NullPointerException). Assertions are a highly-underused Java feature that was added in 1.4. The syntax is:
assert <condition>or
assert <condition> : <object>where <condition> is a boolean expression and <object> is an object whose toString() method's output will be included in the error.
An assert statement throws an Error (AssertionError) if the condition is not true. By default, Java ignores assertions. You can enable assertions by passing the option -ea to the JVM. You can enable and disable assertions for individual classes and packages. This means that you can validate code with the assertions while developing and testing, and disable them in a production environment, although my testing has shown next to no performance impact from assertions.
(1) is a little harder. If you have no control over the code you're calling then you're stuck. If null is a valid response, you have to check for it.
If it's code that you do control, however (and this is often the case), then it's a different story. Avoid using nulls as a response. With methods that return collections, it's easy: return empty collections (or arrays) instead of nulls pretty much all the time.
With non-collections it might be harder. There you should make use of the Null Object Pattern
Sometimes you may have to compare two Strings. Without much thinking you could write code like this:
public void testMyString(String testStr){
testStr.equals("mySample");
}But what happens if the testStr is or could be null due to bad design of the Software or because you can't change where you get the String from?
A NullPointerException will be thrown.
You can easily improve the code so that no Exception will be thrown if the String you get to test is null:
public void testMyString(String testStr){
"mySample".equals(testStr);
}This easy switch prevents your code from throwing a NullpointerException and helps your Software to keep stable. If the testStr is null the equlas() Method will automatically return false and the Software will work furtheron.
If you append or modify a String inside of a loop or a lot of modifications are done with this String a StringBuilder should be used:
Bad example:
private String example(){
String result = "My Starting String";
for(int i = 0; i < 10; i++){
result += " Some append(" + i + ")";
}
return result;
}Improved Code:
private String example(){
StringBuilder sb = new StringBuilder("My Starting String");
for(int i = 0; i < 10; i++){
sb.append(" Some append(").append(i).append(")");
}
return sb.toString();
}Another Improvement:
The starting point is something like this:
String temp = "SELECT Item FROM Invoice WHERE id = '" + id + "'";This code can be easily improved:
String temp = "SELECT Item FROM Invoice WHERE id = '" + id + '\'';This way the last add to the String will not be handled as String.Instead It will be handled as char and is an primitive datatype from java. This improvement gives the code a slightly better performance because only one char needs to be attatched instead of a potential Array of chars (like a String would be). The pervormance improvement from a + "b" to a + 'b' is round about 10% (measured by benchmarks).\
Note:
Except some people always saying you should use a
StringBuilderfor such cases: Java automatically uses aStringBuilderfor such cases in the background. Only in loops or if a lot gets appended a explicit StringBuilder is better in performance and best practice. Link: Link
Implementing Runnable is the preferred way to do it. You're not really specialising the thread's behaviour. You're just giving it something to run. That means composition is the philosophically "purer" way to go.
In practical terms, it means you can implement Runnable and extend from another class as well.