java语言设计对象导论二

版权声明:此文章转载自_infocool

原文链接:http://www.infocool.net/kb/Java/201610/205483.html

如需转载请联系听云College团队成员小尹 邮箱:yinhy#tingyun.com

今天继续写java编程思想中的,对象导论问题。一起来探索java的奥秘。

1.1继承。

有了对象的概念我们就可以形象的描述问题空间的表示,而不用受制于必须使用底层机器语言。这些概念用class表示,这是变成的基本单位。遗憾的是这样做有很多麻烦,比如我们创建一个类,即使另一个新类与其具有相似的功能,我们也不得不重新建立一个新类,如果我们能以此为基础,复制他,然后通过添加和修改副本来创建新类,那就好多了。通过继承便可打到这效果。不过当几类发生改变时,副本也会发生改变。

两个类型可以有相同的特性的和行为,但其中一个类型可能具有更多的特性和行为,并且可以处理更多的消息。继承使用基类和导出类型的概念来表示两者之间的相似性。一个基类包含它所有导出类型的共享特性和行为。其他导出类型表示次核心何以实现的各种不同方式。比如我们把几何形比作基类,每一个几何形都具有,尺寸,颜色,位置,都可以被移除,绘制,着色,等。再次基础上可以导出继承类具体的几何形状——圆形,正方形,三角形。而每一种都具有额外的特征和属性。当继承现有类型时,不仅包含了基类的所有成员,同时也复制了基类的接口。也就是说发给基类对象的消息,也可以发给导出类。这意味这导出类和基类具有相同的类型。

由于基类和导出类具有相同的接口,所以伴随此接口必定有某些实现。也就是说当对象接收到某消息时,必定有某些代码去执行,如果只是简单的继承,而不做任何处理,这样做将没有任何意义。有两种方法可以使基类和导出类产生差异:一种是直接在导出类添加新的方法,另一种是覆盖基类的方法创建该方法的新定义,以做不同的事情。

1.2是一个还是像一个的关系。

继承应该只覆盖基类的方法,如果这样做就意味着导出类和基类是完全相同的类型,因为他们具有完全相同的接口。结果可以用一个导出类对象,来替换基类对象。这可以被视为纯粹替换。在某种意义上,这事处理继承的理想方式。我们在这种情况下称导出类和基类是is-a(是一个)的关系。有时候必须在导出类中添加新的接口,这样也就是扩展接口。这个新的类型仍然可以代替基类,但是这种代替不完美,因为基类无法访问新添加的方法。这种情况我们可以描述为is-like-a(像一个)的关系。

1.3伴随多态的可互换对象。

先看下面的截图,以便更好的理解多态。

java语言设计对象导论二

BirdController对象仅仅处理泛化的Bird对象,而不了解他们的确切实现类,从BirdController角度来看,这么做很方便,不许要编写特定的代码来判定确切的实现类的类型。当调用move方法时,每个实现类都会正确的执行各自的行为。

这就是面向对象程序设计的最重要的秘诀:编译器在运行时将这个调用解析到将要被执行代码的绝对地址。然而在oop中,程序直到运行时才知道具体的地址。所以当发送一个消息到泛化对象时,必须采用别的机制。为了解决这个问题,面向对象采用了后期绑定的概念。在对泛化类型发送消息时,编译器只需要去报被调用方法的存在,并对调用参数和返回值执行类型检查。为了执行后期绑定,java使用了一小段特殊的代码,来代替绝对地址调用。这段代码使用在对象中存储的信息来计算方法提的地址。这样就可以使每一个对象具有不同的行为表现。

下面来写一个例子:

   void doSometh(Shape shape){
    shape.erase();
    shape.draw();
}

这个方法可以和任何Shape对话,因此他是独立于它要绘制和移除的对象的具体类型。

  
 public class Circle extends Shape {
public static void main(String[] args) {
// TODO Auto-generated method stub
}
@Override
public void onDraw() {
// TODO Auto-generated method stub
super.onDraw();
}
}
public class TestOne {
public static void main(String[] args) {
// TODO Auto-generated method stub
Circle circle = new Circle();
doSomething(circle);
}
private static void doSomething(Circle circle) {
// TODO Auto-generated method stub
}
}

这样当java编译器调用doSomething方法时,并不知道要处理的确切类型。所以通常期望它的编译结果使调用基类的方法,而不是具体实现类的方法。

正式因为多态,才使得事物总是能够被正确处理。编译器和运行系统能够处理相关的细节。

1.4单根继承结构

1.5容器

通常来说,如果不知道在解决某些问题时,需要多少个对象,或者他们能存活多久,那么就不可能知道如何存储对象。对于面向对象设计的大多数语言来说,这个问题的解决方案似乎有点轻率(这个问题各位网友可以互相探讨一下,我不太理解。):创建另一种对象类型,来持有其他对象类型的引用。当然java提供了集合这个对象来实现,我们称之为容器的新对象。在任何需要的时候都可以扩充自己以容纳你至于其中的任何东西。java提供了满足不同类型的容器,比如List(用于存储序列),Map(关联数组,建立对象之间的关系),Set(每种对象类型只有一个).我们需要堆容器有所选择。第一不同的容器提供了不同类型的接口和外部行为。第二不同的容器对于不同的操作具有不同的效率。ArrayList中随机访问元素时花费固定事件的操作,但在LinkedList中随机选取元素需要在列表中移动,这种代价时高昂的。但是需要在序列中插入一个元素,LinkedList要比ArrayList要简单高效的多。

1.6参数化类型

在java5出现之前容器存储的对象都时具有java中的通用类型:object.单根继承结构保证了,可以存储Object的容器可以存储任何东西。要使用这样的容器,只需要在其中置入对象的引用,稍后还可以将他取回。因为容器只存储Object,所以将对象置入时,必须向上转型为Object,因此会丢失其身份。所以将他取回时就得到一个Object的引用,而不是对置入对象的引用。所以为了将他变为植入前的对象,这里再度使用转型,向上转型是安全的,向下转型是不安全的,因为不知道Object是某个具体类型。所以除非你知道具体的类型否则向下转型是不安全的。

java5提供了参数化类型,在java中称之为泛型。一对小尖括号,中间包含类型值信息。这就是参数化类型,编译器可以定制一个只接收和去除某个对象的容器。

ArrayList<Shape> array = new ArrayList<Shape>();

1.7对象的创建和生命周期

java使用关键字new来创建对象。

程序中的处理对象是很困难的过程,假如处理完某个对象之后,系统的某个部分可能还在处理他。那么怎么控制对象的生命周期呢。在C++中为了追求更快的速度,对象的存储空间和生命周期可以在编写程序时确定,这可以将对象置于堆栈或静态存储区域来实现。这种方式将空间分配和释放置于优先考虑的位置。但是缺乏灵活性。第二种方式就是在被称为堆的内存池中动态的创建对象。java完全采用动态内存分配方式。没当用户像创建对象时,就是用new关键字来构建此对象的动态实例。允许在堆上创建对象的语言,编译器可以确定对象的存活时间,并可以自动销毁。java提供了垃圾回收器的机制,他可以自动发现对象何时不被使用,并继续而销毁他。

1.8异常处理:处理错误

异常处理将错误处理直接置于编程语言中,有时置于操作系统中,异常是一种对象,他在出错点被抛出,并被专门设计用来处理特定类型错误的异常处理器“捕获”,异常处理与程序正常执行是并行的,在错误发生时执行的是另一条路径。不会干扰正常的代码。被抛出的异常不像方法所返回的错误值可以被忽略。异常不可被忽略,所以保证它一定在某处得到处理。java一开始就内置了异常处理,而且强制必须使用它。尽管在面向对象中异常常常被表示为一个对象,但异常不是面向对象的特征。

1.8并发编程

并发编程就是同一时间处理多个任务的思想,我们把问题切分成多个可独立运行的部分,从而提高程序的响应能力。在程序中,这些彼此独立运行的部分被称之为线程,上述概念称之为并发。通常线程只是为单一处理器分配执行时间的手段。但是如果操作系统支持多处理器,那么每个任务都可以被分配给独立的处理器,并且他们是真正的执行。但是有一个隐患:共享资源。如果有多个并行任务要访问同一个资源,那么就出问题了。所以共享资源必须在使用期间被锁定。因此整个过程是:某个任务锁定某项资源,完成其任务,然后释放资源,使其它任务可以使用这个资源。想一下我们买火车票的过程,一个车票只能被一个身份证购买者所操作,在操作的过程中就是锁定的过程。


想阅读更多技术文章,请访问听云技术博客,访问听云官方网站感受更多应用性能优化魔力。

关于作者

coco秋洁

我爱学习,学习使我快乐

我要评论

评论请先登录,或注册