
The Well-Grounded Java Developer, Second Edition
Description
Alles über E-Books | Antworten auf Fragen rund um E-Books, Kopierschutz und Dateiformate finden Sie in unserem Info- & Hilfebereich.
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.
Foreword by Heinz Kabutz.
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 author
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.
Table of Contents
PART 1 - FROM 8 TO 11 AND BEYOND!
1 Introducing modern Java
2 Java modules
3 Java 17
PART 2 - UNDER THE HOOD
4 Class files and bytecode
5 Java concurrency fundamentals
6 JDK concurrency libraries
7 Understanding Java performance
PART 3 - NON-JAVA LANGUAGES ON THE JVM
8 Alternative JVM languages
9 Kotlin
10 Clojure: A different view of programming
PART 4 - BUILD AND DEPLOYMENT
11 Building with Gradle and Maven
12 Running Java in containers
13 Testing fundamentals
14 Testing beyond JUnit
PART 5 - JAVA FRONTIERS
15 Advanced functional programming
16 Advanced concurrent programming
17 Modern internals
18 Future Java
More details
Other editions
Additional editions

Persons
Martijn Verburg is the principal SWE group manager for the Java Engineering Group at Microsoft. He is the co-leader of the London Java User Group (LJC) where he co-founded AdoptOpenJDK, the world's leading (non-Oracle) OpenJDK distribution. He has been made a Java Champion in recognition for his contribution to the Java ecosystem.
Jason Clark is a principal engineer and architect at New Relic, and was previously an architect at WebMD. A regular conference speaker, Jason contributes to the open-source project Shoes, aiming to make GUI programming easy and fun for beginners.
Content
- Intro
- The Well-Grounded Java Developer
- Copyright
- Praise for the First Edition
- brief contents
- contents
- front matter
- 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!
- 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)
- Summary
- 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
- Summary
- 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
- Summary
- Part 2. Under the hood
- 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
- Summary
- 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
- Summary
- 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
- Summary
- 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
- Summary
- Part 3. Non-Java languages on the JVM
- 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
- Summary
- 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
- Summary
- 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
- Summary
- Part 4. Build and deployment
- 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
- Summary
- 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
- Summary
- 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
- Summary
- 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
- Summary
- Part 5. Java frontiers
- 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
- Summary
- 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
- Summary
- 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
- Summary
- 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
- Summary
- 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?
System requirements
File format: ePUB
Copy protection: Adobe-DRM (Digital Rights Management)
System requirements:
- Computer (Windows; MacOS X; Linux): Install the free reader Adobe Digital Editions prior to download (see eBook Help).
- Tablet/smartphone (Android; iOS): Install the free app Adobe Digital Editions or the app PocketBook before downloading (see eBook Help).
- E-reader: Bookeen, Kobo, Pocketbook, Sony, Tolino and many more (not Kindle).
The file format ePub works well for novels and non-fiction books – i.e., „flowing” text without complex layout. On an e-reader or smartphone, line and page breaks automatically adjust to fit the small displays.
This eBook uses Adobe-DRM, a „hard” copy protection. If the necessary requirements are not met, unfortunately you will not be able to open the eBook. You will therefore need to prepare your reading hardware before downloading.
Please note: We strongly recommend that you authorise using your personal Adobe ID after installation of any reading software.
For more information, see our ebook Help page.