多态 / 面向接口编程
一、对象与多态
1. 重载与重写
1.1 重载
(1)发生在一个类中,方法名相同,参数列表不同;
(2)遵循"编译器"绑定,看引用的类型绑定方法;
(3)方法的重载可以看成完全不同的方法,只不过是方法名恰好相同;
遵循:
(1)方法名相同,参数列表(类型和个数)不同;
(2)不能通过访问权限,返回值类型和抛出的异常来实现重载;
(3)可以有不同的访问修饰符、返回值类型、抛出不同的异常;
1.2 重写
(1)发生在父子类中,方法名称相同,参数列表相同,方法体不同;
(2)重写方法被调用时,看对象的类型(遵循"运行期"绑定,看对象的类型绑定方法);
遵循(两同两小一大原则):
两同:方法名相同,参数类型相同;
两小:子类返回类型小于等于父类方法返回类型;
子类抛出异常小于等于父类方法抛出异常;
一同:子类访问权限大于等于父类方法访问权限;
强制类型转换:
子类对象可以声明为父类类型,父类对象不可以声明为子类类型;
在子类对象声明为父类类型后,可以通过强制转型,转型回来;
而父类对象声明为父类类型之后,并不能执行强制类型转化;
因为在子类对象声明为父类类型后,其实对象的真实意义还是子类对象; 参考[类型实例]
1.3 多态
重载(overload)和重写(override)是实现多态的两种主要方式。
实现多态: 接口多态性。 继承多态性。 通过抽象类实现的多态性
多态的三个条件:
a. 继承的存在(继承是多态的基础,没有继承就没有多态).
b. 子类重写父类的方法(多态下调用子类重写的方法).
c. 父类引用变量指向子类对象(子类到父类的类型转换).
当父类的引用指向子类对象时,通过这个引用能调用什么方法,看引用的类型,而调用的是子类对象里面的方法。

/** * 向上造型:能点出什么来,看引用的类型,调用的方法是对象的 */ public class Test { public static void main(String[] args) { A o = new B(); o.f1(); //B.f1() B b= (B)o; b.f1(); //B.f1() b.f3(); //B.f3() } } class A{ void f1(){ System.out.println("A.f1()"); } } class B extends A{ @Override void f1(){ System.out.println("B.f1()"); } void f3(){ System.out.println("B.f3()"); } }
2. 面向接口编程
为什么要面向接口编程?
减少耦合性,令各个成员依赖于抽象, 而不是依赖于具体,方便维护和扩展[封闭-开放原则]。
用法:传递的参数为接口/抽象类

public class Test { public static void main(String[] args) { //面向接口编程 Test.test01(new B()); // B.f1() Test.test01(new C()); // C.f1() } public static void test01(A a) { a.f1(); } } class A{ void f1(){ System.out.println("A.f1()"); } } class B extends A{ @Override void f1(){ System.out.println("B.f1()"); } } class C extends A{ @Override void f1(){ System.out.println("C.f1()"); } }
3、对象创建
方式一:new Person()
普通的对象实例化语法,调用的是类的默认构造方法(如果没有显式定义构造方法的话,默认会有一个无参构造方法)。这个对象的类型是 Person
类的一个实例。
使用反射获取 new Person()
创建的对象的类类型时,将直接得到 Person
类的类型。
方式二:new Person(){}
匿名内部类的语法,创建一个匿名子类的实例,它继承了 Person
类并且可以覆写父类的方法或添加新的方法。它实际上是在定义一个继承了 Person
类的匿名子类,并且在创建子类对象时执行一些代码块。这种方式通常用于创建临时的、仅在特定地方使用的对象。Person可以是接口,也可以是类。
使用反射获取 new Person(){}
创建的对象的类类型时,将得到一个匿名子类的类型。因为 new Person(){}
创建的是一个继承了 Person
类的匿名子类的对象,而匿名子类是动态生成的,没有显式的类名,因此反射获取的类型将是这个匿名子类的类型。
class Person { public void display() { System.out.println("Person class"); } } public class Main { public static void main(String[] args) { Person person = new Person() { @Override public void display() { System.out.println("Anonymous subclass"); } }; Class<? extends Person> classType = person.getClass(); System.out.println(classType.getName()); // 输出:Main$1 } }
在上面的例子中,new Person(){}
创建了一个匿名子类的对象,并重写了 display()
方法。当使用反射获取类型时,返回的类型是 Main$1
,其中 $1
表示这是外部类 Main
的第一个匿名内部类。
总结:
new Person()
用于实例化一个具体的类(对象)。new Person(){}
用于创建一个匿名内部类的实例,可以用来实现接口或扩展类的行为。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!