Java Fundamentals: Streams, Exceptions, Annotations, Class Loading, Keywords, Multithreading, Thread Pools, and Memory Model
This article provides a comprehensive overview of core Java concepts, including the hierarchy of I/O streams, checked and unchecked exceptions, built‑in and custom annotations, the class‑loader delegation model, important language keywords, multithreading techniques, thread‑pool implementations, and the Java memory model.
Streams
All Java stream classes are located in the java.io package and inherit from four abstract stream types.
Type
Byte Stream
Character Stream
Input Stream
InputStream
Reader
Output Stream
OutputStream
Writer
Streams that inherit from InputStream / OutputStream operate on bytes (8 bits). Streams that inherit from Reader / Writer operate on characters (2 bytes).
Exceptions
Java exceptions (both Exception and Error ) are divided into checked and unchecked exceptions.
Checked exceptions
All Exception subclasses except RuntimeException are checked; the compiler forces you to handle them with try‑catch or declare them with throws .
Unchecked exceptions
Includes RuntimeException and its subclasses as well as Error .
Examples of runtime exceptions are NullPointerException and IndexOutOfBoundsException , which are typically caused by logical errors and are not required to be declared.
Non‑runtime checked exceptions such as IOException and SQLException must be handled or declared.
Annotations
Java SE 5 introduced three standard annotations:
@Override // Indicates method overrides a superclass method
@Deprecated // Marks element as deprecated, causing a compiler warning
@SuppressWarnings // Suppresses specified compiler warningsFour meta‑annotations are provided for creating custom annotations:
@Target
Specifies where the annotation can be applied. Possible ElementType values include CONSTRUCTOR , FIELD , LOCAL_VARIABLE , METHOD , PACKAGE , PARAMETER , TYPE , and ENUM .
@Retention
Defines how long the annotation is retained: SOURCE (discarded by compiler), CLASS (present in class file, discarded by VM), or RUNTIME (available at runtime via reflection).
@Documented
Indicates that the annotation should be included in Javadoc.
@Inherited
Allows subclasses to inherit the annotation from a superclass.
Example of a custom annotation and its usage:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface UseCase {
String id();
String description() default "no description";
} public class PasswordUtils {
@UseCase(id = 47, description = "Passwords must contain at least one numeric")
public boolean validatePassword(String password) {
return password.matches("\\w*\\d\\w*");
}
@UseCase(id = 48)
public String encryptPassword(String password) {
return new StringBuilder(password).reverse().toString();
}
}Parsing the annotation at runtime:
public static void main(String[] args) {
List
useCases = new ArrayList<>();
Collections.addAll(useCases, 47, 48, 49, 50);
trackUseCases(useCases, PasswordUtils.class);
}
public static void trackUseCases(List
useCases, Class
cl) {
for (Method m : cl.getDeclaredMethods()) {
UseCase uc = m.getAnnotation(UseCase.class);
if (uc != null) {
System.out.println("Found Use Case:" + uc.id() + " " + uc.description());
useCases.remove(new Integer(uc.id()));
}
}
for (int i : useCases) {
System.out.println("Warning: Missing use case-" + i);
}
}
// Output:
// Found Use Case:47 Passwords must contain at least one numeric
// Found Use Case:48 no description
// Warning: Missing use case-49
// Warning: Missing use case-50Security Features
Strict object‑oriented encapsulation provides data‑level security.
No pointer arithmetic; references cannot be manipulated directly.
Array‑bounds checking prevents buffer‑overflow vulnerabilities.
Forced type casting throws ClassCastException for incompatible types.
Built‑in thread‑safety support at the language level.
Automatic garbage collection.
Robust exception handling mechanism.
Class Loading
Principle
The JVM uses a parent‑delegation model: each ClassLoader has a parent; the bootstrap class loader has none. When loading a class, the request is delegated up the hierarchy (Bootstrap → Extension → Application) before the loader attempts to find the class itself.
The JVM considers two classes identical only if they have the same fully qualified name **and** were loaded by the same ClassLoader instance.
Loaders
Bootstrap ClassLoader
Loads core JDK libraries such as rt.jar , resources.jar , and charsets.jar .
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
System.out.println(urls[i].toExternalForm());
}
System.out.println(System.getProperty("sun.boot.class.path"));Extension ClassLoader
Loads JARs from $JAVA_HOME/jre/lib/ext .
App ClassLoader
Loads classes from the application classpath.
Custom class loaders must extend java.lang.ClassLoader ; the bootstrap loader is implemented in native code and does not extend ClassLoader .
Keywords
strictfp
Ensures that all floating‑point calculations conform to IEEE‑754, providing consistent results across platforms.
transient
Marks a field to be excluded from serialization.
volatile
Prevents the JVM and compiler from caching the variable, guaranteeing visibility of changes across threads.
final
Marks primitive fields as constant. Marks object references as immutable. Prevents method overriding. Can be applied to classes and parameters.
Initialization Order
Static initialization: parent static → child static. Instance initialization: parent fields → parent init block → parent constructor → child fields → child init block → child constructor.
Multithreading
Java supports three ways to create threads: extending Thread , implementing Runnable , or using the Executor framework (including Callable and Future for tasks with return values).
Thread Pools
Key interfaces in java.util.concurrent :
Name
Function
ExecutorService
Core thread‑pool interface
ScheduledExecutorService
Supports periodic task execution, similar to
TimerThreadPoolExecutor
Default implementation of
ExecutorServiceScheduledThreadPoolExecutor
Implements
ScheduledExecutorServicewith scheduling capabilities
Common factory methods from Executors :
newSingleThreadExecutor
Creates a single‑threaded pool; if the thread dies, a new one replaces it, preserving task order.
newFixedThreadPool
Creates a pool with a fixed number of threads; excess tasks wait in a queue.
newCachedThreadPool
Creates an unbounded pool that reuses idle threads and creates new ones as needed.
newScheduledThreadPool
Creates a pool capable of scheduling delayed or periodic tasks.
Memory Model
The Java Memory Model (JMM) defines how threads interact through main memory and each thread's working memory. Threads operate on local copies of variables and synchronize changes back to main memory using eight atomic actions: lock , unlock , read , load , use , assign , store , and write .
Memory Areas
Heap
Runtime data area where all object instances and arrays are allocated. It is managed by the garbage collector and divided into Young Generation (Eden, From‑Space, To‑Space) and Old Generation (Tenured).
Non‑Heap Memory
Includes the Method Area (stores class metadata, constant pool, field and method data) and other native structures such as the JIT compiled code cache.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.