// try-with-resources - the best way to close resources! publicstatic String firstLineOfFile(String path)throws IOException { try (BufferedReaderbr=newBufferedReader(newFileReader(path))) { return br.readLine(); } }
// try-with-resources on multiple resources - short and sweet publicstaticvoidcopy(String src, String dst)throws IOException { try (FileInputStreamin=newFileInputStream(src); OutputStreamout=newFileOutputStream(dst)) { byte[] buf = newbyte[BUFFER_SIZE]; int n; while ((n = in.read(buf)) >= 0) out.write(buf, 0, n); } }
Oracle Java SE 21 API, Closeable, documents close() as the operation that closes a stream and releases associated system resources. ↩︎
Joshua Bloch, Effective Java, 3rd Edition, Item 9, provides the practice-level argument for preferring try-with-resources to try-finally when working with closeable resources. ↩︎↩︎↩︎↩︎↩︎
Oracle Java SE 21 API, AutoCloseable, defines the interface used by objects that are automatically closed when leaving a try-with-resources block. ↩︎
Oracle Java SE 21 API, Throwable.getSuppressed(), documents suppressed exceptions, including the mechanism used by try-with-resources. ↩︎
Oracle, The try-with-resources Statement, explains that try-with-resources can be used with catch and finally, and that those blocks run after declared resources are closed. ↩︎
/** * DON'T DO THIS! * Error: Method does not override method from its superclass */ @Override publicbooleanequals(Bigram b) { return first == b.first && second == b.second; }
如果插入这个注解,会发现错误信息。将其改正为:
1 2 3 4 5 6 7
@Override publicbooleanequals(Object o) { if (!(o instanceof Bigram)) returnfalse; Bigramb= (Bigram) o; return first == b.first && second == b.second; }
Oracle Java SE 21 API, java.lang.Override, defines @Override as a method-declaration annotation and specifies the compiler error condition when the annotated method does not override, implement, or match an Object public method. ↩︎
Joshua Bloch, Effective Java, 3rd Edition, Item 40, uses the Bigram example to motivate consistent use of @Override. ↩︎
Java Language Specification SE 21, §8.4.2 Method Signature and §8.4.8.1 Overriding, distinguish methods by name and parameter types and define when an instance method overrides another. ↩︎
Oracle Java SE 21 API, Object.equals(Object), documents that the default implementation uses reference equality, equivalent to == for non-null references. ↩︎
Oracle Java SE 21 API, HashSet, documents a hash-table-backed Set; together with Object.equals(Object) and Object.hashCode(), this supports why distinct Bigram instances remain distinct when equals(Object) is not actually overridden. ↩︎
Oracle Java SE 21 API, java.lang.Override, requires compilers to report an error when an annotated method does not meet one of the permitted override/implementation conditions. ↩︎
Effective Java Item 40 gives the practice rule to use @Override consistently; Error Prone, MissingOverride, shows the same rule encoded as a static-analysis check. ↩︎
// DON'T DO THIS! /** * I'm Son, I have $0 * I'm Son, I have $4 * This guy has $2 */ publicclassFieldHasNoPolymorphic { publicstaticvoidmain(String[] args) { Fatherguy=newSon(); System.out.println("This guy has $" + guy.money); }
staticclassFather { publicintmoney=1;
publicFather() { money = 2; showMeTheMoney(); }
publicvoidshowMeTheMoney() { System.out.println("I'm Father, I have $" + money); } }
staticclassSonextendsFather { publicintmoney=1;
publicSon() { money = 4; showMeTheMoney(); }
publicvoidshowMeTheMoney() { System.out.println("I'm Son, I have $" + money); } } }
输出的两句都是 “I’m Son”,因为 Son 类在创建的时候,首先隐式调用了 Father 的构造函数,而 Father 构造函数中对 showMeTheMoney 的调用是一次虚方法的调用,执行的版本是 Son::showMeTheMoney 方法,所以输出的是 “I’m Son”。虽然父类的 money 已经初始化成 2,但 Son::showMeTheMoney 方法中访问的是子类的 money,这里的结果是 0,因为它要到子类的构造函数执行时才会被初始化。之后子类构造方法执行输出 4,main 的最后一句通过外观类型访问到了父类中的 money,输出 2。
This “static multi-dispatch” description is a teaching model over JLS §15.12.2, because overload resolution considers the compile-time receiver and argument types. ↩︎
This “dynamic single-dispatch” description follows JVMS invokevirtual: after the signature is fixed, run-time lookup is driven by the receiver class. ↩︎
JVMS SE 21, §1.2 The Java Virtual Machine, defines the JVM as an abstract machine; vtables, itables, CHA, guarded inlining, and inline caches are implementation strategies rather than Java language semantics. ↩︎
OpenJDK HotSpot Wiki, VirtualCalls and InterfaceCalls, describes HotSpot-style vtable and itable dispatch mechanics; OpenJDK source such as klassVtable.cpp shows these as implementation structures rather than language-level guarantees. ↩︎
Intel’s Optimization Reference Manual is the vendor reference for cache and memory-access optimization; the common 64-byte cache-line model is a platform assumption, while JLS SE 21 §4.2.1 defines Java int as 32-bit. ↩︎
Intel’s Optimization Reference Manual treats cache locality and memory access as platform performance concerns; in a contiguous int[] model, i++ and i += 2 can touch nearly the same cache-line set even though one loop updates fewer elements. Java object/array layout should be checked with JOL when exact layout matters. ↩︎
Large stride access can reduce touched cache lines, but the measured ratio is not guaranteed: prefetching, JVM layout, JIT compilation, memory bandwidth, and timer noise all matter, so Java microbenchmarks should use JMH or equivalent measurement. ↩︎
publicstaticvoidradixSort(int[] arr) { intmax= arr[0]; int exp; for (int num : arr) { if (num > max) { max = num; } } for (exp = 1; max / exp > 0; exp *= 10) { int[] tmpArr = newint[arr.length]; int[] buckets = newint[10]; // 0 ~ 9 for (int value : arr) { buckets[(value / exp) % 10]++; } for (inti=1; i < 10; i++) { buckets[i] += buckets[i - 1]; } for (inti= arr.length - 1; i >= 0; i--) { tmpArr[buckets[(arr[i] / exp) % 10] - 1] = arr[i]; buckets[(arr[i] / exp) % 10]--; } System.arraycopy(tmpArr, 0, arr, 0, arr.length); } }
Robert Sedgewick and Kevin Wayne, Algorithms, 4th Edition, Bubble.java, describes bubble sort as a stable sort using constant extra memory. ↩︎
Sedgewick and Wayne, Algorithms, 4th Edition, §2.1 Elementary Sorts, covers selection sort as an elementary method that repeatedly selects the next item for the sorted prefix. ↩︎
Sedgewick and Wayne, Algorithms, 4th Edition, §2.1 Elementary Sorts, presents insertion sort as maintaining a sorted prefix and inserting the next element into it. ↩︎
Sedgewick and Wayne, Algorithms, 4th Edition, §2.1 Elementary Sorts, describes Shellsort as a variation of insertion sort over decreasing gap subsequences. ↩︎
Introduction to Algorithms, 4th Edition presents quicksort as partitioning around a pivot and recursively sorting subarrays; its average behavior is good, while bad pivot choices can produce quadratic worst-case time. ↩︎
Introduction to Algorithms, 4th Edition presents merge sort as recursively dividing input and merging sorted subarrays; array merge sort typically uses linear extra space and can be stable. ↩︎
Introduction to Algorithms, 4th Edition presents heapsort as building a heap and repeatedly moving the maximum element to the end; it has O(n log n) worst-case time and constant auxiliary space in the usual array implementation. ↩︎
Introduction to Algorithms, 4th Edition presents radix sort as a non-comparison sort that processes digit positions with a stable intermediate sort; its cost depends on digit count and bucket/radix size. ↩︎
ArrayList<Integer> iList = newArrayList<>(); ArrayList<String> sList = newArrayList<>(); ArrayList list; // Raw use of parameterized class 'ArrayList' list = iList; list = sList;
上述代码,写一个从泛型版本的从 List 到数组的转换方法,由于不能从 List 中取得参数化类型 T,所以不得不从另一个额外参数中再传一个数组的组件类型进去,实属无奈。
通过擦除实现泛型,还丧失了一些面向对象应有的优雅,带来了一些模糊情况。
1 2 3 4 5 6 7 8 9
// 'method(List<String>)' clashes with 'method(List<Integer>)' // both methods have same erasure publicstaticvoidmethod(List<String> list) { System.out.println("invoke method(List<String> list)"); }
实际上这当然不是根据返回值来确定的,能编译和执行成功,是因为两个 method() 方法加入了不同的返回值后才能共存在一个 Class 文件中。方法重载要求方法具备不同的特征签名,返回值并不包含在方法的特征签名中,所以返回值不参与重载选择,但是在 Class 文件格式之中,只要描述符不是完全一致的两个方法,就可以共存。
Microsoft Learn, Generic types and methods - C#, states that C# generic type information is available at runtime; Java Language Specification SE 21, §4.6 Type Erasure, defines Java’s erasure model. ↩︎
JLS SE 21, §4.6 Type Erasure, specifies erasure of parameterized types and type variables; §4.8 Raw Types defines raw types as the erased form used for legacy compatibility. ↩︎
JLS SE 21, §4.7 Reifiable Types, explains why many parameterized types and type variables are not fully available at runtime, which underlies generic array creation limits. ↩︎
Microsoft Learn, Generics in the runtime, describes runtime handling of generic type parameters; Boxing and Unboxing, documents heap allocation and copying when a value type is boxed. ↩︎
JLS SE 21, §4.8 Raw Types, defines the raw form of a parameterized type and the legacy compatibility role it plays. ↩︎
JLS SE 21, §4.6 Type Erasure, and the OpenJDK erasure design note describe erasure plus compiler-inserted casts as part of the migration-compatible generics model. ↩︎
OpenJDK, Project Valhalla, describes value objects, primitive classes, and a Parametric JVM as work aimed at improving Java’s object and generic type model. ↩︎
JLS SE 21, §4.7 Reifiable Types, defines the runtime-availability boundary that makes many concrete parameterized types non-reifiable. ↩︎
Java Virtual Machine Specification SE 21, §4.7.9.1 The Signature Attribute, defines the class-file attribute used to record generic signature information. ↩︎
JLS SE 21, §4.6 Type Erasure, explains why different parameterized signatures can erase to the same type-level shape. ↩︎
JVMS SE 21, §4.7.9.1 The Signature Attribute, together with the OpenJDK erasure note, supports the boundary: generic metadata may be present in class files even though JVM linkage primarily uses erased descriptors. ↩︎
Microsoft Learn, System.ValueType, says value types are either stack-allocated or allocated inline in a structure; Boxing and Unboxing explains when boxing allocates a heap object and copies the value. ↩︎
Joshua Bloch, Effective Java, 3rd Edition, Item 54, gives the rule to return empty collections or arrays instead of null. ↩︎
Effective Java Item 54 frames null collection returns as an API contract cost because every caller must remember and handle the extra path. ↩︎
Effective Java Item 54 directly addresses the claim that returning null avoids allocating empty containers. ↩︎
Effective Java Item 54 ties this performance concern to the broader measurement-first rule in Item 67: optimize only when profiling shows the method is actually a problem. ↩︎
The same boundary is reflected in Oracle’s Optional documentation: Optional<T> represents the possible absence of one non-null value, while collection APIs already have a natural zero-element value. ↩︎
Law of Demeter 来源于 Lieberherr、Holland 和 Riel 的 Object-Oriented Programming: An Objective Sense of Style,核心是限制对象之间的结构穿透,减少调用方对内部对象图的知识。它不要求无意义地层层转发,而是提醒模块不要依赖过深的内部结构:https://dl.acm.org/doi/10.1145/62083.62095。 ↩︎
Joshua Bloch, Effective Java, 3rd Edition, Item 24, gives the design rule that nested classes should serve their enclosing class; Java Language Specification SE 21, §8.1 Class Declarations, defines where class declarations may appear. ↩︎
Oracle Java Tutorials, Nested Classes, distinguishes static nested classes from inner classes; JLS SE 21, §8.1.3 Inner Classes and Enclosing Instances, defines inner classes as nested classes that are not explicitly or implicitly static. ↩︎
Oracle Java Tutorials, Nested Classes, describes static nested classes as members of the enclosing class and notes their access relationship with the enclosing class members. ↩︎
JLS SE 21, §8.5 Member Type Declarations, treats member classes and interfaces as class members; the usual member access-control rules apply to their declarations. ↩︎
Effective Java Item 24 uses public static member classes as helper types whose names and meaning are tied to the enclosing type. ↩︎
Oracle Java Tutorials, Nested Classes, explains the terminology distinction between static nested classes and inner classes. ↩︎
JLS SE 21, §15.8.4 Qualified this, defines qualified this expressions used to refer to lexically enclosing class instances. ↩︎
Effective Java Item 24 draws the practice conclusion: if a member class does not require an enclosing instance, make it static; this follows from the enclosing-instance rules in JLS §8.1.3. ↩︎