黑马程序员_java 内部类
概述:将类定义在一个类的内部,这个定义在其他类内部的类称为内部类,包含内部类的类称为外部类。Java从JDK1.1开始引入内部类。
格式举例:
public class Outer{
private class Inner
{
}
}
内部类的作用:
1、内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类
2、内部类成员可以直接访问外部类的成员,包括私有数据,因为内部类被当成其外部类成员,同一个类的成员之间可以相互访问。但外部类不能访问内部类的实现细节
3、匿名内部类适合用于创建那些仅需一次使用的类
访问特点:
1、内部类可以直接访问外部类的成员,包括私有成员
2、外部类要访问内部类中的成员必须创建内部类对象
内部类的分类:内部类又分为成员内部类和局部内部类。成员内部类是一种和属性、方法、构造器和初始化块的类成员,局部内部类和匿名内部类不是类成员。成员内部类又分为两种:静态内部类和非静态内部类,使用是他提出修饰的成员内部类是静态内部类。
1、非静态内部类:
非静态内部类作为外部类,所以可以使用任意访问修饰符如public、private、protect等修饰。因为此类型内部类的上一程序单元是外部类,它就具有4个作用域:同一个类,同一个包,父子类和任何位置,因此可以使用4中访问修饰符。
非静态内部类里可以直接访问外部类的成员,包括私有成员,当在非静态内部类访问某个变量时,优先级从内而外:内部类方法的局部变量>内部类的成员变量>外部类的成员变量。因此,如果外部类的成员变量、内部类的成员变量、内部类方法里的局部变量同名,则可以通过使用外部类类名.this.、this作为限定来区分。而如果在外部类想要访问内部类则必须在外部类中创建非静态内部类对象,根据静态不能访问非静态成员的原则,不允许在外部类的静态成员中直接使用非静态内部类。如下代码演示:
public class OuterClass {
String str = "这是外部类中的属性";
private class InnerClass
{
String str = "这是内部类中的属性";
/**
* 打印外部类成员和内部类成员的方法
*/
public void showField()
{
String str = "这是局部变量";
System.out.println("访问外部类成员:" + OuterClass.this.str);
System.out.println("访问内部类成员:" + this.str);
System.out.println("访问内部类方法中的局部变量:" + str);
}
}
/**
* 创建访问内部类的方法
*/
public void visitInnerClass() //如果加上static修饰,编译不通过
{
//创建内部类对象
InnerClass in = new InnerClass();
//调用打印方法
in.showField();
}
public static void main(String[] args) {
//创建外部类对象
OuterClass oc = new OuterClass();
oc.visitInnerClass();
}
}
编译上面程序,看到在文件所在路径生成了两个class文件,一个是OuterClass.class,另一个是OuterClass$InnerClass.class,前者是外部类的文件,后者是内部类文件,即成员内部类(包括静态和非静态)的class文件形式:外部类名$内部类名.class。
Java不允许在非静态内部类里定义静态成员,否则编译出错。下面代码演示:
public class Outer{
private class Inner{
//下面三个静态声明都将引发编译错误
static{}
private static int a;
private static void show(){}
}
}
在外部类以外使用非静态内部类,则非静态内部类不能使用private修饰。语法如下:
外部类名.内部类名 标识符 = new 外部类名().new 内部类名();
代码示例:
public class Outer {
public class Inner
{
public void show()
{
System.out.println("这是内部类");
}
}
}
public class Test{
public static void main(String[] args) {
Outer.Inner in = new Outer().new Inner();
in.show();
}
}
2、静态内部类:
使用static来修饰的成员内部类称为静态内部类。根据静态成员不能访问非静态的成员的规则,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员(static修饰)。即使是在静态内部类的实例方法也不能访问外部类的实例成员。代码演示:
public class Outer{
private int age = 0;
private static class Inner{
public void show()
{
System.out.println(age); //编译出错
}
}
}
静态内部类里也可以直接访问外部类的静态成员,当在静态内部类访问某个静态属性时,优先级也是从内而外:内部类的静态成员变量>外部类的静态成员变量。因此,如果外部类的静态成员变量、内部类的静态成员变量同名,则可以通过使用外部类类名.变量名、内部类名.变量名作为限定来区分。代码举例:
public class Outer {
static String str = "这是外部类中的属性";
private static class Inner
{
private static String str = "这是内部类中的属性";
/**
* 打印外部类成员和内部类成员的方法
*/
public void showField()
{
//System.out.println(age);
String str = "这是局部变量";
System.out.println("访问外部类成员:" + Outer.str);
System.out.println("访问内部类成员:" + Inner.str);
System.out.println("访问内部类方法中的局部变量:" + str);
}
}
/**
* 创建访问内部类的方法
*/
public static void visitInnerClass() //如果加上static修饰,编译不通过
{
//创建内部类对象
Inner in = new Inner();
//调用打印方法
in.showField();
}
public static void main(String[] args) {
//创建外部类对象
Outer oc = new Outer();
oc.visitInnerClass();
}
}
在外部类以外使用静态内部类,内部类也不能用private修饰,语法如下:
外部类名.内部类名 标识符 = new 外部类名.内部类名();
代码实例:
public class Outer {
public static class Inner
{
public void show()
{
System.out.println("这是内部类");
}
}
}
public class Test{
public static void main(String[] args) {
Outer.Inner in = new Outer.Inner();
in.show();
}
}
3、局部内部类:
如果把一个内部类放在方法里定义,则这个内部类就是一个局部内部类,局部内部类仅在该方法里有效。因此局部内部类不能使用任何修饰符,如果需要使用局部内部类定义变量、创建实例或派生子类,那么都只能在局部内部类所在的方法内进行。代码实例:
Public class Outer{
Public static void main(String[] args){
Class Inner{
Int a;
}
Class InnerSub extends Inner{
Int b;
}
Inner i = new InnerSub();
I.a = 8;
}
}
编译上面程序,生成了三个class文件:Outer.class、Outer$1Inner.class、Outer$1InnerSub.class。局部内部类的class文件比成员内部类的class文件多了一个数字,这是因为同一个类里不可能有两个同名的成员内部类,而同一个类里则可能有一个以上同名的局部内部类(处于不同的方法中),所以java为局部内部类的class文件名中增加了一个数字,作为区分。
4、匿名内部类:
匿名内部类适合创建只需一次使用的类,定义匿名内部类的格式如下:
new 父类构造器(实参列表) 或者 实现接口()
{
//实体部分
}
从定义格式可以看出,匿名内部类必须继承一个父类,或实现一个接口,有且继承或实现一个。
关于内部类的两条规则:
1:匿名内部类不能是抽象类,因为在创建匿名内部类的同时,会立即创建匿名内部类的对象,因此不允许将匿名内部类定义成抽象类
2:匿名内部类不能定义构造器。因为匿名内部类没有类名。但可以定义实例初始化块。
匿名内部类最常见的创建方式是定义在接口中,代码演示如下:
public class FileTest {
public static void main(String[] args) {
File file = new File("h:");
String[] strArr = file.list(new FilenameFilter()
{
public boolean accept(File dir, String name) {
return new File(dir, name).isFile() && name.endsWith(".java");
}
});
for(String item : strArr)
{
System.out.println(item);
}
}
}
注意事项:当创建匿名内部类是,必须实现接口或抽象父类里的所有抽象方法。如果匿名内部类和局部内部类里需要访问外部类的局部变量,则必须使用final修饰符来修饰外部类的局部变量。代码示例:
public class OuterClass {
public static void main(String[] args)
{
final int a = 20;
class InnerClass
{
public void show()
{
System.out.println(a);//如果不加final,此处报错
}
}
}
}