成员内部类
成员内部类:顾名思义、成员中的类(类种类)。
同时在编译后会出现【外部类名.class 和 外部类名$内部类名.class文件】这两个字节码文件
话不多少先上代码介绍:
class Demo {
class Demo1 {
}
}
特点:
- 内部类写在一个类中,同时创建对象需要依赖于外部类对象。
- 可以为外部类提供必要的内部功能组件。
- 内部类可以直接访问外部类的私有成员,从而不会去破坏封装。
- 编译之后会生成独立的字节码文件。(详情看下面)
内部类访问外部类
1.可以访问任何属性和方法【包括私有的、静态的】
访问方式有2种 :直接写属性名或者是外部类.this.属性【方法】
1.1直接写属性名
这种方式有个缺点:如果内部类定义的属性【方法】和外部类一样,会发生隐藏现象,取得的是内部类的值,若想取得外部类的值,采用外部类.this.属性【方法】
1.2若采用直接写属性名获得外部类值,其实本质还是外部类.this.属性
public class InnerClass {
public static void main(String[] args) {
//创建内部类对象
TestInner.Inner in = new TestInner().new Inner();
in.print();
}
}
class TestInner {
private int a = 2;
class Inner {
public void print() {
System.out.println(TestInner.this.a);
System.out.println(a);
}
}
}
//结果:2
// 2
【注意】:
1.可以定义非静态属性和方法,不可以定义static修饰的属性和方法,可以定义static final修饰的编译期变量【不用加载类的字节码】
1.1为什么不可以定义static修饰的属性和方法?
首先内部类是外部类的一个成员,只有当外部类初始化的时候,内部类才能初始化,静态变量属于类级别,在类加载的时候就初始化,所以两者本身在语法上就有矛盾。
1.2为什么可以定义static final修饰的变量?
首先要先知道static final修饰的变量叫做常量,常量分为编译期常量和非编译期常量
编译期常量:在程序编译阶段【不需要加载类的字节码】,就可以确定常量的值
非编译期常量:在程序运行阶段【需要加载类的字节码】,可以确定常量的值
public class OutterClass {
class Inner{
int a = 0;//非静态变量
static int b = 0;//静态变量【报错!!】
static final int c = 0;//编译期常量,不需要类加载
static final Integer d = new Integer(2);//非编译期常量,需要类加载【报错!!】
}
public static void main(String[] args) {
System.out.println(OutterClass.Inner.b);
System.out.println(OutterClass.Inner.c);
System.out.println(OutterClass.Inner.d);
}
}
b属性是静态的,它需要加载类的字节码Inner类,由于它是在OutterClass内部,需要外部类实例化才能加载,但是调用处没有实例化外部类,所以Inner也不会类加载,所以报错!!
c属性是编译期常量,它不需要加载类的字节码Inner类,所以不会报错
d属性是非编译期常量。它需要加载类的字节码Inner类,所以报错!!
2.内部类可以被任何访问修饰符修饰
因为它也是外部类的一个成员。
静态内部类
简介:静态内部类 顾名思义内部静态的类,即:被static修饰的类【因为内部类是外部类的成员,因此内部类可以被任何访问修饰符所修饰,同时也可以被static所修饰。】
【注意】:
1、因为静态内部类是静态的,因此同理无需依赖于外部类对象的创建才可以做访问。
2、静态内部类可以访问外部类的所有属性和方法。
3、静态内部类中实例方法必须通过创建内部类对象才可以访问。
class Demo {
public static void main(String[] args) {
Outer.Inner in = new Outer.Inner();
in.m1();
Outer.Inner.m2();
}
}
class Outer {
private int a = 1;
static class Inner {
public void m1() {
System.out.println(a);
}
public static void m2() {
System.out.println("我是静态内部类的静态方法,无法访问非静态属性。");
}
}
}
/**
* 执行结果:
* 1
* 我是静态内部类的静态方法,无法访问非静态属性。
*/
匿名内部类
简介:没有类名的内部类
interface AnonymousInnerClass {
default void m1() {
System.out.println("我是匿名内部类");
}
}
class Test {
//第一种创建对象时创建匿名内部类。
AnonymousInnerClass ais = new AnonymousInnerClass() {
public void m1() {
System.out.println("我是匿名内部类");
}
};
ais.m1();
//第二种直接new对象创建匿名内部类,同时调用里面的方法。
new AnonymousInnerClass() {
public void m1() {
System.out.println("我是匿名内部类");
}
}.m1();
//第三种匿名对象直接调用
finalAnonymousInner(new AnonymousInnerClass() {
public void m1() {
System.out.println("我是匿名内部类");
}
});
public static void finalAnonymousInner(AnonymousInnerClass ais) {
ais.m1();
}
}
局部内部类
简介:定义在外部类方法中,作用范围和创建对象范围仅限于当前方法。
1、局部内部类访问外部类当前方法中的局部变量时,因无法保障生命周期于自身相同,变量必须修饰为final(此时为常量,加载时位于【常量池】中)。
2、限制类的使用范围。
class Outer {
int a = 2;
public void m1() {
final int a = 1;
}
@Test
public void TestInner() {
int a = 3;
class Inner {
public void m2() {
System.out.println("访问外部类中的属性a = " + Outer.this.a);
System.out.println("访问外部类中的m1方法中的属性a = " + a);
System.out.println("访问外部类中的m2方法中的局部内部类Inner中的属性a = " + this.a);
}
}
Inner in = new Inner();//实例化局部内部类Inner的对象。
in.m2();//调用方法
}
}
/**
* 执行结果:访问外部类中的属性a = 2
* 访问外部类中的m1方法中的属性a = 1
* 访问外部类中的m2方法中的局部内部类Inner中的属性a = 3
/
同时再补充一点,在外部类如何访问静态内部类的非静态方法:
public class TestInnerClass {
public static void main(String[] args) {
Inner inner1 = new TestInnerClass().new Inner();
inner1.print();
inner1.print("inner1");
System.err.println(inner1.print(1));
Inner2 inner2 = Inner2.print();
inner2.print("inner2");
System.err.println(inner2.print(1));
}
public static void print(){
System.out.println("TestInnerClass");
}
public void print2(){
System.out.println("TestInnerClass");
}
class Inner {
public void print() {
System.out.println("inner1");
}
private void print(String b) {
System.out.println(b);
}
public int print(int a) {
return a;
}
}
static class Inner2 {
int a = 0;
public static Inner2 print() {
TestInnerClass.print();
TestInnerClass aClass = new TestInnerClass();
aClass.print2();
System.out.println("inner2");
return new Inner2();
}
private void print(String b) {
System.err.println(b);
}
public int print(int a) {
return a;
}
}
}