热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Java面向对象编程进阶指南:第四章

本章深入探讨了Java中的多态特性,这是面向对象编程的核心概念之一。多态指的是同一操作作用于不同的对象时,可以有不同的解释和执行方式。在Java中,多态通过父类引用变量引用子类对象来实现,即`父类类型引用变量名=new子类类型();`。这种方式允许程序在运行时根据实际对象的类型动态地选择合适的方法执行,从而提高代码的灵活性和可扩展性。此外,本章还详细介绍了多态的应用场景和注意事项,帮助读者更好地理解和运用这一重要概念。

一·多态

多态:主要指同一种事务表现出来的多种形态。

语法格式 父类类型引用 引用变量名 = new 子类类型();

多态特点:



  1. 当父类类型的引用指向子类类型的对象时,父类类型的引用可以直接调用父类独有的方法。



  2. 当父类类型的引用指向子类类型的对象时,父类类型的引用不可以直接调用子类独有的方法。



  3. 对于父子类都有的非静态方法来说,编译阶段调用父类版本,运行阶段调用子类重写的版本(动态绑定)。



  4. 对于父子类都有的静态方法来说,编译阶段和运行阶段都调用父类版本。




二·引用数据类型间的转换



  1. 引用数据类型之间的转换方式有两种,自动类型和强制类型转换。



  2. 自动类型转换主要指小类型向大类型的转换,也就是子类转为父类,也叫向上转型。



  3. 强制类型转换主要指大类型向小类型的转换,也就是父类转为子类,也叫做向下类转换或显示转换。



  4. 引用数据类型之间的转换必须发生在父子类之间,否则编译报错。



  5. 若强转的目标类型并不是该引用真正指向的数据类型时编译通过,运行阶段发生类型转换异常。



  6. 为了避免上述错误的发生,应该在强转之前进行判断,格式如下 if(引用变量 instanceof 数据类型) ,判断引用变量指向的对象是否为后面的数据类型。




三·多态的实际意义

  1.多态的实际意义在于屏蔽不同子类型的差异性实现通用的编程带来不同的效果。


四·抽象方法的概念

  

  注意:抽象类主要指不能具体实例化的类并且使用abstract关键字修饰,也就是不能创建对象



  1. 抽象方法主要指不能具体实现的方法并且使用abstract关键字修饰,也就是没有具体方法。




  1. 抽象类的概念:指不能具体实例化的类并且使用abstract关键字修饰,也就是不能创建对象。




  1. 抽象类中可以有成员变量,构造方法,成员方法。




  1. 抽象类中可以没有抽象方法,也可以有抽象方法。




  1. 拥有抽象方法的类必须是抽象类,因此真正意义上的抽象类应该是具有抽象方法并且使用abstract关键字修饰的类。




五·抽象类的实际意义



  1. 抽象类的实际意义不在于创建对象而在于被继承。



  2. 当一个类继承抽象类后必须重写抽象方法,否则该类也编程抽象类,也就是抽象类对子类具有强制性和规范性,因此叫做模本设计模式。




六·经验分享



  1. 在以后的开发中推荐使用多台的格式,此时父类类型医用直接调用的所有方法一定是父类中拥有的方法,若以后更换子类时只需将new关键字后面的子类类型修改而无需改变就可以立即生效,从而提高额代码的可维护性和可扩展性。



  2. 该方式得缺点是:父类引用不能直接调用子类独有的方法,若调用则需要强制类型转换。




七·多态示例


public class Shape {
private int x;
private int y;
public Shape() {
}
public Shape(int x, int y) {
setX(x);
setY(y);
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public void show() {
System.out.println(
"横坐标:" + getX() + ",纵坐标:" + getY());
}
// 自定义静态方法
public static void test() {
System.out.println(
"Shape类中的静态方法!");
}
}


public class Rect extends Shape {
private int len;
private int wid;
public Rect() {
}
public Rect(int x, int y, int len, int wid) {
super(x, y);
setLen(len);
setWid(wid);
}
public int getLen() {
return len;
}
public void setLen(int len) {
if(len > 0) {
this.len = len;
}
else {
System.out.println(
"长度不合理哦!!!");
}
}
public int getWid() {
return wid;
}
public void setWid(int wid) {
if (wid > 0) {
this.wid = wid;
}
else {
System.out.println(
"宽度不合理哦!!!");
}
}
@Override
public void show() {
super.show();
System.out.println(
"长度是:" + getLen() + ",宽度是:" + getWid());
}
// 自定义静态方法
//@Override Error: 历史原因、不是真正意义上的重写
public static void test() {
System.out.println(
"---Rect类中的静态方法!");
}
}


public class ShapeRectTest {
public static void main(String[] args) {
// 1.声明Shape类型的引用指向Shape类型的对象并打印特征
Shape s1 = new Shape(1, 2);
// 当Rect类中没有重写show方法时,下面调用Shape类中的show方法
// 当Rect类中重写show方法后,下面调用Shape类中的show方法
s1.show(); // 1 2
// 使用ctrl+d快捷键可以复制当前行
System.out.println("------------------------------------");
// 2.声明Rect类型的引用指向Rect类型的对象并打印特征
Rect r1 = new Rect(3, 4, 5, 6);
// 当Rect类中没有重写show方法时,下面调用Shape类中的show方法
// 当Rect类中重写show方法后,下面调用Rect类中的show方法
r1.show(); // 3 4 5 6
// 使用alt+shift+上下方向键 可以移动代码
System.out.println("------------------------------------");
// 3.声明Shape类型的引用指向Rect类型的对象并打印特征
// 相当于从Rect类型到Shape类型的转换 也就是子类到父类的转换 小到大的转换 自动类型转换
Shape sr = new Rect(7, 8, 9, 10);
// 当Rect类中没有重写show方法时,下面调用Shape类中的show方法
// 当Rect类中重写show方法后,下面的代码在编译阶段调用Shape类的方法,在运行阶段调用Rect类中的show方法
sr.show(); // 7 8 9 10

System.out.println(
"------------------------------------");
// 4.测试Shape类型的引用能否直接调用父类和子类独有的方法呢???
int ia = sr.getX();
System.out.println(
"获取到的横坐标是:" + ia); // 7
//sr.getLen(); error Shape类中找不到getLen方法,也就是还在Shape类中查找
// 调用静态方法
sr.test(); // 提示:不建议使用引用.的方式访问
Shape.test(); // 推荐使用类名.的方式访问

System.out.println(
"------------------------------------");
// 5.使用父类类型的引用调用子类独有方法的方式
// 相当于从Shape类型到Rect类型的转换,也就是父类到子类的转换 大到小的转换 强制类型转换
int ib = ((Rect) sr).getLen();
System.out.println(
"获取到的长度是:" + ib); // 9
// 希望将Shape类型转换为String类型 强制类型转换要求必须拥有父子类关系
//String str1 = (String)sr; Error
// 希望将Shape类型强制转换为Circle类型,下面没有报错
//Circle c1 = (Circle)sr; // 编译ok,但运行阶段发生 ClassCastException类型转换异常
// 在强制类型转换之前应该使用instanceof进行类型的判断
// 判断sr指向堆区内存中的对象是否为Circle类型,若是则返回true,否则返回false
if(sr instanceof Circle) {
System.out.println(
"可以放心地转换了!");
Circle c1
= (Circle)sr;
}
else {
System.out.println(
"强转有风险,操作需谨慎!");
}
}
}


public class Circle extends Shape {
private int ir;
public Circle() {
}
public Circle(int x, int y, int ir) {
super(x, y);
setIr(ir);
}
public int getIr() {
return ir;
}
public void setIr(int ir) {
if (ir > 0) {
this.ir = ir;
}
else {
System.out.println(
"半径不合理哦!!!");
}
}
@Override
public void show() {
super.show();
System.out.println(
"半径是:" + getIr());
}
}


public class ShapeTest {
// 自定义成员方法实现将参数指定矩形对象特征打印出来的行为,也就是绘制图形的行为
// Rect r = new Rect(1, 2, 3, 4);
// public static void draw(Rect r) {
// r.show(); // 1 2 3 4
// }
// 自定义成员方法实现将参数指定圆形对象特征打印出来的行为
// public static void draw(Circle c) {
// c.show(); // 5 6 7
// }
// 自定义成员方法实现既能打印矩形对象又能打印圆形对象的特征,对象由参数传入 子类 is a 父类
// Shape s = new Rect(1, 2, 3, 4); 父类类型的引用指向子类类型的对象,形成了多态
// Shape s = new Circle(5, 6, 7); 多态
// 多态的使用场合一:通过参数传递形成了多态
public static void draw(Shape s) {
// 编译阶段调用父类的版本,运行阶段调用子类重写以后的版本
s.show();
}
public static void main(String[] args) {
// Rect r = new Rect(1, 2, 3, 4);
// r.show();
ShapeTest.draw(new Rect(1, 2, 3, 4));
ShapeTest.draw(
new Circle(5, 6, 7));
}
}


八·抽象类示例


public /*final*/ abstract class Account {
private int money;
public Account() {
}
public Account(int money) {
setMoney(money);
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
if (money >= 0) {
this.mOney= money;
}
else {
System.out.println(
"金额不合理哦!!!");
}
}
// 自定义抽象方法实现计算利息并返回的功能描述
public abstract double getLixi();
// private 和 abstract 关键字不能共同修饰一个方法
//private abstract double getLixi();
// final 和 abstract 关键字不能共同修饰一个方法
//public final abstract double getLixi();
// static 和 abstract 关键字不能共同修饰一个方法
//public static abstract double getLixi();
}


public class FixedAccount extends Account {
public FixedAccount() {
}
public FixedAccount(int i) {
super(i); // 表示调用父类的有参构造方法
}
@Override
public double getLixi() {
// 利息 = 本金 * 利率 * 时间
return getMoney() * 0.03 * 1;
}
public static void main(String[] args) {
// 1.声明Account类型的引用指向子类类型的对象,形成了多态
//Account acc = new FixedAccount(1000);
Account acc = new FixedAccount();
acc.setMoney(
1000);
double res = acc.getLixi();
System.out.println(
"计算的利息是:" + res); // 30.0
}
}


九·接口



  1. 接口就是一种比抽象类还抽象的类,体现在所有方法都为抽象方法。



  2. 定义类的关键字是class,而定义接口的关键字是interface。




十·类和接口之间的关系



  1. 类和类之间的关系 ,使用extends关键字表达继承关系 ,支持单继承。



  2. 类和接口之间的关系 ,使用implements关键字表达实现关系 ,支持多实现。



  3. 接口和接口之间的关系 ,使用extends关键字表达继承关系 ,支持多继承。




十一·抽象类和接口的主要区别

       ? 定义抽象类的关键字是abstract class,而定义接口的关键字是interface。

       ? 继承抽象类的关键字是extends,而实现接口的关键字是implements。

       ? 继承抽象类支持单继承,而实现接口支持多实现。

       ? 抽象类中可以有构造方法,而接口中不可以有构造方法。

       ? 抽象类中可以有成员变量,而接口中只可以有常量。

       ? 抽象类中可以有成员方法,而接口中只可以有抽象方法。

       ? 抽象类中增加方法时子类可以不用重写,而接口中增加方法时实现类需要重写(Java8以前的版本)。

       ? 从Java8开始增加新特性,接口中允许出现非抽象方法和静态方法,但非抽象方法需要使用default关键字修饰。

       ? 从Java9开始增加新特性,接口中允许出现私有方法。

 


public interface InterfaceTest {
/*public static final */int CNT = 1; // 里面只能有常量
//private void show(){} // 从Java9开始允许接口中出现私有方法
/*public abstract */void show(); // 里面只能有抽象方法(新特性除外),注释中的关键字可以省略,但建议写上
}

 

接口使用示例


public interface Metal {
// 自定义抽象方法描述发光的行为
public abstract void shine();
}


public interface Money {
// 自定义抽象方法描述购物的行为
public abstract void buy();
}


// 使用implements关键字表达实现的关系,支持多实现
public class Gold implements Metal, Money {
@Override
public void shine() {
System.out.println(
"发出了金黄色的光芒...");
}
@Override
public void buy() {
System.out.println(
"买了好多好吃的...");
}
public static void main(String[] args) {
// 1.声明接口类型的引用指向实现类的对象,形成了多态
Metal mt = new Gold();
mt.shine();
Money mn
= new Gold();
mn.buy();
}
}

 

JAVA面向对象04



推荐阅读
  • 问题背景:在使用Struts2注解实现ZIP文件下载功能时,由于InputStream未正确关闭,导致Tomcat服务器异常终止。重启后,系统抛出`java.io.EOFException`错误。具体表现为,在文件下载过程中,如果请求未正常完成或客户端提前中断连接,未关闭的InputStream会占用资源,最终导致服务器资源耗尽,触发异常。为解决此问题,建议在代码中确保InputStream在使用完毕后能够及时且正确地关闭,以避免资源泄露和服务器崩溃。 ... [详细]
  • Oracle程序包基础入门:了解核心概念与基本结构
    本文旨在为初学者介绍 Oracle 程序包的基础知识,涵盖其核心概念和基本结构。通过详细解析程序包的组成元素,如过程、函数和变量,帮助读者理解如何在实际应用中有效使用 Oracle 程序包。此外,文章还提供了实例代码,以便读者更好地掌握这些关键概念。 ... [详细]
  • 教程:使用Source Monitor进行代码质量分析
    Source Monitor 是一款强大的代码分析工具,能够对 Java、C++、C、C# 和 Delphi 等多种编程语言进行复杂度分析,帮助开发者有效评估和提升代码质量。通过详细的指标和报告,该工具可辅助团队识别潜在问题并优化代码结构。 ... [详细]
  • 本文深入探讨了 AdoDataSet RecordSet 的序列化与反序列化技术,详细解析了将 RecordSet 转换为 XML 格式的方法。通过使用 Variant 类型变量和 TStringStream 流对象,实现数据集的高效转换与存储。该方法不仅提高了数据传输的灵活性,还增强了数据处理的兼容性和可扩展性。 ... [详细]
  • 下面的代码旨在输出其类文件的完整名称。对于不熟悉类字面量的读者,`Me.class.getName()` 方法会返回类的全称,例如 “com.javapuzzlers.Me”。通过这一机制,可以深入了解 Java 类加载和反射机制的内部工作原理。 ... [详细]
  • Dapper:一款高效轻量的ORM框架
    Dapper 是一个高效且轻量级的 ORM(对象关系映射)框架,由 StackExchange 开发并维护。它旨在提供快速的数据访问性能,同时保持代码的简洁性和易用性。Dapper 可以显著提高开发效率,特别适用于需要高性能数据操作的应用场景。更多详细信息可参考其官方文档和 GitHub 仓库。 ... [详细]
  • 本文探讨了在 SQL 中将中文字符转换为拼音首字母的有效方法和技巧。通过使用特定的函数和算法,可以实现中文名称的快速拼音首字母提取,从而提高数据处理的效率和准确性。文中还提供了具体的示例和代码片段,帮助读者更好地理解和应用这些技术。 ... [详细]
  • 深入浅出解析HTTP协议的核心功能与应用
    前言——协议是指预先设定的通信规则,确保双方能够按照既定标准进行有效沟通,从而实现准确的信息交换。例如,驯兽师通过拍手使动物坐下,这实际上是一种预设的协议。本文将详细探讨HTTP协议的核心功能及其广泛应用,解析其在现代网络通信中的重要作用。 ... [详细]
  • 正则表达式与文本处理三剑客深入解析
    本文深入解析了正则表达式及其在文本处理中的应用,详细介绍了常用的正则表达式模式,如 `[0-9]` 用于匹配任意一个数字字符,`[^0-9]` 匹配任意一个非数字字符,`^[0-9]` 表示以数字开头,`[a-z]` 匹配任意一个小写字母,而 `[a-zA-Z]` 则匹配任意一个字母,并强调了正则表达式中大小写的区分。此外,文章还探讨了正则表达式在文本处理中的高级用法,包括模式匹配、字符串替换和数据提取等技术,为读者提供了丰富的实战案例和应用场景。 ... [详细]
  • 题目要求在给定的数组中找到一个连续子数组,使其乘积最大。本文详细介绍了使用动态规划算法解决这一问题的方法,包括状态定义、状态转移方程和初始化步骤。通过具体的例子和代码实现,帮助读者深入理解该算法的核心思想和实现细节。 ... [详细]
  • 触发器是数据库中一种特殊类型的存储过程,其执行依赖于预定义的事件,而非直接调用。在数据库管理中,触发器主要用于实现数据完整性、自动化日志记录及复杂业务规则的执行。当对数据库中的表、视图等对象进行插入、更新或删除操作时,系统将自动激活相关的触发器,以确保数据的一致性和安全性。此外,通过合理设计和优化触发器,还可以显著提升数据库性能和响应速度。 ... [详细]
  • 超链接作为网页间的重要连接方式,不仅是信息流动的关键通道,还极大地提升了网络资源的可访问性和互联性。通过超链接,用户能够便捷地在不同网站和页面之间跳转,获取所需信息,促进了互联网内容的广泛传播与高效利用。 ... [详细]
  • Tornado硬件管理平台中的设备信息采集技术深入解析(三)
    深入解析 Tornado 硬件管理平台中的设备信息采集技术,本文聚焦于 `monitor.py` 脚本的关键字段分析。该脚本通过导入 `psutil`、`time` 和 `datetime` 模块,以及使用 `pprint` 进行数据格式化输出,实现对系统资源和设备状态的高效监控与数据采集。 ... [详细]
  • 敏捷开发对于众多经历过复杂编程项目的开发者而言,无疑是一项宝贵的实践。尽管敏捷方法能够加速项目交付,但快速迭代也可能导致较高的Bug率。然而,通过在后期进行严格的测试和持续改进,这些问题可以得到有效解决。此外,敏捷开发还强调团队协作、客户反馈和适应变化,这些因素共同促进了项目的成功。 ... [详细]
  • 如何在SharePoint 2013中使用不同用户身份进行登录操作
    在创建了SharePoint 2013网站后,我注意到其界面与2010版本有所不同,特别是缺少了“以其他用户身份登录”的功能,这对测试工作造成了不便。通过查阅一些国外的技术资源,最终找到了有效的解决方案。这一方法不仅解决了登录问题,还提升了多用户环境下的测试效率和安全性。 ... [详细]
author-avatar
双鱼2502858483
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有