我们接着上次讨论的Java是一门面向对象的编程语言这个话题继续下去。在前边的两篇文章里,我谈到了Java中有些做的不好的地方。今天就来谈谈做得好的地方。当然,这个好,只是我个人觉得好,还是有不少人是有反对意见的。

2006年,在北京的一次Java会议上,我见到James Gosling,有人在茶歇的时候问了一个问题,说Java什么时候会支持操作符重载?Gosling回答,never。我是举双手赞成Java里不支持操作符重载的,但一直到现在还是有人在要求Java中支持操作符重载。你看scala就忍不住,已经把操作符重载加进来了。所以,我们今天讨论的话题,在不同的人看来会有不同的看法,我这里只是表达一下我的看法。大家可以在以后的编程实践里慢慢体会,不要囿于我的说法。

今天的重点:

1. 掌握 Java 的类继承机制

2. 了解C++的类机制以作为对比

Java的面向对象编程机制

Java 面向对象的继承体系从设计之初,直到现在就没有发生过变化,足以见到这套体系之成熟稳定。我们一条条来看吧。

1. interface的引入

“这不就是一个只包含纯虚函数的基类吗?还要起个名字叫interface。” 我现在还记得一位前辈这样嘲讽过Java没有新意。但是事实证明,他搞错了,interface的意义绝不仅仅是工程上的便利。现在回过头来看,使用interface来定义某一类通用操作,而又不强制规定其实现,对于Java的流行真是太重要了。

以JDBC举例。在Java之前,C++与数据库建立连接,常用的一个技术是OLEDB。这个技术我刚才搜索了一下,已经找不到太有效的内容了。我只记得开发比较复杂,如果我的应用要使用不同的数据库,就得为不同的数据库编写适配的代码。这是一件很头疼的事情。

JDBC是怎么做的呢?java 提供了一个名为 java sql 的包,大家可以去看一下源码,这个包里的Statement, ResultSet 等等其实都是 interface。这些 interface 就定义了个标准,每个数据库厂商要支持Java连接,就得遵守这个标准。就是说,Oracle,要支持Java连接,就得提供自己的JDBC库,mysql要支持Java连接,也不例外,也得提供自己的JDBC库。这些由厂商提供的库,都严格遵守JDBC的标准。那么,我们在写数据库应用的时候,就使用这些标准的接口进行编程,当需要换一个数据库的时候,只需要换成这个数据库的JDBC就行了。我们看具体的例子:

public class MysqlExample {public static void main(String[] args) throws Exception {Connection conn = null;String sql;String url = "jdbc:mysql://localhost:3306/javademo?" + "user=root&password=root&useUnicode=true&characterEncoding=UTF8";try {Class.forName("com.mysql.jdbc.Driver");conn = DriverManager.getConnection(url);Statement stmt = conn.createStatement();sql = "create table student(NO char(20),name varchar(20),primary key(NO))";int result = stmt.executeUpdate(sql);if (result != -1) {sql = "select * from student";ResultSet rs = stmt.executeQuery(sql);                while (rs.next()) {System.out.println(rs.getString(1) + "t" + rs.getString(2));}}} catch (SQLException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();} finally {conn.close();}}}

上面这个程序中除了创建连接的URL,其他的地方都是遵守JDBC标准,而与具体的数据库没有关系了。这样一来,我们就可以为不同的数据库连接只写一份代码了。有了JDBC标准,数据库的具体实现与应用程序就真正做到了解耦。而JDBC标准,正是由 java.sql 这个package 里定义的各个 interface 来具体体现的。

2. 多态

其实在Java之前,大规模流行的面向对象编程语言,大概只有C++了。Java中有多种针对C++的改进,多态就是其中之一。对比两段代码。

#include<iostream>
using namespace std;class Shape {public:
//  virtual void draw() {void draw() {cout << "draw Shape" << endl;}
};class Circle : public Shape {public:
//  virtual void draw() {void draw() {cout << "draw Circle" << endl;}
};int main() {Shape * s = new Circle();s->draw();
}

这段C++代码的输出是"draw Shape"。要使得它输出"draw Circle",就要把draw函数变成virtual,如注释中所写。这个功能,看上去使得C++的类机制更加灵活,因为静态绑定和运行时动态绑定两种机制都支持,而我们知道静态绑定,运行效率更高,C++程序可以自由地根据情况来使用两种绑定机制。

Java抛弃了静态绑定。Java认为,既然实现了继承,并且在子类中又提供了实现,那就应该是覆写。在Java中实现同样的代码,看看效果。

public class Main {public static void main(String args[]) throws IOException {Shape s = new Circle();s.draw();}
}class Shape {public void draw() {System.out.println("draw Shape");}
}class Circle extends Shape {public void draw() {System.out.println("draw Circle");}
}

嗯,这次输出的是draw Circle。用C++的术语来说,Java中所有的成员方法都自动是virtual的。所有的子类方法会自动覆写父类的同名方法。

3. 抽象类和抽象方法

这一点,其实没什么特别的,C++中,如果一个类中定义了纯虚函数(没有实现,只有声明的虚函数),那这个类也是不能被实例化的。Java的类中,如果定义了一个抽象方法(以abstract修饰,不提供方法实现),那这个类就是抽象类,也不能实例化的。这一点上,两者是对应的。例如:

public class Main {public static void main(String args[]) throws IOException {Shape s = new Circle();s.draw();}
}abstract class Shape {public void draw() {System.out.println("draw Shape");}   public abstract double area();
}class Circle extends Shape {public void draw() {System.out.println("draw Circle");}   public double area() {return 3.14;}
}

大家可以试一下,1. Shape类前面的abstract不加,会报什么错。2. shape中的area的定义前不加abstract会怎么样? 3. 如果Circle中没有实现area会怎么样。

4. 拒绝操作符重载

文章的一开始,我们就提到了,Java中是拒绝操作符重载的。理由是,如果实现了这个功能,用户就会定义出奇奇怪怪不可读的操作符。这一点,我是赞同的,看看scala中的flatMap这么清晰的一个函数,变成Haskell中的(>>=),可读性确实是下降了。

但有些地方,操作符重载也确实能提供更清晰的语法,比如BigInteger。这个类是Java中的高精度整型类。我们看一下,它的例子。

public class Main {public static void main(String args[]) {BigInteger one = BigInteger.valueOf(1);BigInteger two = BigInteger.valueOf(2);BigInteger three = one.add(two);System.out.println(three);}
}

可以看到,Java中只能用 add 来表示两个大整数的相加。如果支持了操作符重载呢?我们用C++举个例子。

class BigInteger {
public:int value;BigInteger(int v) : value(v) {}BigInteger operator + (BigInteger & that) {return BigInteger(this->value + that.value);}
};int main() {BigInteger one(1);BigInteger two(2);BigInteger three = one + two;cout << three.value << endl;
}

在这个例子中,使用+比使用add方法更简洁清晰。但是,允许了操作符重载,开发者为了图方便,就有可能写出这种东西来:

class BigInteger {
public:int value;BigInteger(int v) : value(v) {}int operator >>= (int v) {return this->value + v;}
};int main() {BigInteger one(1);int a = one >>= 3;cout << a << endl;
}

这就失去了操作符重载原有的简洁性,而使代码变得不可读了。在这一点上,做得比较好是Python,它限制了可以重载的操作符的类型。

class BigInteger(object):def __init__(self, v): self.value = v def __add__(self, that):return BigInteger(self.value + that.value)

一个定义了 __add__ 方法的类,是可以直接使用 + 操作符的。但你不能定义乱七八糟意义不明的操作符。Python中是受限的操作符重载。这是我认为的最好的机制。退而求其次,应该是Java的做法,而不是C++的做法。

今天的课程就到这里了。下节课,我们会就extends 和 implements 来继续探讨Java的对象模型。

这节课的作业:把文章中的例子都跑一跑,编译执行一下,看看输出,体会一下C++的“繁琐”和Java的“简洁” :)

上一节:Java语言的品味(二)

下一节:

回目录:课程目录

java oracle executeupdate 无效_Java语言的品味(三)相关推荐

  1. java c++的区别_Java语言与C、C++之间的区别?

    小伙伴你知道吗?Java是由C++开发而来的,并且在当时一直被搁置.随着Java语言的崛起那么Java和C/C++有什么不同和相同之处呢? 通过上述我们知道那Java前身是C++,并且保留了C++的大 ...

  2. java程序设计基础篇_Java语言程序设计(基础篇) 第一章

    第一章 计算机.程序和Java概述 1.1 引言 什么是程序设计呢? 程序设计就是创建(或者开发)软件,软件也称为程序. 1.2 什么是计算机 计算机是存储和处理数据的电子设备,计算机包括硬件(har ...

  3. java zip解压_Java语言入门第一课

    Java最初的目标是嵌入式设备,不过在嵌入式设备方面并未取得成功.失之东隅,收之桑榆,Java却在Web领域被广泛接受.近年来,随着Java在服务器领域的不断突破,让这门语言越来越流行. 有人喜欢Ja ...

  4. java for update 无效_java.sql.BatchUpdateException:调用中的无效参数

    检查此循环. 这里,在i = 0的情况下,取出(0-1)为-1. 我们知道List提供了基于0的索引,所以你的str.get(..)应该从0开始,而不是从-1开始 试试这个 public void i ...

  5. java 与c 运行效率_Java语言与C语言代码运行效率的比较

    <Java语言与C语言代码运行效率的比较>由会员分享,可在线阅读,更多相关<Java语言与C语言代码运行效率的比较(2页珍藏版)>请在人人文库网上搜索. 1.Java语言与C语 ...

  6. java注解式开发_JAVA语言之Spring MVC注解式开发使用详解[Java代码]

    本文主要向大家介绍了JAVA语言的Spring MVC注解式开发使用详解,通过具体的内容向大家展示,希望对大家学习JAVA语言有所帮助. MVC注解式开发即处理器基于注解的类开发, 对于每一个定义的处 ...

  7. java程序并行机制_Java语言具有多种优点和特点,下列选项中,______反映了Java程序并行执行机制的特点。A.多线程B.健...

    Java语言具有多种优点和特点,下列选项中,______反映了Java程序并行执行机制的特点.A.多线程B.健 更多相关问题 根据句意及首字母完成单词1. -What c__________ is i ...

  8. 非洲瓜哇JAVA布的特点_java语言的基本特性以及编程细节

    前言 java语言的学习是一个体系,所以如果想要对java的编程有一个很精通的一个掌握,它离不开很多基础的知识点,比如JVM的原理.java多线程并发编程.数据结构等等.所以我这里对我学习的java的 ...

  9. java oracle数据备份_Java备份还原Oracle数据库

    Java备份还原Oracle数据库,不知道还有没好点的方法 希望有的也能提供下方法或者代码. Java备份还原Oracle数据库,,不知道还有没好点的方法 希望有的也能提供下方法或者代码. packa ...

最新文章

  1. 经验 | OpenCV图像旋转的原理与技巧
  2. 将毫秒转换_Matlab将Unix时间戳转为可读日期
  3. javascript基础 (2)
  4. sevlet实现下载文件功能
  5. 递归javascript_使用freeCodeCamp挑战解释了JavaScript中的递归
  6. android c++ gizp 调用 so,使用ndk-build编译 android调用的so库
  7. 机器学习:用梯度下降法实现线性回归
  8. 【keras】有关loss function的定义-返回的是`矩阵`还是`标量`
  9. 资产泡沫即将湮灭! 转折全面到来!
  10. phalapi可以依赖注入么_[2.11]-核心思想:DI依赖注入-让资源更可控 | PhalApi(π框架) - PHP轻量级开源接口框架 - 接口,从简单开始!...
  11. html书写表单laber,laber(labelhood是什么意思)
  12. 让View具有弹性效果的动画——SpringAnimation
  13. js 实现筋斗云效果(点击tab栏里面的某个地方,会有图片移动到此地方)
  14. 掌握这5款 Edge插件,让你的浏览器使用更高效!
  15. 2022年全球市场救生艇总体规模、主要生产商、主要地区、产品和应用细分研究报告
  16. python中split()函数讲解
  17. 【Spring框架一】——Spring框架简介
  18. 昭通计算机一级b考试试题及答案,2022云南昭通事业单位考试综合应用能力(B类)考试如何备考?...
  19. 如何恢复电脑的administrator账户
  20. 【ivp6服务器可通过ivp4网络访问教程】

热门文章

  1. 从源码角度解析线程池中顶层接口和抽象类
  2. 收藏!数据建模最全知识体系解读
  3. 【华为云技术分享】mongos-sharding连接池配置
  4. 【Python成长之路】从零学GUI -- 制作智能聊天机器人
  5. 使用Python开发小说下载器,不再为下载小说而发愁 #华为云·寻找黑马程序员#
  6. 【华为云技术分享】#华为云·寻找黑马程序员#海量数据的分页怎么破?
  7. 用Python识别验证码
  8. mysql hy093_请问SQLSTATE [HY093]:参数号无效:未定义参数
  9. mysql+concat函数问题_Mysql5.7中使用group concat函数数据被截断的问题完美解决方法...
  10. 高等组合学笔记(十一):分拆与Gauss二项式系数,恒等式与展开式