内部类
在类中再定义类称为内部类,内部类可以称为内部类,局部内部类以及匿名类
(1).成员内部类
一个类中使用内部类,可以在内部类中直接存取其所在类的私有变量。
public class OuterClass{
private class InnerClass{
}
}
在内部类中可以随意使用外部类的成员方法以及成员变量,即使这些类成员被修饰为private
内部类的实例一定要绑定在外部类的实例上,如果从外部类中初始化一个内部类对象,那么内部类对象就会绑定在外部类对象上。
public class OutClass {
class innerClass{
innerClass(){ //内部类构造方法
}
int x=0; //内部类成员变量
public void doit(){ //内部类方法
System.out.println("内部类doit()方法");
}
}
innerClass in=new innerClass(); //实例化一个内部类
public void outdoit(){ //外部类方法
in.doit(); //调用内部类方法
}
public innerClass inf(){ //外部类方法,返回值为内部类引用
// x=1; //外部类不能直接访问内部类的成员变量
in.x=2; //改变内部类的成员变量
System.out.println(in.x); //此时内部类的对象in的x成员变量已经被改变成2
return new innerClass(); //返回一个新的内部类引用,这个新对象的成员变量x为初始值0
}
public static void main(String[] args) {
OutClass out=new OutClass();
//!!!内部类对象实例化操作必须在外部类或外部类的非静态方法中实现!!!!
OutClass.innerClass in=out.inf(); //通过外部类对象的inf方法的返回值创建一个新的对象
OutClass.innerClass in2=out.new innerClass();//在外部类中实例化操作
//innerClass in3=new innerClass(); 编译报错
innerClass in4=out.new innerClass();
in2.doit();
System.out.println(in.x);
out.outdoit(); //调用外部类的方法
}
}
输出结果:
2
内部类doit()方法
0
内部类doit()方法
内部类对象和外部类对象关系非常紧密,内外可以交互使用彼此类中定义的变量。只不过外部类调用内部类的变量需要通过内部类的对象。
如果在外部类和非静态方法之外实例化内部类对象,需要使用外部类,比如上面的main函数是个static方法。 实例化一个内部类需要OutClass.innerClass in=out.new innerClass(); 应该使用外部类对象来创建内部类对象
内部类对象会依赖于外部类对象,除非已经存在一个外部类对象,否则类中不会出现内部类对象。
(2)内部类向上转型为接口
如果将一个权限修饰符为private的内部类向上转型为其父类对象,或者直接向上为一个接口,在程序中就可以完全隐藏内部类的具体实现过程。可以在外部提供一个接口,在接口中声明一个方法。在实现该接口的内部类中实现该方法(也就是重写方法),就可以定义多个内部类以不同的方式实现接口同一个方法,而在一般类中是不能多吃实现接口中的同一个方法的,这种技巧经常被应用在Swing编程中,可以在一个类中做出多个不同的响应事件
interface OutInterFace{ //定义一个接口
public void f();
}
public class InterfaceInclass{
public static void main(String[] args) {
OutClass out=new OutClass(); //实例化一个OutClass对象
OutInterFace outinter=out.doit(); //调用doit()方法,返回一个OutInterFace接口
outinter.f(); //调用f()方法
}
}
class OutClass { //实现一个内部类实现OutInterFace接口
private class InnerClass implements OutInterFace{
InnerClass(String s){ //内部类的构造方法
System.out.println(s);
}
@Override
public void f() { //内部类方法
System.out.println("访问内部类中的f()");
}
}
public OutInterFace doit(){
return new InnerClass("访问内部类的构造方法");
}
}
输出接口:
访问内部类的构造方法
访问内部类中的f()
OutClass类中定义了一个修饰权限为private的内部类,所以除了OutClass类之外任何类都不能去调用内部类,这个内部类实现了OutInterFace()接口,还重写了接口的f()方法。OutClass类中定义了一个doit()方法,返回一个OutInterFace()接口。外部类不能访问内部类,但是可以访问这个doit()方法。由于该方法返回一个外部接口类型,这个接口可以作为外部全部使用的接口。它包含一个f()方法,在继承该接口的内部类中实现(重写)了该方法。如果某个类继承了外部类OutClass,由于内部权限不可以向下转型为内部类InnerClass,同时也不能访问f()方法,但是可以访问接口的f()方法。
outinter.f()通过接口访问f()方法,很好地对继承该类的子类隐藏了实现细节,仅为编写子类的人留下一个接口和一个外部类,同时也可以调用f()方法,但是f()方法的具体实现过程却很好地被隐藏了。
(3)使用this关键字获取内部类与外部类的引用
如果在外部类中定义的成员变量与内部类的成员变量名称相同,可以使用this关键字
public class UseThis {
private int x;
private class Inner{
private int x=11;
public void doit(int x){
x++; //形参x++
System.out.println("形参x++后:"+x);
this.x++; //内部类x++
UseThis.this.x++; //外部类x++;
//UseThis.x++ 报错
}
}
public static void main(String[] args) {
UseThis u=new UseThis();
Inner inner=u.new Inner(); //在外部类中实例化一个内部类
inner.doit(1);
System.out.println(u.x);
System.out.println(inner.x);
}
}
输出结果:
形参x:2
1
12
遇到内部类和外部类的成员变量重名的情况可以使用this关键字处理
外部类名称后跟一个点操作符和this操作符可以获取一个外部类的一个引用
(2)局部内部类
内部类不仅可以在类中定义,也可以在类的局部位置定义,如在类的方法或任意的作用域中均可以定义内部类
interface InterFace2{
}
public class OutClass2{
public static void main(String[] args) {
OutClass3 out=new OutClass3();
out.doit("方法形参doit");
}
}
class OutClass3 {
public InterFace2 doit(final String x){ //形参设为final
class InnerClass2 implements InterFace2{
InnerClass2(String s) {
s=x;
System.out.println(s);
}
}
return new InnerClass2("doit");
}
}
输出结果:
方法形参doit
内部类定义在doit()方法内部。所以在doit()外部不能访问内部类,但是该内部类可以访问当前代码块的常量以及此外部类的所有成员。
如果内部类需要在方法体中该局部变量,该局部变量需要设为final.使内部类不会改变该局部变量的值
(3)匿名内部类
匿名内部类的含义:定义子类,实现接口,创建子类对象一步完成
new 接口或父类(){
重写抽象方法
};
package com.lzw;
interface InterFace2{
}
public class OutClass2{
public static void main(String[] args) {
OutClass4 out=new OutClass4();
InterFace2 in=out.doit();
System.out.println(out.doit());
}
}
class OutClass4{
public InterFace2 doit(){ //实现接口
return new InterFace2() { //匿名内部类
private int i=0;
public int getValue(){
return i;
}
};
}
}
输出结果:
com.lzw.OutClass4$1@16d3586
匿名内部类没有名称,所以匿名内部类使用默认构造方法来产生对象。内部类定义结束后需要加分号,代表引用表达式的创建。
匿名内部类在编译后会产生"内部类名$序号"为名称的.class文件,序号以1 ~ n排列,代表1~n个匿名内部类
匿名内部类成员方法的调用
public class OutClass2{
public static void main(String[] args) {
OutClass4 out=new OutClass4();
System.out.println(out.doit());
}
}
class OutClass4{
public int doit(){
return new InterFace2() { //匿名内部类
private int i=0;
public int getValue(){
return i;
}
}.getValue(); //调用内部类的成员方法
}
}
输出结果:
0
匿名内部类只能写到方法中
(4)静态内部类
在内部类前添加修饰符static,这个内部类就成为静态内部类。
一个静态内部类中可以声明static成员,但是在非静态内部类中不可以声明静态成员。
静态内部类不可以使用外部类的非静态成员,静态内部类在程序开发中比较少见。
public class OutClass2{
int x=100;
static int y=11;
static class Inner { //定义内部类
void doit() {
System.out.println("外部类x:"+x); //编译报错
System.out.println("外部类y:"+y);
}
}
public static void main(String[] args) {
OutClass2 out=new OutClass2();
Inner in=new Inner(); //静态内部类对象的创建不需要其他外部类的对象
//也就是不需要普通内部类对象创建的,Inner in=out.new Inner();
in.doit();
}
}
输出结果:
外部类y:11
创建静态内部类对象不需要其他外部类对象
不能从静态内部类对象中访问非静态外部类对象
进行软件测试时,如果在每个java文件中都设置一个主方法,将会出现很多额外代码,而程序不需要这些主方法,可以在静态内部类中定义主方法。
public class OutClass2{
int x=100;
static int y=11;
static class Inner { //定义内部类
void doit() {
System.out.println("外部类x:"+x); //编译报错
System.out.println("外部类y:"+y);
}
public static void main(String[] args) {
System.out.println("静态内部类主方法");
}
}
}
输出结果:
静态内部类主方法
(5)内部类的继承
内部类继承要设立专门的方法
public class father {
public father(){
System.out.println("外部类father");
}
class Son{
int x=0;
void doit(){
System.out.println("内部类son");
}
}
}
public class OutClass2 extends father.Son{
int x=100;
static int y=11;
public OutClass2(father f){
f.super();
super.doit(); //使用父类的方法
System.out.println(super.x); //输出父类的成员变量
}
void doit(){ //重写doit()方法
System.out.println("outclass类");
}
public static void main(String[] args) {
OutClass2 out = new OutClass2(new father());
}
}
输出结果:
外部类father
内部类son
0
在某个类继承内部类时,必须让这个类一个带参数的构造方法,并且该构造方法的参数是需要继承内部类的外部类的引用,同时在构造方法体中是用参数对象名.super()语句