工欲善其事必先利其器! java
再快不能快基础,再烂不能烂语言! 程序员
"咱们之因此将天然界分解,组织成各类概念,并按其含义分类,主要是由于咱们是整个口语交流社会共同遵照的协定的参与者,这个协定以语言的形式固定下来......除非同意这个协定中规定的有关语言信息的组织和分类,不然咱们根本没法交谈。———Benjamin Lee Whorf( 1897~1941 )"
建立抽象数据类型(类)是面向对象程序设计的基本概念之一。编程
抽象数据的运行方式与内置(built-in)类型几乎彻底一致:你能够建立某一类型的变量(按 照面向对象的说法,称其为对象或者实例),而后操做这些变量(称其为发送消息或者请 求;发送消息,对象就知道要作什么)。数组
每个对象都属于定义了特性和行为的某个特定的类。安全
由于类描述了具备相同特性(数据元素)和行为(功能)的对象集合,因此一个类实际上就是一个数据类型。bash
接口肯定了对某一特定对象所能发出的请求。并发
高内聚低耦合函数
内聚: 是从功能角度来度量模块内的联系,一个好的内聚模块应当刚好作一件事。它描述 的是模块内的功能联系;工具
耦合: 是软件结构中各模块之间相互链接的一种度量,耦合强弱取决于模块间接口的复杂 程度、进入或访问一个模块的点以及经过接口的数据。布局
高内聚低耦合,是软件工程中的概念,是判断设计好坏的标准,主要是面向对象的设 计,主要是看类的内聚性是否高,耦合度是否低。
耦合性: 也称块间联系。指软件系统结构中各模块间相互联系紧密程度的一种度量。模 块之间联系越紧密,其耦合性就越强,模块的独立性则越差。模块间耦合高低取决于模 块间接口的复杂性、调用的方式及传递的信息
内聚性: 又称块内联系。指模块的功能强度的度量,即一个模块内部各个元素彼此结合 的紧密程度的度量。若一个模块内各元素(语名之间、程序段之间)联系的越紧密,则 它的内聚性就越高。
所谓高内聚是指一个软件模块是由相关性很强的代码组成,只负责一项任务,也就是常 说的单一责任原则。
耦合: 一个软件结构内不一样模块之间互连程度的度量。
对于低耦合,粗浅的理解是:一个完整的系统,模块与模块之间,尽量的使其独立存 在。 也就是说,让每一个模块,尽量的独立完成某个特定的子功能。模块与模块之间的 接口,尽可能的少而简单。若是某两个模块间的关系比较复杂的话,最好首先考虑进一步 的模块划分。这样有利于修改和组合。
将对象看做是服务提供者还有一个附带的好处:它有助于提升对象的内聚性。高内聚是 软件设计的基本质量要求之一:这意味着一个软件构造(例如一个对象,固然它也有可能 是指一个方法或一个对象库)的各个方面“组合”得很好。人们在设计对象时所面临的一个 问题是,将过多的功能都塞在一个对象中。
在良好的面向对象设计中,每一个对象均可以很好地完成一项任务,可是它并不试图作更多的事情。
将对象做为服务提供者看待是一件伟大的简化工具,这不只在设计过程当中很是有用,而 且当其余人试图理解你的代码或重用某个对象时,若是他们看出了这个对象所能提供的 服务的价值,它会使调整对象以适应其设计的过程变得简单得多。
代码复用是面向对象程序设计语言锁提供的最了不得的优势之一。
新的类型能够由任意数量,任意类型的其余对象以任意能够实现新的类中想要的功能的方式组成。
由于是在使用现有的类合成新的其余对象的类,因此这种概念被称为组合(composition),若是组合是动态发生的,那么它一般被称为聚合(aggregation)。
在创建新类时,应该首先考虑组合,由于它更加简单灵活。 若是采用这种方式,设计会变得更加清晰,一旦有了一些经验以后,便可以看出必须使用继承的场合了。
当源类(被称为基类,超类或父类)发生变更时,被修改的“副本”(被称为导出类,继承类或者子类)也会反应出变更。
能够建立一个基类来表示系统中某些对象的核心概念,从基类型中导出其余类型,来表示此核心能够被实现的各类不一样方式。
当继承现有类型时,也就建立了新的类型。 这个新的类型不只包括如今类型的全部成员(尽管private成员被隐藏起来,而且不可访问),并且更重要的是它复制了基类的接口。也就是说,全部能够发送给基类对象的消息同时也能够发送到导出类对象。
经过继承而产生的类型等价性是理解面向对象程序设计方法内涵的重要门槛!
有两种方法可使基类与导出类产生差别
继承应该只覆盖基类的方法(而不添加在基类中没有的新方法),在某种意义上,这是一种处理继承的理想方式。咱们常常将这种状况下的基类与导出类之间的关系称为is-a(是一个)关系。
有时必须在导出类型添加新的接口元素,这样也就扩展了接口。这种状况咱们能够描述为is-like-a(像是一个)关系。
经过导出新的子类型而轻松扩展设计的能力是对改动进行封装的基本方式之一。
这种能力能够极大地改善咱们的设计,同时也下降软件维护的代价。
面向对象程序设计的最重要妙诀:编译器不可能产生传统意义上的函数调用。 一个非面向对象编程的编译器产生的函数调用会引发所谓的前期绑定 ,这个术语你可能之前从未据说过,可能从未想过函数调用的其余方式。这么作意味着编译器将产生对一个具体函数名字的调用,而运行时将这个调用解析到将要被执行的代码的绝对地址。然而在OOP(面向对象编程)中,程序直到运行时才可以肯定代码的地址,因此当消息发送到一个泛化对象时,必须采用其余的机制。
当向对象发送消息时,被调用的代码直到运行时才能肯定。 编译器确保被调用方法的存在,并对调用参数和返回值执行类型检查(没法提供此类保证的语言被称为是弱类型的),可是并不知道将被执行的确切代码。
在java中,动态绑定是默认行为,不须要添加额外的关键字来实现多态。
把将被导出类看作是它的基类的过程称为向上转型。
转型这个名称的灵感来自于模型铸造的塑模动做;而向上(up)这个词来源于继承图的典型布局方式;一般基类在顶部,而导出类在其下部散开。所以,转型为一个基类就是在继承图中向上移动,即“向上转型”。
正是由于多态才使得事情老是可以被正确处理。编译器和运行系统会处理相关的细节,你须要立刻知道的只是事情会发生,更重要的是怎样经过它来设计。当向一个对象发送消息时,即便涉及向上转型,该对象也知道要执行什么样的正确行为。
容器(也称为集合,不过java类库以不一样的含义使用“集合”这个术语,因此本书使用“容器”这个词),在任何须要时均可扩充本身以容纳你置于其中的全部东西。所以不须要知道未来会把多少个对象置于容器中,只须要建立一个容器对象,而后让它处理全部细节。
Java容器
使用时仍是要对容器有所选择,缘由以下:
咱们能够在一开始使用LinkedList构建程序,而在优化系统性能时改用ArrayList。接口List所带来的抽象,把在容器之间进行转换时对代码产生的影响下降到最小限度。
参数化类型(范型) 在JavaSE5以前,容器存储的对象都只具备Java中的通用类型:Object。
单根继承结构意味着全部东西都是Object类型,因此能够存储Object的容器能够存储任何东西。这使得容器很容易被复用。
向上转型是安全的,例如Circle是一种Shape类型;可是不知道某个Object是Circle仍是Shape,因此除非确切知道所要处理的对象的类型,不然向下转型几乎是不安全的。
若是向下转型错误的话,就会获得被称为异常的运行时错误。
参数化类型(范型)就是一个编译器能够自动定制用于特定类型上的类。 例如,经过使用参数化类型,编译器能够定制一个只接纳和取出Shape对象的容器。
为了利用泛型的优势,不少标准类库构建都已经进行了修改。就像咱们将要看到的那样,范型对本书中的许多代码都产生了重要的影响。
在使用对象时,最关键的问题之一即是它们的生成和销毁方式。
每一个对象为了生存都须要资源,尤为是内存。当咱们再也不须要一个对象时,它必须被清理掉。使其占有的资源能够被释放和重用。
为了追求最大的执行速度,对象的存储空间和生命周期能够在编写程序时肯定。
这能够经过将对象置于堆栈(它们有时被称为自动变量)或限域变量或静态存储区域内来实现。
这种方式将存储空间分配和释放置于优先考虑的位置,某些状况下这样的控制很是有价值。可是,也牺牲了灵活性,由于必须在编写程序时知道对象确切的数量,生命周期和类型。
第二种方式是在被称为堆(heap)的内存池中动态地建立对象。
在这种方式中,直到运行时才知道须要多少对象,它们的生命周期如何,以及它们的具体类型是什么。
若是须要一个新对象,能够在须要的时刻直接在堆中建立。由于存储空间是在运行时被动态管理的,因此须要大量的时间在堆中分配存储空间,这可能要远远大于在堆栈中建立存储空间的时间。
建立堆栈存储空间的时间依赖于存储机制的设计
动态方式有这样一个通常性的逻辑假设:对象趋向于变得复杂。因此查找和释放存储空间的开销不会对对象的建立形成重大冲击。动态方式所带来的更大的灵活性正是解决通常化编程问题的要点所在。
java彻底采用动态内存分配方式。每当想要建立新对象时,就要使用new关键字来构建此对象的动态实例。
还有一个议题,就是生命周期。对于容许在堆栈上建立对象的语言,编译器能够肯定对象存活的时间,并能够自动销毁它。
java提供了被称为“垃圾回收器”的机制,它能够自动发现对象什么时候再也不被使用,并继而销毁它。
垃圾回收器很是有用,由于它减小了所必须考虑的议题和必须编写的代码。更重要的是,垃圾回收器提供了更高层的保障,能够避免暗藏的内存泄漏问题。
java的垃圾回收器被设计用来处理内存释放问题(尽管它不包括清理对象的其余方面)。垃圾回收器“知道”对象什么时候再也不被使用,并自动释放对象占用的内存。
"若是咱们说另外一种不一样的语言,那么咱们就会发觉一个有些不一样的世界。——Luduing Wittgerstein( 1889~1951 )"
java中,一切都被视为对象,所以可采用单一的语法。尽管一切都看做是对象,但操做的标识符其实是对象的一个“引用”。
例:遥控器(引用)操做电视机(对象)。
寄存器: 最快的存储区,但因为寄存器的数量极其有限,因此根据需求进行分配。你不能直接控制,在程序中也感受不到寄存器存在的任何迹象。
堆栈: 位于通用RAM(随机访问存储器)中。堆栈指针若往下移则分配新内存,若往上移动则释放内存。
堆栈:就是STACK。其实是只有一个出入口的队列,即后进先出(First In Last Out),先分配的内存一定后释放。通常由系统自动分配,存放函数的参数值,局部变量等,自动清除。
堆栈是每一个函数进入的时候分一小块,函数返回的时候就释放了
局部变量放在堆栈中,因此函数返回,局部变量就全没了。
堆: 一种通用的内存池(位于RAM区),用来存放全部的Java对象。
当须要一个对象时,只需用new写一行简单的代码,当执行这行代码时,会自动在堆里进行存储分配。
用堆进行存储分配和清理可能比用堆栈进行存储分配须要更多的时间。
常量存储: 常量值一般是直接存放在程序代码内部。
非RAM存储: 若是数据彻底存活于程序以外,那么它能够不受程序的任何控制,在程序没有运行也能够存在。例如:流对象和持久化对象。
class Banana { void peel(int i) {/*...*/} }
public class BananaPeel{
public static void main(String[] args) {
Banana a = new Banana();
Banana b = new Banana();
a.peel(1);
b.peel(2);
}
}
复制代码
【问题】peel()如何知道是被a仍是被b调用的呢?
【答】:为了能简便面向对象的语法来编写代码,编译器暗自把“操做对象的引用”做为第一个参数传递给peel(),
a.peel(a,1);这是内部表现形式。
复制代码
java中的垃圾回收器负责回收无用对象占据的内存资源。
垃圾回收器只知道释放由new分配的内存。
finalize():在垃圾回收器准备好释放回收对象占用的存储空间以前调用的方法,能够自定义作一些清理工做。
【问题】finalize()和析构函数的区别:
析构函数(C++中销毁对象必须用这个函数):“析构”函数与构造函数相反,当对象结束其生命周期,
如对象所在的函数已调用完毕时,系统自动执行析构函数。析构函数每每用来作“清理善后”的工做。
复制代码
Java对象并不是老是被垃圾回收:
在java中,只要程序没有濒临存储用完的那一刻,对象占用的空间就总也得不到释放。
使用垃圾回收器的惟一缘由是为了回收程序再也不使用的内存。
finalize()的用途之——释放空间:
当代码中使用“本地方法”的状况下,好比调用C的malloc()函数系列来分配存储空间时,须要在finalize中调用free()来释放内存空间。
注:本地方法是一种在java中调用非java代码,通常用“native”关键字修饰。
finalize()的用途之——终结条件验证:
在对象资源被释放以前验证对象的某个状态,将验证内容放在finalize()的方法中,能够避免一些因为代码书写问题致使的缺陷。
注:【Effective Java】中“避免使用终结方法和清楚方法”一节中提到:终结方法(finalize)一般是不可预测的,也是很危险的,通常状况下是没必要要的。使用终结方法会致使行为不稳定,性能下降,以及可移植性问题,根据经验,应该避免使用终结方法。
垃圾回收器如何工做
在堆上分配对象代价是十分高昂的,所以java中全部对象的分配方式也是很是高昂的。然而垃圾回收器对于提升对象的建立速度,却具备明显的效果。
垃圾回收器的实现:
垃圾回收器依据的思想是:对任何“活”的对象,必定能最终追溯到其存在在堆栈或静态存储区之中的引用。
引用计数器: 是一种简单但速度很慢的垃圾回收技术。
每一个对象都含有一个引用计数器,当有引用链接至对象时,引用计数加1。当引用离开做用域或设置为null的时候,引用计数器减1。垃圾回收器会在含有所有对象的列表上便利,当发现某个对象的引用计数为0时,就释放其占用的空间。但循环引用会致使“对象应该被回收,计数却不为0”。
引用计数未被应用于任何一种java虚拟机实现中。
中止,复制:
先暂停程序的运行(不属于后台回收模式),而后将全部存活的对象从当前堆复制到另外一个堆,没有被复制的所有都是垃圾。当把对象从一处搬到另外一处时,全部指向它的那些引用都必须修正了。位于堆或静态存储的引用能够直接被修改,但还有其余指向的引用,在遍历的过程才能被找到。
这种方式,效率会很低。一方面须要在两个分离的堆之间来回倒腾;另外一方面是程序稳定后,可能只会产生少许垃圾,甚至没有垃圾,但仍然会将全部的内存自一处复制到另外一处。
垃圾回收动做不是在后台进行的,垃圾回收动做发生时,程序会被暂停。
“中止-复制”要求在释放旧的对象以前,必须把全部存活的对象从旧堆里复制到新堆。这将致使大量内存复制行为。
标记,清扫
从堆栈和静态存储区出发,遍历全部的引用,进而找出全部存活的对象。每当找到一个存活对象,就会给对象设一个标记,但这时不回收任何对象。只有所有标记工做完成,才开始清理。没有标记的将被释放,有标记才会进行复制动做。必须在程序暂停的状况下才能进行。
“自适应的,分代的,中止-复制,标记-清扫”式垃圾回收器
类中的变量初始化在构造器以前
不管建立多少个对象,静态数据都只占一份存储区域。
static关键不能应用于局部变量,只能做用于域。
若是是一个静态的基本类型域,且没有初始化,那么它就会得到基本类型的标准初值。
若是它是一个对象引用,那么它的默认初始值就是null。
静态存储初始化时间:
类中的静态成员都会随着类的加载而加载,好比建立对象时,或者是被引用时!
类中的静态成员都会随着类的加载而加载,好比建立对象时,或者是被引用时!
类中的静态成员都会随着类的加载而加载,好比建立对象时,或者是被引用时!
初始化的顺序是先静态对象,然后“非静态”对象。
对象的建立过程
【推荐篇】- 书籍内容整理笔记 | 连接地址 |
---|---|
【推荐】【Java编程思想】【笔记】 | juejin.im/post/5dbb7a… |
【推荐】【Java核心技术 卷Ⅰ】【笔记】 | juejin.im/post/5dbb7b… |
后期持续更新中。。。。。。
如有书写错误的地方,欢迎留言,但愿咱们能够一块儿进步,一块儿加油!😜😜