(thing in java) 阅读笔记(第一章至第六章)

java语言支持四种类型:接口,类,数组和基本类型。
前三种类型通常被成为引用类型,
类实例和数组是对象,而基本类型的值则不是对象。
类的成员由它的域、方法、成员类和成员接口组成。
方法的签名由它的名称和所有参数类型组成,签名不包括它的返回类型。
类、接口、构造器、成员以及序列化形成被统称为API元素。
导出的API由所有可在定义该API的包之外访问的API元素组成。

静态工厂方法与构造器不同的第一大优势在于,它们有名称。如果构造器本身没有确切地描述正被返回的对象,那么具有适当名称的静态工厂会更容易使用,产生的客户端代码更易于阅读。一个类只能有一个带有指定签名的构造器,编程人员通过提供两个构造器来避免,它们的参数列表只在参数类型的顺序上有所不同。

静态工厂方法构造器不同的第二大优势在于,不必每次调用它们的时候都创建一个新对象。静态工厂方法能够为重复的调用返回相同对象,这些有助于总能严格控制某个时刻哪些实例存在。

静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类型的对象。这种灵活性的应用时,API可以返回,同时又不会使对象的类变成公有的。以这种方式隐藏实现类会是API变得非常简洁。这项技术适用于基于接口的框架,因为在这种框架中,接口为静态工厂方法提供了自然返回类型。接口不能有静态方法,因此按照惯例,接口Type的静态工厂方法被放在一个名为Type的不可实例化的类中。
公有的静态工厂方法所返回的对象的类不仅可以是非公有的,而且该类还尅随着每次调用而发生变化。这取决于静态工厂方法的参数值。


Thinking in Java 
所有变成语言都是提供抽象机制。可以认为,人们所能解决的问题的负载型直接取决于抽象的类型和质量。
汇编语言是对底层机器语言的轻微抽象,这些语言在汇编语言基础上有了大幅的改进,但是它们所做的主要抽象扔要求在解决问题时要基于计算机的结构,而不是基于所要解决的问题的结构来考虑。
面向对象方式通过向程序提供表示问题空间中的元素的工具而更进了一步。这种表示方式非常通用。使得程序员不会受限于任何特定类型的问题。
这种思想的本质是:程序可以通过新类型的对象使自身适用于某个特定问题。,当你在阅读描述解决方案的代码的同时,也是在阅读问题的表述。OOP中每个对象看起来都有点像一台微型计算机,它具有状态,还具有操作,用于可以要求对象执行这些操作。如果要对现实世界中的对象做类比,它们都具有特性和行为似乎不错。
java表现了一种纯粹的面向对象程序设计方式:
1、万物皆为对象。将对象视为奇特的变量,它可以存储数据,除此之外,你还可以要求它在自身上执行操作。理论上讲,你可以抽取待求解问题的任何概念化构件,将其表示为程序中的对象。
2、程序是对象的集合,它们可以通过发送消息来告知彼此所要做的。
3、每个对象都有自己的由其他对象所构成的存储。换句话说,可以通过创建包含现有对象的包的方式来创建新类型的对象,因此,可以在程序中构建复杂的体系,同时将其复杂性隐藏在对象的简单性背后。
4、每个对象都有其类型。每个对象都是某个类的一个实例。这里类就是类型的同义词。每个类最重要的的区别在于其他类的特性就是可以发送什么样的信息给它。
5、某一特定类型的所有对象都可以接受同样的消息。圆形类型的对象同时也是几何形类型的对象,所以一个圆形对象必定能够接受发送给几何形对象的消息,这意味着可以编写与几何形交互并自动处理所有与几何形性质相关的事事物的代码。
6、对象具有状态、行为和标识。这意味着每一个对象都可以拥有内部数据(它们给出了该对象的状态)和方法(它们产生行为),并且每一个对象都可以唯一地与其他对象区分开来,具体说来,就是每一个对象在内存中都有一个唯一的地址。

类描述了具有相同特性(数据元素)和行为(功能)的对象集合,所以一个类实际上就是一个数据类型。差别在于,程序通过定义类来适应问题,而不再被迫只能使用现有的用来表示机器中的存储单元的数据类型,可以根据需求,通过添加新的数据类型来扩展变成编程语言。变成系统欣然接受新的类,并且像对待内置类型一样照管它们和进行类型检查。

接口确定了对某一特定对象所能发出的请求。但是,在程序中必须有满足这些请求的代码。这些代码与隐藏的数据一起构成了实现。在类型中,每一个可能的请求都有一个方法与之相关联,当向对象发送请求时,与之相关联的方法就会被调用。此过程通常概括为,向某个对象发送消息,这个对象便知道此消息的目的,然后执行对应的程序代码。
Light it =new Light();  
it.on();
类/类型的名称是Light,特定的Light对象的名称是It,可以向Light对象发出的请求是:打开它,关闭它,将它调亮,将它调暗。你以下列方式创建了一个Light对象:定义这个对象的引用(It),然后调用new方法来创建该类型的新对象。为了向对象发送消息,需要声明对象的名称,并以圆点连接一个消息请求。

当正在视图开发或理解一个程序设计时,最好的办法之一就是将对象想象为“服务提供者”。程序本身将向用于提供服务,它将通过调用其他对象提供的服务来实现这一目的。你的目标就是去创建能够提供立项的服务来解决问题的一系列对象。

复用具体实现
一旦类被创建并测试完,那么它应该代表一个有用的代码单元。
最简单的复用某个类的方式就是使用该类的一个对象,此外也可以将那个类的一个对象置于某个新的类中。我们称其为“创建一个成员对象”。新的类可以由任意数量、任意类型的其他对象以任意可以实现新的类中想要的功能的方式所组成。因为是在使用现有的类合成新的类,所以这种概念成为组合。

继承
由于通过发送给类的消息的类型可知类的类型,所以这也就意味着导出类与基类具有相同的类型。在前面例子中,一个圆形也就是一个几何形。通过继承而产生的类型等价性是理解面向对象程序设计方法内涵的重要门槛。

伴随多态的可互换对象
编译器不可能产生传统意义上的函数调用。一个非面向对象编程的编译器产生的函数调用会引起所谓的前期绑定。这么做意味着,编译器将产生对一个具体函数名字的调用,而运行时将这个调用解析到将要被执行的代码的绝对地址。在OOP中,程序直到运行时才能够确定代码的地址,所以当消息发送到一个泛化对象时,必须采用其他的机制。
为了解决这个问题,面向对象程序设计语言试用了后期绑定的概念。当向对象发送消息时,被调用的代码直到运行时才能确定。编译器确定被调用方法的存在,并对调用参数和返回值执行类型检查(无法提供此类保证的语言被称为弱类型),但是并不知道将被执行的确切代码。
为了执行后期绑定,java使用一小段特殊的代码来替代绝对地址调用。这段代码使用在对象中存储的信息来计算方法体的地址。

将Circle被传入到预期接受Shape的方法中,究竟会发生什么。由于Circle可以被doSomething()看作是
shape,也就是说,dosomething()可以发送给shape的任何消息,circle都可以接受,那么,这么做就是完全
且合乎逻辑的。
把将导出类看作是它的基类的过程称为向上转型。转型这个名称的灵感来自于模型铸造的塑膜动作;而向上
这个词来源于继承图的典型布局方式:通常基类在顶部,而导出类在其下部散开,因此,转型为一个基类就
是在继承图中向上移动,即向上转型。

一个面向对象程序肯定会在某处包含向上转型,因为这正是将自己从必须知道确切类型中解放出来的关键。

在单根继承结构中的所有对象都具有一个共用接口,所以它们归根到底都是相同的基本类型。另一种结构(
C++)是无法确保所有对象都属于同一个基本类型的。
单根继承结构保证所有对象都具备某些功能。因此你知道,在你的系统中你可以在每个对象上执行某些基本操作
所有对象都可以很容易地在堆上传递,而参数传递也得到了极大的简化。
单根继承结构使垃圾回收期的实现变得容易得多,而垃圾回收器正是java相对C++的重要改进之一。由于所有
对象都保证具有其类型信息,因此不会因无法确定对象的类型而陷入僵局。

通常说来,如果不知道在解决某个问题时需要多少个,或者它们将存活多久,那么就不可能知道如何存储这些
对象。如何才能知道需要多少空间来创建这些对象呢?答案是你不可能知道,因为这类信息只有在运行时才能
获得。

从设计的观点来看,真正需要的只是一个可以被操作,从而解决问题的序列。如果单一类型的容易可以满足所有
需要,那么就没有理由设计不同种类的序列了。然而还是需要对容器有所选择,这有两个原因,第一,不同容器
提供了不同类型的接口和外部行为。堆栈相比于队列就提供了不同的接口和行为,也不同于集合和列表的接口和行为。它们之中的某种容器提供的解决方案可能比其他容器要灵活得多。第二,不同的容器对于某些操作具有不同的
效率。

向下转型和运行时的检查需要额外的程序运行时间,需要程序付出更多的心血,那么创建这样的容器,它知道自己
保存的对象的类型,从而不需要向下转型以及消除犯错误的可能,这样不是更有意义吗?这种解决方案被成为参数
化类型机制。
java SE5的重大变化之一就是增加了参数化类型,在java中它称为范型。一对尖括号,中间包含类型信息,通过
这些特征就可以识别对范型的使用。

对于允许在堆栈上创建对象的语言编辑器,可以确定对象的存活的时间,并可以自动销毁它。然而,如果是在堆上
创建对象,编译器就会对它的生命周期一无所知,就像C++这样的语言中,必须通过变成的方式来确定何时销毁对象
,这可能会因为不能正确处理而导致内存泄漏。Java提供了被成为垃圾回收期的机制,它可以自动发现对象何时不再
被使用,并继而销毁它。
java的垃圾回收期知道对象何时不再被使用,并自动释放对象占用的内存。这一点同所有对象都是继承自单根基类
Object以及只能以一种方式创建对象(在堆上创建)这两个特性结合起来。

异常处理不是面向对象的特征——尽管在面向对象语言中异常常常被表示成为一个对象。异常处理在面向对象语言
出现之前就已经存在了。

通常,线程只是一种为单一处理器分配执行时间的手段。但是如果操作系统支持多处理器,那么每个任务都可以被
指派给不同的处理器,并且它们是在真正的并行执行。在语言级别上,多线程所带来的遍历之一便是程序员不在
操心机器上是有多个处理器还是只有一个处理器。由于程序在逻辑上被称分为线程,所以如果机器拥有多个处理器
那么程序不需要特殊调整也能执行得更快。

客户/服务器系统的核心思想是:系统具有一个中央信息存储池,用来存储某种数据,它通常存在于数据库中,你可以根据需要将它发给某些人员或机器集群。客户/服务器概念的关键在于信息存储池的位置集中于中央,这使得它可以被修改,并且这些修改将被传播给信息消费者。总之,信息存储池、用于分发信息的软件以及信息与软件所驻留的机器或机群被总称为服务器。驻留在用户机器上的软件与服务器进行通信,以获取信息、处理信息,然后将它们显示在被称为客户机的用户机器上。

所有这一切在java里都得到了简化。一切都被视为对象,因此可采用单一固定的语法。尽管这一切都看作对象,但
操纵的标识符实际上是对象的一个“引用”,可以将这一情形想象成遥控器(引用)来操纵电视机(对象),只要
握住这个遥控器,就保持与电视机的连接。当有人想概念频道或者减小音量时,实际操控的是遥控器(引用),再由
遥控器来调控电视机(对象)。如果想在房间里四处走走,同时仍能调控电视机,那么只需携带遥控器(引用)而不是
电视机(对象)。
此外,即使没有电视机,遥控器亦可独立存在,也就是说,你拥有一个引用,并不一定需要有一个对象与它关联。因此
如果想操纵一个词或句子,则可以创建String引用 String s;但这里所创建的只是引用,并不是对象。如果此时向s发送
一个消息,就会返回一个运行时的错误,因为此时s实际上没有与任何事物相关联(即,没有电视机)。

存储到什么地方
1、寄存器,这事最快的存储区。
2、堆栈,通过堆栈指针可以从处理器那里获得直接支持。所以虽然某些java数据存储
于堆栈中,特别是对象引用,但是java对象并不存储于其中。
3、堆。一种通用的内存池,用于存放所有的java对象。用堆进行存储分配和清理可能比用堆栈进行存储分配需要更多时间。
4、常量存储,可以选择将其存放在ROM(只读存储器)中。

特例:基本类型
基本类型new将对象存储在堆里,故用new创建一个对戏那个,特别是小的,简单的变量,往往不是很有效,因此,对于
这些类型,java才去C和C++相同的方法,也就是说,不用new来创建变量,而是创建一个并非是引用的“自动变量”。
这个变量直接存储“值”,并置于堆栈中,因此更加高效。
基本类型具有的包装器类,使得可以在堆中创建一个非基本对象,用来表示对应的基本类型。
javase5的自动包装功能将自动地将基本类型转换为包装器类型。

大多数过程语言都有作用域的概念。作用域决定了在其内定义的变量名的可见性和生命周期。
在作用域里定义的变量只可用于作用域结束之前。

对象的作用域
java对象不具备和基本类型一样的生命周期。当用new创建一个java对象时,它可以存活于作用域之外的。
引用s在作用域终点就消失了。然而,s指向的String对象仍继续占据内存空间。

如果一切都是对象,那么是什么决定了某一类对象的外观与行为呢?换句话说,是什么确定了对象的类型?大多数
面向对象的程序设计语言习惯用关键字class来表示“我准备告诉你一种新类型的对象看起来像什么样子”。class
关键字之后紧跟着的是新类型的名称。

一旦定义了一个类(在java中你所做的全部工作就是定义类,产生那些类的对象,以及发送消息给这些对象),就
可以在类中设置两种类型的元素。字段(有时被称作数据成员)和方法(有时被称作成员函数)。字段可以是任何类型
的对象,可以通过其引用与其进行通信;也可以是基本类型汇总的一种。如果字段是对某个对象的引用,那么必须初始化
该引用,以便使其与一个实际的对象相关联。

java的方法决定了一个对象能够接受什么样的信息。方法的基本组成部分包括:名称、参数、返回值和方法体。
返回类型描述的是在调用方法之后从方法返回的值。参数列表给出了要传给方法的信息的类型和名称。方法名和参数列表
(它们合起来被成为“方法签名”)唯一标识出某个方法。
java的方法只能作为类的一部分来创建。方法只有通过对象才能被调用,且这个对象必须能执行这个方法调用。且这个对
象必须能执行这个方法调用。通过对象调用方法时,需要先列出对象名,紧接着是句点,然后是方法名和参数列表。
面向对象的程序设计通常简单地归纳为“向对象发送消息”。

参数列表
方法的参数列表指定要传递给方法什么样的信息。这些信息像java中的其他信息一样,采用的都是对象形式。因此,在
参数列表中必须指定每个所传递对象的类型及名字。像java中任何传递对象的场合一样,这里传递的实际上也是引用。
并且引用的类型必须正确。
return关键字的用法,它包括两方面:首先,它代表“”已经做完,离开次方法“,其次,如果此方法产生了一个值,
这个值要放在return语句后面。若返回类型是void,return关键字的作用只是用来退出方法。

有两种情形用上述方法是无法解决的。一种情形时,只向为某特定域分配单一存储控件,而不去考虑究竟要创建多少
对象,甚至根本就不创建任何对象。另一种情形时,希望某个方法不与包含它的类的任何对象关联在一起,也就是说
即使没有创建对象,也能够调用这个方法。
通过static关键字可以满足这两方面的需要,当声明一个事物是static时,就意味着这个域或方法不会与包含它的那个
类的任何对象实例关联在一起。所以,即使从未创建某个类的任何对象,也可以调用其static方法或访问其static域。
通常你必须创建一个对象,并用它来访问数据或方法。因为非static域和犯法必须知道它们一起运作的特定对象。

尽管static作用域某个字段时,肯定会改变数据创建的方式(因为一个static字段对每个类来说都只有一份存储控件,而非
static字段则是对每个对象有一个存储空间),但是如果static作用于某个方法,差别却没有那么大。static方法的一个重要
用法就是在不创建任何对象的前提下就可以调用它。正如我们将会看到的那样,这一点对定义main()方法很重要,这个
方法是运行一个应用时的入口点。
和其他任何方法一样,static方法可以创建或使用与其类型相同的被命名对象,因此,static方法常常拿来做”牧羊人“的
角色,负责看护与其隶属同一类型的实例群。

所有类型的注释文档——类、域和方法——都支持嵌入式HTML
”JAVA变成语言编码约定“中,代码风格是这样规定的:类名的首字母要大写;如果类名由几个单词构成,那么把它们并在
一起(也就是说,不要用下划线来分隔名字),其中每个内部单词的首字母都采用大写形式。

操作符 
在最底层,java中的数据是通过使用操作符来操作的。
操作符接受一个或多个参数,并生成一个新值。参数的形式与普通的方法调用不同,但效果是相同的。加号与一元的正号,
减号和一元的负号,乘号,除号以及赋值号的用法与其他编程语言类似。
操作符用于操作数,生成一个新值。另外,有些操作符可能会改变操作数自身的值,这被称为”副作用“,那些能改变其
操作数的操作符,最普遍的用途就是用来产生副作用;但要记住,使用此类操作符生成的值,与使用没有副作用的操作符
生成的值,没有区别。几乎所有的操作符都只能操作基本类型。
优先级
java对计算顺勋做了特别的规定,其中最简单的规则就是先乘除后加减,应该用括号明确规定计算顺序。
在上文环境中,”+“意味着”字符串连接“,并且如果必要,它还要执行”字符串转换“。当编译器观察到一个String
后面紧跟一个"+",而这个”+“的后面又紧跟一个非String类型的元素时,就会尝试着将这个非String类型的元素转换
为String。
赋值
赋值使用操作”=“。它的意思是取右边的值,把它复制给左边。右值可以是任何常数、变量或者表达式(只要它能生成
一个值就行)。但左值必须是一个明确的,已命名的变量。也就是说,必须有一个物理空间可以存储等号右边的值。
t1.level=t2.level,这样就可以保持两个对象彼此独立。而不是将t1和t2绑定到相同的对象。

自动递增和递减
前缀递增表示”++“操作符位于变量或表达式的前面;而”后缀递增“表示”++“操作符位于变量或表达式的后面。
类似地,”前缀递减“意味着”--“操作符位于变量或表达式的前面,而”后缀递减“意味着”--“操作位于变量或
表达式的后面。对于前缀递增和前缀递减,会先执行运算,再生成值,而对于后缀递增和后缀递减,会先生成值,再
执行运算。

==和!= 比较的就是对象的引用。想比较两个对象的实际内容是否相同,必须使用所有对象都适用的特殊方法equales()
equals()的默认行为是比较引用。
大多数java类库都实现了equals()方法,以便用来比较对象的内容,而非比较对象的引用。

十六进制数适用于所有整数数据类型,以前缀0X,后面跟随0-9或小写的a-f来表示。如果试图将一个变量初始化成超出自身
范围的值(无论这个值的数值形式如何),编译器都会向我们报错。已经给出的char,byte以及short所能表示的最大的十六
进制值。如果超过范围,编译器会将值自动转换成int型,并告诉我们需要对这次赋值进行窄化转型。这样,我们就可清楚的
知道自己的操作是否越界了。
八进制数由前缀0以及后续的0-7的数字来表示。

按位操作符用来操作证书基本数据类型中的单个比特,即二进制位,按位操作符会对两个参数中对应的位执行布尔代数运算。

三元操作符if-else
其表达式才去下述形式:boolean-exp?value0:value1
如果boolean-exp(布尔表达式)的结果为true,就计算value0,而且这个计算结果也就是操作符最终产生的值,如果boolean-exp
的结果为false,就计算value1,同样,它的结果也就成为了操作符最终产生的值。

java有一个与C和C++中类似的问题,即使用位”与“和按位”或“代替逻辑”与“和逻辑”或“。按位”与“和按位”或“使用
使用单字符(&或|),而逻辑”与“和逻辑”或“使用双字符(&&或||)。就像”=“和”==“一样,键入一个字符当然要比
键入两个简单,java编译器可防止这个错误产生,因为它不循序我们随便把一种类型当作另一种类型来用。

在java中,类型转换则是一种比较安全的操作。然而,如果要执行一种窄化转换的操作(也就是说,将能容纳更多信息的数据类型
转换成无法容纳那么多信息的类型),就有可能面临信息丢失的危险。此时,编译器会强制我们进行类型转换。而对于扩展转换,
则不必显式地进行类型转换,因为新类型肯定能容纳原来类型的信息,不会造成任何信息的丢失。

提升
如果对基本数据类型执行算数运算或按位运算,大家会发现,只要类型比int小,那么在运算之前,这些值会自动转换成int。这样
一来,最终生成的结果就是int类型。如果把结果赋值给较小的类型,就必须使用类型转换。通常,表达式中出现的最大的数据类型
决定了表达式最终结果的数据类型。

就像有知觉的生物一样,程序必须在执行过程中控制它的世界,并作出选择。在Java中,你要使用执行控制语句来做出选择。
while和do-while唯一的区别就是do-while中的语句至少会执行一次,即便表达式第一次就被计算为false,而在while循环结构中,
如果条件第一次为false,那么其中的语句根本不会执行。在实际应用中,while和do-while更常用一些。

逗号操作符
java里位移用到逗号操作符的地方就是for循环的控制表达式。在控制表达式的初始化和步进控制部分,可以使用一系列由逗号
分隔的语句;而且那些语句均会独立执行。
在任何迭代语句的主体部分,都可用break和continue控制循环的流程。其中,break用于强行退出循环,不执行循环中剩余的
语句。而continue则停止执行当前的迭代,然后退回循环起始处。开始下一次迭代。

switch-case 
每个case均以一个break几位,这样就可使执行流程跳转至switch主体的末尾。这是构建switch语句的一种传统方式,但break
是可选的。若省略break,会继续执行后面的case语句,直到遇到一个break为止。

初始化和清理正是涉及安全的两个问题。如果用户不知道如何初始化库的构件,更是如此。当使用完一个元素时,它对你也就
不会有什么影响了。
C++引入了构造器的,这是一个创建对象时被自动调用的特殊方法。java中也采用了构造器,并额外提供了”垃圾回收
器",对不再使用的内存资源,垃圾回收器能自动将其释放。

用构造器确保初始化。调用构造器是编译器的责任,所以必须让编译器知道应该调用哪个方法。在java中也采用了这种方案:
即构造器采用与类相同的名称。考虑到在初始化器件要自动调用构造器。

不接受任何参数的构造器叫做默认构造器。java文档中通常使用术语“无参构造器”。
在java中,“初始化”和“创建”捆绑在一起,两者不能分离。
构造器是一种特殊类型的方法,因为它没有返回值。这与返回值为空(void)明显不同。对于空返回值,尽管方法本身不会自动
返回什么,但扔可选择让它返回别的东西。构造器则不会返回任何东西。

在java里,构造器是强制重载方法名的另一个原因。既然构造器的名字已经由类名所决定,那就只能有一个构造器名,如果
想要用多种方式创建一个对象该怎么办呢?为了让方法名相同而形式参数不同的构造器同时存在,必须用到方法重载。同时,
尽管方法重载是构造器所必需的,但它亦可用于其他方法,且用法同样方便。

区分重载方法:每个重载的方法都必须有一个独一无二的参数类型列表。

默认构造器是没有形式参数的——它的作用是创建一个默认对象,如果你写的类中没有构造器,则编译器会自动帮你创建
一个默认的构造器。

this关键字 
为了能用简便,面向对象的语法来编写代码—即“发送消息给对象”,编译器做了一些幕后工作,它暗自把“所操作对象的应用”作为第一个参数传递给peel()。所以上述两个方法的调用就变成了这样:Banana.peel(a,1) Banana(b,2) 
假设你希望在方法的内部获得当前对象的引用。由于这个引用是由编译器“偷偷”传入的,所以没有标识符可用。但是,为此有个专门的关键字:this。this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。this的用法和其他对象引用并无不同。但要注意,如果在方法内部调用同一个类的另一个方法, 就不必用this,直接调用即可。当前方法中的this应用会自动应用于同一类中的其他方法。
只有当你需要明确指出对当前对象的引用时,才需要使用this关键字。例如,当需要返回对当前对象的引用时,就常常在return语句里这样写。

在构造器中调用构造器  86页
可能为一个类写了多个构造器,有时可能想在一个构造器中调用另一个构造器,以避免重复代码。
通常写this的时候,都是指“这个对象”或者“当前对象”,而且它本身表示对当前对象的引用。在构造其中,如果为this添加了参数列表,那么就有了不同的含义。这将产生对复合此参数列表的某个构造器的明确调用。
由于参数s的名称和数据成员s的名字相同,所以会产生歧义。使用this.s来代表数据成员就能解决这个问题。

static方法就是没有this的方法。在static方法的内部不能调用非静态方法。反过来倒是可以的。而且可以在没有创建任何对象的前提下,仅仅是通过类本身来调用static方法。这实际上正是static方法的主要用途,它很像全局方法。

垃圾回收只与内存有关。
使用垃圾回收器的唯一原因是为了回收程序不再使用的内存。所以对于与垃圾回收有关的任何行为来说,它们也必须同内存及回收有关。

垃圾回收器依据的思想是:对任何“活”的对象,一定能最终追溯到其存货在堆栈或静态存储区之中的引用。这个引用链条可能会穿过数个对象层次。由此,如果从堆栈和静态存储区开始,遍历所有的引用,就能找到所有“活”的对象。对于发现的每个引用,必须追踪它所引用的对象,然后是此对象包含的所有引用。如此反复进行,直到“根源于堆栈和静态存储区的引用”所形成的网络全部被访问为止。

java尽力保证:所有变量都在使用前都能得到恰当的初始化。

构造器初始化  94页
构造器初始化:在运行时刻,可以调用方法或执行某些动作来确定初值,这为变成带来了更大灵活性。但要牢记:无法阻止自动初始化的进行,它将在构造器被调用之前发生。
对于所有基本类型和对象应用,包括在定义时就已经制定初值的变量,这种情况是成立的;因此编译不会强制在你一定要在构造器的某个地方或在使用它们之前对元素进行初始化。

在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。

初始化的顺序是先静态对象(如果它们尚未因前面的对象创建过程而被初始化),而后是“非静态对象“。

总结一下对象的创建过程,假设有个名为”Dog“的类:
1、即使没有显式地使用static关键字,构造器实际上也是静态方法,因此,当首次创建类型为dog的对象时,(构造器可以看成静态方法),或者Dog类的静态方法/静态域受词被访问时,java解释器必须查找类路径,以定位Dog.class文件。
2、然后载入Dog.class(后面会学到,这将创建一个Class对象),有关静态初始化的所有动作都会执行。因此,静态初始化只在class对象首次加载的时候进行一次。
3、但用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够的存储空间。
4、这块存储空间会被清零,这就自动地将Dog对象中的所有基本类型数据都设置成了默认值(对数字来说就是0,对布尔型和字符型也相同),而引用则被设置成了null。
5、执行所有出现于字段定义处的初始化动作。
6、执行构造器。

97页
尽管上面的代码看起来像个方法,但它实际只是一段跟在static关键字后面的代码。与其他静态初始化动作一样,这段代码只执行一次:当首次生成这个类的一个对象时,或首次访问属于那个类的静态数据成员时。

数组初始化
数组只是相同类型的、用一个标识符名称封装到一起的一个对象序列或基本类型数据序列。
数组是通过方括号下表操作符来定义和使用的。要定义一个数组,只需在类型名后加上一对空方括号即可。
编译器不允许指定数组的大小。这就又把我们带回到有关”引用“的问题上。现在拥有的只是对数组的一个引用(你已经为该引用分配了足够的存储空间),而且也没给数组对象本身分配任何空间。为了给数组创建相应的存储控件,必须写初始化表达式。对于数组,初始化动作可以出现在代码的任何地方,但也可以使用一个特殊的初始化表达式,它必须在创建数组的地方出现。这种特殊的初始化是由一堆花括号括起来的值组成的。
所有数组都有一个固定成员,可以通过它获知数组包含了多少个元素,但不能对其修改。

枚举类型
在Java SE5中添加了一个看似很小的特性。即enum关键字,它使得我们在需要群组并使用枚举类型集时,可以很方便地处理,在此之前,你需要创建一个整形常量集,但是这些枚举值并不会必然地将其自身的取值限制在这个常量集的范围之内,因为它们显得更有风险,且更难以使用。
尽管enum看起来像是一种新的数据类型,但是这个关键字只是为enum生成对应的类时,产生了某些编译器行为,因此在很大程度上,你可以将enum当作其他任何类来处理,事实上,enum确实是类,并且具有自己的方法。

访问权限控制
访问控制(或隐藏具体实现)与最初的实现并不恰当有关。
如何把变动的事物与保持不变的事物区分开来。
我们之所以要导入包,就是要提供一个管理名字空间的机制。所有类成员的名称都是彼此隔离的。A类中的方法f()与B类中具有相同特征标记(参数列表)的方法f()不会彼此冲突。但是如果类名称相互冲突由该怎么办。
在java中对名称空间进行完全控制并为每个类创建唯一的标识符组合就成为了非常重要的事情。
当编写一个java源代码文件时,此文件通常被成为编译单元(有时也被成为转译单元)。每个编译单元都必须有一个后缀名.java,而在编译单元内则可以有一个public类。该类的名称必须与文件的名称相同(包括大小写,但不包括文件的后缀名)每个编译单元只能有一个public类,否则编译器就不会接受。如果在该编译单元之中还有额外的类的话,那么在包之外的世界是无法看见这些类的,这是因为它们不是public类,而它们主要用来为主public类提供支持。
如果在该编译单元之中还有额外的类的话,那么在包之外的世界是无法看到这些类的。
java的命名规则全部使用小写子目,包括中间的字也是如此。

身为一名类库设计员,很有必要牢记:package 和import关键字允许你做的,是将单一的全局名字空间分隔开,使得无论多少人使用Internet以及java开始编写类,都不会出现名称冲突问题。

按照惯例,package名称的第一部分是类的创建者的范顺序的Internet域名。
Java解释器的运行过程如下:首先,找出环境变量CLASSPATH。CLASSPATH包含一个或多个目录,用作查找.class文件的根目录。从根目录开始,解释器获取包的名称并将每个举点替换成反斜杠,以从CLASSPATH根中产生一个路径名称(于是,package.foo.bar.baz就变成foo\bar\baz或其他,一切取决于操作系统)。得到的路径会与CLASSPATH中的各个项相连接,解释器就在这些目录中查找与你所要创建的类名称相关的.class文件。
使用jar文件时,会有一点变化。必须在类路径中将JAR文件的实际名称写清楚,而不仅是指明它所在位置的目录。

java没有C的条件编译功能,该功能可以使你不必更改任何程序代码,就能够切换开关并产生不同的行为。

对使用包的警告 
无论何时创建包,都已经给定包的名称的时候隐含地指定了目录结构。这个包必须位于其名称所指定的目录之中,而该目录必须是以CLASSPATH开始的目录中可以查询到的。

java访问权限修饰词
如果不提供任何访问权限修饰词,则意味着它是“包访问权限”。

包访问权限
默认访问权限没有任何关键字,但通常是指包访问权限,这就意味着,当前包中的所有其他类对那个成员都有访问权限,但对于这个包之外的所有类,这个成员却是private。
由于一个编译单元,只能隶属于一个包,所以经由包访问权限,处在同一个编译单元中的所有类彼此之间都是自动可访问的。

public 接口访问权限
使用关键字public,就意味着public之后紧跟着的成员声明自己对每个人都是可用的,尤其是使用类库的客户端程序员更是如此。
不要错误地认为Java总是将当前目录视作是查找行为的起点之一。如果你的CLASSPATH只占总缺少一个“。”作为路径的之一的话,java就不会查找那里。

private:你无法访问
关键字private的意思是:除了包含该成员的类之外,其他任何类都无法访问这个成员。
任何可以肯定只是该类的一个助手方法的方法,都可以把它指定为private,以确保不会在包内的其他地方误用到它,于是也就是防止了你回去改变或删除这个方法,将方法指定为private确保了你拥有这种选择权。

protected:继承访问权限
关键字protected处理的是继承的概念。
有时,基类的创建者希会希望有某个特定成员,把对它的访问权限赋予派生类而不是所有类。这就需要protected来完成这一工作。protected也提供包访问权限,也就是说,相同包内的其他类可以访问protected元素。

6.3接口和实现
访问权限的控制常常被称为具体实现的隐藏。
把数据和方法包装进类中,以及具体实现的隐藏,常共同被称作是封装。其结果是一个同时带有特征和行为的数据类型。出于两个很重要的原因,访问权限控制将权限的边界划在了数据类型的内部。第一,要设定客户端程序员可以使用和不可以使用的界限。可以在结构中建立自己的内部机制。第二,接口和具体实现进行分离。如果结构是用于一组程序之中,而客户端程序员除了可以向接口发送消息之外什么也不可以做的话,那么就可以随意更改所有不是public的东西,而不会破坏客户端代码。
在计算机中,接口是计算机系统中两个独立的部件进行信息交换的共享边界。这种交换可以发生在计算机软、硬件,外部设备或进行操作的人之间,也可以是它们的结合。

6.4类的访问权限
还有一些额外限制
1、每个编译单元(文件)都只能有一个public类。这表示,每个编译单元都有单一的公共接口,用public类来表现。该接口可以按要求包含众多的支持包访问权限的类。
2、public类的名称必须完全与含有该编译单元的文件名相匹配,包括大小写。如果不匹配,同样将得到编译时错误。
3、虽然不常用,但编译单元内完全不带public类也是可能的。
请注意,类既不可以是private的,也不可以是protected。所以对于类的访问权限,仅有两个选择:包访问权限或public。如果不希望其他任何人对该类拥有访问权限,可以把所有的构造器都指定为private,从而阻止任何人创建该类的对象,但是有一个例外,就是你该类的static成员内部可以创建。
如果没能为类访问权限指定一个访问修饰符,它就会默认得到包访问权限。这就意味着该类的对象可以由包内任何其他类来创建,但在包外则是不行的。

6.5总结(理解不深刻,无实例)
无论是什么样的关系之中,设立一些为各成员所遵守的界限始终是很重要的。当创建了一个类库,也就愈该类库的用户建立了某种关系。
控制对成员的访问权限有两个原因。第一是为了使用户不要触碰那些他们不该触碰的部分,这些部分对类内部的操作是必要的,但它并不属于客户端程序所需接口的一部分。因此,将方法和域指定成private。第二原因,也是最重要的原因,是为了让类库设计者可以更改类的内部工作方式,而不必担心这样会对客户端程序员产生重大的影响。访问权限控制可以确保不会有任何客户端程序员依赖于某个类的底层的实现的任何部分。

你可能感兴趣的:(读书笔记)