**”Thinking in java” 被誉为Java界的圣经,做为进阶是一个不错的选择,暑假在家闲着无聊,看着打发打发时间,把一些自己认为不错的点做了一些笔记。文字部分是我的见解,代码大部分取自原书。顺便我想给三个看Tij的建议:
1).看英文原著!中文版的翻译并不是原汁原味的翻译,而是意译,为了中文句式的流畅,有些意思会被篡改。
2).不要抱着想一次读完的心态!一本好书,是经得起时间的考验,必定要反复啃,温故而知新。
3).学习就像吃饭,书是香喷喷的米饭,视频是脍炙人口的菜,优酷上有一套斯坦福大学的《编程方法》公开课,刚好是选择以java为讲解语言的,可以跟看书同步:http://list.youku.com/albumlist/show/id_17643804.html?sf=10100&spm=0.0.0.0.bL8hk1**
下面我们开启Thinking之旅:
1.什么是面向对象编程?
1.)我们所处的世界是由对象构成的,java的设计的本质也是基于世界的!在理解什么是面向对象要先清楚“自顶而下”的设计思想,就是把大的方面细分为小的方面,直到原子性,比如,早上洗漱,细分为洗脸,刷牙,刷牙细分为挤牙膏,牙刷刷动牙齿…
2.)为什么是面向对象?举个例子:人类的上层是灵长类,灵长类的上层是哺乳动物,哺乳动物的上层是动物,那么人类、灵长类、哺乳动物、动物就称为类,我/你/他是人类的一个实例,称为对象,那么推广出去,java是面向对象的语言,不就反映出世界的物质性吗。
3.)对象的作用是接收发送过来的指令,做出回应。每个对象之间相互提供服务。对象向对象发送信息。好比生活中的例子:我把CD插入CD播放器,播放音乐。我、CD、CD播放器、音乐均是对象。
2面向对象特性?
抽象、继承、多态、封装。
本质是实现代码高内聚,低耦合,增强代码复用性。
3.String s = new String(“string s”);包含了什么?
里面的s代表一个引用,存在栈中,引用的名字 s 存在变量区,new 把 s 引用与String关联,String是一个类,存在堆。
4.Array 细节
1.)Array在创建时它的每个元素默认为null。
2.)如果:
List list=null;
list=…
会报空指针异常。
5.Java命名规则
类名的每个单词首字母大写
变量名和方法名的第一个单词的首字母小写,其余的首字母大写
6.注释
具体看下面的hello world 例子:
//:object/HelloDate.java
import java.util.*;
/**The first Thinking in Java example program.
*Displays a String and today's date
*@author wayne
*@version 4.0
*/
public class HelloDate{
/**Entry point to class & application.
*@param args array of string arguments
*@throws exceptions No exception thrown
*/
public static void main(String[] args){
System.out.println("Hello World,it's:");
System.out.println(new Date());
}
}/*Output:(55% match)
Hello World. it's:
Web July 14 17:42:36 MCT 2016
*///~
7.equals()方法陷阱
看例子:
public class EqualsMethod{
public static void main(String[] args){
Integer n1=new Integer(47);
Integer n2=new Integer(47);
System.out.println(n1.equals(n2));
System.out.println("***********");
Value v1=new Value();
Value v2=new Value();
v1.i=v2.i=100;
System.out.println(v1.equals(v2));
}
}
class Value{
int i;
}/*Output
true
**********
false
*/
很奇怪为什么一个是true,一个是false?
equals()是object的方法,用来比较对象的地址。所有类都是object的子类,Integer同样也是object的子类,也包含有equals()方法,但是Integer重写了equals()方法,用来比较两个Integer对象的数值。上面的例子中,Integer.equals()比较两个47的值相等而返回true,而Value类并没有重写equals()方法,比较的是new出来的对象的地址,两个对象的地址不同而返回false。
8.Java没有sizeof()函数
C语言中,经常用sizeof()在来定义数据类型的长度。操作系统是16/32/64…位的时候,不同数据类型的大小不一样。但是Java语言在设计时,不要sizeof(),所以在每个机器上各种数据类型的大小是固定的,使得java可以在各个平台来去自如,这就是Java的可移植性,所谓的跨平台。
9.Java的变量可以边用边定义
for(int i=0;i<10;i++)
而C不可以,i的作用域(生命周期)只在当前for循环中。
10goto做为Java的保留字,但是并不使用
在Java中,如果有嵌套的循环语句,当需要从某个循环跳出到指定的循环时,可以用label,定义一个label,然后再加在break或continue后面,看例子:
public class LabeledFor{
public static void main(String[] args){
int i=0;
outer:
for(;true;){
inner:
for(;i<10;i++){
System.out.println("i="+i);
if(i==2){
System.out.println("continue");
continue;
}
if(i==3){
System.out.println("break");
i++;
break;
}
if(i==7){
System.out.println("continue outer");
i++;
continue outer;
}
if(i==8){
System.out.println("break outer");
break outer;
}
for(int k=0;k<5;k++){
if(k==3){
System.out.println("continue inner");
continue inner;
}
}
}
}
}
}/*Output
i=0
continue inner
i=1
continue inner
i=2
continue
i=3
break
i=4
continue inner
i=5
continue inner
i=6
continue inner
i=7
continue outer
i=8
break outer
*///~
11.overloading(重载)为什么要使用传入的参数区分?
在开发中我们定义对象名称和方法时, 一般采取语义法,但是为了拓展方法,会多出几个方法,又不想改动通俗易懂的方法名,于是采用不同参数区分开来。那位什么不通过返回值呢?因为当你只声明而不接收方法的返回值时,java编译器无法区分哪个方法被调用。
12.初始化一个对象的顺序
先初始化变量,再初始化方法。
13.方法的权限控制
public、package-access、private、protected
public:Everything naked to the world.所有对象都可以访问
package-access:顾名思义,同一个包内的可以访问(包:存放一堆类的namespace)
private:除了对象本身,其他对象均不能访问。主要用于解耦。
protected:住要用于修饰父类的方法。被修饰的方法可以被继承该对象的子类访问,其他对象不能访问
14.类的权限
类的权限之有两个:public、package-access。用法与方法一致。当public修饰一个类时,代表该类的所有内容都是public。另外,在一个java文件中,必须要有一个public的类。
15.is-a(继承/inheritance) vs has-a(合成/composition)
合成就是在一个类中整合进其他类构成一个新的类。它的灵活性,适应性比继承更好,但是每个类之间的变量和方法必须是public,封装性不如继承。
Bruce建议只有在确定能用继承是才用继承。实际编程中是继承和合成混用的。
16.final关键字修饰方法的作用
1.防止方法被重写
2.优化程序:一般的程序是通过压栈和出栈实现的,被final修饰的方法不会进栈,当程序运行到调用被final修饰方法的语句时,编译器直接把被final修饰的方法的代码拼接到语句后面,直接编译,提高速度。
17.构造器的调用顺序
1.从继承的根部开始调用构造方法
2.根据声明的顺序初始化变量
3.子类构造器的内部开始执行
看例子:
class Meal{
Meal(){
System.out.println("Meal()");
}
}
class Bread{
Bread(){
System.out.println("Bread()");
}
}
class Cheese{
Cheese(){
System.out.println("Cheese()");
}
}
class Lettuce{
Lettuce(){
System.out.println("Lettuce()");
}
}
class Lunch extends Meal{
Lunch(){
System.out.println("Lunch()");
}
}
class PortableLunch extends Lunch{
PortableLunch(){
System.out.println("PortableLunch()");
}
}
public class Sandwich extends PortableLunch{
private Bread b=new Bread();
private Cheese c=new Cheese();
private Lettuce l=new Lettuce();
public Sandwich(){
System.out.println("Sandwich()");
}
public static void main(String[] args){
new Sandwich();
}
}/*Output
Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()
*///~
18.继承和清除对象的顺序
从17的例子可以看出在继承关系中,方法的调用是从根部,最先的父类开始逐级调用。如果,想在对象被使用后回收该对象,释放内存空间。那么,调用回收函数的顺序是与继承相反的,为的是防止父类的方法被子类调用而回收父类的情况。
19.private 和 final
private默认是final。在使用多态时,如果想在构造器中调用方法,尽量把方法写在父类的构造器里。而且方法最好不要被重写,以免造成一些不必要的难以察觉的惊喜。
20.接口与抽象类的作用
在一般的继承关系中,父类的方法往往不会用到,而是使用子类重写的方法。父类的方法只是声明,所以接口和抽象类出现了。但是接口比抽象类更抽象。
抽象类不可被实例化,一般用来给子类继承;
接口最主要还是用来解耦合,接口允许被多个类实例化,增强代码的复用性,主要体现在适配器模式中。接口声明的方法默认为public,变量默认为static final
21.接口的“工厂”用法
直接看例子:
interface Game{
boolean move();
}
interface GameFactory{
Game getGame();
}
class Checkers implements Game{
private int moves = 0;
private static final int MOVES = 3;
public boolean move(){
System.out.println("Checkers move "+moves);
return ++moves != MOVES;
}
}
class CheckersFactory implements GameFactory{
public Game getGame(){
return new Checkers();
}
}
class Chess implements Game{
private int moves = 0;
private static final int MOVES = 4;
public boolean move(){
System.out.println("Chess move "+moves);
return ++moves!=MOVES;
}
}
class ChessFactory implements GameFactory{
public Game getGame(){
return new Chess();
}
}
public class Games{
public static void playGame(GameFactory factory){
Game s= factory.getGame();
while(s.move()){
;
}
}
public static void main(String[] args){
playGame(new CheckersFactory());
playGame(new ChessFactory());
}
}/*Output
Checkers move 0
Checkers move 1
Checkers move 2
Chess move 0
Chess move 1
Chess move 2
Chess move 3
*///~
22.内部类的作用
内部类的设计初衷是解决接口的实现类的拓展缺陷。
内部类与外围类一起使用,外围类不管继承/实现什么接口,内部类也可以实现什么接口,独立于外围类。但是访问时要先访问外围类(除非内部类被static final修饰)。具体在程序中的作用可以参考 “Thinking in java” 原著第四版,P376~P380的例子。
内部类也称为闭包。
23.Local Inner Class vs anonymous Inner Class(匿名内部类)
先看一个例子:
interface Counter{
int next();
}
public class LocalInnerClass{
private int count =0;
Counter getCounter(final String name){
class LocalCounter implements Counter{
public LocalCounter(){
System.out.println("LocalCounter()");
}
public int next(){
System.out.println(name);
return count++;
}
}
return new LocalCounter();
}
Counter getCounter2(final String name){
return new Counter(){
{
System.out.println("Counter()");
}
public int next(){
System.out.println(name);
return count++;
}
};
}
public static void main(String[] args){
LocalInnerClass lic=new LocalInnerClass();
Counter
c1=lic.getCounter("Local inner"),
c2=lic.getCounter2("Anonymous inner");
for(int i=0;i<5;i++){
System.out.println(c1.next());
}
for(int i=0;i<5;i++){
System.out.println(c2.next());
}
}
}/*Output
LocalCounter()
Counter()
Local inner
0
Local inner
1
Local inner
2
Local inner
3
Local inner
4
Anonymous inner
5
Anonymous inner
6
Anonymous inner
7
Anonymous inner
8
…
1.如果需要自己定义内部类的构造器或重写内部类的构造器,还是用local inner class
2.如果想为一个内部类创建多个对象,也还是使用 local inner class
24.集合的关系
collection 和 map 是最上层的类,其中:List,Set,Queue继承collection。List又衍生出ArrayList 和 LinkedList,Set衍生出HashSet 和 TreeSet,HashSet又衍生出LinkedHashSet,Queue 衍生出 PriorityQueue。HashMap ,TreeMap 继承Map ,HashMap又衍生出LinkedHashMap。
1.Map的功能强于Collection
2.List中元素是有顺序的
3.ArrayList适用于大规模查找,LinkedList适用于大规模插入和删除元素
4.TreeMap的key值是经过排序的,所以速度不如HashMap快
5.Set的元素不能重复,LinkedHashSet的元素的顺序是跟插入顺序一样的
25.stack 和 queue
这两个集合用的比较少几乎没用了,但是原理跟数据结构是一样的。stack是先入后出,queue是先入先出:
Stack:
import java.util.*;
public class StackTest{
public static void main(String[] args){
Stack<String> stack=new Stack<String>();
for(String s:"My dog has fleas".split(" ")){
stack.push(s);
}
while(!stack.empty()){
System.out.print(stack.pop()+" ");
}
}
}/*Output
fleas has dog My
*///~
Queue:
import java.util.*;
public class QueueDemo{
public static void printQ(Queue queue){
while(queue.peek()!=null){
System.out.print(queue.remove()+" ");
}
System.out.println();
}
public static void main(String[] args){
Queue<Integer> queue=new LinkedList<Integer>();
Random rand=new Random(47);
for(int i=0;i<10;i++){
queue.offer(rand.nextInt(i+10));
}
printQ(queue);
Queue<Character> qc=new LinkedList<Character>();
for(char c: "Brontosaurus".toCharArray()){
qc.offer(c);
}
printQ(qc);
}
}/*Output
8 1 1 1 5 14 3 1 0 1
B r o n t o s a u r u s
*///~