thinking in java笔记

1.上溯造型:我们将这种把衍生类型当作它的基本类型处理的过程叫作“Upcasting”(上溯造型)。其中,“cast”(造型)是指根据一个现成的模型创建;而“Up”(向上)表明继承的方向是从“上面”来的——即基础类位于顶部,而衍生类在下方展开。所以,根据基础类进行造型就是一个从上面继承的过程,即“Upcasting”

2.动态绑定动态绑定是指在执行期间(非编译期)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法

3.多态接口的多种不同的实现方式即为多态

4.重载:在一个类定义中,可以编写几个同名的方法,但是只要它们的签名参数列表不同,Java就会将它们看做唯一的方法。简单的说,一个类中的方法与另一个方法同名,但是参数表不同,这种方法称之为重载方法

5.覆盖:覆盖是指派生类函数覆盖基类函数

6.抽象类含有抽象方法的类称为(且必须是)抽象类,只能做为基类,不能实例化

7.接口:接口将抽象类的概念更延伸了一步,它完全禁止了所有的函数定义

8.堆栈:驻留于常规 RAM(随机访问存储器)区域,但可通过它的“堆栈指针”获得处理的直接支持。堆栈指针若向下移,会创建新的内存;若向上移,则会释放那些内存。这是一种特别快、特别有效的数据保存方式,仅次于寄存器。http://baike.baidu.com/view/93201.htm

9.集合(容器):集合会自动扩充自己,以便适应我们在其中置入的任何东西。所以我们事先不必知道要在一个集合里容下多少东西。只需创建一个集合,以后的工作让它自己负责好了。

10.迭代器(Iterator):迭代器是一种对象,它能够用来遍历标准模板库容器中的部分或全部元素,每个迭代器对象代表容器中的确定的地址。

11.单根结构:所有类都继承自Object

12.参数化类型:保证下溯造型时获得正确的类型。JAVA:ArrayList list = new ArrayList<String>()。在C++中能表现为模版(template)

13.多线程:JAVA对多线程处理的支持是在对象这一级支持的,所以一个执行线程可表达为一个对象。

14.JAVA的资源锁定方案:Java 也提供了有限的资源锁定方案。它能锁定任何对象占用的内存(内存实际是多种共享资源的一种),所以同一时间只能有一个线程使用特定的内存空间。为达到这个目的,需要使用synchronized关键字。其他类型的资源必须由程序员明确锁定,这通常要求程序员创建一个对象,用它代表一把锁,所有线程在访问那个资源时都必须检查这把锁。 

15.永久性:Java 1.1 提供了对“有限永久性”的支持,这意味着我们可将对象简单地保存到磁盘上,以后任何时间都可取回。之所以称它为“有限”的,是由于我们仍然需要明确发出调用,进行对象的保存和取回工作。这些工作不能自动进行。

16.内存的分配:有六个地方都可以保存数据: 
(1) 寄存器。这是最快的保存区域,因为它位于和其他所有保存方式不同的地方:处理器内部。然而,寄存器的数量十分有限,所以寄存器是根据需要由编译器分配。我们对此没有直接的控制权,也不可能在自己的程序里找到寄存器存在的任何踪迹。 
(2) 堆栈。驻留于常规 RAM(随机访问存储器)区域,但可通过它的“堆栈指针”获得处理的直接支持。堆栈指针若向下移,会创建新的内存;若向上移,则会释放那些内存。这是一种特别快、特别有效的数据保存方式,仅次于寄存器。创建程序时,Java 编译器必须准确地知道堆栈内保存的所有数据的“长度”以及“存在时间”。这是由于它必须生成相应的代码,以便向上和向下移动指针。这一限制无疑影响了程序的灵活
性,所以尽管有些Java 数据要保存在堆栈里——特别是对象句柄,但Java 对象并不放到其中。   47
(3) 堆。一种常规用途的内存池(也在 RAM区域),其中保存了Java 对象。和堆栈不同,“内存堆”或“堆”(Heap)最吸引人的地方在于编译器不必知道要从堆里分配多少存储空间,也不必知道存储的数据要在堆里停留多长的时间。因此,用堆保存数据时会得到更大的灵活性。要求创建一个对象时,只需用new命令编制相关的代码即可。执行这些代码时,会在堆里自动进行数据的保存。当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会花掉更长的时间! 
(4) 静态存储。这儿的“静态”(Static)是指“位于固定位置”(尽管也在RAM里)。程序运行期间,静态存储的数据将随时等候调用。可用static 关键字指出一个对象的特定元素是静态的。但 Java 对象本身永远都不会置入静态存储空间。 
(5) 常数存储。常数值通常直接置于程序代码内部。这样做是安全的,因为它们永远都不会改变。有的常数需要严格地保护,所以可考虑将它们置入只读存储器(ROM)。 
(6) 非RAM 存储。若数据完全独立于一个程序之外,则程序不运行时仍可存在,并在程序的控制范围之外。其中两个最主要的例子便是“流式对象”和“固定对象”。对于流式对象,对象会变成字节流,通常会发给另一台机器。而对于固定对象,对象保存在磁盘中。即使程序中止运行,它们仍可保持自己的状态不变。对于这些类型的数据存储,一个特别有用的技巧就是它们能存在于其他媒体中。一旦需要,甚至能将它们恢复成普通的、基于RAM的对象。Java 1.1 提供了对Lightweight persistence 的支持。未来的版本甚至可能提供更完整的方案。

17.基本类型:boolean(1位),char(16位),byte(8位),short(16位),int(32位),long(64位),float(32位),double(64位),Void(java1.1加入)

18.高精度数字:BigInteger,BigDecimal。在java1.1加入

19.对基本类型赋值时是复制实际数值,对对象赋值时复制的是句柄

20.equals:equals的默认行为是比较句柄,除非覆盖equals方法。大多数Java 类库都实现了equals(),所以它实际比较的是对象的内容,而非它们的句柄。如:Integer i = new Integer(47);Integer m = new Integer(47);System.out.println(i.equals(m));//true

21.continue,break:配合标签使用可以达到跳出多个循环的功能。见p88

22.finalize:

(1)应用范围:释放不是由new分配的内存,如调用第三方库时可能调用了c的malloc,这时就需要在finalize里释放内存

(2)使用方法:System.gc();System.runFinalization();或System.runFinalizersOnExit();

23.类成员初始化:静态成员的初始化:static {},非静态成员初始化{}

24.数组初始化:(1)int arr[] = {1,2,3};(2)int arr[] = new int[]{1,2,3};(3)int arr[];arr = new int[3];与1相比,2的使用更灵活,可动态创建,但1却必须在定义的同时进行初始化

25.this调用构造方法:this可以用来在一个构造方法中调用另外一个构造方法,并且只能在第一行,this(arg1, arg2...);

26.类成员的访问权限:包访问(无关键词,对包内的所有类都可见,但对包外的类则是private)、public、protected、private

27.继承类对父类的访问权限:子类能访问父类的public、protected成员(不能访问private),当子类和父类处于同一包时可访问package access成员(否则也不可访问)

28.protected:不管是否在一个包内,子类都能访问父类的protected成员。同一包内的其它类也能访问类的protected成员。与package access成员相比,区别在于protected成员不管父类和子类是否在一个包内,都是可见的

29.SomeClass.java文件中只能有一个public类且名字一定是SomeClass。但可以存在任意的package access类(即class没有访问限制符的)。如:

package com.wogu;

public class Test {
	
	public static void main(String[] args) {
		
	}
}

class Test2 {
	
}

30.类只能是public或package access的,但内部类除外(可以是protected或private)

31.java的main:

public class Test {
	public Test() {
		System.out.println("xxx");
	}
	
	public static void main(String[] args) {
		//运行时会调用main,但并不实例化Test,如果
		//想输出xxx则必须自己实例化
		new Test();
	}
}

32.单元测试:在每个类内部都置入public static void main(String[] args){}方法,并在里面加入测试代码,然后在调用类中调用此方法

33.final数据:代表常数

(1)声明的同时必须赋值(或者将赋值放在构造函数中)

(2)只有基本数据类型才能在编译时封闭进计算过程里

(3)如果不是基本数据类型,则指句柄为常数,永远只能指向定义时的对象

(4)对于含有固定初始化值(即编译期常数)的fianl static基本数据类型,它们的名字根据规则要全部采用大写。但如果值不是固定值(如通过表达式计算得出)则还是采用小写

34.接口:接口内的方法即使不加public默认也是public的,数据成员即使不加static final默认也是static final的。实现接口时,必须将接口的方法定义为public的,否则会使该方法只能package access

35.接口数据成员的初始化:由于接口数据成员默认是static final,所以必须在声明时即赋值(可以是表达式)。另外,数据成员并不是接口的一部分,而是保存于那个接口的static存储区中

36.内部类

(1)普通类只能定义成public或package access,而内部类可定义成任何类型

(2)内部类保存了外部类的引用,可以访问外部类的所有成员(外部类Test内部类Inner,Inner想获得Test的句柄只需Test.this)

(3)要实例化一个内部类,必须先拥有外部类的实例化对象,再由这个实例化对象生成内部类

package com.wogu;

class Animal {
	class Dog {
		void eat() {
			System.out.println("dog eat");
		}
	}
}

public class Test {

	public static void main(String[] args) {
		Animal animal = new Animal();
		Animal.Dog dog = animal.new Dog();
		dog.eat();
	}
}

(4)内部类的继承:由于内部类构建器必须同封装类对象的一个句柄联系到一起,所以从一个内部类继承的时候,情况会稍微变得有些复杂。这儿的问题是封装类的“秘密”句柄必须获得初始化,而且在衍生类中不再有一个默认的对象可以连接。解决这个问题的办法是采用一种特殊的语法,明确建立这种关联

class Outer {
	class Inner {
		void say() {
			System.out.println("I'm Inner Class");
		}
	}
}

public class Test extends Outer.Inner {
	
	Test(Outer outer) {
		outer.super();
	}

	public static void main(String[] args) {
		Outer outer = new Outer();
		Test test = new Test(outer);
		test.say();
	}
}
(5)内部类的覆盖:继承时我们无法覆盖父类的内部类,java没有这种机制。但可以自己编码达到相同效果

class Egg2 {
	protected class Yolk {
		public Yolk() {
			System.out.println("Egg2.Yolk()");
		}
		public void f() {
			System.out.println("Egg2.Yolk.f()");
		}
	}
	private Yolk y = new Yolk();
	public Egg2() {
		System.out.println("New Egg2()");
	}
	public void insertYolk(Yolk yy) { y = yy; }
	public void g() { y.f(); }
}

public class BigEgg2 extends Egg2 {
	public class Yolk extends Egg2.Yolk {
		public Yolk() {
			System.out.println("BigEgg2.Yolk()");
		}   192
		public void f() {
			System.out.println("BigEgg2.Yolk.f()");
		}
	}
	public BigEgg2() { insertYolk(new Yolk()); }
	public static void main(String[] args) {
		Egg2 e2 = new BigEgg2();
		e2.g();
	}
}

37.内部类的应用场景

(1)实现一个接口,使自己能创建并返回一个句柄

(2)隐藏类,使外部类不能访问他

38.内部类的作用域:类嵌套于一个if语句的作用域内,这并不意味着类是有条件创建的(它会随同其他所有东西得到编译)。然而,在定义它的那个作用域之外,它是不可使用的

39.静态内部类:倘若为了创建内部类的对象而不需要创建外部类的一个对象,那么可将所有东西都设为static

(1)为创建一个static内部类的对象,我们不需要一个外部类对象

(2)不能从static内部类的一个对象中访问一个外部类对象

(3)可用于单元测试(在本书早些时候,我建议大家在每个类里都设置一个main(),将其作为那个类的测试床使用。这样做的一个缺点就是额外代码的数量太多。若不愿如此,可考虑用一个static内部类容纳自己的测试代码)

40.匿名类

(1)匿名类不能拥有一个构造函数,但可以用“{}”(类似普通类的非静态成员初始化,但匿名类不能有static{}初始化块)达到类似效果

(2)匿名类里不能有静态数据成员,除非定义成final(如final static int i = 0;)(static成员只能位于一个类的外部级别)

(3)匿名类不能有静态方法(static成员只能位于一个类的外部级别)

(4)若想使用匿名内部类外部定义的一个对象,则编译器要求外部对象为final属性

package com.wogu;

class Animal {
	private String name;
	
	Animal() {
		
	}
	
	Animal(String name) {
		this.name = name;
	}
	
	void eat() {
		System.out.println("Animal eat");
	}
	
	String getName() {
		return name;
	}
}

public class Test {

	public static void main(String[] args) {
		Animal dog = new Animal() {
			void eat() {
				System.out.println("dog eat");
			}
		};
		
		Animal cat = new Animal("cat") {
			void eat() {
				System.out.println(this.getName()+" eat");
			}
		};
		
		dog.eat();
		cat.eat();
	}
}

41.初始化顺序

(1)在采取其他任何操作之前,为对象分配的存储空间初始化成二进制零

(2)父类构造函数

(3)子类数据成员

(4)子类构造函数

42.构造函数和多态可能导致的陷阱:用尽可能简单的方法使对象进入就绪状态;如果可能,避免调用任何方法。在构建器内唯一能够安全调用的是在基础类中具有final属性的那些方法(也适用于private方法,它们自动具有final属性)。这些方法不能被覆盖,所以不会出现上述潜在的问题。 

abstract class Glyph {
  abstract void draw(); 
  Glyph() { 
    System.out.println("Glyph() before draw()"); 
    draw();  
    System.out.println("Glyph() after draw()"); 
  } 
} 
 
class RoundGlyph extends Glyph { 
  int radius = 1; 
  RoundGlyph(int r) { 
    radius = r; 
    System.out.println( 
      "RoundGlyph.RoundGlyph(), radius = " 
      + radius); 
  } 
  void draw() {  
    System.out.println( 
      "RoundGlyph.draw(), radius = " + radius); 
  } 
} 
 
public class PolyConstructors { 
  public static void main(String[] args) { 
    new RoundGlyph(5); 
  } 
}
//输出
//Glyph() before draw() 
//RoundGlyph.draw(), radius = 0 
//Glyph() after draw() 
//RoundGlyph.RoundGlyph(), radius = 5

43.下溯造型

(1)下溯造型可能不安全,但java会在运行时强制检查,如果调用非法会产生ClassCastException

(2)在运行期间对类型进行检查的行为叫作“运行期类型标识”(RTTI)

44.内建集合-数组

(1)java的内建集合是数组

(2)使用数组的优点:1.效率更高 2.支持基本数据类型(而其它集合类中要容纳基本数据类型则需要使用基本数据类型的包装类如:Integer,这是因为集合类只能容纳Object)

(3)数组的缺点:大小固定

45.java1.0,1.1的集合类:Vector(矢量,被java1.2中的ArrayList替代),BitSet(位集),Stack(堆栈,后进先出,继承自Vector),HashTable(实现了Dictionary(字典)抽象类)

46.java1.0,1.1的遍历:Enumeration(枚举)

47.java1.2集合库

(1)将集合库分为集合(collection)和映射(map)两个明确的概念:

a、collection包括List(必须按特定的顺序容纳元素。实现List接口的有ArrayList、LinkedList)、Set(不能有重复元素。实现Set接口的有TreeSet、HashSet)

b、一系列“键-值”对(这已在散列表身上得到了充分的体现)。从表面看,这似乎应该成为一个“键-值”对的“集合”,但假若试图按那种方式实现它,就会发现实现过程相当笨拙。只需创建一个集合,然后用它表示那一部分即可。这样一来,Map就可以返回自己键的一个Set、一个包含自己值的List或者包含自己“键-值”对的一个List。和数组相似,Map可方便扩充到多个“维”,毋需涉及任何新概念。只需简单地在一个Map里包含其他Map(后者又可以包含更多的Map,以此类推)

c、实现Map接口的有HashMap、TreeMap、WeakHashMap

48.java1.2集合示意图:虚线框代表“接口”,点线框代表“抽象”类,而实线框代表普通(实际)类。点线箭头表示一个特定的类准备实现一个接口(在抽象类的情况下,则是“部分”实现一个接口)。双线箭头表示一个类可生成箭头指向的那个类的对象


49.如何选择使用哪种集合【参考地址】【本书8.7.5 决定实施方案】

List的功能方法
    实际上有两种List: 一种是基本的ArrayList,其优点在于随机访问元素,另一种是更强大的LinkedList,它并不是为快速随机访问设计的,而是具有一套更通用的方法。
    List : 次序是List最重要的特点:它保证维护元素特定的顺序。List为Collection添加了许多方法,使得能够向List中间插入与移除元素(这只推荐LinkedList使用。)一个List可以生成ListIterator,使用它可以从两个方向遍历List,也可以从List中间插入和移除元素。
    ArrayList : 由数组实现的List。允许对元素进行快速随机访问,但是向List中间插入与移除元素的速度很慢。ListIterator只应该用来由后向前遍历ArrayList,而不是用来插入和移除元素。因为那比LinkedList开销要大很多。
    LinkedList : 对顺序访问进行了优化,向List中间插入与删除的开销并不大。随机访问则相对较慢。(使用ArrayList代替。)还具有下列方法:addFirst(), addLast(), getFirst(), getLast(), removeFirst() 和 removeLast(), 这些方法 (没有在任何接口或基类中定义过)使得LinkedList可以当作堆栈、队列和双向队列使用。
     Set的功能方法
    Set具有与Collection完全一样的接口,因此没有任何额外的功能,不像前面有两个不同的List。实际上Set就是Collection,只是行为不同。(这是继承与多态思想的典型应用:表现不同的行为。)Set不保存重复的元素(至于如何判断元素相同则较为负责)
    Set : 存入Set的每个元素都必须是唯一的,因为Set不保存重复元素。加入Set的元素必须定义equals()方法以确保对象的唯一性。Set与Collection有完全一样的接口。Set接口不保证维护元素的次序。
    HashSet : 为快速查找设计的Set。存入HashSet的对象必须定义hashCode()。
    TreeSet : 保存次序的Set, 底层为树结构。使用它可以从Set中提取有序的序列。
    LinkedHashSet : 具有HashSet的查询速度,且内部使用链表维护元素的顺序(插入的次序)。于是在使用迭代器遍历Set时,结果会按元素插入的次序显示。
     Map的功能方法
    方法put(Object key, Object value)添加一个“值”(想要得东西)和与“值”相关联的“键”(key)(使用它来查找)。方法get(Object key)返回与给定“键”相关联的“值”。可以用containsKey()和containsValue() 测试 Map中是否包含某个“键”或“值”。标准的 Java 类库中包含了几种不同的Map:HashMap, TreeMap, LinkedHashMap, WeakHashMap, IdentityHashMap。它们都有同样的基本接口Map,但是行为、效率、排序策略、保存对象的生命周期和判定“键”等价的策略等各不相同。
    执行效率是Map的一个大问题。看看get()要做哪些事,就会明白为什么在ArrayList中搜索“键”是相当慢的。而这正是HashMap提高速度的地方。HashMap使用了特殊的值,称为“散列码”(hash code),来取代对键的缓慢搜索。“散列码”是“相对唯一”用以代表对象的int值,它是通过将该对象的某些信息进行转换而生成的。所有 Java 对象都能产生散列码,因为hashCode()是定义在基类Object中的方法。
    HashMap就是使用对象的hashCode()进行快速查询的。此方法能够显着提高性能。
    Map : 维护“键值对”的关联性,使你可以通过“键”查找“值”
    HashMap : Map基于散列表的实现。插入和查询“键值对”的开销是固定的。可以通过构造器设置容量capacity和负载因子load factor,以调整容器的性能。
    LinkedHashMap : 类似于HashMap,但是迭代遍历它时,取得“键值对”的顺序是其插入次序,或者是最近最少使用(LRU)的次序。只比HashMap慢一点。而在迭代访问时发而更快,因为它使用链表维护内部次序。
    TreeMap : 基于红黑树数据结构的实现。查看“键”或“键值对”时,它们会被排序(次序由Comparabel或Comparator决定)。TreeMap的特点在于,你得到的结果是经过排序的。TreeMap是唯一的带有subMap()方法的Map,它可以返回一个子树。
    WeakHashMao : 弱键(weak key)Map,Map中使用的对象也被允许释放: 这是为解决特殊问题设计的。如果没有map之外的引用指向某个“键”,则此“键”可以被垃圾收集器回收。
    IdentifyHashMap : 使用==代替equals()对“键”作比较的hash map。专为解决特殊问题而设计。

50.数组的排序和搜索:

(1)排序用Arrays.sort(),搜索用Arrays.binarySearch()。注意在搜索前一定要先排序,否则可能出现不可测行为

(2)若想实现自定义的排序,则可在sort()的第二个参数中传入一个实现了Comparator的类

public class AlphaComp implements Comparator { 
  public int compare(Object o1, Object o2) { 
    // Assume it's used only for Strings... 
    String s1 = ((String)o1).toLowerCase(); 
    String s2 = ((String)o2).toLowerCase(); 
    return s1.compareTo(s2); 
  } 
  public static void main(String[] args) { 
    String[] s = Array1.randStrings(4, 10); 
    Array1.print(s); 
    AlphaComp ac = new AlphaComp(); 
    Arrays.sort(s, ac); 
    Array1.print(s); 
    // Must use the Comparator to search, also: 
    int loc = Arrays.binarySearch(s, s[3], ac); 
    System.out.println("Location of " + s[3] + 
     " = " + loc); 
  } 
}

(3)实现自定义排序的另一种方法:被比较的对象自己实现Comparable接口

public class CompClass implements Comparable { 
  private int i; 
  public CompClass(int ii) { i = ii; } 
  public int compareTo(Object o) { 
    // Implicitly tests for correct type: 
    int argi = ((CompClass)o).i; 
    if(i == argi) return 0; 
    if(i < argi) return -1; 
    return 1; 
  } 
  public static void print(Object[] a) { 
    for(int i = 0; i < a.length; i++) 
      System.out.print(a[i] + " "); 
    System.out.println(); 
  } 
  public String toString() { return i + ""; } 
  public static void main(String[] args) { 
    CompClass[] a = new CompClass[20]; 
    for(int i = 0; i < a.length; i++) 
      a[i] = new CompClass( 
        (int)(Math.random() *100)); 
    print(a); 
    Arrays.sort(a); 
    print(a); 
    int loc = Arrays.binarySearch(a, a[3]); 
    System.out.println("Location of " + a[3] + 
     " = " + loc); 
  } 
}
51. 链接的排序和搜索:Collections接口中的sort和binarySearch

52.Collections还含有大量实用工作:max,min等

53.Exception

(1)重新抛出Exception:作用是将方法不能处理异常,交给调用者去处理

(2)Exception在继承中的行为:用于一个特定方法的“违例规范接口”可能在继承和覆盖时变得更“窄”,但它不会变得更“宽”——这与继承时的类接口规则是正好相反的

(3)finally:不管异常是否发生,都会被执行的部分

(4)缺点:违例可能被替换

class VeryImportantException extends Exception { 
	  public String toString() { 
	    return "A very important exception!"; 
	  }
} 

class HoHumException extends Exception { 
  public String toString() { 
    return "A trivial exception"; 
  } 
}

public class Test {
	void f() throws VeryImportantException {
		throw new VeryImportantException();
	}

	void dispose() throws HoHumException {
		throw new HoHumException();
	}

	public static void main(String[] args) throws Exception {
		Test lm = new Test();
		try {
			lm.f();
		} finally {
			lm.dispose();
		}
	}
}

54.IO库

(1)大致可以分为两类:InputStream(与输入有关的所有类都从InputStream继承),OutputStream(与输出有关的所有类都从OutputStream继承)

(2)InputStream分类:字节数组(ByteArrayInputStream)、String对象(StringBufferInputStream)、文件(FileInputStream)、管道(PipedInputString)、其他起源地(如Internet连接等)

(3)SequenceInputStream:将两个或更多的InputStream对象转换成单个InputStream使用

(4)OutputStream分类:ByteArrayOutputStream、FileOutputStream、PipedOutputStream等

54.java1.1的IO流:主要为国际化,因为老式的IO流只支持8位字节流,不能很好的控制16位Unicode字符

(1)新增Reader和Writer层次

(2)InputStreamReader将一个InputStream转换成Reader,OutputStreamWriter将一个OutputStream转换成Writer

(3)新库也对速度进行了优化,可比旧库更快地运行

(4)由于受到库设计的一些限制,有时我们不得不使用Java 1.0的IO流类。特别要指出的是,在旧流库的基础上新加了java.util.zip库,它们依赖旧的流组件。所以最明智的做法是“尝试性”地使用Reader和Writer类。若代码不能通过编译,便知道必须换回老式库。

(5)旧库和新库对照表


(6)过滤器:Java 1.0类 对应的Java 1.1类 
FilterInputStream FilterReader 
FilterOutputStream FilterWriter(没有子类的抽象类) 
BufferedInputStream BufferedReader(也有readLine()) 
BufferedOutputStream BufferedWriter 
DataInputStream 使用DataInputStream(除非要使用readLine(),那时需要使用一个BufferedReader) 
PrintStream PrintWriter 
LineNumberInputStream LineNumberReader 
StreamTokenizer StreamTokenizer(用构建器取代Reader) 
PushBackInputStream PushBackReader 

(7)没有对应Java 1.1类的Java 1.0类 

DataOutputStream、File、RandomAccessFile、SequenceInputStream

55.压缩:gzip、zip

56.序列化:

(1)Serializable:不想让java自动序列化的字段可以用transient修饰

(2)Externalizable:如果想选择性的序列化一个对象的内容,则需要实现Externalizable接口,并实现writeExternal()和readExternal()

57.RTTI

(1)类标记(class)不仅可以应用于普通类,也可以应用于接口、数组以及基本数据类型。除此以外,针对每种基本数据类型的封装器类,它还存在一个名为TYPE的标准字段。TYPE字段的作用是为相关的基本数据类型产生Class对象的一个句柄

(2)RTTI形式包括:

a、instanceof(静态),Class.isInstance(动态的instanceof)

b、经典造型,如"(Shape)",它用RTTI确保造型的正确性,并在遇到一个失败的造型后产生一个ClassCastException违例

c、代表对象类型的Class对象。可查询Class对象,获取有用的运行期资料。 

(3)Class.newInstance创建的类必须有一个默认构建器,没有办法用newInstance()创建拥有非默认构建器的对象

58.反射

(1)RTTI和“反射”之间唯一的区别就是对RTTI来说,编译器会在编译期打开和检查.class文件。换句话说,我们可以用“普通”方式调用一个对象的所有方法;但对“反射”来说,.class文件在编译期间是不可使用的,而是由运行期环境打开和检查。

(2)用get()和set()方法读取和修改与Field对象关联的字段,以及用invoke()方法调用与Method对象关联的方法。此外,我们可调用方法getFields(),getMethods(),getConstructors(),分别返回用于表示字段、方法以及构建器的对象数组

59.clone

(1)通过clone复制的对象只能进行浅层复制,即不能复制被复制对象所包含的对象

(2)要使自己的类可以被clone,首先要覆盖父类(Object)的clone,并把访问权限改为public,然后使类实现Cloneable接口(虽然是空接口)

(3)为什么Cloneable是空接口,但还要实现它?

a、首先,可能有一个上溯造型句柄指向一个基础类型,而且不知道它是否真的能克隆那个对象。在这种情况下,可用instanceof 关键字(第 11章有介绍)调查句柄是否确实同一个能克隆的对象连接

b、考虑到我们可能不愿所有对象类型都能克隆。所以Object.clone()会验证一个类是否真的是实现了Cloneable 接口。若答案是否定的,则“掷”出一个CloneNotSupportedException违例。所以在一般情况下,我们必须将“implement Cloneable”作为对克隆能力提供支持的一部分

(4)==和!=运算符只是简单地对比句柄的内容。若句柄内的地址相同,就认为句柄指向同样的对象,所以认为它们是“等价”的

(5)一个ArrayList包含若干个自定义对象MyObj,若想对这个ArrayList进行深度克隆,首先应clone ArrayList,然后再遍历以克隆ArrayList包含的所有MyObj

(6)可以通过序列化和反序列化达到克隆目的,但效率较clone差很多

70.Runnable:实现Runnable接口的类本身不具有线程处理能力(只是指明实现了run方法),需将其传给一个Thread对象才能执行

71.线程同步:

(1)synchronized方法(不能是字段)

(2)synchronized块

synchronized(syncObject) { 
  // This code can be accessed by only 
  // one thread at a time, assuming all 
  // threads respect syncObject's lock 
}

(3)synchronized可以修饰方法、代码块,但不能修饰属性、构造方法

(4)sleep,yield,(suspend, resume)会交出CPU时间,但都不会解除对共享资源的锁定,而wait却会

(5)wait与notify配合使用,且只能在同步方法或块内部使用

72.优先级Thread.setPriority,Thread.getPriority

73.java1.2起不支持使用Thread的stop(),suspend(),resume()以及destroy()方法

74.释放同步监视器的锁定

    任何线程进入同步代码块、同步方法之前,必须先获得对同步监视器的锁定,那么何时会释放对同步监视器锁定?
    程序无法显示的释放对同步监视器的锁定,线程可以通过以下方式释放锁定:
    A、当线程的同步方法、同步代码库执行结束,就可以释放同步监视器
    B、当线程在同步代码库、方法中遇到break、return终止代码的运行,也可释放
    C、当线程在同步代码库、同步方法中遇到未处理的Error、Exception,导致该代码结束也可释放同步监视器
    D、当线程在同步代码库、同步方法中,程序执行了同步监视器对象的wait方法,导致方法暂停,释放同步监视器
 
    下面情况不会释放同步监视器:
    A、当线程在执行同步代码库、同步方法时,程序调用了Thread.sleep()/Thread.yield()方法来暂停当前程序,当前程序不会释放同步监视器
    B、当线程在执行同步代码库、同步方法时,其他线程调用了该线程的suspend方法将该线程挂起,该线程不会释放同步监视器。注意尽量避免使用suspend、resume
75.同步锁
    使用锁和使用同步很类似,只是使用Lock时显示的调用lock方法来同步。而使用同步方法synchronized时系统会隐式使用当前对象作为同步监视器,
    同样都是“加锁->访问->释放锁”的操作模式,都可以保证只能有一个线程操作资源。
    同步方法和同步代码块使用与竞争资源相关的、隐式的同步监视器,并且强制要求加锁和释放锁要出现在一个块结构中,而且获得多个锁时,
    它们必须以相反的顺序释放,且必须在与所有锁被获取时相同的范围内释放所有资源。
    Lock提供了同步方法和同步代码库没有的其他功能,包括用于非块结构的tryLock方法,已经试图获取可中断锁lockInterruptibly()方法,
    还有获取超时失效锁的tryLock(long, timeUnit)方法。
    ReentrantLock具有重入性,也就是说线程可以对它已经加锁的ReentrantLock再次加锁,ReentrantLock对象会维持一个计数器来追踪lock方法的嵌套调用,
    线程在每次调用lock()加锁后,必须显示的调用unlock()来释放锁,所以一段被保护的代码可以调用另一个被相同锁保护的方法。
76.Callable和Future:实现线程的又一方式,jdk5后新增的:
    Callable接口定义了一个call方法可以作为线程的执行体,但call方法比run方法更强大:
    A、call方法可以有返回值
    B、call方法可以申明抛出异常
 
    Callable接口是JDK5后新增的接口,而且不是Runnable的子接口,所以Callable对象不能直接作为Thread的target。而且call方法还有一个返回值,
    call方法不能直接调用,它作为线程的执行体被调用。那么如何接收call方法的返回值?
    JDK1.5提供了Future接口来代表Callable接口里的call方法的返回值,并为Future接口提供了一个FutureTask实现类,该实现类实现Future接口,
    并实现了Runnable接口—可以作为Thread的target。
 
    Future接口里定义了如下几个公共方法控制他关联的Callable任务:
    A、boolean cancel(Boolean mayInterruptlfRunning):试图取消该Future里关联的Callable任务
    B、V get():返回Callable任务里的call方法的返回值,调用该方法将导致线程阻塞,必须等到子线程结束才得到返回值
    C、V get(long timeout, TimeUnit unit):返回Callable任务里的call方法的返回值,该方法让程序最多阻塞timeout和unit指定的时间。
        如果经过指定时间后Callable任务依然没有返回值,将会抛出TimeoutException。
    D、boolean isCancelled:如果在Callable任务正常完成前被取消,则返回true。
    E、boolean isDone:如果Callable任务已经完成,则返回true
 
    创建、并启动有返回值的线程的步骤如下:
    一、创建Callable接口的实现类,并实现call方法,该call方法的返回值,并作为线程的执行体。
    二、创建Callable实现类的实例,使用FutureTask类来包装Callable对象,该FutureTask对象封装了该Callable对象的call方法的返回值
    三、使用FutureTask对象作为Thread对象的target创建、并启动新线程
    四、调用FutureTask对象的方法来获得子线程执行结束后的返回值

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值