目录
介绍:
内部类就是一个类的内部又完整的嵌套了另一个类结构,被嵌套的类称为内部类(inner class),嵌套其他类的类称为外部类,是我们类的第五大成员(属性,方法,构造器,代码块,内部类),内部类的最大特点是可以直接访问私有属性,可以体现类与类之间的包含关系。
class Outer{ 外部类
class Inner{ 内部类
}
}
class Other{ 外部其他类
}
应用:
1.当某个类只为一个类提供服务时,可以将这个类定义成内部类
2.可以解决接口或者抽象类不能实例化的问题
分类:
定义在外部类的局部位置上(方法体内):
1)局部内部类---有类名
2)匿名内部类---没有类名
定义在外部类的成员位置上:
3)成员内部类---没用static修饰
4)静态内部类---使用static修饰
详解:
1.局部内部类(方法内部类):
1)可以直接访问外部类的所有成员,包含私有的
2)不能添加访问修饰符,因为他的地位就是一个局部变量,局部变量是不能使用修饰符的。但是可以使用final修饰,因为局部变量也可以使用final
3)作用域:仅仅在定义他的方法或代码块中
4)局部内部类---访问---外部类的成员【直接访问】
5)外部类---访问---局部内部类的成员【创建对象,再访问】且必须在作用域内
public class demo1 {
public static void main(String[] args) {
Outer outer = new Outer();
outer.f1();//这里就体现出外部类可以访问内部类的成员
}
}
class Outer{
private int num=100;
public void show(){
System.out.println("外部类的方法被调用");
}
public void f1(){
class Inner{
public void start(){//内部类访问外部类的变量和方法
System.out.println("外部类的num="+num);
show();
}
}
Inner inner = new Inner();
inner.start();
}
}
6)外部其他类---不能访问---局部内部类【他的地位只是一个局部变量,但他的本质是一个类】
7)如果外部类和局部内部类的成员重名时,遵守就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问。
假设我们在Inner类中也创建一个num让他的值为200;
class Inner{
private int num=200;
public void start(){
System.out.println("内部类的num="+num);
show();
}
}
当运行时我们可以发现num的值不再是外部类中的100,而是变成了200,此时就符合了就近原则,因为在内部类定义的num位置离他更近。
那假如我们就是明确想要访问外部类的num属性时,我们就可以借助 【外部类名.this.成员】来访问他,即Outer.this.num。
而Outer.this.num的本质就是外部类的对象,我们可以通过输出他们两个的hashcode值来进行对比,他们两个的地址完全相同
2.匿名内部类(重点!!!)
1)匿名内部类是定义在外部类的局部位置,比如方法中,且没有类名
2)匿名内部类同时还是一个对象
基本语法:
new 类或接口(参数列表){
类体;
};
2.1.基于接口的匿名内部类
使用场景:当我们只是想使用一次接口中的方法时,按照常规做法需要new一个对象实例来继承接口并且重写方法,但当我们有大量此需求时,不断new对象会造成资源的占用和浪费,此时可以使用匿名内部类来实现
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer outer = new Outer();
outer.show();
}
}
class Outer{
private int num=100;
public void show(){
A a= new A(){
public void cry(){
System.out.println("cry");
}
};
a.cry();
}
}
interface A{
public void cry();
}
匿名内部类虽然看起来没用名字但通过底层代码我们可以发现系统会自动分配他的名字,只是我们在外部看不见而已,我们可以通过看他的运行类型来确定他实际的类名
System.out.println("匿名内部类的运行类型是:"+a.getClass());
他的名称其实就是外部的类名+$1来构成的 ,jdk底层在创建了Outer$1实例,就立马把地址返回给了 a
注意: 匿名内部类使用一次就不能使用了
2.2基于类的匿名内部类
Father father = new Father("jack") {
public void test(){
System.out.println("重写了test方法");
}
};
class Father{
public Father(String name){}
public void test(){}
}
与接口的匿名内部类大同小异,此时我们来看他的运行类型,也就是说内部会按照创建次序为匿名内部类分配名字
2.3.应用场景
将匿名内部类当作一个实参直接传递,简洁高效
3.成员内部类
定义在外部类的成员位置上,并且没有static修饰
1)可以直接访问外部类的所有成员,包含私有的
2)可以添加认识访问修饰符,因为他的地位就是一个成员
3)作用域:和外部类的其他成员一样
4)成员内部类---访问---外部类【直接访问】
5)外部类---访问---内部类【创建对象,再访问】
6)外部其他类---访问---成员内部类
public class MemberClass {
public static void main(String[] args) {
Outer outer = new Outer();
outer.test();
}
}
class Outer{
private String name="jack";
int age=18;
public class Inner{
public void show(){
//可以直接访问外部类的成员
System.out.println("名字是:"+name+"年龄是:"+age);
}
}
public void test(){
//使用成员内部类
Inner inner = new Inner();
inner.show();
}
}
4.静态内部类
1)定义在外部类的成员位置,但有static修饰
2)可以访问外部类的所有静态成员,但不能访问非静态的成员
3)作用域:同其他的成员,为整个类体
public class StaticInnerClass {
public static void main(String[] args) {
Outer outer = new Outer();
outer.test();
}
}
class Outer{
private int n1=10;
private static String name="jack";
static class Inner{
public void show(){
//这里就不能访问到n1,因为n1是非静态的变量
System.out.println("name="+name);
}
}
public void test(){
Inner inner = new Inner();
inner.show();
}
}
4)外部类访问内部类(直接通过类名)
Outer.Inner inner = new Outer.Inner();
inner.show();