1. 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不加,会报什么错?

如果Shape类不加abstract,则无法拥有abstract方法.

2. shape中的area的定义前不加abstract会怎么样?

如果area方法不加abstract,则必须提供该方法的实现.

3. 如果Circle中没有实现area会怎么样?

如果Circle类没有实现area方法,则Circle必须指定为abstract,因为从基类得到abstract方法的类要么实现该方法,要么声明为abstract类.

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();
BigInteger two();
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();
int a = one >>= ;
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++的做法。

java的类继承(与c++对比)的更多相关文章

  1. java一个类 继承HttpServlet 和实现Servlet区别

    java一个类 继承HttpServlet 和实现Servlet区别 servlet 是一个接口,如果实现这个接口,那么就必须实现接口里面定义的所有方法 而HttpServlet实现了servlet接 ...

  2. Java的类继承

    知识点1.继承作用:提高代码的重用性,继承之后子类可以继承父类中的属性和方法减少重复代码条件:子类和父类要满足is a的逻辑关系,才能使用继承.如:苹果 is a水果语法:使用extends 连接子类 ...

  3. 【JAVA】类继承对父类静态变量的操作

    对静态变量的操作存在继承时还是有一些模糊,做了个简单的测试: class Test { private String mName; public Test(String name) { setName ...

  4. java基础 类 & 继承

    类 在Java中,类文件是以.java为后缀的代码文件,在每个类文件中可以有多个类,但是最多只允许出现一个public类,当有public类的时候,类文件的名称必须和public类的名称相同,若不存在 ...

  5. C++ 和 Java 对类继承的差异

    #include <iostream> using namespace std; class Base { public: int i; Base() { i = ; fun(); } v ...

  6. Java: 类继承中 super关键字

    super 关键字的作用有两个: 1)在子类中调用超类的构造器,完成实例域参数的初始化,调用构造器的语句只能作为另一个构造器(通常指的是子类构造器)的第一条语句出现, 2)在子类中调用超类的方法,如: ...

  7. java面向对象类的继承~ 匿名类 ;多态特性;强制类型转换

    类的继承 创建子类语法:     修饰符 class 子类名 extends 父类名{        } 匿名子类语法: 直接实例化,过程中通过匿名类 继承父类,在实例化过程中将子类匿名 <父类 ...

  8. (转)Java:类与继承

    原文地址: http://www.cnblogs.com/dolphin0520/p/3803432.html 对于面向对象的程序设计语言来说,类毫无疑问是其最重要的基础.抽象.封装.继承.多态这四大 ...

  9. Java:类与继承

    Java:类与继承 对于面向对象的程序设计语言来说,类毫无疑问是其最重要的基础.抽象.封装.继承.多态这四大特性都离不开类,只有存在类,才能体现面向对象编程的特点,今天我们就来了解一些类与继承的相关知 ...

随机推荐

  1. DNA拷贝数变异CNV检测——基础概念篇

    DNA拷贝数变异CNV检测——基础概念篇   一.CNV 简介 拷贝数异常(copy number variations, CNVs)是属于基因组结构变异(structural variation), ...

  2. Laravel 上使用 phpexcel的两种方式

    原创 2017年06月24日 20:24:31 1229 文章采集与网上 方式1.使用原生的phpexcel , http://blog.csdn.net/CSwfe/article/details/ ...

  3. JMeter Ant Task 生成的*.jtl打开之后request和response data是空的,怎样让其不是空的呢?

    JMeter Ant Task 生成的*.jtl打开之后request和response data是空的,怎样让其不是空的呢?修改JMeter.properties,将jmeter.save.save ...

  4. TLS/SSL简单过程

    .wcf的认证分为两种 1.1 transport模式,在传输层完成认证(只能在传输层完成认证,利用硬件加速效率更高) a.在使用transport模式,非windows凭证的情况下默认使用TLS/S ...

  5. 学习GIT 版本控制的好去处 另GDB资料

    廖雪峰的官方网站 http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 作者不仅仅是做技 ...

  6. QQ使用技巧

    1.改变真实地理位置 大家知道,现在很多QQ都是显示IP和地理位置的版本,这样一来,自己的位置就暴露了.其实想隐藏自己的位置也简单,只要用代理服务器就是了.不要把它看成很复杂的东西,建议去下载&quo ...

  7. 2018.10.16 spoj Can you answer these queries V(线段树)

    传送门 线段树经典题. 就是让你求左端点在[l1,r1][l1,r1][l1,r1]之间,右端点在[l2,r2][l2,r2][l2,r2]之间且满足l1≤l2,r1≤r2l1\le l2,r1 \l ...

  8. 2018.10.12 NOIP训练 01 串(倍增+hash)

    传送门 一道挺不错的倍增. 其实就是处理出每个数连向的下一个数. 由于每个点只会出去一条边,所以倍增就可以了. 开始和zxyzxyzxy口胡了一波O(n+m)O(n+m)O(n+m)假算法,后来发现如 ...

  9. 19. Fight over Fox-hunting 猎狐引发的冲突

    . Fight over Fox-hunting 猎狐引发的冲突 ① Foxes and farmers have never got on well.These small dog-like ani ...

  10. Linux上查看造成IO高负载的进程

    方法1:使用iotop工具这是一个python脚本工具,使用方法如:iotop -o方法2:使用工具dmesg使用dmesg之前,需要先开启内核的IO监控:echo 1 >/proc/sys/v ...