文章目录
- 1.内部类的分类
- 2.局部内部类
- 2.1.基本语法
- 2.2就近原则的理解
- 3.匿名内部类
- 3.1基于接口的匿名内部类
- 3.2基于普通类的匿名内部类
- 3.3基于抽象类的匿名内部类
- 3.4匿名内部类的细节
- 3.5匿名内部类实践
- 3.5.1作为实参进行传递
- 3.5.2实践案例
- 4.成员内部类
- 4.1基本介绍
- 4.2外部类,内部类访问
- 4.3外部其他类如何使用
- 5.静态内部类
- 5.1静态内部类的语法
- 5.2静态内部类的使用
1.内部类的分类
2.局部内部类
2.1.基本语法
1)定义位置:就是在一个方法的里面,我们的类里面有一个方法,我们的这个内部类就是在这个方法的里面;
2)本质还是一个类:因此我们的这个内部类也是可以有自己的这个成员变量和方法的;
3)作用域就是在这个方法体或者是代码块里面:因为这个内部类就类似于我们之前学习的这个局部变量的权限,这个就只是在我们的这个方法里面使用,作用域就是在这个方法里面或者是自己所在这个代码块里面;
4)内部类可以调用外部类的这个成员变量和方法(即使是私有的,像这个a和test02都是私有的,我们的内部类也是可以调动的);
5)在相同的方法里面,我们可以使用外部类(相对于内部类而言,不是在这个内部类里面)创建局部的内部类对象,调用这个局部内部类里面的方法;
2.2就近原则的理解
下面的这个代码里面可以看到,我们的这个outer01外部类里面是定义了一个变量a=10的,我们的inner01这个内部类里面定义的这个a=100,两个名字是一样的,这个时候在名字一样的情况下,我们会遵循就近原则;
我在这个上面贴了两张图,一个就是我们的正常打印输出内部类里面的这个a=100的情形,另外一个就是我们的这个使用this打印外部类里面的这个a=10的情况;
outer01.this的本质就是外部类的对象,谁调用的这个test01(),谁就是我们的外部类对象,我们的这个a就打印谁的;
this表示的是这个调用func的方法的对象;
outer01.this表示的是调用这个test01()方法的对象;
两个的层级关系不一样,这个func方法是包含在这个test01里面的;
3.匿名内部类
首先我们需要弄清楚上面的这个代码,这个里面是有一个IA接口,我们的tiger是对于这个接口里面的方法进行了重写,我们的这个Outer04这个类里面,使用tiger创建一个对象,使用接口引用接受这个创建的对象,我们调用这个cry方法,但是如果我们只需要创建一个对象,多写一个类显得很多余;
3.1基于接口的匿名内部类
这个时候,我们就可以使用这个匿名内部类:
就是我们的这个类不需要在这个Outer04外面单独的进行实现,而是直接在这个类里面去匿名写出来;
下面的这个new IA()这个本来是不可以直接去实例化我们的接口的,但是我们的这个后面加上了这个大括号,对于这个里面的cry方法进行了重写,实际上这个匿名内部类的底层代码就是:
class xxxxx implements IA{
@override
public void cry(){systemo.out.println("老虎在叫唤");}
};
然后我们的这个搞完之后,相当于这个地方new的就是这个xxxxx,只不过我们的简写了罢了,这个就是底层的操作,虽然我们没有去定义哦这个类,但是实际上他是有的,而且他是有名字的,就是我们的外部类+$1,这个就是匿名内部类的名字,我们使用这个Outer.$1这个类迅速的创建出来一个对象,然后赋值给了我们的这个tiger,这个就是匿名内部类被我们使用的过程;
其实我始终觉得,如果把这个过程还原出来,对于这个匿名内部类不是很难理解,对于一个匿名内部类,我们也可以按照这个方式还原回去,就是我们可以自己构想出来一个类,这个类被创建对象,然后传递给了我们的接口的引用,。这样这个匿名内部类对于我们就不难理解了~~
3.2基于普通类的匿名内部类
上面演示的是基于这个接口的匿名内部类,下面的这个是基于我们已知的类的匿名内部类,就是我们有一个Father类,这个里面有一个tets方法;
我们还是按照上面的这个方式去new Father(),这个括号里面的参数就是这个传递给构造器里面的name的;我们还是还原一下这个底层的内部类的构造:-----------本质:这个基于类的匿名内部类就是这个类的子类对象!!!!
class xxxxx implements Father{
@override
void test(){system.out.println("匿名内部类重写我们的test方法")}
}
下面的这个图片里面的黄色区域就是我们的这个实现的重写方法:就是这个father是普通的类,我们的这个匿名内部类可以去实现这个方法的重写,也可以不用重写,下面的这个就是没有重写的,下下面的这个就是对于这个里面的test方法进行了重写的情况,因为我们上面已经有了一个接口的匿名内部类,因此这个匿名内部类实际的名字就是Outer$2(这个2表示的就是第二个匿名内部类,其他的和上面的这个没有本质的区别);
下面的这个就是上面提到的对于这个普通类里面的方法进行了重写,并且使用这个father类的应用对象去调用这个方法的情况;
3.3基于抽象类的匿名内部类
上面的这个是个普通的类,这个里面涉及到的方法我们可以进行实现,也可以不用重写,但是下面的这个抽象类Animal里面的这个方法,如果我们使用匿名内部类创建这个类的子类对象,这个时候我们比需要对于这个抽象方法进行实现;
下面的这个就是我们对于这个里面的eat这个抽象方法进行实现;这个时候我们的匿名内部类创建的是抽象类的子类的对象;
3.4匿名内部类的细节
上面展示的三种匿名内部类:分别是基于这个抽象类,普通了和接口的,我们通过上面的这三个例子,应该知道:
接口的匿名内部类本质是一个对象,是实现我们的这个接口的类的实例化对象;
普通类的匿名内部类的本质也是一个对象,是继承我们的普通类的子类的对象;
抽象类的匿名内部类的本质也是一个对象,是继承我们的抽象类并且对于这个抽象类里面的方法进行重写的这样的一个类的实例化对象;
缩句之后就是:匿名内部类就是一个对象(是不是很离谱),而且不是没有名字,我们可以使用这个实例化对象的这个getglass属性打印出来这个类的名字;
下面的这个是我们的一个匿名内部类的使用,我们基于这个案例进行细节说明,相信理解了上面的介绍,这个下面的代码对于你而言不是问题:
- Outer05是我们的一个外部类,person是一个类,我们的这个匿名内部类就是这个person的子类,其实这个不准确,主要就是因为我们使用这个person()这个创建匿名对象,毕竟这个是new,肯定是new对象的,这个隐藏的就是一个new class xxxxx entends person内容;
- 知道了这个匿名内部类是这个person的子类,这个时候我们创建的这个匿名内部类给了p这个父类进行接收,因为我们的这个里面对于方法进行了重写,因此这个里面的p.hi()调用的就是我们的这个匿名内部类(子类)里面重写的方法;
- 如果我们的这个匿名内部类没有重写,就会调用父类person里面的这个方法;
我们的这个匿名内部类直接打印这个n1的值是可以的,打印输出99;
如果我们的这个代码块里面本来就有这个n1同名变量,这个时候也是符合就近原则的(局部内部类也有这个就近原则),我们会优先打印这个内部类里面的;这个图上面就是打印我们自己的88;
如果两个同时存在,这个时候想要打印99,我们可以使用这个Outer.this.n1的方式,这个Outer.this就是调用这个f1方法的对象,我们的这个对象是Outer05实例化的,因此这个时候就是打印自己的99(我们的对象如果是这个匿名类 Outer$1这样的类实例化的,就可以打印这个88了);
下面的这个是为了进行验证上面的说法,我们在这个main方法里面打印输出这个outer05对象的地址,内部类里面使用这个Outer05.this打印输出,发现两个的地址是一样的,这个就说明我们的这个Outer.this就是外部类的实例化对象,因此这个Outer05.this.n1打印的就是外部类里面的这个99;
3.5匿名内部类实践
3.5.1作为实参进行传递
下面的这个是基于接口实现的匿名内部类,其中我们的这个f1方法的参数就是一个接口,使用普通的实参,就是我们需要定义一个类去实现这个接口,然后用这个类的实例化对象去作为实参传递,但是,如果使用匿名内部类,这个时候我们就可以直接使用这个new IL$1这个类的实例化对象作为参数传递;
而且我们的这个定义类实现接口,如果用的很多,只要我们的这个类进行修改,其他的都会变化,但是我们的这个匿名内部类只会使用一次,我们修改一个地方就可以了;
3.5.2实践案例
下面的这个是实现两个匿名内部类进行不同的输出,我们使用匿名内部类就可以很灵活的控制,但是使用普通类实现接口,用这个类实例化对象也是可以的,因为我们的实现接口之后的这个类实例化的对象也是可以对于这个接口里面的方法进行重写的,重写的时候我们也是可以输出不同内容的;
这个只是看一下两个不同方式呢都可以实现这个效果,并不是孰优孰劣,但是上面的实参传递,我们的匿名内部类不需要显示的写这个类,确实很高效,对于两个方式,我们仔细体会即可~~
4.成员内部类
4.1基本介绍
下面的这个定义在方法位置的就是我们的成员内部类,因为这个类就是我们的一个成员,和我们的成员是放在一起的,因此这个也叫做成员内部类;这个成员内部类的作用域就是这个外部类,因此对于这个外部类里面使用到的这个hi()方法,我们在这个成员内部类里面是可以进行调用的;
4.2外部类,内部类访问
就是在我们的成员内部类里面,可以访问这个外部类里面的成员变量和方法,在我们的外部类里面,也是可以访问这个内部类里面的成员变量和方法的;
这个主要就是因为我们的这个成员内部类可以理解为一个外部类的成员,这个时候无论我们的权限是私有的还是共有的,我们都是可以在这个类里面进行访问的,类里面的相互访问不受这个权限修饰符的限制;
4.3外部其他类如何使用
我们需要首先知道外部类和外部其他类的区别:
外部类(上面的4.2里面介绍的),就是包含了我们的成员内部类的这个类,我们的成员内部类是在这个类里面的,成员内部类和这个类是包含关系,虽然这个类相对于内部类是外部类,但是两者是有关系的;
外部其他类就是完全没有关系(也不是很合适),就只没有直接的从属关系,像下面的这个Test就是外部其他类,上面的这个Outer08就是外部类,这个类包含了成员内部类;
我们的外部其他类想要访问成员内部类可以用下面的两个方式:一个就是new Inner()这个是实例化成员内部类的对象,outer08.new Inner()这个就是点出来这个outer08这个对象的属性,然后这个inner08作为接收值,这个时候inner08就可以访问这个成员内部类里面的方法;
至于这个里面的Outer.Inner就是一个嵌套的写法,因为我们这个里面的类太多了,因此我们使用这个方式进行指定,相当于是复合类,一层一层的进去;
第二种方式就是实现下面的一个方法,返回值是我们的成员内部类的实例化对象,然后我们在main方法里面可以使用外部类的对象outer08调用这个get方法,返回值inner对象就可以访问我们的内部类里面的test方法;
5.静态内部类
5.1静态内部类的语法
静态内部类和上面的这个成员内部类没有差别,就是多了这个static关键字(因此相关的这个静态的特性我们的这个静态内部类也是需要满足的);
这个时候,我们的类是一个静态的,我们的这个里面只可以访问静态的属性方法,不可以访问非静态的;
5.2静态内部类的使用
我们的这个外部类想要访问静态内部类还是要创建对象,这个时候非静态的可以访问我们的静态内部类。如下所示的这个f1方法(我用的还是4里面成员内部类的例子);
外部其他类想要访问,这个时候就略有不同,但是主要思路一致,一个就是new对象,只不过上面的时候,我们需要使用对象,但是我们的这个静态的不需要对象:
outer08.new Inner();--------成员内部类需要借助对象;
new Outer.Inner();----------静态内部类不需要借助对象,因为静态的属于类,不依赖于对象存在;
上面的就是两个第一种访问方式创建对象的区别,需要注意一下,忘记也可以返回4.3里面看一下这个第一中访问方式的区别,其实就是上面的这个代码块里面内容;
另外就是我们的这个多了一种访问方式,除了定义一个方法,return我们的new Inner();
我们还可以定义静态方法,下面的这个get_就是静态的方法,在主方法main里面,我们可以直接使用类点出来这个方法(静态的嘛)