编写java程序时,一般一个类(或者接口)都是放在一个独立的java文件中,并且类名同文件名(如果类是public的,类名必须与文件名一致;非public得,无强制要求)。如果想把多个java类放在一个java文件中,则只能有一个public类。如下面的两个类放在同一个文件中就会报错,无法编译通过。

可以看出,因为TestOne.java文件中已经有一个public类TestOne,这时再加了一个public类TestTwo就报错了。如果将类TestTwo前面的public修饰符去掉就没有问题了。

我们下面介绍内部类的概念和使用,所谓内部类,简单的说,就是一个类定义在另一个类的内部。与上面的两个类在同一个文件中不同(TestOne和TestTwo虽然在一个文件中,但相互没有嵌套,是并列的)。

采用内部类,有时会对代码的可读性带来一点问题,但很多场景下还是很有必要的。尤其在使用一些框架(如java的swing,集合框架中的排序操作)等,使用内部类(尤其是匿名内部类)会带来很多便利;再比如我们在开发Android app时,就会大量的使用内部类,如果不了解内部类的含义和使用规则,几乎无法顺利的进行Android app的开发。

内部类有其特别的地方,其中核心之一是,内部类实例可以访问包含它的外部类的所有成员,包括private成员。另外非常关键的一点是,内部类的使用必须与一个外部类的实例绑定,注意是实例,后面的例子中会说明这点。

内部类也分好几种情况,下面一一来解释。

一、一般内部类

例1:TestTwo就是一个内部类,它可以直接访问外部类中的私有成员a。

public class TestOne {

	private int a = 2;

	public static void main(String[] args) {
new TestOne().call();
} public void call(){
TestTwo two = new TestTwo();
two.show();
} class TestTwo {
public void show(){
System.out.println(a);
}
}
}

 例2:如果需要在外部类的静态方法中使用内部类,必须创建一个外部类的实例,通过该实例来new内部类。不能直接new,否则会报编译错误。

public class TestOne {

	private int a = 2;

	public static void main(String[] args) {
TestOne testOne = new TestOne();
TestTwo testTwo = testOne.new TestTwo();
testTwo.show();
} public void call(){
TestTwo two = new TestTwo();
two.show();
} class TestTwo {
public void show(){
System.out.println(a);
}
}
}

 例3:如何在内部类中引用外部类的实例。有两种方法,一是普通的方法,在new内部类时,将外部类的this作为内部类的构造函数参数传给内部类;还有一种更简单的方法,直接在内部类中通过 "外部类类名.this" 就可以使用外部类的实例。如下面代码:

public class TestOne {

	private int a = 2;

	public static void main(String[] args) {
new TestOne().call();
} public void call(){
new TestTwo(this).show();
} class TestTwo {
private TestOne one; public TestTwo(TestOne one){
this.one = one;
}
public void show(){
System.out.println(a);
System.out.println(one.a);
System.out.println(TestOne.this.a);
}
}
}

上述情况下使用内部类,通常该内部类只是被该外部类使用,这样可以对内部类起到隐藏作用,并且跟外部类绑定的更紧密。

二、方法内的内部类

就是内部类定义在一个方法的内部。这种情况下,只有在该方法内才能使用该内部类;内部类也可以访问外部类的成员;但注意不能访问包含它的方法的局部变量(包括参数),除非方法内的变量(或参数)是final的。 举例:

public class TestOne {
private int a = 2; public static void main(String[] args) {
new TestOne().call();
} public void call(){
final int b=3;
class TestTwo {
private TestOne one; public TestTwo(TestOne one){
this.one = one;
}
public void show(){
System.out.println(a);
System.out.println(one.a);
System.out.println(TestOne.this.a);
System.out.println(b);
}
}
new TestTwo(this).show();
}
}

  需要注意的是,如果是在静态方法中定义内部类,则该内部类只能使用外部类的静态成员。

三、匿名内部类

匿名内部类,其实也分很多情况。最常见的情况是,直接通过父类(比如抽象类)创建子类的实例,或直接通过接口创建对象,该对象可传递作为方法的参数,或者赋值给某个变量。实际上在内部类的使用中,匿名类是用途最广泛的。

因为我们都知道,在java中,一般情况下,不像c/c++一样,我们可以把一个方法作为一个变量进行传递或引用。

我们只能传递和引用java对象。比如我们定义了一个方法,有个参数是接口类型或抽象类,这样不使用匿名内部类,我们每次都需要先定义一个类实现该接口或抽象类,再创建该类的实例传递给该方法。当接口或抽象类中需要实现的方法数量比较少时(比如只有1个),这个先创建类,再使用就显的比较啰嗦了。而且这种场景下,往往创建的类就这一个地方使用。而通过匿名内部类,就可以让编写的代码简洁很多。 实际上,java本身提供的一些api(如多线程、io、集合框架等)就大量的使用了这个特性。 我们来看几个例子:

例1:通过接口创建对象

public class TestThread {
public static void main(String[] args) {
TestThread testThread = new TestThread();
testThread.fun1();
testThread.fun2();
}
public void fun1(){
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("fun1");
}
};
new Thread(runnable).start();
} public void fun2(){
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("fun2");
}
}).start();
}
}

可以看出,方法fun1中直接通过Runnable接口创建了对象,供Thread类使用。fun2更简化了下,直接连变量名也省掉了,对象的创建直接在传入参数时创建。

例2:直接创建父类的子类对象

public class TestChild {
public static void main(String[] args) {
new Thread() {
@Override
public void run() {
System.out.println("thread is run");
}
}.start(); }
}

  可以看出,Thread类有一个run方法,但这个run方法是空的,我们如果要用继承Thread类来实现多线程,必须在子类中重载run方法。上面的代码我们实际上没有显示的定义一个Thread的子类,而是直接通过上述形式创建了一个线程实例,并启动它。

四、静态嵌套类(内部类)

也就是在一个类中定义一个静态类(注意不能在方法中定义)。 与前面介绍的内部类不同。这个嵌套类只是命名空间限制在外部类中。实质上它不需要与外部类的实例绑定,也无法访问外部类的实例成员,只能访问外部类的静态成员。代码如:

public class TestOne {
private int a = 2;
static private int b = 2; public static void main(String[] args) {
TestTwo testTwo = new TestTwo();
} public void call(){
TestTwo testTwo = new TestTwo();
} static class TestTwo {
public void show(){
System.out.println(b);
}
}
} class TestThree{
public static void main(String[] args) {
TestOne.TestTwo testTwo = new TestOne.TestTwo();
} public void call(){
TestOne.TestTwo testTwo = new TestOne.TestTwo();
}
}

可以看出,TestTwo的使用不需要依赖外部类TestOne的实例。

上面介绍了内部类的几种情况,使用的时候稍加注意即可。  

最新文章

  1. PyQt4入门学习笔记(四)
  2. js回车动态添加表格,右键动态删除表格行
  3. Crystal Report 遇到需要登录的问题
  4. -include和sinclude 作用
  5. 膜拜acm大牛 虽然我不会这题,但是AC还是没有问题的~(转自hzwer)
  6. AOP切入点注解报错
  7. Uva 11183 - Teen Girl Squad (最小树形图)
  8. OC最实用的runtime总结,面试、工作你看我就足够了!
  9. java常用包
  10. mybatis获取插入的语句主键(自增主键)
  11. C#利用Emit反射实现AOP,以及平台化框架封装思路
  12. rabbitmq配置镜像模式
  13. php一致性hash算法的应用
  14. 4.1 ORACLE DATAGUARD SWITCHOVER 步骤
  15. 用Github发布静态页面
  16. 深入理解Java虚拟机3-chap4-5-斗之气10段
  17. HIBERNATE与 MYBATIS的对比
  18. git push declined due to email privacy restrictions 解决方法
  19. OpenCV——模板匹配
  20. Git修改已经提交的注释或日志

热门文章

  1. 常见排序算法C++总结
  2. perl5 第七章 控制结构
  3. C语言 HTTP上传文件-利用libcurl库上传文件
  4. 获取中央气象台API 完整城市列表简单方式
  5. HDOJ 1423 Greatest Common Increasing Subsequence(dp)
  6. SPOJ1811最长公共子串问题(后缀自动机)
  7. probing元素
  8. SharePoint2013切换账户身份登录设置
  9. vagrant 使用方法
  10. 深入浅出—JAVA(8)