Java内部类
一、内部类是什么
将一个类的定义放在另一个类的内部,这就是内部类。
/**
* 外部类
*/
public class Outer {
/**
* 内部类
*/
class inner{
}
}
二、为什么要使用内部类
使用内部类最吸引人的原因是:每个类都能够独立的继承一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对内部类都没有影响。
内部类拥有类的基本特征(可以继承父类,实现接口),在实际问题中我们会遇到一些接口无法解决或难以解决的问题,此时我们可以使用内部类继承某个具体的或抽象的类,间接解决类无法多继承引起的一系列问题。
注:内部类可以嵌套内部类,但是这极大的破坏了代码的结构,这里不推荐使用。
除了上面的优点之外还有如下几点:
-
内部类可以用多个实例,每个实例都有自己的状态信息,并且于其他外围对象的信息相互独立。
-
内部类并没有令人迷惑的"is-a"关系,他就是一个独立的个体。
-
内部类提供了更好的封装,除了外围类,其他类都不能访问。
-
创建内部类对象的时刻并不依赖与外围类对象的创建。
具体来说,内部类信息(属性、方法)可以和外部类重名;内部类是具有类的基本特征的独立实体;可以利用访问修饰符隐藏内部类的实施细节,提供了更好的封装;静态内部类使用时可以直接使用,不需先创造外部类。
三、内部类的共性
- 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号。
-
内部类不能用普通的方式访问,内部类是外部类的一个成员,因此内部类可以自由访问外部类的成员变量,无论是否是private的。
-
内部类声明成静态的,就不能随便的访问外部类的成员变量了,此时内部类只能访问外部类的静态成员变量。
四、使用内部类
(一)、成员内部类
public class Outer {
int id = 1;
/**
* 成员内部类
*/
class Inner{
private int id = 2;
public void say(){
// 访问的是内部类的变量
System.out.println(id); // 2
// 访问外部类的变量
System.out.println(Outer.this.id); // 1
System.out.println("Inner.say");
}
}
public void show() {
// 外部类访问内部类的成员需要先创建一个成员内部类的对象
Inner inner = new Inner();
System.out.println(inner.id);
}
public static void main(String[] args) {
Outer outer = new Outer();
// 访问类的成员
System.out.println(outer.id);
// 创建内部类的实例,需要外部类的实例
Outer.Inner1 inner1 = new Outer().new Inner();
inner1.say();
}
}
当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员,如果要访问外部类的同名成员,需要以下形式进行访问:
外部类.this.成员变量
外部类.this.成员方法
虽然成员内部类可以无条件的访问外部类的成员,但是外部类想要访问成员内部类,就不可以直接访问了,需要先创建一个成员内部类的对象,在通过指向这个对象的引用来访问。
在其他类使用成员内部类有两种方式:
方式一:
Outer.Inner inner = new Outer().new Inner();
方式二:
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
(二)、静态内部类(静态嵌套类)
public class Outer {
int id = 1;
static int id2 = 2;
/**
* 静态成员内部类
*/
static class Inner{
public void say(){
System.out.println("Inner.say");
// 只能访问外部类的静态信息(成员变量和方法)
System.out.println("Outer.id2=" + id2);
}
public static void show() {
System.out.println("Inner.show");
}
}
public static void main(String[] args) {
// 创建静态内部类实例
Outer.Inner inner = new Outer.Inner();
inner.say();
// 访问静态内部类的静态方法,Inner类被加载,此时外部类未被加载,独立存在,通过类名.方法即可访问。
Outer.Inner.show();
}
}
静态内部类也是定义在类里面的类,在类前面多了一个static关键字,内部类可以独立存在,不依赖与其他外围类,它不能使用外部类的非静态成员变量或方法。
(三)、方法内部类
public class Outer {
int id = 1;
public void work(){
final int id1=1;
int id2 = 2;
/**
* 方法内部类,只能在方法内部使用
*/
class Inner{
public Inner(){
System.out.println(id1);
// 报错,Variable 'id2' is accessed from within inner class, needs to be declared final
// 变量'id2'是从内部类中访问的,需要声明为final
// System.out.println(id2);
System.out.println("method work Inner created");
}
void work(){
System.out.println("method Inner.work");
}
}
// 方法内部类只能在方法中使用,在方法中创建实例并调用内部类的方法
Inner inner = new Inner();
inner.work();
}
public static void main(String[] args) {
Outer outer = new Outer();
outer.work();
}
}
方法内部类的限制:
-
类前不能有访问修饰符。
-
仅在此方法中使用。
-
无法创造静态信息。
-
只能访问final变量和形参
(四)、匿名内部类
public class Outer {
int id = 1;
/**
* 匿名内部类,一个没有名称的实现了IAnimal接口的类
*/
IAnimal animal = new IAnimal() {
@Override
public void say() {
System.out.println("匿名内部类的say方法");
}
};
public static void main(String[] args) {
Outer outer = new Outer();
// animal是Outer类的一个成员,这个成员是匿名内部的实例
outer.animal.say();
}
}
在使用匿名内部类时,这个new之后的类首先是要存在的,其次我们要重写new后的类的某个或某些方法。匿名内部类没有构造方法。
使用匿名内部类有一个前提条件:必须继承一个父类或实现一个接口,但最多只能继承一个父类或实现一个接口,new之后的类就是匿名内部类要继承的父类或实现的接口;匿名内部类不能是抽象类,因为系统在创建匿名内部类的时候,会立即创建内部类的对象。因此不允许将匿名内部类定义成抽象类。