找往期文章包括但不限于本期文章中不懂的知识点:
个人主页:我要学编程(ಥ_ಥ)-CSDN博客
所属专栏:JavaSE
目录
内部类的概念
内部类的种类
使用举例:
1. 静态内部类:
2. 实例内部类
3. 局部内部类
4. 匿名内部类
内部类的概念
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提供服 务,那么这个内部的完整结构最好使用内部类。在 Java 中,可以将一个类定义在另一个类或者一个方法的内部, 前者称为内部类,后者称为外部类。内部类也是封装的一种体现。
最常见的语法格式:
// OutClass是外部类
// InnerClass是内部类
public class OutClass {
//……
class InnerClass{
//……
}
//……
}
注意:内部类和外部类共用同一个 .java后缀 的源文件,但是经过编译之后,内部类会形成单独的字节码文件。 如下:
细心的小伙伴应该也已经发现了:内部类的类名前面有其所在的外部类类名加上一个$符号。这个就是用来区分的。就是告诉我们,A这个类是Test这个类中的内部类。
那么外部类和不同的类相比,有什么变化呢?其实没什么变化,我们就可以把内部类看成是外部类的一个成员变量,仅仅只是多个成员变量而已。因此可以把内部类看成是一个成员变量。
内部类的种类
内部类其实有很多种。 根据其所处的位置不同,分为3种。
1. 实例内部类。其所处的位置是成员变量的位置,并且没有被 static 修饰。
// 外部类
public class Test {
// 实例内部类
class A {
}
}
2. 静态内部类。同样是在成员变量的位置,不过有个 static 修饰它。
public class Test {
// 静态内部类
static class B {
}
}
3. 局部内部类。在方法内定义的类。这种类用的比较少,因为它的作用域有限。
public class Test {
public static void main(String[] args) {
// 局部内部类
class C {
}
}
}
注意:局部内部类不能被 static 修饰,就和局部变量一样。被 static 修饰的都是类的属性和方法了,其作用域都已经改变了,和 “局部” 两个字发生了冲突。
4. 还有一种不是根据所处的位置定义的,那就是匿名内部类。
匿名内部类没有名字,直接在创建对象时定义并实例化,常用于实现接口或继承抽象类。虽然匿名内部类并非强制要求在方法中使用,实践中几乎总是将其放在方法内以体现其临时性和局部性,便于管理和理解代码。
public class Test {
public static void main(String[] args) {
// 匿名内部类
new Test() {
};
}
}
使用举例:
1. 静态内部类:
// 外部类
public class Test {
public static int a = 1;
public static int b = 2;
public static int c = 3;
// 静态内部类
static class B {
public static int d = 4;
public static int e = 5;
public static int a = 100; // 和外部类的成员变量名相同
public int f = 6;
public static void main(String[] args) {
// 由于main方法时静态方法,不创建对象的情况下,只能访问静态的东西。
// 因此变量全部都是static修饰的。
// 当外部类和内部类同名时,要通过外部类对象的引用或者类名来访问
System.out.println(Test.a);
System.out.println(a);
System.out.println(b);
System.out.println(c);
System.out.println(d);
System.out.println(e);
// 虽然静态内部类中可以有非静态的成员变量和方法,但是却还是不能在静态方法中使用
// System.out.println(f);
}
}
}
运行结果:
如果要在外部访问静态内部类怎么做呢?其实就是和访问静态成员变量是一样的。通过类名来访问。如下:
2. 实例内部类
// 外部类
public class Test {
public int a = 1;
public static int b = 2;
public int c = 3;
public int d = 4;
// 实例内部类
class B {
public int f = 5;
public int e = 6;
public int a = 100;
public void func(){
// 同样有同名的变量是优先访问自己的
System.out.println(a);
// 如果想要访问外部的,得通过 外部类名称.this.同名成员名字 的方法
System.out.println(Test.this.a);
}
}
}
如果想要创建内部类对象,该怎么做呢?
public class TestDrive {
public static void main(String[] args) {
// 通过外部类访问内部类
// 法一:
Test.B tb = new Test().new B();
// 法二:
Test test = new Test();
test.B tb = tset.new B();
tb.func();
}
}
我们把这个实例内部类看成是一个实例成员变量就好了,用访问实例成员的方法来访问实例内部类。而实例成员是通过对象的引用来访问的。
既然类型是通过外部类来访问内部类的,那么对象的创建也应如此:先创建外部类对象,再通过外部类对象来创建内部类对象。
注意:
1. 外部类中的任何成员都可以在实例内部类方法中直接访问。
2. 在实例内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员,必须:外部类名 称.this.同名成员 来访问。因为实例内部类本身也有一个属于自己的 this 。
3. 实例内部类对象必须在先有外部类对象前提下才能创建。
5. 实例内部类的非静态方法中包含了一个指向外部类对象的引用。这个引用不是显式地在代码中声明的,而是由Java编译器自动添加的。在内部类的方法中,你可以直接使用外部类的成员变量和方法,就像它们是内部类自己的一样。实际上,编译器会在内部类的方法代码中插入必要的代码来通过这个隐含的引用访问外部类的成员。
6. 外部类中,不能直接访问实例内部类中的成员,如果要访问必须先要创建内部类的对象。因为内部类中的成员作用域是只在内部类中。可以理解为在另一个类中访问一个与其不相干的类,自然要通过对象的引用来访问,也就是需要创建一个对象。
3. 局部内部类
// 外部类
public class Test {
public static void main(String[] args) {
// 局部内部类
class B {
public int a;
public int b;
public void func() {
System.out.println("这是内部类中的方法...");
}
}
// 只能在定义的方法内部使用
B b = new B();
b.func();
}
}
注意:
1. 局部内部类既然是在方法内部定义的,那么它们的作用域也只能是在方法内部。
2. 局部内部类不能被public 、 static 等修饰符修饰。因为public 、static 修饰的都是类中的变量或者方法,怎么可能会去修饰局部变量的呢?
4. 匿名内部类
虽然匿名内部类多是在有抽象类和接口的情况下使用,但并不意味着不能普通类不能有匿名内部类。
普通类:
// 普通类
class A {
public void func() {
System.out.println("这是普通类的方法...");
}
}
// 外部类
public class Test {
public static void main(String[] args) {
// 这是匿名对象,也就是创建了一个没有对象引用的对象
// 正因如此,这个对象只能使用一次,匿名内部类同样如此
new A();
// 匿名内部类
new A(){
// 匿名内部类的使用就是为了重写抽象类和接口的方法
@Override
public void func() {
super.func(); // 可以理解为:继承这个普通类之后,又重写了其中的方法
}
}.func(); // 这里就是在调用这个对象的方法
}
}
抽象类:
// 抽象类
abstract class B {
public abstract void func();
}
// 外部类
public class Test {
public static void main(String[] args) {
new B(){
@Override
public void func() {
System.out.println("可以理解为:继承了B这个抽象类,并重写了B这个抽象类中的方法");
}
}.func();
}
}
接口:
// 接口
interface C {
void func();
}
// 外部类
public class Test {
public static void main(String[] args) {
new C(){
@Override
public void func() {
System.out.println("可以理解为:实现了C这个接口,并重写了C这个接口中的方法");
}
}.func();
}
}
上面就是匿名内部类的使用方法。
注意:
1. 在匿名内部类中可以使用对象的引用去接收,但是不能再其后面调用方法了,而是要通过对象的引用来调用方法。如下(注意对比):
好啦!本期 初始Java篇(JavaSE基础语法)—— 内部类 的学习之旅就到此结束啦!我们下一期再一起学习吧!