thinking-in-java(10)内部类

【0】开场白
1)内部类:将一个类的定义放在另一个类的定义内部,这个类就是内部类;
2)内部类优点:匿名内部类的一个优点就是可以将解决问题的代码隔离,聚拢在一点;

【10.1】创建内部类
【荔枝】把类的定义置于外围类的里面
public class Parcel1 {class Contents { // 内部类private int i = 11;public int value() {return i;}}class Destination { // 内部类private String label;Destination(String whereTo) {label = whereTo;}String readLabel() {return label;}}// Using inner classes looks just like// using any other class, within Parcel1://  public void ship(String dest) {Contents c = new Contents(); // Destination d = new Destination(dest);System.out.println(d.readLabel());}public static void main(String[] args) {Parcel1 p = new Parcel1();p.ship("Tasmania");}
} 
/*
Tasmania
*/ 
【荔枝】外部类有一个方法, 该方法返回一个指向内部类的引用, 如下:
public class Parcel2 {class Contents { // 内部类private int i = 11;public int value() { return i; }}class Destination {  // 内部类private String label;Destination(String whereTo) {label = whereTo;}String readLabel() { return label; }}public Destination to(String s) { // 外部类中的方法 返回一个指向 内部类的引用return new Destination(s);}public Contents contents() { // 外部类中的方法 返回一个指向 内部类的引用return new Contents();}public void ship(String dest) {Contents c = contents();Destination d = to(dest);System.out.println(d.readLabel());}public static void main(String[] args) {Parcel2 p = new Parcel2();p.ship("Tasmania");Parcel2 q = new Parcel2();// Defining references to inner classes:Parcel2.Contents c = q.contents(); // 注意它调用内部类的方式 是 Parcel2.ContentsParcel2.Destination d = q.to("Borneo"); // // 注意它调用内部类的方式 是 Parcel2.Destination}
}
/*
Tasmania
*/
【补充】如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象, 那么必须像在main方法中那样,具体指明这个对象的类型: OuterClassName.InnerClassName;

【10.2】链接到外部类
1)内部类拥有访问外围类的所有元素的访问权;

【荔枝】基于内部类实现迭代器设计模式
interface 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 { // 内部类 访问外部类的 items 实例变量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 void reverseSelector() { }}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();}}
} /** Output: 0 1 2 3 4 5 6 7 8 9*/// :~
【补充】当某个外围类的对象创建一个内部类对象时,此内部类对象必定会秘密捕获一个指向那个外围类对象的引用;然后,在你访问此外围类的成员时, 就是用那个引用来选择外围类的成员;

【10.3】使用.this(生成对外部类对象的引用) 与 .new(在 new 表达式中提供对其他外部类对象的引用)
【荔枝】如何使用 .this
public class DotThis {void f() {System.out.println("DotThis.f()");}public class Inner { // 内部类public DotThis outer() {return DotThis.this; // key: 生成对外部类对象的引用.}}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()*/// :~  
【荔枝】如何使用.new
 public class DotNew {public class Inner { }public static void main(String[] args) {DotNew dn = new DotNew();// .new : 在new 表达式中 提供对其他外部类对象的引用, 使用 .new 语法.DotNew.Inner dni = dn.new Inner(); }public void main2() {DotThis dt = new DotThis();DotThis.Inner inner = dt.new Inner();System.out.println(inner);}
} // /:~
【补充】
补充1)在拥有外部类对象之前是不可能创建内部类对象的。这是因为内部类对象会暗暗地连接到创建它的外部类对象上;
补充2)如果创建的是静态内部类, 则静态内部类不需要对外部类对象的引用;

【荔枝】.new 应用于 Parcel
public class Parcel3 {class Contents { // 内部类private int i = 11;public int value() {return i;}}class Destination { // 内部类private String label;Destination(String whereTo) {label = whereTo;}String readLabel() {return label;}}public static void main(String[] args) {Parcel3 p = new Parcel3();// Must use instance of outer class// to create an instance of the inner class:// 必须使用外部类对象实例 创建 内部类实例Parcel3.Contents c = p.new Contents();Parcel3.Destination d = p.new Destination("Tasmania");}
} // /:~ 
【10.4】内部类与向上转型
1)当将内部类向上转型为其基类, 尤其转型为一个接口时,内部类就有了用武之地;

【荔枝】内部类向上转型
// 荔枝:内部类向上转型
class Parcel4 {// private 访问修饰符 的内部类 隐藏子类的实现细节.private class PContents implements Contents {private int i = 11;public int value() { return i; }}protected class PDestination implements Destination {private String label;private PDestination(String whereTo) { label = whereTo; }public String readLabel() { return label; }}public Destination destination(String s) {return new PDestination(s);}public Contents contents() {return new PContents();}
}
public class TestParcel {public static void main(String[] args) {Parcel4 p = new Parcel4();Contents c = p.contents(); // 内部类向上转型.Destination d = p.destination("Tasmania");}
}  
【补充】
补充1)注意private内部类的访问权限;

【10.5】在方法和作用域内的内部类
1)可以在一个方法里面或在任意的作用域内定义内部类;

【荔枝】局部内部类:在方法作用域内创建一个完整的类
// 局部内部类的经典荔枝
public class Parcel5 {public Destination destination(String s) {class PDestination implements Destination { // 局部内部类,在方法中进行定义private String label;private PDestination(String whereTo) {label = whereTo;}public String readLabel() {return label;}}return new PDestination(s); // 在同一个方法中 返回 局部内部类的实例}public static void main(String[] args) {Parcel5 p = new Parcel5();Destination d = p.destination("Tasmania");}
} // 
【荔枝】如何在任意的作用域内嵌入一个内部类
// 荔枝:在任意作用域中嵌入一个内部类.
public class Parcel6 {private void internalTracking(boolean b) {if (b) { class TrackingSlip { // if 条件语句中 定义 局部内部类private String id;TrackingSlip(String s) {id = s;}String getSlip() { return id; }}TrackingSlip ts = new TrackingSlip("slip");String s = ts.getSlip();System.out.println(s);}	// 在 定义 TrackingSlip 的作用域之外 创建 TrackingSlip 实例是不可行的.
//		 TrackingSlip instance = new TrackingSlip("str"); // syntax error.}public void track() {internalTracking(true);}public static void main(String[] args) {Parcel6 p = new Parcel6();p.track();}
}  
【10.6】匿名内部类
1)匿名内部类荔枝:
public interface Contents {int value();
}  
// 荔枝-匿名内部类
public class Parcel7 {public Contents contents() {return new Contents() { // 插入一个类的定义 == 匿名内部类private int i = 11;public int value() {return i;}}; // 匿名内部类需要分号.}public static void main(String[] args) {Parcel7 p = new Parcel7();Contents c = p.contents();}
} 
【补充】
补充1)contents方法将返回值的生成与表示这个返回值的类的定义结合在一起;
补充2)匿名内部类语法说明: 创建一个继承自Contents的匿名类的对象; 通过new表达式返回的引用被自动向上转型为 对 Contents的引用;

【荔枝】上述匿名内部类(Parcel7.java)的语法是以下代码的简化版本,如下:
// 匿名内部类的等同版本(不过本版本要比匿名内部类复杂得多)
public class Parcel7b {// 创建一个继承自 Contents 的 匿名类的对象class MyContents implements Contents {private int i = 11;public int value() {return i;}}// 创建方法返回的引用被自动 向上转型为 对 Contents 的 引用public Contents contents() {return new MyContents();}public static void main(String[] args) {Parcel7b p = new Parcel7b();Contents c = p.contents();}
}  
以上代码使用了默认构造器来生成 Contents对象, 如果构造器是有参数的, 怎么办?

【荔枝】基于有参构造器定义匿名内部类
public class Wrapping {private int i;public Wrapping(int x) { i = x; }public int value() { return i; }
}  
// 荔枝-基于有参构造器 定义匿名内部类
public class Parcel8 {public Wrapping wrapping(int x) {return new Wrapping(x) { // 传递给有参构造器. public int value() {return super.value() * 47; // super.value() 是基类方法返回值}}; // 匿名内部类需要分号}public static void main(String[] args) {Parcel8 p = new Parcel8();Wrapping w = p.wrapping(10);System.out.println(w.value());}
} 
【荔枝】在匿名内部类中定义字段时, 可以对其执行初始化操作
// 荔枝-在匿名内部类中定义字段时, 可以对其执行初始化操作
public class Parcel9 {// 希望 匿名内部类 使用一个 在其外部定义的 对象,其参数引用必须为finalpublic Destination destination(final String dest) { return new Destination() {private String label = dest;public String readLabel() { return label; }}; // 需要分号}public static void main(String[] args) {Parcel9 p = new Parcel9();Destination d = p.destination("Tasmania");System.out.println(d.readLabel());}public static void f1() {}public void f2(){ f1(); }
} 
/*
Tasmania
*/ 
【补充】希望 匿名内部类 使用一个 在其外部定义的 对象,其参数引用必须为final

在匿名内部类中不可能有命名构造器(因为它根本没名字)。但通过实例初始化, 就能够达到为匿名内部类创建一个构造器的效果,就像这样:
【荔枝】通过实例初始化为匿名内部类创建一个构造器
// 荔枝-通过实例初始化为匿名内部类创建一个构造器
abstract class Base {public Base(int i) {  print("Base constructor, i = " + i); }public abstract void f();
}
public class AnonymousConstructor {public static Base getBase(int i) {// 通过实例初始化, 就能够达到为 匿名内部类创建一个构造器的效果.return new Base(i) {{print("Inside instance initializer");}public void f() {print("In anonymous f(), and i = " + i);}};}public static void main(String[] args) {Base base = getBase(47);base.f();}
}  
/*
Base constructor, i = 47
Inside instance initializer
In anonymous f(), and i = 47
*/ 
【补充】在上述荔枝中, 不要求变量i一定是final的。因为 i 被传递给匿名类的基类构造器, 他并不会在匿名内部类的内部被直接使用;

【荔枝】为内部类字段进行赋值,则方法参数必须是 final
// 荔枝-为内部类字段进行赋值, 则方法参数 必须是 final
public class Parcel10 {	//  为内部类字段进行赋值, 则方法参数 必须是 finalpublic Destination destination(final String dest, final float price) {return new Destination() {private int cost;// 对每个对象进行初始化{cost = Math.round(price); // 四舍五入if (cost > 100)System.out.println("Over budget!");}private String label = dest;public String readLabel() {return label;}};}public static void main(String[] args) {Parcel10 p = new Parcel10();Destination d = p.destination("Tasmania", 101.395F);System.out.println(d.readLabel());}
}
/*
Over budget!
Tasmania
*/ 
【补充】匿名内部类与正规的继承相比有些受限:因为匿名内部类既可以扩展类,也可以实现接口,但不能两者兼备。如果实现接口,也只能实现一个接口;

【10.6.1】在访工厂方法
【荔枝】基于匿名内部类的工厂方法
interface Service {  void method1(); void method2(); 
}
interface ServiceFactory { Service getService(); 
}
// 荔枝-通过匿名内部类 实现工厂方法模式(经典荔枝)
class Implementation1 implements Service {private Implementation1() { }public void method1() { print("Implementation1 method1"); }public void method2() { print("Implementation1 method2"); }public static ServiceFactory factory = new ServiceFactory() { // 静态匿名内部类public Service getService() { return new Implementation1(); }};
}
class Implementation2 implements Service {private Implementation2() { }public void method1() { print("Implementation2 method1"); }public void method2() { print("Implementation2 method2"); }public static ServiceFactory factory = new ServiceFactory() { // 静态匿名内部类public Service getService() { return new Implementation2(); }};
}
public class Factories {public static void serviceConsumer(ServiceFactory fact) {Service s = fact.getService();s.method1();s.method2();}public static void main(String[] args) {serviceConsumer(Implementation1.factory);serviceConsumer(Implementation2.factory);}
} /** Output: * Implementation1 method1 * Implementation1 method2 * Implementation2 method1* Implementation2 method2*/// :~  
【荔枝】通过匿名内部类 实现工厂方法模式(经典荔枝)
interface Game { boolean move(); }
interface GameFactory { Game getGame(); }
//通过匿名内部类 实现工厂方法模式(经典荔枝)
class Checkers implements Game {private Checkers() {}private int moves = 0;private static final int MOVES = 3;public boolean move() {print("Checkers move " + moves);return ++moves != MOVES;}public static GameFactory factory = new GameFactory() { // 匿名内部类.public Game getGame() { return new Checkers(); }};
}
class Chess implements Game {private Chess() {}private int moves = 0;private static final int MOVES = 4;public boolean move() {print("Chess move " + moves);return ++moves != MOVES;}public static GameFactory factory = new GameFactory() { // 匿名内部类.public Game getGame() { return new Chess(); }};
}
public class Games {public static void playGame(GameFactory factory) {Game s = factory.getGame();while (s.move()) ;}public static void main(String[] args) {playGame(Checkers.factory);playGame(Chess.factory);}
}  
/*
Checkers move 0
Checkers move 1
Checkers move 2
Chess move 0
Chess move 1
Chess move 2
Chess move 3
*/ 
【10.7】嵌套类(静态内部类)
1)静态内部类意味着:
1.1)要创建匿名内部类的对象,并不需要其外围类对象;
1.2)不能从匿名内部类的对象中访问非静态的外围类对象;
2)静态内部类与普通内部类的区别:普通内部类的字段与方法, 只能放在类的外部层次上, 所以普通内部类不能有static数据和static字段,也不能包含 静态内部类。 但是静态内部类里是可以包含所有这些东西的;

【荔枝】静态内部类可以包含static数据,static字段和方法,也可以包含普通的字段和方法
// 嵌套类(静态内部类)的荔枝
public class Parcel11 {// 静态内部类private static class ParcelContents implements Contents {private int i = 11;public int value() { return i; }}// 静态内部类protected static class ParcelDestination implements Destination {private String label; // 普通变量private ParcelDestination(String whereTo) {label = whereTo;}public String readLabel() { // 普通方法return label;}public static void f() { } // 静态方法static int x = 10; // 静态变量static class AnotherLevel { // 静态内部类( 嵌套类 )public static void f() { } // 静态方法static int x = 10; // 静态变量int y = 10; // 普通变量}class A { // 普通内部类class B {class C { }}}}public static Destination destination(String s) {return new ParcelDestination(s);}public static Contents contents() {return new ParcelContents();}public static void main(String[] args) {Contents c = contents();Destination d = destination("Tasmania");}
}  
【10.7.1】接口内部的类
1)正常情况下, 不能在接口内部放置任何代码,但静态内部类可以作为接口的一部分;
2)放置到接口中的任何类都默认是 public static;

【荔枝】在接口内部定义静态内部类
// 在接口作用域内放置 嵌套类(静态内部类)
// 接口中的类 自动是 public 和 static 的.
interface ClassInInterface{void howdy();class Test implements ClassInInterface { // 默认是 public staticpublic void howdy() {System.out.println("Howdy!");}public static void test() {System.out.println("my name is test.");}public static void main(String[] args) {new Test().howdy(); }}
}
/*
错误: 找不到或无法加载主类 chapter10.ClassInInterfaceTest$Test
*/ 
【补充】在每个类中都写main方法来测试。这样做有一个缺点: 那就是必须带着那些已经编译过的额外代码。如果这对你是个麻烦,那就可以使用匿名内部类来放置测试代码;
//荔枝-使用匿名内部类来放置测试代码
public class TestBed {public void f() {System.out.println("f()");}public static class Tester {public static void main(String[] args) {TestBed t = new TestBed();t.f();}}
} /** Output: f()*/ 
【说明】这生成了一个独立的类 TestBed$Tester(要运行这个程序, 执行 java TestBed$Tester 即可);可以使用这个类来做测试, 但不必再发布的产品中包含它, 在将产品打包前可以简单地删除 TestBed$Tester.class;
【10.7.2】从多层嵌套类中访问外部类的成员
1)一个内部类被嵌套多少层并不重要:它能透明地访问所有它所嵌入的外围类的所有成员;

【荔枝】从多层嵌套类中访问外部类的成员
 // 荔枝-从多层嵌套类中访问外部类的成员 
class MNA {private void f() {}class A {private void g() {}public class B {// 从多层嵌套类中 访问外部类的成员.void h() {g(); // 调用 A.g()f(); // 调用 MNA.f()}}}
}public class MultiNestingAccess {public static void main(String[] args) {MNA mna = new MNA();MNA.A mnaa = mna.new A(); // .new 表达式提供对其他外部类对象的引用.MNA.A.B mnaab = mnaa.new B();mnaab.h();}
} // /:~ 
【10.8】为什么需要内部类?
1)内部类最吸引人的原因: 每个内部类都能独立继承自一个(接口的)实现, 所以无论外围类是否已经继承了某个(接口的)实现, 对于内部类都没有影响;
2)内部类使得多重继承的解决方案变得完整。接口解决了多重继承的部分问题, 内部类有效地实现了 多重继承。也就是说, 内部类允许继承多个非接口类型(译注:类或抽象类);

3)考虑以下情形:必须在一个类中以某种方式实现两个接口。 有两种实现方式:使用单一类, 或者使用内部类;
// 荔枝-实现多重接口的荔枝
// 方式1-使用单一类
class X implements A, B {}// 方式2-使用内部类, 如下:
// 外部类本身实现一个接口;
// 外部类的方法返回一个匿名内部类(匿名内部类就是一个接口类型), 以达到实现两个接口的目的;
class Y implements A {B makeB() {// 匿名内部类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);takesA(y);takesB(x);takesB(y.makeB()); // this line.(bingo)}
}  
4)如果拥有的是抽象类或具体类,而不是接口, 那就只能使用内部类才能实现多重继承;
(干货——使用内部类才能实现多重继承)

【荔枝】使用内部类才能实现多重继承
class D {} // 具体类
abstract class E {} // 抽象类// 荔枝-使用内部类才能实现多重继承
class Z extends D {E makeE() {return new E() {};}
}public class MultiImplementation {static void takesD(D d) {}static void takesE(E e) {}public static void main(String[] args) {Z z = new Z();takesD(z);takesE(z.makeE());}
} 
5)内部类有以下特性:
特性1)内部类可以有多个实例, 每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立;
特性2)在单个外围类中, 可以让多个内部类以不同方式实现同一个接口,或继承同一个类;
特性3)创建内部类对象的时刻并不依赖于外围类对象的创建;
特性4)内部类并没有 is-a 关系, 他就是一个独立实体;

【10.8.1】闭包与回调
1)闭包是一个可调用的对象,他记录了一些信息,这些信息来自于创建它的作用域;
2)内部类是面向对象的闭包: 因为内部类不仅包含外围类对象(创建内部类的作用域)的信息,还自动拥有一个指向指向此外围类对象的引用,在此作用域内,内部类有权操作所有成员,包括private成员;

【荔枝】通过内部类提供闭包功能
// 荔枝-通过内部类提供闭包功能
interface Incrementable {  void increment(); }
// 类本身实现接口
class Callee1 implements Incrementable {private int i = 0;public void increment() { i++; print(i); }
}class MyIncrement {public void increment() { print("Other operation"); }static void f(MyIncrement mi) { mi.increment(); }
}
// 类的内部类实现接口
class Callee2 extends MyIncrement {private int i = 0;@Overridepublic void increment() {super.increment();  i++; print(i);}// 闭包内部类private class Closure implements Incrementable {public void increment() {// 指定外部类方法,否则你将得到一个无限循环.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); // Other operation 1Caller caller1 = new Caller(c1);Caller caller2 = new Caller(c2.getCallbackReference()); // 获得回调引用caller1.go(); // 1 caller1.go(); // 2caller2.go(); // Other operation 2caller2.go(); // Other operation 3}
}  
/*
Other operation
1
1
2
Other operation
2
Other operation
3
*/ 
分析1)内部类Closure 实现了 Incrementable接口,以提供返回 Callee2的钩子;
分析2)Caller的构造器需要一个 Incrementable 的引用作为参数(虽然可以在任意时刻捕获回调引用),然后在以后的某个时刻,Caller对象可以使用此引用回调Callee类;

【10.9】内部类的继承
// 荔枝-内部类的继承
class WithInner {class Inner {}
}public class InheritInner extends WithInner.Inner {// InheritInner() {} // 这个无参构造器 无法编译,Won't compileInheritInner(WithInner wi) {wi.super();}public static void main(String[] args) {WithInner wi = new WithInner();InheritInner ii = new InheritInner(wi);}
} // /:~ 
分析1)InheritInner只继承自内部类,而不是外围类;
分析2)当要生成一个构造器时,默认的构造器并不算好,而且不能只是传递一个指向外围类对象的引用。
分析3)必须在构造器内使用如下语法: enclosingClassReference.super(); 这样才提供了必要的引用, 然后程序才能通过编译;

【10.10】内部类可以被覆盖吗?
1)内部类覆盖:如果创建一个内部类,然后继承其外围类并重新定义此内部类时,会发生什么呢?
2)覆盖内部类并不起什么作用;
// 荔枝-覆盖内部类(不起任何作用)
class Egg {private Yolk y;protected class Yolk {public Yolk() { print("Egg.Yolk()"); } // 2, 而是调用这个 Yolk 构造方法。}public Egg() {print("New Egg()"); // 1y = new Yolk();}
}public class BigEgg extends Egg {// 内部类 BigEgg.Yolk 没有覆盖 内部类 Egg.Yolk public class Yolk {public Yolk() { print("BigEgg.Yolk()"); } // not this one. 并没有调用这个 Yolk 构造方法。}public static void main(String[] args) {new BigEgg();}
}
/*
New Egg()
Egg.Yolk()
*/ 
3)明确地继承某个内部类是奏效的, 如下:
// 荔枝-明确地继承某个内部类是奏效的
class Egg2 {protected class Yolk {public Yolk() { print("Egg2.Yolk()"); } // 1, 3public void f() { print("Egg2.Yolk.f()"); }}private Yolk y = new Yolk();public Egg2() { print("New Egg2()"); } // 2public void insertYolk(Yolk yy) { y = yy; }public void g() { y.f(); }
}public class BigEgg2 extends Egg2 {// 内部类 BigEgg2.Yolk 明确继承继承 另一个外部类的内部类 Egg2.Yolkpublic class Yolk extends Egg2.Yolk {public Yolk() { print("BigEgg2.Yolk()"); } // 4public void f() { print("BigEgg2.Yolk.f()"); } // 5}public BigEgg2() { insertYolk(new Yolk()); }public static void main(String[] args) {Egg2 e2 = new BigEgg2();e2.g();}
}
/*
Egg2.Yolk()
New Egg2()
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk.f()
*/ 
【10.11】局部内部类
1)介绍: 局部内部类不能有访问说明符,因为它不是外围类的一部分;但是他可以访问当前代码块内的常量,以及此外围类的所有成员;
2)对局部内部类与匿名内部类的创建进行了比较,荔枝如下:
// 荔枝-局部内部类
// 荔枝-对局部内部类与匿名内部类的创建进行了比较
interface Counter { int next(); }
public class LocalInnerClass {private int count = 0;Counter getCounter(final String name) {class LocalCounter implements Counter { // 方法域中声明 局部内部类public LocalCounter() { print("LocalCounter()"); }public int next() {printnb(name); return count++; // 共同操作 外部类的字段}}return new LocalCounter();}Counter getCounter2(final String name) {return new Counter() { // 匿名内部类完成 与 局部内部类相同的工作{ print("Counter()"); }public int next() {printnb(name); return count++; // 共同操作 外部类的字段}};}public static void main(String[] args) {LocalInnerClass lic = new LocalInnerClass();Counter c1 = lic.getCounter("Local inner "), c2 = lic.getCounter2("Anonymous inner ");for (int i = 0; i < 5; i++)print(c1.next());for (int i = 0; i < 5; i++)print(c2.next());}
} 
/*
LocalCounter()
Counter()
Local inner 0
Local inner 1
Local inner 2
Local inner 3
Local inner 4
Anonymous inner 5
Anonymous inner 6
Anonymous inner 7
Anonymous inner 8
Anonymous inner 9
*/ 
【补充】为什么有些时候仍然使用局部内部类,不是已经有匿名内部类了吗?
理由1)需要一个已命名的构造器,或者需要重载构造器,而匿名内部类只能用于实例化;
理由2)需要不止一个该内部类的对象;

【10.12】内部类标识符
1)内部类生成一个 .class文件以包含他们的 Class 对象信息;
2)这些类文件的命名有严格的规则: 外围类的名字, 加上 $ , 再加上内部类的名字;
3)荔枝: LocalInnerClass.java 生成的 .class 文件包括:
Counter.class // 接口
LocalInnerClass$1.class // 匿名内部类 
LocalInnerClass$1LocalCounter.class // 局部内部类
LocalInnerClass.class // LocalInnerClass类


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/330732.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

金融系统中正确的金额计算及存储方式

转载自 金融系统中正确的金额计算及存储方式经典的精度丢失问题 Java中的类型float、double用来做计算会有精度丢失问题&#xff0c;下面来看下面的示例。 public static void main(String[] args) {test1();test2(); }private static void test1() {double totalAmount 0.09;…

JSTL标签库

1.JSTL介绍 JSTL(Java Server Pages Standarded Tag Library) : JSP标准标签库。主要提供给开发人员一个标准通用的标签库。 开发人员可以利用这些标签取代JSP页面上的Java 代码&#xff0c;从而提高程- 序的可读性&#xff0c;降低程序的维护难度 组成部分如下&#xff1a; …

缓存雪崩,缓存穿透,缓存预热,缓存热备都是什么鬼?

转载自 缓存雪崩&#xff0c;缓存穿透&#xff0c;缓存预热&#xff0c;缓存热备都是什么鬼&#xff1f;缓存雪崩&#xff0c;缓存穿透&#xff0c;缓存预热&#xff0c;缓存热备是在做缓存设计或者缓存应用时经常遇到的概念&#xff0c;也是缓存应用过程中必须熟知及知道 的东…

Multi-catch parameters are not allowed for source level below 1.7 解决方法

转自&#xff1a; https://stackoverflow.com/questions/21778922/eclipse-false-error-with-jdk7 You can solve this by setting up correct JRE environment in Eclipse as below. Go to Project > Properties > Java Build Path Click on Libraries Select JRE Syste…

使用Eclipse构建Maven项目 (step-by-step)

转自&#xff1a; http://blog.csdn.net/qjyong/article/details/9098213 Maven这个个项目管理和构建自动化工具&#xff0c;越来越多的开发人员使用它来管理项目中的jar包。本文仅对Eclipse中如何安装、配置和使用Maven进行了介绍。完全step by step。 如果觉得本文对你有用&a…

字符串substring方法在jkd6,7,8中的差异

转载自 注意&#xff1a;字符串substring方法在jkd6,7,8中的差异 标题中的substring方法指的是字符串的substring(int beginIndex, int endIndex)方法&#xff0c;这个方法在jdk6,7是有差异的。 substring有什么用&#xff1f; substring返回的是字符串索引位置beginIndex开始&…

过滤器五种拦截行为

1.问题&#xff1a;如何使过滤器拦截转发的请求和响应&#xff1f; Filter 过滤器默认拦截的是客户端发送过来的请求&#xff0c;但是在实际开发中&#xff0c;我们还有请求转发&#xff0c;以及由服务器触发调用的全局错误页面。默认情况下过滤器是不参与过滤的&#xff0c;要…

redis的主从数据库复制功能

【0】开场白&#xff1a; redis提供了复制功能&#xff0c; 实现当一台数据库中的数据更新后&#xff0c; 自动将更新的数据同步到其他数据库上&#xff1b; 这样即使一条server 发生故障&#xff0c;其他服务器仍然可以继续提供服务&#xff1b;&#xff08;为数据生成多个副本…

轻松几步搞定SSH连接Git配置

转载自 轻松几步搞定SSH连接Git配置 如果使用ssh的方式管理&#xff0c;需要配置ssh key. 1、打开git bash命令窗口 2、生成ssh key ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" your_emailexample.com为github上你注册的email地址。 如下面完整创建过程…

redis哨兵

【0】redis的主从数据库实现&#xff0c; 参见&#xff1a; redis的主从数据库复制功能 1&#xff09;redis的主从数据库的作用&#xff1a; 在一主多从的redis系统中&#xff0c; 从数据库起到了 数据冗余备份和读写分离的作用&#xff1b; 2&#xff09;redis2.8提供的哨兵…

Git安装及配置5分钟快速教程

转载自 Git安装及配置5分钟快速教程 Git是什么 Git是一款免费、开源的分布式版本控制系统&#xff0c;可以有效、高速的处理从很小到非常大的项目版本管理。 与常用的版本控制工具CVS、Subversion等不同的是它采用了分布式版本库的方式&#xff0c;不必服务器端软件支持&#x…

MySQL基础---增删改查语法

一、DDL-数据定义语言&#xff0c;操作数据库(CRUD)和表(CRUD) 1 创建数据库(指定字符集) create database 数据库名称 character set utf8; 数据库和表修改都是 Alter 查看都是show 删除都是drop2 创建表 create table 表名称(字段名 数据类型,字段名 数据类型,... ...字…

理解水平扩展和垂直扩展

转自&#xff1a; http://yunjiechao-163-com.iteye.com/blog/2126981 当一个开发人员提升计算机系统负荷时&#xff0c;通常会考虑两种方式垂直扩展和水平扩展。选用哪种策略主要依赖于要解决的问题 以及系统资源的限制。在这篇文章中我们将讲述这两种策略并讨论每种策越的…

JavaWeb核心常用API一览

1.核心知识点 2.常用API

Java中的宏变量,宏替换详解。

转载自 Java中的宏变量&#xff0c;宏替换详解。群友在微信群讨论的一个话题&#xff0c;有点意思&#xff0c;特拿出来分享一下。输出true false来看下面这段程序&#xff0c;和群友分享的大致一样。 public static void main(String[] args) {String hw "hello world&q…

eclipse如何设置js源文件编码

window -> preferences -> 输入 content type -> 然后在右侧栏点击 javascript source file&#xff0c; 然后default encoding 输入 gbk&#xff08;或者其他编码格式&#xff09;&#xff0c; 点击 update &#xff0c; 然后可以看见文件编码已经改变&#xff0c; 最…

SQL编程---存储过程和存储函数

1.基本概念 存储过程和函数是事先经过编译并存储在数据库中的一段 SQL 语句的集合。 2.存储过程和函数的好处 提高代码的复用性。减少数据在数据库和应用服务器之间的传输&#xff0c;提高效率。减少代码层面的业务处理。 3.创建和调用存储过程 <1>创建存储过程 创…

jvm 启动参数设置

-Xms256m -Xmx256m -XX:MaxPermSize64m 如果 jvm 启动失败&#xff0c; 说堆内存不够&#xff0c; 需要调小 初始堆和最大堆大小&#xff0c; 持久代大小&#xff1b; 第一行的参数是调节后的vm参数荔枝 &#xff1b;

类、变量、块、构造器、继承初始化顺序,终极解答

转载自 类、变量、块、构造器、继承初始化顺序&#xff0c;终极解答最近发现微信群里面有些群友在讨论类的初始化顺序&#xff0c;如类的静态变量、成员变量、静态代码块、非静态代码块、构造器&#xff0c;及继承父类时&#xff0c;它们的初始化顺序都是怎样的&#xff0c;下面…

数据表触发器

1.概念 触发器就是在表数据发生变化的时候&#xff0c;自动触发的一些 SQL 操作,查询不影响表中的数据&#xff0c;所以没有触发器。类似于web监听器机制,监听对应表的增删改。 2.触发器分类 触发器类型OLD 触发器之前的效果NEW 触发器之后的效果INSERT 类型的触发器无&…