java基础之 Advanced Class Design
Abstract Classes
In many programming situations, you want to specify an abstraction without specifying
implementation-level details. In such cases, you can use either abstract classes or interfaces. Abstract classes
are used when you want to define an abstraction with some common functionality.
Points to Remember
Review the following points about abstract classes and abstract methods :
? The abstract keyword can be applied to a class or a non-static method.
? An abstract class may have methods or fields declared static. However, the abstract
keyword cannot be applied to fields or static methods.
? An abstract class can extend another abstract class or can implement an interface.
? An abstract class can be derived from a concrete class! Although the language allows
it, it is not a good idea to do so.
? An abstract class need not declare an abstract method, which means it is not
necessary for an abstract class to have any methods declared as abstract. However, if
a class has an abstract method, it should be declared as an abstract class.
? A subclass of an abstract class needs to provide implementation of all the abstract
methods; otherwise you need to declare that subclass as an abstract class.
Using the “final” Keyword
The final keyword can be applied for classes, methods, and variables. You cannot extend a final class,
you cannot override a final method, and you cannot change the value of a final variable once it is initialized.
Final Classes
A final class is a non-inheritable class—that is to say, if you declare a class as final, you cannot subclass it.
Two important reasons you may not want to allow a class to be subclassed are:
1. To prevent a behavior change by subclassing. In some cases, you may think that
the implementation of the class is complete and should not change. If overriding
is allowed, then the behavior of methods might be changed. You know that a
derived object can be used where a base class object is required, and you may
not prefer it in some cases. By making a class final, the users of the class are
assured the unchanged behavior.
2. Improved performance. All method calls of a final class can be resolved at
compile time itself. As there is no possibility of overriding the methods, it is not
necessary to resolve the actual call at runtime for final classes, which translates to
improved performance. For the same reason, final classes encourage the inlining
of methods. With inlining, a method body can be expanded as part of the calling
code itself, thereby avoiding the overhead of making a function call. If the calls
are to be resolved at runtime, they cannot be inlined.
In the Java library, many classes are declared as final; for example, the String (java.lang.String)
and System (java.lang.System) classes. These classes are used extensively in Java programs. If these
two classes are not declared final, it is possible for someone to change the behavior of these classes by
subclassing and then the whole program can start behaving differently. To avoid such a problem, widely
used classes like these and wrapper classes such as Number and Integer are made final in the Java library.
Final Methods and Variables
In a class, you may declare a method final. The final method cannot be overridden. Therefore, if you have
declared a method as final in a non-final class, then you can extend the class but you cannot override the
final method. However, other non-final methods in the base class can be overridden in the derived class
implementation.
Final variables are like CD-ROMs: once you write something on them, you cannot write again. In
programming, constants such as PI can be declared as final since you don’t want anyone to modify their
values. If you try to change a final variable after initialization, you will get a compiler error.
Points to Remember
Review the following points :
? The final modifier can be applied to a class, method, or variable. All methods of a
final class are implicitly final (hence non-overridable).
? A final variable can be assigned only once. If a variable declaration defines a
variable as final but did not initialize it, then it is referred to as blank final. You need
to initialize a blank final in all the constructors you have defined in the class or in an
initialization block.
? The keyword final can be applied to parameters. The value of a final parameter
cannot be changed once assigned.
Flavors of Nested Classes
Create inner classes including static inner class, local class, nested class, and anonymous inner class
Classes defined within the body of another class (or interface) are known as nested classes. Typically
you define a class, which is a top-level class directly belonging to a package. In contrast, nested classes are
classes contained within another class or interface.
What is the benefit of creating classes inside another class or interface? There are several benefits. First,
you can put related classes together as a single logical group. Second, nested classes can access all class
members of the enclosing class, which might be useful in certain cases. Third, nested classes simplify code.
For example, anonymous inner classes are useful for writing simpler event-handling code with AWT/Swing.
There are four types or flavors of nested classes in Java:
? Static nested class
? Inner class
? Local inner class
? Anonymous inner class
The distinctions among these four flavors are not evident at first sight. Figure 3-1 helps clarify the
differences between them. A local class is defined within a code block (whether a method, constructor, or
initialization block), whereas a non-local class is defined inside a class. A static class is qualified using the
static keyword, whereas a non-static class does not use the static keyword with the class definition. In an
anonymous class, you don’t provide the name of the class; you just define its body.
As you can observe in Figure 3-1, static nested classes are static and non-local, whereas inner classes
are non-static and non-local. A non-static and local nested class is a local inner class, and a local and
anonymous nested class is an anonymous inner class.
Static Nested Classes (or Interfaces)
You can define a class (or an interface) as a static member inside another class (or interface). Since the
outer type can be a class or an interface and the inner ones can also be a class or interface, there are four
combinations. The following are examples of these four types so that you can see their syntax:
class Outer { // an outer class has a static nested class
static class Inner {}
}
interface Outer { // an outer interface has a static nested class
static class Inner {}
}
class Outer { // an outer class has a static nested interface
static interface Inner {}
}
interface Outer { // an outer interface has a static nested interface
static interface Inner {}
}
You don’t have to explicitly use the static keyword with a nested interface, since it is implicitly static.
Now, let’s look at an example that creates and uses static nested classes.
Points to Remember
Here are some notable aspects of static nested classes (and interfaces) that will help you on the OCPJP 8 exam:
? The accessibility (public, protected, etc.) of the static nested class is defined by
the outer class.
? The name of the static nested class is expressed with
OuterClassName.NestedClassName syntax.
? When you define an inner nested class (or interface) inside an interface, the nested
class is declared implicitly public and static. This point is easy to remember: any
field in an interface is implicitly declared public and static, and static nested
classes have this same behavior.
? Static nested classes can be declared abstract or final.
? Static nested classes can extend another class or they can be used as base classes.
? Static nested classes can have static members. (As you’ll see shortly, this statement
does not apply to other kinds of nested classes.)
? Static nested classes can access the members of the outer class (only static members,
obviously).
? The outer class can also access the members (even private members) of the nested
class through an object of a nested class. If you don’t declare an instance of the
nested class, the outer class cannot access nested class elements directly.
Inner Classes
You can define a class (or an interface) as a non-static member inside another class. How about declaring a
class or an interface inside an interface? As you just saw in the third bullet above about static inner classes,
when you define a class or an interface inside an interface, it is implicitly static. So, it is not possible to
declare a non-static inner interface! That leaves two possibilities:
class Outer { // an outer class has an inner class
class Inner {}
}
class Outer { // an outer class has an inner interface
interface Inner {}
}
Every inner class is associated with an instance of the outer class. in other words, an inner class is always
associated with an enclosing object.
The outer and inner classes share a special relationship, like friends or members of same family.
Member accesses are valid irrespective of the access specifiers such as private. However, there is subtle
difference. You can access members of an outer class within an inner class without creating an instance; but
this is not the case with an outer class. You need to create an instance of inner class in order to access the
members (any members, including private members) of the inner class.
One limitation of inner classes is that you cannot declare static members in an inner class, like this:
class Outer {
class Inner {
static int i = 10;
}
}
If you try to do so, you’ll get the following compiler error:
Outer.java:3: inner classes cannot have static declarations
static int i = 10;
Points to Remember
Here are some important rules about inner classes and interfaces that might prove useful in the OCPJP 8 exam:
? The accessibility (public, protected, etc.) of the inner class is defined by the
outer class.
? Just like top-level classes, an inner class can extend a class or can implement
interfaces. Similarly, other classes can extend an inner class, and other classes or
interfaces can extend or implement an inner interface.
? An inner class can be declared final or abstract.
? Inner classes can have inner classes, but you’ll have a hard time reading or
understanding such complex nesting of classes. (Meaning: Avoid them!)
Local Inner Classes
A local inner class is defined in a code block (say, in a method, constructor, or initialization block). Unlike
static nested classes and inner classes, local inner classes are not members of an outer class; they are just
local to the method or code in which they are defined.
Here is an example of the general syntax of a local class:
class SomeClass {
void someFunction() {
class Local { }
}
}
As you can see in this code, Local is a class defined within someFunction. It is not available outside of
someFunction, not even to the members of the SomeClass. Since you cannot declare a local variable static,
you also cannot declare a local class static.
Since you cannot define methods in interfaces, you cannot have local classes or interfaces inside an
interface. Nor can you create local interfaces. In other words, you cannot define interfaces inside methods,
constructors, and initialization blocks.
You can pass only final variables to a local inner class. if you don‘t declare a variable that a local inner
class accesses, the compiler will treat it as effectively final.
Points to Remember
The following points about local classes may come up in the OCPJP 8 exam:
? You can create a non-static local class inside a body of code. Interfaces cannot have
local classes, and you cannot create local interfaces.
? Local classes are accessible only from the body of the code in which the class is
defined. The local classes are completely inaccessible outside the body of the code in
which the class is defined.
? You can extend a class or implement interfaces while defining a local class.
? A local class can access all the variables available in the body of the code in which it
is defined. Variables accessed by local inner classes are considered effectively final.
Anonymous Inner Classes
As the name implies, an anonymous inner class does not have a name. The declaration of the class automatically
derives from the instance-creation expression. They are also referred to simply as anonymous classes.
An anonymous class is useful in almost all situations where you can use a local inner class. A local
inner class has a name, whereas an anonymous inner class does not—and that’s the main difference. An
additional difference is that an anonymous inner class cannot have any explicit constructors. A constructor
is named after the name of the class, and since an anonymous class has no name, it follows that you cannot
define a constructor!
(A note here before we proceed: there are no such things as “anonymous interfaces.”)
Here is an example to understand the syntax of a local class:
class SomeClass {
void someFunction() {
new Object() { };
}
}
This code looks cryptic, doesn’t it? What is going on here? In the statement new Object() { };, you are
declaring a derived class of Object directly using the new keyword. It doesn’t define any code and returns an
instance of that derived object. The created object is not used anywhere, so it is ignored. The new expression
invokes the default constructor here; you could choose to invoke a multiple argument constructor of the
base class by passing arguments in the new expression.
Points to Remember
Note these points about anonymous classes that may be useful for the OPCJP 8 exam:
? Anonymous classes are defined in the new expression itself.
? You cannot explicitly extend a class or explicitly implement interfaces when defining
an anonymous class.
Enum Data Type
Use enumerated types including methods, and constructors in an enum type
Points to Remember
? Enums are implicitly declared public, static, and final, which means you cannot
extend them.
? When you define an enumeration, it implicitly inherits from java.lang.Enum.
Internally, enumerations are converted to classes. Further, enumeration constants
are instances of the enumeration class for which the constant is declared as a
member.
? You can apply the valueOf() and name() methods to the enum element to return the
name of the enum element.
? If you declare an enum within a class, then it is by default static.
? You cannot use the new operator on enum data types, even inside the enum class.
? You can compare two enumerations for equality using == operator.
? If enumeration constants are from two different enumerations, the equals() method
does not return true.
? When an enumeration constant’s toString() method is invoked, it prints the name
of the enumeration constant.
? The static values() method in the Enum class returns an array of the enumeration
constants when called on an enumeration type.
? Enumeration constants cannot be cloned. An attempt to do so will result in a
CloneNotSupportedException.
Enum avoids magic numbers, which improves readability and understandability of the source code. also,
enums are typesafe constructs. therefore, use enums wherever you need a set of related constants.
Interfaces
Develop code that declares, implements, and/or extends interfaces and use the atOverride annotation
An interface is a set of abstract methods that defines a protocol (i.e., a contract for conduct). Classes
that implement an interface must implement the methods specified in the interface. An interface defines a
protocol, and a class implementing the interface honors the protocol. In other words, an interface promises
certain functionality to its clients by defining an abstraction. All the classes implementing the interface
provide their own implementations for the promised functionality.
Points to Remember
Here are some key points about interfaces that will help you in the OCPJP 8 exam:
? An interface cannot be instantiated. A reference to an interface can refer to an object
of any of its derived types implementing it.
? An interface can extend another interface. Use the extends (and not the implements)
keyword for extending another interface.
? Interfaces cannot contain instance variables. If you declare a data member in an
interface, it should be initialized, and all such data members are implicitly treated as
“public static final” members.
? An interface can have three kinds of methods: abstract methods, default methods,
and static methods.
? An interface can be declared with empty body (i.e., an interface without any
members). For example, java.util defines the interface EventListener
without a body.
? An interface can be declared within another interface or class; such interfaces are
known as nested interfaces.
? Unlike top-level interfaces that can have only public or default access, a nested
interface can be declared public, protected, or private.
? If you are implementing an interface in an abstract class, the abstract class does not
need to define the method. But, ultimately a concrete class has to define the abstract
method declared in the interface.
? You can use the @Override annotation for a method to indicate that it is overriding a
method from its base type(s).
Points to Remember
Here are some key points about abstract, default, and static methods that will help you in the OCPJP 8 exam:
? You cannot declare members as protected or private. Only public access is
allowed for members of an interface. Since all methods are public by default, you can
omit the public keyword.
? All methods declared in an interface (i.e., without a method body) are implicitly
considered to be abstract. If you want, you can explicitly use the abstract qualifier
for the method.
? Default methods must have a method body. Default methods must be qualified
using the default keyword. The classes implementing the interface inherit the
default method definitions and they can be overridden.
? A default method can be overridden in a derived class as an abstract method; for
such overriding, the @Override annotation can also be used.
? You cannot qualify default methods as synchronized or final.
? Static methods must have a method body and they are qualified using the static
keyword.
? You cannot provide abstract keyword for static methods: Remember that you
cannot override static methods in derived classes, so it’s conceptually not possible to
leave static methods abstract by not providing a method body.
? You cannot use default keyword for static methods because all default methods are
instance methods.
Lambda Functions
introduction of lambdas required coordinated changes in the language, library, and the vM
implementation:
? the arrow operator (“->”) for defining lambda functions, the double colon operator
(“::”) used for method references, and the default keyword
? the streams library and the integration of the collections library with streams
? lambda functions are implemented using the invokedynamic instruction introduced
in Java 7
to support introduction of lambdas into the language, the type inference has also been strengthened in
Java 8. lambdas enabled library writers to create parallel algorithms in the library to exploit inherent
parallelism in the modern hardware (i.e., multi cores).
Lambda Functions: Syntax
A lambda function consistss of optional parameters, the arrow token, and the body:
LambdaParameters -> LambdaBody
? LambdaParameters are parameters to the lambda function are passed within opening
parenthesis “(“ and closing parenthesis ”)”. When more than one parameter is
passed, they are separated by commas.
? The arrow operator. To support lambdas, Java has introduced a new operator “->”,
also known as lambda operator or arrow operator. This arrow operator is required
because we need to syntactically separate the parameter from the body.
? LambdaBody can be an expression or a block. The body could consist of single
statement (in that case no explicit curly braces defining a block are required); such
a lambda body is known as "expression lambda." If there are many statements in a
lambda body, they need to be in a block of code; such a lambda body is known as
“block lambda.”
Compiler performs type inference for lambda expressions:
? The compiler infers the type of the parameters if you do not specify the type
parameters in a lambda function definition. When you specify the type of
parameters, you need to specify all or none; or else you will get a compiler error.
? You can omit the parenthesis if there is only one parameter. But in this case, you
cannot provide the type explicitly. You should leave it to the compiler to infer the
type of that single parameter.
? The return type of the lambda function is inferred from the body. If any of the code in
the lambda returns a value, then all the paths should return a value; or else you will
get a compiler error.
Some examples of valid lambda expressions (assuming that relevant functional interfaces are available):
? (int x) -> x + x
? x -> x % x
? () -> 7
? (int arg1, int arg2) -> (arg1 + arg2) / (arg1 – arg2)
Effectively Final Variables
Lambda functions can refer to local variables from the enclosing scope. The variable needs to be explicitly
declared final or that variable will be treated as effectively final. Effectively final means that the compiler treats
the variable as a final variable and will issue an error if we try to modify it within the lambda function or in the
rest of the function. This behavior of lambdas is similar to accessing variables in outer scope from local and
anonymous classes. The variables they access are also considered effectively final。nctions
Create and use Lambda expressions
introduction of lambdas required coordinated changes in the language, library, and the vM
implementation:
? the arrow operator (“->”) for defining lambda functions, the double colon operator
(“::”) used for method references, and the default keyword
? the streams library and the integration of the collections library with streams
? lambda functions are implemented using the invokedynamic instruction introduced
in Java 7
to support introduction of lambdas into the language, the type inference has also been strengthened in
Java 8. lambdas enabled library writers to create parallel algorithms in the library to exploit inherent
parallelism in the modern hardware (i.e., multi cores).
Lambda Functions: Syntax
A lambda function consistss of optional parameters, the arrow token, and the body:
LambdaParameters -> LambdaBody
? LambdaParameters are parameters to the lambda function are passed within opening
parenthesis “(“ and closing parenthesis ”)”. When more than one parameter is
passed, they are separated by commas.
? The arrow operator. To support lambdas, Java has introduced a new operator “->”,
also known as lambda operator or arrow operator. This arrow operator is required
because we need to syntactically separate the parameter from the body.
? LambdaBody can be an expression or a block. The body could consist of single
statement (in that case no explicit curly braces defining a block are required); such
a lambda body is known as "expression lambda." If there are many statements in a
lambda body, they need to be in a block of code; such a lambda body is known as
“block lambda.”
Compiler performs type inference for lambda expressions:
? The compiler infers the type of the parameters if you do not specify the type
parameters in a lambda function definition. When you specify the type of
parameters, you need to specify all or none; or else you will get a compiler error.
? You can omit the parenthesis if there is only one parameter. But in this case, you
cannot provide the type explicitly. You should leave it to the compiler to infer the
type of that single parameter.
? The return type of the lambda function is inferred from the body. If any of the code in
the lambda returns a value, then all the paths should return a value; or else you will
get a compiler error.
Some examples of valid lambda expressions (assuming that relevant functional interfaces are available):
? (int x) -> x + x
? x -> x % x
? () -> 7
? (int arg1, int arg2) -> (arg1 + arg2) / (arg1 – arg2)
Effectively Final Variables
Lambda functions can refer to local variables from the enclosing scope. The variable needs to be explicitly
declared final or that variable will be treated as effectively final. Effectively final means that the compiler treats
the variable as a final variable and will issue an error if we try to modify it within the lambda function or in the
rest of the function. This behavior of lambdas is similar to accessing variables in outer scope from local and
anonymous classes. The variables they access are also considered effectively final。
Points to Remember
Here are some key points about lambda functions that will help you in the OCPJP 8 exam:
? Lambda expressions can occur only in the contexts where assignment, function calls,
or casts can occur.
? A lambda function is treated as a nested block. Hence, just like a nested block, we
cannot declare a variable with the same name as a local variable in the enclosing
block.
? Lambda functions must return values from all the branches—otherwise it will result
in a compiler error.
? When argument types are declared, the lambda is known as “explicitly typed”; if they
are inferred, it is “implicitly typed.”
? What happens if a lambda expression throws an exception? If it is a checked
exception, then the method in the functional interface should declare that;
otherwise it will result in a compiler error.
读书笔记:
Oracle Certified Professional Java SE 8 Programmer Exam 1Z0-809: A Comprehensive
OCPJP 8 Certification Guide
Copyright ? 2016 by S G Ganesh, Hari Kiran, and Tushar Sharma
ISBN-13 (pbk): 978-1-4842-1835-8
ISBN-13 (electronic): 978-1-4842-1836-5