一.内部类

1.分类:

  • 成员内部类
  • 静态内部类
  • 局部内部类
  • 匿名内部类

2.内部类的概念

将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。可以把内部类理解成寄生,外部类理解成宿主。

3.内部类的特点(何时用)

比如汽车的发动机,ArrayList的迭代器,人的心脏等。

  • 内部类表示的事物是外部类的一部分
  • 内部类单独出现没有任何意义

4.内部类的访问特点

  • 内部类可以直接访问外部类的成员,包括私有
  • 外部类要访问内部类的成员,必须创建对象

例如:

public class Car {
    private String carName;
    int carAge;
    String carColor;


    public void show(Car){//调用时都会隐含this用以指代调用者,这里完整时"Car this"
        //是打印调用者车的名字:宾利
        System.out.println(this.carName);
        Engine e = new Engine();
        System.out.println(e.engineName);
        //System.out.println(engineName);因此如果像这样直接调用内部类,内部类不知道调用的对象
    }


    class Engine{
        String engineName;
        int engineAge;

        public void show(){
            System.out.println(engineName);
            System.out.println(carName);
        }
    }
}

二.成员内部类的书写

1.注意点:

  1. 成员内部类可以被一些修饰符所修饰,比如: private,默认,protected,public,static等
  2. 在成员内部类里面,JDK16之前不能定义静态变量,JDK16开始才可以定义静态变量。
  3. 创建内部类对象时,对象中有一个隐含的Outer.this记录外部类对象的地址值。

2.获取内部类对象的两种方式

**方式一:**外部直接创建成员内部类的对象

外部类.内部类 变量 = new 外部类().new 内部类();

**例如:**调用Outer的内部类Inner

Outer.Inner oi = new Outer().new Inner();

演示:

方式一:
public class Test {
    public static void main(String[] args) {
        //  宿主:外部类对象。
       // Outer out = new Outer();
        // 创建内部类对象。
        Outer.Inner oi = new Outer().new Inner();
        oi.method();//内部类中的方法被调用了
    }
}

class Outer {
    // 成员内部类,属于外部类对象的。
    // 拓展:成员内部类不能定义静态成员。
    public class Inner{
        // 这里面的东西与类是完全一样的。
        public void method(){
            System.out.println("内部类中的方法被调用了");
        }
    }
}

**方式二:**在外部类中定义一个方法提供内部类的对象

**使用场景:**内部类被private修饰,外界无法直接获取内部类的对象的时候

案例演示

方式二:
public class Outer {
    String name;
    private class Inner{
        static int a = 10;
    }
    public Inner getInstance(){
        return new Inner();
    }
}

public class Test {
    public static void main(String[] args) {
        Outer o = new Outer();
        System.out.println(o.getInstance());//打印的是地址值


    }
}

三.外部类成员变量和内部类成员变量重名时的访问

注意:内部类访问外部类对象的格式是:外部类名.this

访问方法:System.out.println(Outer.this.变量名)

public class Test {
    public static void main(String[] args) {
        Outer.inner oi = new Outer().new inner();
        oi.method();
    }
}

class Outer {	// 外部类
    private int a = 30;

    // 在成员位置定义一个类
    class inner {
        private int a = 20;

        public void method() {
            int a = 10;
            System.out.println(???);	// 10   答案:a
            System.out.println(???);	// 20	答案:this.a
            System.out.println(???);	// 30	答案:Outer.this.a
        }
    }
}

1.成员内部类内存图

  • 内部类和外部类的字节码文件是分开的,分别是两个.class
  • Outer.inner oi = new Outer().new inner();new创建对象的顺序是:先创建外部类对象,然后再创建内部类对象。
  • 其中,内部类对象里面有个隐藏的Outer.this,用来记录外部类的地址值001,最终赋值的是002内部类的地址值
  • 当使用Outer.this.a调用对象时,就等于时外部类地址值.a直接调用外部类的a了

内部类内存图

四.静态内部类

注意事项:

1.静态内部类也是成员内部类中得到一种

2.静态内部只能访问外部类的静态成员静态方法,如果要访问需要创建外部类的对象

3.静态内部类中没有隐藏的Outer.this

静态内部类的创建:

public class Outer {
    static class Inner {//这个就是静态内部类了
        .......
    }
}

创建静态内部类对象的创建格式

外部类名.内部类  变量 = new  外部类.内部类名;

例子:OUter.Inner oi = new Outer.Inner();

调用静态内部类内方法的格式:

  • 调用其中的非静态方法的格式:先创建对象,用对象调用

    Outer.Inner oi = new Outer.Inner();//赋予的地址值是Inner的。
    oi.show1();
    
  • 调用其中的静态方法的格式:外部类名.内部类名.方法名();(一路点到尾)

    外部类.内部类.静态方法名();
    

    例如:Outer.Inner.show2();

案例演示

// 外部类:Outer0
public class Outer {

    //int a = 10;
    //static int b = 20;

    //静态内部类
    static class Inner {
        public void show1(){
            System.out.println("非静态的方法被调用了");
        }

        public static void show2(){
            System.out.println("静态的方法被调用了");
        }
    }
}
//测试类Test
public class Test {
    public static void main(String[] args) {
        Outer.Inner oi = new Outer.Inner();
        oi.show1();

        //静态方法
        Outer.Inner.show2();

    }
}

五.局部内部类

1.将内部类定义在方法里面就叫做局部内部类,类似于方法里面的局部变量

2.外界是无法直接使用局部内部类的,需要在其所在方法内部创建对象并使用。

3.该类可以直接访问外部类的成员方法,也可以访问方法内的局部变量。

1.定义格式:

class 外部类名 {
	数据类型 变量名;
	
	修饰符 返回值类型 方法名(参数列表) {
	public void show(){
		class 内部类 {
			// 成员变量
			// 成员方法
		 }  
     }
  }
}

2.调用:

直接创建对象调用其方法即可

类名 对象名 = new 类名();
对象名.方法名

六.匿名内部类

1.定义

  • 隐藏了名字的内部类(并不是真的没有名字,而是由java自动取,名字大概是成员内部类$序号)
  • 可以写在成员位置(方法外面的写法)
  • 也可以写在局部内部类(方法里面的写法)

2.细节

包含了继承或实现,方法重写,创建对象

  • 内部重写的方法是对类的继承或对接口的实现
  • 匿名类指的:不是这个整体,而是两个花括号之间的东西
  • 匿名内部类整体就是一个类的子类对象,或者接口的实现类对象(整体就是个对象,类名/接口名就是对象名)
    • 简单来说,这个整体就是对象

    • 因此还能这么玩,直接将末尾的;换成.直接调用

      new 类名/接口名() {
          重写方法;
      }.方法名
      

3.匿名内部类前提和格式

匿名内部类必须继承一个父类或者实现一个父接口

匿名内部类格式

new 父类名或者接口名(){
    // 方法重写
    @Override 
    public void method() {
        // 执行语句
    }
};

4.匿名内部类的使用场景

1.通常在方法的形式参数接口或者抽象类时,可以将匿名内部类作为参数传递

代码如下:

interface Swim {
    public abstract void swimming();
}

public class Demo07 {
    public static void main(String[] args) {
        // 普通方式传入对象
        // 创建实现类对象
        Student s = new Student();
        
        goSwimming(s);
        // 匿名内部类使用场景:作为方法参数传递
        Swim s3 = new Swim() {
            @Override
            public void swimming() {
                System.out.println("蝶泳...");
            }
        };
        // 传入匿名内部类
        goSwimming(s3);

        // 完美方案: 一步到位
        goSwimming(new Swim() {
            public void swimming() {
                System.out.println("大学生, 蛙泳...");
            }
        });

        goSwimming(new Swim() {
            public void swimming() {
                System.out.println("小学生, 自由泳...");
            }
        });
    }

    // 定义一个方法,模拟请一些人去游泳
    public static void goSwimming(Swim s) {
        s.swimming();
    }
}
最后修改:2023 年 11 月 23 日
如果觉得我的文章对你有用,请随意赞赏