在java中,每一个定义好的类,在编译的时候,都会对应地产生一个.class文件。如果程序的规模越来越大,那么类文件也会越来越多,管理起来也会越来越麻烦,很容易发生命名的冲突。因此,java中引入了"包"(package)的概念。


一、内部类

在类中还可以再定义类,这种类叫做内部类(Inner Class)。使用内部类主要有三个好处:一是可以任意地访问对应的外部类的私有(private)成员;二是如果一个内部类只服务于对应的外部类,那么外界就不必知道有这个内部类的存在;三是对于外部类来说,内部类可以将一系列数据类型“打包”,就像C++的类中再定义结构体一样。当然类的功能要比结构体强大多了。

1、访问外部类的prvate成员

import static java.lang.System.out;

class Outer
{
    private int x = 0;
    
    //内部类Inner
    class Inner
    {
        public int accessOuter() //访问外部类的private成员
        {
            return x;
        }
    }
    
    public Inner getInner() //返回内部类对象
    {
        return new Inner();
    }
    
    public static void main(String[] args)
    {
        Outer objOut = new Outer();
        out.println(objOut.getInner().accessOuter());
    }
}



文件系统 java数据结构怎么写_c/c++


可以看到,程序打印出了外部类的private成员x的值。


2、将数据类型打包

public class InnerDemo
{
    //内部类Point,表示一个点
    private class Point
    {
        private int x,y;
        
        Point(int x,int y)
        {
            this.x = x;
            this.y = y;
        }
        public String toString()
        {
            return "(x = " + x + ",y = " + y + ")";
        }
    }
    
    //创建3个部类Point对象
    Point[] pt = new Point[]{new Point(1,1),new Point(2,2),new Point(3,3)};
    
    //输出点集
    public void getAllPoints()
    {
        for(Point point : pt)
        {
            System.out.println(point);
        }
    }
    
    public static void main(String[] args)
    {
        InnerDemo inner = new InnerDemo();
        inner.getAllPoints();
    }
}

InnerDemo类表示一个点集,而点集中的每一个点元素就可以用内部类来定义表示。


程序运行结果:

文件系统 java数据结构怎么写_java_02


对于一个包含内部类的类,程序编译后会产生一个名为 "外部类名$内部类名.class" 的类文件。如上例,编译后会生成InnerDemo$Point.class文件。由于Point类仅仅服务于其对应的外部InnerDemo类,因而在实际使用InnerDemo类的时候,我们不必关心Point类的存在。


二、匿名内部类

匿名内部类可以不用声明类名称,直接使用new关键字来产生一个对象:

new Object(){
    public void doSomethig(){
    //... ...
    }
}

这样的好处是,对于一些接口或者方法的参数类型是一个类的引用的时候,我们不必单独定义一个新的类去产生一个对象再把对象传进去,直接使用匿名内部类可以省略类的定义。如getInner方法需要一个Point类的对象,那么我们可以这样写:


getInner(new Point()
    public void method()
    {
          //do something
    }
);

值得注意的是,如果在一个匿名的内部类中想要访问外部的局部变量,那么就必须将这个要被访问的变量声明为final.否则在编译的时候就会报错,如:


public void method()
    {
        int x = 100; //没有声明为final
        Object obj = new Object(){
            public String toString()
            {
                System.out.println(x); //访问外部的x变量
                return "New Object!";
            }   
        };
    }



文件系统 java数据结构怎么写_文件系统 java数据结构怎么写_03


那么,为什么编译器要强制我们把x声明为final?这是因为,在匿名内部类中访问x时,内部类中访问的只是变量x的一份拷贝,如果在匿名内部类中修改了变量x,那么这种修改是不会影响到外部的变量x的。所以把x声明为final后,就起到一个提醒的作用,表示程序员不可以在内部类中修改x的值。这样就降低了出错的可能性。

三、包(package),导入(import)

1、包

Java 提供包来管理类。包就对应着我们文件系统的文件目录。我们可以使用关键字 package来定义包,如:

package test;
public class Point
{
}

编译时加入-d参数,并在后面指定要把生成的类放在哪一个文件之下,如:


javac -d . Point.java

这将会在当前目录下生成一个 test目录,Point.class文件就在这个test目录里。

注意,一旦设置了包后,这个包的名字就变成了类名的一部分。如上例,类Point的类名应为test.Point而不是Point了。


2、import

有时候我们的类的包名非常长,那么在使用的时候就会非常麻烦,因为要写上一长串包名。这时候就可以用import语句来导入一个包中的类,然后再使用此包中的类的时候,就不必加上长长的包名了。如:

不导入时:

test.Point pt = new test.Point();

使用import后:


import test.Point;//导入test包中的Point类
Point pt = new Point();

编译器在编译这个源文件时,首先会在当前目录下寻找Point类文件,如果没有找到,就会试着组合import上的设置来寻找Point类。这样在使用类的时候就不需要加包名了。


但在使用import时,如果你把源文件跟生成的类文件都放在同一个目录,那么在编译时极可能会发生“找不到类”的错误。下面就是一个这样的例子:

我们在test目录下有这样两个文件:

文件系统 java数据结构怎么写_内部类_04

假定在Point.class的上层目录里有这样一个程序:

import test.*;

class ImportError
{
    Point pt = new Point();
}



它使用通配符*导入了test目录下的所有类,但在编译时就会报错:


文件系统 java数据结构怎么写_文件系统 java数据结构怎么写_05

为什么会发生这个错误呢?

因为,当使用通配符*时,编译器就会导入test目录下的所有文件。当我们用到Point 类时,由于Point.java文件和Point.class文件都在test目录下,编译器会首先找到Point.java文件而不是Point.class类。所以会报错。

为了避免此类错误的发生,我们要把源文件跟类文件分开,专门建立一个src目录存放.java 文件,建立classes目录存放.class文件。


特别要说明的是,java.lang包是默认被自动导入的。



3、import静态成员

在J2SE 5.0后增加了import static 语法,允许我们导入一个类或接口中的静态成员。这样就可以让程序员写得更少了。如:

System.out.println("");



可以写成:


import static java.lang.System.out;

...
out.println("");
...




嗯嗯,就先到这吧。。