一、定义
在一个类的内部定义的一个类,这个定义在内部的类叫内部类
二、 1、内部类作为外部类的一个"成员",可以被外部类的其它的成员访问。也可以使用各种访问修饰符。
2、内部类:可以访问外部类的成员(即使外部类成员是私有的),也可以访问本类的成员;
3、内部类的常用修饰符:
1.可用修饰符:四种访问权限修饰符、static、final、abstract(外部类可以不是abstract),可以调用外部类的属性
2.private:此内部类被私有,只能在外部类内部被实例化;
static :在其它类中实例化内部类对象,就不需要外部类对象了;
1.被静态修饰的成员内部类只能访问外部类的静态成员
2.内部类被静态修饰后的方法
可以是静态方法
可以是非静态方法
三、内部类分类:
1、java内部类分为:成员内部类、方法内部类、静态内部类,匿名内部类
2、成员内部类:定义在外部类的成员位置,跟外部类的其他成员是并列关系
①具有成员的特征:可以有四种访问修饰符、static和final修饰
②具有类的特征:abstract,可以有具体成员和方法、构造器
3、 成员内部类定义:
classOuter {
classInner{}
}
编译上述代码会产生两个文件:Outer.class和Outer$Inner.class
3/1 成员内部类不能含有静态变量或静态方法,因为实例化成员内部类对象要先实例化外部类对象。如果一个内部类的成员变量被声明为static的,则这个内部类也要被声明为static的,这个内部类就成为了静态内部类
2/3 创建成员内部类的对象
①创建静态内部类的对象可以直接通过 外部类.内部类 引用名 = new 外部内.内部内的构造方法,如Outer.Inneroi = new Outer.Inner();
②创建非静态内部类的对象
Outer out = new Outer();
Outer.Inner in = out.new Inner();
in.show();
或者
Outer.Inner in = new Outer().new Inner();
in.show();
3/2 成员内部类可以直接访问外部类包括private型的所有成员,外部类要访问内部类成员要先创建内部类的对象
3/3 在成员内部类要引用外部类对象时,使用outer.this来表示外部类对象
4、局部内部类:定义在某个方法的内部。只有在内部类的方法时才会创建内部类的对象
常用形式,定义一个方法,使其返回类型为某个类或接口的对象,这个类或接口在这个方法内部定义,实现接口或类
把类放在方法内
比较少用的形式:
classOuter {
publicvoid doSomething(){
classInner{
publicvoid seeOuter(){
}
}
}
}
常用形式:
class Outer{
//定义一个返回类型为某个类或接口的方法
public Interfunction(){
//class Studentimplements Inter{
String name;
int age;
}
//返回实现类的对象
return newStudent();
}
}
4/1 方法内部类只能在定义该内部类的方法内实例化,不可以在此方法外对其实例化。
4/2 方法内部类对象不能使用该内部类所在方法的非final局部变量。
因为方法的局部变量位于栈上,只存在于该方法的生命期内。当一个方法结束,其栈结构被删除,局部变量成为历史。但是该方法结束之后,在方法内创建的内部类对象可能仍然存在于堆中!例如,如果对它的引用被传递到其他某些代码,并存储在一个成员变量内。正因为不能保证局部变量的存活期和方法内部类对象的一样长,所以内部类对象不能使用它们。
下面是完整的例子:
classOuter {
publicvoid doSomething(){
finalint a =10;
classInner{
publicvoid seeOuter(){
System.out.println(a);
}
}
Innerin = new Inner();
in.seeOuter();
}
publicstatic void main(String[] args) {
Outerout = new Outer();
out.doSomething();
}
}
4/3 方法内部类不能定义成static类型的,其成员变量也不能是static的
5、匿名内部类
5/1 定义
顾名思义,没有名字的内部类。表面上看起来它们似乎有名字,实际那不是它们的名字。
正因为没有名字,所以匿名内部类只能使用一次,它通常用来简化代码编写
但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口
5/2 声明
当程序中使用匿名内部类时,在定义匿名内部类的地方往往直接创建该类的一个对象。匿名内部类的声明格式如下:
new ParentName(){
...// 内部类的定义
}
例:
class Outer{
public Interfunction(){
return new Inter{
};
}
}
5/3 使用匿名内部类的情况
3/1 只用到类的一个实例 。
3/2 类在定义后马上用到。
3/3 类非常小(SUN推荐是在4行代码以下)
3/4 给类命名并不会导致你的代码更容易被理解。
5/4 匿名内部类的使用原则
4/1 匿名内部类不能有构造方法。
4/2 匿名内部类不能定义任何静态成员、静态方法。
4/3 匿名内部类不能是public,protected,private,static。
4/4 只能创建匿名内部类的一个实例。
4/5 一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类。
4/6 因匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。
5/5 匿名内部类的应用:
4/5/1 当我们需要调用某个方法,这个方法需要一个某接口类型的实参。我们为了调用这个方法,可
以先定义一个这个接口的子类,然后实例化这个子类对象,并将引用传递到这个方法;
4/5/2 如果,这个子类,只在调用这个方法时使用,其他地方不需要用到这个子类,这时,可以不用
定义这个子类。在调用这个方法时,使用"匿名内部类"的方式。那么这个子类就没有了"性"。
6、匿名内部类的使用实例
实例1:不使用匿名内部类来实现抽象方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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(); } } |
运行结果:eatsomething
可以看到,我们用Child继承了Person类,然后实现了Child的一个实例,将其向上转型为Person类的引用
但是,如果此处的Child类只使用一次,那么将其编写为独立的一个类岂不是很麻烦?
这个时候就引入了匿名内部类
实例2:匿名内部类的基本实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | abstract class Person { public abstract void eat(); }
public class Demo { public static void main(String[] args) { Person p = new Person() { public void eat() { System.out.println("eat something"); } }; p.eat(); } } |
运行结果:eat something
可以看到,我们直接将抽象类Person中的方法在大括号中实现了
这样便可以省略一个类的书写
并且,匿名内部类还能用于接口上
实例3:在接口上使用匿名内部类
interface Person{
public void eat();
}
public class Demo{
public static void main(String[]args) {
Personp = new Person() {
public void eat(){
System.out.println("eatsomething");
}
};
p.eat();
}
}
运行结果:eat something
由上面的例子可以看出,只要一个类是抽象的或是一个接口,那么其子类中的方法都可以使用匿名内部类来实现
最常用的情况就是在多线程的实现上,因为要实现多线程必须继承Thread类或是继承Runnable接口
实例4:Thread类的匿名内部类实现
public class Demo{
public static void main(String[]args) {
Threadt = 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接口的匿名内部类实现
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class Demo { public static void main(String[] args) { Runnable r = new Runnable() { public void run() { for (int i = 1; i <= 5; i++) { System.out.print(i + " "); } } }; Thread t = new Thread(r); t.start(); } } |
运行结果:1 2 3 4 5
四、内部类的共性
1、内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号 。
2、内部类不能用普通的方式访问。内部类是外部类的一个成员,因此内部类可以自由地访问外部类的成员变量,无论是否是private的 。
3、内部类声明成静态的,就不能随便的访问外部类的成员变量了,此时内部类只能访问外部类的静态成员变量
五、内部类的常用修饰符
1、可用修饰符:四中访问权限修饰符、static、final、abstract(外部类可以不是abstract)
2、内部类被私有化,则只能在外部类内部被实例化
3、内部类被static修饰,在其他类中实例化内部类对象,不需要外部类对象
4、内部类被static修饰后可以有静态成员和非静态成员,但若内部类中有由静态成员,则内部类必须是静态的
5、被static修饰的成员内部类只能访问外部类的静态成员
六、为什么需要内部类
典型的情况是,内部类继承自某个类或实现某个接口,内部类的代码操作创建其的外围类的对象。所以你可以认为内部类提供了某种进入其外围类的窗口。使用内部类最吸引人的原因是:
每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。如果没有内部类提供的可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。