Previous Next

Java in a Nutshell (Benjamin J. Evans etc.) (z-library.sk, 1lib.sk, z-lib.sk)

Author: Benjamin J. Evans, Jason Clark & David Flanagan

技术

Java in a Nutshell doesn't just help experienced Java programmers get the most out of versions through Java 25. It also provides a learning path for new developers. Chock-full of examples that demonstrate how to take complete advantage of modern Java APIs and development best practices, this thoroughly revised guide includes new material on recent enhancements to Java, such as the groundbreaking Compact Source Files and Instance Main Methods feature, which makes the language more accessible than ever for beginners.

📄 File Format: PDF
💾 File Size: 1.6 MB
19
Views
0
Downloads
0.00
Total Donations

📄 Text Preview (First 20 pages)

ℹ️

Registered users can read the full content for free

Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.

📄 Page 1
(This page has no text content)
📄 Page 2
Java in a Nutshell A Desktop Quick Reference NINTH EDITION With Early Release ebooks, you get books in their earliest form—the authors’ raw and unedited content as they write—so you can take advantage of these technologies long before the official release of these titles. Benjamin J. Evans, Jason Clark, and David Flanagan
📄 Page 3
Java in a Nutshell by Benjamin J. Evans, Jason R. Clark, and David Flanagan Copyright © 2026 Benjamin J. Evans and Jason Clark. All rights reserved. Published by O’Reilly Media, Inc., 141 Stony Circle, Suite 195, Santa Rosa, CA 95401. O’Reilly books may be purchased for educational, business, or sales promotional use. Online editions are also available for most titles (https://oreilly.com). For more information, contact our corporate/institutional sales department: 800-998-9938 or corporate@oreilly.com. Acquisitions Editor: Simina Calin Development Editor: Melissa Potter Production Editor: Ashley Stussy Interior Designer: David Futato Interior Illustrator: Kate Dullea March 2027: Ninth Edition Revision History for the Early Release 2026-01-30: First Release See https://oreilly.com/catalog/errata.csp?isbn=9798341669543 for release details. The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. Java in a Nutshell, the cover image, and related trade dress are trademarks of O’Reilly Media, Inc. The views expressed in this work are those of the authors and do not represent the publisher’s views. While the publisher and the authors have used good faith efforts to ensure that the information and instructions contained in this work are accurate, the publisher and the authors disclaim all responsibility for errors or omissions, including without limitation responsibility for damages resulting from the use of or reliance on this work. Use of the information and instructions contained in this work is at your own risk. If any code samples or other technology this work contains or describes is subject
📄 Page 4
to open source licenses or the intellectual property rights of others, it is your responsibility to ensure that your use thereof complies with such licenses and/or rights. 979-8-341-66950-5 [FILL IN]
📄 Page 5
Brief Table of Contents (Not Yet Final) Chapter 1: Introduction to the Java Environment (available) Chapter 2: Java Syntax from the Ground Up (available) Chapter 3: Your First Java Programs (available) Chapter 4: Object-Oriented Programming in Java (available) Chapter 5: The Java Type System (unavailable) Chapter 6: Introduction to Object-Oriented Design in Java (unavailable) Chapter 7: Programming and Documentation Conventions (unavailable) Chapter 8: Working with Java Collections (unavailable) Chapter 9: How Java Handles Memory (unavailable) Chapter 10: Handling Common Data Formats (unavailable) Chapter 11: File Handling and I/O (unavailable) Chapter 12: Concurrency in Java (unavailable) Chapter 13: Classloading, Reflection, and Method Handles (unavailable) Chapter 14: Java Platform Modules (unavailable) Appendix A: Future Java (unavailable) Appendix B: Platform Tools (unavailable)
📄 Page 6
Chapter 1. Introduction to the Java Environment A NOTE FOR EARLY RELEASE READERS With Early Release ebooks, you get books in their earliest form—the author’s raw and unedited content as they write—so you can take advantage of these technologies long before the official release of these titles. This will be the 1st chapter of the final book. Please note that the GitHub repo will be made active later on. If you’d like to be actively involved in reviewing and commenting on this draft, please reach out to the editor at mpotter@oreilly.com. Welcome to Java in 2026. You may be coming to the Java world from another tradition, or maybe this is your first taste of computer programming. Whatever road you may have traveled to get here, welcome— we’re glad you’ve arrived. Java is a powerful, general-purpose programming environment. It is one of the most widely used programming environments in the world and has been exceptionally successful in business and enterprise computing for over 30 years. In this chapter, we’ll set the scene by describing the Java language (which programmers write their applications in), the Java execution environment (known as the “Java Virtual Machine,” which actually runs those applications), and the Java ecosystem (which provides a lot of the value of the programming environment to development teams). All three of these concepts (language, execution environment, and ecosystem) are habitually referred to just as “Java,” with the precise usage inferred from context. In practice, they are such connected ideas that this isn’t as confusing as it might first seem. We’ll briefly cover the history of the Java language and virtual machine, move on to discuss the lifecycle of a Java program, and then clear up some common questions about the differences between Java and other environments.
📄 Page 7
The Language, the JVM, and the Ecosystem The base Java programming environment has been around since the late 1990s. It is composed of the Java language and the supporting runtime, the Java Virtual Machine (JVM). The third leg—the Java ecosystem beyond the standard library that ships with Java—is provided by third parties, such as open-source projects and Java technology vendors. At the time that Java was initially developed, this split was considered novel, but trends in software development in the intervening years have made it more commonplace. Notably, Microsoft’s .NET environment, announced a few years after Java, adopted a very similar approach to platform architecture. One important difference between Microsoft’s .NET platform and Java is that Java was always conceived as a relatively open ecosystem of multiple vendors, albeit led by a steward who owns the technology. Throughout Java’s history, these vendors have both cooperated and competed on aspects of Java technology. One of the main reasons for Java’s success is that this ecosystem is a standardized environment. This means there are specifications for the technologies that comprise the environment. These standards give the developer and consumer confidence that the technology will be compatible with other components, even if they come from a different technology vendor. The current steward of Java is Oracle Corporation (which acquired Sun Microsystems, the originator of Java). Other corporations, such as IBM, Red Hat, Amazon, Microsoft, SAP, Azul Systems, and Bellsoft, are also involved in producing implementations of standardized Java technologies. TIP The reference implementation of Java is the open source OpenJDK (Open Java Development Kit), which many of these companies collaborate and base their shipping products upon. Java originally comprised several different, but related, environments and specifications, such as Java Mobile Edition (Java ME),1 Java Standard Edition (Java SE), and Java Enterprise Edition (Java EE).2 In this book, we’ll only cover Java SE, version 25, with some historical notes related to when certain features were introduced
📄 Page 8
into the platform. Generally speaking, if someone says “Java” without any further clarification, they mean Java SE. We will have more to say about standardization later, so let’s move on to discuss the Java language and JVM as separate but related concepts. What Is the Java Language? Java programs are written as source code in the Java language. This is a human- readable programming language, which is strictly class based and object-oriented. The language syntax is deliberately modeled on that of C and C++, and it was explicitly intended to be familiar to programmers coming from those languages, which were the dominant languages at the time Java was created. NOTE Although the source code is similar to C++, in practice Java includes features and a managed runtime that has much more in common with dynamic languages. Java is considered to be relatively easy to read and write (if occasionally a bit verbose). It has a rigid grammar and simple program structure and is intended to be easy to learn and to teach. It is built on industry experience with languages like C++ and tries to remove complex features as well as preserving “what works” from previous programming languages. The Java language is governed by the Java Language Specification (JLS), which defines how a conforming implementation must behave. Overall, Java is intended to provide a stable, solid base for companies to develop business-critical applications. As a programming language, it has a relatively conservative design and a slow rate of change. These properties are a conscious attempt to serve the goal of protecting the investment that organizations have made in Java technology. The language has undergone gradual revision (but no complete rewrites) since its inception in 1995. This does mean that some of Java’s original design choices, which were expedient in the late 1990s, are still affecting the language today—see Chapters 2 and 4 for more details.
📄 Page 9
On the other hand, in the last 10 or so years, Java has modernized its language syntax somewhat, to address concerns about verbosity and provide features more familiar to programmers coming from other popular languages. For example, in 2014, Java 8 added the most radical changes seen in the language for almost a decade. Features like lambda expressions and the introduction of the Streams API were enormously popular and changed forever the way that Java developers write code. TIP If you’re returning to Java programming after some time away, you should be pleasantly surprised by the quality-of-life improvements provided for developers in recent versions. As we’ll discuss later in this chapter, the Java project has transitioned to a new release model. In this new model Java versions are released every 6 months but only certain versions (8, 17, 21 and 25) are considered eligible for LTS. All other versions are supported for only 6 months and have not seen widespread adoption by development teams. What Is the JVM? The JVM is a program that provides the runtime environment necessary for Java programs to execute. Java programs cannot run unless there is a JVM available for the appropriate hardware and OS platform we wish to execute on. Fortunately, the JVM has been ported to run on a large number of hardware environments—anything from a set-top box or Blu-ray player to a huge mainframe will probably have a JVM available for it. The JVM has its own specification, the Java Virtual Machine Specification, and every implementation must conform to the rules of the specification. When new hardware types arrive in the mainstream market then it is likely that companies or individuals interested in the hardware will start a project to port OpenJDK to the new chip. A recent example of this was the arrival of Apple’s M- series chips—Red Hat ported the JVM to the AArch64 architecture and then Microsoft ported the build changes needed to build on Apple’s silicon. Java programs can be started in several ways, but the simplest (and oldest) method is to start from a command line:
📄 Page 10
java <arguments> <program name> This brings up the JVM as an operating system process that provides the Java runtime environment and then executes our program in the context of the freshly started (and empty) virtual machine. It is important to understand that when the JVM takes in a Java program for execution, the program is not provided as Java language source code. Instead, the Java language source must be compiled into a form known as Java bytecode. Java bytecode is then supplied to the JVM in a format called class files (which always have a .class extension). We can contrast this with Python, where the script is run directly by the Python interpreter.3 TIP The Java platform has always emphasized backward compatibility, and code written for Java 1.0 will still run on today’s JVMs without modification or recompilation. The JVM provides an execution environment for the program. It starts an interpreter for the bytecode form of the program that steps through one bytecode instruction at a time. However, production-quality JVMs also provide a special compiler that operates while the Java program is running. This compiler (known as a “JIT” or just-in-time compiler) will accelerate the important parts of the program by replacing them with equivalent compiled (and heavily optimized) machine code. You should also be aware that both the JVM and the user program are capable of spawning additional threads of execution, so that a user program may have many different functions running simultaneously. The original design of the JVM was built on many years of experience with earlier programming environments, notably C and C++, so we can think of it as having several different goals—all intended to make life easier for the programmer: Comprise a standard execution environment for application code to run inside Facilitate secure and reliable code execution (as compared to C/C++) Take low-level memory management out of the hands of developers Provide a cross-platform execution environment
📄 Page 11
These objectives are often mentioned together in discussions of the platform. We’ve already mentioned the first of these goals, when we discussed the JVM and its bytecode interpreter—it functions as the container for application code. We’ll discuss the second and third goals in Chapter 9, when we talk about how the Java environment deals with memory management. The fourth goal, sometimes called “write once, run anywhere” (WORA), is the property that Java class files can be moved from one execution platform to another, and they will run unaltered provided a JVM is available. This means that a Java program can be developed (and converted to class files) on a machine running macOS on an M-series chip, and then the class files can be moved to Linux or Microsoft Windows on Intel hardware (or other platforms) and the Java program will run without any further work needed. NOTE The Java environment has been very widely ported, including to platforms that are very different from mainstream platforms like Linux, macOS, and Windows. In this book, we use the phrase “most implementations” to indicate those platforms that the majority of developers are likely to encounter; macOS, Windows, Linux, BSD Unix, and the like are all considered “mainstream platforms” and count within “most implementations.” In addition to these four primary goals, there is another aspect of the JVM’s design that is not always recognized or discussed—it uses runtime information to self-manage. Software research in the 1970s and 1980s revealed that the runtime behavior of programs has a large number of interesting and useful patterns that cannot be deduced at compile time. The JVM was the first truly mainstream programming environment to use the results of this research. It collects runtime information to make better decisions about how to execute code. That means that the JVM can monitor and optimize a program running on it in a manner not possible for platforms without this capability. A key example is the runtime fact that not all parts of a Java program are equally likely to be called during the lifetime of the program—some portions will be called far, far more often than others. The Java platform takes advantage of this fact with a technology called just-in-time (JIT) compilation.
📄 Page 12
In the HotSpot JVM (which was the JVM that Sun first shipped as part of Java 1.3, and is still in use today), the JVM first identifies which parts of the program are called most often—the “hot methods.” Then the JVM compiles these hot methods directly into machine code, bypassing the JVM interpreter. The JVM uses the available runtime information to deliver higher performance than would be possible from purely interpreted execution. In fact, the optimizations that the JVM uses now in many cases produce performance that surpasses compiled C and C++ code. The standard that describes how a properly functioning JVM must behave is called the JVM Specification. What is the Java Ecosystem? The Java language is easy to learn and contains relatively few abstractions, compared to other programming languages. The JVM provides a solid, portable, high-performance base for Java (or other languages) to execute on. Taken together, these two connected technologies provide a foundation that businesses can feel confident about when choosing where to base their development efforts. The benefits of Java do not end there, however. Since Java’s inception, an extremely large ecosystem of third-party libraries and components has grown up. This means that a development team can benefit hugely from the existence of connectors and drivers for practically every technology imaginable—both proprietary and open source. TIP Java’s build tools (the most popular two are called Maven and Gradle) agree on the dependency formats and standards, and the Maven Central infrastructure serves as a source for libraries, in the same way that npm or pypi do in the JavaScript and Python communities. In the modern technology ecosystem, it is now rare indeed to find a technology component that does not offer a Java connector. From databases (both traditional relational and NoSQL), to AI and machine learning, to every type of enterprise monitoring system, to messaging systems, to Internet of Things (IoT)—everything integrates with Java. It is this fact that has been a major driver of adoption of Java technologies by enterprises and larger companies. Development teams have been able to unlock their
📄 Page 13
potential by making use of preexisting libraries and components. This has promoted developer choice and encouraged open, best-of-breed architectures with Java technology cores. NOTE Google’s Android environment is sometimes thought of as being “based on Java.” However, the picture is actually rather more complicated. Android code is written in Java (or, more usually, the Kotlin language) but uses, not javac, but a different compiler to convert to a different file format for a virtual machine that is not the JVM. The combination of a rich ecosystem and a first-rate virtual machine with an open standard for program binaries makes the Java platform a very attractive execution target. In fact, there are a large number of non-Java languages that target the JVM and also interoperate with Java (which allows them to piggyback off the platform’s success). These languages include Kotlin, JRuby, Scala, Clojure, and many others. While all of them are small compared to Java, they have distinct niches within the Java world and provide a source of innovation and healthy competition to Java. The Lifecycle of a Java Program To better understand how Java code is compiled and executed, and the difference between Java and other types of programming environments, consider the pipeline in Figure 1-1, which represents the traditional way that Java programs are created, compiled and executed. Figure 1-1. How Java code is compiled and loaded
📄 Page 14
This starts wth Java source and passes it through the javac program to produce class files—which contain the source code compiled to Java bytecode. The class file is the smallest unit of functionality the platform will deal with and the only way to get new code into a running program. New class files are onboarded via the classloading mechanism (see Chapter 13 for a lot more detail on how classloading works). This makes the new code (represented as a type) available to the interpreter for execution, and execution begins in the main() method. TIP There are alternatives to this traditional approach, and they are the subject of Chapter 3. The performance analysis and optimization of Java program is a major topic, and interested readers should consult a specialist text, such as Optimizing Cloud-Native Java (O’Reilly). Frequently Asked Questions In this section, we’ll discuss some of the most frequently asked questions about Java and the lifecycle of programs written in the Java environment. What is a virtual machine? When developers are first introduced to the concept of a virtual machine, they sometimes think of it as “a computer inside a computer” or “a computer simulated in software.” It’s then easy to imagine bytecode as “machine code for the CPU of the internal computer” or “machine code for a made-up processor.” However, this simple intuition can be misleading. What is bytecode? In fact, JVM bytecode is actually not very similar to machine code that would run on a real hardware processor. Instead, computer scientists would call bytecode a type of intermediate representation — a midpoint between source code and machine code. Is javac a compiler?
📄 Page 15
Compilers usually produce machine code, but javac produces bytecode, which is not that similar to machine code. However, class files are a bit like object files (like Windows .dll files, or Unix .so files)—and they are certainly not human readable. In theoretical computer science terms, javac is most similar to the front half of a compiler—it creates the intermediate representation that can then be used later to produce (emit) machine code. However, because creation of class files is a separate build-time step that resembles compilation in C/C++, many developers consider running javac to be compilation. In this book, we will use the terms “source code compiler” or "javac compiler” to mean the production of class files by javac. We will reserve “compilation” as a standalone term to mean JIT compilation—as it’s JIT compilation that actually produces machine code. Why is it called “bytecode”? The instruction code (opcode) is just a single byte (some operations also have parameters that follow them in the bytestream), so there are only 256 possible instructions. In practice, some are unused—about 200 are in use, but some of them aren’t emitted by recent versions of javac. Is bytecode optimized? In the early days of the platform, javac produced heavily optimized bytecode. This turned out to be a mistake. With the advent of JIT compilation, the important methods are going to be compiled to very fast machine code. It’s therefore very important to make the job of the JIT compiler easier—as there are much bigger gains available from JIT compilation than there are from optimizing bytecode, which will still have to be interpreted. Is bytecode really machine independent? What about things like endianness? The format of bytecode is always the same, regardless of what type of machine it was created on. This includes the byte ordering (sometimes called “endianness”) of the machine. For readers who are interested in the details, bytecode is always big-endian. Is Java an interpreted language?
📄 Page 16
The JVM is basically an interpreter (with JIT compilation to give it a big performance boost). However, most interpreted languages directly interpret programs from source form (usually by constructing an abstract syntax tree from the input source file). The JVM interpreter, on the other hand, requires class files—which, of course, require a separate source code compilation step with javac. In fact, the modern version of many languages that were traditionally interpreted (such as JavaScript, Python, PHP, and Ruby) now also have JIT compilers, so the divide between “interpreted” and “compiled” languages is increasingly blurred. Once again, Java’s design decisions have been validated by their adoption in other programming environments. Can other languages run on the JVM? Yes. The JVM can run any valid class file, so this means that non-Java languages can run on the JVM in several ways. First, they could have a source code compiler (similar to javac) that produces class files, which would run on the JVM just like Java code (this is the approach taken by languages like Kotlin and Scala). Alternatively, a non-Java language could implement an interpreter and runtime in Java and then interpret the source form of their language directly. This second option is the approach taken by languages like JRuby (but JRuby has a very sophisticated runtime that is capable of secondary JIT compilation in some circumstances). Comparing Java to Other Languages In this section, we’ll briefly highlight some differences between the Java platform and other programming environments you may be familiar with. Java Compared to Python Java is statically typed; Python is dynamically typed (with optional, gradual typing). Java is an OO language with functional programming (FP) features; Python is a hybrid OO / procedural language with some FP support. Java and Python both have a bytecode format—Java uses JVM class files; Python uses Python bytecode (PBC), but this is really an implementation detail that most developers don’t have to care about.
📄 Page 17
Java’s bytecode has extensive static checks; Python’s bytecode does not. Java is multithreaded; Python allows only one thread to execute Python bytecode at once (the Global Interpreter Lock or GIL) — this is changing slowly but it is a huge project within Python. Java Compared to JavaScript Java is statically typed; JavaScript is dynamically typed. Java uses class-based objects; JavaScript is prototype based (the JS keyword class is syntactic sugar). Java provides good object encapsulation; JavaScript does not. Java has namespaces; JavaScript does not. Java is multithreaded; JavaScript is not. Java Compared to C Java is object-oriented; C is procedural. Java is portable as class files; C needs to be recompiled. Java provides extensive instrumentation as part of the runtime. Java has no pointers and no equivalent of pointer arithmetic. Java provides automatic memory management via garbage collection (GC). Java currently has no ability to lay out memory at a low level (no structs). Java has no preprocessor. Java Compared to C++ Java has a simplified object model compared to C++. Java’s method dispatch is virtual by default. Java is always pass-by-value (but one of the only possibilities for Java values is object references).
📄 Page 18
Java does not support full multiple inheritance. Java’s generics are less powerful (but also less dangerous) than C++ templates. Java has no operator overloading. Answering Some Criticisms of Java Java has had a long history in the public eye and, as a result, has attracted its fair share of criticism over the years. Some of this negative press can be attributed to some technical shortcomings combined with rather overzealous marketing in the first versions of Java. Some criticisms have, however, entered technical folklore despite no longer being very accurate. In this section, we’ll look at some common grumbles and the extent to which they’re true for modern versions of the platform. Overly Verbose The Java core language has sometimes been criticized as overly verbose. Even simple Java statements such as Object o = new Object(); seem to be repetitious—the type Object appears on both the left and right side of the assignment. Critics point out that this is essentially redundant, that other languages do not need this duplication of type information, and that many languages support features (e.g., type inference) that remove it. The counterpoint to this argument is that Java was designed from the start to be easy to read (code is read more often than written) and that many programmers, especially novices, find the extra type information helpful when reading code. Java is widely used in enterprise environments, which often have separate dev and ops teams. The extra verbosity can often be a blessing when you are responding to an outage call, or when you need to maintain and patch code that was written by developers who have long since moved on. In recent versions of Java, the language designers have attempted to respond to some of these points by finding places where the syntax can become less verbose and by making better use of type information. For example:
📄 Page 19
// Files helper methods byte[] contents = Files.readAllBytes(Paths.get("/home/ben/myFile.bin")); // Diamond syntax for repeated type information List<String> l = new ArrayList<>(); // Local variables can be type inferred var threadPool = Executors.newScheduledThreadPool(2); // Lambda expressions simplify Runnables threadPool.submit(() -> { System.out.println("On Threadpool"); }); However, Java’s overall philosophy is to make changes to the language only very slowly and carefully, so the pace of these changes may not satisfy detractors completely. Slow to Change The original Java language is now over 30 years old and has not undergone a complete and total revision in that time. Many other languages (e.g., Microsoft’s C#) have released backward-incompatible versions in the same period, and some developers criticize Java for not doing likewise. Furthermore, the Java language has sometimes come under fire for being slow to adopt language features that are now commonplace in other languages. The conservative approach to language design that Oracle (and originally Sun) has taken is an attempt to avoid imposing the costs and externalities of misfeatures on a very large user base. Many Java shops have made major investments in the technology, and the language designers have taken seriously the responsibility of not disrupting the existing user and install base. Each new language feature needs to be very carefully thought about—not only in isolation but in terms of how it will interact with all the existing features of the language. New features can sometimes have impacts beyond their immediate scope— and Java is widely used in very large codebases, where there are more potential places for an unexpected interaction to manifest. It is almost impossible to remove a feature that turns out to be incorrect after it has shipped. Java has a couple of misfeatures (such as the serialization mechanism) that have been all-but-impossible to remove safely without impacting the install base. The language designers have taken the view that extreme caution is required when evolving the language.
📄 Page 20
Having said that, the new language features that have arrived in recent versions are a significant step toward addressing the most common complaints about missing features, and they should cover many of the idioms that developers have been asking for. Performance Problems The Java platform is still sometimes criticized for being slow—but of all the criticisms that are leveled at the platform, this is probably the one that is least justified. It is a genuine myth about the platform. Release 1.3 of Java brought in the HotSpot Virtual Machine and its JIT compiler. Since then, there have been over 25 years of continual innovation and improvement in the virtual machine and its performance. The Java platform is now blazingly fast, regularly winning performance benchmarks on popular frameworks, and even beating native- compiled C and C++. Criticism in this area appears to be largely caused by a folk memory that Java was slow at some point in the past. Some of the larger, more sprawling architectures that Java has been used within may also have contributed to this impression. The truth is that any large architecture will require benchmarking, analysis, and performance tuning to get the best out of it—and Java is no exception. The core of the platform—language and JVM—was and remains one of the fastest general-use environments available to the developer. One area that still has a lot of misconceptions is developer’s understanding of Java’s GC, and this deserves a mention here (we’ll do a full treatment of GC in Chapter 9). Many universities teach an introduction to GC as part of their curriculum, but it is the very simplest form of the algorithm, and doesn’t reflect in any way the techniques used in a modern production garbage collector. A modern GC (Java/JVM is best-in-class, but other modern languages also have pretty good implementations) gives you safe and flexible automatic memory management at extremely low cost. GC is not a significant performance concern for most serverside workloads anymore, apart from very specific niche use cases. Insecure Some people have historically criticized Java’s record of security vulnerabilities.
The above is a preview of the first 20 pages. Register to read the complete e-book.

💝 Support Author

0.00
Total Amount (¥)
0
Donation Count

Login to support the author

Login Now

Recommended for You

Loading recommended books...
Failed to load, please try again later
Back to List