Chapt 6
所有非primitive对象都有一个toString()方法,当编译器需要一个String而它是对象的时候,编译器自动调用该方法。可以对其进行重写(overwrite)
可以为每个类都写main函数用作测试,只要在与文件名同名的那个main里面调用其他类的main函数就可以了
继承设计的通用准则,把数据设成private,把方法设成public的。
子类继承父类,子类overwrite父类函数时候,如果要调用所overwrite的函数,需用super
派生类对象初始化时,先初始化基类,在初始化自己。如果基类的构造函数是无参数的,编译器会自动帮你调用,否则需要通过super(参数列表)来完成基类的初始化。
编译器会强制你将基类构造函数的调用放在派生类的构造函数的最前面。
在清理方法中,如果子对象之间有依赖关系,则其清理顺序:先按照创建对象的相反顺序进行类的清理,然后调用基类的清理方法。这与C++一样
最好不要依赖垃圾回收器去做任何与内存回收无关的事情,如果你要进行清理,一定要自己写清理方法,别用finalize
合成用于新类要用到旧类的功能,而不是其接口, 故一般将其private。表达的是(has-a)关系
继承时对已有的类做改造,获得新的版本,即将一个较为抽象的类改造成能适用于某些特定需求的类。表达的是(is-a)关系。能使用上传特性。
最好的做法:将数据成员设成private的,然后用protected的方法来控制继承类的访问权限
上传(upcasting): 将派生类的对象的reference转化成基类对象的reference来使用,这是完全安全的。
常见的定义常量: public static final int a = 0;
final的数据一定要进行赋值,可以在定义的时候就初始化,可以在构造函数里面初始化,也可以用{} 或static{} 来进行初始化。
final也可以用来修饰参数变量,但用处不大。
final方法目的:1)禁止派生类进行修改或覆盖。2)效率:final方法会转换成内联。(这个用处不大)
final类:这个类不能被继承。数据可以是final的,也可以不是final的。但类不能被继承,也就没了被overwrite的可能,所以类里面定义的方法可以看成是final的了。
Chapt 7
多态性:通过继承类对象赋给基类对象,通过调用该引用就知道之前它是由什么继承类对象所创建的,这个过程是怎样的?(或者说编译器是怎么知道它原来是什么派生类,如果说上传之后就不知道对象类型的话)
下传的时候又是怎么知道的呢?(强制转换之后怎么才会抛出ClassCastException?编译器是怎么知道的啊.)
运行时类型鉴别?
Chapt 8
abstract与interface的功能类似,为什么还要interface?
interface能够提供多重继承(能让你将一个对象上传到多个基类的手段)
一般优先使用interface,在不得不定义方法或成员变量的情况下,你才能 把它改成abstract类。
interface里面的方法自动声明为public,所以实现接口方法的时候一定要声明为public,否则报错。
interface里面可以包含数据成员,但它们天生就是static和final的
故可以用来定义常量,JAVA编程风格中,常量用大写字母,中间用下划线分隔。
派生类implements 某接口:
1)如果基类已经实现了该接口的方法,可以不用再实现,再实现的话等于override基类的那个方法,不过基类的那个方法必须是public的
2)基类implements某接口,但其没有实现,可以留给其派生类实现,但是创建对象时候主要要new派生类的。故只要类系中某个implements了接口,该类系的类只要实现了该接口,就可以上传成为接口类型。注意,实现的时候要public类型
合并接口的时候会产生名字冲突:最好就是减少同名函数,这里重载,override,实现等都出现,注意就是不能仅依靠返回值来区分两个不同的函数
interface可以用extends实现继承多个接口,注意只对interface有效
接口与类之间,接口与接口之间都可以嵌套(具体再查看)
内部类,在一个类里面定义一个新的class
除非是在“宿主类(outer class)”的非static方法里面,否则无论你在那里创建内部类的对象,都必须用OuterClassName.InnerClassName的形式来表示这个对象的类型(这只能在宿主类和内部类之间是这样,外面的类就不能访问到内部类了,无论用什么修饰符号,要想将内部类也用在外面,就可以通过接口实现,通过upcast)
常见的是,让宿主类提供一个会返回内部类的reference的方法
普通类是不能定义成private和protected的,而内部类却可以做到。
内部类可以定义在方法和作用域里面。
理由:你在实现某个接口,这样你才能创建并且返回这个接口的reference
你正在处理一个复杂问题,需要创建一个类,但是不想让大家知道
匿名类:举例:创建的匿名类就是继承Contents的,故可以上传
public Contents cont(){
return new Contents() {
private int i = 11;
public int value() {return i;}
}; //注意
}
//若构造函数有参数,则在return new Contents(参数列表)
换成内部类就是这样:
class MyContents implements Contents {
private int i = 11;
public int vlaue() {return i;}
}
return new MyContents();
在定义匿名内部类的时候,还要用到外面的对象,那编译就会要求你把这个参数的
reference声明成final的,否则编译报错。
进行类似构造函数的操作:实例初始化(instance initializaion),传给实例初始化的可以不用final,其他的还是要定义为final
{
//初始化
}
内部类到底有什么好处?
当你需要实现一个类多重继承多个非interface的类(abstract和非abstract)时。这也是interface不能解决的。
当你要同时实现interface和继承某个class的时候,但是接口和类都有相同的方法,你现在你不想override类的那个方法,但你却必须实现它(接口要求)。这时候只能用内部类来实现
内部类与宿主类怎么打交道?
内部类能够自动获得访问其宿主类成员的权限(private成员也可以)-----怎么做到呢?内部类里肯定会有一个指向“要负责创建它的”宿主类对象的reference.这样,当你引用宿主类的成员的时候,就会使用那个(隐蔽的)reference来选取成员。这是编译器做的。
创建内部类对象的前提就是,要获得宿主类对象的reference。
内部类对象到底是怎样的?
内部类的初始化与宿主类是什么样的顺序呢?
内部类对象创建的时机与宿主类对象的创建没什么关系
内部类的标识符
内部类也会生成.class文件,文件名为:宿主类的名字,加上'$',加上内部类名字
如果是匿名类,那么'$'后跟的是数字
创建内部类的时候,别去扩展或者修改宿主类的接口
回调:你给别的对象一段信息,然后在未来某个时点,让它反过来调用发起方对象。
C/C++:用指针实现
JAVA:用内部类实现:当你想回调某些方法的时候,将该方法放在内部类的方法中(内部类可以调用宿主类的数据结构和方法),然后在宿主类提供方法返回该内部类的对象。这样你就能通过该方法操控回宿主类了
内部类更实际的用法:控制框架(conrol framework)
定义抽象类event,提供一些方法
定义控制类Controller,对event进行控制。
宿主类继承控制类,内部类继承event
Chapt 9
异常在类系中是怎样的?
JVM是怎样捕捉到异常并对异常匹配的呢?
try...catch...finally与throw各有什么优势?
try...catch:“继续”
throw:“终止”
常见的异常:
自定义异常:
能在源代码里面定义当出现异常的时候自动进行处理吗?
重抛异常:将异常往上一级抛
printStackTrace()打印出来的还是异常发生的地方
若在重抛的时候e.fillInStackTrace()那么打印出来的就是重抛出来的地方
将Throwable对象(cause)作为参数初始化异常。
在Throwable的子类中,只有三种基本的异常类提供了带cause参数的构造函数:Error,Exception, RuntimeException
其他不能用构造函数,只能重写Throwable的initCause()方法。
Java会用它的标准运行时检查系统为你搞定这类问题(RuntimeException下面的异常)
如果RuntimeException不受阻拦的冲到main(),那么它就会在程序退出的时候,调用printStackTrace().
在有break和continue语句的情况下,finally语句也会得到执行。
第一个异常尚未得到处理,就产生了第二个异常。-----在finally里面抛出异常
丢失的异常---Java的异常处理的实现中的错误
加在异常上面的限制:覆写方法的时候,你只能抛出这个方法在基类中的版本所声明的异常。这是一个有用的限制,因为同基类打交道的代码能自动地同它的派生类,包括它抛出的异常打交道。
但这种限制对构造函数不起作用。
由于派生类的构造函数总是会以这样或那样的形式调用基类的构造函数(默认的构造函数),派生类的构造函数必须在异常说明中声明,它可能会抛出其基类构造函数所抛出的异常。注意:派生类的构造函数不能捕获任何由基类构造函数抛出的异常
如果你用的是具体的某个类的对象的reference的话,编译器只会强制要求你捕获这个类抛出的异常。但是如果你将它上传到基类,那么编译器就会(很严格地)要求你捕获基类的异常。
不能根据异常说明来重载方法。
在继承和覆写的过程中,方法的“异常说明接口”不是变大而是变小。
checked exception 与 unchecked exception
异常的使用准则
Chapt10
RTTI:run-time type identification:运行时类型识别
让你在只持有这个对象的基类的reference的情况下,找出它的确切的类型。
Class对象(Class object)在程序运行时候保存类的信息
实际上类的“常规”对象是有Class对象创建的。
程序里的每个类都要有一个Class对象。即每次写好并且编译了一个新的类的时候,你就创建了一个新的Class对象。
当你需要创建类对象的时候:
JVM会检查是否已经装载那个Class对象,若没有,则找那个.class文件装载。
class常数:可用于普通类和接口,数组和primitive类型。
比如你创建了一个类Gum
那么你就可以通过Gum.class来获得Class对象的reference,从而调用Class里面的方法。
instanceof判断一个对象是否是一个类的实例:静态比较
isstanceof(object obj) 看这两个对象是否兼容:动态比较
java.util提供了好集中关联性数组(Map)
在关联性数组中,下标被称为键(key),而与之相关联的对象则被称为值(value)
如果我们知道某个类名的String值,比如说“c1.Dog”。那么可以通过下面的方法来创建实例
Class.forName("c1.Dog").newInstance()。
Java以JavaBeans的形式提供了基于组件的编程的支持。
Chapt11
数组标识符其实只是一个引用,指向在堆(heap)中创建的一个真实对象,这个(数组)对象用以保存指向其他对象的引用。
数组的length只表示数组能容纳多少元素,不是实际保存的元素个数。
容器类只能保存对象的引用,不能直接保存基本类型,这与数组不同。所以需要使用基本类型的包装类。
浅复制?
通过使用回调,可以将会发生变化的代码分离开来,然后由不会发生变化的代码回头调用会发生变化的代码。
Java有两种方式提供比较功能,
1.实现java.lang.Comparable接口,实现compareTo就行,如不实现,那么调用Arrays.sorts()就会产生ClassCastException异常
使用策略(strategy)设计模式。通过使用策略,将会发生变化的代码包装在类中(即所谓策略对象)。将策略对象交给保持不变的代码,后者使用此策略实现它的算法。也就是说,可以为不同的比较方式生成不同的对象,将他们用在同样的排序程序中。
JAVA容器类:能够自动调整容量
Set:每个对象只保存一份
Map:一种关联性数组,允许你将任意一个对象与另一个对象关联起来。
对容器的默认的打印动作(使用容器提供的toString()方法)即可生成可读性很好的结果。数组则不行
List按对象进入的顺序保存对象,不做排序或编辑操作。
Set对每个对象只接受一次,并使用自己内部的排序方法。
Map对每个元素只保存一份,但这是基于“键”的。Map也有内置的排序。
LinkedHashSet、LinkedHashMap对添加元素的顺序很重要的话
填充容器:
与Arrays一样,Collections也有一个使用的static方法集,其中包括有fill(),用同一个对象的引用来填充容器,并且只对List对象有用。对Set或Map并不起作用。
ArrayList:“能够自动扩展的数组”
用法:创建一个ArrayList,使用add()添加对象,使用get()和索引取出对象。size()告诉你容器中有多少元素。
容器操控的对象都是Object
参数化类型:
许多情况下会需要基于某种类型而生成新的类型,如果编译期可以获得特定类型的信息,将会很有用
在Java中称为范型。一对尖括号,中间包含类型信息,通过这些特征就可以识别对范型的使用。如:创建一个存储Shape的ArrayList
ArrayList<Shape> shapes = new ArrayList<Shape>();
接口:
public interface Iterable<T>
Iterator<T> iterator()
返回一个在一组 T 类型的元素上进行迭代的迭代器。
实现类:ArrayList
clone一般做的是浅复制???
使用linkedList构造栈和队列(使用合成)
TreeSet:红黑树的数据结构
HashSet:散列函数
LinkedHashSet:用散列函数加快查询速度,用链表维护元素的次序
Set需要维护元素的存储顺序,故必须实现Comparable接口,并且定义compareTo()方法
HashMap使用:hashCode()和equals()
散列算法与散列码
HashMap的性能因子
持有引用
三个继承自抽象类Reference的类:SoftReference(软引用)、WeakReference(弱引用)、PhantomReference(虚引用)
Chapt12
目录列表器:
File类:根据字符串(路径名)创建文件对象,通过list()方法将所有目录和文件列出来
可对list()内容进行过滤,实现FilenameFilter,list会自动为该目录下的所有对象调用accept()进行比较。
Java中"流类库":创建一个单一的结果流,却需要创建多个对象。
"修饰器模式":利用层叠的数个对象为当个对象动态地和透明地添加职责的方式。
规定:所有封装于初始对象内部的对象具有相同的接口。使得修饰器的基本应用具有透明性---可以向修饰过或没有修饰过的对象发送相同的消息。
FilterInputStream和FilterOutputStream是提供给修饰接口用于控制特定输入流(InputStream)和输出流(OutputStream)的两个类。
FilterInputStream和FilterOutputStream这两个类有很多子类,完成各种不同的功能
Reader、Writer提供面向字符(Unicode)操作。
InputStream、OutputStream提供面向字节的操作。
InputStreamReader继承自Reader,提供字节流通向字符流的桥梁
OutputStramWriter继承自Writer,提供字符流通向字节流的桥梁
InputStream和OutputStream的子类都存在相应的Reader和Writer进行适配
System的in是InputStream类型的
System的out、err是PrintStream类型的
一个程序的标准输出可以称为另一程序的标准输入。
IO重定向操控的是字节流而不是字符流
怎么重定向还不是很了解
对基本类型的IO输入是怎么实现的?先用String处理,再转化成基本类型?
需要先了解“修饰器模式”才能更好地理解IO类库的设计思想。
Chapt 13
线程,使用动机:
快速响应用户界面
优化程序吞吐量
编程便利,松散耦合
加入到某个线程:
一个线程在其他线程之上调用join()方法-------等待一段时间直到第二线程结束才继续执行。如果第二线程被终止(interrupt),那么该线程就直接返回
一个线程在该线程上调用interrupt()时,将给该线程设定一个标志,表明该线程已经被中断。
catch捕获InterruptedException异常会清除该线程上的interrupted标记
sleep、join会产生异常,
yield不会产生异常。
实现线程的两种方法:
实现Runnable接口:通过实现Runnable接口的类作为Thread构造函数的参数产生Thread 线程对象,再通过此对象调用start();
继承Thread类:可以在构造函数里面调用start();
可以使用(命名、匿名)内部类实现Runnable接口,使得结构紧凑点。可以在方法里面实现内部类,使得某个方法可以用多线程方式处理。
不正确地访问资源的例子------结果有时候很难出现
在一个方法里面定义内部类,要用到方法里内部类外的对象,那么该对象要定义为final
一个资源测试框架 --- 没看