JAVA学习脚印9:外部类与嵌套类

Posted The fool

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA学习脚印9:外部类与嵌套类相关的知识,希望对你有一定的参考价值。

JAVA学习脚印9:外部类与嵌套类

本节要点

  • 为什么使用嵌套类?

  • 外部类与嵌套类的定义、嵌套类的分类?

  • 嵌套类的特性有哪些?

  • 内部类的分类及每种内部类的特性有哪些?


外部类与嵌套类是java语言中一个稍显繁琐的知识点,但确实又带来很多便利之处。

嵌套类可以从逻辑上只对一个类有用的类嵌入在这个外部类的里面,从而把这两个类放在一起;同时嵌套类提高了封装性,嵌套类可以在访问外部类的成员的的同时对外部不可见;另外嵌套类可以增强可读性,便于代码的维护。因此java语言使用这一特性来优化编程。

1.外部类(enclosingclass)与嵌套类(nestedclasses)的定义

java语言中允许这样定义类:


classOuterClass 
...
staticclass StaticNestedClass 
...

classInnerClass 
...



上述OuterClass被称为外部类,而StaticNestedClass和InnerClass称为嵌套类。嵌套类是外部类的成员,因此可以声明可以为private,public,protected,或者packageprivate。嵌套类有两种,静态和非静态的。声明为静态的嵌套类即为静态嵌套类StaticNestedClass,非静态的嵌套类称为内部类如InnerClass。

2.静态嵌套类

静态嵌套类是和外部类相关联的,它不可以直接访问外部类的成员变量和方法,只能通过对象引用来访问。静态嵌套类可以通过外部类的名字来访问,例如:

OuterClass.StaticNestedClass

要创建静态嵌套类,使用语法格式使用为:

OuterClass.StaticNestedClassnestedObject = new OuterClass.StaticNestedClass();

例如: java.awt.geom.Rectangle2D抽象类内部定义了两个静态嵌套类Float和Double用于构造不同数据类型的三角形。


publicabstract class Rectangle2D extends RectangularShape 

publicstatic class Float extends Rectangle2D implements Serializable 
...

publicstatic class Double extends Rectangle2D implements Serializable 
...



3.内部类

内部类一般声明在一个类中,与类中的方法成为并列的块,也可以声明在类的方法体中。

在方法体中声明的内部类,即为局部类;在方法体中声明的没有名称的内部类,即为匿名内部类。

1)内部类一般特性


特性之一:内部类必须与外部类实例关联,且可以直接访问外部类实例的方法和域

内部类和外部类的一个实例关联,内部类的实例只能在外部类的实例存在时才能存在,它可以直接访问该对象的方法和域,即使它们被声明为private。内部类通过OuterClass.this来访问外部类的成员。

创建内部类对象的语法格式如下:

OuterClass.InnerClassinnerObject = outerObject.new InnerClass();


特性之二:内部类不能定义自己的任何静态成员

内部类和外部类的实例相关,因此不能定义自己的静态成员。因为静态成员在类加载类时即被确定,而此时是还未创建外部类的实例,因此内部类不可以有静态成员。

下面是使用内部类的一个典型例子:


package com.learningjava;
/**
* a program to demonstrate using innerclass.

* from :http://docs.oracle.com/javase/tutorial/java/javaOO/innerclasses.html

*/
public class DataStructure 

public static void main(String s[]) 

// fill the array with integervalues and print out only

// values of even indices

DataStructure ds = newDataStructure();

ds.printEven();



// create an array

private final static int SIZE = 15;

private int[] arrayOfInts = newint[SIZE];

public DataStructure() 

// fill the array withascending integer values

for (int i = 0; i < SIZE;i++) 

arrayOfInts[i] = i;





public void printEven() 

// print out values of evenindices of the array

InnerEvenIterator iterator =this.new InnerEvenIterator();

while (iterator.hasNext()) 

System.out.println(iterator.getNext() + " ");





// inner class implements theIterator pattern

private class InnerEvenIterator 

// start stepping through thearray from the beginning

private int next = 0;

public boolean hasNext() 

// check if a currentelement is the last in the array

return (next <= SIZE -1);



public int getNext() 

// record a value of aneven index of the array

int retValue =arrayOfInts[next];

//get the next even element

next += 2;

return retValue;








2)局部类特性


特性之一:局部类可以访问局部变量,但是目前版本要求局部类只能访问声明为final的局部变量,也许在javase 8版本中会得到改进。同时局部类也不能定义或者声明任何静态成员。

代码1:局部类只能访问局部常量


publicclass LocalClassTest 

publicstatic void main(String[] args) 

LocalClassTesttest = new LocalClassTest();

test.sayGoodbyeTo("Tom");



publicvoid sayGoodbyeTo(final String name) 

classEnglishGoodbye 

publicvoid sayGoodbye() 

System.out.println( "Byebye"+","+name);




EnglishGoodbyemyEnglishGoodbye = new EnglishGoodbye();

myEnglishGoodbye.sayGoodbye();






这里局部变量name声明为finalString name则可以在局部类中访问,否则提示错误信息如下:

Cannotrefer to a non-final variable name inside an inner class defined in adifferent method


特性之二:在静态方法中声明的局部类,只能访问外部类的静态成员。


特性之三:局部类可以访问包围它的块的实例成员,它不是静态的;因此,在局部类里面不能包含大部分种类的静态声明,但是局部类可以声明静态常量域。接口与生俱来即为静态的,因此在块中不能声明接口。


代码2:代码块中不能定义接口,接口与生俱来即为static.


public void greetInEnglish() 
        interface HelloThere 
           public void greet();
        
        class EnglishHelloThere implements HelloThere 
            public void greet() 
                System.out.println("Hello " + name);
            
        
        HelloThere myGreeting = new EnglishHelloThere();
        myGreeting.greet();
    


将不能通过编译,因为greetInEnglish()方法中不允许声明接口HelloThere;


代码3:局部类不允许声明静态方法


    public void sayGoodbyeInEnglish() 
        class EnglishGoodbye 
            public static void sayGoodbye() 
                System.out.println("Bye bye");
            
        
        EnglishGoodbye.sayGoodbye();
    


将不能通过编译,因为局部类不允许声明静态方法,开发环境提示错误信息如下:

Themethod sayGoodbye cannot be declared static; static methods can onlybe declared in a static or top level type 。


代码4:局部类允许声明静态常量域可以通过编译,因为farewell被声明为静态常量。


publicclass LocalClassTest 

publicstatic void main(String[] args) 

LocalClassTesttest = new LocalClassTest();

test.sayGoodbyeInEnglish();//printBye bye



publicvoid sayGoodbyeInEnglish() 

classEnglishGoodbye 

publicstatic final String farewell = "Bye bye";//ok

publicvoid sayGoodbye() 

System.out.println(farewell);





EnglishGoodbyemyEnglishGoodbye = new EnglishGoodbye();

myEnglishGoodbye.sayGoodbye();





3)匿名内部类特性


匿名内部类可以使你的代码更简洁,它允许你在声明一个类的同时实例化该类。它与局部类的差别在于它是没有名字的类,局部类有类的声明而匿名内部类则是一个表达式。如果你一个局部类你仅使用一次,那么就可以使用匿名内部类。匿名内部类有如下特性:


特性之一:匿名内部类可以访问外部类的成员,也可以访问声明为常量的局部变量。


特性之二:在匿名内部类里面同样不能声明静态初始化代码段和接口,但可以持有静态常量域。


特性之三:在匿名内部类在里面在可以在声明在域、额外的方法、局部类;但是不可以定义构造器。


下面是使用匿名内部类用于处理GUI事件的典型例子:


packagecom.learningjava;

importjava.awt.*;

importjava.awt.event.*;

importjavax.swing.*;

/**

*a program to demonstrate using anonymous class

*@author wangdq

*2013-09-14

*/

publicclass AnonymousClassTest 

publicstatic void main(String[] args) 

TestFrameframe = new TestFrame();

frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

frame.setVisible(true);





/**

*a frame with a button to change the background color of the panel

*/

classTestFrame extends JFrame 

publicTestFrame() 

this.setTitle("AnonymousClassTest");

this.setSize(DEFAULT_WIDTH,DEFAULT_HEIGHT);

panel= new JPanel();

JButtonbutton = new JButton("Change background color");

//declarea annonymous class and instantiate it

button.addActionListener(newActionListener()

@Override

publicvoid actionPerformed(ActionEvent e) 

intred = (int) (Math.random()*255) ;

intgreen = (int) (Math.random()*255) ;

intblue = (int) (Math.random()*255) ;

ColorbgColor = new Color (red,green,blue);

panel.setBackground(bgColor);



);

this.add(panel,BorderLayout.CENTER);

panel.add(button);



privateJPanel panel;

privatefinal int DEFAULT_WIDTH = 300;

privatefinal int DEFAULT_HEIGHT = 300;

privatestatic final long serialVersionUID = 1L;





在这个例子中,button.addActionListener()方法需要一个实现了ActionListener接口(ActionListener接口声明为:publicinterface ActionListener extendsEventListener)的类的一个实例,我们使用匿名内部类使用来使用完成了使用此项功能。这段代码等价于:


step1:先声明一个类


classButtonListener implements ActionListener 

publicButtonListener(JPanel panel) 

this.panel= panel;



@Override

publicvoid actionPerformed(ActionEvent e) 

//TODO Auto-generated method stub

intred = (int) (Math.random()*255) ;

intgreen = (int) (Math.random()*255) ;

intblue = (int) (Math.random()*255) ;

ColorbgColor = new Color (red,green,blue);

panel.setBackground(bgColor);



privateJPanel panel;



step2:再创建一个实例并传递给addActionListener方法。

button.addActionListener(newButtonListener(panel));

由此可见匿名内部类简化了代码,使用很方便。


java嵌套类的理解,尤其是这些细微的差别,个人感觉更需要在实际开发中去体会。

参考资料: http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html



以上是关于JAVA学习脚印9:外部类与嵌套类的主要内容,如果未能解决你的问题,请参考以下文章

java中,类走之间可以嵌套吗?

内部类

外部类与内部类的访问

java 如何调用局部内部类和匿名类

java学习之内部类

ZeroC ICE源代码中的那些事 - 嵌套类和局部类