内部类特点

  1. 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问
    该类 。
  2. 内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成
    员之间可以互相访问。但外部类不能访问内部类的实现细节 ,例如内部类的成员变量 。
  3. 匿名内部类适合用于创建那些仅需要一次使用的类

内部类与外部类的区别

  1. 内部类比外部类可以多使用三个修饰符: privateprotectedstatic ,外部类不可以使用 这三个
    修饰符 。
  2. 非静态内部类不能拥有静态成员。

非静态内部类

  1. 内部类一定是放在另一个类的类体部分(也就是类名后的花括号部分)定义
  2. 在非静态内部类里可以直接访问外部类的 private 成员
  3. 非静态内部类不能拥有静态成员
  4. 静态内部类的成员只在非静态内部类范围内是可知的,并不能被外部类直接使用。如果外部类需要访问非静态内部类的成员,则必须显式创建非静态内部类对象来调用访问其实例成员
  5. 不允许在外部类的静态成员中直接使用非静态内部类

外部类和非静态内部类的成员调用

当内部类的方法调用变量时,

  1. 首先在该方法中查找该变量名的局部变量

  2. 在内部类中查找该变量名的成员变量

  3. 在内部类所在的外部类中查询该变量名的成员变量

  4. 如果没有查找到则编译错误

  5. 如果内部类方法中的局部变量、内部类成员变量、外部类成员变量的名字相同,则可通过使用 this、外部类类名.this作为限定来区分 。

  6. 外部类不能直接访问内部类的成员,需要创建内部类的实例

    public class InnerClassTest {
     public static void main(String[] args) {
         Cow cow = new Cow(333);
         cow.test();
     }
    }
    
    class Cow {
    
     private double weight;
     private String var = "外部类成员变量";
    
     public Cow() {
     }
    
     public Cow(double weight) {
         this.weight = weight;
     }
    
     public double getWeight() {
         return weight;
     }
    
     public void setWeight(double weight) {
         this.weight = weight;
     }
    
     private class CowLeg {
    
         private String color;
         private double length;
         private String var = "内部类成员变量";
    
         public CowLeg() {
         }
    
         public CowLeg(String color, double length) {
             this.color = color;
             this.length = length;
         }
    
         public String getColor() {
             return color;
         }
    
         public void setColor(String color) {
             this.color = color;
         }
    
         public double getLength() {
             return length;
         }
    
         public void setLength(double length) {
             this.length = length;
         }
    
         private void info() {
             String var = "局部变量";
             System.out.println("CowLeg color:" + color + "--" + "length:" + length);
             System.out.println("Cow weight:" + weight);
                //同名变量引用
             System.out.println("外部类成员变量:" + Cow.this.var);
             System.out.println("内部类成员变量:" + this.var);//或CowLeg.this.var
             System.out.println("局部变量:" + var);
         }
     }
    
     public void test() {
            //外部类引用内部类成员
         new CowLeg("red", 20).info();
     }
    
    }

    输出:

静态内部类

  1. 使用 static 修饰的成员内部类是静态内部类
  2. 如果使用 static 来修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。因此使用 statlc 修饰的内部类被称为类内部类,有的地方也称为静态内部类 。
  3. 静态内部类可以包含静态成员, 也可以包含非静态成员
  4. 根据静态成员不能访问非静态成员的规则,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员
  5. 静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的静态成员

外部类和静态内部类的成员调用

public class StaticInnerClassTest {

    public static void main(String[] args) {
        new OuterClass.InnerClass().innerClassMethod();
        new OuterClass().outerClassMethod();
    }

}

class OuterClass {

    private String var1 = "外部类成员变量";
    private static String var2 = "外部类静态变量";

    static class InnerClass {
        // 静态内部类可以由静态变量和非静态变量
        private String innerVar = "静态内部类成员变量";
        private static String staticInnerVar = "静态内部类静态变量";

        public void innerClassMethod() {
            // System.out.println(var1); // 静态内部类无法调用外部类成员变量
            System.out.println(var2); // 静态内部类调用外部类类变量
            System.out.println(new OuterClass().var1); // 静态内部通过外部类的实例调用外部类成员变量
        }
    }

    public void outerClassMethod() {
        System.out.println(InnerClass.staticInnerVar);// 外部类调用内部类类变量
        System.out.println(new InnerClass().innerVar);// 外部类调用内部类成员变量
    }

}

输出:

局部内部类

局部内部类定义在方法中,且仅在该方法中有效。不能使用访问权限控制符和static修饰。

匿名内部类

创建匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用。匿名内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类,或实现一个接口。

  1. 匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部类的对象。因
    此不允许将匿名内部类定义成抽象类。
  2. 匿名内部类不能定义构造器 。由于匿名内部类没有类名,所以无法定义构造器,但匿名内部类
    可以定义初始化块 ,可以通过实例初始化块来完成构造器需要完成的事情 。
  3. 当通过实现接口来创建匿名内部类时 , 匿名内部类也不能显式创建构造器,因此匿名内部类只有一
    个隐式的无参数构造器,故 new 接口名后的括号里不能传入参数值 。
    但如果通过继承父类来创建匿名内部类时, 匿名 内部类将拥有和父类相似的构造器,此处的相似指
    的是拥有相同的形参列表 。
//调用接口创建匿名内部类
public class AnonymousInnerClassTest {

    public static void eatFruit(Fruit fruit) {
        System.out.println("fruit color :" + fruit.getColor() + " -- fruit taste :" + fruit.getTaste());
    }

    public static void main(String[] args) {
        eatFruit(new Fruit() {

            @Override
            public String getColor() {
                return "red";
            }

            @Override
            public String getTaste() {
                return "sweet";
            }

        });
    }
}

interface Fruit {

    public String getColor();

    public String getTaste();
}

//调用抽象类创建匿名内部类
class Anonymouslnner {
    public void test(Device d) {
        System.out.println("购买了一个 " + d.getName() + " ,花掉了 " + d.getPrice());
    }

    public static void main(String[] args) {
        Anonymouslnner ai = new Anonymouslnner();
        // 调用有参数的构造器创建 Device 匿名实现类的对象
        ai.test(new Device(" 电子示波器") {
            public double getPrice() {
                return 67.8;
            }
        });
        // 调用无参数的构造器创建 Device 匿名实现类的对象
        Device d = new Device() {
            // 初始化块
            {
                System.out.println(" 匿名内部类的初始化块 .. . ");
            }

            // 实现抽象方法
            public double getPrice() {
                return 56.2;
            }

            // 重写父类的实例方法
            public String getName() {
                return "键盘";
            }
        };
        ai.test(d);
    }
}

abstract class Device {
    private String name;

    public abstract double getPrice();

    public Device() {
    }

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

    public String getName() {
        return name;
    }

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

调用接口创建输出:

调用抽象类创建输出:

JDK8之前匿名内部类访问的局部变量必须使用final修饰,JDK8不在需要final修饰,但是会默认改变量式final修饰的,即在匿名内部类中访问的局部变量不能再被赋值。

interface A {
    void test();
}

class ATest {
    public static void main(String[] args) {
        int age = 8; // ①
        A a = new A() {
            public void test() {
                // 在 Java 8 以前下面语句将提示错误 : age 必须使用 final 修饰
                // 从 Java 8 开始,匿名内部类、局部内部类允许访问非 final 的局部变量
                System.out.println(age);
            }
        };
        a.test();
        //age = 1; // 不能重新赋值
    }
}
02-11 09:27