Thinking In Java

第一章  对象导论

1、可以认为,人们所能够解决的问题的复杂性直接取决于抽象的类型和指令。所谓的“类型”是指“所抽象的是什么 ?”(p1)

2、类描述了具有相同特性(属性)和行为(功能方法)的对象集合,所以一个类实际上就是一个数据类型。(p3)

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

     开始从事这件事的一种方式就是问一下自己:“如果我可以将问题从表象中抽取出来,那么什么样的对象可以马上解决我的问题呢 ?”。这是将问题分解为对象集合的一种合理方式。(p4)

4、高内聚是软件设计的基本质量要求之一:这意味着一个软件构件(例如一个对象、方法或者对象库)的各个方面“组合”得很好。人们在设计对象时所面临的一个问题是将过多的功能都塞在一个对象中。(p4)

5、代码复用是面向对象程序设计语言所提供的最了不起的优点之一,产生一个可复用的对象设计需要丰富的经验和敏锐的洞察力。(p5)

6、子类仅仅继承父类而不做任何扩展不添加任何方法,这种情况我们称其关系为is-a(是一个)关系。如果子类添加了一些新的接口元素,这样就扩展了父类的接口,父类中无法访问子类中的新接口,这样的情况我们可以描述为is-like-a(像是一个)关系。

类是对逻辑上相关函数与数据的封装,是对问题的抽象,而对象就是类的实例

第二章、一切都是对象

1、字段可以是任何类型的对象,如果字段是对某个对象的引用(即如果字段是某个类的时候),那么必须初始化该引用,以便使其与一个实际的对象相关联(p71)

2、方法名和参数列表(它们合起来被称为“方法签名”)唯一地标识出某个方法。(p27)

3、字符串中的每个字符的尺寸都是16位或2个字节,以此来提供对Unicode字符集的支持(p27)

4、Java消除了所谓的“向前引用”问题(p28)

5、(1)引用static变量有两种方法,可以通过一个对象去定位它,如objName.i,也可以通过其类名直接引用,如,StaticTest.i,而这对于非静态成员则不行。使用类名是引用static变量的首选方式,这不仅是因为它强调了变量的static结构,而且在某些情况下它还为编译器进行优化提供了更好的机会。(p29-30)

(2)当声明一个事物是static时,就意味着这个域或方法不会与包含它的类的任何对象实例关联在一起。(p29)

(3)static字段对每个类来说都只有一份存储空间。它不依赖类特定的实例,被类的所有实例共享。(p30)

类的静态属性放在内存的一个区域,相同类的对象指向公共的存放静态属性的地址。

staitc不是放在堆存储的,static数据设计到并发数据安全

 

public class Test {
	static String a = "你好";
}

public class Test2 {
	public static void main(String[] args) {
		Test t = new Test();
		Test t2 = new Test();
		System.out.println(t.a == t2.a);//true
		
	}
}


6、(p31)

public static void main(String[] args) {
	System.getProperties().list(System.out);//从运行程序的系统中获取的所有属性,因此它可以向你提供环境信息
	System.out.println(System.getProperty("user.name"));
	System.out.println(System.getProperty("java.library.path"));
}

 

第三章、操作符

 

1、别名现象(赋值操作符“=”)

对于基本数据类型的赋值是很简单的。基本类型存储了实际的数值,而并非指向一个对象的引用的引用,所以在为其赋值的时候,是直接将一个地方的内容复制到另一个地方。例如,对基本数据类型使用a=b,那么b的内容就复制给a。若接着又修改了a,而b根本不会受到这种修改影响。

但是在为对象“赋值”的时候,情况却发生了编号。对一个对象进行操作时,我们真正操作的是对对象的引用。所以倘若“将一个对象赋值给另一个对象”,实际是将“引用”从一个地方复制到另一个地方。这意味着假若对对象使用c=d,那么c和d都指向原本只有d指向的那个对象。(p39-40)

2、%:取模操作符(取余)(p41)

3、==和!=比较的就是对象引用,基本数据类型用==和!=即可(p44)

4、equals( )方法的默认行为是比较引用的,所以除非在自己的新类中覆盖equals()方法,否则不可能表现出我们希望的行为(p45)

第四章、控制执行流程

1、逗号操作符(p67)

     注意:这里不是逗号分隔符,逗号用作分隔符时用来分隔函数的不用的参数

     Java里唯一用到逗号操作符的地方就是for循环的控制表达式,在控制表达式的初始化和步进控制部分,可以使用一系列由逗号分隔的语句,而且那些语句均会独立运行。

     通过使用逗号操作符,可以在for语句内定义多个变量,但是它们必须具有相同的类型。    

for(int i=1,j = i + 10; i<5; i++,j = i*2){
	System.out.println("i = " + i + " j = " + j);
}

 

     for语句中的int定义涵盖了 i 和 j ,在初始化部分实际上可以拥有任意数量的具有相同类型的变量定义。在一个控制表达式中,定义多个变量的这种能力只限于for循环使用,在其他任何选择或者迭代语句中都不能使用这种方式。

 

2、如果在返回void的方法中没有return 语句,那么在该方法的结尾处会有一个隐式的return。(p69)

3、break用于强行退出循环,不执行循环中的剩余的语句。而continue则停止执行当前的迭代,然后退回循环起始处,开始下一次迭代。(p70)

4、关键字goto,事实上goto起源于汇编语言的程序控制,如果阅读由编译器最终生成的汇编代码,就会发现程序控制里包含了许多跳转(Java编译器生成它自己的“汇编代码”,但是这个代码是运行在Java虚拟机上的,而不是直接运行在CPU硬件上)(p70)

5、标签,标签是后面跟有冒号的标识符,例如:label1:(p71)

label1:
outer-iteration{
	inner-iteration{
		//...
		break;//(1)
		//...
		continue;//(2)
		//...
		continue label1;//(3)
		//...
		break label1;//(4)
	
	}
}

在(1)中,break中断内部迭代,回到外部迭代

在(2)中,continue使执行点移回内部迭代的起始处

在(3)中,continue label1同时中断内部迭代以及外部迭代,直接转到label1处,随后继续从外部迭代开始

在(4)中,break label1会中断所有迭代,并回到label1处,但不重新进入迭代

 

第五章、初始化与清理

1、this关键字,对象在调用方法的时候编译器做了一些幕后工作,它暗自把“所操作对象的引用”作为第一个参数传递给方法。例如:(p84)

      Banana a = new Banana();

      a.peel(1);//编译器实际上是a.pell( a , 1),当然我们不能这样书写代码

public class Leaf {
	int i = 0;
	public static void main(String[] args) {
		Leaf x = new Leaf();
		x.increment().increment().increment().print();//i = 3
	}
	
	Leaf increment(){
		i++;
		return this;//通过this返回对当前对象的引用
	}
	
	void print(){
		System.out.println("i = " + i);
	}
	
}


2、在构造器中调用构造器

 

     在构造器中,如果为this添加了参数列表,这将对符合此参数列表的某个构造器的明确调用。尽管可以在构造器中用this调用另一个构造器,但却不能调用两个。另外,必须将构造器调用放置在最起始处,否则编译器会报错

 

 

3、static的含义

    static方法就是没有this的方法,在static方法内部不能调用非静态方法(这不是完全不可能),反过来倒是可以的。而且可以在没有创建任何对象的前提下,通过类本身来调用static方法

4、构造器初始化。必须牢记:无法阻止自动初始化的进行,它将在构造器被调用之前发生。(p94)

5、数组元素中的基本数据类型值会自动初始化成空值,对于数字和字符,就是0,对于布尔型,是false

6、可变参数(p102)

7、枚举类型enum(p106)

public enum Spiciness {
	NOT,MILD,MEDIUM,HOT,FLAMING
}

public class SimpleEnumUse {
	public static void main(String[] args) {
		Spiciness howHot = Spiciness.MEDIUM;
		System.out.println(howHot);
		
		for(Spiciness s : Spiciness.values()){
			System.out.println(s + ", ordinal " + s.ordinal());
		}
		
		
	}
}

enum有一个特别使用的特性,即它可以在switch语句内使用:

public class Burrito {
	Spiciness degree;
	
	public Burrito(Spiciness degree){
		this.degree = degree;
	}
	
	public void describe(){
		System.out.println("This burrito is");
		switch (degree) {
			case NOT: System.out.println("not spicy at all");break;
			case MILD: 
			case MEDIUM: System.out.println("a little hot");break;
			case HOT:
			case FLAMING:
			default:System.out.println("maybe too hot");break;
		}
	}
	
	public static void main(String[] args) {
		Burrito 
			plain = new Burrito(Spiciness.NOT),
			greenChile = new Burrito(Spiciness.MEDIUM),
			jalapeno = new Burrito(Spiciness.HOT);
			
		plain.describe();
		greenChile.describe();
		jalapeno.describe();
	}
}

8、垃圾回收机制

 

第六章、访问权限控制

1、每个编译单元(文件)都只能有一个public类。这表示,每个编译单元都有单一的公共接口,用public类来表现。但是编译单元内完全不带public类也是可能的,在这种情况下,可以随意对文件命名。(编译单元就是指一个.java文件)

2、类既不可以是private(这样会使除该类之外,其他任何类都不可以访问它),也不可以是protected的。所以对于类的访问权限,仅有两个选择:包访问权限或public。如果不希望其他任何人对该类拥有访问权限,可以吧构造器都指定为private,从而阻止任何人创建该类的对象,但是有一个例外,就是你在该类的static成员内部可以创建。如下:

class Soup1 {
	private Soup1(){
		
	}
	
	public static Soup1 makleSoup(){
		return new Soup1();
	}
}

 

与单例模式对比

 

class Soup2{
	private Soup2(){
		
	}
	
	private static Soup2 s = new Soup2();
	
	public static Soup2 makeSoup(){
		return s;
	}
}


3、方法的返回类型返回对象时,只是返回一个对象的引用地址

 

4、protected除了子类可以访问外,也提供了包内访问权限

 

第七章、复用类

1、组合与继承

public class User {
	private Chinese chinese;//这个就是组合,将Chinese复用到了User对象里
	private String name;
}

 

2、子类在初始化时会最先从最低层的父类进行初始化,然后再向外一层一层的初始化。如果父类没有默认的构造器而是带参数的构造器,就必须使用关键字super显式地调用父类的构造器,并且配以适当的参数列表。

 

class Game{
	Game(int i){
		System.out.print("Game constructor");
	}
}

class BoardGame extends Game{
	BoardGame(){
		super(1);//调用父类的构造器,因为父类没有无参构造器,所以这里就必须指明调用的是父类哪个构造器
			     //否则子类在初始化时,无法去先初始化父类,同时编译器也会提示错误
		System.out.println("BoardGame constructor");
	}
}


3、向上转型。由于向上转型是从一个较专用类型向较通用类型转换,所以总是很安全的。(p139)

 

4、什么情况下我们需要使用继承?继承的使用场合仅限于你确信使用该技术确实有效的情况。到底是该用组合还是继承,一个最清晰的判断方法就是问一问自己是否需要从新类向基类进行向上转型。如果必须向上转型,则继承是必要的;但如果不需要,则应当好好考虑自己是否需要继承。只要记住“我真的是否需要向上转型吗?”就能较好地在这两种技术中做出决定。

 

5、final关键字。final可以作用在数据、方法和类上。

    (1)final数据:如果是基本数据类型,则该数据不能被修改。如果是对象引用,则该引用不能被修改,但是对象里的内容可以进行修改。

    (2)final方法:子类将不能覆盖、修改该方法。应当只有在明确禁止覆盖时,才将方法设置为final的。

                             类中所有的private方法都隐式地指定为是final的。

    (3)final类:final类不能被继承,另外final类中所有的方法都隐式指定为final的。

6、类的加载。类的代码只有在初次使用时才加载,这通常是指加载发生于创建类的第一个对象实例之时,但是当访问static域或static方法时,也会发生加载。请注意,构造器也是static方法,尽管static关键字并没有显示地写出来。因此更准确地讲,类是在其任何static成员被访问时加载的。初次使用之处也是static初始化发生之处。所有的static对象和static代码段都会在加载时依程序中的顺序而依次初始化。

7、静态方法初始化在构造器只前(p147)

 

第八章、多态

1、多态通过分离做什么和怎么做,从另一个角度将接口和实现分离开来。

2、后期绑定机制。

     将一个方法调用同一个方法主体关联起来被称作绑定。若在程序执行前进行绑定叫前期绑定。在运行时根据对象的类型进行绑定叫后期绑定,也叫做动态绑定或者运行时绑定。Java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是后期绑定。

     将方法声明为final,她可以禁止他人覆盖该方法,但是更重要的是这样做可以关闭动态绑定。(p151)

3、构造器执行完后才初始化私有属性

4、用继承表达行为间的差异,并用字段表达状态上的变化

第七章和第八章需细看下

 

第九章、接口

1、域指的是其访问权限,域是指类的一个变量?

2、我们使用接口的原因:(p179)

     (1)为了能够向上转型为多个基类型(以及由此带来的灵活性)(核心原因)

     (2)与抽象基类相同,防止客户端程序员创建该类的对象,并确保这仅仅是建立一个接口。

3、一般情况下,只可以将extends用于单一类,但是可以引用多个基类接口。准确的说,接口类可以继承多个接口类(p180)    

4、策略模式:接口的一种常见用法就是前面提到的策略设计模式,此时你编写一个执行某些操作的方法,而该方法将接受一个同样是你指定的接口。你主要就是要声明:”你可以用任何你想要的对象来调用我的方法,只要你的对象遵循我的接口“(p182)

5、static变量或块被存储在静态存储区域内

6、“确定接口是理想选择,因而应该总是选择接口而不是具体的类”。这其实是一种引诱。当然,对于创建类,几乎在任何时刻,都可以替代为创建一个接口和一个工厂。

        许多人都掉进了这种诱惑的陷阱,只要有可能就去创建接口和工厂。这种逻辑看起来好像是因为需要使用不同的具体实现,因此总是应该添加这种抽象性。这实际上已经变成了一种草率的设计优化。

        任何抽象性都应该是应真正的需求而产生的。当必需时,你应该重构接口而不是到处添加额外级别的间接性,并由此带来的额外的复杂性。这种额外的复杂性非常显著,如果你让某人去处理这种复杂性,只是因为你意识到由于以防万一而添加了新接口,而没有其他更有说服力的原因,那么好吧,如果我碰上了这种事,那么就会质疑此人所作的所有设计了。

        恰当的原则应该是优先选择类而不是接口。从类开始,如果接口的必需性变得非常明确,那么就进行重构。接口是一种重要的工具,但是它们容易被滥用。

       

第十章、内部类

1、内部类与组合是完全不同的概念,这一点很重要。内部类有:局部内部类、匿名内部类、静态内部类。(p190)

2、当生成一个内部类的对象时,此对象与制造它的外围对象之间就有一种联系,所以它能访问其外围对象的所有成员,而不需要任何特殊条件。此外内部类还拥有其外围类的所有元素的访问权限。

     内部类自动拥有对其外围类所有成员的访问权限。这是如何做到的呢?

     当某个外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用。然后在你访问此外围类的成员时,就是用那个引用来选择外围类的成员。

3、如果你需要生成对外部类对象的引用,可以使用“外部类名.this”。(p193)

public class DotThis {
	
	/* 内部类 */
	public class Inner{
		/* 内部类的方法 */
		public DotThis outer(){
			return DotThis.this;//对外部对象的引用,必须是“外部类名.this”
		}
	}
	
	/* 外部类的方法 */
	public Inner inner(){
		return new Inner();
	}
}

 

4、创建某个内部类的对象

 

public class DotNew {
	/* 内部类 */
	public class Inner{}
	
	public static void main(String[] args) {
		DotNew dn = new DotNew();
		DotNew.Inner dni = dn.new Inner();//外部类创建内部类的对象
		/*
		 * 创建内部类的对象,必须使用外部类的对象来创建该内部类对象,必须先拥有外部类对象才能创建内部类对象。
		 * 这是因为内部类对象会暗暗地连接到创建它的外部类对象上。但是如果你创建的是嵌套类(静态内部类),那么就不需要外部类对象的引用
		 */
	}
}

 

5、内部类可以在一个方法里面或者在任意的作用域内定义内部类。
6、局部内部类(在某个作用域内创建了一个完整的类)

 

public class Parcel5 {
	/* 外部类的一个方法 */
	public Destination destination(String s){
		/*方法内定义了一个内部类,这个是局部内部类
		 *注意PDestination是destination方法的一部分,而不是Parcel5的一部分,所以在该方法之外是不能访问PDestination
		 */
		class PDestination implements Destination{
			private String label;
			private PDestination(String whereTo){//内部类的构造器
				label = whereTo;
			}
			public String readLabel() {//内部类对接口方法的实现
				return label;
			}
		}
		return new PDestination(s);
	}
}</span></span>
/**
 * 在任意的作用域内嵌入一个内部类 
 */
public class Parce6 {
	private void internaltracking(boolean b){
		if(b){
			/***** 内部类开始 *****/
			class TrackingSlip{
				private String id;
				TrackingSlip(String s){//内部类构造方法
					id = s;
				}
				String getSlip(){
					return id;
				}
			}
			/***** 内部类结束 *****/
			
			TrackingSlip ts = new TrackingSlip("slip");
			String s = ts.getSlip();
		}
	}
}


7、匿名内部类构造器带参数

8、在匿名内部类内使用一个在其外部定义的对象,那么必须是final的

9、匿名内部类不可能有命名构造器,因为他根本没名字

10、将内部类声明为static的,通常称为嵌套类,也叫静态内部类。普通的内部类对象隐式的保存了一个外围类的对象引用,而静态内部类,不在需要外围类的对象,不能从静态类中访问非静态类的外围对象(p201)

11、接口内部的类。正常情况下,不能再接口内部放置任何代码,但是嵌套类(静态内部类)可以作为接口的一部分。因为接口中的任何类都自动地是public 和 static的。你甚至可以在嵌套类中实现外围接口类中的接口。

public interface ClassInInterface {
	void howdy();
	
	class Test implements ClassInInterface{//静态内部类,默认是public static
		public void howdy() {
			System.out.println("Howdy!");
		}
		
		public static void main(String[] args) {
			new Test().howdy();
		}
		
	}
}

12、一个内部类被嵌套多少层并不重要,它能透明地访问所有它所嵌入的外围类的所有成员。(p203)
13、内部类实现一个接口与外围类实现这个接口有什么区别?

       答:后者不是总能享用到接口带来的方便,有时需要用到接口的实现。所以使用内部类最吸引人的原因是:每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都么有影响。

       如果没有内部类提供的、可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整。接口解决了部分问题,而内部类有效地实现了“多重继承”。

14、单个外围类中,可以让多个内部类以不同的方式实现同一个接口,或继承同一个类。

 

第十一章、Java容器

在《Thinking In Java》中关于容器的章节有第十一章(持有对象)、第十六章(数组)、第十七章(容器深入研究),为了更好的学习Java容器,已经将三章内容整合到了一块。http://blog.csdn.net/wandan_/article/details/17081485

 

第十二章、通过异常处理错误

第十三章、字符串

第十四章、类型信息

 

http://blog.csdn.net/wandan_/article/details/17146831

第十九章、枚举类型

1、创建enum时,编译器会为你生成一个相关的类,这个类继承自java.lang.Enum

2、switch语句中的enum

在switch中使用enum,是enum提供的一项非常便利的功能,一般来说,在switch中只能使用整数值,而枚举实例天生就具备整数值的次序并且可以通过ordinal()方法取得其次序,因此我们可以在switch语句中使用enum

3、values的神秘之处,编译器为你创建的enum类都继承自Enum类,然而你会发现Enum里没有values()方法,其实values方法是由编译器添加的static方法,另外还添加了valueOf()的重载方法(只需要一个参数)

4、创建一个新的enum,可以实现一个或多个接口

5、EnumSet,与HashSet相比,EnumSet更快更高效。使用EnumSet的优点是,它在说明一个二进制位是否存在时,具有更好的表达能力,并且无需担心性能。EnumSet的基础是long,一个long值有64位,而一个enum实例只需一位bit表示其是否存在。也就是说,在不超过一个long的表达能力的情况下,你的EnumSet可以应用于最多不超过64个元素的enum。当元素超过64时,Enum会在必要的时候增加一个long

 

interface Command {
	void action();
}

public class Test1 {
	public static void main(String[] args) {
		//EnumSet
		EnumSet<AlarmPoints> points = EnumSet.noneOf(AlarmPoints.class); // Empty
		points.add(BATHROOM);
		points.addAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
		points = EnumSet.allOf(AlarmPoints.class);
		points.removeAll(EnumSet.of(STAIR1, STAIR2, KITCHEN));
		points.removeAll(EnumSet.range(OFFICE1, OFFICE4));
		points = EnumSet.complementOf(points);

		//EnumMap
		EnumMap<AlarmPoints, Command> em = new EnumMap<AlarmPoints, Command>(AlarmPoints.class);
		em.put(KITCHEN, new Command() {
			public void action() {
				print("Kitchen fire!");
			}
		});
		em.put(BATHROOM, new Command() {
			public void action() {
				print("Bathroom alert!");
			}
		});
		for (Map.Entry<AlarmPoints, Command> e : em.entrySet()) {
			printnb(e.getKey() + ": ");
			e.getValue().action();
		}
		try { // If there's no value for a particular key:
			em.get(UTILITY).action();
		} catch (Exception e) {
			print(e);
		}

	}
}

 

第二十章、注解

 

1、定义注解,定义注解时需要一些元注解(meta-annotation)。Java目前只内置了三种标准注解以及四种元注解(元注解专职负责注解其他的注解),如下:

内置注解:

      @Override:表示当前的方法定义将覆盖超类中的方法

      @Deprecated:表示弃用,如果某元素使用了该注解,那么编译器会发出警告信息。

      @SuppressWarnings,关闭不当的编译器警告信息

元注解:

     

 

 

//BigDecimal运算
BigDecimal add(BigDecimal value);      //加法
BigDecimal subtract(BigDecimal value); //减法 
BigDecimal multiply(BigDecimal value); //乘法
BigDecimal divide(BigDecimal value);   //除法

-1 小于  0 等于  1 大于

小数位处理:http://www.cnblogs.com/liqforstudy/p/5652517.html

 

 

 

转载于:https://my.oschina.net/weslie/blog/471968

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值