Notes for <<Thinking In Java>>
String
Thus, when you create a toString( ) method, if the operations are simple ones that the compiler can figure out on its own, you can generally rely on the compiler to build the result in a reasonable fashion. But if looping is involved, you should explicitly use a StringBuilder in your toString( )
StringBuilder was introduced in Java SE5. Prior to this, Java used StringBuffer, which ensured thread safety (see the Concurrency chapter) and so was significantly more expensive. Thus, string operations in Java SE5/6 should be faster.
- Holding
An iterator is an object whose job is to move through a sequence and select each object in that sequence without the client programmer knowing or caring about the underlying structure of that sequence. In addition, an iterator is usually what’s called a lightweight object: one that’s cheap to create. For that reason, you’ll often find seemingly strange constraints for iterators; for example, the Java Iterator can move in only one direction. There’s not much you can do with an Iterator except:
1.
2. 3.
AskaCollectiontohandyouanIteratorusingamethodcallediterator().That Iterator will be ready to return the first element in the sequence.
Getthenextobjectinthesequencewithnext().
See if there are any more objects in the sequence with hasNext( ).
4. Remove the last element returned by the iterator with remove( ).
ListIterator
The ListIterator is a more powerful subtype of Iterator that is produced only by List classes. While Iterator can only move forward, ListIterator is bidirectional.
LinkedList
The LinkedList also implements the basic List interface like ArrayList does, but it performs certain operations (insertion and removal in the middle of the List) more efficiently than does ArrayList. Conversely, it is less efficient for random-access operations.
Set
A Set refuses to hold more than one instance of each object value. If you try to add more than one instance of an equivalent object, the Set prevents duplication. The most common use for a Set is to test for membership, so that you can easily ask whether an object is in a Set. Because of this, lookup is typically the most important operation for a Set, so you’ll usually choose a HashSet implementation, which is optimized for rapid lookup.
PriorityQueue
First-in, first-out (FIFO) describes the most typical queuing discipline. A queuing discipline is what decides, given a group of elements in the queue, which one goes next. First-in, first- out says that the next element should be the one that was waiting the longest.
Apriority queue says that the element that goes next is the one with the greatest need (the highest priority). For example, in an airport, a customer might be pulled out of a queue if their plane is about to leave. If you build a messaging system, some messages will be more important than others, and should be dealt with sooner, regardless of when they arrive. The PriorityQueue was added in Java SE5 to provide an automatic implementation for this behavior.
Iterable interface -> foreach
The reason that this works is that Java SE5 introduced a new interface called Iterable which contains an iterator( ) method to produce an Iterator, and the Iterable interface is what foreach uses to move through a sequence. So if you create any class that implements Iterable, you can use it in a foreach statement:
- Enumerated
Adding methods to an enum
Except for the fact that you can’t inherit from it, an enum can be treated much like a regular class. This means that you can add methods to an enum. It’s even possible for an enum to have a main( ).
Notice that if you are going to define methods you must end the sequence of enum instances with a semicolon. Also, Java forces you to define the instances as the first thing in the enum.
Constant-specific methods
Java enums have a very interesting feature that allows you to give each enum instance different behavior by creating methods for each one. To do this, you define one or more abstract methods as part of the enum, then define the methods for each enum instance.
- Annotation
Java SE5 contains three generalpurpose built-in annotations, defined in java.lang:
@Override, to indicate that a method definition is intended to override a method in the base class. This generates a compiler error if you accidentally misspell the method name or give an improper signature.2
@Deprecated, to produce a compiler warning if this element is used.
@SuppressWarnings, to turn off inappropriate compiler warnings. This annotation
is allowed but not supported in earlier releases of Java SE5 (it was ignored).
Apart from the @ symbol, the definition of @Test is much like that of an empty interface. An annotation definition also requires the meta-annotations @Target and (@Retention. @Target defines where you can apply this annotation (a method or a field, for example). @Retention defines whether the annotations are available in the source code (SOURCE), in the class files (CLASS), or at run time (RUNTIME).
Meta-annotations
There are currently only three standard annotations (described earlier) and four meta- annotations defined in the Java language. The meta-annotations are for annotating annotations:
@Target
Where this annotation can be applied. The possible ElementType arguments are:
CONSTRUCTOR: Constructor declaration
FIELD: Field declaration (includes enum constants) LOCAL_VARIABLE: Local variable declaration METHOD: Method declaration
PACKAGE: Package declaration
PARAMETER: Parameter declaration
TYPE: Class, interface (including annotation type), or enum declaration@Retention
How long the annotation information is kept. The possible RetentionPolicy arguments are:
SOURCE: Annotations are discarded by the compiler.
CLASS: Annotations are available in the class file by the compiler but can be discarded by the VM. RUNTIME: Annotations are retained by the VM at run time, so they may be read reflectively.
@Documented
Include this annotation in the Javadocs.
@Inherited
@Inherited
Allow subclasses to inherit parent annotations.
Default value constraints
The compiler is quite picky about default element values. No element can have an unspecified value. This means that elements must either have default values or values provided by the class that uses the annotation.
There is another restriction, which is that none of the non-primitive type elements are allowed to take null as a value, either when declared in the source code or when defined as a default value in the annotation interface.
There are many available frameworks for mapping objects to relational databases, and more and more of them are making use of annotations.
Each annotation you write will need its own processor, but the apt tool can easily group several annotation processors together. It allows you to specify multiple classes to be processed, which is a lot easier than having to iterate through File classes yourself. You can also add listeners to receive notification of when an annotation processing round is complete.
- Concurrency
The Thread class
The traditional way to turn a Runnable object into a working task is to hand it to a Thread constructor.
Daemon:
You can find out if a thread is a daemon by calling isDaemon( ). If a thread is a daemon, then any threads it creates will automatically be daemons
You should be aware that daemon threads will terminate their run( ) methods without executing finally clauses:
- Error Handling with Exceptions
Creating your own exceptions
You’re not stuck using the existing Java exceptions. The Java exception hierarchy can’t foresee all the errors you might want to report, so you can create your own to denote a special problem that your library might encounter.
To create your own exception class, you must inherit from an existing exception class, preferably one that is close in meaning to your new exception (although this is often not possible). The most trivial way to create a new type of exception is just to let the compiler create the default constructor for you, so it requires almost no code at all:
The exception specification
In Java, you’re encouraged to inform the client programmer, who calls your method, of the exceptions that might be thrown from your method. This is civilized, because the caller can then know exactly what code to write to catch all potential exceptions. Of course, if the source code is available, the client programmer could hunt through and look for throw statements, but a library might not come with sources. To prevent this from being a problem, Java provides syntax (and forces you to use that syntax) to allow you to politely tell the client programmer what exceptions this method throws, so the client programmer can handle them. This is the exception specification and it’s part of the method declaration, appearing after the argument list.
The exception specification uses an additional keyword, throws, followed by a list of all the potential exception types. So your method definition might look like this:
void f() throws TooBig, TooSmall, DivZero { //...
However, if you say
void f() { //...
it means that no exceptions are thrown from the method {except for the exceptions inherited from RuntimeException, which can be thrown anywhere without exception specifications—these will be described later).
There is one place you can lie: You can claim to throw an exception that you really don’t. The compiler takes your word for it, and forces the users of your method to treat it as if it really does throw that exception. This has the beneficial effect of being a placeholder for that exception, so you can actually start throwing the exception later without requiring changes to existing code. It’s also important for creating abstract base classes and interfaces whose derived classes or implementations may need to throw exceptions.
Exceptions that are checked and enforced at compile time are called checked exceptions.
Exception chaining
Often you want to catch one exception and throw another, but still keep the information about the originating exception—this is called exception chaining. Prior to JDK 1.4, programmers had to write their own code to preserve the original exception information, but now all Throwable subclasses have the option to take a cause object in their constructor. The cause is intended to be the originating exception, and by passing it in you maintain the stack trace back to its origin, even though you’re creating and throwing a new exception.
It’s interesting to note that the only Throwable subclasses that provide the cause argument in the constructor are the three fundamental exception classes Error (used by the JVM to report system errors), Exception, and RuntimeException. If you want to chain any other exception types, you do it through the initCause( ) method rather than the constructor.
What’s finally for?
In a language without garbage collection and without automatic destructor calls,5 finally is important because it allows the programmer to guarantee the release of memory regardless of what happens in the try block. But Java has garbage collection, so releasing memory is virtually never a problem. Also, it has no destructors to call. So when do you need to use finally in Java?
The finally clause is necessary when you need to set something other than memory back to its original state. This is some kind of cleanup like an open file or network connection, something you’ve drawn on the screen, or even a switch in the outside world, as modeled in the following example:
Exception restrictions
When you override a method, you can throw only the exceptions that have been specified in the base-class version of the method. This is a useful restriction, since it means that code that works with the base class will automatically work with any object derived from the base class (a fundamental OOP concept, of course), including exceptions.
Converting checked to unchecked exceptions
Throwing an exception from main( ) is convenient when you’re writing simple programs for your own consumption, but is not generally useful. The real problem is when you are writing an ordinary method body, and you call another method and realize, "I have no idea what to do with this exception here, but I don’t want to swallow it or print some banal message." With chained exceptions, a new and simple solution prevents itself. You simply "wrap" a checked exception inside a RuntimeException by passing it to the RuntimeException constructor, like this:
try{
// ... to do something useful
} catch(IDontKnowWhatToDoWithThisCheckedException e) {
throw new RuntimeException(e);
}
- Interface
A class containing abstract methods is called an abstract class. If a class contains one or more abstract methods, the class itself must be qualified as abstract. (Otherwise, the compiler gives you an error message.)
interface is more than just an abstract class taken to the extreme, since it allows you to perform a variation of "multiple inheritance" by creating a class that can be upcast to more than one base type.
An interface can also contain fields, but these are implicitly static and final.
You can choose to explicitly declare the methods in an interface as public, but they are public even if you don’t say it. So when you implement an interface, the methods from the interface must be defined as public. Otherwise, they would default to package access, and you’d be reducing the accessibility of a method during inheritance, which is not allowed by the Java compiler.
Multiple Inheritence
When you combine a concrete class with interfaces this way, the concrete class must come first, then the interfaces.
Keep in mind that one of the core reasons for interfaces is shown in the preceding example: to upcast to more than one base type (and the flexibility that this provides). However, a second reason for using interfaces is the same as using an abstract base class: to prevent the client programmer from making an object of this class and to establish that it is only an interface.
This brings up a question: Should you use an interface or an abstract class? If it’s possible to create your base class without any method definitions or member variables, you should always prefer interfaces to abstract classes. In fact, if you know something is going to be a base class, you can consider making it an interface (this subject will be revisited in the chapter summary).
Using the same method names in different interfaces that are intended to be combined generally causes confusion in the readability of the code, as well. Strive to avoid it.
Interface Fields:
Notice the Java style of using all uppercase letters (with underscores to separate multiple words in a single identifier) for static finals that have constant initializers. The fields in an interface are automatically public, so that is not explicitly specified.
Fields defined in interfaces cannot be "blank finals," but they can be initialized with non- constant expressions. For example:
Nestinglnterfaces shows the various ways that nested interfaces can be implemented. In particular, notice that when you implement an interface, you are not required to implement any interfaces nested within. Also, private interfaces cannot be implemented outside of their defining classes.
- Polymorphism
Polymorphism is the third essential feature of an object-oriented programming language, after data abstraction and inheritance.
Wouldn’t it be much nicer if you could just write a single method that takes the base class as its argument, and not any of the specific derived classes? That is, wouldn’t it be nice if you could forget that there are derived classes, and write your code to talk only to the base class?
That’s exactly what polymorphism allows you to do.
All method binding in Java uses late binding unless the method is static or final (private methods are implicitly final). This means that ordinarily you don’t need to make any decisions about whether late binding will occur—it happens automatically.
Pitfall: fields and static methods
Once you learn about polymorphism, you can begin to think that everything happens polymorphically. However, only ordinary method calls can be polymorphic. For example, if you access a field directly, that access will be resolved at compile time, as the following example demonstrates: 1
static methods are associated with the class, and not the individual objects.
the order of constructor calls for a complex object is as follows:
1. Thebase-classconstructoriscalled.Thisstepisrepeatedrecursivelysuchthatthe root of the hierarchy is constructed first, followed by the next-derived class, etc., until the most-derived class is reached.
2. Memberinitializersarecalledintheorderofdeclaration.
Polymorphism 205
3. Thebodyofthederived-classconstructoriscalled.
The actual process of initialization is:
Thestorageallocatedfortheobjectisinitializedtobinaryzerobeforeanythingelse happens.
Thebase-classconstructorsarecalledasdescribedpreviously.Atthispoint,the overridden draw( ) method is called (yes, before the RoundGlyph constructor is called), which discovers a radius value of zero, due to Step 1.
Memberinitializersarecalledintheorderofdeclaration.
Thebodyofthederived-classconstructoriscalled.
There’s an upside to this, which is that everything is at least initialized to zero (or whatever zero means for that particular data type) and not just left as garbage. This includes object references that are embedded inside a class via composition, which become null. So if you forget to initialize that reference, you’ll get an exception at run time. Everything else gets zero, which is usually a telltale value when looking at output.
On the other hand, you should be pretty horrified at the outcome of this program. You’ve done a perfectly logical thing, and yet the behavior is mysteriously wrong, with no complaints from the compiler. (C++ produces more rational behavior in this situation.) Bugs like this could easily be buried and take a long time to discover.
As a result, a good guideline for constructors is, “Do as little as possible to set the object into a good state, and if you can possibly avoid it, don’t call any other methods in this class.” The only safe methods to call inside a constructor are those that are final in the base class. (This also applies to private methods, which are automatically final.) These cannot be overridden and thus cannot produce this kind of surprise. You may not always be able to follow this guideline, but it’s something to strive towards.
- Reusing
Delegation
A third relationship, which is not directly supported by Java, is called delegation. This is midway between inheritance and composition, because you place a member object in the class you’re building (like composition), but at the same time you expose all the methods from the member object in your new class (like inheritance).
Upcasting
Casting from a derived type to a base type moves up on the inheritance diagram, so it’s commonly referred to as upcasting. Upcasting is always safe because you’re going from a more specific type to a more general type. That is, the derived class is a superset of the base class. It might contain more methods than the base class, but it must contain at least the methods in the base class. The only thing that can occur to the class interface during the upcast is that it can lose methods, not gain them. This is why the compiler allows upcasting without any explicit casts or other special notation.
Just because something is final doesn’t mean that its value is known at compile time. This is demonstrated by initializing i4 and INT_5 at run time using randomly generated numbers. This portion of the example also shows the difference between making a final value static or non-static. This difference shows up only when the values are initialized at run time, since the compile-time values are treated the same by the compiler. (And presumably optimized out of existence.) The difference is shown when you run the program. Note that the values of i4 for fd1 and fd2 are unique, but the value for INT_5 is not changed by creating the second FinalData object. That’s because it’s static and is initialized once upon loading and not each time a new object is created.
Blank finals
Java allows the creation of blank finals, which are fields that are declared as final but are not given an initialization value. In all cases, the blank final must be initialized before it is used, and the compiler ensures this. However, blank finals provide much more flexibility in the use of the final keyword since, for example, a final field inside a class can now be different for each object, and yet it retains its immutable quality.
You’re forced to perform assignments to finals either with an expression at the point of definition of the field or in every constructor. That way it’s guaranteed that the final field is always initialized before use.
final arguments
Java allows you to make arguments final by declaring them as such in the argument list. This means that inside the method you cannot change what the argument reference points to
final methods
There are two reasons for final methods. The first is to put a “lock” on the method to prevent any inheriting class from changing its meaning. This is done for design reasons when you want to make sure that a method’s behavior is retained during inheritance and cannot be overridden.
The second reason for final methods is efficiency.
final classes
When you say that an entire class is final (by preceding its definition with the final keyword), you state that you don’t want to inherit from this class or allow anyone else to do so. In other words, for some reason the design of your class is such that there is never a need to make any changes, or for safety or security reasons you don’t want subclassing.
- Access Control
The levels of access control from “most access” to “least access” are public, protected, package access (which has no keyword), and private.
Note that a class cannot be private (that would make it inaccessible to anyone but the class) or protected.6 So you have only two choices for class access: package access or public. If you don’t want anyone else to have access to that class, you can make all the constructors private, thereby preventing anyone but you, inside a static member of the class, from creating an object of that class.
- Distinguishing overloaded methods
If the methods have the same name, how can Java know which method you mean? There’s a simple rule: Each overloaded method must take a unique list of argument types.
Even differences in the ordering of arguments are sufficient to distinguish two methods
if you have a data type that is smaller than the argument in the method, that data type is promoted. char produces a slightly different effect, since if it doesn’t find an exact char match, it is promoted to int.
If your argument is wider, then you must perform a narrowing conversion with a cast.
void testDouble() {
double x = 0;
print("double argument:");
f1(x);f2((float)x);f3((long)x);f4((int)x); f5((short)x);f6((byte)x);f7((char)x);
}
The this keyword—which can be used only inside a non-static method—produces the reference to the object that the method has been called for.
The this keyword is used only for those special cases in which you need to
explicitly use the reference to the current object. For example, it’s often used in return statements when you want to return the reference to the current object:
—————
Now consider an unusual case: Suppose your object allocates “special” memory without using new. The garbage collector only knows how to release memory allocated with new, so it won’t know how to release the object’s “special” memory. To handle this case, Java provides a method called finalize( ) that you can define for your class. Here’s how it’s supposed to work. When the garbage collector is ready to release the storage used for your object, it will first call finalize( ), and only on the next garbage-collection pass will it reclaim the object’s memory. So if you choose to use finalize( ), it gives you the ability to perform some important cleanup at the time of garbage collection.
It would seem that finalize( ) is in place because of the possibility that you’ll do something Clike by allocating memory using a mechanism other than the normal one in Java. This can happen primarily through native methods, which are a way to call non-Java code from Java.
Note that System.gc( ) is used to force finalization.
=================
Order of initialization
Within a class, the order of initialization is determined by the order that the variables are defined within the class. The variable definitions may be scattered throughout and in between method definitions, but the variables are initialized before any methods can be called—even the constructor.
The order of initialization is statics first, if they haven’t already been initialized by a previous object creation, and then the non-static objects. You can see the evidence of this in the output. To execute main( ) (a static method), the StaticInitialization class must be loaded, and its static fields table and cupboard are then initialized, which causes those classes to be loaded, and since they both contain static Bowl objects, Bowl is then loaded. Thus, all the classes in this particular program get loaded before main( ) starts. This is usually not the case, because in typical programs you won’t have everything linked together by statics as you do in this example.
In fact, enums are classes and have their own methods.
- Controlling Execution
Java SE5 introduces a new and more succinct for syntax, for use with arrays and containers (you’ll learn more about these in the Arrays and Containers in Depth chapter). This is often called the foreach syntax, and it means that you don’t have to create an int to count through a sequence of items—the foreach produces each item for you, automatically.
for(float x : f) {
A second form of the infinite loop is for(;;). The compiler treats both while(true) and for(;;) in the same way, so whichever one you use is a matter of programming taste.
The switch statement is a clean way to implement multiway selection (i.e., selecting from among a number of different execution paths), but it requires a selector that evaluates to an integral value, such as int or char. If you want to use, for example, a string or a floating point number as a selector, it won’t work in a switch statement. For non-integral types, you must use a series of if statements. At the end of the next chapter, you’ll see that Java SE5’s new enum feature helps ease this restriction, as enums are designed to work nicely with switch.
switch(integral-selector) {
case integral-value1 : statement; break;
case integral-value2 : statement; break;
case integral-value3 : statement; break;
case integral-value4 : statement; break;
case integral-value5 : statement; break;
// ...
default: statement;
}
Notes for <<Thinking In Java>>的更多相关文章
- 转发——推荐一些国外高质量Java开发者的博客
学习Java很不错的一篇博客,总结了很详尽的Java开发者博客. http://www.admin10000.com/document/3373.html 这些博客具有以下特点: 文章的可读性和有独创 ...
- java使用dom4j和XPath解析XML与.net 操作XML小结
最近研究java的dom4j包,使用 dom4j包来操作了xml 文件 包括三个文件:studentInfo.xml(待解析的xml文件), Dom4jReadExmple.java(解析的主要类), ...
- TOJ 2596: Music Notes
2596: Music Notes Time Limit(Common/Java):1000MS/10000MS Memory Limit:65536KByteTotal Submit: 3 ...
- JAVA Native Interface (JNI)
1. Introduction At times, it is necessary to use native (non-Java) codes (e.g., C/C++) to overcome ...
- Java 8 New Features
What's New in JDK 8 https://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html Java Pla ...
- Java三种IO模型和LinuxIO五种IO模型
Java: https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/BIO-NIO-AIO.md https://github.co ...
- Java随谈(六)## 我们真的理解 Java 里的整型吗?
我们真的理解 Java 里的整型吗 整型是我们日常生活中最常用到的基础数据类型,看这篇文章之前,我想问: 我们真的像自己认为的那么理解 Java 内的整型吗? 也许看完本篇文章你就有自己的答案. C ...
- Java SE 16 新增特性
Java SE 16 新增特性 作者:Grey 原文地址:Java SE 16 新增特性 源码 源仓库: Github:java_new_features 镜像仓库: GitCode:java_new ...
- Java SE 19 虚拟线程
Java SE 19 虚拟线程 作者:Grey 原文地址: 博客园:Java SE 19 虚拟线程 CSDN:Java SE 19 虚拟线程 说明 虚拟线程(Virtual Threads)是在Pro ...
随机推荐
- [转]Microsoft VS Code 改变默认文字编码
概要:文件->首选项->设置 输入: "files.autoGuessEncoding": true, 然后勾上. 链接地址:https://jingyan.baidu ...
- Nginx日志挂载目录为nfs文件服务器时开机无法自启动的问题解决
为了方便收集查看日志把nginx日志输出至nfs文件服务器,nfs文件服务器使用autofs自动挂载,nginx和autofs都使用systemctl设置了开机自启动. 但是在重启主机的时候nginx ...
- DES 指定键的大小对于此算法无效
KEY (byte[]) 长度不为8. 一般KEY使用UTF8编码. byte[] byKey = Encoding.UTF8.GetBytes(key); 加密内容的编码,由两方协商. Sys ...
- 未处理的异常:system.io.file load exception:无法加载文件或程序集“ 。。。。 找到的程序集的清单定义与程序集引用不匹配。
问题描述: 添加控制器的时候,突然就报了这个错: Unhandled Exception: System.IO.FileLoadException: Could not load file or as ...
- mysql You can't specify target table for update in FROM clause解决方法
mysql You can't specify target table for update in FROM clause解决方法出现这个错误的原因是不能在同一个sql语句中,先select同一个表 ...
- poj1228(稳定凸包+特判最后一条边)
题目链接:https://vjudge.net/problem/POJ-1228 题意:我是真的没看懂题意QAQ...搜了才知道.题目给了n个点,问这n个点确定的凸包是否能通过添加点来变成一个新的凸包 ...
- 使用Docker-Compose编排发布.Net Core+Redis应用两个镜像到Docker
对于刚刚完成的Alipay支的Demo, 我想要把它部署到Docker中去, 下面我来演示相关步骤. 创建配置文件 配置文件的重中之重是Dockerfile, 他的内容如下: # 第一部分是编译并发布 ...
- 5-24 c++语言之【基础知识】
最近一段时间继续开始了c++的学习,作为c plus plus 难免会与c语言做一个对比,很明显的感受到c++语言注重代码的复用性和拓展性,而c语言更加注重其算法的高效性,这也是今后需要注意的地方,避 ...
- gopacket 在 windows 上面遇到的问题
前阵子有个需求是使用 golang 抓包改包,我用到了 gopacket 这个包,但是出了一些小问题. 我按照网上的方法进行使用 OpenLive 抓包,发现并不行,报错 error open ada ...
- gitlab私有环境搭建
1. 环境准备 安装所需的依赖包 yum install curl openssh-server openssh-clients postfix cronieGitLab使用postfix发送邮件 s ...