The Well-Grounded Java Developer

This document was uploaded by one of our users. The uploader already confirmed that they had the permission to publish it. If you are author/publisher or own the copyright of this documents, please report to us by using this DMCA report form.

Simply click on the Download Book button.

Yes, Book downloads on Ebookily are 100% Free.

Sometimes the book is free on Amazon As well, so go ahead and hit "Search on Amazon"

Understanding Java from the JVM up gives you a solid foundation to grow your expertise and take on advanced techniques for performance, concurrency, containerization, and more. In The Well-Grounded Java Developer, Second Edition you will learn: • The new Java module system and why you should use it • Bytecode for the JVM, including operations and classloading • Performance tuning the JVM • Working with Java’s built-in concurrency and expanded options • Programming in Kotlin and Clojure on the JVM • Maximizing the benefits from your build/CI tooling with Maven and Gradle • Running the JVM in containers • Planning for future JVM releases The Well-Grounded Java Developer, Second Edition introduces both the modern innovations and timeless fundamentals you need to know to become a Java master. Authors Ben Evans, Martijn Verburg, and Jason Clark distill their decades of experience as Java Champions, veteran developers, and key contributors to the Java ecosystem into this clear and practical guide. You’ll discover how Java works under the hood and learn design secrets from Java’s long history. Each concept is illustrated with hands-on examples, including a fully modularized application/library and creating your own multithreaded application. About the technology Java is the beating heart of enterprise software engineering. Developers who really know Java can expect easy job hunting and interesting work. Written by experts with years of boots-on-the-ground experience, this book upgrades your Java skills. It dives into powerful features like modules and concurrency models and even reveals some of Java’s deep secrets. About the book With The Well-Grounded Java Developer, Second Edition you will go beyond feature descriptions and learn how Java operates at the bytecode level. Master high-value techniques for concurrency and performance optimization, along with must-know practices for build, test, and deployment. You’ll even look at alternate JVM languages like Kotlin and Clojure. Digest this book and stand out from the pack. What's inside • The new Java module system • Performance tuning the JVM • Maximizing CI/CD with Maven and Gradle • Running the JVM in containers • Planning for future JVM releases About the reader For intermediate Java developers. About the authors Benjamin J. Evans is a senior principal engineer at Red Hat. Martijn Verburg is the principal SWE manager for Microsoft’s Java Engineering Group. Both Benjamin and Martijn are Java Champions. Jason Clark is a principal engineer and architect at New Relic.

Author(s): Benjamin Evans, Martijn Verburg, Jason Clark
Edition: 2
Publisher: Manning Publications
Year: 2022

Language: English
Commentary: Publisher's PDF
Pages: 704
City: Shelter Island, NY
Tags: Programming; Java; Concurrency; Functional Programming; Docker; Modules; Clojure; Testing; Performance; Kotlin; Gradle; Test-Driven Development; Maven; JUnit; Build Systems; JVM; Java Memory Model; JDK; Testcontainers

The Well-Grounded Java Developer, Second Edition
Praise for the First Edition
brief contents
contents
foreword
preface
acknowledgements
From Jason
From Martijn
From Ben
about this book
Who should read this book?
How to use this book
About the code
liveBook DIscussion Forum
Other online resources
about the authors
about the cover illustration
Part 1: From 8 to 11 and beyond!
Chapter 1: Introducing modern Java
1.1 The language and the platform
1.2 The new Java release model
1.3 Enhanced type inference (var keyword)
1.4 Changing the language and the platform
1.4.1 Sprinkling some sugar
1.4.2 Changing the language
1.4.3 JSRs and JEPs
1.4.4 Incubating and preview features
1.5 Small changes in Java 11
1.5.1 Collections factories (JEP 213)
1.5.2 Remove enterprise modules (JEP 320)
1.5.3 HTTP/2 (Java 11)
1.5.4 Single-file source-code programs (JEP 330)
Chapter 2: Java modules
2.1 Setting the scene
2.1.1 Project Jigsaw
2.1.2 The module graph
2.1.3 Protecting the internals
2.1.4 New access control semantics
2.2 Basic modules syntax
2.2.1 Exporting and requiring
2.2.2 Transitivity
2.3 Loading modules
2.3.1 Platform modules
2.3.2 Application modules
2.3.3 Automatic modules
2.3.4 Unnamed module
2.4 Building a first modular app
2.4.1 Command-line switches for modules
2.4.2 Executing a modular app
2.4.3 Modules and reflection
2.5 Architecting for modules
2.5.1 Split packages
2.5.2 Java 8 Compact Profiles
2.5.3 Multi-release JARs
2.6 Beyond modules
Chapter 3: Java 17
3.1 Text Blocks
3.2 Switch Expressions
3.3 Records
3.3.1 Nominal typing
3.3.2 Compact record constructors
3.4 Sealed Types
3.5 New form of instanceof
3.6 Pattern Matching and preview features
Part 2: Under the hood
Chapter 4: Class files and bytecode
4.1 Class loading and class objects
4.1.1 Loading and linking
4.1.2 Class objects
4.2 Class loaders
4.2.1 Custom class loading
4.2.2 Modules and class loading
4.3 Examining class files
4.3.1 Introducing javap
4.3.2 Internal form for method signatures
4.3.3 The constant pool
4.4 Bytecode
4.4.1 Disassembling a class
4.4.2 The runtime environment
4.4.3 Introduction to opcodes
4.4.4 Load and store opcodes
4.4.5 Arithmetic opcodes
4.4.6 Execution flow control opcodes
4.4.7 Invocation opcodes
4.4.8 Platform operation opcodes
4.4.9 Shortcut opcode forms
4.5 Reflection
4.5.1 Introducing reflection
4.5.2 Combining class loading and reflection
4.5.3 Problems with reflection
Chapter 5: Java concurrency fundamentals
5.1 Concurrency theory primer
5.1.1 But I already know about Thread
5.1.2 Hardware
5.1.3 Amdahl’s law
5.1.4 Explaining Java’s threading model
5.1.5 Lessons learned
5.2 Design concepts
5.2.1 Safety and concurrent type safety
5.2.2 Liveness
5.2.3 Performance
5.2.4 Reusability
5.2.5 How and why do the forces conflict?
5.2.6 Sources of overhead
5.3 Block-structured concurrency (pre-Java 5)
5.3.1 Synchronization and locks
5.3.2 The state model for a thread
5.3.3 Fully synchronized objects
5.3.4 Deadlocks
5.3.5 Why synchronized?
5.3.6 The volatile keyword
5.3.7 Thread states and methods
5.3.8 Immutability
5.4 The Java Memory Model (JMM)
5.5 Understanding concurrency through bytecode
5.5.1 Lost Update
5.5.2 Synchronization in bytecode
5.5.3 Synchronized methods
5.5.4 Unsynchronized reads
5.5.5 Deadlock revisited
5.5.6 Deadlock resolved, revisited
5.5.7 Volatile access
Chapter 6: JDK concurrency libraries
6.1 Building blocks for modern concurrent applications
6.2 Atomic classes
6.3 Lock classes
6.3.1 Condition objects
6.4 CountDownLatch
6.5 ConcurrentHashMap
6.5.1 Understanding a simplified HashMap
6.5.2 Limitations of Dictionary
6.5.3 Approaches to a concurrent Dictionary
6.5.4 Using ConcurrentHashMap
6.6 CopyOnWriteArrayList
6.7 Blocking queues
6.7.1 Using BlockingQueue APIs
6.7.2 Using WorkUnit
6.8 Futures
6.8.1 CompletableFuture
6.9 Tasks and execution
6.9.1 Modeling tasks
6.9.2 Executors
6.9.3 Single-threaded executor
6.9.4 Fixed-thread pool
6.9.5 Cached thread pool
6.9.6 ScheduledThreadPoolExecutor
Chapter 7: Understanding Java performance
7.1 Performance terminology: Some basic definitions
7.1.1 Latency
7.1.2 Throughput
7.1.3 Utilization
7.1.4 Efficiency
7.1.5 Capacity
7.1.6 Scalability
7.1.7 Degradation
7.2 A pragmatic approach to performance analysis
7.2.1 Know what you’re measuring
7.2.2 Know how to take measurements
7.2.3 Know what your performance goals are
7.2.4 Know when to stop
7.2.5 Know the cost of achieving higher performance
7.2.6 Know the dangers of premature optimization
7.3 What went wrong? Why do we have to care?
7.3.1 Moore’s law
7.3.2 Understanding the memory latency hierarchy
7.4 Why is Java performance tuning hard?
7.4.1 The role of time in performance tuning
7.4.2 Understanding cache misses
7.5 Garbage collection
7.5.1 Basics
7.5.2 Mark and sweep
7.5.3 Areas of memory
7.5.4 Young collections
7.5.5 Full collections
7.5.6 Safepoints
7.5.7 G1: Java’s default collector
7.5.8 The Parallel collector
7.5.9 GC configuration parameters
7.6 JIT compilation with HotSpot
7.6.1 Why have dynamic compilation?
7.6.2 Introduction to HotSpot
7.6.3 Inlining methods
7.6.4 Dynamic compilation and monomorphic calls
7.6.5 Reading the compilation logs
7.6.6 Deoptimization
7.7 JDK Flight Recorder
7.7.1 Flight Recorder
7.7.2 Mission Control
Part 3: Non-Java languages on the JVM
Chapter 8: Alternative JVM languages
8.1 Language zoology
8.1.1 Interpreted vs. compiled languages
8.1.2 Dynamic vs. static typing
8.1.3 Imperative vs. functional languages
8.1.4 Reimplementation vs. original
8.2 Polyglot programming on the JVM
8.2.1 Why use a non-Java language?
8.2.2 Up-and-coming languages
8.2.3 Languages we could have picked but didn’t
8.3 How to choose a non-Java language for your project
8.3.1 Is the project area low-risk?
8.3.2 Does the language interoperate well with Java?
8.3.3 Is there good tooling and test support for the language?
8.3.4 How hard is the language to learn?
8.3.5 Are there lots of developers using this language?
8.4 How the JVM supports alternative languages
8.4.1 Performance
8.4.2 Runtime environments for non-Java languages
8.4.3 Compiler fictions
Chapter 9: Kotlin
9.1 Why use Kotlin?
9.1.1 Installing
9.2 Convenience and conciseness
9.2.1 Starting with less
9.2.2 Variables
9.2.3 Equality
9.2.4 Functions
9.2.5 Collections
9.2.6 Express yourself
9.3 A different view of classes and objects
9.3.1 Data classes
9.4 Safety
9.4.1 Null safety
9.4.2 Smart casting
9.5 Concurrency
9.6 Java interoperability
Chapter 10: Clojure: A different view of programming
10.1 Introducing Clojure
10.1.1 Hello World in Clojure
10.1.2 Getting started with the REPL
10.1.3 Making a mistake
10.1.4 Learning to love the brackets
10.2 Looking for Clojure: Syntax and semantics
10.2.1 Special forms bootcamp
10.2.2 Lists, vectors, maps, and sets
10.2.3 Arithmetic, equality, and other operations
10.2.4 Working with functions in Clojure
10.2.5 Loops in Clojure
10.2.6 Reader macros and dispatch
10.3 Functional programming and closures
10.4 Introducing Clojure sequences
10.4.1 Sequences and variable-arity functions
10.5 Interoperating between Clojure and Java
10.5.1 Calling Java from Clojure
10.5.2 The nature of Clojure calls
10.5.3 The Java type of Clojure values
10.5.4 Using Clojure proxies
10.5.5 Exploratory programming with the REPL
10.5.6 Using Clojure from Java
10.6 Macros
Part 4: Build and deployment
Chapter 11: Building with Gradle and Maven
11.1 Why build tools matter for a well-grounded developer
11.1.1 Automating tedious operations
11.1.2 Managing dependencies
11.1.3 Ensuring consistency between developers
11.2 Maven
11.2.1 The build lifecycle
11.2.2 Commands/POM intro
11.2.3 Building
11.2.4 Controlling the manifest
11.2.5 Adding another language
11.2.6 Testing
11.2.7 Dependency management
11.2.8 Reviewing
11.2.9 Moving beyond Java 8
11.2.10 Multirelease JARs in Maven
11.2.11 Maven and modules
11.2.12 Authoring Maven plugins
11.3 Gradle
11.3.1 Installing Gradle
11.3.2 Tasks
11.3.3 What’s in a script?
11.3.4 Using plugins
11.3.5 Building
11.3.6 Work avoidance
11.3.7 Dependencies in Gradle
11.3.8 Adding Kotlin
11.3.9 Testing
11.3.10 Automating static analysis
11.3.11 Moving beyond Java 8
11.3.12 Using Gradle with modules
11.3.13 Customizing
Chapter 12: Running Java in containers
12.1 Why containers matter for a well-grounded developer
12.1.1 Host operating systems vs. virtual machines vs. containers
12.1.2 Benefits of containers
12.1.3 Drawbacks of containers
12.2 Docker fundamentals
12.2.1 Building Docker images
12.2.2 Running Docker containers
12.3 Developing Java applications with Docker
12.3.1 Selecting your base image
12.3.2 Building an image with Gradle
12.3.3 Running the build in Docker
12.3.4 Ports and hosts
12.3.5 Local development with Docker Compose
12.3.6 Debugging in Docker
12.3.7 Logging with Docker
12.4 Kubernetes
12.5 Observability and performance
12.5.1 Observability
12.5.2 Performance in containers
Chapter 13: Testing fundamentals
13.1 Why we test
13.2 How we test
13.3 Test-driven development
13.3.1 TDD in a nutshell
13.3.2 A TDD example with a single use case
13.4 Test doubles
13.4.1 Dummy object
13.4.2 Stub object
13.4.3 Fake object
13.4.4 Mock object
13.4.5 Problems with mocking
13.5 From JUnit 4 to 5
Chapter 14: Testing beyond JUnit
14.1 Integration testing with Testcontainers
14.1.1 Installing testcontainers
14.1.2 An example with Redis
14.1.3 Gathering container logs
14.1.4 An example with Postgres
14.1.5 An example for end-to-end testing with Selenium
14.2 Specification-style testing with Spek and Kotlin
14.3 Property-based testing with Clojure
14.3.1 clojure.test
14.3.2 clojure.spec
14.3.3 test.check
14.3.4 clojure.spec and test.check
Part 5: Java frontiers
Chapter 15: Advanced functional programming
15.1 Introduction to functional programming concepts
15.1.1 Pure functions
15.1.2 Immutability
15.1.3 Higher-order functions
15.1.4 Recursion
15.1.5 Closures
15.1.6 Laziness
15.1.7 Currying and partial application
15.2 Limitations of Java as a FP language
15.2.1 Pure functions
15.2.2 Mutability
15.2.3 Higher-order functions
15.2.4 Recursion
15.2.5 Closures
15.2.6 Laziness
15.2.7 Currying and partial application
15.2.8 Java’s type system and collections
15.3 Kotlin FP
15.3.1 Pure and higher-order functions
15.3.2 Closures
15.3.3 Currying and partial application
15.3.4 Immutability
15.3.5 Tail recursion
15.3.6 Lazy evaluation
15.3.7 Sequences
15.4 Clojure FP
15.4.1 Comprehensions
15.4.2 Lazy sequences
15.4.3 Currying in Clojure
Chapter 16: Advanced concurrent programming
16.1 The Fork/Join framework
16.1.1 A simple F/J example
16.1.2 Parallelizing problems for F/J
16.1.3 Work-stealing algorithms
16.2 Concurrency and functional programming
16.2.1 CompletableFuture revisited
16.2.2 Parallel streams
16.3 Under the hood with Kotlin coroutines
16.3.1 How coroutines work
16.3.2 Coroutine scoping and dispatching
16.4 Concurrent Clojure
16.4.1 Persistent data structures
16.4.2 Futures and pcalls
16.4.3 Software transactional memory
16.4.4 Agents
Chapter 17: Modern internals
17.1 Introducing JVM internals: Method invocation
17.1.1 Invoking virtual methods
17.1.2 Invoking interface methods
17.1.3 Invoking “special” methods
17.1.4 Final methods
17.2 Reflection internals
17.3 Method handles
17.3.1 MethodHandle
17.3.2 MethodType
17.3.3 Looking up method handles
17.3.4 Reflection vs. proxies vs. method handles
17.4 Invokedynamic
17.4.1 Implementing lambda expressions
17.5 Small internal changes
17.5.1 String concatenation
17.5.2 Compact strings
17.5.3 Nestmates
17.6 Unsafe
17.7 Replacing Unsafe with supported APIs
17.7.1 VarHandles
17.7.2 Hidden classes
Chapter 18: Future Java
18.1 Project Amber
18.2 Project Panama
18.2.1 Foreign Function and Memory API
18.3 Project Loom
18.3.1 Virtual threads
18.3.2 Thread builders
18.3.3 Programming with virtual threads
18.3.4 When will Project Loom arrive?
18.4 Project Valhalla
18.4.1 Changing the language model
18.4.2 Consequences of value objects
18.4.3 Generics revisited
18.5 Java 18
appendix A: Selecting your Java
A.1 Java is still free
A.1.1 Java SE/OpenJDK/Oracle OpenJDK Builds/Oracle JDK
A.2 Staying with Java SE 8
A.2.1 $Free (as in beer) and free (as in use) Java SE 8
A.3 Getting Java SE 11
A.3.1 $Free (as in beer) and free (as in use) Java SE 11
A.4 Getting Java SE 17 (LTS)
A.4.1 $Free (as in beer) and free (as in use) Java SE 17
A.5 Paid support
appendix B: Recap of streams in Java 8
B.1 Backward compatibility
B.2 Default methods
B.3 Streams
B.3.1 Example
B.4 The limits of collections
B.5 Infinite streams
B.6 Handling primitives
B.7 Parallel operations?
index
Symbols
A
B
C
D
E
F
G
H
I
J
K
L
M
N
O
P
Q
R
S
T
U
V
W
X
Y
Z