zl程序教程

您现在的位置是:首页 >  其它

当前栏目

四种内部类

内部 四种
2023-09-11 14:20:29 时间


  内部类,又叫做类中的类,在Java中有四种,根据作用域不同分为三类:成员内部类、静态内部类和局部内部类。而局部内部类里又有一种特殊的匿名内部类。

成员内部类

  成员内部类首先有这么一个特征,多个内部实例,对应外部类一个实例。我举个例子,假设有以下外部类和内部类:

public class University {

    private String name;

    public University(String name) {
        this.name = name;
    }

    private class College {
        private String name;

        public College(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return University.this.name + name;
        }
    }
}

  再创建实例

final University university = new University("家里蹲大学");
final College college1 = university.new College("文法学院");
final College college2 = university.new College("外语学院");

  可以看到两个College对象共享一个University实例。但是不推荐这种共享对象的用法。为什么?因为使用组合(composition)更适合这种场景。
  因为共享实例的原因,枚举只能作为嵌套类,不能作为成员内部类,内部枚举编译器会自动加上static关键字,而不是不加static就报错。
  成员内部类的出现,其实是为了解决多继承问题的。因为外部类可以继承一个类,而内部类又可以继承第二个类,并且可以访问外部类的所有属性和方法,相当于内部类继承了两个类了。假如要继承第三个类,那么就在内部类里再写一个内部类。我举个斜杠青年如何诞生的例子:

interface Skill {
    String skill();
}

class Mother implements Skill {

    @Override
    public String skill() {
        return "踢足球";
    }

}

class Other implements Skill {

    @Override
    public String skill() {
        return "打篮球";
    }
}

public class Father implements Skill {

    @Override
    public String skill() {
        return "下棋";
    }

    public class Child extends Mother implements Skill {

        public class Grandchild extends Other {
            @Override
            public String skill() {
                return Father.this.skill() + "/" + Child.this.skill() + "/" + super.skill();
            }
        }
    }

    public static void main(String[] args) {
        final Father father = new Father();
        final Child child = father.new Child();
        final Child.Grandchild grandchild = child.new Grandchild();
        System.out.println(grandchild.skill());
    }
}

  测试结果:

下棋/踢足球/打篮球

  在语法上,内部类是不能有静态字段和静态方法的。如果有这个需求,那么只能使用嵌套类,也就是静态内部类。

静态内部类

  静态内部类,也叫做嵌套类Nested Class。在JDK的集合类源码中,经常看到静态内部类。前面说过成员内部类不能有静态字段和静态方法,但是静态内部类是可以用非静态字段和方法的。这说明了静态内部类一个普遍的用法是用在数据类Data Class的属性装配上,而这种属性所属的类还是专有的,JDK的Map中的Entry就是这样的,其他的类没有Entry, Entry是专属于Map的。这句话说得有点拗口,我举个例子吧,以下是我举的一个例子:

package com.youngthing.demo;

/**
 * 12/5/2022 1:54 AM 创建
 *
 * @author 花书粉丝
 */
public class Hero {

    /**
     * 名字
     */
    private String name;
    /**
     * 等级
     */
    private int level;
    /**
     * 武器
     */
    private Weapon weapon;

    public static class Weapon {
        /**
         * 名字
         */
        private String name;
        /**
         * 等级
         */
        private int level;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getLevel() {
            return level;
        }

        public void setLevel(int level) {
            this.level = level;
        }
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getLevel() {
        return level;
    }

    public void setLevel(int level) {
        this.level = level;
    }

    public Weapon getWeapon() {
        return weapon;
    }

    public void setWeapon(Weapon weapon) {
        this.weapon = weapon;
    }
}

  我建的类叫英雄,是游戏里常见的,然后把武器作为英雄的嵌套类。当然这个例子并不恰当,实际的项目中武器比较重要,代码非常复杂,所以一般是不会用嵌套类来实现。

局部内部类

  局部内部类是在方法内部定义的类,一般比较少用,反而是它的匿名形式,匿名内部类比较常见。比如要实现一个字符串按长度比较,可以写一个局部内部类来实现,如以下代码:

package com.youngthing.demo;

import java.util.Arrays;
import java.util.Comparator;

/**
 * 12/5/2022 2:02 AM 创建
 *
 * @author 花书粉丝
 */
public class SortDemo {

    public static void main(String[] args) {
        String[] strings = {"Abandon", "Zoo", "Park", "Union"};
        class MyComparator implements Comparator<String> {
            @Override
            public int compare(String s, String t1) {
                return s.length()-t1.length();
            }
        }
        Arrays.sort(strings, new MyComparator());
        System.out.println(Arrays.toString(strings));
    }
}

  局部内部类有一些限制,不能有静态方法和静态字段。

匿名内部类

  上面的例子中,局部内部类只用一次,所以可以改成匿名内部类,代码可以更简洁一些,如下:

package com.youngthing.demo;

import java.util.Arrays;
import java.util.Comparator;

/**
 * 12/5/2022 2:02 AM 创建
 *
 * @author 花书粉丝
 */
public class StringSortDemo {

    public static void main(String[] args) {
        String[] strings = {"Abandon", "Zoo", "Park", "Union"};
        Arrays.sort(strings, new Comparator<>() {
            @Override
            public int compare(String s, String t1) {
                return 0;
            }
        });
        System.out.println(Arrays.toString(strings));

    }
}

  同局部内部类一样,匿名内部类不能有静态方法和静态字段。
  作用域内部类和匿名内部类只能使用final的局部变量的问题。这个问题的原因是,局部变量传递到内部类时,已经是另一个变量了。如果在内部类中修改这个变量,并不会修改外部的局部变量的值。为了避免开发人员写出错误的代码,编译器就强制要求变量为final。但是如果要传递一个可能动态改变的值怎么办呢?可以定义一个长度为1的final数组,将值传递过去。