关于JAVA核心技术(卷一)读后的思考(内部类的讨论)

内部类

内部类是定义在另一个类中的类。

定义内部类的原因有:

1)内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。

2)内部类可以对同一个包中的其他类隐藏起来。

3)当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。

使用内部类访问对象状态

分析一下下面一段代码

public class TalkingClock
{
  private int interval;
  private boolean beep;

  public TalkingClock(int interval,boolean beep)
  {……}

  public class TimerPrinter implements ActionListener
  {……}
}

其中TimePrinter位于TalkingClock类的内部,并不代表每个TalkingClock都有一个TimePrinter实例域。TimePrinter是由TalkingClock类的方法构造。

在计算机中内部类的对象总有一个隐式引用,他只想了创建它的外部类对象。这个引用在内部类的定义中是不可见的。然而,为了说明这个概念,可以将外围对象的引用称为outer。所以那些实例域就可以outer.实例域。

outer引用是在内部类的构造器中设置的,编译器为所有的内部类构造器添加了一个外部类引用的参数。这里的outer只是一个借代,并非真的是outer,只是表示外部类的引用。

下面是一个测试内部类的完整程序:

package com.java.innerClass;

import javax.swing.JOptionPane;

public class InnerClassTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        TalkingClock clock = new TalkingClock(1000,true);
        clock.start();
        JOptionPane.showMessageDialog(null, "Quit program?");
        System.exit(0);

    }

}
package com.java.innerClass;

import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;

import javax.swing.Timer;

class TalkingClock {
    private int interval;
    private boolean beep;

    public TalkingClock(int interval,boolean beep) {
        this.interval=interval;
        this.beep=beep;
    }
    public void start() {
        ActionListener listener=new TimePrinter();
        Timer t = new Timer(interval,listener);
        t.start();
    }
    public class TimePrinter implements ActionListener{
        public void actionPerformed(ActionEvent event) {
            System.out.println("At the tone,this time is"+new Date());
            if(beep)
                Toolkit.getDefaultToolkit().beep();
        }
    }

}

内部类的特殊语法规则

其实用表达式OuterClass.this来表示外围类引用。其中OuterClass表示的是外部类的类名。

如果在外部类的作用域之外要使用OuterClass.InnerClass

内部类是否有用、必要和安全在Java1.1增加内部类时,违背了Java简单的设计理念。内部类是一种编译器现象。与虚拟机无关,编译器会把内部类翻译成用$分隔外部类名与内部类的常规内文件,而虚拟机则对此一无所知。

在TalkingClock类内的TimerPrinter类将被翻译成类文件TalkingClock$TimePrinter.class。我们可以通过原先内部类和 将内部类调出变为常规类之后再用引用 将其调用发现,内部类可以访问外部类的私有数据,但是调出去的常规类就无法完成了。这也从侧面说明内部类拥有访问特权。与常规类功能相比起来功能更加强大。至于安全问题,在绝大部分情况下是安全的,书中说的方法,我大致看懂了,但是我觉得这个并没有总结的必要,知道其中原理就好。

局部内部类

如果仔细地阅读TalkingClock实例方法就会发现,TimerPrinter这个类名字只在start方法中创建这个类型的对象时使用了一次。

当遇到这类情况时,可以在一个方法中定义局部类。

public void start(){
class TimePrinter implements ActionListener
  {
   public void actionPerformed(ActionEvent event){
        System.out.println("At the tone,the time is"+new Date());
        if(beep)
              Toolkit.getDefaultToolkit().beep();

    }
  }
     ActionListener listener = new TimerPrinter();
     Timer t=new Timer(interval,listener);
     t.start();
}

局部类不能用public或private访问说明符进行声明。它的作用域被限定在声明这个局部类的块中。

局部类有一个勇士,即对外部世界可以完全地隐藏起来。

由外部方法访问变量

与其他内部类相比较,局部类还有一个有点。他们不仅能够访问包含它们的外部类,还可以访问局部变量,不过这些局部变量必须事实上为final,即它们赋值之后绝对不会改变。。

public void start(int interval,boolean beep)
{
  class TimerPrinter implements ActionListener{
    public void actionPerformed(ActionEvent vent){
       System.out.println("At the tone,this time is"+new Date());
       if(beep)
          Toolkit.getDefaultToolkit().beep();
         }
      }
ActionListener listener =new TimePrinter();
Timer t = new Timer(interval,listener);
}

请注意,TalkingClock类不再需要存储实例变量beep了,他只是引用start方法中的beep参数变量。

这看起来好像没什么值得大惊小怪。程序行

if(beep)...

毕竟在start方法内部,但已经不能访问beep变量的值了。

为了清楚的看到内部问题,可以分析控制流程,

1)调用start方法。

2)调用内部类TimerPrinter构造器,以便初始化对象listener。

3)将listener引用传递给Timer构造器,定时器开始计时,start方法结束。此时,start方法的beep参数不复存在。

4)然后,actionPerformed方法执行if(beep)……。

所以可能会出现外部方法修改引用,而导致内部类得到的引用值不一致或者内部类修改引用,而导致外部方法的参数值在修改前和修改后不一致这种问题。我们一般在局部内部类中调用形参和方法中定义的变量,则这些变量需要加上final。

匿名内部类

将局部内部类的使用再深入一步。假如只创建这个类的一个对象,就不必命名,这种类被称为匿名内部类。

代码如下:

public void start (int interval, boolean beep){
  ActionListener listener = new ActionListener(){
     public void actionPerformed(ActionEvent event){
          System.out.println("At the tone,the time is "+new Date());
          if(beep)
              Toolkit.getDefaultToolkit().beep();
        }
    };
   Timer t= new Timer(interval,listener);
   t.start();
}

可以将这种语法理解为创建一个实现ActionListener接口的类的新对象,需要实现的方法在其定义的括号里。

对比匿名内部类和普通类之间的差别就是构造函数紧跟着一个大括号,正在定义的的就是匿名内部类

原文地址:https://www.cnblogs.com/zzuzhouxiang/p/10355726.html

时间: 2024-10-08 02:01:20

关于JAVA核心技术(卷一)读后的思考(内部类的讨论)的相关文章

java核心技术卷一

java核心技术卷一 java基础类型 整型 数据类型 字节数 取值范围 int 4 +_2^4*8-1 short 2 +_2^2*8-1 long 8 +_2^8*8-1 byte 1 -128-127       浮点类型 数据类型 字节数 取值范围 小数位数 float 4 10^-38~10^38和-10^-38~-10^38 小数位数6-7 double 4 10^-308~10^308和-10^-308~-10^308 15位小数         boolean 类型和char 类

Java核心技术 第六章 接口和内部类

Java核心技术  第六章  接口与内部类 接口: 任何实现Comparable接口的类都需要包含compareTo方法,并且这个方法的参数必须是一个Object对象,返回一个整数数值. 在Java SE 5.0中,Comparable接口已经改进为泛型类型. 接口中所有的方法自动的属于public.因此,在接口中声明方法时,不必提供关键字public. 接口中决不能含有实例域,也不能在接口中实现方法. 要让一个类使用排序服务,必须让它实现compareTo方法,因此必须实现Comparable

读《java核心技术卷一》有感

过去一个多月了吧.才囫囵吞枣地把这书过了一遍.话说这书也够长的,一共706页.我从来不是个喜欢记录的人,一直以来看什么书都是看完了就扔一边去,可能有时候有那么一点想记录下来的冲动,但算算时间太紧,很多也是有始无终,毕竟在之前研究研究程序也只是自己的一个爱好而已,但没有想到签了一个程序员的工作.唉,这老天也太捉弄人了吧,让一个学电气工程(强电方向)学生毕业之后去写代码,而且是与硬件完全无关的代码.真是白念几年大学了.行了,就行发这么多牢骚吧. <java核心技术>有两个卷,我只看了卷一,从我的感

Java核心技术 卷一 笔记六 Date类

在Java核心技术卷就行了一前期  date类出现的频率很高  所以就对date类进行了小小的整合 Date类有两个 date类 表示特定时间的类 这个构造函数分配一个Date对象并初始化它代表指定的毫秒数,因为被称为"纪元",即1970年1月1日00:00:00 GMT标准基准时间. 就像格林尼治时间一样  作为一种基准值而存在 一般常用的是date转为string类型 String s=new Date(0).toString(); System.out.println(s.toS

关于JAVA核心技术(卷一)读后的思考(接口的基本知识的讨论)

接口 接口技术:这种技术主要用来描述类具有什么功能,而并不给出每个功能的具体实现.一个类可以实现一个或多个接口,并在需要接口的地方,随时使用实现了相应接口对象. 接口概念:接口不是类,而是对类的一组需求的描述,这些类要遵从接口描述的统一格式进行定义. 举个简单的例子:假设有一个类A,其中有一个sort方法是对对象数组进行排序,但要求对象所属的类必须实现了 B接口.假设B接口定义如下: public interface B{ int compareTo(Object other); } 即所有实现

关于JAVA核心技术(卷一)读后的思考(lambda表达式的讨论)

lambda表达式 lambda表达式是一个可传递的代码块.可以以后执行一次或者多次.在程序运行中我们经常会遇到传递代码段的情况,但是我们一般都是先构造一个对象,然后通过对象调用相应的代码块来实现传递代码块的目的,这个方法十分繁琐,设计师为解决这个问题,就设计出了lambda表达式. lambda表达式的语法我们可以参考之前写的方法,可以发现如果方法可以分为方法名,参数和方法主体.lambda表达式就是将方法名省去: 标准情况: (参数类型1 参数1,参数类型2 参数2,.....,参数类型n

关于JAVA核心技术(卷一)读后的思考(对象与类,日历的构造)

关于这本书,前三张都是基本内容,我觉得个人掌握的还可以,所以从第四章开始整理每日所学. 第四章主要说的是对象和类.第一部分是面向对象程序设计的概述.其中面向对象程序设计简写OOP,接下来写的是类,即构造对象的模板,由类构造对象的过程称创建类的实例(instance)这里可以想到instanceof,这也在后面学习中会有提到 封装是将数据和行为组合到一个包中,并对对象的使用者隐藏了数据的实现方式.数据成为实例域,操纵数据的过程称为方法.封装赋予了对象"黑盒"的特征,这是提高重用性和可靠性

关于JAVA核心技术(卷一)读后的思考(用户自定义类,静态域和静态方法的思考以及方法参数)

用户自定义类: 这部分并没有太过于困难的部分,借由代码进行复习: Employee类的定义: package com.java.EmployeeTest; import java.time.*; public class Employee {    private String name;    private double salary;    private LocalDate hireDay;//以上分别是Employee类的实例域        public Employee(Strin

关于JAVA核心技术(卷一)读后的思考(equals方法的讨论)

这是我反复看了两遍才理解的部分.其中也不乏参考了他人的微博内容,才大致对这个方法有所理解. 首先我们从Object类开始说起, 书中已经说了Object类是Java中所有类的始祖,在Java中的每个类都是由他扩展而来的,但在现实使用中并没有要求写出.如果一个类并没有明确指出超类,Object就是被认为是这个类的超类. 可以用Object类型的变量引用任何类型的对象: Object obj = new Employee("Harry Hacker",35000); 正如昨天的博文所提到过