Data Structures
Collections
- ‘Collection’ in general refers to a single unit of objects.
- In Java, the Collections framework provides many Interfaces (Set, List, Queue, Deque, Map) and Classes (ArrayList, Vector, LinkedList, PriorityQueue, HashSet, LinkedHashSet, TreeSet).
- The interface
Collection
extendsIterable
.
- ArrayLists, Vectors, Queues (with implementations using the former as containers) use
add()
andremove()
, maps useput()
(to add key-value pairs) andremove()
, stacks usepush()
andpop()
, and queues useadd()
andpoll()
. - ArrayLists vs LinkedList: Inserting/Removing are way faster due to O(1) operations, especially useful for large lists (best bet if there are not too many accesses). ArrayLists are good at retrieving/accessing values (good for static lists), but terrible for adding and removing elements. (has to create a separate array and copy over elements, then add the new one in the required position for instance)
- ArrayLists can hold just objects like it cannot use primitives, but wrapper classes (for instance,
Integer
would work but notInt
) - Arrays vs ArrayLists: The basic difference is that the former has a fixed size, whereas the latter is dynamic (use
ArrayName[index]
vsArrayListName.get(index)
for accessing, useArrayName.length
vsArrayListName.size()
, field vs method call), ArrayLists are a wrapper over the base Arrays which makes working with them easier to deal with (does add overhead for copying and recreation of arrays when adding and removing values, but the difference in speed or performance loss doesn’t matter for regular/small cases in comparison to arrays) - Maps: Collection of key-value pairs, acts as a lookup table often. Use the
get
method with a key to obtain its corresponding value if present, checks usingcontainsKey(key)
orcontainsValue(value)
methods,put(key, value)
to change the value for a key and to add the key-value pair if not present,replace
to change the value for a key but not add if the key-value pair (or simply the key) is not present etc. -
- HashMap O(1) lookup and insertion, implemented by an array of linked lists
- TreeMap O(log N) lookup and insertion, keys are sorted, must implement the Comparable interface. It is implemented by a Red-Black Tree
- LinkedHashedMap O(1) lookup and insertion, keys are ordered by the order of insertion, implemented via doubly-linked buckets
- Hashtable is thread-safe, whereas HashMap is not
- At a time only one thread is allowed to operate the Hashtable’s object. Hence it is thread-safe.
- Hashtable<Integer, String> ht = new Hashtable<>();
- Vector (implementation of List, and extends class Stack) is thread-safe, whereas ArrayList is not, hence vector is slow (the same case, due to some sort of locking mechanism behind the scenes probably given by mutexes/semaphores that guarantees atomicity for singular-thread access but makes it lockstep-slow),
- Dictionary having a large number of words: Would probably use an ordered List (to maintain the lexicographical order) where one can perform a binary search, or a TreeSet if duplicate words are not allowed
- PhoneBook: HashMap or nested Hashmaps, depending on the number of fields
- IntelliSense or text-based auto-completion in IDEs: would use a Trie (prefix tree)
- Queue interface implemented as ArrayDeque, LinkedList, and PriorityQueue
- Stack is a class (extended by Vector, which is an implementation of List)
- Performance-wise, HashSet > LinkedHashSet > TreeSet
By using Set/List/Queue interfaces instead of its implemented class(es), I can change the data structure: (upcasting)
import java.util.*; //Iterator and Collection used here. // Set<String> s = new LinkedHashSet<String>(); // Don't need to use the type again, and simply use the diamond operator post Java 7 Set<String> s = new LinkedHashSet<>(); Set<String> s = new TreeSet<>(); // upcasting, since every TreeSet is a Set // Superclass/Interface <WrapperClassforPrimitive> = new SubClass<>(); Set<String> s = new HashSet<>(); for(String elements : args) s.add(elements); Iterator <String> i = s.iterator(); while(i.hasNext()) System.out.println(i.next()); LinkedList<String> l = new LinkedList<>(); l.add("Greetings"); l.add("visitor!"); Iterator<String>itr = l.iterator(); while(itr.hasNext()) System.out.println(itr.next()); Collections.sort(l); // ascending order Collections.sort(l, Collections.reverseOrder()); // descending order
import java.util.*; class MapExample { public static void main(String args[]) { Map<Integer,String> map = new HashMap<>(); // <Integer, String> implicitly taken map.put(100, "Hundred"); map.put(101, "HundredOne"); // Elements can traverse in any order: for(Map.Entry m : map.entrySet()) // For-each loop { System.out.println(m.getKey() + " " + m.getValue()); } } }
Stack<Integer> s = new Stack<>(); public class Stack<E> extends Vector<E> Queue<Integer> q = new Queue<>(); Queue<Node> q = new LinkedList<>(); public interface Queue extends Collection PriorityQueue<E> pq = new PriorityQueue<E>();
Iterators
An Iterator is an object that enables one to traverse through a collection and to remove elements from the collection selectively when desired. An Iterator for a collection is obtained by calling its iterator()
method.
Iterator interface:
public interface Iterator<E> // E means any type, a generic template type.
{
boolean hasNext();
E next();
void remove(); // Optional
}
Microservices
- General Architecture implies breaking down or segregating a big application into smaller pieces (self-sufficient in their way, but needs communication among these sub-applications or processes to perform a larger task; individually, each microservice solves a smaller problem somewhat like a function/bunch-of-functions or module), basically smaller autonomous units of deployment involving a different process for each when run together.
- They are independent and loosely coupled, and the communication happens via APIs, with the payload being JSON usually (could be XML or some other form, but since JavaScript object notation works well with JS, it is fairly common; also JSON strings can be parsed in Java, apart from integrating easily with JS and being easier to read than forms like XML).
- API acts as an interface that sends the requests and gets back to you with a response, usually over HTTP (basically transferring text) with get requests thrown at an endpoint which someone can hit at and get a response back at.
- Used in opposition to the Monolithic architecture, wherein an application is deployed as a single consolidated unit/package which executes as one thing, and is tightly coupled. (fault tolerance problem exists)
- Monolithic applications need to be scaled vertically given there is only one process, but there is a limit and we need to resort to horizontal scaling, i.e. taking more processes/systems for scaling, which needs microservices
- Changes are specific to one microservice, and not the entire application (avoids testing and breaking the whole thing, fault-tolerant), and overall it’s faster to deploy changes individually or independently, but have to test and monitor each
- Can also mix with other services or libraries and frameworks
- One challenge is how one microservice or process discovers others (service discovery), as they have to rely on each other (distributed systems type of communication) to perform bigger tasks. Another core challenge is to create an efficient split of the tasks to microservices, such that they are modularized well, such that for a new feature one does not has to change ten other microservices. (could be complex to achieve this sometimes)
- Overall, deployment, monitoring, and managing a product release might be more complex, although certain things are simplified - there is a trade-off and we need to find the sweet spot for profit and convenience.
- Semantic monitoring: Pertains to observing and testing independent workflows for critical business operations. For instance, say there’s some merchandise available to purchase from the DreamWorks website, then the checkout process could be handled by a microservice (calling a payment API like Stripe for instance), which is critical (involves transactions) and has to be monitored separately and tested sufficiently. (the semantic definition of services defines them as stateless reusable objects, preferably with singleton scope, that are used to perform business logic operations on other objects passed as arguments)
- In general too, a robust monitoring system should be in place for observing resources being used (memory, CPU cores, etc.) by a microservice
- Client certificates: A digital certificate used to make an authenticated request to a server (or a secure request to an endpoint), digitally signed via some industry-level encryption scheme (probably ECDSA with a key length of 256 bits, given that asymmetric cryptography with elliptic curves are the norm still/work well enough for the moment)
- Spring reference: Defines microservices to be small, self-contained, ready-to-run applications.
- Types of testing that exist in microservice architecture:
- Unit Testing (at the unit level, probably something like JUnit or Mockito)
- Integration/Contract Testing (involving performance tests)
- Acceptance Testing
- OAuth: Open Authorization Protocol, which, as indicative from the nomenclature itself, allows one to access client applications on HTTP for third-party providers (for example, GitHub, can be used with tokens to connect with DreamWorks Careers).
- CDC (Consumer-driven contract) - Consumer/Client specifies what the contract is, directs it entailing details as to what they want (and we as developers or service providers have to prepare according to their needs instead of coming up with our own stuff or going with defaults)
- Common tools used for microservices include Postman and Docker (for containerization, wherein containers are used in microservices because deployment is fairly common, with the need to develop and deploy n number of machines with either same or different configuration/settings with particular software to be installed; in order to do this in a hassle-free manner for multiple machines, using Docker or a related container service becomes a must. For instance, I might need to deploy EC2 instances to multiple machines which is an example use-case)
- RESTful (Representational State Transfer) is a way to build APIs that follow the rest specification/protocol that involves treating an individual resource and having an endpoint that maps to the resource (to be updated)
- Java Database Connectivity (JDBC) is an application programming interface (API) for Java, which defines how a client may access a database. Basically, an interface used for database connectivity, with software like Hibernate abstracting some of its operations for ease of ORM
Spring
Framework for convenience, featuring:
- Dependency injection
- Database integration
- Spring MVC
- Alternative to Spring: Java Enterprise Edition (EE) What does the Spring framework provide: (right now an ecosystem) Let’s see, we create a java program with classes, objects are created, related to each other After initialization is done, there is a network of objects connected via some references Initialization of objects and the object graph should be pretty much the same over time instead of morphing Instances of class with business logic
For any business class that has some functionality to offer, the only instance of the class is required Every class that needs such services should share the same instance Singleton design pattern - implement only one single class
- How do we share objects is the main question here -> the first problem Spring solves
It acts as a container for your application, adds a wrapper called ‘Spring Context’, Spring initializes the instances (one or multiple) and manages them for us, connects the objects, like every class declares dependencies (say class A ones one instance of class B), so spring ensures each process gets their dependencies through dependency injection
Dependency is not hardcoded in the class, but it is rather injected by an entity outside the class (separate the class which is dependent and inject them when needed from outside) It’s about decoupling/separating and delegating dependencies out of the class where it matters.
- Database connectivity: the traditional method of connecting to a database in Java is via JDBC
Spring Framework comes with various data access APIs and mechanisms for connectivity, querying, and transaction management, allows you to continue to work with JDBC and hides or abstracts much complex stuff, making the experience better (easier, convenient)
- Web applications: many enterprise applications are web applications, they need to either serve up dynamic HTML pages (frontend) or they need to expose REST apis that can be consumed by web applications or rich JS applications, to facilitate this, Spring Framework comes with a web framework call MVC to help with. Spring Boot An open-source Java-based framework is used to create a microservice.
- The framework for packaging the spring application with sensible defaults.
- Spring Boot does that for you with Zero XML configuration in your project
- bundles all the dependencies for you. Finally, your web application will be a standalone JAR file with embedded servers.
- Controller: deals with the HTTP requests (get/post) to and from an endpoint or API
- Repository: deals with the database or storing of data
- Other components exist (to be updated)
Basics
- Class naming convention: all starting letters of words in capitals (ClassName for instance)
- Constants: all capitals (e.g., PI)
<>
Diamond operator, used in generics and for type specification- Package name convention: prefix in small letters (e.g.: package test, package java.util.Scanner), function names: funcName (camelCase)
- All objects are created in the heap section of memory.
- Arrays are of fixed length just like in C++ (
String[] arrName = new String[50];
) - Filename should match public class name (only one) If not -> throws compiler error
- Native method stack can access runtime data areas of the virtual machine.
- Stuff present inside JDK: JRE, libraries and files, compiler debugger, and developmental tools
- Types of variables: local, static (class), object (instance)
- Constructor is a block of code that initializes the newly created object. It resembles an instance method in java and does not have a return type.
- Constructor types: default/no argument, parameterized (for copy constructor, simply take the object as parameter) - invoked using ‘new()’
- Base class constructor is called first, or the topmost/oldest (grandparent->parent->child) one is called first.
- Defaults: classes are private, booleans are false.
- Error types: logical, syntax, runtime
- ‘Final’ keyword in Java: (depends on the context of use)
- Final classes: to stop not being extended (or inherited) further (
public final class X {}
) - Final methods: to stop parent class method from not be changed or overridden for child/derived classes (use final keyword in parent class method to prevent it from being changed for the child classes)
- Final variables: once initialized, it cannot be changed or assigned to something else (useful for creating constants)
- Final classes: to stop not being extended (or inherited) further (
- ‘Super’ keyword in Java: used to call the parent method of the superclass, only use when we need to use parent class implementation for a method that you have overridden in your subclass of that parent (and note that private methods of the parent class can’t be accessed, even if super is used in the subclass, it will not be visible)
- ‘This’ keyword is used to specify to Java that we are referring to the field on the object that this method was called on:
public class X { private String name; public void SetName(String name) { this.name = name; // name = name won't make sense! } public X() { this("Anirban166"); // call the parameterized constructor below } public X(String name) { this.name = name; } }
- Java compiler (javac) generates byte code (an intermediate form, closer to machine code representation) from source code for cross-platform support. (interpreted by different JVM interpreters for different operating systems)
- javap - print bytecodes, javadoc - documentation generator, jdb - debugger, javaprof - profiler
- Use Maven or Gradle for building instead of Make, since javac (compiler to convert Java into bytecode) is slow to start up
- public static void main(String[] args) (global access specifier visible to all, called directly on the class instead of an object of main, doesn’t return anything)
- JRE needs to call the main method, the use of public makes it visible to the JRE and JVM
- Designed to make the call directly on the class (not gonna be called on an instance of the main class), so static
- Doesn’t return anything so void
- Array of strings - arguments to my program (check ith argument using
System.out.print(args[i]);
, where i = 0 to n-1, where n is the number of arguments one passes)
- The main program also runs if we write
static public void main
(interchange public and static) since there is no specific rule for the order of specifiers in Java.
Access specifiers
- Private: same class only.
- Public: everywhere.
- Protected: same class (of course) and any subclass that inherits.
- Default: same class, same package.
Types
Primitives: (basic built-in types, others can be derived from these) Int, Char, Boolean, Byte, Short, Long, Float, Double.
Promotion: byte → short → int → long → float → double. Can’t downcast automatically -> throws compile-time error.
Non-primitives: Arrays, Strings, Classes, Interfaces.
Strings
Every string one creates is actually an object of type String in Java. Even string constants are actually String objects. These objects are immutable, i.e. once a String object is created, its contents cannot be altered (if we change a string object’s value, the object is not changed, a new one is created with the new value and the reference is moved to that instead). If the need arises to change a string, I can instead of creating a new one that contains the modifications, use the peer class of String called StringBuffer, which allows strings to be altered.
Java saves strings in a pool, and if a string object points to a string variable already existant in the pool, a separate value is not created, but the reference is shifted to that value in the pool automatically. If we use the new keyword, however, it won’t use the shared object in the string pool, instead, it will create a brand new object outside the string pool with the same value.
String name = "X"; // In a string pool
name = "Y"; // String object's value isn't changed in memory -> immutable
String anotherName = "X"; // In the same string pool, using less memory, that's the reason strings are immutable
// Instead of assigning a string literal, if we use the 'new' keyword, Java will create a separate object not in the pool even with same value:
String yetAnotherName = new String("X"); // Not in the string pool
The same goes for comparison, as strings will have to use the equals
method instead of ==
because what the latter does is it compares the memory location referenced by those string objects, instead of comparing the value (which can be done if we just use literals, instead of comparing two different objects).
Exceptions
- Unwanted event (can occur both during compile and run time), e.g. parse int which cannot be parsed or isn’t an integer (
int myInt = Integer.parseInt("hey");
, statements after the exception throwing statement in the try block will not be executed) - Use a
try
block to catch an exception - Use a
catch(Exception e)
block to execute code that we want to run for that caught exception - Use a
finally
block to execute code irrespective of whether the exception was caught or not, and this shit always executes (and also overrides return stuff like return statements with respect to try or catch blocks, for instanceint method() { try { return 1; } catch(Exception e) { return 2; } finally { return 3 }; }
will make the method return 3 always.
Exceptions implement throwable. Object->Throwable->Errors and Object->Throwable->Exceptions (compile time, I/O), Unchecked (null pointer)
try { } catch(ExceptionClass ex) { print() }
ExceptionClasses e.g.: IOException, AWTException,
Runtime Exceptions: IndexOutOfBounds, InputTypeMismatch, ArithmeticExpression, NullPointerException, IllegalArgumentException
Errors include Linkage and VM errors.
Errors vs Exceptions: An Error indicates a serious problem that a reasonable application should not try to catch, while an exception indicates conditions that a reasonable application might try to catch.
Runtime exceptions and errors are unchecked exceptions. (rest are checked, or rather the compiler forces us to check)
Every method must show their thrown exceptions - eg: void method() throws IOException, OtherException
Static Binding
In case of method overloading, the JVM can decide which method to be called at compile-time based on the number and type of parameters. This is also known as static binding or static polymorphism or compile-time polymorphism.
Wrapper class
A wrapper class wraps (encloses) around a data type and gives it an object appearance. Whenever the data type is required as an object, this object can be used. There are eight wrapper classes available in java.lang package.
Garbage collection
Garbage collector is a program in Java that manages the de-allocation of objects in memory rather automatically without the need for us to do it explicitly or manually. (which I need to do via the use of ‘delete’ in C++, unless I am using smart pointers) When there are no references to an object, it is assumed to be no longer needed and the memory occupied by the object is released. It’s important as memory leaks occur (naturally) if not de-allocated. (A common example would be the Scanner object’s case)
OOP
Polymorphism
- In general, it means many forms of (many-shapes), In Java, it relates to methods with the same signatures. Overloading and Overriding
- Method overloading: Two or more methods within the same class that share the same name but with different parameter declarations. When an overloaded method is invoked, Java uses the type and/or the number of arguments as its guide to determine which version of the overloaded method to actually call during compile time.
- Method overriding: Two methods with the same method name, signature and return type in different classes, related by inheritance.
Represents runtime polymorphism. (single action can be performed by many ways)
Parent object = new Parent();
->object.method()
here will call the parent class method as expected. But if a classChild
extendsParent
thenParent object = new Child();
->object.method()
will call the child class method, withmethod()
being overridden.
Worth nothing that Final, Static and Private methods cannot be overridden.
Pass by Reference (or pass by object)
Simply use objects as parameters, for e.g.:
class test
{
int x,y;
test(int i, int j) { x = i; y = j; }
void func(test obj) { obj.x = 2; obj.y = 2; }
}
public class CallByRef
{ main
{ test obj = new test(1,1);
print(); obj.func(); print();
}
}
Interfaces and multiple inheritance
Multiple inheritance is not supported by Java using classes, since handling the ambiguity and complexity that causes due to multiple inheritance tends to be very complex. It creates problems during various operations like casting, constructor chaining etcetera and above all, the prime rationale is that there are very few scenarios where we actually require multiple inheritance, so better to omit it for keeping things simple and straightforward.
Java 8 supports default methods where interfaces can provide default implementation of methods. And a class can implement two or more interfaces. In case both the implemented interfaces contain default methods with the same method signature (name+type), the implementing class should explicitly specify which default method is to be used or it should override the default method. (use default before function type - eg: default void function)
Uses of interfaces:
- It is used to achieve total abstraction.
- Java does not support multiple inheritance in the case of a class, but by using an interface, one can achieve multiple inheritance.
- Used to achieve loose coupling.
- Abstract classes may contain non-final variables, whereas variables in an interface are final, public and static.
Note that the class which implements the interface needs to provide functionality for the methods declared in the interface. A few more points to be taken note of are:
- All methods in an interface are implicitly public and abstract
- An interface cannot be instantiated (like an abstract class)
- An interface reference can point to objects of its implementing classes
- An interface can extend from one or many interfaces. A class can extend only one class but implement any number of interfaces
Abstract class One you can’t instantiate or create objects out of. (public abstract class X
-> can’t use X objectName = new X();
) While the class won’t have objects, its child/derived classes the abstract methods of that class will need to be implemented.
We can only extend one abstract class, but we can implement any number of interfaces.
Everything field/variable declared inside of an interface is static and final, so we mostly use methods than those.
Abstract class vs Interfaces
- Check this thread for a much better and comprehensive conceptual and when-to-use type answers
- Interfaces can have only abstract methods, whereas abstract classes can have abstract and non-abstract methods. From Java 8 onwards, it can have default and static methods as well.
- Variables declared in a Java interface are by default final whereas an abstract class may contain non-final variables.
- Abstract classes can have final, non-final, static and non-static variables. Interface has only static and final variables.
- An abstract class can provide the implementation of an interface. But an interface can’t provide the implementation of an abstract class. (one-way street)
- A Java interface can be implemented using the keyword “implements”, whereas an abstract class can be extended using the keyword “extends”
- An interface can extend another Java interface only, but an abstract class can extend another Java class and implement multiple Java interfaces.
- Members of a Java interface are public by default. On the other hand, a Java abstract class can have class members with all access specifiers, including private and protected.
- Both abstract classes and interfaces are used to achieve abstraction up to some degree, and the way I like to think of it is that they provide some sort of skeleton or boilerplate for one to extend or implement in their classes respectively,
- A class can implement multiple interfaces (and that’s how it achieves multiple inheritance is a pseudo manner) but it can extend only one abstract class
- Abstract classes are designed for inheritance and they could mask or account for what is being covered in interfaces (given the latter has only methods, with its fields being final)
When to use which?
- I’d use an abstract class when I want each individual object of the derived classes should have its own different values, and if I have a lot of closely related classes I’d want to have the same functionality and the same types of fields/variables available.
- I’d use an interface (every method in an interface is assumed to be abstract, interface can have any number of implementations, every field is static and final, same value applies to all objects that implement the interface) instead if I want a bunch of unrelated classes that I want all to be able to do a certain thing or exhibit a certain behaviour given by methods which are common in some sense and representable via a skeleton.
Concurrency, Multi-threading and Parallelism
Threads
Thread is a path of execution in a process, and in Java, there is a Thread
class given by public class Thread
which extends Object
or implements Runnable
. (i.e., it either extends class Thread
or implements Runnable
)
The first approach can be used to extend thread to a subclass we require, whereas the second one takes it up as an implementation of the Runnable interface. (with public and final methods)
- Basically, have a class extend the Thread class, then have it override the run method (or then call objects of that class using the
start()
method in main) - Have a class implement the Runnable interface and then define the run method (or for an object ‘X’ for that class, create a new Thead object and use
start()
on it) - Then have it override the run method and implement the public void run() method ()
- Note that every chunk of code is not thread-safe, and some collection objects aren’t (ArrayLists and HashMaps aren’t for e.g., whereas Vector and Hashtable are, but of course slower due to locking and sequential execution)
//Using subclasses of Thread: class ThreadA extends Thread { public void run() { for(int i = 1; i <= N; i++) { System.out.println("Hello from thread X with index = "+ -1*i); } System.out.println("Exiting Thread X."); } } // Using the Runnable interface: public interface Runnable { public abstract void run(); } class ThreadExample implements Runnable { public void run() { for(int i = 1; i <= N; i++) { System.out.println("Hello from thread with index = "+ -1*i); } System.out.println("Exiting Thread."); } }
A thread can be directed to start its body by the
start()
method:public class Test { public static void main(String args[]) { ThreadA a = new ThreadA(); a.start(); } }
The Java Virtual Machine allows an application to have multiple threads of execution running concurrently.
Some commonly used methods include:
currentThread()
: CurrentThread() is a static method that returns the Thread object which is the currently running thread.
setName(String s)
: SetName() is used to assign a name (s) for a thread prior its execution. This identifies the thread with a string name and is helpful for debugging multi-threaded programs.
getName()
: This method returns the current string value of the thread’s name as set by SetName().
getPriority()
: This method returns the thread’s current priority, which is a value between 1 and 10.
setPriority(int p)
: This method sets the thread’s priotity to an integer value p passed in. There are several predefined priority constants defined in class Thread: MIN-PRIORITY, NORM-PRIORTY and MAX-PRIORITY, which are 1, 5, and 10 respectively.
isAlive()
: This method returns true if the thread is started but not dead yet.
isDaemon()
: This method returns true if the thread is a daemon thread.
Every thread has a priority. Threads with higher priority are executed in preference to threads with lower priority. Each thread may or may not also be marked as a daemon. When code running in some thread creates a new Thread
object, the new thread has its priority initially set equal to the priority of the creating thread and is a daemon thread if and only if the creating thread is a daemon.
Daemon Threads
Threads that run in the background and are created by the JVM for performing background tasks like garbage collection. Any thread created by the main thread which runs the main method in Java is by default non-daemon because Thread
inherits its daemon nature from the thread which creates it i.e. the parent Thread
and since the main thread is a non-daemon one, any other thread created from it will remain non-daemon until explicitly made daemon by calling setDaemon(true)
.
Thread.setDaemon(true)
makes a Thread daemon but it can only be called before starting Thread
in Java. It will throw IllegalThreadStateException
if the corresponding Thread
is already started and running.
The core difference between Daemon and Non-Daemon threads in Java is that the JVM doesn’t wait for any daemon thread to finish before exiting.
public class DaemonThreadExample1 extends Thread
{
public void run()
{
// Checking whether the thread is Daemon or not
if(Thread.currentThread().isDaemon())
{
System.out.println("Daemon thread executing");
}
else
{
System.out.println("User (normal) thread executing");
}
}
public static void main(String[] args)
{
// Creating two threads: by default they are user threads (non-daemon threads)
DaemonThreadExample1 t1 = new DaemonThreadExample1();
DaemonThreadExample1 t2 = new DaemonThreadExample1();
// Making user thread t1 to Daemon
t1.setDaemon(true);
//starting both the threads
t1.start();
t2.start();
}
}
Using thread pools would be better instead of threads per session.
To be updated: Producer-Consumer Scenario, Mutexes, and Semaphores (block/wait mechanisms in order to ensure thread-safe or atomic operations to avoid race conditions)
- Lambdas:
public interface Printable { void Print(String suffix); } main { Printable lambdaVar = (args...) -> System.out.println("Hi"); printStuff(lambdaVar) } static void printStuff(Printable stuff) { stuff.print(); }
To be updated
JVM
Java virtual machine is an engine that provides a runtime environment to drive the Java Code or applications.
It is also a virtual machine that enables a computer to run Java programs as well as programs written in other languages that are also compiled to Java bytecode. It then converts Java bytecode into machine language. Each java application uses an independent JVM, and each JVM is a seperate process, with no sharing of stacks or heaps. (only thing shared is the read-only code segments that hold the code for JVM and native libraries)
Compiled vs Interpreted
A good thread for lookup.
IDEs VSC, Eclipse, IntelliJ, intellisense/auto-complete feature being made available via Trie (prefix tree).
- REST defines constraints like client-server architecture, statelessness, cache ability (clients and intermediaries can cache responses),
- Socket programming in java: Socket s = new Socket(IPaddress, port); // (String, int), http runs on port 80, 127.0.0.1 localhost Scanner is a class for reading from the keyboard when taking the special object
- Singleton: Only one object of the class can be defined at the time
- When a Java program is compiled via Javac (java compiler), bytecode is generated in the form of a .class file. This .class file is then interpreted by the JVM which translates it into machine level or assembly instructions and executes that during the run time.
- It is the JVM that is built and customized for each platform that supports Java.
- The super keyword is a reference variable that refers to an immediate parent class object
- Heap is not thread-safe, while Stack is (like HashMap/ArrayList vs Hashtable/Vector), stack only has one thread of execution
- Shallow copy: create copy of pointer or reference, deep copy: copy entire object
- Packages in Java, are the collection of related classes and interfaces which are bundled together. By using packages, developers can easily modularize the code and import it to other classes for reuse.
- We can call a constructor of a class inside another constructor. This is also called as constructor chaining. Constructor chaining can be done in 2 ways:
- Within the same class: For constructors in the same class, the this() keyword can be used.
- From the base class: The super() keyword is used to call the constructor from the base class.
The constructor chaining follows the process of inheritance. The constructor of the sub class first calls the constructor of the super class. Due to this, the creation of sub class’s object starts with the initialization of the data members of the superclass. The constructor chaining works similarly with any number of classes. Every constructor keeps calling the chain till the top of the chain.
- Within the same class: For constructors in the same class, the this() keyword can be used.
- When we create a string using new(), a new object is created. Whereas, if we create a string using the string literal syntax, it may return an already existing object with the same name.
- Steps while connecting to the database:
- Registering the driver class (JDBC driver)
- Creating connection
- Creating statement
- Executing queries
- Closing connection
- Object-relational mapping or ORM is the programming technique to map application domain model objects to the relational database tables. Hibernate is a Java-based ORM tool that provides a framework for mapping application domain objects to the relational database tables and vice versa. Hibernate eliminates all the boilerplate code that comes with JDBC and takes care of managing resources.
Anirban | 12/25/19 |