内部类
局部内部类
局部内部类定义在外部类的局部位置,就是代码块和方法内,作用域也是在其定义所在的代码块和方法内。
1.它可以访问外部类的私有成员。
2.不能给局部内部类加访问修饰符,因为它的地位跟局部变量一样,局部变量是不能加访问修饰符的,同理,由于局部变量是可以加final的,所以局部内部类也可以加final,并且外部其他类访问不到局部变量,所以也是访问不到已创建的内部类的
3.局部内部类想访问外部类的成员,直接访问即可,而外部类想要访问局部内部类的成员时,要在作用域内,创立局部内部类的对象,然后才能访问
4.当外部类的成员与局部内部类的成员重名时,默认就近原则,若要访问外部类成员,格式为:外部类.this.成员名
5.局部内部类可以嵌套
匿名内部类
有时开发中某些类只用到一次,那么如果特意写一个类的话会很不划算,这时匿名内部类就派上用场了,可以很好地简化开发
匿名内部类也定义在外部类的局部位置中。
语法:new 类名/接口名(参数列表){
//类体
} ;
匿名内部类注意:
①本质之一是类,且是内部类,匿名内部类显式匿名,隐式有名,就是说你不能直接看到它的名字,需要用方法来获取,如下列代码
public class Test {
public static void main(String[] args) {
new B().getAnonymousClassName();
}
}
interface AA {
void introduce();
}
class B {
public void getAnonymousClassName() {
AA kzh = new AA() {
@Override
public void introduce() {
System.out.println("大家好,我叫kzh,目标是天天向上,天天进步");
}
};
kzh.introduce();
//获取匿名内部类的类名
System.out.println("kzh的运行类型是" + kzh.getClass());
}
}
运行结果
大家好,我叫kzh,目标是天天向上,天天进步
kzh的运行类型是class com.kzh.test.B$1
============当再次加一个外部类==============
public class Test {
public static void main(String[] args) {
new A().callB();
}
}
class A {
class B {
public void getAnonymousClassName() {
AA kzh = new AA() {
@Override
public void introduce() {
System.out.println("大家好,我叫kzh,目标是天天向上,天天进步");
}
};
kzh.introduce();
//获取匿名内部类的类名
System.out.println("kzh的运行类型是" + kzh.getClass());
}
}
public void callB() {
new B().getAnonymousClassName();
}
}
运行结果
大家好,我叫kzh,目标是天天向上,天天进步
kzh的运行类型是class com.kzh.test.A$B$1
======探究$后面的数字是否是序号以及序号的排列方式======
public class Test { public static void main(String[] args) { new A().callB(); } } class A { class B { public void getAnonymousClassName() { AA kzh = new AA() { }; System.out.println("kzh的运行类型是" + kzh.getClass()); } class C { public void getAnonymousClassName() { AA pg = new AA() { }; AA wyt = new AA() { }; //获取匿名内部类的类名 System.out.println("wyt的运行类型是" + wyt.getClass()); System.out.println("pg的运行类型是" + pg.getClass()); } } public void callC() { new B.C().getAnonymousClassName(); } } public void callB() { new B().getAnonymousClassName(); new B().callC(); } } 运行结果 kzh的运行类型是class com.kzh.test.A$B$1 wyt的运行类型是class com.kzh.test.A$B$C$2 pg的运行类型是class com.kzh.test.A$B$C$1
可以看出,匿名内部类的类名的格式是:外部类名$外部类名$.......外部类名$ + 序号(此匿名内部类在最近一级外部类的序号,按定义顺序来排)。
jdk底层在创建匿名内部类后,就立刻创建其实例,并且将地址返回给栈的对象名,然后这个匿名内部类就不能使用了。
②当匿名内部类用new 类名时,能加新普通属性√,新普通方法√,构造器×,普通代码块√,可以重写和重载方法
③对于new 类名{},如果想在{ }内建立匿名内部类,则必须要有引用指向,因为底层是匿名内部类继承new后面的类,那么按照继承的规则,肯定要优先完成父类的初始化
而这个{ }里面建立的匿名内部类(下面代码中的c)是在匿名内部类的父类里的,那么在初始化的时候完成建立,这时如果你没有引用指向它,那么将找不到匿名内部类c,所以必须要有一个引用
②、③如下面代码
public class Test { public static void main(String[] args) { A a = new A() { int a; static int b;//报错,jdk8不支持内部类的static @Override public void a(){ System.out.println("call a()"); }//注意,如果是抽象类,要遵循抽象类的规则,即一定要重写类中的抽象方法
public void a(int a){}
public void b(){} {} static {}//报错,jdk8不支持内部类的static public A(){}//报错,并且new A()的形参列表会传给A类的构造器 new A(){};//报错
A c = new A(){};
}; System.out.println("a的运行类型是" + a.getClass()); //a的运行类型是class com.kzh.test.Test$1,而不是以new后面的类为运行类型 //因为在底层,匿名内部类会继承new后面的类 //匿名内部类的类名与new后面的类名无关,只与匿名内部类定义位置所在的外部类有关
} } class A { public A() { } public void a(){ System.out.println("CALL a()"); } }
④匿名内部类不仅是一个类,也是一个对象,它可以直接访问成员
public class Test { public static void main(String[] args) { new A(){ //这里依然可以写东西 }.a();//正确 } } class A { public A() { } public void a(){ System.out.println("CALL a()"); }
⑤指向匿名内部类的引用,仍然是遵循向上转型和向下转型的规则,即必须要有继承关系
public class Test { public static void main(String[] args) { B a = new A() {};//报错 A b = new B() {};//报错 } } class A{} class B{}
⑥和局部内部类一样,匿名内部类不可以加修饰符,作用域仅在局部位置,并且注意,如果用引用来指向匿名内部类,依然遵守向上转型和向下转型的规则,即只能访问编译类型的所有成员
interface Computer { } public class Test { public static void main(String[] args) { Computer computer = new Computer() { int a; }; System.out.println(computer.a);//报错,computer的编译类型是Computer
System.out.println(new Computer() { int a; }.a);//正确,等效于是编译类型是这个匿名内部类的引用在访问特有变量a } }
⑦外部其他类不能使用已创建的匿名内部类,因为匿名内部类在局部位置,用完就没,而外部类想要访问匿名内部类的成员时,要在作用域内,创立局部内部类的对象,然后才能访问
⑧当外部类的成员与匿名内部类的成员重名时,默认就近原则,若要访问外部类成员,格式为:外部类.this.成员名
匿名内部类一般的应用场景
①当想创建一个只用一次的类时,比如作为一个形参
②如果想更改某个类的对象{ }里的内容时不会影响到其他对象,直接匿名内部类继承该类,就可以达成这个目的。
③如果某些类或接口的方法每次调用时都可能要更改方法体(比如方法每次需要实现的功能都可能不同时),那么直接匿名内部类就可以安全(不影响到下次该方法的使用),便捷又简洁(不用特意去另一个类重写或者在类里多添加一个方法)地帮你达成这个目的
成员内部类
1.定义在外部类的全局位置上
2.可以直接访问外部类的所有成员
3.可以添加访问修饰符和final,但不能加static
4.外部其他类可以访问已创建的成员内部类
5.当外部类的成员与成员内部类的成员重名时,默认就近原则,若要访问外部类成员,格式为:外部类.this.成员名
静态内部类
1.定义在外部类的全局位置上
2.可以直接访问所有的静态成员,且不能访问任何非静态成员
3.可以加任意访问修饰符和final,并且必须要有static
4.当外部类的成员与静态内部类的成员重名时,默认就近原则,若要访问外部类成员,格式为:外部类.成员名(一般用这个)/外部类对象.成员名
5.外部其他类可以访问已创建的静态内部类