前言

  如果你是一个急性子,没什么耐性的人,可以只看下句,自己去品味理解:

  内部类:就是我是你的一部分,我了解你,我知道你的全部,没有你就没有我。(所以内部类对象是以外部类对象存在为前提的)
  静态内部类:就是我跟你没关系,自己可以完全独立存在,但是我就借你的壳用一下,来隐藏自己。

  如果还不知道静态和普通成员的区别,就先学static吧。
  静态成员:属于这个类,数据存放在class文件中,程序运行之前就已经存进去数据了。
  普通成员:属于这个类的对象,程序运行后生成堆栈中。

  先来看一下官方说法,根据Oracle官方的说法:

 Terminology: Nested classes are divided into two categories: static and non-static. Nested classes that are declared static are called static nested classes. Non-static nested classes are called inner classes.

  一个称为静态嵌套类(静态内部类),一个称为内部类。那么两者到底有什么区别呢?很多JDK源码用到了静态内部类。HashMap、ThreadLocal、AQS的sync等。那么接下来学习一下内部类吧!

内部类

 内部类是定义在另外一个类中的类,主要原因有:
1.内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据;
2.内部类可以对同一个包的其他类隐藏;
  静态内部类和非静态内部类最大的区别是:非静态内部类编译后隐式保存着外部类的引用(就算外部类对象没用了也GC不掉),但是静态内部类没有。

1.1 非静态内部类

1.1.1 定义

  内部类定义语法格式如下:

  class OuterClass {
    ...
    class NestedClass {
        ...
    }
}

  我们直接先看来一个例子吧,在Human类里定义了一个HumanLeg非静态内部类,并且在HumanLeg类的实例方法中直接访问外部类的private访问权限的实例变量和类变量。

  /**
 * 非静态内部类可以直接访问外部类的实例变量、类变量、实例方法、类方法。这是因为在非静态内部类对象里,
 * 保存了一个它所寄生的外部类对象的引用(非静态内部类实例必须寄生在外部类实例里)。
 * 也就是说,非静态内部类对象总有一个隐式引用,指向了创建它的外部类对象
 * @author fuzhuan
 *
 */
public class Human {

    private int eyes = 2;

    private static String hair = "straight";

    public void count() {
        System.out.println("i can count number");
    }

    public static void say() {
        System.out.println("hello java!");
    }

    public class HumanLegs{

        private  int legsLength = 100;

        public  void  testInner() {
            count();
            say();
            System.out.println("human eyes :"+eyes);
            System.out.println("human hair :"+hair);
            System.out.println("humanlegs length :"+legsLength);
        }
    }

    //验证:非静态内部类可以访问外部类的 成员变量,类变量,成员方法,静态方法
    public static void main(String[] args) {
        Human human = new Human();
        HumanLegs legs = human.new HumanLegs();
        legs.testInner();
    }
}

运行结果

i can count number
hello java!
human eyes :2
human hair :straight
humanlegs length :100

另外,还有一些要注意的点

  • 非静态内部类的成员只是在非静态内部类范围是可知的,并不能被外部类直接使用,如果要访问非静态内部类的成员必须显示创建非静态内部类对象来调用访问!
  • 根据静态成员不能访问非静态成员的规则,外部类的静态方法不能访问非静态内部类。
  • 非静态内部类不允许定义静态成员。

1.1.2 内部类的特殊语法规则

如果非静态内部类方法访问某个变量,其顺序为

  • 该方法是否有该名字的成员变量 - 直接用该变量名
  • 内部类中是否有该名字的成员变量 - 使用this.变量名
  • 外部类中是否有该名字的成员变量 - 使用外部类的类名.this.变量名
    接下来看一个例子:
public class Outer {

    private int i = 1;

    public class Inner {
        private int i = 2;

        public void print() {
            int i = 3;
            System.out.println(i);
            System.out.println(this.i);
            System.out.println(Outer.this.i);
        }
    }

    public static void main(String[] args) {
        Outer outer = new Outer();
        Inner inner = outer.new Inner();
        inner.print();
    }
}

1.2 静态内部类

  如果用static来修饰一个内部类,那么就是静态内部类。这个内部类属于外部类本身,但是不属于外部类的任何对象。因此使用static修饰的内部类称为静态内部类。静态内部类有如下规则:

  • 静态内部类不能访问外部类的实例成员,只能访问外部类的类成员。
  • 外部类可以使用静态内部类的类名作为调用者来访问静态内部类的类成员,也可以使用静态内部类对象访问其实例成员。
/**
   *  静态内部类可以拥有普通成员属性,也可以有静态类成员属性
  *  静态内部类不能访问外部类的实例成员
 * @author fuzhuan
 *
 */
public class StaticInnerTest {

    private int a =3 ;
    private static int b =4;

    static class StaticInner {

        private int aa = 33;

        private static int bb = 44;

        public void test() {
            System.out.println("outer static field b:"+b);
//            System.out.println("outer  field a:"+a);
            System.out.println("inner  field :"+aa);
        }
    }

    public void test() {
        StaticInner si = new StaticInner();
        System.out.println("外部类访问静态类成员方法 aa:"+si.aa);
        System.out.println("外部类访问静态类静态方法 bb:"+StaticInner.bb);
    }

    public static void main(String[] args) {

        StaticInnerTest test = new StaticInnerTest();    
        test.test();
        System.out.println(StaticInner.bb);
    }

}

文章来源于 博客园 稍作修改 作者 GrimMjx

文档更新时间: 2021-05-13 11:42   作者:fuluola