(一)内部类是什么
内部类是定义一个类中的类。为什么要使用内部类? 有以下3点
- 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
- 内部类可以对同一个包中的其他类隐藏起来。
- 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。
public class A{
private int m;
private int n;
private boolean flag;
public A(int m,int n){...}
//内部类B
public class B implemnts C{
public void add(...){
if(flag){
...
}
}
}
}
令人惊讶的的是,B类没有实例域或者名为flag的变量,取而代之的是flag引用创建B的A对象的域,这是一种创新的想法。内部类既可以访问自身的数据域,也可以访问它的外围类对象的数据域。
内部类的对象总有一个隐式引用,它指向创建它的外部类对象。
这个引用在内部类的定义是不可见的,为了说明这个概念,将外围类对象的引用称为outer,等价于以下形式:
//内部类B
public class B implemnts C{
public void add(...){
if(outer.flag){
...
}
}
}
outer不是java关键字,只是用它说明内部类中的机制。因为内部类B没有设置构造器,所以编译器为这个类生成了一个默认的构造器,器代码如下所示:
public B(A a){
outer = a;
}
必须先有外部类的对象才能生成内部类的对象,因为内部类的作用就是为了访问外部类中的成员变量。
- 还有:
内部类中声明的所有静态域都必须是final,内部类不能有static方法,java语言规范的没有解释,也可以有,但是只能访问外围类的静态域和方法。
内部类是一种编译器现象,与虚拟机无关。
(二)成员内部类
成员内部类是最普通的内部类,它的定义为位于另一个类的内部,形如下面的形式:
class A {
private int m=0;
private static int n=1;
public A(int m) {
this.m = m;
}
class B { //内部类
public void print() {
System.out.println(m); //外部类的private成员
System.out.println(n); //外部类的静态成员
}
}
}
成员内部类可以无条件访问外部类的所有成员属性和成员方法(包括private成员和静态成员)。
当成员内部类拥有和外部类同名的成员变量或者方法时,会发生隐藏现象,即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员,需要以下面的形式进行访问:
外部类.this.成员变量
外部类.this.成员方法
成员内部类可以随意访问外部类,但是外部类访问内部类必须创建一个内部类对象,通过这个对象的引用来访问:
class A {
private int m=0;
private static int n=1;
public A(int m) {
this.m = m;
}
private B getBInstance(){
return new B();
}
class B { //内部类
public void print() {
System.out.println(m); //外部类的private成员
System.out.println(n); //外部类的静态成员
}
}
}
创建内部类对象:
public class Test {
public static void main(String[] args) {
//第一种方式:
A a = new A();
A.B b = a.new B(); //必须通过A对象来创建
//第二种方式:
A.B b1 = a.getBInstance();
}
}
如果成员内部类 B 用 private 修饰,则只能在外部类的内部访问,如果用 public 修饰,则任何地方都能访问;如果用 protected 修饰,则只能在同一个包下或者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。这一点和外部类有一点不一样,外部类只能被 public 和包访问两种权限修饰。
(三)局部内部类
它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。即对外部世界可以完全的隐藏起来。不仅可以访问包含他们的外部类没还可以访问局部变量,不过局部变量必须为final。
局部内部类就像是方法里面的一个局部变量一样,是不能有 public、protected、private 以及 static 修饰符的。
class D{
public D() {
}
}
class A{
public Man(){
}
public D getB(){
class B extends D{ //局部内部类
int age =0;
}
return new B();
}
}
(四)静态内部类
有时候,使用内部类只是把一个类隐藏在另一个类的内部,并不需要内部类引用外围类对象。可以将内部类声明为static,以便取消产生的引用。
静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似,并且它:只能使用外部类的static成员变量或者方法,因为在没有外部类的对象的情况下,可以创建静态内部类的对象,如果允许访问外部类的非static成员就会产生矛盾,因为外部类的非static成员必须依附于具体的对象。
class A {
private static int age = 12;
static class B {
public void print() {
System.out.println(age);
}
}
}
public class Test {
public static void main(String[] args) {
A.B b = new A.B();//把A.B看成一个整体,这里直接一步,不需要创建A对象再才能创建B对象
in.print();
}
}
运行结果为:12
在内部类不需要访问外部类对象时应使用静态内部类,有些程序员用嵌套类表示静态内部类,静态类可以有静态域与静态方法,声明在接口中的内部类自动成为static和public类。
(五)匿名内部类
- 假如一个局部内部类只被用一次(只用它构建一个对象),就可以不用对其命名了,这种没有名字的类被称为匿名内部类,用的最多。
- 由于构造器名字必须与类名相同,而匿名内部类没有类名,所以它也没有构造器
- 匿名内部类不能定义任何静态成员、方法。
- 匿名内部类中的方法不能是抽象的。
- 匿名内部类必须实现接口或抽象父类的所有抽象方法。
- 匿名内部类访问的外部类成员变量或成员方法必须用static修饰。
- 当匿名内部类和外部类有同名变量(方法)时,默认访问的是匿名内部类的变量(方法),要访问外部类的变量(方法)则需要加上外部类的类名。
父类或者接口必须先存在才能使用匿名内部类 格式如下:
new 父类构造器(参数列表)|实现接口()
{
//匿名内部类的类体部分
}
因为没有类名,怎么判断是不是匿名内部类?
abstract class Father(){
....
}
public class Test{
Father f1 = new Father(){ .... } //这里就是有个匿名内部类
}
一般来说,new 一个对象时小括号后应该是分号,也就是new出对象该语句就结束了。但是出现匿名内部类就不一样,小括号后跟的是大括号,大括号中是该new 出对象的具体的实现方法。因为我们知道,一个抽象类是不能直接new 的,必须先有实现类了我们才能new出它的实现类。上面的伪代码就是表示new 的是Father的实现类,这个实现类是个匿名内部类。
其实拆分上面的匿名内部类可为:
class SonOne extends Father{
... //这里的代码和上面匿名内部类,大括号中的代码是一样的
}
public class Test{
Father f1 = new SonOne() ;
}
实例:
public abstract class Bird {
public abstract int fly();
}
public class Test {
public void test(Bird bird){
System.out.println(能够飞 " + bird.fly() + "米");
}
public static void main(String[] args) {
Test test = new Test();
test.test(new Bird(){
public int fly()
{ return 10000;}
});
}
}
------------------
Output:
能够飞 10000米
1.静态内部类才可以声明静态方法
2.静态方法不可以使用非静态变量
3.抽象方法不可以有函数体