资源描述
Java编程思想笔记
全面的将Thinking in java的知识点系统整理了下.与之前整理的东西不同,这部分可能更适合自己看,不过还是希望对大家有所帮助(其实还有很多java基础东东,thinking in java没有提到,后面也会给出相应的整理文档,主要是net,rmi,security等)
若有疑问<ymkyve501@>
目录
Java编程思想笔记 1
第1章 对象导论 1
第2章 一切都是对象 1
第3章 操作符 3
第4章 控制执行流程 6
第5章 初始化和清理 6
第6章 访问权限控制 8
第7章 复用类 8
第8章 多态 9
第9章 接口 11
第10章 内部类 13
第11章 持有对象 16
第12章 通过异常处理错误 21
第13章 字符串 25
第14章 类型信息 27
第15章 泛型 32
第16章 数组 33
第17章 容器深入研究 34
第18章 Java I/O系统 37
第19章 枚举类型 41
第20章 注解 44
第20章 并发 45
第22章 图形化用户界面 62
Java编程思想笔记
第1章 对象导论
看完整本书再来看第1章会更有体会.
1. 万物皆对象
2. 程序是对象的集合,它们通过发送消息来告知彼此所要做的
3. 每个对象都有自己的由其他对象所构成的存储
4. 每个对象都拥有类型
5. 某一特定类型的所有对象都可以接收同样的消息
第2章 一切都是对象
1. OOP:Object Oriented Programming 面向对象的程序设计
2. Java用引用(reference)操纵对象
String s;
3. 必须由你创建所有对象
3.1 一般用new 来创建(堆)
String s = new String("str");
对象的存储的地方(内存)
a. 寄存器
b. 栈
c. 堆
d. 常量存储
e. 非RAM存储
3.2 基本类型(栈)
Java的基本类型所占存储空间的大小是确定的
基本类型
大小
最小值
最大值
包装类型
boolean
-
-
-
Boolean
char
16bit
Unicode
Character
byte
8bit
Byte
short
16bit
Short
int
32bit
Integer
long
64bit
Long
float
32bit
Float
double
64bit
Double
void
-
-
-
Void
因为char是unicode(采用双字节对字符进行编码),可以存储汉字
char a ='工';//char可以存储汉字,unicode编码 2bitjavascript:void(0)
String str ="工";//占多少bit要视其编码 UTF-8占3bit GBK占2bit
3.3 高精度数字
没有对应的基本类
BigInteger:支持任意精度的整数
BigDecimal:支持任务精度的定点数
4. 类
4.1 成员变量中基本类型成员默认值
boolean
false
char
‘\u0000’ null
byte
(byte)0
short
(short)0
int
0
long
0L
float
0.0f
double
0.0d
注意:这里是指基本类型作为类成员变量时有默认值,若在方法中出现的局部变量,未初始化是不能引用的(编译不通过)
4.2 参数传送
一般对象传递的是reference,而基本类型传递的是值
public static void main(String[] args) {
int c =2;
System.out.println(get(c));
System.out.println(c);
}
static int get(int i){
i =3;
return i;
}
5. Java编译与运行(具体参考JVM部分文档)
Javac编译
javac TestLoader.java
Java 运行(-cp –classpath可指定classpath)
java TestLoader
Javadoc 提取注释
javadoc TestLoader.java
另外也要注意类加载问题(具体参考ClassLoader部分文档)
//BootStrapClassLoader加载的class
System.out.println(System.getProperty("sun.boot.class.path"));
//ExtClassLoader加载的class
System.out.println(System.getProperty("java.ext.dirs"));
//AppClassLoader加载的class
System.out.println(System.getProperty("java.class.path"));
6. 注释和嵌入式文档
解决代码文档的维护问题:将代码同文档”链接”起来.
第3章 操作符
在最底层,java中的数据是通过使用操作符来操作的
原码: 一个整数,按照绝对值大小转换成的二进制数,称为原码
反码: 将二进制数按位取反,所得的新二进制数称为原二进制数的反码
补码:反码加1称为补码
1. Java中的二进制(以byte为例)
42的正数表示: 00101010
42的负数表示: 00101010取反再加1,即 11010110
高位表示符号位,所以byte型的最大表示范围为-128~127
// 以下解析过程就会越界
// 1.Integer.toBinaryString(-100) 解析-100为二进制
// 2.Integer.parseInt("11111111111111111111111110011100",2)
// 2.把11111111111111111111110011100当作无符号表示来解析.
Integer.parseInt(Integer.toBinaryString(-100), 2);
2. 逻辑运算符
&& || !
3. 按位操作符
& | ~ ^
位运算符应用于每个二进制位
位运算都要当作是补全位数的值来运算
~ 按位非(NOT)(一元运算)
& 按位与(AND)
| 按位或(OR)
^ 按位异或(XOR) (位相同为0,不同为1)
>> 右移 (高位不变)
>>> 右移 (无符号位移,高位一起移动)
运算符 结果
<< 左移
&= 按位与赋值
|= 按位或赋值
^= 按位异或赋值
>>= 右移赋值
>>>= 右移赋值,左边空出的位以0填充
<<= 左移赋值
//以下按byte来计算
//0000 0000 & 0000 0001
System.out.println(0 & 1);
//0000 0000 | 0000 0001
System.out.println(0 | 1);
//~0000 0001
System.out.println(~1);
//0000 0000 ^ 0000 0001
System.out.println(0 ^ 1);
输出:0,1,-2,1
另外注意:对byte和shor的移位运算,会先转换成int再移位.再截断再赋值
4. 三元操作符
boolean?truevalue:falsevalue
5. ++操作符
int i =0;
//表达式先返回i,再自加
System.out.println(i++);
System.out.println(i);
i =0;
//表达式先自加,再返回i
System.out.println(++i);
System.out.println(i);
6. 进制转换
二进制转八进制:三位转一位
二进制转十六进制:四位转一位
0000 1111 转十六进制就是0f
二八十六进制转十进制:各位的2,8,16的N-1次方相加
十进制转二八十六进制:连接除2,8,16,余数倒过来
7. 取模运算
public class Test816 {
public static void main(String[] args) {
char hexDigits[] = { // 用来将字节转换成 16 进制表示的字符
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c',
'd', 'e', 'f' };
//取模运算 其实就是 n & (N-1)
byte a = 11 & 15;//取16的模(与上面的数组结合,即可转16进制)
a = 11 & 7;//取8的模
a = 11 & 9;//取10的模
a = 11 & 1;//取2的模(即可判断奇偶数)
//转16进制时,只要 7 0xf即可 0x开关的为16进制数
//其中的0xf 写成15也可以(只是一种表示形式)
char char16 = hexDigits[16 & 0xf];
System.out.println(char16);
//8进制数以0开头
char char8 = hexDigits[16 & 07];
System.out.println(char8);
}
}
第4章 控制执行流程
1. If else
2. While , do while , for
3. 无限循环
For(;;) while(true)
4. Continue:退回当前循环的开头,并继续执行
Continue带标签:退回到标签的位置,并继续执行
Break:中断并跳出当前循环
Break带标签:中断并跳出标签所指定的循环
outer:
for(int i=0;i<3;i++){
System.out.println("i:"+i);
inter:
for(int j=0;j<3;j++){
System.out.println("j:"+j);
continue outer;
}
break outer;
}
5. Switch 可以与enum一起使用
Switch只能与int char enum三种类型一起使用
int c=0;
switch (c) {
case 0:
break;
default:
break;
}
第5章 初始化和清理
1. 用构造器确保初始化
2. 以参数列表的不同来确保方法重载
3. 基本类型的方法重载
4. 基本类型的自动提升
public static void main(String[] args) {
get('a');
}
static void get(int a){
System.out.println(a);
}
5. This关键字
表示当前对象本身
有三种使用场景
5.1 构造方法中调用其它构造方法
5.2 参数相同时,使用this表示对象本身
5.3 将对象本身做为参数传递
public class Test {
String a ;
public Test(){
}
public Test(String a){
//this调用其它构造方法
this();
//参数名称相同时,使用this
this.a =a ;
}
void getA(){
//this 表示当前对象本身
new A().getA(this);
}
class A{
void getA(Test test){
}
}
public static void main(String[] args) {
get('a');
}
static void get(int a){
System.out.println(a);
}
}
6. Finalize方法
通用清理方法
7. 一个类的创建过程
New Dog();
即使没有的使用static关键字,构造器实际上也是静态方法
a. Java解释器查找类路径,定位dog.class文件
b. 类加载器加载dog.class ,此时有关静态初始化的所有动作都会被执行
c. 当用new 一个对象时,首先将在堆上为DOG对象分配足够的内存空间
d. 这块内在会被清零,所有基本类型设置成默认值(数字为0,布尔和字符也相同),引用置为null
e. 执行所有出现于字段(dog类的属性)定义处的初始化动作(private int a=1;private String a = new String(“aa”))
f. 执行构造器.(继承时会先执行父类构造器)
第6章 访问权限控制
Public:所有可见
Private:内部可见
默认:包内可见
Protected:继承可见
第7章 复用类
代码复用的两种方式:组合和继承
另外代理是组合和继承的中庸之道
组合和继承都允许在新的类中放置子对象,组合是显示的这样做,而继承是隐式的这样做.
组合技术通常用于想在新类中使用现有类的功能而非它的接口这种情形,即在新类中嵌入某个对象,让其实现所需要的功能,但新类的用户看到的只是为新类所定义的接口,而非所嵌入对象的接口
继承和组合能从现有类型生成新类型.组合一般是将现有类型作为新类型底层实现的一部分来加以复用.而继承用的是接口
上下转型问题
上转型为丢失信息,下转型可能出异常
Final关键字
Final类 不可继承,final方法 不可重写,final变量 不可改变引用
所有private方法都隐式的指定为final
第8章 多态
Java面向对象的程序设计的基本特征:封装,继承,多态
1. 多态概念
多态:指父类中定义的属性和方法被子类继承后,可以具有不同的数据类型或行为. 也就是同一个属性名或方法名在父类及其子类中具有不同语义
Java编程思想中:
多态通过分离做什么和怎么做,从另一角度将接口和实现分离开来
thinking in java 里多态主要还是指”向上转型“ 和 运行时的动态绑定
举例
多态是通过覆盖父类的方法来实现,在运行时根据传递的对象引用,来实现相应的方法。
public static void main(String[] args) {
A a = new B();
a.print();
}
static class A {
void print(){
System.out.println("this is A");
}
}
static class B extends A{
void print(){
System.out.println("this is B");
}
}
2. 忘记对象类型
所有的调用都是使用基类或接口
3. 方法调用绑定
前期绑定:static final方法是前期绑定的.因为static为静态方法,final不可继承
Private属于final
静态方法是与类,而并非与单个的对象相关联
后期绑定(动态绑定):运行时根据对象的类型进行绑定
方法声明为final:这样做可以有效的”关闭”动态绑定,或者说,告诉编译器不需要对其进行动态绑定
这样就可以保证代码的可扩展性
4. 构造器和多态
基类的构造器总是在子类的构造过程中被调用,而且按照继承层次逐渐向上链接,以使每个基类的构造器都能得到调用.
在一个组合,继承以及多态都存在的类的构造时
其它构造器调用顺序是:
第一步应该是:在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零
a. 调用基类构造器
b. 按声明顺序调用成员的初始化方法
c. 调用子类构造器,构造主体
例子见Sandwich
原因:必须令所有依赖的构造器都得到调用,才能正确的构造对象
5. 构造器内的多态方法的行为
例子见PolyConstructors
这个例子说明两个问题
5.1 构造器内也可以存在多态, 基类构造时,调用的是子类的实现
5.2 而此时子类未构造,分配给对象的存储空间初始化为二进制的零
编写构造器的有效准则:
用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调用其他方法
在构造器内唯一能够安全调用的那些方法是基类中的final方法(private也自动属于final)
6. 协变返回类型
在子类中的被重写的方法可以返回基类方法的返回类型的子类
7. 用继承进行设计
组合优于继承.
组合不会强制我们的程序设计进入继承的层次结构中.而且,组合更加灵活,因为它可以动态选择类型(因此也就选择了行为);相反,继承在编译时就需要知道确切类型
组合技术通常用于想在新类中使用现有类的功能而非它的接口这种情形
详见P165的例子
一条通用的准则:用继承表达行为间的差异,并用组合表达状态上的变化
8. 上下转型
上转型可能会丢失部分子类的信息,但上转型是安全的
下转型可以获得子类的具体信息,但要注意转型的安全性
第9章 接口
接口和内部类我们提供了一种将接口与实现分离的更加结构化的方法
接口与内部类共同实现了java的多重继承
1. Java 中的多重继承
接口是实现多重继承的途径
多重继承:子类可以实现多个接口 (这样就可以上转型为多个接口类型)
子类只能继承自一个父类,但可以实现多个接口
同时接口,可以继承自多个接口
class A{}
class B{}
class C extends A{}
interface D{}
interface E{}
interface F extends D,E{}
public class Test extends C implements D,E{}
使用接口的核心原因:
a. 为了能够向上转型为多个基类型(以及由此而带来的灵活性)
b. 与使用抽象类相同:防止客户端程序员创建该类的对象,并确保这仅仅是建立一个接口
注:如果知道某事物应该成为一个基类,那么第一选择应该是使它成为一个接口
2. 适配接口
接口最吸引人的原因之一,就是允许同一个接口具有多个不同的具体实现.
它的体现形式通常是一个接受接口类型的方法 ,而该接口的实现和向该方法传递的对象则取决于方法的使用者
典型应用就是策略模式
public Context(IStrategy strategy) {
this.straegy = strategy;
}
Scanner类的构造方法:参数也是使用接口
public Scanner(Readable source) {
this(source, WHITESPACE_PATTERN);
}
3. 接口中的域
接口中的域默认就是static final
JDK1.5以前其可以实现与enum类似的作用
接口可以嵌套在类或其它接口中
例如java.util.Map<K, V>中就嵌套了 interface Entry<K,V>
4. 接口与工厂
接口是实现多重继承的途径,而生成遵循某个接口的对象的典型方式就是工厂方法设计模式
通过这种方式,我们的代码将完全与接口的实现分离,这就使得我们可以透明的将某个实现替换为另一个实现
public interface Human {
public void laugh();
public void cry();
public void talk();
}
public static Human createHuman(Class c) {
Human human = null;
try {
human = (Human) Class.forName(c.getName()).newInstance();
} catch (ClassNotFoundException e) {
}
return human;
}
参见设计模式部分的代码
另外Spring的FactoryBean接口也是类似的处理
参见Spring部分的代码
public Object getObject() throws Exception {
TestFImpl testF = new TestFImpl();
if ("A".equals(type)) {
testF.setTestA(testA);
}
return testF;
}
总结:
确定接口是理想选择,因而应该总是选择接口而不是具体的类.—这是一种引诱
对于创建类,几乎在任何时刻,都可以替代为创建一个接口和一个工厂
恰当的原则:
优先选择类而不是接口. 从类开始,如果接口的必需性变得非常明确,那么就进行重构.
接口是一种 重要的工具,但是它容易被滥用.
第10章 内部类
可以将一个类的定义放在另一个类的定义内部,这就是内部类
1. 内部的创建及链接到外部类
第一个例子中 Sequence 说明了内部的使用,也用到了迭代器模式
由此也可以想到HashMap中Entry的关系
interface Selector {
boolean end();
Object current();
void next();
}
这里应该注意下,此接口的定义可以在任意位置
也可以看出, SequenceSelector 是针对Sequence类的一个迭代器实现
在SequenceSelector中可以直接访问Sequence中的items
在构造此内部类时,需要一个外部类的引用
/**
* 内部实现迭代模式,好处是此内部类可以直接访问Sequence的所有成员
*
* @author gao
*
*/
private class SequenceSelector implements Selector {
private int i = 0;
public boolean end() {
return i == items.length;
}
public Object current() {
return items[i];
}
public void next() {
if (i <= items.length) {
i++;
}
}
}
如果这个内部类要用一个外部类来实现的话,会相当麻烦
会涉及到几方面的问题
a. 该类的访问权限
b. 与sequence的数据同步问题(items)
c. 封装性问题
2. 使用.this 与.new
a. 在内部类中生成对外部类对象的引用
.this
private void inner(){
//在内部类的方法中生成外部类的引用
Sequence s = Sequence.this;
}
b. 在其它类中创建内部类对象
.new
要通过外部类的一个引用来创建
class A{
public static void main(String[] args) {
//其它类要访问内部类时
//要使用外部类的引用来创建内部类的引用
Sequence s = new Sequence(10);
Sequence.SequenceSelector selector = s.new SequenceSelector();
}
}
3. 在方法和作用域内的内部类
内部类的作用:
1.内部类可以很好的实现隐藏,也就是封装性。一般的非内部类,是不允许有 private 与protected权限的,但内部类可以
2.内部类拥有外围类的所有元素的访问权限
3.可是实现多重继承
4.可以避免修改接口而实现同一个类中两种同名方法的调用
在方法中也可以使用内部类,作用也可以同上理解,只是范围更加缩小到方法内部
4. 匿名内部类
匿名内部类更像是一个表达式
具体语法见后面,大概如下
new XXX(){}
它可以理解为两个过程
a. 继承或实现(new XXX() XXX可以表示类也可以是接口)
b. 实例化
class Wrapping{
private int i;
public Wrapping(int x){
i=x;
}
public int value(){
return i;
}
}
public class Inner {
public Wrapping getWrapping(final int x){
//new AE 的意义是实现AE接口
//{}内为实体
AE ae = new AE(){
public int value(){
return x;
}
};
//匿名内部类继承自Wrapping
//该匿名类的构造使用父类的Wrapping(x)
//该匿名类的实体为{}的内容
return new Wrapping(x){
public int value(){
int a = x;
//使用Wrapping.value() 再-a
return super.value()-a;
}
};//最后使用分号,它不是表示内部类的结束,而是表示表达式的结束
}
}
以下使用spring的事务代码
transactionTemplate.execute(new TransactionCallback() {
public Object doInTransaction(TransactionStatus status) {
return null;
}
});
5. 为什么需要内部类
一般说来,内部类继承自某个类或实现某个接口,内部类的代码操作创建它的外围类的对象,所以可以认为内部类提供了某种进入其外围类的窗口
内部类最吸引人的地方:
每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响
内部类使得多重继承的解决方案变得完整.接口解决了部分问题,而内部类有效的实现了”多重继承”(原本一个类只能继承自一个父类,但现在有了内部类,可以继承多个父类)
6. 内部类标识符
$
产生的.class文件名字
外部类$内部类
如果是匿名内部类,则会产生一个数字来表示
外部类$1
第11章 持有对象
1. 基本概念
Java 容器类类库的用途是保存对象
要深刻理解容器的理念
a. Collection
b. Map
要注意的是,接口没有数据结构,接口只定义了行为. 数据结构是对于实现类而言的
如list, 不能说list就是数组,因为linkedlist是链表
ArrayList允许你使用数字来查找值 数字-对象关联
Map 允许使用对象来查找对象 对象-对象关联(称为关联数组)
List<String> list = new ArrayList<String>();
在很多情况下,我们都会将对象上转型为接口
原因在于,如果你决定去修改你的实现,你所需要的只是在创建处修改.(改new处)
2. Arrays和Collections类的使用
3. 容器的打印
数组的打印:Arrays.toString()
其它容器使用toString方法
4. Collection 的接口
5. List
ArrayList:读快改慢
LinkedList:读慢改快
6. Set
HashSet使用的是散列
TreeSet使用的是红-黑树
LinkedHashSet使用的也是散列,但是使用了链表来维护插入顺序
7. Map
8. Queue 先进先出 LinkedList实现Queue接口,可以上转型为Queue,一个典型的接口多重继承例子
因为Queue继承了Collection接口,所以它也可以add.
public boolean offer(E e) {
return add(e);
}
Queue<String> queue = new LinkedList<String>();
queue.offer("1");//优先使用
queue.add("2");
System.out.println(queue);
System.out.println(queue.poll());
System.out.println(queue);
方法摘要
E
element()
检索,但是不移除此队列的头。
boolean
offer(E o)
如果可能,将指定的元素插入此队列。
E
peek()
检索,但是不移除此队列的头,如果此队列为空,则返回 null。
E
poll()
检索并移除此队列的头,如果此队列为空,则返回 null。
E
remove()
检索并移除此队列的头。
从LinkedList的实现来看下peek和poll
Peek使用了getFirst,Poll使用了removeFirst
public E peek() {
if (size==0)
return null;
return getFirst();
}
public E poll() {
if (size==0)
return null;
return removeFirst();
}
9. 迭代器实现
public interface Collection<E> extends Iterable<E>
实现Iterable接口,就可以使用iterator()方法
10. 适配器惯用法
当你有一个接口并需要另一个接口时,编写适配器就可以解决问题
总结
a. 数据将数字与对象联系起来
b. Collection保存单一的元素,而Map保存相关联的键值对.
c. 像数组一样,List也建立数字索引与对象的关联,因此,数组和List都是排好序的容器
d. 如果要进行大量的随机访问,就使用ArrayList,如果要经常从表中插入或删除元素,则应该使用LinkedList
e. 各种queue以及栈的行为,由LinkedList提供支持
f. Map是一种将对象与对象相关联的设计.HashMap设计用来快速访问,而TreeMap保持键始终处于排序状态,所以没有HashMap快.LinkedHashMap保持元素插入的顺序,但是也通过散列提供了快速访问的能力
g. Set 不接受重复元素。HashMap提供最快的查询速度,而TreeSet保持元素处于排序状态,LinkedHashSet以插入顺序保存元素
h. 新程序中不应该使用过时的Vector,Hashtablet和Stack
P246的集合简图,可以看一下
第12章 通过异常处理错误
Java 的基本理念是: 结构不佳的代码不能运行
1. 异常机制使代码的阅读,编写和调试工作更加井井有条
目的:将正常执行过程和异常处理分开
异常处理的重要原则:
只有在你知道如何处理的情况下才捕获异常
2. 基本异常
当错误发生时,由异常处理机制接管程序,并寻找一个恰当的地方继续执行程序. 这个恰当的地方就是异常处理程序
它的任务是将程序从错误状态中恢复,以使程序能要么换一种方式运行,要么继续运行下去
异常类型的根类是Throwable,以下为Throwable的构造方法
3. 异常捕获
监制区域:try块后面跟的就是监控区域
异常处理程序:
终止模式与恢复模式:java使用 的是终止模式
System.err 标准错误流 log就是将错误信息输出到标准错误流
提取异常信息的方法
a. 可以通过getStackTrace查看栈轨迹
b. 重新抛出异常
Throw e; 它不会记录新异常的抛出地点.只是将原先的异常原样抛出
要想把目前的异常地点也记录下来,可以调用 fillInStackTrace ()
在Throwable的所有构造器都有调用fillInStackTrace,所以说,每次new一个异常,都会被记录到StackTrace中.
public Throwable() {
fillInStackTrace();
}
所以,如果使
展开阅读全文