Thinking in Java---内部类及一个迭代器实例

可以将一个类的定义放在另一个类的定义内部,这就是内部类.

内部类的定义是简单的,但是它的语法确实很是复杂,让人不是很好理解.下面就内部类做一个小结.

一.内部类的分类

总的来讲内部类分为普通内部类,匿名内部类,局部内部类,嵌套类(静态内部类)等.下面简要的介绍以下这些内部类定义的语法.

(1).对于普通的内部类,就是在外围类中插入另一个类的定义.如下面的代码:

package lkl1;

///封装一个包裹类
public class Parcel {

    ///在一个类的内部定义的另一个类,称之为内部类
    ///它的定义形式并没有什么不同
    public class Destination{
        private String label;
        Destination(String whereto){
            label=whereto;
        }
        public String getLabel(){
            return label;
        }
    }

    public class Contents{
        private int i=11;
        public int value(){
            return i;
        }
    }
    ///提供一个统一的创建内部类的接口
    public Destination destination(String s){
        return new Destination(s);
    }
    public Contents contents(){
        return new Contents();
    }

    public void ship(String dest){
        //其实也可以直接调用构造器定义
        //Contents c1 = new Contents();
        Contents c = contents();
        Destination d = destination(dest);
        System.out.println(d.label);
    }
    public static void main(String[] args){
        Parcel p = new Parcel();
        p.ship("changsha");

        Parcel p1= new Parcel();

        ///为内部类定义引用,注意名字的层次
        ///在非静态方法中定义内部类的对像需要具体指明这个对象的类型
        ///OuterClassName.InnerClassName
        Parcel.Contents  c =p1.contents();
        System.out.println(c.i);
        Parcel.Destination d = p1.destination("wuhan");
    }
}

(2).所谓的匿名内部类的定义语法比较奇特.匿名内部类是没有名字的,所以我们不能想一般的类那样调用构造器得到它的对象,一般我们都将它放在一个方法中,这个方法负责返回这个匿名内部类的一个对象.因为匿名内部类没有名字,所以也就不能通过构造器来实现初始化了,我们可以通过初始化块的形式达到构造器的效果(当然我们是可以调用基类的构造器来初始化基类的成员变量).如下面的代码:

package lkl1;

public class Parcel3 {
    ///用于匿名内部类变量初始化的形参必须要用final修饰
    //Destination是前面定义的一个类.
    public Destination destination(String dest,final double price,int i){
        ///创建匿名内部类的一般格式
        return new Destination(i){  ////可以通过调用基类的构造器进行基类的初始化
            private int cost;
            {///用初始化块达到类似于构造器的初始化过程
                cost =(int) Math.round(price);
                if(cost>100){
                    System.out.println("Over budget!");
                }
            }
            private String label=dest;
            public String readLabel(){
                return label;
            }
        };
    }
    public static void main(String[] args){
        Parcel3  p3= new Parcel3();
        Destination d = p3.destination("changsha", 109.234,10);
        System.out.println(d.readLabel());
    }
}

(3)所谓的局部内部类其实就是在方法和作用域内定义的内部类.这种内部类只在一定的范围是有效的(其实上面的内部类就是一种局部内部类).一般我们是将其当成工具使用的,不希望它是公共可见的.如下面的代码:

package lkl1;

public class Test {

          private PrintId create(){
          ///外部类中的一个方法中定义内部类用于实现某个接口;
          ///然后返回这个内部类的一个引用
            {
                  ///PrintId是前面的定义的一个接口
                  class Print implements PrintId{
                   private String id;
                   public Print(){
                       id="longkaili";
                   }
                   public void print(){
                       System.out.println(id);
                   }
               }
               return new Print();
          }
            //试图在作用域外访问内部类出错
            //(new Print()).id;
      }
      public static void main(String[] args){
          ///以下说明了虽然内部类是定义在一个作用域类的
          ///但是在外面还是可以使用它实现的功能的
          Test ts = new Test();
          ts.create().print();  ///在本类方法中创建本类的对象,其private接口是可见的
      }
}

(4).嵌套类其实就是就将内部类声明成static.对于普通的内部类其必须要依赖于一个外部类的对象,而嵌套类是不需要的,我们可以将其看成一个static型的变量.如下面的代码:

package lkl1;

///当内部类被声明成静态时,我们就将其当成一个静态成员来看待
///此时内部类不在和外部类的对象绑定在一起.
public class Parcel1 {

    private static  class Parcel1Contents extends  Contents{
        private int i=1;
        public int value(){
            return i;
        }
    }
    protected static class Parcel1Destination extends Destination{
        private String label;
        private Parcel1Destination(String whereTo){
            label=whereTo;
        }
        public String readLabel(){
            return label;
        }
    }
    //我们可以通过外部类的静态方法创建嵌套类的对象
    public  static Destination destination(String s){
        return new Parcel1Destination(s);
    }
    public static Contents contents(){
        return new Parcel1Contents();
    }
    public static void main(String[] args){
        Contents c= contents();
        Destination d =destination("changsha");
    }
}

二.外部类和内部类的联系.

既然内部类定义在了外部类的内部,那么肯定就具有一定的联系的.具体的我们分成非static型的一般内部类和static修饰的嵌套类来分析.

(1).首先对于一般的内部类,其和外部类的联系是很紧密的.体现在内部类的对象必须依附于一个外部类的对象,也就是说我们必须通过一个外部类的对象才能创建内部类的对象.其次,外部类的一切成员变量对于内部类都是可见的,包括private成员变量,而外部类也可以访问内部类的所有变量.另外,内部类还可以通过.this方式显式的访问其对于的外部类对象,外部类也可以通过.new方式创建内部类的对象(一般我们都是通过外部类的一个方法返回内部类的对象引用).下面的代码示范了这几点:

package lkl1;

///测试外部类对内部类的访问权限
public class Outer {
    private int k=100;
    private class Inner{
        private int i=100;
        public int j=1111;
        private void print(){
            System.out.println("Outer.k = "+ k); ///内部类可以访问外部类的所有成员变量
            System.out.println("Inner.print()");
        }
        ///非static内部类中不能创建static类型的变量
       /// public static int k=999;
    }
    public void print(Inner in){
        ///事实证明外部类同样可以访问内部类的所有方法,变量
        ///当然前提是我们有一个内部类的对象,直接通过类名来访问是不行的
        in.print();
        in.i++; in.j++;
        System.out.println(in.i);
        System.out.println(in.j);
    }
   public static void main(String[] args){
       Outer ot = new Outer();
       Outer.Inner in = ot.new Inner(); ///通过.new创建内部类的引用,注意内部类引用的声明方式
       ot.print(in);
   }
}

(2).对于static修饰的嵌套类来说,情况就不同了.通过上面的例子我们可以看到普通的内部类对象隐式的保存了一个引用,指向创建它的外围对象.但对于嵌套类,它的对象不依赖于外部类的对象而存在,当然它也不能访问非静态的外围类对象.另外还有一点.普通类中是不能包括static方法,变量的,但是嵌套类中是可以包含这些东西的.如下面的代码所示:

package lkl1;

///当内部类被声明成静态时,我们就将其当成一个静态成员来看待
///此时内部类不在和外部类的对象绑定在一起.
public class Parcel1 {
//ContentsheDestination都是前面定义的抽象类
    private static  class Parcel1Contents extends  Contents{
        private static int k=110;
        private int i=1;
        public int value(){
            return i;
        }
    }

    //我们可以通过外部类的静态方法创建嵌套类的对象
    public static Contents contents(){
        return new Parcel1Contents();
    }
    public static void main(String[] args){
        ///访问Parecel1的静态变量,注意调用格式
        System.out.println("Parcel1Contents的静态变量k  "+Parcel1.Parcel1Contents.k);
        Contents c= contents();
    }
}

三.内部类的作用

内部类的语法是很复杂的,但是在学习这些复杂语法的时候更令我迷惑的是:内部类有什么用?java编程思想一书是这么讲的:

1.每个内部类都能独立的继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响.

2.可以更好的实现多重继承.我们知道普通的类是不可以多重继承的,但是现在通过内部类,我们就可以对普通类达到多重继承的效果.

3.在一些设计模式中有重要的应用.

对于这些现在还不是都能理解的很清楚,希望在以后的学习中能够搞清楚.

下面给一个通过内部类实现迭代器的实例.下面的Sequence类是一个封装了一个Object数组的普通类,而Selector是一个通用的迭代器接口,我们在Sequence中通过一个内部类实现了Selector接口,然后通过这个内部类的接口向上转型后的对象对Sequence对象进行迭代访问.其实以前我们接触过得集合类的迭代器也就是这么实现的.

package lkl1;

///定义一个通用的迭代器接口
public interface Selector {

    boolean end();
    Object current();
    void next();
}

package lkl1;

///Sequence类封装一个固定大小的
///Object数组,然后提供它自己的一个迭代器
///这里利用的是内部类可以访问外部类的所有数据的性质
public class Sequence {
    private Object[] items;
    private int next=0;///保留当前元素个数
    public Sequence(int size){
        items=new Object[size];
    }
    public void add(Object obj){
        if(next<items.length){
            items[next++]=obj;
        }
    }

    ///封装一个内部类实现迭代器
    private class SequenceSelector implements Selector{
        private int i=0; ///当前访问到元素的编号
        public boolean end(){
            return i==items.length;
        }
        public Object current(){
            return items[i];
        }
        public void next(){
            if(i<items.length) i++;
        }
    }
    ///提供迭代器对外的接口
    public Selector selector(){
        return new SequenceSelector();
    }

    public static void main(String[] args){
        Sequence sq= new Sequence(10);
        for(int i=0;i<10;i++)
             sq.add(Integer.toString(i));

        ///利用Sequence本身的迭代器来访问
        Selector se =sq.selector();
        while(!se.end()){
            System.out.print(se.current()+" ");
            se.next();
        }
        System.out.println();
    }
}
时间: 2024-10-29 19:08:23

Thinking in Java---内部类及一个迭代器实例的相关文章

java rmi的一个简单实例

参考网站  http://www.cnblogs.com/leslies2/archive/2011/05/20/2051844.html 实体类PersonEntity package net.cs30.rmi; import java.io.Serializable; /** * Created by caochenghua on 2017/4/4. */ public class PersonEntity implements Serializable { private int id;

Thinking in Java--内部类+一个迭代器的实例

可以将一个类的定义放在另一个类的定义内部,这就是内部类. 内部类的定义是简单的,但是它的语法确实很是复杂,让人不是很好理解.下面就内部类做一个小结. 一.内部类的分类 总的来讲内部类分为普通内部类,匿名内部类,局部内部类,嵌套类(静态内部类)等.下面简要的介绍以下这些内部类定义的语法. (1).对于普通的内部类,就是在外围类中插入另一个类的定义.如下面的代码: package lkl1; ///封装一个包裹类 public class Parcel { ///在一个类的内部定义的另一个类,称之为

Java内部类this$0字段产生的一个bug

首先查看下面一段代码,我指出了问题代码的所在,读者先自己思考一下这段代码会有什么问题. 这是用clone方法完整拷贝一个二项堆(BinomialHeap)结构的代码.二项堆中包含一个内部类BinomialHeapEntry,这个内部类的对象即二项堆中的每一个结点,除了包含结点对应的关键字外,还记录父节点parent,下一个兄弟结点sibling和第一个孩子结点child三个指针.二项堆的根表通过每棵二项树根节点的sibling指针链接. cloneBinomialTree(BinomialHea

【Java】new一个内部类

1 class Outer{ 2 3 class Inner{ 4 5 } 6 } 7 8 Inner in = new Outer().new Inner(); [Java]new一个内部类,布布扣,bubuko.com

Java-Runoob-高级教程-实例-环境设置实例:2.Java 实例 – Java 如何运行一个编译过的类文件?

ylbtech-Java-Runoob-高级教程-实例-环境设置实例:2.Java 实例 – Java 如何运行一个编译过的类文件? 1.返回顶部 1. Java 实例 - 如何执行编译过 Java 文件  Java 实例 本文我们演示如何执行编译过的 HelloWorld.java 文件,其中 Java 代码如下: HelloWorld.java 文件 public class HelloWorld { public static void main(String []args) { Syst

【Java内部类】用法总结

前言 内部类在学习基础Java知识的时候大部分人都了解过,但也是大部分人都只是了解而已,在长年累月的开发中你才会发现内部类的真正好处 定义 可以将一个类定义在另一个类的内部,这就是内部类.内部类是一种非常有用的的特征,因为它允许你把一些逻辑相关的数据组织在一起,并控制内部类的可视性. 静态内部类:静态内部类除了访问权限修饰符比外围类多以外, 和外围类没有区别, 只是代码上将静态内部类组织在了外部类里面 非静态内部类:创建该类的前提是外部类实例已经被创建好了,而内部类可以访问外部类的实例变量,也就

Java内部类

本文是<Java核心技术 卷1>中第六章接口与内部类中关于内部类的阅读总结. Java中的内部类(inner class)是定义在另一个类内部的类.那么内部类有什么用呢?这里主要由三个内部类存在的原因: 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据.即,如果类A中定义了类B,那么类B可以访问类A中的数据,甚至是私有数据,但类A不能访问类B中的私有数据: 内部类可以对同一个包中的其他类隐藏起来.在一个包中,定义一个类时,即使不加上访问权限关键词,这个类也是包内其他类可访问的,不

Java内部类总结 (吐血之作)

内部类是指在一个外部类的内部再定义一个类.内部类作为外部类的一个成员,并且依附于外部类而存在的.内部类可为静态,可用protected和private修饰(而外部类只能使用public和缺省的包访问权限).内部类主要有以下几类:成员内部类.局部内部类.静态内部类.匿名内部类 为什么需要内部类? 典型的情况是,内部类继承自某个类或实现某个接口,内部类的代码操作创建其的外围类的对象.所以你可以认为内部类提供了某种进入其外围类的窗口.使用内部类最吸引人的原因是: 每个内部类都能独立地继承自一个(接口的

Java内部类的使用小结

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