1.内部类的概念
将一个类定义在另一个类的里面或者定义在一个方法的内部,把该类就称之为内部类;内部类也是对封装的一种体现。
public class A {
// A 就称为外部类
// B 就称为内部类
class B{
}
}
需要注意的是:
(1)定义在class 类名{}花括号外部的,即使是在一个文件里,也不能称为内部类;
// 同一个Java源文件中可以有多个类
// 但这种方式不能被称为内部类
// 最多只有一个被public修饰
public class Test{
}
class B{
}
就如同 Test
和 B
就是两个独立的类,彼此之前并没有关系;
(2) 内部类和外部类共用同一个java的源文件,但是经过编译之后,内部类会形成自己单独的字节码文件;
就如同定义里面的类A
和类B
,在经过编译之后,就会产生两个字节码文件。
如下所示:
2.内部类的分类及范例
根据内部类定义位置的不同,可以将内部类分为成员内部类
、局部内部类
和匿名内部类
,而成员内部类又根据是否被static
修饰分为普通内部类
和静态内部类
;
2.1 成员内部类
2.1.1 普通内部类
未被static
修饰的成员内部类就称为普通内部类;
// 测试普通内部类
public class TestInnerClass {
//外部类的成员变量:a,b
int a;
int b;
public void methodA(){
//methodB() ; 编译失败 原因:外部类不能直接访问内部类成员
//外部类若要访问内部类成员,
//1)先创建内部类对象;2)通过该对象访问内部成员
InnerClass ic=new InnerClass();
ic.methodB();
}
//成员内部类:普通内部类
class InnerClass{
//内部类的成员变量:b,this&0(编译器添加的),将来指向外部类对象
int b;
void methodB(){
a=10;
methodA();
b=20; //给内部类自己的成员变量赋值(就近原则)
// 当内部类成员变量与外部类成员变量相同名字时,
//如何给外部的b赋值?
TestInnerClass.this.b=200;
}
}
public static void main(String[] args) {
// 如何使用普通内部类
//先创建对象ti,通过对象访问内部成员
TestInnerClass ti=new TestInnerClass();
ti.methodA();
// InnerClass ic=new InnerClass(); 编译失败
// 只能通过借助外部类的对象来创建内部类的对象
InnerClass ic=ti.new InnerClass();
ic.methodB();
}
}
当打断点运行时,就可以看见如下所示界面,有创建的两个对象ti
和ic
,在程序的外部类中,我们定义了两个成员变量a
和b
,在内部类中,我们自己只定义了一个b
,但我们却看到了两个,多的那个this&0
就是编译给我们自动添加的,用来指向外部类的对象(即:编译器会给内部类维护一个外部类对象的引用)(可能有点绕)
从上面可得到以下:
(1)外部类中的任何成员都可以在普通内部类方法中直接被访问
;
(2)普通内部类所处的成员与外部类成员位置相同,因此也受public、private等访问限定符的约束
;
(3) 在内部类方法中访问同名的成员时,优先访问自己的,如果要访问外部类同名的成员
,需采用
:
外部类名称.this.同名成员名
(4)普通内部类对象必须在先有外部类对象前提下才能被创建
;
(5)外部类中不能直接访问内部类中的成员,如果要访问必须先要创建内部类的对象
;
2.1.2 静态内部类
被static
修饰的内部成员类就称为静态内部类;
//测试静态内部类
public class OutClass {
private int a;
//静态成员变量b
static int b;
public void methodA(){
a=10;
System.out.println(a);
}
public static void methodB(){
System.out.println(b);
}
// InnerClass 就是静态内部类
static class InnerClass{
public void methodInner(){
// 在内部类中只能访问外部类的静态成员变量及方法
// a=100; 编译失败,原因:a不是外部类中的静态成员变量
// methodA(); //编译失败,原因:methodA()不是静态类成员方法
b=100;
methodB();
}
}
public static void main(String[] args) {
//静态成员变量访问:类名.成员变量名
System.out.println(OutClass.b);
// 静态内部类对象的创建
//不需要借助外部类对象
InnerClass ic=new InnerClass();
// 静态成员方法访问
innerClass.methodInner();
}
}
运行结果:
0
100
由上我们可以知道:
(1) 在内部类中只能访问外部类中的静态成员(包括静态成员变量和静态成员方法)
;
(2)创建内部类对象时,不需要先创建外部类对象
;
(3) 成员内部类,经过编译之后会生成独立的字节码文件,命名格式为:外部类名称$内部类名称
;
2.2 局部内部类(不常用)
定义在外部类的方法体或者代码块{}中,该种内部类只能在其定义的位置进行使用,其他位置均不能使用;
//测试局部内部类
public class OutClass {
int a=20;
public void method(){
int b=10;
// InnerClass 就是局部内部类
// 定义在外部类(OutClass)的(method)方法体中
class InnerClass{
//static int c; 编译失败 不能在局部内部类中定义静态成员
public void methodInnerClass(){
System.out.println(a);
System.out.println(b);
}
}
// 只能在该方法体内部使用,其他位置不行
InnerClass innerClass=new InnerClass();
innerClass.methodInnerClass();
}
public static void main(String[] args) {
// OutClass.InnerClass.ic=null;
// 编译失败,原因是:在外部使用了局部内部类
}
}
局部内部类不能被public
、static
等访问限定符修饰,若加上修饰符,就会报错,光标点上去之后会显示提示信息,提示信息如下所示:
由上面可以知道:
(1)局部内部类只能在所定义的方法体内部使用
;
(2)不能被public、static等修饰符修饰
;
(3)不能在局部内部类中定义静态成员
;
2.3 匿名内部类
匿名内部类属于局部内部类,不同的是,它是没有名字的局部内部类,通常和匿名对象一起使用,(接口部分为大家仔细介绍该类的使用);
2.4 对象打印(补充一个)
对于私有的成员变量,我们都需要提供Set
和Get
的访问方式,当私有成员较多时,可以采用IDEA
直接生成,生成方式:alt+insert+Set
或 alt+insert+Get
;
打印时:
(1)基本类型打印:结果为设定的值;
(2)引用类型打印:打印的是相当于对象的地址,而不是对象本身;
范例:定义一个Person类
public class Person {
String name;
private String gender;
private int age;
public Person(String name, String gender, int age) {
this.name = name;
this.gender = gender;
this.age = age;
}
public static void main(String[] args) {
//基本类型
int a=10;
System.out.println(a);
//引用类型
Person p=new Person("韩磊","男",13);
System.out.println(p);
}
}
运行结果:
10
day20210913.Person@1b6d3586
如果想要打印对象中内容,如该如何处理呢?
方法: 重写 toString()
快捷键:(alt+insert+toString
)
使用toString()
后,再次运行,结果如下:
10
Person{name='韩磊', gender='男', age=13}
欢迎批评指正!