可爱的我只是链接的搬运工~
闭包(closure):
一个可调用的对象,纪录了来自于创建它的作用域的信息。a外部有一个变量引用指向a内部的函数b,因为b需要使用a的成员变量(外部只能根据b中所定义的方式来操作a的成员变量),所以GC不会在a执行完毕后马上回收i。
JAVASCRIPT 例子:
function a() {
var i = 0;
function b() {
alert(++i);
}
return b;
}
var c = a();
c();
闭包的意义:
- 保护函数的内部变量
- 在内存中维持一个变量
- 防止方法重名
JAVA通常使用内部类和回调来实现闭包。JAVA内部类不仅可以包含外围类对象的信息,还自动拥有一个指向此外围类对象的引用。在此作用域内,内部类有权操作所有的成员,包括private成员。JAVA 8中引入了新特性lambda表达式。
回调(callback):
- 同步调用
- 回调,被调用方在接口被调用时也调用对方的接口(幽默风趣的回调文章:http://www.importnew.com/19301.html)
- 异步调用,类似消息或事件的机制
package InnerClass;
interface Incrementable {
void increment();
}
class Callee1 implements Incrementable {
private int i = 0;
@Override
public void increment() {
i++;
System.out.println(i);
}
}
class MyIncrement {
public void increment() {
System.out.println("Other operation");
}
static void f(MyIncrement mi) {
mi.increment();
}
}
class Callee2 extends MyIncrement {
private int i = 0;
public void increment() {
super.increment();
i++;
System.out.println(i);
}
private class Closure implements Incrementable {
@Override
public void increment() {
Callee2.this.increment();
}
}
Incrementable getCallbackRef() {
return new Closure();
}
}
class Caller {
private Incrementable ref;
Caller(Incrementable ref) {
this.ref = ref;
}
void go() {
ref.increment();
}
}
public class Test5 {
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.getCallbackRef());
caller1.go();
caller1.go();
caller2.go();
caller2.go();
}
}
内部类:可以将一个类定义在另一个类内部,允许你把一些逻辑相关的类组织起来,像是一种代码隐藏机制,内部类了解外部类并能与之通信。一个内部类被嵌套多少层并不重要,依然能够透明的访问它所有外围类的所有成员。
为什么需要内部类?
内部类提供了某种进入其外围类的窗口。如果只需要一个对接口的引用,应该直接通过外围类实现那个接口。使用内部类时,每个内部类都能独立地继承一个实现,所以无论外围类是否已经继承了接口,对内部类没有任何影响。
package InnerClass;
interface AInterface {
void printSth ();
}
public class Test6 implements AInterface{
@Override
public void printSth() {
System.out.println("out");
}
public class Inner implements AInterface{
@Override
public void printSth() {
System.out.println("in");
}
}
public static void main(String[] args) {
Test6 t6 = new Test6();
Test6.Inner ti = t6.new Inner();
ti.printSth();
t6.printSth();
}
}
定义一个内部类时,这个内部类会有一个隐式引用(implicit reference)指向外部类的实例。子类继承内部类需要保证外部类实例先于内部类实例存在,详细见:http://www.tuicool.com/articles/7jM7Fb。
内部类的覆盖:
package InnerClass;
public class Test7 {
private int p;
public class Inner {
private int p;
}
}
package InnerClass;
import static java.lang.System.out;
class Egg2 {
protected class Yolk {
public Yolk() {
out.println("Egg2.Yolk()");
}
public void f() {
out.println("Egg2.Yolk.f()");
}
}
private Yolk y = new Yolk();
public Egg2() {
out.println("New Egg2()");
}
public void insertYolk(Yolk yy) {
y = yy;
}
public void g() {
y.f();
}
}
class Yolk2 extends Egg2.Yolk {
public Yolk2 (Egg2 eg) {
eg.super();
}
}
public class BigEgg2 extends Egg2 {
public class Yolk extends Egg2.Yolk {
public Yolk() {
out.println("BigEgg2.Yolk()");
}
public void f() {
out.println("BigEgg2.Yolk().f()");
}
}
public BigEgg2() {
insertYolk(new Yolk());
}
public static void main(String[] args) {
Egg2 e2 = new BigEgg2();
e2.g();
}
}
public class BigEgg2 extends Egg2 {
// public class Yolk extends Egg2.Yolk {
// public Yolk() {
// out.println("BigEgg2.Yolk()");
// }
// public void f() {
// out.println("BigEgg2.Yolk().f()");
// }
// }
public BigEgg2() {
insertYolk(new Yolk());
}
public static void main(String[] args) {
Egg2 e2 = new BigEgg2();
e2.g();
}
}内部类的标识:
内部类也会生成一个.class文件,命名规则为“外围类名字$内部类名字”,如果内部类时匿名的,编译器会简单的产生一个数字作为标识符。如果内部类嵌套在别的内部类之中,只需要直接将它们的名字加在其外围类名字与“$”后面。
package InnerClass;
public class Test7 {
public class Inner {
// Test7$Inner$InnerInner.class
public class InnerInner {
}
}
}
- 普通内部类
package InnerClass;
//普通内部类
interface Printer {
void PrintVal();
}
public class Test1 {
private String outVal;
class Inner implements Printer {
private int i;
Inner(String val) {
/*
* 内部类可以访问外部类所有成员,当外部类对象创建一个内部类对象时,内部类对象会捕获一个指向外围类对象的引用,
* 只有内部类对象与外部类对象相关联时,内部类实例下才会被创建,所以内部类不能有static数据
*/
outVal = val;
i = 5;
}
public void PrintVal() {
System.out.println(outVal);
}
//获取外部类引用
public Test1 getOuter() {
//如果只有this的话,其指向内部类实例,而Test1.this引用指向外部类实例
return Test1.this;
}
}
public Inner inner(String val) {
return new Inner(val);
}
public static void main(String[] args) {
Test1 test1 = new Test1();
//下面一行代码是invalid,在拥有外部类对象之前是不可能创建内部类对象
//Test1.Inner ti = new Test1.Inner("...");
Test1.Inner ti1 = test1.inner("...1...");
ti1.PrintVal();
Test1.Inner ti2 = test1.new Inner("...2...");
ti2.PrintVal();
//外部类可以访问内部类的私有变量
System.out.println(ti2.i);
Printer p = test1.inner("...3...");
p.PrintVal();
}
}
- 局部内部类
局部内部类不能有访问说明符,因为它们不是外围类的一部分,但可以访问外围类的成员。
package InnerClass;
//局部内部类
interface Sample {
public void printStr();
}
public class Test2 {
private String strVal = "...";
//局部内部类只能在作用域内被使用
public Sample getSample() {
//不能给Inner设置public,private等修饰符,可以在同一个子目录下的任意类中采用Inner同名,不会影响
class Inner implements Sample {
//private int i = 1;
@Override
public void printStr() {
System.out.println(strVal);
}
}
return new Inner();
}
public static void main(String[] args) {
Test2 t2 = new Test2();
Sample s = t2.getSample();
s.printStr();
}
}
- 匿名内部类
匿名内部类既可以扩展类,也可以实现接口,但是不能两者兼备,而且也只能实现一个接口。
package InnerClass;
//匿名内部类
interface Sample2 {
public void printStr();
}
class Wrapper {
private int i;
public Wrapper(int i) {
this.i = i;
}
public int getI() {
return i;
}
}
public class Test3 {
private String strVal = "...";
//返回实现Sample2的类,向上转型
public Sample2 getSample() {
return new Sample2() {
public void printStr() {
System.out.println(strVal);
}
}; //分号不可少,return表达式
}
//返回实现Wrapper的类,向上转型
public Wrapper getWrapper(int x, Sample2 y) {
return new Wrapper(x) {
private Sample2 s = y;
private int p;
{
//实例初始化
p = 7;78
System.out.println(p);
}
public int getI() {
y.printStr();
s.printStr();
return super.getI();<span style="white-space:pre"> </span>
}
};
}
public static void main(String[] args) {
Test3 t3 = new Test3();
Sample2 s = t3.getSample();
Wrapper w = t3.getWrapper(5, s);
w.getI();
}
}
- 嵌套内部类
package InnerClass;
//如果不需要内部类与外围类有联系,可以将内部类声明为static,称为嵌套类
//不需要外围类对象来创建内部类的对象,不能从嵌套类的对象中访问非静态的外围类对象(外围类.this不可用)
//可以包含static数据
//在接口中所有类都是自动的public和static的
//Main Class: InnerClass.ClassInInterface$Nested
//会生成一个独立的类文件InnerClass.ClassInInterface$Nested.class
public interface ClassInInterface {
void howdy();
class Nested implements ClassInInterface {
public void howdy() {
System.out.println("howdy");
}
public static void main(String[] args) {
new Nested().howdy();
}
}
}
- 工厂方法
package InnerClass;
//工厂方法
interface Service {
void m1();
void m2();
}
interface ServiceFactory {
Service getService();
}
class Impl1 implements Service {
@Override
public void m1() {
System.out.println("m1");
}
@Override
public void m2() {
System.out.println("m2");
}
public static ServiceFactory factory = new ServiceFactory() {
public Service getService() {
return new Impl1();
}
};
}
class Impl2 implements Service {
@Override
public void m1() {
System.out.println("m11");
}
@Override
public void m2() {
System.out.println("m22");
}
public static ServiceFactory factory = new ServiceFactory() {
public Service getService() {
return new Impl2();
}
};
}
public class Test4 {
public static void main(String[] args) {
//不同的工厂对应不同的服务,易于扩展
ServiceFactory sf;
Service s;
sf = Impl1.factory;
//sf= Impl2.factory;
s = sf.getService();
s.m1();
s.m2();
}
}
- 变量捕获
C#变量捕获:
一个函数运行完,对应的栈帧将退出,局部变量所占用的空间将被释放。如果外部函数已执行结束,那内部函数还怎么访问这些“死了的”变量?
C#会在后台构造一个类型,将所有需要被捕获的变量(包括值类型变量和引用类型变量)作为它的公有字段。
http://www.cnblogs.com/instance/archive/2011/05/22/2053541.html
而JAVA只实现了不变量final的捕获(因为就算函数GO DIE了,不变量并不会随着stack frame马上消失,所以局部和匿名内部类只能捕获final值)。
http://www.cnblogs.com/chenjunbiao/archive/2011/01/26/1944417.html
http://www.zhihu.com/question/28190927
http://www.zhihu.com/question/27416568/answer/36565794
http://www.zhihu.com/question/21395848
package InnerClass;
interface InnerInf {
}
public class Test8 {
public InnerInf m(int a) {
class Inner implements InnerInf {
private int b = a;
public void mm() {
// 会出错,因为a是不允许被改变的
// a++;
}
}
// 会出错,因为a是不允许被改变的
// a = 6;
return new Inner();
}
}
package InnerClass;
interface InnerInf {
}
public class Test8 {
public InnerInf m(int a) {
final int b = a + 1;
class Inner implements InnerInf {
public void mm() {
System.out.println(b);
}
}
a = 8;
return new Inner();
}
}
当a是引用类型时,final只是限定对象的引用不变,它的成员变量值可以改变。
- 内部类与控制框架
应用程序框架用以解决某类特定问题的一个类或一组类。要运用某个应用程序框架,通常是继承一个或多个类,并覆盖某些方法。模板是保持不变的事物,而可覆盖的方法就是变化的事物。控制框架是一类特殊的应用程序框架,用来解决响应事件的需求。
Reference:
1. http://blog.youkuaiyun.com/yaerfeng/article/details/7326956
2. http://www.zhihu.com/question/21395848
3. Thinking in JAVA 4th
本文详细解析了闭包的概念及其在JavaScript和Java中的应用,同时深入探讨了Java内部类的各种形式,如普通内部类、局部内部类、匿名内部类、嵌套内部类等,以及它们的特点和应用场景。
172万+

被折叠的 条评论
为什么被折叠?



