在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());
}
}
可以看到,程序打印出了外部类的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类表示一个点集,而点集中的每一个点元素就可以用内部类来定义表示。
程序运行结果:
对于一个包含内部类的类,程序编译后会产生一个名为 "外部类名$内部类名.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!";
}
};
}
那么,为什么编译器要强制我们把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目录下有这样两个文件:
假定在Point.class的上层目录里有这样一个程序:
import test.*;
class ImportError
{
Point pt = new Point();
}
它使用通配符*导入了test目录下的所有类,但在编译时就会报错:
为什么会发生这个错误呢?
因为,当使用通配符*时,编译器就会导入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("");
...
嗯嗯,就先到这吧。。