C#和Java对static关键字的理解有很大分歧,主要就是在内部类上。此外,由于Java没有类似委托这种数据结构,内部类还要担当封装方法和响应事件这样的重要责任。
从C#到Java入门指引之一——基本类型和字符串
从C#到Java入门指引之二——类
从C#到Java入门指引之三——内部类
截然不同的内部类
与C#不一样,Java不允许外部类使用static关键字修饰。那么我们来看看static关键字修饰内部类的一个例子(来自《细说Java》)
public class Circle { private static int radius; public static class Center{ // 静态内部类也可声明静态成员 private static Center defaultCenter; static { defaultCenter = new Center(10, 10); // 访问外部类的私有成员 System.out.println(radius); } public int x, y; public Center(int x, int y){ this.x = x; this.y = y; } } } public class Main{ public static void main(String[] args){ // 静态内部类可以不依赖外部来实例的存在而存在 Circle.Center center = new Circle.Center(15, 20); } }
习惯C#的同学只怕又会觉得纳闷,明明这个内部类都static了,为啥还有构造方法?!
这是两种语言对static的不同解读造成的:
在C#中,static修饰内部类,表示这是个不能够实例化的静态类;而在Java中,这表示这个内部类可以不依赖于外部类的实例而实例化。
所以,Java中加了static的内部类相当于C#中普通的内部类。
那么,没加static修饰的内部类,又表示什么呢?自然是依赖于外部类的实例才能实例化的内部类嘛!
public class Circle { private int radius; public class Center{ // 普通内部类也可声明静态成员 //private static Center defaultCenter; // 但是可以含有静态常量 private static final int FINAL_STATIC = 10; public int x, y; private int radius; public Center(int x, int y){ this.x = x; this.y = y; radius = 3; // 访问内部类的成员 this.radius = 3; // this指向内部类 Circle.this.radius = 3; // Circle.this指向外部类 } } } public class Main{ public static void main(String[] args){ // 普通内部类必须依赖外部类的实例 //Circle.Center center = new Circle.Center(15, 20); // 必须先创建外部类的实例 Circle circle = new Circle(); Circle.Center center = circle.new Center(15, 20); } }
看到创建内部类实例的那行代码,是否觉得很别扭?
没错,而且如果又涉及到继承的话,比如继承某个类的内部类,那就更加繁琐了。所以我的建议是,非static的内部类,能不用就不用。
方法内声明的局部类
普通内部类可以在语句块(比如方法)内声明,那就是局部类。
局部类只在当前语句块内可访问。
局部类可以访问语句块中的局部变量(但必须用final修饰),以及外部类中的成员。
更为常用的匿名类
如果局部类只是用一次,就可以声明为匿名类。例子同样来自《细说Java》:
interface Center{} public class Circle { public Center getCenter(final int pointX, final int pointY){ return new Center(){ public int x, y; { x = pointX; y = pointY; } }; } } public class Main{ public static void main(String[] args){ Circle circle = new Circle(); Center center = circle.getCenter(10, 15); } }
匿名类不能声明构造方法,所以只能用初始化块的方式对成员初始化。
匿名类也不是new后面的那个类型:如果new后面是一个类,那么匿名类的类型是这个类的子类;如果new后面是一个接口,那么匿名类的类型是这个接口的一个实现。
比如我们常常会给Activity添加一个Handler,以处理各种消息。每个Activity的Activity各不相同,所以也就没有重用的必要,用匿名类即可:
private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { //... } };
匿名类用于封装方法
许多时候,我们需要把方法当成一个变量处理,传给另一个方法。C/C++可以用函数指针,C#可以用委托。Java没有比较直观的方式,但一般可以这么做:
1.创建接口IFunc,里面放一个方法的原型,比如void func();
2.创建实现了IFunc的类FuncClass,实现其中的func方法;
3.创建类FuncClass的对象funcObject,传递给别的方法。
很麻烦是不,好在有匿名类,可以将创建类和创建对象合成一步。比如,想要把一段代码放到新线程去跑,就得把代码塞到很麻烦是不,好在有匿名类,可以将创建类和创建对象合成一步。
比如,想要把一段代码放到新线程去跑,就需要创建Runnable接口的实现,把代码塞到run方法,传给Thread的构造方法:
Thread thread = new Thread(new Runnable() { @Override public void run() { //... } }); thread.start();
注:实际使用时,只需要直接重写Thread的run方法,这么写其实是绕了弯路。
实现那个方法人们会把需要传递的方法放在一个类里面,然后用这个类创建一个对象,通过传递这个对象来实现传递方法。这时候,匿名类就可以大显身手了。
匿名类与事件响应机制
许多时候,我们需要把方法当成一个变量处理,传给另一个方法。C/C++可以用函数指针,C#可以用委托,但Java没有比较直观的方式。通常,在Java代码中,人们会把需要传递的方法放在一个类里面,然后用这个类创建一个对象,通过传递这个对象来实现传递方法。这时候,匿名类就可以大显身手了。
最简单的例子,响应按钮的点击事件:
mButton.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View view){ //... } }
缺省为静态的内部接口
声明在类内部的接口,即为内部接口。
内部接口缺省就是静态的————这个也容易理解,既然是接口,难道还要依赖于外部类的实例而存在么。