内部类特点
- 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问
该类 。 - 内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成
员之间可以互相访问。但外部类不能访问内部类的实现细节 ,例如内部类的成员变量 。 - 匿名内部类适合用于创建那些仅需要一次使用的类
内部类与外部类的区别
- 内部类比外部类可以多使用三个修饰符:
private
、protected
、static
,外部类不可以使用 这三个
修饰符 。 - 非静态内部类不能拥有静态成员。
非静态内部类
- 内部类一定是放在另一个类的类体部分(也就是类名后的花括号部分)定义
- 在非静态内部类里可以直接访问外部类的 private 成员
- 非静态内部类不能拥有静态成员
- 静态内部类的成员只在非静态内部类范围内是可知的,并不能被外部类直接使用。如果外部类需要访问非静态内部类的成员,则必须显式创建非静态内部类对象来调用访问其实例成员
- 不允许在外部类的静态成员中直接使用非静态内部类
外部类和非静态内部类的成员调用
当内部类的方法调用变量时,
首先在该方法中查找该变量名的局部变量
在内部类中查找该变量名的成员变量
在内部类所在的外部类中查询该变量名的成员变量
如果没有查找到则编译错误
如果内部类方法中的局部变量、内部类成员变量、外部类成员变量的名字相同,则可通过使用 this、外部类类名.this作为限定来区分 。
外部类不能直接访问内部类的成员,需要创建内部类的实例
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(); } }
输出:
静态内部类
- 使用 static 修饰的成员内部类是静态内部类
- 如果使用 static 来修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。因此使用 statlc 修饰的内部类被称为类内部类,有的地方也称为静态内部类 。
- 静态内部类可以包含静态成员, 也可以包含非静态成员
- 根据静态成员不能访问非静态成员的规则,静态内部类不能访问外部类的实例成员,只能访问外部类的类成员
- 静态内部类的实例方法也不能访问外部类的实例成员,只能访问外部类的静态成员
外部类和静态内部类的成员调用
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修饰。
匿名内部类
创建匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用。匿名内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类,或实现一个接口。
- 匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部类的对象。因
此不允许将匿名内部类定义成抽象类。 - 匿名内部类不能定义构造器 。由于匿名内部类没有类名,所以无法定义构造器,但匿名内部类
可以定义初始化块 ,可以通过实例初始化块来完成构造器需要完成的事情 。 - 当通过实现接口来创建匿名内部类时 , 匿名内部类也不能显式创建构造器,因此匿名内部类只有一
个隐式的无参数构造器,故 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; // 不能重新赋值
}
}