{Effective Java} Chap 8 General Programming

来源:互联网 发布:网络直播扰民 如何取证 编辑:程序博客网 时间:2024/06/03 20:20


This Chapter discusses about the treatment of local variables, control structures, the use of libraries, the use of various data types and the use of reflection and native methods. Finally, it talks about optimization and naming conventions.


Item 45: Minimize the scope of local variables

The most powerful technique for minimizing the scope of a local variable is to declare it where it is first used.

Nearly every local variable declaration should contain an initializer.

Prefer for loops to while loops. Less copy-and-paste error.

Keep methods small and focused.



Item 46: Prefer for-each loops to traditional for loops

After 1.5, we have for-each, it may offer a slight performance advantage because it computes the limit of the array index only once.

Nested for-each loop is more clear.

If you are writing a type that represents a group of elements, implement Iterable. 


In summary, use for-each wherever you can. But except 3 situations:

1. Filtering---traverse a collection and remove selected elements then you need to use an explicit iterator so you can call its remove method.

2. Transforming---traverse a list or array and replace some values, you need the list iterater or array index in order to set the value of the element.

3.Parallel iteration---need explicit control over the iterater or index variable, so they can be advanced in lockstep.


Item 47: Know and use the libraries

By using a standard library you take advantage of the knowledge of the experts who wrote it and the experience of those who used it before you.

You don't waste your time writing ad hoc solutions to problems that are only marginally related to your work.

Standard libraries performance tends to improve over time, with no effort on your part. 

Library also tends to gain new functionality over time.

You place your code in the mainstream.

Numerous features are added to the libraries in every major release, and it pays to keep abreast of these additions.

The libraries are too big to study all the docs, but we should be familiar with the contents of java.lang, java.util, and java.io.


In 1.2, Collections Framework was added to the java.util package which allows collections to be manipulated independently. It allows for interoperability among unrelated APIs, and fosters software reuse.

In 1.5, java.util.concurrent was added which contains high-level concurrency utilities to simplify the task of multithreaded programming and low-level concurrency primitives to allow higher-level concurrent abstractions.


To summarize, if you don't know, check first. Generally speaking, library code is likely to be better.



Item 48: Avoid float and double if exact answers are required

Float and double types are designed for scientific and engineering calculations. They perform binary floating-point arithmetic. They are particularly illsuited for monetary calculations. 

They can't represent 0.1 exactly.

Use BigDecimal, int or long for monetary calculations.

BigDecimal: it's less convenient and slower. But it givescontrol over rounding, if legally mandated (指定) rounding behavior, we can select from 8 rounding modes.


In summary, use BigDecimal if you want to keep track of the decimal point and don't mind the cost/inconvenience. If the quantities don't exceed nine decimal digits, use int. If don't exceed 18 digits, use long. If over 18 digits, must use BigDecimal.



Item 49: Prefer primitive types to boxed primitives

Differences:

1. primitives have only values, boxed have identities distinct from their values. E.g 2 boxed may have the same value, but different identities.

2. primitive has only fully functional values, but boxed has one nonfunctional value which is null, in addition to all of the functional values of its corresponding primitive type. 

3. primitives are more time- and space- efficient.


Applying == to boxed primitives is almost always wrong.

In nearly every case when you mix primitives and boxed primitives in a single operation, the boxed is auto-unboxed. If a null object reference is auto-unboxed, you get a NullPointerException.


When to use boxed:

1. elements, keys, and values in collections.

2. must use boxed as type parameters in parameterized types.

3. when making reflective method invocations.


In summary, Autoboxing reduces the verbosity (冗长) but not the danger of using boxed. When comparing 2 boxed with ==, it does an identity comparison. When mix boxed and primitive, it does unboxing which may throw a NullPointerException. If boxing primitive values, it can result in costly and unnecessary object creations.


Item 50: Avoid strings where other types are more appropriate

Strings are designed to represent text.

Strings are poor substitutes for other value types. So when data coms into a program, it should be translated to the right type.

Strings are poor substitutes for enum types.

Strings are poor substitutes for aggregate types. If an entity have multiple components, it's better to write a private static member class to represent the aggregate.

Strings are poor substitutes for capabilities. E.g Thread-local variable facility at first use String to get key from clients, but it's easy to fail if clients provide same keys or use other's keys.


To summarize, avoid natural tendency to use strings to represent objects. Types for which strings are commonly misused include primitive types, enums and aggreagte types.


Item 51: Beware the performance of string concatenation

String is immutable. Using the string concatenation operator repeatedly to concatenate n strings requires time quadratic in n.

Use a stringbuilder which was added since 1.5 and is an unsynchronized replacement for stringbuffer which is now obsolete.

Alternatively, use a character array, or process the strings one at a time instead of combining them.



Item 52:Refer to objects by their interfaces

If appropriate interface types exist, then parameters, return values, variables, and fields should all be declared using interface types.

If you get into the habit of using interfacces as types, your program will be much more flexible.

If you need any special properties of an implementation,document these requirements when you declare the variable.

It is entirely appropriate to refer to an object by a class rather than an interface if no appropriate interface exists.

Value classes are often final and rarely have corresponding interfaces. So just use concrete classes. E.g BigInteger and Random.

If an object belongs to such a class-based framework, it is preferable to refer to it by the base abstract class. E.g java.util.TimerTask.

If we need extra functions in subclasses, just use concrete classes.



Item 53: Prefer interfaces to reflection

Reflection was designed for component-based application builder tools. Such tools generally load classes on demand and use reflection to find out what methods and constructors they support. They let users interactively construct applications that access these classes normally.

Reflection is usedonly at design time.

Suchas class browsers, object inspectors, code analysis tools and interpretive embedded systems, RPC .

Reflection allows to manipulates their underlying Constructor, Method and Field reflectively.

You lose all the benefits of compile-time type checking including exception checking. E.g only at runtime, can you know you have called a nonexisting method.

The code required to perform reflective access is clumsy and verbose.

Performance suffers. Much slower than normal method invocation.


Objects should not be accessed reflectively in normal applications at runtime.

You can obtain many of the benefits of reflection while incurring few of its costs by using it only in a very limited form,

Create instances reflectively and access them normally via their interface of superclass.

If the constructor has no parameters, use Class.newInstance method.

E.g

//Reflective instantiation with interface accesspublic static void main(String[] args) {//Translate the class name into a Class objectClass<?> cl = null;try {cl = Class.forName(args[0]);} catch (ClassNotFoundException e) {System.err.println("Class not found.");System.exit(1);}//Initialize the classSet<String> s = null;try {s = (Set<String>) cl.newInstance();} catch (IllegalAccessException e) {System.err.println("Class not accessible.");System.exit(1);}catch (InstantiationException e) {System.err.println("Class not instantiable.");System.exit(1);}//Exercise the sets.addAll(Arrays.asList(args).subList(1, args.length));System.out.println(s);}



The program can be turned intoa generic set tester that validates the specified implementation and also a generic set performance analysis tool. E.g service provider framework.

Disadvantages when initialization:

1. may generate runtime errors.

2. codes are long


It's bad to use System.exit which terminatesthe entire JVM

A legitimate use of reflection is when your class depends on other classes that may not exist at runtime. It's usable if you are writing a package that must run against the minimal environment required to support it.


In summary, reflection has many disadvantages. If you are writing a program that has to work with classes unknown at compile time, you should use reflection only to initialize objects, and access the objects using some interface or superclass that is known at compile time.


Item 54: Use native methods judiciously

JNI(java native interface) allows us call methods that are written in other language.

Native methods' uses:

1. provide access to platform-specific facilities such as registries and file locks.

java.util.prefs since 1.4 offers registry and java.awt.SystemTray since 1.6 offers access to the desktop system tray area.

2. provide access to libraries of legacy code, which can provide access to legacy data.

3. write performance-critical parts of applications in native languages for improved performance.

But it's rarely advisable to do so. Because JVM is much faster and e.g java.math was added since 1.1, BigInteger was written based on C library. In 1.3, it used Java entirely.


Disadvantages:

native language is not safe, is platform dependent, and hard to debug and also require 'glue code' that is hard to read and write.


In summary, think twice before using native methods. Rarely use them for improved performance. If must use native methods to access low-level resources or legacy libraries, use as little native code as possible and test it thoroughly.


Item 55: Optimize judiciously

More computing sins are committed in the name of efficiency (without necessarily achieving it) than for any other single reason --- including blind stupidity.

We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of evil.

We follow two rules in the matter of optimization:

Rule1. Dont do it.

Rule2. (for experts only) Don't do it yet --- that is not until you have a perfectly clear and unoptimized solution. 

 

It's easy to do more harm than good, especially if you optimize prematurely.

Strive to write good softwares rather than fast ones.

Strive to avoid design decisions that limit the performance. Such as APIs, wire-level protocols, and persistent data formats. They are important and might not changeable.

Comsider the performance comsequences of your API decisions.


e.g make a public type mutable may require a lot of needless defensive coping.

It is a very bad idea to wrap an API to achieve good performance.


Once you've carefully designed your program and produced a clear, concise and well-structured implementation, then it may be time to consider optimization.

Measure performance before and after each attempted optimization.

Profiling tools help us know runtime information. It's really useful for us to have aprofiler.

Not only is Java's performance model ill-defined, but it varies from JVM implementation to JVM implementation, from release to release and from processor tp processor. Better test different platforms and make trade-offs.


In summary, strive to write good softwares not fast ones. Don't think about optimization while designing systems and especially APIs, wire-level protocols and persistent data formats. When finished implementation, measure the performance. If not fast enough, use profiler to help locate problems. The first step is to examine algorithms. Repeat this process as necessary, measuring the performance after every change.


Item 56: Adhere to generally accepted naming conventions

Read The Java Language Specification.

  • Typographical (字面上的):

Package: com.google.inject, org.joda.time.format

Class or Interface: Timer, FutureTask, LinkedHashMap, HttpServlet

Method or Filed: remove, ensureCapacity, getCrc

Local Variable: i, xref, houseNumber

Type Parameter: T, E, K, V, X, T1, T2


  • Grammatical:

is, to, as, Value, valueOf, of, get, newInstance and so on.



To summarize, internalize the standard naming conventions and learn to use them as second nature. The typographical naming conventions are straightforward and largely unambiguous; the grammatical conventions are more complex and looser. These conventions should not be followed slavishly if long-held conventional usage dictates otherwise. Use common sense.







0 0