Java编程思想(一、对象导论)
记录学习中的思考。主要为了帮助自己巩固所学知识,也希望能通过自己的总结,为java初学者提供一些启发。痛并快乐着,坚持就好。
一开始在各种大牛的网上博客上看到推荐《Java编程思想》 买来之后,发现确实还是很不错的一本书,推荐有一定编程基础的人学习。
了解Java。首先从jdk开始介绍。也是最基础的东西。
JRE(Java Runtime Environment)java运行时环境。包括了java虚拟机和java类库。安装了jre的计算机就可以运行java程序。JRE是使用java语言编写的程序运行所需要的软件环境,是提供给想运行java程序的用户使用的。(总的来说,JRE是用来支持java程序运行的)
JDK(Java Development Kit)java开发工具包。JDK包含了JRE,同时还包含了编译java源码的编译器javac,还包含了很多java程序调试和分析的工具:jconsole,jvisualvm等工具软件。是提供给程序员使用的。(总的来说,JDK是用来支持java程序的开发的)
1、面向对象程序设计(Object-oriented Progarmming,OOP)。
2、抽象。人们能够解决的问题的复杂性直接取决于抽象的类型和质量。
3、纯粹的面向对象程序设计方式有这么5个特性。1)万物皆为对象 2)程序是对象的集合,它们通过发送消息来告知彼此所要做的。3)每个对象都有自己的、由其他对象所构成的存储。(比方说是一个Car对象它包含Tyre对象。)
4)每个对象都拥有其类型。也就是每个对象都是某个类的一个实例的意思。5)某一特定类型的所有对象都可以接收同样的消息。很简单理解,每一个人都可以吃饭。
4、每个对象都有一个接口。接口确定了对某一特定对象所能发出的请求。但是,在程序中必须有满足这些请求的代码,这些代码与隐藏的数据一起构成了实现。
Light lt = new Light(); It.on();
Light(类型名称) |
on()接口 off()接口 brighten()接口 dim()接口 |
当对It对象“发送消息”(产生请求),与之相关联的方法就会被调用。上面的代码,使用以下方式创建了一个Light对象:定义这个对象的“引用”,然后调用new方法来创建该类型的新对象。为了向对象发送消息,需要申明对象的名称,并以原点符号连接一个消息请求。
5、每个对象都提供服务。当正在试图开发或理解一个程序设计时,最好的方法之一就是将对象想象为“服务提供者”,程序本身向用户提供服务,它将通过调用其它对象提供的服务来实现这一目的。将对象看作是服务提供者还有一个附带的好处:它有助于提高对象的内聚性。高内聚是软件设计的基本质量要求之一:这意味着一个软件构件(例如一个对象,当然它也有可能是指一个方法或一个对象库)的各个方面“组合”得很好。在良好的面向对象设计中,每个对象都可以很好地完成一项任务,但是它并不试图做更多的事。将对象作为服务提供者是一件伟大的简化工具,这不仅是在设计过程中非常有用,而且当其他人试图理解你的代码或者重用某个对象时,如果他们看出了这个对象所能提供的服务的价值,它会调整对象以适应其设计的过程变得简单很多。
6、被隐藏的具体实现。程序员分为两种:一个是类创建者(那些创建新数据类型的程序员)、二是客户端程序员(那些在其应用中使用数据类型的类消费者)。大部分的程序员都属于第二种,但都应该努力向一转变。不应只满足与业务上的书写。
客户端程序员的目标是收集各种用来实现快速应用开发的类。类创建者的目标是构建类,这种类只向客户端程序员暴露必须的部分,而隐藏其他部分。隐藏的部分通常是对象内部脆弱的部分,一但修改出错,这个接口将直接无法使用。所以,防止客户端程序员不小心修改毁坏,隐藏实现可以减少程序bug,也方便客户端程序员使用。于是便出现了访问控制,一是可以让客户端程序员无法触及他们不应该触及的部分,二是允许库设计者可以改变类内部的工作方式而不用担心会影响到客户端程序员。
在java中,使用了三个关键字在类的内部设定边界:public、private、protected。这些访问指定词决定了紧跟其后被定义的东西可以被谁使用。public表示紧随其后的元素对任何人都是可用的。private表示紧随其后的元素除类型创建者和类型的内部方法之外的任何人都不能访问。private就像是类创建者与客户端程序员之间的一堵墙,如果有人试图访问private成员,就会在编译时得到错误信息。protected关键字与private作用相当,差别仅在于继承的类可以访问protected成员,但是不能访问private成员。Java还有一种默认的访问权限,当没有使用前面提到的任何访问指定词时,它将发挥作用。这种权限通常被称为包访问权限。在这种权限下,类可以访问在同一个包中的其他类的成员,但在包之外,这些成员如同指定了private一样。
7、复用具体实现。一个类一旦被创建并测试完,那么它就应该代表一个有用的代码单元。最简单的复用某个类的方式,就是直接使用该类的一个对象。此外也可以将那个类的一个对象置于某个新的类中。我们将其称为“创建一个成员对象”。新的类可以由任意数量、任意类型的其他对象以任意可以实现新的类中想要的功能的方式组成。在现有的类合成新的类,这种概念称为组合。如果组合式动态发生的,那么它通常被还称为聚合。组合经常被视为“has-a”(拥有)关系。新类的成员通常都被声明为private,使得使用新类的客户端程序员不能访问它们。这也使得你可以在不干扰现有客户端代码的情况下,修改这些成员,也可以在运行时修改这些成员对象,以实现动态修改程序的行为。
8、继承。当继承现有类型时,也就创造了新的类型。这个新的类型不仅包括现在类型的所有成员(包括private成员),而且它复制了基类的接口。也就是说,所有可以发送给基类对象的消息同时也可以发送给导出类对象。导出类与基类具有相同的类型。基类与导出类具有相同的基础接口,所以伴随这些接口必定有某些具体的实现。简单地继承一个类而不做其他任何事那么基类接口中的方法将会直接继承到导出类中。这样意味着导出类的对象不仅与基类具有相同的类型,而且还具有相同的行为,这样做没什么特别意义。有两种方法可以使基类与导出类产生差异。第一种:直接在导出类中添加新方法。这意味着基类方法无法满足你的所有需求,因此必须添加更多的方法。第二种:直接改变现有基类的方法的行为,覆盖基类方法。
9、“是一个”与“像是一个”关系。如果导出类只是覆盖基类方法。那么导出类和基类就是完全相同的类型。这就是纯粹替代。这种情况吓得基类与导出类之间的关系称为是is-a的关系。有时如果必须在导出类型中添加新的接口元素,这样也扩展了接口。虽然这个新的类型仍然可以替代基类,但是这种替代并不完美,基类无法访问新添加的方法。这种情况就是 is-like-a 的关系。
10、伴随多态的可互换对象。有时候,想把一个对象不当作它所属的特定类型来对待,而是将其当作基类的对象来对待。这使得人们可以编写出不依赖于特定类型的代码。编译器不可能产生传统意义上的函数调用。一个非面向对象程序的编译器产生的函数调用会引起所谓的前期绑定,这么做意味着编译器将产生对一个具体函数名字的调用,而运行时将这个调用解析到将要被执行的代码的绝对地址。然而在OOP中,程序直到运行时才能确定代码的地址,所以当消息发送到一个泛化对象时,必须采用其他的机制。在面向对象程序设计语言中,使用了后期绑定,当向对象发送消息时,被调用的代码直到运行时才能确定。编译器确保被调用方法的存在,并对调用参数和返回值执行类型检查(无法提供此类保证的语言被称为是弱类型),但是并不知道将被执行的确切代码。在执行后期绑定中,java使用一小段特殊的代码来代替绝对地址的调用。这段代码使用在对象中存储的信息来计算方法体的地址。这样,根据这一小段代码的内容,每一个对象都可以有不同的行为表现。jvm运行时会确定对象的正确类型,而后调用正确的方法。
11、单根继承结构。在java中,所有的类都最终继承自单一的基类(Object)。这能保证所有对象都具备某些功能(Object基类的接口)。这使得垃圾回收器的实现变得容易很多。所有对象都保证具有其类型信息。因此不会因无法确定对象的类型而陷入僵局。
12、容器。用于存放对象。在java中拥有能满足不同需要的各种类型的容器。List(用于存储序列),Map(关联数组,用来建立对象之间的关联),Set(每种对象类型只持有一个),还有队列,树,堆栈等构件。从设计角度来看,真正只需要一个可以被操作,从而解决问题的序列就可以了。但是实际上还是要对容器有所选择。第一:不同容器提供了不同类型的接口和外部行为。比如堆栈和队列。这两个是不一样的。第二:不同的容器对某些操作具有不同的效率。比如ArrayList和LinkedList的区别。在ArrayList中,随机访问元素是一个花费固定时间的操作。但对于LinkedList中,随机选取元素需要在列表中移动。这种代价是高昂的,越靠近列尾的元素,搜索的时间就越长。
13、参数化类型。也就是熟知的范型。
ArrayList<Shape> shapes = new ArrayList<Shape>();//编译器会自动检查传入的参数是否符合要求。
14、对象的创建和生命周期。C++认为效率控制是最重要的议题,所以给了程序员选择的权力。为了追求最大的执行速度。对象的存储空间和生命周期可以在编写程序时确定,这可以通过将对象置于堆栈或限域变量或静态存储区域内来实现。这种方式将存储空间分配和释放置于优先考虑的位置,某些情况下这样控制非常有价值。但是,这也牺牲了灵活性。因为必须在编写时知道对象确切的数量、生命周期和类型。第二种方式是在堆的内存池中动态创建对象。在这种方式中,直到运行时才知道需要多少对象,它们的生命周期如何,以及它们的具体类型。这样的话,因为存储空间是在运行时被动态管理的所以需要大量的时间在堆中分配存储空间,这可能要远远大于在堆栈中创建存储空间的时间。堆栈中创建和释放存储空间只需要一条汇编指令即可,分别对应栈顶指针向下移动还是向上移动。而创建堆存储空间的时间依赖于存储机制的设计。
Java完全采用了动态内存分配方式。每当想要创建新对象时,就要使用new关键字来构件此对象的动态实例。
对象的生命周期。对于允许在堆栈上创建对象的语言,编译器可以确定对象存活的时间,并可以自动销毁它。然而,如果是在堆上创建对象,编译器就会对它的生命周期一无所知。java提供了垃圾回收器。它可以自动发现对象何时不再被使用,并继而销毁它,不同于C++必须手动管理内存。
15、异常处理。为了程序员更方便地dbug。让程序免于崩溃,可以继续运行下去。
16、并发编程。同一时刻处理多个任务。一般来说,只有被指派给不同的处理器,那才是真正地并行执行。在语言上,多线程可以让程序员不用在乎机器上到底是多个处理器还是只有一个处理器。