内部类
我们在java面向对象(一)中大概了解了类,但是那个时候主要是外部类,并不涉及到内部类,因此在这次专题内我们来讲一下内部类的分类和内部类的用法。
- 内部类的定义:大部分时间内类被定义为一个独立的程序单元,但是在某些时候也会把一个类放在另一个类的内部。
- 内部类的优点:
1.提供更好的封装
2.内部类可以访问外部类的私有数据
3.适合于创建只使用一次的类 - 内部类和外部类的区别:
1.内部类可以使用private,protected,static,外部类仅仅可以使用default和public
2.非静态内部类不可以有静态成员
- 成员内部类
成员内部类是在类中的类可以使用类的修饰符private,public,default,protected,使用时类似于普通的类
public class Cat{
private double weight;
//外部类的两个重载的构造器
public Cat(){ }
public Cat(double weight){
this.weight=weight;
}
//定义一个非静态的成员内部类
private class CatColor{
//非静态类内部的实例变量
private String color;
public CatColor() { }
public CatColor(String color){
this.color=color;
}
public void info(){
System.out.println("猫的颜色是"+color);
//直接访问外部类的private修饰的成员变量
System.out.println("猫的重量为"+weight);
}
}
public void test(){
CatColor c1=new CatColor("黑白相间");
c1.info();
}
public static void main(String[] args){
Cat ca = new Cat(2.3);
ca.test();
}
}
将一个普通的类进行定义在另一个类的内部,因此成为一个内部类,在Catlei中含有yigetest()方法,该方法创建一个CatColor对象并且调用该对象的info()方法,编译后生成两个class文件,一个是Cat.class一个是Cat $ CatColor.class(成员内部欸的class总是这种形式OuterClass$InnerClass.class)非静态方法必须寄生在外部实例中
当内部类访问某个变量的时候,系统优先在该方法内找是否存在该名字局部变量,不存在的时候到该方法所在内部类寻找,还不存在则到外部类寻找该成员变量。当出现重名的情况,使用this,外部类名.this作为限定区分
语法:
//外部实例变量;
外部类名.this.外部类的实例变量
//内部实例变量
this.内部类的实例变量;
//局部变量
局部变量名;
- 局部内部类
定义在方法内部的内部类,仅仅在 该方法中可见可使用。因为局部内部类布恩那个在外部类的方法以外的地方使用,因此局部内部类不可以用访问控制符和static修饰。
public class LocalInnerClass{
public static void main(Strng[] args){
//定义局部内部类
class InnerBase{
int a;
}
//定义局部内部类的子类
class InnerSub extends InnerBase{
int b;
}
//创建局部内部类对象
InnerSub is =new InnerSub();
is.a=5;
is.b=8;
System.out.println("InnerSub对象的a,b实例变量是:"+is.a+","+is.b);
}
}
编译程序后产生三个class文件:LocalInnerClass.class,LocalInnerClass $ 1InnerBase
.class和LocalInnerClass $ 1InnerSub.class
因此局部内部类的class文件总是遵循以下命名格式:OuterClass$NInnerClass.class
局部内部类文件名比成员内部类文件名多一个数字
因为同一个类不可能有多个同目的成员内部类二同一个类中可能有两个以上的同名局部内部类(处于不同方法)因此增加数字来加以区分
- 静态内部类
使用static修饰一个内部类,这个内部类属于外部类本身而不属于外部类的某个对象。static关键字是类的成员变成类相关而不是实例相关,static修饰的成员属于整个类而不是属于单个对象。
静态内部类无法访问外部的实例成员,只能访问外部类的类成员
例如
public class StaticInnerClassTest{
private int prop1 = 5;
private static int prop2 = 9;
static class StaticInnerClass{
private static int age;
public void accessOuterProp(){
//下面是个错误代码因此我先注释掉
//错误原因是静态内部类不可以直接访问外部类的实例变量
//System.out.println(prop1);
//下面代码正确因为调用的是静态成员变量
System.out.println(prop2);
间泰内部类是外部类的一个静态成员因此外部类的所有方法,所有初始化块中可以使用静态内部类来定义变量创建对象等,外部类不可以直接访问静态内部类成员,但是可以使用静态内部类作为调用者来访问静态内部类的类成员,也可以使用静态内部类对象作为调用者来访问静态内部类的实例成员
public class AccessStaticInnerClass{
static class StaticInnerClass{
private static int prop1 = 5;
private int prop2 = 9;
}
public void acessInnerProp(){
//下面代码出现错误
//System.out.println(prop1);
//System.out.println(prop2);
//下面才是正确调用
//使用类名访问静态内部类的类成员
System.out.println(StaticInnerClass.prop1);
//使用实力访问静态内部类的实例成员
System.out.println(new StaticInnerClass().prop2);
}
}
另外java允许在接口内定义内部类,接口内的内部类默认使用public static 休斯,因此接口内部类只能是静态内部类
- 匿名内部类
适合创建只需要使用一次的类,创建匿名内部类会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不可重复使用
语法为
new 父类构造器(参数列表)|实现接口(){
匿名内部类的类体
};
匿名内部类需要继承一个父类或者实现一个接口
1)匿名内部类不能是抽象类它必须要实现继承的类或者实现的接口的所有抽象方法。
2)匿名内部类没有类名,因此匿名内部类无法定义构造器,只能定义初始化模块
3)匿名内部类中不能存在任何的静态成员变量和静态方法。
4)感觉上类似于父类的一个对象但是其实为一个匿名内部类
5)匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
6)使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
7)我们给匿名内部类传递参数的时候,若该形参在内部类中需要被使用,那么该形参必须要为final(jdk8可以不显式写出,编译器默认是final不可以进行更改)
abstract class Outer{
public abstract void info();
}
public class TestDemo{
public static void main(String[] args){
Outer o=new Outer(){
public void info(){
System.out.println("调用匿名内部类");
}
};
o.info();
}
}
因此我们产生了一个匿名内部类,编译器自动加上了extends Outer即匿名内部类为Outer的子类
为了看得更清楚我将他写成普通的类进行说明,注意下面代码是上面代码的等价
abstract class Outer{
public abstract void info();
}
public class Inner extends Outer{
public void info(){
System.out.println("调用匿名内部类");
}
}
public class TestDemo{
public static void main(String[] args){
Outer o=new Inner();
o.info();
}
}