欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

全面解读Java编程中的内部类

程序员文章站 2024-03-06 20:07:38
java内部类及其实例化 在 java 中,允许在一个类(或方法、语句块)的内部定义另一个类,称为内部类(inner class),有时也称为嵌套类(nested cla...

java内部类及其实例化
在 java 中,允许在一个类(或方法、语句块)的内部定义另一个类,称为内部类(inner class),有时也称为嵌套类(nested class)。

内部类和外层封装它的类之间存在逻辑上的所属关系,一般只用在定义它的类或语句块之内,实现一些没有通用意义的功能逻辑,在外部引用它时必须给出完整的名称。

使用内部类的主要原因有:
内部类可以访问外部类中的数据,包括私有的数据。
内部类可以对同一个包中的其他类隐藏起来。
当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous)内部类比较便捷。
减少类的命名冲突。

请看下面的例子:

public class outer {
  private int size;
  public class inner {
    private int counter = 10;
    public void dostuff() {
      size++;
    }
  }
  public static void main(string args[]) {
    outer outer = new outer();
    inner inner = outer.new inner();
    inner.dostuff();
    system.out.println(outer.size);
    system.out.println(inner.counter);
    // 编译错误,外部类不能访问内部类的变量
    system.out.println(counter);
  }
}

这段代码定义了一个外部类 outer,它包含了一个内部类 inner。将错误语句注释掉,编译,会生成两个 .class 文件:outer.class 和 outer$inner.class。也就是说,内部类会被编译成独立的字节码文件。

内部类是一种编译器现象,与虚拟机无关。编译器将会把内部类翻译成用 $ 符号分隔外部类名与内部类名的常规类文件,而虚拟机则对此一无所知。

注意:必须先有外部类的对象才能生成内部类的对象,因为内部类需要访问外部类中的成员变量,成员变量必须实例化才有意义。

内部类是 java 1.1 的新增特性,有些程序员认为这是一个值得称赞的进步,但是内部类的语法很复杂,严重破坏了良好的代码结构, 违背了java要比c++更加简单的设计理念。

内部类看似增加了—些优美有趣,实属没必要的特性,这是不是也让java开始走上了许多语言饱受折磨的毁灭性道路呢?本教程并不打算就这个问题给予一个肯定的答案。

java静态内部类、匿名内部类、成员式内部类和局部内部类
内部类可以是静态(static)的,可以使用 public、protected 和 private 访问控制符,而外部类只能使用 public,或者默认。
成员式内部类

在外部类内部直接定义(不在方法内部或代码块内部)的类就是成员式内部类,它可以直接使用外部类的所有变量和方法,即使是 private 的。外部类要想访问内部类的成员变量和方法,则需要通过内部类的对象来获取。

请看下面的代码:

public class outer{
  private int size;
  public class inner {
    public void dostuff() {
      size++;
    }
  }
  public void testtheinner() {
    inner in = new inner();
    in.dostuff();
  }
}

成员式内部类如同外部类的一个普通成员。

成员式内部类可以使用各种修饰符,包括 public、protected、private、static、final 和 abstract,也可以不写。

若有 static 修饰符,就为类级,否则为对象级。类级可以通过外部类直接访问,对象级需要先生成外部的对象后才能访问。

非静态内部类中不能声明任何 static 成员。

内部类可以相互调用,例如:

class a {
  // b、c 间可以互相调用
  class b {}
  class c {}
}


成员式内部类的访问

内部类的对象以成员变量的方式记录其所依赖的外层类对象的引用,因而可以找到该外层类对象并访问其成员。该成员变量是系统自动为非 static 的内部类添加的,名称约定为“outclassname.this”。

1) 使用内部类中定义的非静态变量和方法时,要先创建外部类的对象,再由“outobjectname.new”操作符创建内部类的对象,再调用内部类的方法,如下所示:

public class demo{
  public static void main(string[] args) {
    outer outer = new outer();
    outer.inner inner = outer.new inner();
    inner.dostuff();
  }
}
class outer{
  private int size;
  class inner{
    public void dostuff() {
      size++;
    }
  }
}

2) static 内部类相当于其外部类的 static 成员,它的对象与外部类对象间不存在依赖关系,因此可直接创建。示例如下:

public class demo{
  public static void main(string[] args) {
    outer.inner inner = new outer.inner();
    inner.dostuff();
  }
}
class outer{
  private static int size;
  static class inner {
    public void dostuff() {
      size++;
      system.out.println("size=" + size);
    }
  }
}

运行结果:

size=1

3) 由于内部类可以直接访问其外部类的成分,因此当内部类与其外部类中存在同名属性或方法时,也将导致命名冲突。所以在多层调用时要指明,如下所示:

public class outer{
  private int size;
  public class inner{
    private int size;
    public void dostuff(int size){
      size++; // 局部变量 size;
      this.size; // 内部类的 size
      outer.this.size++; // 外部类的 size
    }
  }
}

局部内部类

局部内部类(local class)是定义在代码块中的类。它们只在定义它们的代码块中是可见的。

局部类有几个重要特性:

  • 仅在定义了它们的代码块中是可见的;
  • 可以使用定义它们的代码块中的任何局部 final 变量;
  • 局部类不可以是 static 的,里边也不能定义 static 成员;
  • 局部类不可以用 public、private、protected 修饰,只能使用缺省的;

局部类可以是 abstract 的。

请看下面的代码:

public class outer {
  public static final int total_number = 5;
  public int id = 123;
  public void func() {
    final int age = 15;
    string str = "http://www.weixueyuan.net";
    class inner {
      public void innertest() {
        system.out.println(total_number);
        system.out.println(id);
        // system.out.println(str);不合法,只能访问本地方法的final变量
        system.out.println(age);
      }
    }
    new inner().innertest();
  }
  public static void main(string[] args) {
    outer outer = new outer();
    outer.func();
  }
}

运行结果:

5
123
15


匿名内部类

匿名内部类是局部内部类的一种特殊形式,也就是没有变量名指向这个类的实例,而且具体的类实现会写在这个内部类里面。

注意:匿名类必须继承一个父类或实现一个接口。

不使用匿名内部类来实现抽象方法:

abstract class person {
  public abstract void eat();
}
class child extends person {
  public void eat() {
    system.out.println("eat something");
  }
}
public class demo {
  public static void main(string[] args) {
    person p = new child();
    p.eat();
  }
}

运行结果:

eat something

可以看到,我们用child继承了person类,然后实现了child的一个实例,将其向上转型为person类的引用。但是,如果此处的child类只使用一次,那么将其编写为独立的一个类岂不是很麻烦?

这个时候就引入了匿名内部类。使用匿名内部类实现:

abstract class person {
  public abstract void eat();
}
public class demo {
  public static void main(string[] args){
    
    // 继承 person 类
    new person() {
      public void eat() {
        system.out.println("eat something");
      }
    }.eat();
  }
}


可以看到,匿名类继承了 person 类并在大括号中实现了抽象类的方法。

内部类的语法比较复杂,实际开发中也较少用到,本教程不打算进行深入讲解,各位读者也不应该将内部类作为学习java的重点。