Java内部类_动力节点Java学院整理

内部类是指在一个外部类的内部再定义一个类。类名不需要和文件夹相同。内部类可以是静态static的,也可用public,default,protected和private修饰。(而外部顶级类即类名和文件名相同的只能使用public和default)。

注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。所以内部类的成员变量/方法名可以和外部类的相同。

1. 成员内部类

成员内部类,就是作为外部类的成员,可以直接使用外部类的所有成员和方法,即使是private的。同时外部类要访问内部类的所有成员变量/方法,则需要通过内部类的对象来获取。

要注意的是,成员内部类不能含有static的变量和方法。因为成员内部类需要先创建了外部类,才能创建它自己的,了解这一点,就可以明白更多事情,在此省略更多的细节了。

在成员内部类要引用外部类对象时,使用outer.this来表示外部类对象;

而需要创建内部类对象,可以使用outer.inner  obj = outerobj.new inner();

public class Outer {
  public static void main(String[] args) {
    Outer outer = new Outer();
    Outer.Inner inner = outer.new Inner();
    inner.print("Outer.new");
    inner = outer.getInner();
    inner.print("Outer.get");
  }
  // 个人推荐使用getxxx()来获取成员内部类,尤其是该内部类的构造函数无参数时
  public Inner getInner() {
    return new Inner();
  }
  public class Inner {
    public void print(String str) {
      System.out.println(str);
    }
  }
} 

2. 局部内部类

局部内部类,是指内部类定义在方法和作用域内。Thinking in Java给了这么两个例子:

定义在方法内:

public class Parcel4 {
  public Destination destination(String s) {
    class PDestination implements Destination {
      private String label;
      private PDestination(String whereTo) {
        label = whereTo;
      }
      public String readLabel() {
        return label;
      }
    }
    return new PDestination(s);
  }
  public static void main(String[] args) {
    Parcel4 p = new Parcel4();
    Destination d = p.destination("Tasmania");
  }
} 

定义在作用域里:

public class Parcel5 {
  private void internalTracking(boolean b) {
    if (b) {
      class TrackingSlip {
        private String id;
        TrackingSlip(String s) {
          id = s;
        }
        String getSlip() {
          return id;
        }
      }
      TrackingSlip ts = new TrackingSlip("slip");
      String s = ts.getSlip();
    }
  }
  public void track() {
    internalTracking(true);
  }
  public static void main(String[] args) {
    Parcel5 p = new Parcel5();
    p.track();
  }
} 

局部内部类也像别的类一样进行编译,但只是作用域不同而已,只在该方法或条件的作用域内才能使用,退出这些作用域后无法引用的。

3. 嵌套内部类

嵌套内部类,就是修饰为static的内部类。声明为static的内部类,不需要内部类对象和外部类对象之间的联系,就是说我们可以直接引用outer.inner,即不需要创建外部类,也不需要创建内部类。
      嵌套类和普通的内部类还有一个区别:普通内部类不能有static数据和static属性,也不能包含嵌套类,但嵌套类可以。而嵌套类不能声明为private,一般声明为public,方便调用。

4. 匿名内部类

有时候我为了免去给内部类命名,便倾向于使用匿名内部类,因为它没有名字。例如:

((Button) findViewById(R.id.start)).setOnClickListener(new Button.OnClickListener() {
  @Override
  public void onClick(View v) {
    new Thread() {
      @Override
      public void run() {
        // TODO Auto-generated method stub
      }
    }.start();
  }
}); 

匿名内部类是不能加访问修饰符的。要注意的是,new 匿名类,这个类是要先定义的,看下面例子:

public class Outer {
  public static void main(String[] args) {
    Outer outer = new Outer();
    Inner inner = outer.getInner("Inner", "gz");
    System.out.println(inner.getName());
  }
  public Inner getInner(final String name, String city) {
    return new Inner() {
      private String nameStr = name;
      public String getName() {
        return nameStr;
      }
    };
  }
}
//注释后,编译时提示类Inner找不到
/* interface Inner {
  String getName();
} */ 

同时在这个例子,留意外部类的方法的形参,当所在的方法的形参需要被内部类里面使用时,该形参必须为final。这里可以看到形参name已经定义为final了,而形参city 没有被使用则不用定义为final。为什么要定义为final呢?在网上找到本人比较如同的解释:

“这是一个编译器设计的问题,如果你了解java的编译原理的话很容易理解。

首先,内部类被编译的时候会生成一个单独的内部类的.class文件,这个文件并不与外部类在同一class文件中。 
当外部类传的参数被内部类调用时,从java程序的角度来看是直接的调用例如:

public void dosome(final String a,final int b){
 class Dosome{public void dosome(){System.out.println(a+b)}};
 Dosome some=new Dosome();
 some.dosome();
} 

从代码来看好像是那个内部类直接调用的a参数和b参数,但是实际上不是,在java编译器编译以后实际的操作代码是

class Outer$Dosome{
 public Dosome(final String a,final int b){
 this.Dosome$a=a;
 this.Dosome$b=b;
}
 public void dosome(){
 System.out.println(this.Dosome$a+this.Dosome$b);
}
}} 

从以上代码看来,内部类并不是直接调用方法传进来的参数,而是内部类将传进来的参数通过自己的构造器备份到了自己的内部,自己内部的方法调用的实际是自己的属性而不是外部类方法的参数。 
这样理解就很容易得出为什么要用final了,因为两者从外表看起来是同一个东西,实际上却不是这样,如果内部类改掉了这些参数的值也不可能影响到原参数,然而这样却失去了参数的一致性,因为从编程人员的角度来看他们是同一个东西,如果编程人员在程序设计的时候在内部类中改掉参数的值,但是外部调用的时候又发现值其实没有被改掉,这就让人非常的难以理解和接受,为了避免这种尴尬的问题存在,所以编译器设计人员把内部类能够使用的参数设定为必须是final来规避这种莫名其妙错误的存在。”

(简单理解就是,拷贝引用,为了避免引用值发生改变,例如被外部类的方法修改等,而导致内部类得到的值不一致,于是用final来让该引用不可改变)

因为匿名内部类,没名字,是用默认的构造函数的,无参数的,那如果需要参数呢?则需要该类有带参数的构造函数:

public class Outer {
  public static void main(String[] args) {
    Outer outer = new Outer();
    Inner inner = outer.getInner("Inner", "gz");
    System.out.println(inner.getName());
  }
  public Inner getInner(final String name, String city) {
    return new Inner(name, city) {
      private String nameStr = name;
      public String getName() {
        return nameStr;
      }
    };
  }
}
abstract class Inner {
  Inner(String name, String city) {
    System.out.println(city);
  }
  abstract String getName();
} 

注意这里的形参city,由于它没有被匿名内部类直接使用,而是被抽象类Inner的构造函数所使用,所以不必定义为final。

而匿名内部类通过实例初始化,可以达到类似构造器的效果:

public class Outer {
  public static void main(String[] args) {
    Outer outer = new Outer();
    Inner inner = outer.getInner("Inner", "gz");
    System.out.println(inner.getName());
    System.out.println(inner.getProvince());
  }
  public Inner getInner(final String name, final String city) {
    return new Inner() {
      private String nameStr = name;
      private String province;
      // 实例初始化
      {
        if (city.equals("gz")) {
          province = "gd";
        }else {
          province = "";
        }
      }
      public String getName() {
        return nameStr;
      }
      public String getProvince() {
        return province;
      }
    };
  }
}
interface Inner {
  String getName();
  String getProvince();
} 

5.内部类的继承

内部类的继承,是指内部类被继承,普通类 extents 内部类。而这时候代码上要有点特别处理,具体看以下例子:

public class InheritInner extends WithInner.Inner {
  // InheritInner() 是不能通过编译的,一定要加上形参
  InheritInner(WithInner wi) {
    wi.super();
  }
  public static void main(String[] args) {
    WithInner wi = new WithInner();
    InheritInner obj = new InheritInner(wi);
  }
}
class WithInner {
  class Inner {
  }
}

可以看到子类的构造函数里面要使用父类的外部类对象.super();而这个对象需要从外面创建并传给形参。

以上所述是小编给大家介绍的Java内部类_动力节点Java学院整理,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Java中的匿名内部类小结

    java内部类分为: 成员内部类.静态嵌套类.方法内部类.匿名内部类 . 在java的世界里,提供了匿名内部类语法,用于帮助大家简化代码,本文简要从接口,抽象类以及常规类以代码的形式描述其常用模式. 1. 接口模式 public interface IWriter { void write(); } public static void main(String[] args) { IWriter writer = new IWriter() { @Override public void wri

  • 讲解Java中如何构造内部类对象以及访问对象

    通过反射构造内部类对象 首先在 javalang 包下写一个包含内部类的类: package javalang; public class Outer { public static class Inner1{} } 注意这个类是 public static,后面我们慢慢把这些修饰符去掉. 要想通过反射来创建 Inner1 对象,首先要获得 Inner1 的 Class 对象.我们在 Outer 中写上 main 方法: public class Outer { public static cl

  • 深入浅析Java中Static Class及静态内部类和非静态内部类的不同

    java中的类可以是static吗?答案是可以.在java中我们可以有静态实例变量.静态方法.静态块.类也可以是静态的. java允许我们在一个类里面定义静态类.比如内部类(nested class).把nested class封闭起来的类叫外部类.在java中,我们不能用static修饰顶级类(top level class).只有内部类可以为static.      静态内部类和非静态内部类之间到底有什么不同呢?下面是两者间主要的不同. (1)内部静态类不需要有指向外部类的引用.但非静态内部

  • 简单谈谈java中匿名内部类构造函数

    先看看下面的代码能不能编译通过: public static void main(String[] args) { List l1 = new ArrayList(); List l2 = new ArrayList(){}; List l3 = new ArrayList(){{}}; System.out.println(l1.getClass() == l2.getClass() ); System.out.println(l2.getClass() == l3.getClass() );

  • Java编程中静态内部类与同步类的写法示例

    java静态内部类 将某个内部类定义为静态类,跟将其他类定义为静态类的方法基本相同,引用规则也基本一致.不过其细节方面仍然有很大的不同.具体来说,主要有如下几个地方要引起各位程序开发人员的注意.     (一)一般情况下,如果一个内部类不是被定义成静态内部类,那么在定义成员变量或者成员方法的时候,是不能够被定义成静态成员变量与静态成员方法的.也就是说,在非静态内部类中不可以声明静态成员.     (二)一般非静态外部类可以随意访问其外部类的成员变量以及方法(包括声明为private的方法),但是

  • Java内部类_动力节点Java学院整理

    内部类是指在一个外部类的内部再定义一个类.类名不需要和文件夹相同.内部类可以是静态static的,也可用public,default,protected和private修饰.(而外部顶级类即类名和文件名相同的只能使用public和default). 注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类.对于一个名为outer的外部类和其内部定义的名为inner的内部类.编译完成后出现outer.class和outer$inner.class两类.所以内部类的成员变量/方法名可以和

  • Java自定义异常_动力节点Java学院整理

    废话不多说了,直接给大家贴代码了,具体代码如下所示: /*下面做了归纳总结,欢迎批评指正*/ /*自定义异常*/ class ChushulingException extends Exception { public ChushulingException(String msg) { super(msg); } } class ChushufuException extends Exception { public ChushufuException(String msg) { super(m

  • Java枚举_动力节点Java学院整理

    enum 的全称为 enumeration, 是 JDK 5  中引入的新特性,存放在 java.lang 包中. 原始的接口定义常量 public interface IConstants { String MON = "Mon"; String TUE = "Tue"; String WED = "Wed"; String THU = "Thu"; String FRI = "Fri"; String

  • 深入理解Java虚拟机_动力节点Java学院整理

    什么是Java虚拟机 Java程序必须在虚拟机上运行.那么虚拟机到底是什么呢?先看网上搜索到的比较靠谱的解释: 虚拟机是一种抽象化的计算机,通过在实际的计算机上仿真模拟各种计算机功能来实现的.Java虚拟机有自己完善的硬体架构,如处理器.堆栈.寄存器等,还具有相应的指令系统.JVM屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行. 这种解释应该算是正确的,但是只描述了虚拟机的外部行为和功能,并没有针对内部原理

  • Java死锁_动力节点Java学院整理

    死锁是两个甚至多个线程被永久阻塞时的一种运行局面,这种局面的生成伴随着至少两个线程和两个或者多个资源.在这里我已写好一个简单的程序,它将会引起死锁方案然后我们就会明白如何分析它. Java死锁范例 ThreadDeadlock.java package com.bjpowernode.threads; public class ThreadDeadlock { public static void main(String[] args) throws InterruptedException {

  • Java正则表达式_动力节点Java学院整理

    正则表达式是什么?用我的理解就是一个表达式.用来匹配,替换,判断字符串,之前业务就出现过判断返回值是否为邮箱.在程序开发中,难免会遇到需要匹配.查找.替换.判断字符串的情况发生,而这些情况有时又比较复杂,如果用纯编码方式解决,往往会浪费程序员的时间及精力.因此,学习及使用正则表达式,便成了解决这一矛盾的主要手段. 正则表达式是一种可以用于模式匹配和替换的规范,一个正则表达式就是由普通的字符(例如字符a到z)以及特殊字符(元字符)组成的文字模式,它 用以描述在查找文字主体时待匹配的一个或多个字符串

  • Java适配器模式_动力节点Java学院整理

    一.概念 适配器模式将一个接口转换成客户希望的另外一个接口.它使得原来由于接口不兼容而不能在一起工作的那些类可以一起工作. 二.UML 三.更加生动的例子 四.实例分析 去年买了一个本本,另外给本本配了罗技G1光电套.坑爹的是,光电套的鼠标是USB接口,键盘是PS2接口,可我的本本却没有PS2接口啊.于是跑到市场,淘了一个转接器. 于是乎,我抽象了这么几个类. 1.PS2Port(PS2接口). 2.USBPort(USB接口). 3.PS2ToUSB(对象适配器),将PS2接口装换成USB接口

  • EL调用Java方法_动力节点Java学院整理

    简单来说,我们在一个类中的某个方法,可以使用EL进行调用,这个能被EL表达式调用的方法称之为EL函数,但是这种方式必须满足以下两点要求: ① 在EL表达式中调用的只能是Java类的静态方法 ② 这个Java类的静态方法需要在我们另外在自定义的TLD文件中描述 满足以上两点才能被EL表达式调用. 语法:  ${prefix:方法名(参数列表) } 例1:使用EL函数来实现HTML语言的过滤转义 在web工程中自定义一个HTMLFilter类,同时定义一个静态的filter方法: package c

  • Java异常继承结构解析_动力节点Java学院整理

    Java异常类层次结构图: 异常的英文单词是exception,字面翻译就是"意外.例外"的意思,也就是非正常情况.事实上,异常本质上是程序上的错误,包括程序逻辑错误和系统错误.比如使用空的引用.数组下标越界.内存溢出错误等,这些都是意外的情况,背离我们程序本身的意图.错误在我们编写程序的过程中会经常发生,包括编译期间和运行期间的错误,在编译期间出现的错误有编译器帮助我们一起修正,然而运行期间的错误便不是编译器力所能及了,并且运行期间的错误往往是难以预料的.假若程序在运行期间出现了错误

  • Nginx简介_动力节点Java学院整理

    1.什么是Nginx Nginx来自俄罗斯的Igor Sysoev在为Rambler Media(http://www.rambler.ru/)工作期间,使用C语言开发了Nginx.Nginx作为Web服务器,一直为俄罗斯著名的门户网站Rambler Media提供着出色.稳定的服务. Igor Sysoev将Nginx的代码开源,并且赋予其最自由的2-clause BSD-like license许可证.由于Nginx使用基于事件驱动的架构能够并发处理百万级别的TCP连接,高度模块化的设计和自

随机推荐