热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java编程思想:第10章内部类

可以将一个类的定义放在另一个类的内部,这就是内部类。1.可以实现隐藏2.内部类了解外围类,并能与之通信,很多时候可以写出更加优雅和清晰的代码10.1创建内部类publicclass

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

1.可以实现隐藏

2.内部类了解外围类,并能与之通信,很多时候可以写出更加优雅和清晰的代码

10.1创建内部类

public class Outer{

  class Inner{

  }

}

如果想从外部类的"非静态方法之外"的任意位置创建某个内部类对象,那么必须具体指明这个对象的类型:OuterClassName.InnerClassName

10.2链接到外部类

内部类自动拥有对外围类所有成员的访问权,这是如何做到的呢?当外围类对象创建了一个内部类对象时,此内部类对象必定会秘密的捕获一个指向那个外围类对象的引用,在访问外部类对象成员时,就是用这个引用来访问。编译器会帮助我们处理这些细节,而我们使用时,就像是内部类本身带有的一样。所以非static内部类在创建时候必须要由外部类对象来创建。

10.3使用.this与.new

如果要使用外部类对象的引用,OuterClassName.this来获取,编译器会检查类型是否正确,无运行时开销。

创建内部类对象:要先有外部类对象Outer o = new Outer();然后用外部类对象创建内部类对象Outer.Inner i = o.new Inner();不可以用o.new Outer.Inner();

10.4内部类向上转型

当把private内部类向上转型成基类,尤其是接口的时候,内部类就有了用武之地:这个接口的实现可以完全隐藏,完全不可见,因为返回的是接口引用

参考java源码AbstractList类:

public Iterator iterator() {

  return new Itr();

}

private class Itr implements Iterator {

  ...

}

当我们外界使用迭代器的时候

Iterator iter = arrayList.iterator();

只能拿到接口的引用,完全不知道里面是一个private内部类,因为Itr是private的,也不能创建它。

10.5在方法和作用域内的内部类

局部内部类:定义在方法体或者某一作用域里,超出作用域不能使用。

注意:不能使用意味着在外部不能被访问到类型,但是该类型在JVM里是实际存在的,即使这个作用域已经结束。

10.6匿名内部类

没有名字的内部类,可以有带参构造器,可以添加并初始化域。

如果在内部类的内部要使用外部的一个对象,这个对象的引用需要是final的。构造器中使用的参数不需要是final的。

匿名内部类里不能自己添加构造器(因为不能被访问到,而且没名字),需要做一些初始化的时候,可以用代码块来实现。

匿名内部类与正规的继承相比,要么扩展类,要么实现接口,不能二者都有,并且只能实现一个接口。

10.6.1再访工厂方法

可以把工厂方法改成匿名内部类。

interface Game{boolean move();}

interface GameFactory{Game getGame();}

class Checkers implements Game{

  private Checkers(){}

  private int moves = 0;

  private static final int MOVES = 3;

  public static GameFactory factory = new GameFactory(){

    public Game getGame(){return new Checkers();}  

  };

  public boolean move(){

    return ++moves != MOVES;

  }

}

10.7嵌套类-静态内部类

1)创建静态内部类对象不需要外部类对象

2)不能从静态内部类对象访问外围内的非静态成员

普通内部类不可以有static字段,静态可以有

10.7.1接口内部的类

正常情况下,接口内部是不能放任何代码的,但是嵌套类可以放。放入接口中的任何类都是public static的。

用途:如果你想创建某些公共代码,使得它们可以被这个接口的所有实现使用,那么接口中嵌套内部类就会非常方便。

10.7.2在多层嵌套类中访问外部类成员

一个内部类被嵌套多少层并不重要,它可以透明的访问所有外围类的所有成员。

class MMA{

  private void f(){}

  class A{

    private void g(){}

    public class B{

      void h(){

        f();//直接调用 没有问题

        g();

      }

    }

  }

}

10.8为什么需要内部类

一般来说,内部类继承某个类或实现某个接口,并且可以操作外围类对象。

核心原因:内部类可以提供多重继承的能力。

比如: AbstractList里有Itr和ListItr两种迭代器,如果我们只是让AbstractList实现Iterator接口,那么只能写出一个迭代器,通过使用内部类方式可以添加多个迭代器。

10.8.1闭包与回调

如果有一个类需要实现一个接口来提供某个方法,但是发现这个方法已经被另外一个接口使用了,那么可以用内部类代替这个类去实现接口。

回调的价值在于它的灵活性---可以在运行时动态决定需要调用什么方法。

10.8.2内部类与控制框架

设计一个控制框架,要求到了设定时间就执行相应的事件:

//创建一个抽象事件类,有执行时间和延迟时间,在产生新事件的时候用延迟时间计算出执行时间,action方法由实现类实现。

public abstract class Event{

  private long eventTime;

  protected final long delayTime;

  public Event(long delay){

    this.delayTime = delay;

    start();  

  }

  public void start(){

    eventTime = System.nanoTime() + delayTime;

  }

  public boolean ready(){

    return System.nanoTime >= eventTime;

  }

  public abstract void action();

}

//创建一个控制类,循环遍历所有事件,如果事件时间到了就执行action

public class Controller{

  private List eventList = new ArrayList<>();

  public void addEvent(Event e){eventList.add(e);}

  public void run(){

    while(eventlist.size()>0){

      for(Event e : eventList){

        if(e.ready){

          e.action();

          eventlist.remove();

        }

      }

    }

  }

}

从上面这2个类的设计来看,我们完全不知道event具体做什么,但是我们整个控制框架的核心功能:"把变化的事物(event具体实现),与不变的事物(时间发生过程)"分开。而我们只需要创建不同的变化的Event,这正是内部类要做的事情:

1)控制框架里的实现是由单个类创建的,从而使得实现细节被封装起来。内部类用来表示解决问题所必须的不同action。

2)内部类可以很容易访问外部类成员,这种实现不会显得很笨拙。

控制框架:

public class GreenhouseControls extend Controller{

  private boolean light = false;

  public class LightOn extends Event{

    public LightOn(long delay){super(delay);}

    public void action(){

      //hardware control code to turn on light

      light = true;

    }

  }

  public class LightOff extends Event{..}

  private boolean water = false;

  public class WaterOn extends Event{

    public WaterOn(long delay){super(delay);}

    public void action(){

      //..

      water = true;

    }

  }

  //...其他内部类

}

控制框架中用内部类把实现细节都隐藏起来了。

那么如何使用这个框架并执行需要的事件呢:

public static void main(String[] args){

  GreenhouseControls gc = new GreenhouseControls();

  gc.addEvent(gc.new LightOn(100));

  gc.addEvent(gc.new LightOff(200));

  ...

  gc.run();

}

控制框架可以体现出内部类的价值,图形界面设计里更是让人信服。

10.9内部类的继承

Java编程思想:第10章 内部类


推荐阅读
  • 本文介绍了lua语言中闭包的特性及其在模式匹配、日期处理、编译和模块化等方面的应用。lua中的闭包是严格遵循词法定界的第一类值,函数可以作为变量自由传递,也可以作为参数传递给其他函数。这些特性使得lua语言具有极大的灵活性,为程序开发带来了便利。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • 基于layUI的图片上传前预览功能的2种实现方式
    本文介绍了基于layUI的图片上传前预览功能的两种实现方式:一种是使用blob+FileReader,另一种是使用layUI自带的参数。通过选择文件后点击文件名,在页面中间弹窗内预览图片。其中,layUI自带的参数实现了图片预览功能。该功能依赖于layUI的上传模块,并使用了blob和FileReader来读取本地文件并获取图像的base64编码。点击文件名时会执行See()函数。摘要长度为169字。 ... [详细]
  • 《数据结构》学习笔记3——串匹配算法性能评估
    本文主要讨论串匹配算法的性能评估,包括模式匹配、字符种类数量、算法复杂度等内容。通过借助C++中的头文件和库,可以实现对串的匹配操作。其中蛮力算法的复杂度为O(m*n),通过随机取出长度为m的子串作为模式P,在文本T中进行匹配,统计平均复杂度。对于成功和失败的匹配分别进行测试,分析其平均复杂度。详情请参考相关学习资源。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 高质量SQL书写的30条建议
    本文提供了30条关于优化SQL的建议,包括避免使用select *,使用具体字段,以及使用limit 1等。这些建议是基于实际开发经验总结出来的,旨在帮助读者优化SQL查询。 ... [详细]
  • 本文介绍了指针的概念以及在函数调用时使用指针作为参数的情况。指针存放的是变量的地址,通过指针可以修改指针所指的变量的值。然而,如果想要修改指针的指向,就需要使用指针的引用。文章还通过一个简单的示例代码解释了指针的引用的使用方法,并思考了在修改指针的指向后,取指针的输出结果。 ... [详细]
  • 在project.properties添加#Projecttarget.targetandroid-19android.library.reference.1..Sliding ... [详细]
  • CentOS 7部署KVM虚拟化环境之一架构介绍
    本文介绍了CentOS 7部署KVM虚拟化环境的架构,详细解释了虚拟化技术的概念和原理,包括全虚拟化和半虚拟化。同时介绍了虚拟机的概念和虚拟化软件的作用。 ... [详细]
author-avatar
单身王子550130
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有