一:内部类
闭包:闭包就是能够读取其他函数内部变量的函数。例如在javascript中,只有函数内部的子函数才能读取局部变量,所以闭包可以理解成“定义在一个函数内部的函数“。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
1.1 内部类
- 内部类就是将一个类定义在另一个类的内部。
- 在JAVA中,内部类可以访问到外围类的变量、方法或者其它内部类等所有成员,即使它被定义成private了,但是外部类不能访问内部类中的变量。
- 这样通过内部类就可以提供一种代码隐藏和代码组织的机制,并且这些被组织的代码段还可以自由的访问到包含该内部类的外围上下文环境。
public class DemoClass1 {
private int length =0;
//private|public
private class InnerClass implements ILog
{
@Override
public void Write(String message) {
//DemoClass1.this.length = message.length();
length = message.length();
System.out.println("DemoClass1.InnerClass:" + length);
}
}
public ILog logger() {
return new InnerClass();
}
public static void main(String[] args){
DemoClass1 demoClass1 = new DemoClass1();
demoClass1.logger().Write("abc");
//.new
DemoClass1 dc1 = new DemoClass1();
InnerClass ic = dc1.new InnerClass();
ic.Write("abcde");
}
}
该例子的主要功能是实现一个写日志的ILog接口,但是该接口的类被定义在DemoClass1这个外围类中了,而且这个InnerClass内部类还可以访问其外围类中的私有变量length。
1.1 .new
从上面的例子可见,InnerClass是定义在DemoClass1内部的一个内部类,而且InnerClass还可以是Private。
如何创建这个InnerClass的实例? 可以通过外围类的实例进行创建,如:
DemoClass1 dc1 = new DemoClass1();
InnerClass ic = dc1.new InnerClass();
ic.Write("abcde");
1.2 .this
如何通过this显式引用外围类的变量?通过此格式进行引用:{外围类名}.this.{变量名称}。如:
DemoClass1.this.length = message.length();
2. 局部内部类
局部内部类是指在方法的作用域内定义的的内部类。
public class DemoClass2 {
private int length =0;
public ILog logger() {
//在方法体的作用域中定义此局部内部类
class InnerClass implements ILog
{
@Override
public void Write(String message) {
length = message.length();
System.out.println("DemoClass2.InnerClass:" + length);
}
}
return new InnerClass();
}
}
因为InnerClass类是定义在logger()方法体之内,所以InnerClass类在方法的外围是不可见的。
二:匿名内部类
1. 匿名内部类浅析
从重构的角度来看,匿名内部类可以使你少些一些重复代码,案列如下:
首先我们来看一个代码,计算一个方法执行了多少秒! 其实大多数人都会写。像这样:
public static void test() {
long start = System.currentTimeMillis();
//执行打印的业务逻辑
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
long end = System.currentTimeMillis();
System.out.println(end - start);
}
可以将这段代码抽取成一个类,或者一个接口的方法。像这样:
(接口:不知道函数具体实现,程序也可以正常运行,便于协作编程)
public static void test(MyService myService) {
long start = System.currentTimeMillis();
//执行打印的业务逻辑
myService.invoke();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
这段代码抽取成了MyService的invoke()方法:
public interface MyService {
public void invoke();
}
调用:
//使用匿名内部类调用test(MyService myService)
public static void main(String[] args) {
test(new MyService() {
@Override
public void invoke() {
for (int i = 0; i < 10; i++) {
System.out.println(i);
}
}
});
}
2. 定义匿名内部类
首先看下官方文档中给的例子:
public class HelloWorldAnonymousClasses {
/**
* 包含两个方法的HelloWorld接口
*/
interface HelloWorld {
public void greet();
public void greetSomeone(String someone);
}
public void sayHello() {
// 1、局部类EnglishGreeting实现了HelloWorld接口
class EnglishGreeting implements HelloWorld {
String name = "world";
public void greet() {
greetSomeone("world");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hello " + name);
}
}
HelloWorld englishGreeting = new EnglishGreeting();
// 2、匿名类实现HelloWorld接口
HelloWorld frenchGreeting = new HelloWorld() {
String name = "tout le monde";
public void greet() {
greetSomeone("tout le monde");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Salut " + name);
}
};
// 3、匿名类实现HelloWorld接口
HelloWorld spanishGreeting = new HelloWorld() {
String name = "mundo";
public void greet() {
greetSomeone("mundo");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hola, " + name);
}
};
englishGreeting.greet();
frenchGreeting.greetSomeone("Fred");
spanishGreeting.greet();
}
public static void main(String... args) {
HelloWorldAnonymousClasses myApp = new HelloWorldAnonymousClasses();
myApp.sayHello();
}
}
运行结果为:
1 Hello world
2 Salut Fred
3 Hola, mundo
该例中用局部类来初始化变量englishGreeting,用匿名类来初始化变量frenchGreeting和spanishGreeting,两种实现之间有明显的区别:
- 局部类EnglishGreetin继承HelloWorld接口,有自己的类名,定义完成之后需要再用new关键字实例化才可以使用;
- frenchGreeting、spanishGreeting在定义的时候就实例化了,定义完了就可以直接使用;
- 匿名类是一个表达式,因此在定义的最后用分号";"结束。
3.访问作用域内的局部变量、定义和访问匿名内部类成员
匿名内部类与局部类对作用域内的变量拥有相同的的访问权限。
-
匿名内部类可以访问外部内的所有成员;
-
JDK1.8之前匿名内部类不能访问外部类未加final修饰的变量(注意:JDK1.8即使没有用final修饰也可以访问);
-
属性屏蔽,与内嵌类相同,匿名内部类定义的类型(如变量)会屏蔽其作用域范围内的其他同名类型(变量):
public class Hello {
public static void main(String[] args) {
String str="haha";
new Thread() {
@Override
public void run() {
System.out.println(str); //将会报错,不能编过
}
}.start();
}
}
匿名内部类的属性屏蔽:
public class ShadowTest {
public int x = 0;
interface FirstLevel {
void methodInFirstLevel(int x);
}
FirstLevel firstLevel = new FirstLevel() {
public int x = 1;
@Override
public void methodInFirstLevel(int x) {
System.out.println("x = " + x);
System.out.println("this.x = " + this.x);
System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
}
};
public static void main(String... args) {
ShadowTest st = new ShadowTest();
ShadowTest.FirstLevel fl = st.firstLevel;
fl.methodInFirstLevel(23);
}
}
输出结果为:
x = 23
this.x = 1
ShadowTest.this.x = 0
4. 匿名内部类中不能定义静态属性、方法;
public class ShadowTest {
public int x = 0;
interface FirstLevel {
void methodInFirstLevel(int x);
}
FirstLevel firstLevel = new FirstLevel() {
public int x = 1;
public static String str = "Hello World"; // 编译报错
public static void aa() { // 编译报错
}
public static final String finalStr = "Hello World"; // 正常
public void extraMethod() { // 正常
// do something
}
};
}