内部类主要定义在类的内部,定义内部类的作用,主要是因为不希望该类作为大家共同使用访问的类。
成员内部类
- 成员内部类就是作为外部类的成员(没有static修饰),可以直接使用外部类的所有成员和方法,即使是private。
- 成员内部类位于外部类内部,可以直接调用外部类的所有方法(静态方法和非静态方法)
- 成员内部类可以添加任意访问修饰符(public protected 默认 private)因为它的地位就是一个成员。
- 外部类要访问内部类的所有成员变量或方法,则需要通过内部类的对象来获取
- 注意:成员内部类不能含有 static 的变量和方法。
成员内部类的定义如下: ```java public class 外部类{ public class 内部类{
} }
- 内部类的实例化:
```java
外部类 对象 = new 外部类();
外部类.内部类 对象2=对象.new 内部类();
- 如果外部类和成员内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类.this.成员)去访问。 ```java package cn.java.money.innerclass.demo04;
public class Outer { private int i = 10; String str = “str”; static String sta = “static”; static Inner inner = new Outer().new Inner();
private void outer_method() {
//外部类访问成员内部类,创建对象再访问, 可以访问成员内部类的私有属性和方法
Inner inner = new Inner();
inner.inner_method();
System.out.println(inner.i);
}
public static void outer_method_static() {
// 局部变量都不能用static修饰
//static int i = 100;
// 外部类的静态方法中 不能使用内部类
// Inner inner = new Inner();
}
public class Inner {
//成员内部类中不能使用static,
//static int i = 100;
// public static void inner_method_static() { }
private int i = 100;
private void inner_method() {
// 成员内部类可以直接使用外部类的所有成员
System.out.println(i);
System.out.println(str);
System.out.println(sta);
outer_method();
outer_method_static();
}
// 其他第三方的类可以访问
public void inner_method2() {
// 成员内部类可以直接使用外部类的所有成员
System.out.println(i);
System.out.println(str);
System.out.println(sta);
//outer_method(); 会形成循环调用
// outer_method_static();
}
}
public Inner getInnerInstance() {
return new Inner();
}
}
```java
package cn.java.money.innerclass.demo04;
public class Test {
public static void main(String[] args) {
//外部其他类,使用成员内部类的两种种方式
// 方式一
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
inner.inner_method2();
// 方式二 在外部类中,编写一个方法,可以返回Inner对象
Outer.Inner innerInstance = outer.getInnerInstance();
//简写
Outer.Inner inner1 = new Outer().new Inner();
Outer.Inner innerInstance1 = new Outer().getInnerInstance();
}
}
静态内部类
- 被static修饰的成员内部类
- 静态内部类可以直接访问外部类的所有静态成员,包括私有的,但不能直接访问非静态成员
- 静态内部类可以添加任意访问修饰符
- 作用域:整个外部类
- 外部类访问静态内部类
- 对于静态内部类的静态属性和方法,可用通过类名访问
- Inner.inner_method_static(); // 在外部类中可以省略内部类名
- 对于静态内部类的非静态方法和属性,可以通过创建对象访问
- Inner inner = new Inner();
- 对于静态内部类的静态属性和方法,可用通过类名访问
- 外部其他类访问静态内部类(这时候访问权限起作用了)
- 对于静态内部类的静态属性和方法,可用通过类名访问
- Outer.Inner.inner_method_static();
- 对于静态内部类的非静态方法和属性,可以通过创建对象访问
- Outer.Inner inner = new Outer.Inner();
- 对于静态内部类的静态属性和方法,可用通过类名访问
- 如果外部类和静态内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类.成员)去访问。 ```java package cn.java.money.innerclass.demo05;
public class Outer { static String sta = “static”;
private void outer_method() {
//外部类访问静态成员内部类,创建对象再访问, 可以访问成员内部类的私有属性和方法
Inner inner = new Inner();
inner.inner_method();
System.out.println(inner.i2);
//静态内部类的静态方法和属性,外部类可以直接访问 包括私有的
System.out.println(Inner.i);
Inner.inner_method_static();
}
public static void outer_method_static() {
// 局部变量都不能用static修饰
//static int i = 100;
// 外部类的静态方法中 可以使用静态内部类
Inner inner = new Inner();
System.out.println(Inner.i);
Inner.inner_method_static();
}
public static class Inner {
//静态成员内部类中可以使用static,
static int i = 100;
public static void inner_method_static() {
}
private int i2 = 100;
private void inner_method() {
// 静态内部类可以直接访问外部类的所欲静态成员,包括私有的,但不能直接访问非静态成员
System.out.println(sta);
outer_method_static();
}
public void inner_method_public() {
}
}
public static Inner getInnerInstance() {
return new Inner();
}
}
```java
package cn.java.money.innerclass.demo05;
public class Test {
// 前提 外部类访问静态内部类
public static void main(String[] args) {
Outer.Inner inner = new Outer.Inner();
//这里内部类的成员访问修饰符 就起作用了
inner.inner_method_public();
//不能通过静态内部了的实例调用静态方法
//可以通过静态内部类直接访问
Outer.Inner.inner_method_static();
System.out.println(Outer.Inner.i);
Outer.Inner innerInstance = Outer.getInnerInstance();
}
}
局部内部类
- 局部内部类是指内部类定义在方法和作用域内。通俗来说,就是在外部内的方法中定义的内部类就是局部内部类。
- 局部内部类不能添加访问修饰符,但是可以使用final修饰,因为局部内部类还是一个类,可以被继承。
- 局部内部类的作用域,仅仅在定义它的方法或代码块中。
- 局部内部类和局部变量的位置等价,因此可以在代码块中定义。因此,局部内部类的实例化也只能在方法中进行。
- 局部内部类可以访问外部类的所有成员,包括私有成员。
- 局部内部类可以直接访问外部类(包括局部内部类所在方法的局部变量)。
- 外部类(也就是当前方法中,即作用域)访问局部内部类,要先new 局部内部类。
- 局部内部类的调用,其实就是外部类调用局部内部类所在的方法。
- 外部其他类不能访问局部内部类。
- 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类.this.成员)去访问。
- 局部内部类中不能有 static 修饰的 成员变量 和方法 和 静态代码块 ,所以局部内部类的加载只能通过new 局部内部类来加载。
- 局部内部类可以定义在静态和非静态方法内。
- 注意:局部内部类方法中想要使用局部变量,该变量必须声明为 final 类型,但是Java8以后,编译器会自定添加final。
对于局部内部类,只有在方法的局部变量被标记为final或局部变量是effctively final的,内部类才能使用它们
匿名内部类
匿名内部类是一个类,是类就有类名,只是这个类名在编译器生成,我们无法使用。
- 匿名内部类编译以后就是当前类的一个内部类。
- 每创建一个匿名内部类,编译器就会生成一个对应的内部类。
- 匿名内部类的本质是抽象类和接口的实现。
- 匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,一次从语法上看,它既有定义类的特征,也有创建对象的特征。
- 匿名内部类可以直接访问外部类的所有成员,包含私有的。
- 不能添加访问修饰符,因为类时由编译器生成的,你没法添加,但是接受匿名内部类对象的变量可以。
- 匿名内部类的作用域 可以定义由成员变量接受,也可以由局部变量接受,如果么有成员变量接受,就只能定义在方法或代码块中
- 其他外部类无法访问匿名内部类。
- 如果外部类和匿名内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类.this.成员)去访问。
- 匿名内部类的成员,只能自己访问,外部类无法访问。
- 匿名内部类的使用
- 匿名内部类当做实参直接传递
- 匿名内内部类可以当作方法的返回值。 ```java package cn.java.money.innerclass.demo03;
public class Outer { int i = 10; String str = “str”; static String sta = “static”;
public void outer_method() {
//外部类无法访问匿名内部类
}
public static void outer_method_static() {
}
//编译完成以后,编译器给会每一个匿名内部类分配一个类名(类名格式:外部类$N)
//当我们new A()的时候,JVM知道我们要实例化的内部类是哪一个,也就是A接口的实现类
//匿名内部类只能使用一次,是因为我们每 new一个匿名内部类(new A()),编译器就会生成一个对应的内部类,
//因此 new A() 的 getClass() 和 再次new A()的 getClass()的值是不一样的,可能一个是 外部类$1,另外一个是$3
//指向匿名内部类实例的引用可以反复使用
A a = new A() {
@Override
public void cry() {
System.out.println(a.getClass());//查看运行时类型,同时说明匿名内部类不是真的没有类名
//匿名内部类可以直接访问外部类的所有成员,包含私有的。
//内部类是随着外部类的加载而加载,因此内部类可以访问所有的外部类成员
System.out.println(i);
System.out.println(b);
System.out.println(str);
System.out.println(sta);
outer_method();
outer_method_static();
}
};
//A接口只有一个抽象方法, 可以用lamda
A a1 = () -> {
System.out.println(a.getClass());
};
//匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,一次从语法上看,它既有定义类的特征,也有创建对象的特征。
B b = new B() {
@Override
public void cry() {
int i = 10;
//如果外部类和匿名内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类.this.成员)去访问。
System.out.println(Outer.this.i);
}
@Override
public void run() {
}
};
static A a2 = new A() {
@Override
public void cry() {
System.out.println(a2.getClass());
}
};
// 这个类的运行时类型为 Father
Father father = new Father("jack");
//基于类的内部类演示, 这样的内部类其实就是当前类的子类
//这个类的运行时类型为 Father$N
Father father2 = new Father("jack") {
@Override
public void tet() {
System.out.println("我是Father的匿名内部类的test");
}
};
public void test() {
//匿名内部类,想要直接调用新增的方法 如下
new Father("") {
public void ff() {
System.out.println("我是匿名内部类新增的方法,或者说是拓展的功能");
}
}.ff();
}
public static void main(String[] args) {
//外部类调用匿名内部类中方法
Outer outer = new Outer();
outer.a.cry();
Outer.a2.cry();
outer.father.tet();
System.out.println(outer.father.getClass());
outer.father2.tet();
System.out.println(outer.father2.getClass());
}
}
interface A { void cry(); }
interface B { void cry();
void run();
}
class Father { private String name;
public Father(String name) {
this.name = name;
}
public void tet() {
System.out.println("Father 的test方法");
}
}
class C { public void test(A a) { a.cry(); }
public static void main(String[] args) {
//匿名内部类当做实参直接传递
new C().test(Outer.a2);
new C().test(new A() {
@Override
public void cry() {
}
});
new C().test(() -> {
});
}
}
```
特别注意:
在使用匿名内部类时,要记住以下几个原则。
- 匿名内部类不能有构造方法。
- 匿名内部类不能定义任何静态成员,方法和类。
- 匿名内部类不能使用public,protected,private,static。
- 只能创建匿名内部类的一个实例。
- 一个匿名内部类一定时在 new 后面,用其隐含实现一个接口或实现一个类。
- 因匿名内部类为局部内部类,所以,局部内部类的所有限制都对其有效。
- 内部类只能访问外部类的静态变量或静态方法。
- 内部类当中的 this 指的是匿名内部类本身,如果使用外部类中的 this,则“外部类.this”。
使用内部类的主要原因有:
- 内部类可以访问外部类中的数据,包括私有的数据。
- 内部类可以对同一个包中的其他类隐藏起来。
- 当想要定义一个回调函数且不想编写大量代码时,使用匿名(anonymous)内部类比较便捷。
- 减少类的命名冲突。
静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。没有这个引用就意味着:
1、 它的创建是不需要依赖于外围类的。
2、 它不能使用任何外围类的非static成员变量和方法。