🎜 Java インターフェースのフィールドは自動的に Final かつ静的であるため、このインターフェースは定数グループを作成するための便利なツールになります。 🎜Java SE5🎜 より前は、このメソッドは列挙効果を生成するために使用されていました。 🎜Java SE5🎜 以降、Java には enum
キーワードがあるため、🎜定数をグループ化するためにインターフェイスを使用することは無意味です🎜。 🎜🎜🎜🎜🎜第 10 章 内部クラス 🎜🎜10.1 外部クラスへのリンク (Java の非静的な通常の内部クラスは、外部クラスのすべてのメンバーに自動的にアクセスできます)。 🎜🎜 Java の通常の内部クラスは、特別な条件なしで、🎜囲むオブジェクト🎜 (囲みオブジェクト) のすべてのメンバーにアクセスできます。 C++ のネストされたクラスの設計は単なる名前隠蔽メカニズムであり、周囲のオブジェクトへの接続や暗黙的なアクセス権はありません。 Java では、クラスが内部クラス オブジェクトを作成するとき、内部クラス オブジェクトはその外部クラスのオブジェクトへの参照を秘密裏にキャプチャする必要があります。次に、この外側のクラスのメンバーにアクセスするときに、その参照を使用して外側のクラスのメンバーを選択します。これらの詳細はコンパイラによって処理されます。 Java のイテレータはこの機能を再利用します。 🎜// Sequence.javainterface Selector{ boolean end();
Object current(); void next();
}public class Sequence{
private Object[] items; private int next = 0; public Sequence(int size){ items = new Object[size]; } public void add(Object x){ if(next != items.length) items[next++] = x;
} private class SequenceSelector implements Selector{
private int i = 0; public boolean end(){ return i == items.length; } public Object current(){ return items[i]; } public void next(){ if (i < items.length) i++; }
} public Selector selector(){ return new SequenceSelector(); } public static void main(String[] args){
Sequence sequence = new Sequence(10); for(int i = 0; i < 10; i++){
sequence.add(Integer.toString(i));
}
Selector selector = sequence.selector(); while(!selector.end()){
System.out.print(selector.current() + " ");
selector.next();
}
System.out.println();
}
}/**Output:
* 0 1 2 3 4 5 6 7 8 9
*/
10.2 .this
与 .new
// DotThis.java// Qualifying access to the outer-class object.public class DotThis{ void f(){ System.out.println("DotThis.f()"); } public class Inner{ public DotThis outer(){ return DotThis.this; // a plain "this" would be Inner's "this"
}
} public Inner inner(){ return new Inner(); } public static void main(String[] args){
DotThis dt = new DotThis();
DotThis.Inner dti = dt.inner();
dti.outer().f();
}
}/*Output:
* DotThis.f()
*/
10.3 匿名内部类
Anonymous Inner Class.
10.4 Java嵌套类
内部类声明为static
时,不再包含外围对象的引用.this
,称为嵌套类(与C++嵌套类大致相似,只不过在C++中那些类不能访问私有成员,而在Java中可以访问)。
- 创建嵌套类,不需要外围对象。
- 不能从嵌套类的对象中访问非静态的外围对象。
10.4.1 接口内部的类
嵌套类可以作为接口的一部分(正常情况下,接口内部不能放置任何代码)。放到接口中的任何类都自动是public和static的。因为类是static的,只是将嵌套类置于接口的命名空间内,这并不违反接口的规则
10.4.2 从多层嵌套类中访问外部类的成员
一个内部类被嵌套多少层并不重要——它能透明地访问它所嵌入的外围类的所有成员,如下:
// MultiNestingAccess.javaclass MNA{ private void f(){}
class A{ private void g(){} public class B{ void h(){
g();
f();
}
}
}
}public class MultiNestingAccess{ public static void main(String[] args){
MNA mna = new MNA();
MNA.A mnaa = mna.new A();
MNA.A.B mnaab = mnaa.new B();
mnaab.h();
}
}
10.5 为什么需要内部类
内部类继承自某个类或实现某个接口,内部类的代码操作创建它的外围类的对象。所以可以认为内部类提供了某种进入其外围类的窗口。
内部类实现一个接口与外围类实现这个接口有什么区别呢?答案是:后者不是总能享用到接口带来的方便,有时需要用到接口的实现。所以,使用内部类最吸引人的原因是:
每个内部类才能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。也就是说,内部类使得Java实现继承多个非接口类型(类或抽象类)。
// MultiInterfaces.java// two ways tha a clas can implement multiple interface.interface A{}interface B{}class X implements A, B {}class Y implements A {
B makeB(){ // Amonymous inner class
return new B() {};
}
}public class MultiInterfaces{
static void takesA(A a){} static void takesB(B b){} public static void main(String[] args){
X x = new X();
Y y = new Y();
takesA(x);
takesB(x);
takesA(y);
takesB(y.makeB());
}
}
10.6 闭包与回调
闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。通过这个定义可以看出内部类是面向对象的闭包,因为它不仅包含外围类对象(创建内部类的作用域)的信息,还自动拥有一个指向此外围类对象的引用(.this
),在此作用域内,内部类有权操作所有的成员,包括private成员。
回调(callback),通过回调,对象能够携带一些信息,这些信息允许它在稍后的某个时刻调用初始的对象。Java中没有指针,通过内部类提供的闭包功能可以实现回调。
// Callbacks.java// using inner classes for callbacksinterface Incrementable{
void increment();
}// Very simple to just implement the interface:class Callee1 implements Incrementable{
private int i = 0; public void increment(){
System.out.println(++i);
}
}class MyIncrement{
public void increment(){ System.out.println("Other operation"); } static void f(MyIncrement mi) { mi.increment(); }
}// If your class must implement increment() in some other way, you must use an inner class:class Callee2 extends MyIncrement{
private int i = 0; public void increment(){ super.increment();
System.out.println(++i);
} private class Closure implements Incrementable{
public void increment(){ // Specify outer-class method, otherwise you'd get an infinite recursion:
Callee2.this.increment();
}
}
Incrementable getCallbackReference(){ return new Closure();
}
}class Caller{
private Incrementable callbackReference;
Caller(Incrementable cbh){ callbackReference = cbh; } void go(){ callbackReference.increment(); }
}public class Callbacks {
public static void main(String[] args){
Callee1 c1 = new Callee1();
Callee2 c2 = new Callee2();
MyIncrement.f(c2);
Caller caller1 = new Caller(c1);
Caller caller2 = new Caller(c2.getCallbackReference());
caller1.go();
caller1.go();
caller2.go();
caller2.go();
}
}/**Uoutput:
* Other operation
* 1
* 1
* 2
* Other operation
* 2
* Other operation
* 3
**/
10.7 Java接口和内部类总结
Java的接口和内部类比其他面向对象的概念更深奥复杂,C++没有这些,将两者结合起来,同样能够解决C++中的用多重继承所能解决的问题。
第11章 持有对象
11.1 迭代器(Iterator)
11.2 ArrayList 和 LinkedList
11.3 Set 不保存重复元素
11.4 Map 将对象映射到其他对象的能力是一种解决编程问题的杀手锏
11.5 Collection 和 Iterator
在Java中,Collection
是描述所有序列容器的共性的根接口,它可能会被 认为是一个“附属接口”,即因为要表示其他若干个接口的共性而出现的接口。而在标准C++类库中并没有其容器的任何公共基类——容器之间的所有共性都是通过迭代器达成的。Java将两种方法绑定到了一起,因为实现Collection
就意味着需要提供iterator()
方法。
11.5 Foreach与迭代器
foreach
语法用于任何实现了Iterable
接口的类。Collection
接口扩展了Iterable
接口,所以所有Collection
对象都适用foreach
语法。
11.6 容器的元素类型
泛型之前的容器不能持有基本类型元素,显然数组是可以的。但是有了泛型,容器就可以指定并检查它们所持有对象的类型,并且有了自动包装机制,容器看起来还能够持有基本类型。
在Java中,任何基本类型都不能作为类型参数。因此不能创建ArrayList<int></int>
或 HashMap<int int></int>
之类的东西。但是可以利用自动包装机制和基本类型的包装器来解决,自动包装机制将自动地实现int
到 Integer
的双向转换:
// ListOfInt.javaimport java.util.*;public class ListOfInt{ public static void main(String[] args){ // 编译错误:意外的类型
// List<int> li = new ArrayList<int>();
// Map<int, Interger> m = new HashMap<int, Integer>();
List<Integer> li = new ArrayList<Integer>(); for(int i = 0; i < 5; i++){
li.add(i); // int --> Integer
} for(int i : li){ // Integer --> int
System.out.print(i + " ");
}
}
}/* Output:
0 1 2 3 4
*/
第12章 通过异常处理错误
12.1 异常
异常允许我们(如果没有其他手段)强制程序停止运行,并告诉我们出现了什么问题,或者(理想状态下)强制程序处理问题,并返回到稳定状态。
12.2 终止与恢复
异常处理理论上有两种基本模型。长久以来,尽管程序员们使用的操作系统支持恢复模型的异常处理,但他们最终还是转向使用类似“终止模型”的代码,并且忽略恢复行为。
12.3 创建自定义异常
所有标准异常都有两个构造器:一个是默认构造器;另一个是接受字符串作为参数,以便能把相关信息放入异常对象的构造器。
// FullConstructors.javaclass MyException extends Exception{ public MyException(){} public MyException(String msg){ super(msg); }
}public class FullConstructors{ public static void f() throws MyException{
System.out.println("Throwing MyException form f()"); throw new MyException();
} public static void g() throws MyException{
System.out.println("Throwing MyException form g()"); throw new MyException("Originated in g()");
} public static void main(String[] args){ try{
f();
}catch(MyException e){
e.printStackTrace(System.out);
} try{
g();
}catch(MyException e){
e.printStackTrace(System.out);
}
}
}/*Output:
Throwing MyException form f()
MyException
at FullConstructors.f(FullConstructors.java:11)
at FullConstructors.main(FullConstructors.java:19)
Throwing MyException form g()
MyException: Originated in g()
at FullConstructors.g(FullConstructors.java:15)
at FullConstructors.main(FullConstructors.java:24)
*/
12.4 printStackTrace()
Throwable
类声明了printStackTrace()
方法,它将打印“从方法调用处直到异常抛出处”的方法调用序列。printStackTrace()
方法所提供的信息可以通过getStackTrace()
方法来直接访问,这个方法将返回一个由栈轨迹中的元素所构成的数组,其中每一个元素都表示栈中的一桢。元素0是栈顶元素,并且是调用序列中的最后一个方法调用(这个Throwable
被创建和抛出之处)。数组中的最后一个元素和栈底是调用序列中的第一个方法调用。如下:
// WhoCalled.java// Programmatic access to stack trace informationpublic class WhoCalled{ static void f(){ // Generate an exception to fill in the stack trace
try{ throw new Exception();
}catch(Exception e){ for(StackTraceElement ste : e.getStackTrace()){
System.out.println(ste.getMethodName());
}
}
} static void g(){ f(); } static void h(){ g(); } public static void main(String[] args){
f();
System.out.println("-------------------------------");
g();
System.out.println("-------------------------------");
h();
}
}/*Output:
f
main
-------------------------------
f
g
main
-------------------------------
f
g
h
main
*/
12.5 为异常先占个位子
可以声明方法将抛出异常,实际上却不抛出。这样做的好处是,为异常先占个位子,以后就可以抛出这种异常而不用修改已有的代码。
在编译时被强制检查的异常称为被检查的异常。
12.6 异常链
常常会想要在捕获一个异常后抛出另一个异常,并且希望把原始异常的信息保存下来,这被称为异常链。JDK1.4以后,所有Throwable的子类在构造器中都可以接受一个cause
对象作为参数。这个cause
就用来表示原始异常,这样通过把原始异常传递给新的异常,使得即使在当前位置创建并了新的异常,也能通过这个异常链追踪到异常最初发生的位置。
在Throwable
的子类中,只有Error
(用于Java虚拟机报告系统错误)、Exception
以及RuntimeException
三种基本的异常提供了带cause
参数的构造器。
12.7 Java标准异常
只能在代码中忽略RuntimeException
(及其子类)类型的异常,其他类型异常的处理都是由编译器强制实施的。究其原因,RuntimeException
代表的是编程错误。
12.8 缺憾:异常丢失
用某些特殊的方式使用finally
子句,可能会丢失异常,一种简单的丢失异常的方式是从finally
子句中返回。
12.9 finally子句
在异常没有被当前的异常处理程序捕获的情况下,异常处理机制也会在跳到更高一层的异常处理程序之前,执行finanlly
子句。
当涉及break
和continue
语句的时候,finally
子句也会得到执行。
finally
子句会在执行return
语句前执行,即它总是会执行,所以在一个方法中, 可以从多个点返回,并且可以保证重要的清理工作仍旧会执行。
12.10 异常的限制
当要覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常。这个限制很有用,因为这意味着,当基类使用的代码应用到其派生类对象的时候,一样能够工作。
12.11 构造器
如果在构造器内抛出了异常,清理行为也许就不能正常工作了。
12.12 异常匹配
抛出异常的时候,异常处理系统会按照代码的书写顺序抛出“最近”的处理程序。找到匹配的处理程序之后,它就认为异常将得到处理,然后就不再继续查找。
12.13 总结
“报告”功能是异常的精髓所在。Java坚定地强调将所有的错误都以异常形式报告的这一事实,正是它远远超过诸如C++这类语言的长处之一,因为在C++这类语言中,需要以大量不同的方式来报告错误,或者根本就没有提供错误报告功能。
第13章 字符串
13.1 不可变字符串
String对象是不可变的。String类中每个看起来会修改String值的方法,实际上都是创建了一个全新的String对象,以包含修改后的字符串内容。而最初的String对象则丝毫未动。
13.2 重载“+”与 StringBuilder
用于String的“+”与“+=”是Java中仅有的两个重载过的运算符,Java不允许程序员重载任何运算符(但其实Java语言比C++更容易实现运算符的重载)。
String的不可变性带来了一定的效率问题,比如String的“+”运算,每“+”一次都会生成一个新的String对象。Java编译器一般会自动优化,但不同情况下,优化的程度不够。
以下类运行javap -c Concatenation.class
反编译后,可见编译器自动引入了java.lang.StringBuilder
类,帮助了优化。
// Concatenation.javapublic class Concatenation{
public static void main(String[] args){
String mango = "mango";
String s = "abc" + mango + "def" + 47;
System.out.println(s);
}
}/**Output:
* abcmangodef47
**/
Compiled from "Concatenation.java"public class Concatenation { public Concatenation();
Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code: 0: ldc #2 // String mango
2: astore_1 3: new #3 // class java/lang/StringBuilder
6: dup 7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
10: ldc #5 // String abc
12: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: aload_1 16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
19: ldc #7 // String def
21: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: bipush 47
26: invokevirtual #8 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
29: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
32: astore_2 33: getstatic #10 // Field java/lang/System.out:Ljava/io/PrintStream;
36: aload_2 37: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
40: return}
// WhitherStringBuilder.javapublic class WhitherStringBuilder{ public String implicit(String[] fields){
String result = ""; for(int i = 0; i < fields.length; i++){
result += fields[i];
} return result;
} public String explicit(String[] fields){
StringBuilder result = new StringBuilder(); for(int i = 0; i < fields.length; i++){
result.append(fields[i]);
} return result.toString();
}
}
Compiled from "WhitherStringBuilder.java"public class WhitherStringBuilder { public WhitherStringBuilder();
Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public java.lang.String implicit(java.lang.String[]);
Code: 0: ldc #2 // String
2: astore_2 3: iconst_0 4: istore_3 5: iload_3 6: aload_1 7: arraylength 8: if_icmpge 38
11: new #3 // class java/lang/StringBuilder
14: dup 15: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
18: aload_2 19: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: aload_1 23: iload_3 24: aaload 25: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
31: astore_2 32: iinc 3, 1
35: goto 5
38: aload_2 39: areturn public java.lang.String explicit(java.lang.String[]);
Code: 0: new #3 // class java/lang/StringBuilder
3: dup 4: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
7: astore_2 8: iconst_0 9: istore_3 10: iload_3 11: aload_1 12: arraylength 13: if_icmpge 30
16: aload_2 17: aload_1 18: iload_3 19: aaload 20: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
23: pop 24: iinc 3, 1
27: goto 10
30: aload_2 31: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
34: areturn
}
13.3 无意识的递归
由String对象后面跟着一个“+”,再后面的对象不是String时,编译器会使后面的对象通过toString()
自动类型转换成String
,如果这发生在自定义的类的重写的toString()
方法体内,就有可能发生无限递归,运行时抛出java.lang.StackOverflowError
栈溢出异常。
// InfiniteRecursion.javapublic class InfiniteRecursion{ public String toString(){ //应该调用Object.toString()方法,所以此处应为super.toString()。
return " InfiniteRecursion address: " + this + "\n";
} public static void main(String[] args){
List<InfiniteRecursion> v = new ArrayList<InfiniteRecursion>(); for(int i = 0; i < 10; i++)
v.add(new InfiniteRecursion());
System.out.println(v);
}
}
文末:
这些章节内容算是Java的基础,整理出来作为第一部分,算是温故知新吧。
相关文章:
Java编程思想学习课时(二)第14章-类型信息
Javaプログラミング思考学習クラス(3) 第15章 - ジェネリック