彻底理解内部类的使用(详细篇)

这是我参与8月更文挑战的第23天,活动详情查看:8月更文挑战

前言

内部类相信大家都应该用过,但我也相信大家应该都只是很简单的使用。

所以今天,就来详细讲解内部类的使用,废话不多说,我们先赶紧来看吧。

在普通类中使用内部结构

先来看个示例:

Teacher类里面有个内部类Student

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java复制代码public class Teacher {

private String msg = "我是一名老师";//只能在类内部访问

public void say() {
Student s = new Student();//实例化内部类的对象
s.print();
}

//在Teacher类中的内部类
class Student {
public void print() {
System.out.println(Teacher.this.msg); //msg是Teacher类里面的属性
}
}

}

测试类:

1
2
3
4
5
6
7
java复制代码public class Test {

public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.say();
}
}

输出结果:

1
2
3
vbnet复制代码我是一名老师

Process finished with exit code 0

从整体的代码结构来讲,内部类的结构并不合理,所以内部类本身最大的缺陷在于破坏了程序的结构,但是破坏需要有目的的破坏,那么它也一定会有其优势,如果要想更好的观察出内部类的优势,就可以将内部类拿到外面来。我将上面的代码Student拿出来。

Teacher 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码public class Teacher {

private String msg = "我是一名老师";//只能在类内部访问

public void say() {
Student student = new Student(this);//实例化内部类的对象
student.print();
}

public String getMsg() {
return this.msg;
}
}

Student 类:

1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码public class Student {

private Teacher teacher;

public Student(Teacher teacher) {
this.teacher = teacher;
}

public void print() {
System.out.println(this.teacher.getMsg()); //msg是Teacher类里面的属性
}

}

测试:

1
2
3
4
5
6
7
java复制代码public class Test {

public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.say();
}
}

输出结果:

1
2
3
vbnet复制代码我是一名老师

Process finished with exit code 0

我们折腾了半天主要的目的就是为了让Student这个内部类可以访问Teacher这个类中的私有属性,如果不用内部类的时候整体代码会非常的麻烦,所以可以得出内部类的优点:轻松的访问外部类中的私有属性

需要注意的是内部类虽然可以方便的访问外部类中的私有成员或私有方法,同理**,外部类也可以轻松访问内部类中的私有成员或私有方法**。

示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
java复制代码public class Teacher {

private String msg = "我是一名老师";//只能在类内部访问

public void say() {
Student student = new Student();//实例化内部类的对象
student.print();
System.out.println(student.info);
}

class Student {
private String info = "我是一名学生";

public void print(){
System.out.println(Teacher.this.msg);
}
}
}

public class Test {

public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.say();
}
}

输出结果:

1
2
3
4
java复制代码我是一名老师
我是一名学生

Process finished with exit code 0

使用了内部类之后,内部类与外部类之间的私有操作的访问就不再需要通过setter,getter以及其他的间接方式完成了,可以直接进行操作,但是需要注意的是,内部类本身也属于一个类,虽然在大部分情况下内部类往往是被外部类包裹的,但是外部依然可以产生内部类的实例化对象,而此时,内部类实例化对象的格式如下:

外部类.内部类 内部类对象 = new 外部类().new 内部类()

在内部类编译完成之后会自动形成一个Teacher$Student.class类文件,其中“$”这个符号换到程序中就变为“.”,所以内部类的全称:“外部类.内部类”。内部类与外部类之间可以直接进行私有成员的访问,这样一来内部类如果要是提供有实例化对象了,一定要先保证外部类实例化了。

1
2
3
4
5
6
7
java复制代码public class Test {

public static void main(String[] args) {
Teacher.Student student = new Teacher().new Student();
student.print();
}
}

如果此时Student类只允许Teacher类来使用,那么在这样的情况下就可以使用private进行私有定义

这样,此时的Student类就无法再外部使用,即在test中的这条语句 Teacher.Student student = new Teacher().new Student()就失效

在抽象类和接口中使用内部结构

在我们的java之中,类作为最基础的结构体实际上还有与之类似的抽象类或者是接口,抽象类和接口中都可以定义内部结构。

接口中定义内部接口

我们现在定义内部接口

1
2
3
4
5
6
7
8
9
java复制代码public interface Study {

public void study(XiaoMing xiaoMing);

interface XiaoMing {
public String getName();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码public class StudyImpl implements Study {

public void study(XiaoMing xiaoMing) {
System.out.println(xiaoMing.getName());
}

class XiaoHua implements XiaoMing {

public String getName() {
return "我叫小华";
}
}
}

测试:

1
2
3
4
5
6
7
java复制代码public class Test {

public static void main(String[] args) {
Study study = new StudyImpl();
study.study(((StudyImpl)study).new XiaoHua());
}
}

输出结果:

1
2
3
vbnet复制代码我叫小华

Process finished with exit code 0

接口中定义内部抽象类

下面我们继续观察一个内部抽象类,内部抽象类可以定义在普通类,抽象类,接口内部都可以。

1
2
3
4
5
6
7
8
9
java复制代码public interface Study {

public void study();

abstract class XiaoMing {
public abstract String getName();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码public class StudyImpl implements Study {

public void study() {
XiaoMing xiaoMing = new XiaoHua();
System.out.println(xiaoMing.getName());
}

class XiaoHua extends XiaoMing {
public String getName(){
return "我叫小华";
}
}
}

测试:

1
2
3
4
5
6
7
java复制代码public class Test {

public static void main(String[] args) {
Study study = new StudyImpl();
study.study();
}
}

输出结果:

1
2
3
vbnet复制代码我叫小华

Process finished with exit code 0

用内部类实现外部接口

内部类还有一些更为有意思的结构,即:如果现在定义了一个接口,那么可以在内部利用类实现该接口,在JDK1.8之后,接口中追加了static方法可以不受到实例化对象的控制,现在就可以利用此特性来完成功能。

接口内部进行接口实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
java复制代码public interface Study {

public void study();

class XiaoMing implements Study {

public void study() {
System.out.println("我爱学习!!");
}
}

public static Study getInstance() {
return new XiaoMing();
}
}

测试:

1
2
3
4
5
6
7
java复制代码public class Test {

public static void main(String[] args) {
Study study = Study.getInstance();
study.study();
}
}

输出结果:

1
2
3
vbnet复制代码我爱学习!!

Process finished with exit code 0

从上面可以看到,内部类是非常灵活的结构,只要你的语法满足了,各种需求都可以帮你实现!

static 定义内部类

static定义内部类

如果说现在内部类上使用了static定义,那么这个内部类就变成了外部类,static定义的都是独立于类的结构,所以该类结构就相当于是一个独立的程序类了。需要注意,static定义的不管是类还是方法只能够访问static成员,所以static定义的内部类只能够访问外部内中的static属性和方法

1
2
3
4
5
6
7
8
9
10
java复制代码public class Teacher {

private static final String msg = "我是一名老师";

static class Student {
public void print(){
System.out.println(Teacher.msg);
}
}
}

这个时候的Student类是一个独立类,如果此时要想实例化Student类对象,只需要根据“ 外部类.内部类 ”的结构实例化对象即可

格式如下 外部类.内部类 内部类对象 = new 外部类.内部类()

测试:

1
2
3
4
5
6
7
java复制代码public class Test {

public static void main(String[] args) {
Teacher.Student student = new Teacher.Student();
student.print();
}
}

输出结果:

1
2
3
vbnet复制代码我是一名老师

Process finished with exit code 0

所以以后如果发现类名称上提供有.,首先应该立刻想到这是一个内部类的结构,如果可以直接进行实例化,则应该立刻认识到这是一个static定义的内部类,但是static定义内部类的形式来讲并不常用,static定义内部接口的形式最为常用

static定义内部接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
java复制代码public interface StudyWarp {

static interface XiaoMing {
public String getToSchool();
}

static interface GOSchool {
public boolean hasCar();
}

public static void study(XiaoMing xiaoMing, GOSchool goSchool) {
if (goSchool.hasCar()) {
System.out.println(xiaoMing.getToSchool());
} else {
System.out.println("没车送小明上学。");
}
}

}
1
2
3
4
5
6
java复制代码public class DefaultStudy implements StudyWarp.XiaoMing{
@Override
public String getToSchool() {
return "我去上学!";
}
}
1
2
3
4
5
6
java复制代码public class DefaultSchool implements StudyWarp.GOSchool{
@Override
public boolean hasCar() {
return true;
}
}

测试:

1
2
3
4
5
6
java复制代码public class Test {

public static void main(String[] args) {
StudyWarp.study(new DefaultStudy(),new DefaultSchool());
}
}

输出结果:

1
2
3
java复制代码我去上学!

Process finished with exit code 0

方法中定义内部类

内部类可以在任意的结构中定义,这就包括了:类中、方法中、代码块中。但在方法中定义内部类的情况比较多。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java复制代码public class Teacher {

private String msg = "我是一名老师";

public void say(long time){

class Student { //内部类
public void print(){
System.out.println(Teacher.this.msg);
System.out.println(time);
}
}

new Student().print();

}
}
1
2
3
4
5
6
java复制代码public class Test {

public static void main(String[] args) {
new Teacher().say(System.currentTimeMillis());
}
}

输出结果:

1
2
3
4
java复制代码我是一名老师
1629704407680

Process finished with exit code 0

此时在say方法内部提供有Student内部类的定义,并且发现内部类可以直接访问外部类中的私有属性也可以直接访问方法中的参数,但对于方法中的参数直接访问时从 JDK1.8 开始支持的。而在 JDK1.8 之前**,如果方法中定义的内部类要想访问方法中的参数则参数前必须追加final。**

之所以取消这样的限制是为了扩展函数式编程准备的。

匿名内部类

匿名类是一种简化的内部类的处理形式,其主要是在抽象类和接口的子类上使用的。

接口和抽象类是一样的。

1
2
3
4
5
java复制代码public interface Study {

public void study(String book);

}
1
2
3
4
5
6
java复制代码public class XiaoMing implements Study{
@Override
public void study(String book) {
System.out.println(book);
}
}
1
2
3
4
5
6
7
java复制代码public class Test {

public static void main(String[] args) {
Study study = new XiaoMing();
study.study("红楼梦");
}
}

如果说现在Study接口中的XiaoMing子类只使用唯一的一次,那么是否还有必要将其定义为单独的类?那么在这样的要求下就发现这个时候定义的子类是有些多余了,所以就可以利用内部类的形式来解决此问题。

1
2
3
4
5
java复制代码public interface Study {

public void study(String book);

}
1
2
3
4
5
6
7
8
9
10
11
12
java复制代码public class Test {

public static void main(String[] args) {
Study study = new Study() {
@Override
public void study(String book) {
System.out.println(book);
}
};
study.study("红楼梦");
}
}

匿名内部类不一定要在抽象类或接口上,但只有在抽象类和接口上才有意义。有些时候为了更加方便的体现出匿名内部类的使用,往往可以利用静态方法做一个内部类的匿名内部类实现。

在接口中直接定义匿名内部类。

1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码public interface Study {

public void study(String book);

public static Study getInstance(){
return new Study() {
@Override
public void study(String book) {
System.out.println(book);
}
};
}
}
1
2
3
4
5
6
java复制代码public class Test {

public static void main(String[] args) {
Study.getInstance().study("红楼梦");
}
}

与内部类相比匿名内部类只是一个没有名字的只能够使用一次的,并且结构固定的一个子类。

总结:

方法,类,抽象类,接口,代码块中都可以定义内部结构——类,抽象类,接口。

本文主要讲了如何在内部类中使用内部类和接口中使用内部类以及如何使用static修饰的内部类和如何在方法中使用内部类。看完这篇相信你对内部类的理解更加深入了。

结尾

我是一个正在被打击还在努力前进的码农。如果文章对你有帮助,记得点赞、关注哟,谢谢!

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%