匿名内部类
匿名内部类也就是没有名字的内部类
正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写
但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口
实例1:不使用匿名内部类来实现抽象方法
|
abstract class Person { public abstract void eat(); } class Child extends Person { public void eat() { System.out.println("eat something"); } } public class Demo { public static void main(String[] args) { Person p = new Child(); p.eat(); } } |
运行结果:eat something
可以看到,我们用Child继承了Person类,然后实现了Child的一个实例,将其向上转型为Person类的引用
但是,如果此处的Child类只使用一次,那么将其编写为独立的一个类岂不是很麻烦?
这个时候就引入了匿名内部类
实例2:匿名内部类的基本实现
运行结果:eat something
可以看到,我们直接将抽象类Person中的方法在大括号中实现了
这样便可以省略一个类的书写
并且,匿名内部类还能用于接口上
实例3:在接口上使用匿名内部类
运行结果:eat something
由上面的例子可以看出,只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现
最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口
实例4:Thread类的匿名内部类实现
public class Demo {
public static void main(String[] args) {
Thread t =new Thread() {
public void run() {
for(int i = 1; i <= 5; i++) {
System.out.print(i +" ");
}
}
};
t.start();
}
} |
运行结果:1 2 3 4 5
实例5:Runnable接口的匿名内部类实现
publicclass Demo { publicstatic void main(String[] args) { Runnable r =new Runnable() { publicvoid run() { for(int i = 1; i <= 5; i++) { System.out.print(i +" "); } } }; Thread t =new Thread(r); t.start(); } } |
运行结果:1 2 3 4 5
块代码的定义:简单来说就是处于一对{}之间的代码。
内部类的定义:处于另外一个类中的具有类型名称定义块代码。
匿名类的定义:已知父类或者接口标准的内部类,且不具有具体的类型名称的块代码。匿名类的实例创建必须使用new 父类名|接口名 ()来创建。)
一、命名内部类——用于临时创建实例或者类型,且该类型或实例需要控制是否允许使用该类的人调用
1-1:命名局部内部类
public class OutClass{
public int f()
{
private class InnerClass
{
int i=10;
}
return new InnerClass().i;
}
public static void main(String[] args)
{
OutClass o=new OutClass();
System.out.println(o.f());
}
}
此例中对于使用OutClass的人来说,无需关注于InnerClass类的存在,只关注于f()函数具体的返回值;使用者无法访问InnerClass类的成员.
1-2:命名成员内部类
public class OutClass{
public class InnerClass{
int i=10;
}
public int f()
{
return new InnerClass().i;
}
public static void main(String[] args)
{
OutClass o=new OutClass();
System.out.println(o.f());
}
}
此例将简单内部类提升至成员等级,在此等级中,我们可以自由设定该内部类是允许OutClass使用者实例化InnerClass实例,如果为public 类型,则允许实例化,如果为private 类型,则不允许实例化(除开OutClass的成员方法将InnerClass类型的实例作为返回值返回)
1-3:命名内部类的实例化方法
public class OutClass{
public class InnerClass{
public InnerClass()
{
System.out.println("InnerClass construction start");
}
}
//public static void test()
//{
// InnerClass ic=new InnerClass();//错误的实例化内部类
//}
//public static InnerClass ic=new InnerClass();//错误的实例化内部类
public InnerClass ic=new InnerClass();
public InnerClass test()
{
return new InnerClass();
}
public static void main (String[] args)
{
OutClass oc=new OutClass();
OutClass.InnerClass ic=oc.new InnerClass();
}
}
注意:此处的输出结果为:
InnerClass construction start
InnerClass construction start
原因是外部类实例化时,需要产生一个内部类InnerClass成员,因此会调用内部类的构造函数,所以会有第一条
InnerClass construction start
而当在主方法中实例化内部类时,也需要调用内部类的构造函数,因此产生了第二条
InnerClass construction start
内部类实例化的前提条件:内部类实例的存在,必须依赖于外部类实例的存在,也就是说,在没有实例化外部类的情况下,不允许产生内部类实例,同样的,由于静态方法及静态成员的存在必须依赖于包含该静态方法或该静态成员的类型的实例,所以内部类的实例不能存在于外部类的静态方法或静态成员中,同理,非静态的内部类也不能包含静态的成员。
二、匿名内部类——用于临时创建实例,且该实例不希望类的使用者关注
匿名内部类的存在前提有两种:1、已知该类型的父类 或者 2、已知该类型的接口标准
通常对于一个临时的实例,我们并不希望他被使用者关注,比如一个人类Person,我们在处理一些他的特例的时候——比如BlindMan类型的人,而这种类型并不常见,所以我们可以不需要实体创建BlindMan这个类型,而直接使用它的具体实例:
1-1:已知父类的匿名内部类
public class Test
{
public void see()
{
Person p=new Person()
{
name="I";
void r(){
System.out.println(this.name+" can see nothing.");
}
};
p.r();
}
}
注意:由于此处的匿名类是创建p指针的具体膜板,必须在{}外添加";"号,表示是创建Person 实例的语句结束。
1-2:已知接口标准的内部类
interface IFly{
void fly();
}
class Plan{
public void run()
{
IFly f=new IFly()//使用匿名类创建飞机实例
{
public void fly()
{
System.out.println("Plan is flying");
}
};
f.fly();
}
}
三、静态内部类
静态内部类的出现主要是为了解决内部类的重复实例化问题,对于一个经常调用的内部类,在我们不需要关注其具体内容的时候,重复的多次出现会消耗大量资源,因此利用了静态类的特性——只需要实例化一次,产生了静态内部类,静态内部类中可以声明静态成员也可以存在非静态成员,且静态内部类遵循静态规则既:静态的方法或者类型,不能直接调用其外部的非静态成员。
需要注意的是 静态内部类的存在并不依赖于其外部类的实例,原因是普通类的实例化顺序是先静态成员,再成员变量初始化,再构造;由于在外部类实例化之前就已经产生了静态内部类(将他作为静态成员),因此静态内部类并不具有对外部类的依赖性。
四、其他普通类继承内部类
前提:由于内部类实例的存在依赖与外部类的实例,因此普通类继承内部类的时候,必须将外部类的实例作为参数传递给子类的构造函数,此处似乎并不符合语法逻辑:
public class A
{
public class B
{
public B()
{
System.out.println("Construction start");
}
}
}
public class Test extends A.B
{
public Test(A a)
{
a.super();//不符合语法逻辑,a.super怎么能是B的构造函数呢?.操作符应该是表示前一个实例的具体成员阿,a对象不该有super成员函数,况且该函数还是内部类B的构造函数!
}
public static void main(String [] args)
{
Test t=new Test();
}
}
输出结果为:Construction start