这是我参与8月更文挑战的第23天,活动详情查看:8月更文挑战
前言
内部类相信大家都应该用过,但我也相信大家应该都只是很简单的使用。
所以今天,就来详细讲解内部类的使用,废话不多说,我们先赶紧来看吧。
在普通类中使用内部结构
先来看个示例:
Teacher
类里面有个内部类Student
:
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类里面的属性
}
}
}
复制代码
测试类:
public class Test {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.say();
}
}
复制代码
输出结果:
我是一名老师
Process finished with exit code 0
复制代码
从整体的代码结构来讲,内部类的结构并不合理,所以内部类本身最大的缺陷在于破坏了程序的结构,但是破坏需要有目的的破坏,那么它也一定会有其优势,如果要想更好的观察出内部类的优势,就可以将内部类拿到外面来。我将上面的代码Student
拿出来。
Teacher 类:
public class Teacher {
private String msg = "我是一名老师";//只能在类内部访问
public void say() {
Student student = new Student(this);//实例化内部类的对象
student.print();
}
public String getMsg() {
return this.msg;
}
}
复制代码
Student 类:
public class Student {
private Teacher teacher;
public Student(Teacher teacher) {
this.teacher = teacher;
}
public void print() {
System.out.println(this.teacher.getMsg()); //msg是Teacher类里面的属性
}
}
复制代码
测试:
public class Test {
public static void main(String[] args) {
Teacher teacher = new Teacher();
teacher.say();
}
}
复制代码
输出结果:
我是一名老师
Process finished with exit code 0
复制代码
我们折腾了半天主要的目的就是为了让Student
这个内部类可以访问Teacher
这个类中的私有属性,如果不用内部类的时候整体代码会非常的麻烦,所以可以得出内部类的优点:轻松的访问外部类中的私有属性。
需要注意的是
:内部类虽然可以方便的访问外部类中的私有成员或私有方法,同理**,外部类也可以轻松访问内部类中的私有成员或私有方法**。
示例如下:
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();
}
}
复制代码
输出结果:
我是一名老师
我是一名学生
Process finished with exit code 0
复制代码
使用了内部类之后,内部类与外部类之间的私有操作的访问就不再需要通过setter,getter
以及其他的间接方式完成了,可以直接进行操作,但是需要注意的是,内部类本身也属于一个类,虽然在大部分情况下内部类往往是被外部类包裹的,但是外部依然可以产生内部类的实例化对象,而此时,内部类实例化对象的格式如下:
外部类.内部类 内部类对象 = new 外部类().new 内部类();
在内部类编译完成之后会自动形成一个Teacher$Student.class
类文件,其中“$”这个符号换到程序中就变为“.”,所以内部类的全称:“外部类.内部类”。内部类与外部类之间可以直接进行私有成员的访问,这样一来内部类如果要是提供有实例化对象了,一定要先保证外部类实例化了。
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之中,类作为最基础的结构体实际上还有与之类似的抽象类或者是接口,抽象类和接口中都可以定义内部结构。
接口中定义内部接口
我们现在定义内部接口:
public interface Study {
public void study(XiaoMing xiaoMing);
interface XiaoMing {
public String getName();
}
}
复制代码
public class StudyImpl implements Study {
public void study(XiaoMing xiaoMing) {
System.out.println(xiaoMing.getName());
}
class XiaoHua implements XiaoMing {
public String getName() {
return "我叫小华";
}
}
}
复制代码
测试:
public class Test {
public static void main(String[] args) {
Study study = new StudyImpl();
study.study(((StudyImpl)study).new XiaoHua());
}
}
复制代码
输出结果:
我叫小华
Process finished with exit code 0
复制代码
接口中定义内部抽象类
下面我们继续观察一个内部抽象类,内部抽象类可以定义在普通类,抽象类,接口内部都可以。
public interface Study {
public void study();
abstract class XiaoMing {
public abstract String getName();
}
}
复制代码
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 "我叫小华";
}
}
}
复制代码
测试:
public class Test {
public static void main(String[] args) {
Study study = new StudyImpl();
study.study();
}
}
复制代码
输出结果:
我叫小华
Process finished with exit code 0
复制代码
用内部类实现外部接口
内部类还有一些更为有意思的结构,即:如果现在定义了一个接口,那么可以在内部利用类实现该接口,在JDK1.8之后,接口中追加了static
方法可以不受到实例化对象的控制,现在就可以利用此特性来完成功能。
接口内部进行接口实现:
public interface Study {
public void study();
class XiaoMing implements Study {
public void study() {
System.out.println("我爱学习!!");
}
}
public static Study getInstance() {
return new XiaoMing();
}
}
复制代码
测试:
public class Test {
public static void main(String[] args) {
Study study = Study.getInstance();
study.study();
}
}
复制代码
输出结果:
我爱学习!!
Process finished with exit code 0
复制代码
从上面可以看到,内部类是非常灵活的结构,只要你的语法满足了,各种需求都可以帮你实现!
static 定义内部类
static定义内部类
如果说现在内部类上使用了static定义,那么这个内部类就变成了外部类
,static定义的都是独立于类的结构,所以该类结构就相当于是一个独立的程序类了。需要注意,static定义的不管是类还是方法只能够访问static成员,所以static定义的内部类只能够访问外部内中的static属性和方法。
public class Teacher {
private static final String msg = "我是一名老师";
static class Student {
public void print(){
System.out.println(Teacher.msg);
}
}
}
复制代码
这个时候的Student
类是一个独立类,如果此时要想实例化Student
类对象,只需要根据“ 外部类.内部类 ”的结构实例化对象即可
格式如下 外部类.内部类 内部类对象 = new 外部类.内部类();
测试:
public class Test {
public static void main(String[] args) {
Teacher.Student student = new Teacher.Student();
student.print();
}
}
复制代码
输出结果:
我是一名老师
Process finished with exit code 0
复制代码
所以以后如果发现类名称上提供有.
,首先应该立刻想到这是一个内部类的结构,如果可以直接进行实例化,则应该立刻认识到这是一个static定义的内部类,但是static定义内部类的形式来讲并不常用,static定义内部接口的形式最为常用。
static定义内部接口
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("没车送小明上学。");
}
}
}
复制代码
public class DefaultStudy implements StudyWarp.XiaoMing{
@Override
public String getToSchool() {
return "我去上学!";
}
}
复制代码
public class DefaultSchool implements StudyWarp.GOSchool{
@Override
public boolean hasCar() {
return true;
}
}
复制代码
测试:
public class Test {
public static void main(String[] args) {
StudyWarp.study(new DefaultStudy(),new DefaultSchool());
}
}
复制代码
输出结果:
我去上学!
Process finished with exit code 0
复制代码
方法中定义内部类
内部类可以在任意的结构中定义,这就包括了:类中、方法中、代码块中。但在方法中定义内部类的情况比较多。
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();
}
}
复制代码
public class Test {
public static void main(String[] args) {
new Teacher().say(System.currentTimeMillis());
}
}
复制代码
输出结果:
我是一名老师
1629704407680
Process finished with exit code 0
复制代码
此时在say
方法内部提供有Student
内部类的定义,并且发现内部类可以直接访问外部类中的私有属性也可以直接访问方法中的参数,但对于方法中的参数直接访问时从 JDK1.8 开始支持的。而在 JDK1.8 之前**,如果方法中定义的内部类要想访问方法中的参数则参数前必须追加final
。**
之所以取消这样的限制是为了扩展函数式编程准备的。
匿名内部类
匿名类是一种简化的内部类的处理形式,其主要是在抽象类和接口的子类上使用的。
接口和抽象类是一样的。
public interface Study {
public void study(String book);
}
复制代码
public class XiaoMing implements Study{
@Override
public void study(String book) {
System.out.println(book);
}
}
复制代码
public class Test {
public static void main(String[] args) {
Study study = new XiaoMing();
study.study("红楼梦");
}
}
复制代码
如果说现在Study
接口中的XiaoMing
子类只使用唯一的一次,那么是否还有必要将其定义为单独的类?那么在这样的要求下就发现这个时候定义的子类是有些多余了,所以就可以利用内部类的形式来解决此问题。
public interface Study {
public void study(String book);
}
复制代码
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("红楼梦");
}
}
复制代码
匿名内部类不一定要在抽象类或接口上,但只有在抽象类和接口上才有意义。有些时候为了更加方便的体现出匿名内部类的使用,往往可以利用静态方法做一个内部类的匿名内部类实现。
在接口中直接定义匿名内部类。
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);
}
};
}
}
复制代码
public class Test {
public static void main(String[] args) {
Study.getInstance().study("红楼梦");
}
}
复制代码
与内部类相比匿名内部类只是一个没有名字的只能够使用一次的,并且结构固定的一个子类。
总结:
方法,类,抽象类,接口,代码块中都可以定义内部结构——类,抽象类,接口。
本文主要讲了如何在内部类中使用内部类和接口中使用内部类以及如何使用static修饰的内部类和如何在方法中使用内部类。看完这篇相信你对内部类的理解更加深入了。
结尾
我是一个正在被打击还在努力前进的码农。如果文章对你有帮助,记得点赞、关注哟
,谢谢!