这篇文章是对自己学习的一个总结,学习资料是疯狂Java讲义第三版,李刚编,电子工业出版社出版。
内部类简介:
内部类与外部类的关系和区别:
内部类的语法:只要在一个类的花括号内定义,那就是内部类。比如下面的代码。
public class Outer{
public class inner{
...
}
...
}
内部类甚至可以定义在类的方法中,那种叫局部内部类,作用于也只在那个方法中。
下面开始详细说非静态内部类的和静态内部类。
拥有内部类的类,在编译时产生的.class文件比较特殊,比如下面的代码
public class Outer {
private class Inner{
}
public static void main(String[] args) {
}
}
产生的.class文件如下图所示
除了外部类本身会产生的.class文件以外,还会产生 外部类名$内部类名.class文件。
前面说非静态内部类可以直接访问外部类的私有成员,这是因为非静态内部类的对象中保存了关于外部类对象的引用,如下图所示
CowLeg是非静态内部类,Cow是外部类。上面时是两个类的实例对象,可以看出,在CowLeg类对象中有一个Cow.this的引用帮助CowLeg类对象访问Cow类的成员。
当内部类想访问某个变量时,系统是先在内部类中查找有没有该变量名,如果没有再转到外部类中搜索,如果还是没找到,那时候才会报编译错误。
当内部类的变量和外部类的变量重名时,可以使用this,和外部类类名.this作为区分。
非静态内部类必须寄生于某一个外部类对象,也就是说,当存在一个非静态内部类实例时,一定有一个被他寄生的外部类实例,反之则不一定。所以,非静态内部类可以直接访问外部类的成员,因为外部类实例一定存在;而外部类不能直接访问非静态内部类成员,因为内部类对象可能不存在(可能还没实例化,所以不能直接访问。要先实例化内部类,才能通过内部类实例访问内部类成员)。
所以,静态代码块和静态方法中不能使用非静态的内部类,因为静态的代码块和方法属于类本身,而非静态内部类属于类实例,如果静态代码块和方法使用到非静态内部类,那计算机会糊涂,不知道要使用哪一个内部类实例,所以静态代码快和静态方法不能使用非静态内部类。
非静态内部类里不能使用静态的任何东西,包括方法,代码块,变量等。这个按道理来说应该是可以使用的,但这就是规定,不用去理解。
下面讲一下内部类的使用。这得分两种情况
静态内部类就是在前面加上static。静态内部类也就是属于类本身,而不属于类的实例,那它不能调用外部类的非静态成员。其它的没什么好说的和非静态内部类一样。
匿名内部类比较实用,它比较符合当初设计内部类时的场景。就是在某些场合需要使用类,但这个类只使用一次,以后再也不会使用了。
匿名内部类的语法
new 接口或父类构造器(实参列表){
成员变量
}
匿名内部类因为是只使用一次,所以会在定义时就创建对象,所以匿名内部类不能是抽象的类。从上面的语法可以看出,匿名内部类因为没有类名,所以也不能定义构造器,只能通过初始化代码块来代替构造器。
匿名内部类最常用的场景是需要创建某个接口的对象。比如某个方法的参数正好需要一个接口的实例,专门定义一个类去实现这个接口没必要,这时候可以用匿名内部类。代码如下所示
interface Product{
public double getPrice();
public String getName();
}
public class Outer {
public void anonymousTest(Product p){
System.out.println("购买" + p.getName() + "花费了" + p.getPrice());
}
public static void main(String[] args) {
Outer outer = new Outer();
outer.anonymousTest(new Product()
{
@Override
public double getPrice(){
return 2.00;
}
@Override
public String getName(){
return "衣服";
}
});
}
}
类Outer中方法anonymousTest的参数是接口Product,为了这个方法专门定义一个类去实现Product没什么必要,所以这里就用匿名内部类去实现这个接口来满足这个anonymousTest的需要。
匿名内部类继承父类时会涉及到参数列表的问题,这里的参数列表表示调用父类的构造器。
比如下面的代码
abstract class AnonymousTest{
public AnonymousTest(){}
public AnonymousTest(int a){
System.out.println("进入了有a的构造器");
}
}
public class Outer {
public void test(AnonymousTest a){
System.out.println("匿名类测试");
}
public static void main(String[] args) {
Outer outer = new Outer();
outer.test(new AnonymousTest()
{
//donothing
});
int a = 2;
outer.test(new AnonymousTest(2) {
//donothing
});
}
}
输出结果如下
使用匿名内部类时,还需要注意一点,就是匿名内部类使用到匿名类外部的变量时,那个变量会被自动加上final修饰符,比如下面的代码。
interface AnonymousTest{
void test();
}
public class Outer {
public void test(AnonymousTest a){
System.out.println("匿名类测试");
}
public static void main(String[] args) {
int age = 0;
Outer outer = new Outer();
outer.test(new AnonymousTest() {
@Override
public void test() {
System.out.println(age);
}
});
//这句代码会报错
age = 4;
}
}
Java8会为变量自动加上final,但是在java8之前匿名内部类引用类外变量之前必须为变量加上final,否则会报错。
为什么变量必须是final,其实是因为如果不加final,编译器实现起来会很困难,具体原因可以看这篇文章。
https://blog.csdn.net/salahg/article/details/7529091