Andriy has extensive experience in the development of distributed (multi-level) software systems, multi-threaded applications, desktop applications, service-oriented architecture and rich Internet applications. As a professional, he is always open to continuous learning and self-improvement to be more productive in the work he is really passionate about.
Introduction
Instance Construction
- Implicit (Generated) Constructor
- Constructors without Arguments
- Constructors with Arguments
- Initialization Blocks
- Construction guarantee
- Visibility
- Garbage collection
- Finalizers
A given class can have multiple initialization blocks and they will all be called in the order they are defined in the code. This is because Java uses garbage collection to manage the lifecycle of objects, and it is the responsibility of the garbage collector to destroy unneeded objects and reclaim memory.
Static initialization
Construction Patterns
- Singleton
- Utility/Helper Class
- Factory
- Dependency Injection
Basically, its main idea is to ensure that only one instance of a class can be created at any given time. One way to properly format a single element (but in a non-lazy way) is to use a static final property of a class.
Download the Source Code
What’s next
We will discuss this method in part 9 of the tutorial, Concurrency Best Practices. Causes the current thread to wait until another thread calls thenotify() method or thenotifyAll() method for this object.
Methods equals and hashCode
It will guarantee that the behavior of these methods will not be affected by scope changes (however, in real-world projects . it is not always possible). It will guarantee consistent behavior of both methods in case of any changes affecting the fields in question.
Method toString
Method clone
Method equals and == operator
Useful helper classes
Download the Source Code
What’s next
Introduction
Interfaces
First of all, although not explicitly stated, every declaration in the interface is public (and can only be public, for more details on visibility and accessibility rules, please see the Visibility section). And finally, the nested classes, interfaces, or enumerations, in addition to being public, are implicitly declared astatic.
Marker Interfaces
In this more complex case, there are some restrictions implicitly imposed by interfaces on nested constructs and method declarations, and the Java compiler enforces these.
Functional interfaces, default and static methods
This interface marks the class as available for serialization and deserialization, and again it does not specify how this could or should be done. Additionally, the @FunctionalInterface annotation (annotations are covered in detail in Part 5 of the tutorial, How and When to Use Enumerations and Annotations) gives the compiler a hint to verify that the interface contains only one abstract method, so that any changes made in the will be introduced into the interface in the future. do not break this assumption.
Abstract classes
Immutable classes
With arrays, the only way to ensure true immutability is to provide a copy instead of returning the reference to the array. There are some excellent Java source code analyzers (FindBugs) and PMD) that can help a lot by inspecting your code and pointing out common Java programming flaws.
Anonymous classes
Second, follow the correct initialization: if the field is the reference to a collection or an array, don't assign those fields directly from constructor arguments, make the copies instead. It will guarantee that the state of the collection or array will not be changed from outside.
Visibility
Inheritance
However, there are some easy rules that can go a long way in keeping your class hierarchies concise. However, there is no special syntax or keyword to mark the method as overridden, which can cause a lot of confusion.
Multiple inheritance
The well-known "Diamond of Death" problem is often cited as the fundamental flaw of multiple inheritance implementations, so developers are urged to design class hierarchies very carefully. Unfortunately, Java 8 interfaces with default methods are also falling victim to these flaws.
Inheritance and composition
But we can say that Vehicclass is also an engine and could be designed as such using inheritance. IS-A is an inheritance relationship: the subclass also satisfies the specification of the parent class and such an IS-variation of the parent class.
Encapsulation
Consequently, IMA-A is a composition relationship: a class owns (or IMA-A) the objects that belong to it. However, heritage has its place, solves real design issues in a different way, and should not be neglected.
Final classes and methods
Download the Source Code
What’s next
In this section, we will cover the use of generics everywhere, starting from interfaces, classes and methods. However, with a lot of benefits, generics introduce some limitations and side effects, which we will also cover.
Generics and interfaces
It's a very powerful concept (that was discussed a while ago) that allows you to develop abstract algorithms and data structures and provide concrete types that you can work with later. It's very easy to declare and use generic interfaces, but we'll come back to it when we discuss bounded types (wildcards and bounded types) and generic constraints (Constraint on Generic Interfaces).
Generics and classes
Generics and methods
If methods are declared (or defined) as part of generic interface or class, they may (or may not) use the generic types of their owner. They can define their own generic types or mix them with those from their class or interface declaration.
Limitation of generics
Another disadvantage caused by erasing types arises from the fact that it is not possible to use the type parameters of generics in a meaningful way, for example to create new instances of the type, or to change the concrete class of get the type parameter or use it in the instanceof operator. And finally, it is also not possible to create the array instances using the type parameters of generics.
Generics, wildcards and bounded types
If the type parameter is not of interest to the generic class, interface, or method, it can be replaced by . The method store doesn't care what type parameters it's called with, all it needs is to ensure that each type implements the Serializable Interface.
Generics and type inference
Unlike textends, the superkeyword restricts the type parameter to a superclass of another class. Fortunately, the Java 8 release brings more improvements to the compiler and especially to the type inference for generics, so that the code snippet shown above can be compiled successfully, helping the developers to avoid unnecessary typing.
Generics and annotations
The Java 7 release addressed this problem somewhat by making changes in the compiler and introducing the new diamond operator <>. The compiler is able to infer the generic type parameters from the left side and omit them in the right side of the expression.
Accessing generic type parameters
It was a significant step towards making generic syntax less verbose, but the compiler's ability to infer generic type parameters was quite limited. Another interesting use case that often comes up when working with generics in Java is to determine the concrete class of the type with which the generic instance has been parameterized.
When to use generics
This can blow the amount of arguments required by the methods, but with careful design it's not as bad as it seems at first glance. The class itself must be modified to use the intermediate interface // instead of direct implementations.
Download the Source Code
Very clear and concise piece of code, no intermediate interface or other tricks required. In the following parts of the tutorial, generic techniques will often be used to demonstrate other features of the Java language.
What’s next
In this part of the tutorial, we'll cover two more great features introduced to the language as part of the Java 5 release, along with generic articles: enumerations (or enumerations) and annotations. On the other hand, annotations are a special kind of metadata that can be associated with various elements and constructs of the Java language.
Enums as special classes
For example, days of the week are a great example of enums: they are limited to Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, and Sunday. However, the distinguishing part is that every single value is an instance of the enum class in which it is being declared (in our example, DaysOfTheWeek).
Enums and instance fields
From a logical point of view, the day argument should have one of the values declared in the DaysOfTheWeekConstants class. Please note that using the uppercase naming scheme in enums is only a convention, nothing prevents you from not doing so.
Enums and interfaces
The way we implemented the interface is a bit verbose, but it's definitely possible to make it better by combining instance fields and interfaces together. By supporting instance fields and interfaces, enums can be used in a more object-oriented way, bringing some abstraction to rely on.
Enums and generics
Convenient Enums methods
String name() Returns the name of this enum constant, exactly as declared in its enum declaration. Because of the presence of these methods and the hard work of the compiler, there is one more benefit to using enums in your code: they can be used in switching/casting.
Specialized Collections: EnumSet and EnumMap
TheEnumMap
When to use enums
Annotations as special interfaces
Second, it's not possible to create an instance of the annotation programmatically using the thenoperator (we'll take a look at some workarounds for that in part 11 of the tutorial, Supporting Reflection and Dynamic Languages). And third, annotations can only declare attributes of primitive types, StringorClass< ?>types and their sets.
Annotations and retention policy
Annotations and element types
In addition to the elements described above, Java 8 introduces two new element types to which annotations can be applied. Unlike the retention policy, an annotation can declare multiple element types to which it can be associated, using the @Target annotation.
Annotations and inheritance
For the most part, any annotations you create should have both a retention policy and element types defined in order to be useful.
Repeatable annotations
Annotation processors
Annotations and configuration over convention
When to use annotations
Download the Source Code
What’s next
In this section of the tutorial, we are going to spend some time discussing different aspects related to designing and implementing methods in Java. As we have seen in previous parts of the tutorial, it is very easy to write methods in Java, but there are many things that can make your methods more readable and efficient.
Method signatures
Internally, the Java compiler converts varargs to an array of the respective type, and this is how varargs can be accessed by the method implementation. The next example shows the use of the checked exceptions as part of the method signature.
Method body
However, because the argument type is unknown, the Java compiler wants to ensure that the varargs are used responsibly and recommends which method should be final and annotated with the @SafeVarargs annotation (more details on annotations can be found in part 5 of the tutorial, How and when to use lists and annotations). Finally, it is generally recommended (but rarely used) to mark the method arguments as final.
Method overloading
In recent years, the checked exceptions have proven to not be as useful as they were intended, resulting in more boilerplate code being written than problems being solved. Although this code snippet can be written without using generics, it is not important for our demonstration purposes.
Method overriding
Nevertheless, combining both generics and overloading can be very powerful, but often not possible in Java, due to type clearing (for more details, please refer to part 4 of the tutorial, How and When to Use Generics). Again, there is only a subtle difference in the method signature (Doublein instead ofNumber), but in this case it is an overloaded version of the method and does not overwrite the parent version.
Inlining
That's when the help of the Java compiler and @Overrideannotations pays off: annotating the method from the last example with @Override increases the compiler error.
Recursion
Method References
Reference to an instance method of a given object someInstance::instanceMethodName Reference to an instance method of any object of a. The last line of the main method uses the reference to printlnmethod to print each element from the string collection to the console and is taken as an argument passed to another method, forEach.
Immutability
Method Documentation
Let's look at a quick example of how methods can be passed as arguments to other methods. Just by looking at the image above, any Java developer from junior to senior level can understand the purpose of the method and the right way to use it.
Method Parameters and Return Values
Methods as API entry points
Whenever it makes sense, try to design immutable methods (that don't affect the internal state, section Immutability).
Download the Source Code
What’s next
In this part of the tutorial, we will continue to discuss the general principles of good programming style and robust design in Java. We've already seen some of these principles in previous parts of the tutorial, but along the way there will be many new practical tips aimed at improving your skills as a Java developer.
Variable scopes
Class fields and local variables
The calculateValue method introduces a local variable with name value and in doing so hides the class member with the same name. Line08 should have summed the class member and the local variable, but instead it does something very different.
Method arguments and local variables
Boxing and unboxing
Interfaces
However, because the switch is driven by the interface, it is very easy to introduce another implementation using, for example, database or even flat file. Many excellent examples of appropriate use of the interfaces are encapsulated in the Java standard assembly library.
Strings
This implementation can be used anywhere the Timezone service interface is required, isolating the test scenario from dependencies on external components. Although using StringBuilder/StringBuffer is the recommended way to manipulate strings, it can look too murderous in a simple scenario of concatenating two or three strings, so the regular + operator can be used instead, for example: .
Naming conventions
Standard Libraries
Immutability
Testing
Download the Source Code
What’s next
Introduction
Exceptions and when to use them
Checked and unchecked exceptions
Checked exceptions therefore represent invalid conditions in the areas outside the immediate control of the program (such as memory, network, file system, etc. Unlike the unchecked exceptions, checked exceptions must either be caught by the caller or be listed as part of of the method signature (usingthrows keyword).
Using try-with-resources
Fortunately, as of Java 7, there is a new construct introduced into the language called try-with-resources which greatly simplified the overall management of resources. The only thing required for the resource to be used in thetry-with-resources blocks is to implement the AutoCloseable interface.
Exceptions and lambdas
Behind the scenes, the Java compiler expands this construct into something more complex, but to developers the code looks very readable and concise. Many functional interfaces are declared with the ability to throw any exception from the implementation, but if not (like Runnable), wrapping (or catching) the checked exceptions into unchecked exceptions is the only way to go.
Standard Java exceptions
Defining your own exceptions
It is highly recommended that all user-defined exceptions are inherited from the RuntimeException class and fall into the class of unchecked exceptions (however, there are always exclusions from the rule). It is always a good idea to pass the informational message along with the exception as it helps a lot in troubleshooting production systems.
Documenting exceptions
Additionally, if the exception has been re-thrown due to another exceptional condition, the original exception must be preserved using the cause constructor argument. It will help other developers to implement proper exception handling and recovery (fallback) logic from the beginning and save them from the trouble of debugging in production systems.
Exceptions and logging
Download the Source Code
What’s next
To take advantage of the full power of available compute units, applications must be prepared to support multiple threads running concurrently and competing for resources and memory. In this part of the tutorial, we'll look at what Java can offer developers to help them write robust and secure applications in a concurrent world.
Threads and Thread Groups
Concurrent programming comes with many challenges related to data access and non-deterministic flow of events that can lead to unexpected crashes and strange failures. Not all thread states are clear right now, but we'll go over most of them later in the tutorial and discuss what kinds of events cause a thread to be in one state or another.
Concurrency, Synchronization and Immutability
Threads groups are intended as a nice feature, but are not recommended for use today as executors and thread pools (see Futures) are much better alternatives. Finally, the synchronization is Java re-entry: it means the thread can get a lock it already owns.
Futures, Executors and Thread Pools
But if the result of the execution is important, the standard Java library provides another abstraction to represent the computation that will occur at some point in the future, called Future
Locks
When a thread waits for the lock (obtained by another thread) using the lock() method call, it is in WAITING state. However, when a thread waits for the lock (obtained by another thread) using the tryLock() method call with timeout, it is in TIMED_WAITING state.
Thread Schedulers
If these two methods are called on the same class instance in two different threads via program execution, deadlock is very likely: the first thread will wait indefinitely for the lock2 acquired by the second thread, while the second thread waits indefinitely for the lock1 acquired by the first.
Atomic Operations
Concurrent Collections
Explore Java standard library
Using Synchronization Wisely
Wait/Notify
As such, it can cause subtle errors in the running program that will be very difficult and time-consuming to investigate.
Troubleshooting Concurrency Issues
Download
What’s next
In addition to the Java standard library, there are many serialization techniques and frameworks available: some of them use compact binary representation, others put readability first. Although we will mention many alternatives along the way, our attention will be concentrated on those of the Java standard library (and latest specifications): Serializable, Externalizable, Java Architecture for XML Binding (JAXB, JSR-222) and Java API for JSON- processing (JSON-P,JSR-353).
Serializable interface
This part of the tutorial is going to be devoted exclusively to serialization: the process of translating Java objects into a format that can be used to store and later be reconstructed in the same (or another) environment (http:// en.wikipedia.org /wiki/Serialization).Serialization not only allows saving and loading Java objects to/from persistent storage, but is also a very important component of modern distributed system communication. Moreover, it limits the flexibility to change the internal class representation, as it can break the serialization / deserialization process.
Externalizable interface
As we can see, the Serializable interface doesn't offer much control over what should be serialized and how (with the exception of transient keywords that mark the fields as non-serializable). The Externalizable interface allows a fine-grained serialization / deserialization adjustment in the cases when the simpler approach with Serializable interface does not work well.
More about Serializable interface
When the instances of this class are serialized, the classSerializationProxyExampleimplementation provides the replacement object (instance of the SerializationProxy class) instead. Consequently, when the instances of this class are being deserialized, theResolvemethod is called and SerializationProxy also provides the replacement, this time in the form of SerializationProxyExampleinstance.
Serializability and Remote Method Invocation (RMI)
The basic idea of this pattern is to introduce a dedicated companion class for serialization (usually as a private staticinner class), which completes the class required to be serialized. It means that instances of the SerializationProxyExample class will never be serialized (and deserialized) directly.
JAXB
JSON-P
Still, it's a great thing, and the Java community is working to enrich JSON support by introducing the Java API for JSON Binding (JSON-B,JSR-367). With this API, serialization and deserialization of Java objects in/fromJSON should be as transparent as JAXB has.
Cost of serialization
Beyond Java standard library and specifications
Download the Source code
What’s next
Introduction
Reflection API
Annotations by themselves (except those from the standard Java library) have no effect on the code. The recent versions of the very popular Java specifications such as Java API for RESTful Web Services (https://jcp.org/en/jsr/detail?id=339), Bean Validation (https://jcp.org/en/ jsr/detail?id=349), Java Temporary Caching API (https://jcp.org/en/jsr/detail?id=107), Java Message Service (https://jcp.org/en/jsr/detail ?id=343), Java Persistence (https://jcp.org/en/jsr/detail?id=338) and many more are built on top of annotations and their implementations usually make heavy use of the Reflection API to collect metadata about the application being run.
Accessing generic type parameters
However, Java applications can use reflection to examine annotations present on various Java elements of interest at runtime and apply some logic based on the annotation and its attributes. Most of today's Java APIs include annotations that make it easier for developers to use and integrate them.
Reflection API and visibility
Reflection API pitfalls
Method Handles
Method Argument Names
Download the Source Code
What’s next
In this part of the tutorial our attention will be completely focused on scripting and dynamic language support in Java. One of the strengths of the dynamic languages is that the program's behavior is defined at runtime, rather than at compile time.
Dynamic Languages Support
Since Java 7, the JVM has had direct support for modern dynamic (often called scripting) languages, and the Java 8 release has brought even more improvements to this space. These languages include Ruby (https://www.ruby-lang.org/en/), Python (https://www.python.org/), and JavaScript (http://en.wikipedia.org/wiki/ JavaScript ) have gained great popularity and are currently the most widely used.
Scripting API
JavaScript on JVM
Groovy on JVM
Please note that the Groovyscripting engine has full access to the Java standard library and does not require additional bindings. It quickly became very popular due to the innovative language features similar to Java syntax and great interoperability with existing Java code.
Ruby on JVM
Python on JVM
The book 'Learn Python in a Day and Learn it Well' is written by Jamie Chan Powered by PyScriptEngine.
Using Scripting API
Download Code
What’s next
In this part of the tutorial, we will look at the Java Compiler API from 10000 feet. The Java Compiler API also supports annotation processing (see Tutorial Part 5, How and When to Use Enums and Annotations for more details, more in Tutorial Part 14, Annotation Handlers) and is split between three different packages shown below.
Java Compiler API
This API provides programmatic access to the Java compiler itself and allows developers to compile Java classes from source files on the fly from application code. More interestingly, we'll also walk through the Java compiler tree API, which provides access to the functionality of the Java syntax parser.
Annotation Processors
When dealing with more complicated examples, there are some subtle but very important details that can greatly speed up the compilation process.
Element Scanners
One way to invoke the scanner (and visitors) during the translation process is to use a note processor. The compilation process looks exactly as we saw in the Java Compiler API section.
Java Compiler Tree API
The initialization procedure became a little bit more complex, as we need to retrieve the instance of the Trees class and convert each element to tree path representation. The Java Compiler Tree API may look a little shallow, and it certainly is.
Download
To make it a bit more interesting, let's run it on all the source files we've experimented with so far: SampleClassToP arse.java and SampleClass.java. However, it gives full access to the abstract syntax trees and is a life saver when you need to do deep source code analysis and post-processing.
What’s next
Being compiler plugins also means that annotation processors are somewhat low-level and very dependent on the version of Java. But the knowledge of annotations from Part 5 of the tutorial How and When to Use Enums and Annotations and the Java Compiler API from Part 13 of the tutorial Java Compiler API will be very handy in understanding the intrinsic details of how the annotation processors work.
When to Use Annotation Processors
In this part of the tutorial, we're going to elucidate the magic of annotation processing, which is often used to inspect, modify, or generate source code driven solely by annotations. If used wisely, annotation processors can greatly simplify the lives of Java developers. That's why they are often bundled with many popular libraries and frameworks.
Annotation Processing Under the Hood
Essentially, any Java class could become a full-fledged annotation processor by implementing just a single interface: javax.annotation.processing.Processor. However, to become truly useful, any implementation of thejavax.annotation.processing.Processor must provide a public constructor with no arguments (for more details, see part 1 of the tutorial, Creating and Destroying Objects) that can be used to build the processor.
Writing Your Own Annotation Processor
The Java documentation points out that if a note processor instance is created and used without following the above protocol, the behavior of the processor is not defined by this interface specification. The class 'MutableClass' is annotated as @Immutable, but the field 'name' is not declared final. This confirms that the annotation processor has successfully detected an Immutablenotation abuse in a mutable class.
Running Annotation Processors
Nevertheless, the source file and its class have been generated using primitive string concatenations (and in fact this class is really very useless) the goal was to demonstrate how the code generation performed by annotation processors works so that more sophisticated generation techniques can be used.
Download the source code
What’s next
Introduction
Java Agent Basics
Java Agent and Instrumentation
Writing Your First Java Agent
And finally, we returned the new bytecode of the refactored version of the class so that it will be used by the JVM instead of the original one. In the next section of the tutorial, we'll cover one way to run the Java Agent alongside your Java applications.
Running Java Agents
The transformation we performed is quite naive, but it is here for demonstration purposes. Second, we found the HttpURLConection class and changed all of its constructors to put the statement System.out.println(this.getURL());
Download the source code
What’s next