8、接口和内部类
接口和内部类
接口
随着软件规模的日益庞大,我们需要把复杂系统划分成小的组成部分,编程接口的设计十分重要。程序设计的实践中,编程接口的设计首先要使系统的职责得到合理划分。良好的接口设计可以降低系统各部分的相互依赖,提高组成单元的内聚性,降低组成单元间的耦合程度,从而提高系统的维护性和扩展性。
1.关于接口的理解。
接口从更深层次的理解,应是定义(规范,约束)与实现(名实分离的原则)的分离。
接口的本身反映了系统设计人员对系统的抽象理解。
接口应有两类:第一类是对一个体的抽象,它可对应为一个抽象体(abstract class);
第二类是对一个体某一方面的抽象,即形成一个抽象面(interface);
一个体有可能有多个抽象面。
抽象体与抽象面是有区别的。
面向接口编程
在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。
Java接口特性学习
在Java中看到接口,第一个想到的可能就是C++中的多重继承和Java中的另外一个关键字abstract。从另外一个角度实现多重继承是接口的功能之一,接口的存在可以使Java中的对象可以向上转型为多个基类型,并且和抽象类一样可以防止他人创建该类的对象,因为接口不允许创建对象。
interface关键字用来声明一个接口,它可以产生一个完全抽象的类,并且不提供任何具体实现。interface的特性整理如下:
\1. 接口中的方法可以有参数列表和返回类型,但不能有任何方法体。
\2. 接口中可以包含成员变量,但是会被隐式的声明为static和final。public static final
\3. 接口中的成员变量只是被存储在该接口的静态存储区域内,而不属于该接口。
\4. 接口中的方法可以被声明为public或不声明,但结果都会按照public类型处理。
\5. 当实现一个接口时,需要将被定义的方法声明为public类型的,否则为默认访问类型,Java编译器不允许这种情况。
\6. 如果没有实现接口中所有方法,那么创建的仍然是一个接口。
\7. 扩展一个接口来生成新的接口应使用关键字extends,实现一个接口使用implements。
interface在某些地方和abstract有相似的地方,但是采用哪种方式来声明类主要参照以下两点:
\1. 如果要创建不带任何方法定义和成员变量的基类,那么就应该选择接口而不是抽象类。
\2. 如果知道某个类应该是基类,那么第一个选择的应该是让它成为一个接口,只有在必须要有方法定义和成员变量的时候,才应该选择抽象类。因为抽象类中允许存在一个或多个被具体实现的方法,只要方法没有被全部实现该类就仍是抽象类。
以上就是接口的基本特性和应用的领域,但是接口绝不仅仅如此,在Java语法结构中,接口可以被嵌套,既可以被某个类嵌套,也可以被接口嵌套。这在实际开发中可能应用的不多,但也是它的特性之一。需要注意的是,在实现某个接口时,并不需要实现嵌套在其内部的任何接口,而且,private接口不能在定义它的类之外被实现。
如何定义接口?
格式:
[访问修饰符] interface 接口名 [extends 父接口1,父接口2…] {
常量定义 //总是public static final
方法定义 //总是:public abstract
}
如何实现接口
子类通过implements来实现接口中的规范
接口不能创建实例,但是可用于声明引用变量类型。
一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的。
Java的类只支持单继承,接口支持多继承
接口相关规则
接口中所有方法都是抽象的。
即使没有显式的将接口中的成员用public标示,也是public访问类型的
接口中变量默认用 public static final标示,所以接口中定义的变量就是全局静态常量。
可以定义一个新接口,用extends去继承一个已有的接口
接口不能继承普通类
可以定义一个类,用implements去实现一个接口中所有方法。
可以定义一个抽象类,用implements去实现一个接口中部分方法。
案例
//接口 package interfacePractect; public interface UsbInterface { void power(); } //实现类 package interfacePractect; public class Mouse implements UsbInterface { @Override public void power() { System.out.println("插上鼠标"); } } // package interfacePractect; public class Fan implements UsbInterface{ @Override public void power() { System.out.println("插上风扇"); } } // package interfacePractect; public class UPan implements UsbInterface{ @Override public void power() { System.out.println("插上U盘"); } } //表现类 package interfacePractect; public class Computer { private UsbInterface usb;//将接口变为成员变量 public void func() { usb.power();//通过接口调用方法 } //返回值是接口,多态的表现形式 public UsbInterface getUsb() { return usb; } //参数是接口,多态的表现形式 public void setUsb(UsbInterface usb) { this.usb = usb; } } //测试类 package interfacePractect; public class Test { public static void main(String[] args) { Fan fan = new Fan(); Computer com = new Computer(); com.setUsb(fan);//参数是接口,可以用实现类的引用传递过去 com.func(); } }
内部类
成员内部类
package out$in; /** * 成员内部类 class可以用访问修饰符类修饰访问权限 * 定义在外部类里面,方法外面 * 内部类可以直接使用外部类的成员变量,反之则不行 * 当外部类的成员变量与内部类的成员变量重名时, * 要想使用外部类的成员变量,则需要通过 外部类名.this.成员变量 来使用 * @author lgx * */ public class OuterClass { private String name = "外";//外部类成员变量 //成员内部类 class InnerClass{ //内部成员变量 private String name = "内"; //内部成员方法 public void test() { System.out.println("内部类方法" + OuterClass.this.name); } } public void test() {//外部类成员方法 System.out.println("外部类的成员方法"); } } //测试类 package out$in; import out$in.OuterClass.InnerClass; /** * 成员内部类创建对象 * OuterClass oc = new OuterClass(); * InnerClass in = oc.new InnerClass(); * @author lgx * */ public class Test { public static void main(String[] args) { OuterClass oc = new OuterClass(); InnerClass in = oc.new InnerClass(); in.test(); oc.test(); } }
静态内部类
package out$in; /** * 静态内部类 * 在外部类里面,方法外面,使用static修饰。 * 当内部类的成员有static时,该内部类必须是静态内部类 * 在外部类的方法使用内部类时,可以直接通过 外部类.内部类 来使用内部的静态成员 * @author lgx * */ public class OuterClass02 { private int age; static class InnerClass{ static int age = 20; String name = "内"; public void test() { System.out.println("内部类方法"); } } public void test() { System.out.println("外部方法"+OuterClass02.InnerClass.age); } } //测试类 package out$in; import out$in.OuterClass02.InnerClass; /** * 静态内部类创建对象 * 可以直接通过内部类名直接new出来 * InnerClass in = new InnerClass(); * @author lgx * */ public class Test02 { public static void main(String[] args) { InnerClass in = new InnerClass(); in.test(); OuterClass02 oc = new OuterClass02(); oc.test(); } }
方法内部类
package out$in; /** * 方法内部类 * 在成员方法里面创建一个类,该内部类只作用于该方法内,出了方法无效 * 所以class前面不能有访问修饰符,也不可以用static修饰, * 当方法有参数时,参数必须用final修饰 * @author lgx * */ public class OuterClass03 { private int age; public void test(final int a) { class InnerClass{ private int age = 20; public void test() { System.out.println("内部类方法"+ a); } } //要想使用内部类的成员变量和方法,必须先创建内部类的对象 InnerClass in = new InnerClass(); in.test(); } } //测试类 package out$in; /** * 方法内部类不能再其他类中创建对象,因为方法内部类的作用域只在那个方法里面 * @author lgx * */ public class Test03 { public static void main(String[] args) { OuterClass03 oc = new OuterClass03(); oc.test(2); } }
匿名内部类
//接口 package out$in; public interface MyInterface { void test(); } //测试类 package out$in; /** * 匿名内部类 * 适用于只需要使用一次对象的类 * 但使用匿名内部类还有个前提条件:必须继承一个父类或实现一个接口 * * * @author lgx * */ public class Test04 { public static void main(String[] args) { //通过接口来接收一个没有名字的类, //原理是匿名内部类先实现了接口,然后再创建对象 MyInterface mi = new MyInterface() { @Override public void test() { System.out.println("匿名内部类"); } }; //mi是引用,可以调用里面的方法 mi.test(); } }
垃圾回收机制
对象空间的分配:
使用new关键字创建对象即可
对象空间的释放:
传统的C/C++语言,需要程序员负责回收已经分配内存。显式回收垃圾回收的缺点:
程序忘记及时回收,从而导致内存泄露,降低系统性能。
程序错误回收程序核心类库的内存,导致系统崩溃。
Java语言不需要程序员直接控制内存回收,是由JRE在后台自动回收不再使用的内存,称为垃圾回收机制(Garbage Collection)。
可以提高编程效率。
保护程序的完整性。
其开销影响性能。Java虚拟机必须跟踪程序中有用的对象,确定哪些是无用的。
垃圾回收机制关键点
垃圾回收机制只回收JVM堆内存里的对象空间。
对其他物理连接,比如数据库连接、输入流输出流、Socket连接无能为力
现在的JVM有多种垃圾回收实现算法,表现各异。
垃圾回收发生具有不可预知性,程序无法精确控制垃圾回收机制执行。
可以将对象的引用变量设置为null,暗示垃圾回收机制可以回收该对象。
程序员可以通过System.gc()或者Runtime.getRuntime().gc()来通知系统进行垃圾回收,会有一些效果,但是系统是否进行垃圾回收依然不确定。
垃圾回收机制回收任何对象之前,总会先调用它的finalize方法(如果覆盖该方法,让一个新的引用变量重新引用该对象,则会重新激活对象)。
永远不要主动调用某个对象的finalize方法,应该交给垃圾回收机制调用。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· 字符编码:从基础到乱码解决
· Open-Sora 2.0 重磅开源!