Skip to content

Instantly share code, notes, and snippets.

@sats17
Last active April 19, 2024 11:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sats17/1d9d8722332b430fd03a22277c58ea87 to your computer and use it in GitHub Desktop.
Save sats17/1d9d8722332b430fd03a22277c58ea87 to your computer and use it in GitHub Desktop.
Gist contains information about core java things.
public: Visible to all classes and packages.
protected: Visible to subclasses and classes in the same package.
default (package-private): Visible only to classes in the same package.
private: Visible only within the same class.
===========================================================================================================================
===========================================================================================================================
===========================================================================================================================
All about core java
===========================================================================================================================
===========================================================================================================================
===========================================================================================================================
1) Factory design pattern - It is a creational type design pattern where we are hiding implementation of object creations and we are providing
abstraction factory on top of the object creations which will create objects for clients. Here object creation logic is hidden from client.
Example 1 -
Interface Notification
Class PushNotification implements Notification
Class SMSNotification implements Notification
Class MailNotification implements Notification
Class NotificationFactory {
if String == "PUSH" then create object of PushNotification
if String == "MAIL" then create object of MailNotification
if String == "SMS" then create object of SMSNotification
else Error
}
Class Client {
new NotificationFactory("SMS")
}
Example 2 -
Interface Excersize
Class UpperBody implements Excersize
Class LowerBody implements Excersize
Class Cardio implements Excersize
Class ExcersizeFactory {
if String == "UpperBody" then create object of UpperBody
if String == "LowerBody" then create object of LowerBody
if String == "Cardio" then create object of Cardio
else "Get out"
}
Class Gym {
new ExcersizeFactory("UpperBody")
}
Ref link = https://www.geeksforgeeks.org/factory-method-design-pattern-in-java/
Note:
Some of the design pattern used to create spring frameworks - https://www.baeldung.com/spring-framework-design-patterns
Design pattern everyone uses according to their need, spring uses factory design pattern and singleton design pattern combination to
create a Objects container and further operations.
2) Abstract factory pattern = Abstract factory pattern is provide abstraction layer using that layer client can create factories. Later on
using those factories they can get objects(Same like factory method). It is design pattern where we are hiding factories implementation.
Interface UpperBodyExcersize
BackBiceps implements UpperBodyExcersize
ChestShoulderTriceps implements UpperBodyExcersize
Interface LowerBodyExcersize
LowerLegs implements LowerBodyExcersize
UpperLegs implements LowerBodyExcersize
Interface Excersize
UpperBodyFactory implements Excersize
LowerBodyFactory implements Excersize
//CardioClass implements Excersize
Class Gym {
method static getExcersizes('type') {
if String == UpperBody return new UpperBodyFactory
if String == LowerBody return new LowerBodyFactory
if String == Cardio return new CardiClass
}
}
Class client {
main() {
// Today is my day to perform backbiceps, So I will ask trainer to give my schedule
Excersize upperBodyExcersize = Gym.getExcersizes('UpperBody');
UpperBodyExcersize backbiceps = upperBodyExcersize.getBackBiceps();
backbiceps.start()
}
}
So using this abstractions layers we are here abstracted all excersize factories by single excersize factory.
3) Singleton design pattern = Singleton design pattern is another creation design pattern where we create only single object of any class,
and we reused that object throughout program. It is widely used in spring, spring boot and whenevver we want to create DB connections then
we can use this.
Example -
Class Singleton{
private static Singelton instance = null;
private Singleton(){} // private constructor
public static Singelton getInstance() {
if (instance == null)
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
} // end of getInstance()
} //end of Singleton class
4) Prototype Design pattern = Prototype design pattern is another object creation design pattern. In this design pattern we are cloning objects
instead of creating object newly. By cloning means we are using cloneable interface which allows us to get clone method, then we are returning new object
from that clone method with copying all data from previous object.
Example you can say. let's say your object needs to do bulk load from database and object 1 is doing bulk load for you. but for object 2
you don't need to connect database again, you can simply copy data from object 1 to object 2. By doing this we are saving lot of time for
object 2 creation. So this is called prototype design pattern.
Ref - https://www.youtube.com/watch?v=nZ76x13Nm8Q
Example - https://github.com/sats17/java-assignments/blob/main/basic-assignments/src/main/java/com/github/sats17/PrototypeClass.java
5) Builder design pattern - In builder design pattern when you are creating object having lot of variables that you need to pass through contructors
or setters. Usually those variables are optionals, so you might end up with lot of constructors with diffrent permutation and combination. Or else
you might use setters here.
Problem with constructors is that first there will be lot of constructors will be created and second you need to remember for those values in constructor.
Problem with setter is that from whole code point of view you can set those variables from anywhere, which leads to inconsistent and you need
to watch thoroughly. Other is if you want immutable object then you will end up using constructor only and not setter which can lead to problem.
Where we wil use this type of big chain object - Pizza, where pizza creates according user needs.
Solution is using builder design pattern, where you will build object accordingly and client will just pass values to you. Using this your
object can be immutable. Builder design pattern is type of lazy initialization, where all values will be get collected and when build() method
called then only object gets created. In builder pattern we have another class which helps us to build our required object.
Example - https://github.com/sats17/java-assignments/blob/main/basic-assignments/src/main/java/com/github/sats17/BuilderDesignPattern.java
6) Object pool pattern - This pattern is where objects are stored in pool and will be fetched from pool and once his use is complete then,
it will put back into pool.
Example - ThreadPool, DBConnectionPool
7) Template design pattern - In mcd domain gateway application template design pattern was used. As it have abstract class contains single implemented
method which contains abstract methods in order. Like domain gw was middleware application hence it has common methods like validateRequest, modifyRequst,
modifyResponse etc.
===================== Java Only ==================================
Checked vs unchecked exception -
All Exceptions are part of run time and not compile time. There are two kinds of exceptions checked exceptions and unchecked exceptions.
Examples of checked exceptions are IO Exceptions, ClassNotFound Exception and examples of unchecked exceptions are runtime exceptions.
In the case of checked exceptions, error or warning message comes at compile time so that the user will take care of it at runtime by
using throws keyword, which is used to throw exceptions to the default catch mechanism system.
But in case of unchecked exceptions warning is not there at compile time.
Unchecked exceptions are those that extend RuntimeException class. Compiler will never force you to catch such exception or force you to
declare it in the method using throws keyword. All other exception types (that do not extend RuntimeException) are checked and
therefore must be declared to be thrown and/or catched.
Ref - https://www.infoworld.com/article/3649089/how-to-handle-java-errors-and-cleanup-without-finalize.html
Java deprecated finalize() method.
What is use of finalize() method -> 1) The basic idea is to allow you to define a method on your objects that will execute when the object is
ready for garbage collection. Technically, an object is ready for garbage collection when it becomes phantom reachable,
meaning no strong or weak references are left in the JVM.
2) So we can add logic in finalize method to close the resources, such as IO stream or file reader.
Main problem was with finalize is that we don't know when JVM will trigger this method as JVM gc works with different algorithm. So adding
any buisness logic in finalize method was not good. Also, java deprecated this method asked not to use.
Another problem is that finalizers can run on any thread, introducing error conditions that are very hard to debug, just like any other concurrency issue.
If such issues arise in production, they are hard to reproduce and debug.
Alternatives are try with resources, where any class that implements java.lang.AutoCloseable can be supplied to try-with-resource.
1) Garbage Collector in java inbuild functionality and run automatically to free un-referenced objects from heap.
2) Those objects which are not referenced with any variable from stack and subsequent reference(stack -> Object1 -> Object2) are eligible
for garbage collections.
3) What garbage collection do, it scans all the heap memory and collect objects those are referenced with stack. And it not consider other
objects those are unreferened.
https://www.geeksforgeeks.org/mark-and-sweep-garbage-collection-algorithm/
4) there is java.lang.gc() method tell jvm to start collecting unreachable objects. It just notify jvm that collect objects, jvm will do these
things by it's own. gc() will not gurantee garbage will be collected within this time period.
5) java.lang.Object.finalize() method just call before object will be collected by garbage collector.
Heap Memory -
Heap divides into
1) Eden(250%) - Whenever any new objects created they stored into Eden space, when eden gets full garbage gets collected and survival objects
passed to Survivors.
2) Survivors(s1, s2)(5%) - Whatever objects moved to survivors, are shifted each other like s1 to s2 vice versa whenever gc happens. If
it survives for 8th time then those objects moved to old gen. (Even though we get heap out of memory error these s1 and s2 are not actually
occupied for these survived thing)
3) Old gen (70%) - All old objects store here in old generation. When this get full then we received Out of memory issue.
This note is for after java 8 implementation -
Variables and inner classes:
1) transient Node<K,V>[] table = This is the array of Node class in hashmap. What ever key value we are putting inside hashmap those
are get stored inside this array.
Sample Node array after adding map.put("test", "test")
output = [null, null, null, null, test=test, null, null, null, null, null, null, null, null, null, null, null]
putVal() method by calculating index puts those value in this array.
2) Node class = Node class is static class present inside HashMap, implements Map.Entry<K,V>. So basically whatever data we stored in hashmap
are we take that data create node class with those data and put this node class in Node<K,V> table by calculating index. If any collision
happen at index then we create linked list(Prior java 8) inside this table array for particular index.
Fields =
final int hash;
final K key;
V value;
Node<K,V> next;
Constructor =
Node(int hash, K key, V value, Node<K,V> next) {
this.hash = hash;
this.key = key;
this.value = value;
this.next = next;
}
HashMap methods:
1) put(): This method calls internal putVal() method.
2) putVal(): this method mainly insert data into {} in hashmap.
Basically, Hashset is a hashmap with dummy values. So when we create object of HashSet, we are initialising new HashMap object.
Constructor =
public HashSet() {
map = new HashMap<>();
}
When we add element in hashset, there is hashmap which add this key in map.
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
Intermittent operation is only applied during the terminal operation.
Think of something like this:
public Stream filter(Predicate p) {
this.filter = p; // just store it, don't apply it yet
return this; // in reality: return a new stream
}
public List collect() {
for (Object o : stream) {
if (filter.test(o)) list.add(o);
}
return list;
}
Java-Interface -
1) Abstract method are always public, reason behind this is that you cannot make private abstract method because some one other needs to call
those methods when you write Interface as a code.
Quetion - Why we should follow Interface while declaring an object class. in example you can see we have declared as Interface and instantiated a class.
Example : List<String> list = new ArrayList<>();
Answer - 1) By providing Interface as a contract, we are only publishing the methods those are writtern in interface to client(One who is going to use arrayList).
So, apart from these contracted methods client cannot access other methods that present in the class.
2) Using interface seggregation, let's say if class shop have addItem and getItem methods. And customer class want to getItem, But also customer
class should be prohibated from using addItem method. Same for Worker class, for him he has access to addItem but he should prohibited from getItem.
So, using point 1 we cannot achieve this. So, here we can use Interface sagregation and we can create Interface Like ShopRead(Contains only getItem method)
and shopWrite(Contains only addItem method). So by doing this we can provide different contract to different clients and they can access only those methods
which are present in contract.
2) Static vs Default vs Private method in interface:
2.1) Default = Default method introduced in java8 to support backward compatibility in interface. Default method cannot be abstract and
implemenentation must be provided in interface. Default method can be overriden by child class and hence default method cannot be private.
Anyone with default method can enhance their interface without breaking their contracts.
2.2) Static = Static method are bound to interface, static method cannot be abstract, implementation must be provided in interface. Static method cannot be overrident by child class.
2.3) Private = Private method is part of java 9, private method cannot be abstract, implementation must be provided in interface and as this is private method hence it can't be override
3) WHat is marker interface = Interface are those we used in java to mark or tag that class have some special behaviour. Like serializable we implement
so we are telling jvm that serialize this object while transferring this to outside of jvm.
4) Functional Interface = Functional Interface is introduced in JAVA 8. With annotation of @FunctionalInterface that interface marked as
functional interface. Interface should have only one abstract method.
Why only one abstract method -> Java created functional interface so that it can enable functional programming in java. They introduced
lambda expressions and within lambda expression syntax in java we pass functional interface abstract method. Since we have single abstract
method it by default enables lambda expression to execute only single method.
Hence, without functional interface lambda expresion wouldn't be achievable.
INternal working - https://amanagrawal9999.medium.com/internal-working-of-functional-interface-and-lambda-expression-d6a19e5d2f46
public class ExtendsAndImplementsDemo{
public static void main(String args[]){
Dog dog = new Dog("Tiger",16);
Cat cat = new Cat("July",20);
System.out.println("Dog:"+dog);
System.out.println("Cat:"+cat);
dog.remember();
dog.protectOwner();
Learn dl = dog;
dl.learn();
cat.remember();
cat.protectOwner();
Climb c = cat;
c.climb();
Man man = new Man("Ravindra",40);
System.out.println(man);
Climb cm = man;
cm.climb();
Think t = man;
t.think();
Learn l = man;
l.learn();
Apply a = man;
a.apply();
}
}
abstract class Animal{
String name;
int lifeExpentency;
public Animal(String name,int lifeExpentency ){
this.name = name;
this.lifeExpentency=lifeExpentency;
}
public void remember(){
System.out.println("Define your own remember");
}
public void protectOwner(){
System.out.println("Define your own protectOwner");
}
public String toString(){
return this.getClass().getSimpleName()+":"+name+":"+lifeExpentency;
}
}
class Dog extends Animal implements Learn{
public Dog(String name,int age){
super(name,age);
}
public void remember(){
System.out.println(this.getClass().getSimpleName()+" can remember for 5 minutes");
}
public void protectOwner(){
System.out.println(this.getClass().getSimpleName()+ " will protect owner");
}
public void learn(){
System.out.println(this.getClass().getSimpleName()+ " can learn:");
}
}
class Cat extends Animal implements Climb {
public Cat(String name,int age){
super(name,age);
}
public void remember(){
System.out.println(this.getClass().getSimpleName() + " can remember for 16 hours");
}
public void protectOwner(){
System.out.println(this.getClass().getSimpleName()+ " won't protect owner");
}
public void climb(){
System.out.println(this.getClass().getSimpleName()+ " can climb");
}
}
interface Climb{
public void climb();
}
interface Think {
public void think();
}
interface Learn {
public void learn();
}
interface Apply{
public void apply();
}
class Man implements Think,Learn,Apply,Climb{
String name;
int age;
public Man(String name,int age){
this.name = name;
this.age = age;
}
public void think(){
System.out.println("I can think:"+this.getClass().getSimpleName());
}
public void learn(){
System.out.println("I can learn:"+this.getClass().getSimpleName());
}
public void apply(){
System.out.println("I can apply:"+this.getClass().getSimpleName());
}
public void climb(){
System.out.println("I can climb:"+this.getClass().getSimpleName());
}
public String toString(){
return "Man :"+name+":Age:"+age;
}
}
Definition - Abstraction is concept where we hide our implemenation from user and provide some interface to user, using that user can interact
with our application.
Uses of abstraction -
1) Using abstraction we are hiding our implementations from end user
2) In terms of java using abstraction you can have multiple implementation of single interface/definitons. Which you can use to not change
a contract from user, like user does not need to know what is happening behind.
3) You can achieve code reusablity using abstraction and in java using abstract classes. E.g - You have tax abstract class where your all
abstract method would be there and you can have one concrete method for general tax calculation. So whatever classes you would be implements from abstrac
will have that code reusablity using this abstraction
4) When you work on monolith application or library where in single code other people wants to get implementation of your code. You can give them
interface or contract so they can use them.
5) Using interface in microservice is not a good idea, untill you really requires it.
How you can achieve abstraction in Java -
1) Using interface
2) Using abstract class
-------------------------------------------------------------------------------------------------------------------------
Re "hiding the implementation" -- it simply means that the client would have no idea exactly how something is done.
Take for example a method of an interface like List. The client code doesn't know or care about how the add() method would be
implemented. All the client cares is that an element will in fact be added to the list when this method is invoked.
How that happens exactly will be up to whatever the author of a particular implementation decides to do.
In short, using interfaces is all about the intent of the client, not the implementation.
-------------------------------------------------------------------------------------------------------------------------
Links -
1) https://medium.com/w-logs/think-twice-before-implementing-interface-classes-in-microservices-abc67ae11fac
2) https://stackoverflow.com/questions/7716435/why-would-you-declare-an-interface-and-then-instantiate-an-object-with-it-in-jav
Definition - Encapsulation in oops which means we are binding data into a kind of storage, which will be not be accessbile by outside world.
In java terms we can have private variables where this private variables will not be accessible by other classess.
To access private members we can have getter methods which will provide your data for read only to outside class.
Definition - It is the mechanism in java by which one class is allow to inherit the features(fields and methods) of another class.
In java we have use inheritance by using extends keyword.
Inheritance does not inherit private members.
Using inheritance we are achieving code reusability.
Definition - Polymorphism is term which says we can have single object can have many forms.
Compile-time polymorphism (Method overloading) ->
method abc(a, b)
method abc(a, b, c)
Run-time polymorphism (Method Overriding) ->
class a.method abc(a)
|
Inherit
class b.method abc(b)
1) Optional is a container object used to contain not-null objects.
Optional object is used to represent null with absent value.
This class has various utility methods to facilitate code to handle values as 'available' or 'not available' instead of checking null values.
2) Null handling is present inside optional API, so you need to worry all those scenarios and you can deal with objects in oops.
3) There is little performance overhead occurs in optional.
1) We would need a static variable if its value doesn’t need to be altered i.e. constant. Constants are in fact public static final by definition. Critically used by interfaces.
2) Static variables are useful in inheritance hierarchy also. Although both static and instance variables are useful, but I was just extending the specific use of statics to inheritance.
3) We can have static methods that can serve as getters and setters of static variables. These would come in handy if an instance is not needed to invoke them in main method.
4) One of special reasons to use static variables and static methods is a singleton class. They use static variables and methods to return their only instance.
Example of static class -
A good use of a static class is in defining one-off, utility and/or library classes where instantiation would not make sense.
A great example is the Math class that contains some mathematical constants such as PI and E and simply provides mathematical calculations.
Requiring instantiation in such a case would be unnecessary and confusing.
Static variables/fields stored in heap itself after java 8, and it is a class level variables can access by anyone. And static variables loads
in memory at time of class loader(initialize phase).
https://www.google.com/amp/s/www.geeksforgeeks.org/java-string-is-immutable-what-exactly-is-the-meaning/amp/
Thread - Thread is smallest unit of process, it is lightweight and it can be managed by schedular.
Use of threads -
1) We can achieve parrallel works, we can run multiple part of programs using threads without blocking each others.
2) Async we can achieve.
Thread: It simply refers to the smallest units of the particular process. It has the ability to execute different parts
(referred to as thread) of the program at the same time.
Process: It simply refers to a program that is in execution i.e., an active program. A process can be handled using PCB
(Process Control Block).
Ways to create thread in java -
1) Extending java.lang.Thread class.
2) Implementing java.lang.Runnable interface
Internal thread creation process -
1) When we extend class with Thread class. -> When we extend class with Thread class, and we create a object of that class. Then we invoke
start() method of that object(start() method is thread class internal method).
1.1) When start() method gets invoked internally it creates a new thread, and add that thread in thread group to notify others that
thread is created.
1.2) There is native method start0() which gets invoked(I suppose this method is mainly responsible for creating new thread and calling run() method)
2) When we use Runnable interface -> Runnable interface is another way of creating threads. The reason they used runnable interface is because
if we used extends then we are limiting the inheriting of our class. Apart from this runnable helps us to create threads using lambda interface.
As Runnable Interface is Functional Interface.
As we are writing our logic inside run method of runnable interface, framework like executorService or completebleFuture we can pass our logic to this
frameworks and they will execture our logic. They will take care of thread creation and handling.
2.1) When we use runnable interface means we have implementation of run() method and we just passed that implementation to Thread class.
like this Thread t = new Thread(MyClassWithImplementedRunnableInterface); So here Thread is created with the our Runnable implementation that
we provided.
2.2) When we call t.start() method it does same as 1.1) 1.2) above points, additonaly as we have our runnable implementation, while calling
start0() this thread invokes our runnable implementation run() methods.(This is my theroy only)
B) Exectuors Framework =
1) This frameworks helps to create threads by using it's factory method. And help us to submit actions(Runnable/Callable) interface. And those
actions will be run by this frameworks.
2) Hierarchy is Exector -> ExectuorService -> ScheduledExectorService
3) In this service we can passed customize ThreadFactory to have our own thread creation implementation.
4) ManagedExectorService = This service is extension of Java exectuor framework, and part of EE edition.
4.1) This service basically created because EE edition threads are managed by server or any container. So ExecutorService doesn't need any web container,
where as ManagedExecutorService is used in the context of application deployed to a webserver, where threadpools are created and their
life cycles are maintained by the container.
4.2) If we are using ExecutorService It is discouraged because all resources within the environment are meant to be managed, and potentially monitored, by the server.
4.3) https://stackoverflow.com/questions/533783/why-is-spawning-threads-in-java-ee-container-discouraged/533847#533847
Runnable vs Callable -
1) Runnable interface does not return anything, so whatever logic we are going to put into runnable that logic will be run without returning
anything. Usecase of this is fire/forget task or logging.
2) Callable interface return the Generic value what we will provide, with wrapped inside Future block. So when we call future.get() method
it executor service will block the current thread and wait for what callable interface method is returning.
Executor framework have submit method which do method overloading for callable and runnable.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment