《Java编程思想》阅读笔记一

Java编程思想

这是一个通过对《Java编程思想》(Think in java)第四版进行阅读同时对java内容查漏补缺的系列。一些基础的知识不会被罗列出来,这里只会列出一些程序员经常会忽略或者混淆的知识点。

所列知识点全部都是针对自己个人而言,同时也欢迎大家进行补充。


第一章(对象导论)

public class HelloWorld {
    public static void main(String[] args) {
		System.out.println("Hello every one,I'm cpacm");
	}
}

这一章是java的整体介绍,让我们先熟悉了java是什么。其具体的内容都会在后面介绍。

面向基本语言的五大特性

  • 万物皆为对象。
  • 程序是对象的集合,它们通过发送消息来告知彼此所要做的。
  • 每个对象都拥有其类型。
  • 某一特定类型的所有对象都可以接收同样的消息。

第二章(一切都是对象)

一、存储

p22
栈(堆栈):存放基本类型变量和对象引用变量。位于RAM区
堆:存放new得到的对象和数组。也位于RAM区
常量存储:存放常量,包括静态变量。

二、基本类型

p26
基本数据类型在没有初始化的时候会获得一个默认值。

基本数据类型 默认值 大小
boolean false 未确定
char null 16bits
byte 0 8bits
short 0 16bits
int 0 32bits
float 0f 32bits
long 0L 64bits
double 0d 64bits

tip1:String不是基本数据类型
tip2:上面的初始默认值并不适用于方法内部变量。其默认初始化值未知。当使用未初始化的变量编译器会返回错误

public class BaseType {
	static boolean b;
	static char c;
	static byte bt;
	static short s;
	static int i;
	static float f;
	static long l;
	static double d;
	
	public static void main(String[] args) {
		System.out.println("类变量——"+"boolean:"+b+" char:"+c+" byte:"+bt+" short:"+s+" int:"+i+" float:"+f+" long:"+l+" double:"+d);
	}
	
}
//类变量——boolean:false char:  byte:0 short:0 int:0 float:0.0 long:0 double:0.0

第三章(操作符)

一、别名现象

p40
当两个变量包含的是同一个引用时,修改其中一个变量的值另一个变量的值也同时改变。记住new出来的对象的=赋值都是只传递引用。
Tip:减少为对象赋值。

class T{
	int i;
}

public class Assigment {

	public static void main(String[] args) {
		T t1 = new T();
		T t2 = new T();
		t1.i = 5;
		t2.i = 8;
		t1 = t2;
		t1.i = 10000;
		System.out.println(t2.i);
	}

}
//10000

二、equals方法

p45
在自定义的对象中使用equals方法时需要覆盖此方法,否则默认是比较引用

三、短路

p47
其现象本质为:当已经确定一个逻辑表达式的结果时不会再计算剩余的部分。

public class ShortCircuit {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		boolean flag1 = test1()&&test2()||test3()&&test4();
		System.out.println("\n");
		boolean flag2 = test1()&&test3()||test2()&&test4();
	}
	
	static boolean test1(){
		System.out.println("test1");
		return true;
	}
	static boolean test2(){
		System.out.println("test2");
		return false;
	}
	static boolean test3(){
		System.out.println("test3");
		return true;
	}
	static boolean test4(){
		System.out.println("test4");
		return false;
	}

}
/*
test1
test2
test3
test4

test1
test3
*/

boolean flag2 = test1()&&test3()||test2()&&test4();
若test1为true,test3为true时,因为前面这部分已经确定为true,所以后面部分不会被调用。

四、e

p49
科学与工程领域中,"e"代表自然对数的基数,为2.718。
而在C,C++和java(或者更多的语言)中,"e"代表“10的幂次”
$1.39e-43f = 1.39*10^{-43}$

五、位操作

p49
与,或,异或,非 &,|,^,~
与:所有的位都为1则输出1,否则输出0;
或:只要有一个位是1就输出1;
异或:两个位值相等时输出1;
非:1输出0,0输出1.
位运算不会出现短路现象。
p50
移位操作符:
$<<$:操作数向左移动,低位补0;
$>>$:操作数向右移动,(1)符号为正时,高位补0,(2)符号为负时,高位补1;
$>>>$:java独有操作符,操作数向右移动,高位统一补0。
char,byte,short进行移位操作时先会转成int类型,即32位

public class URShift {

	public static void main(String[] args) {
		int i = 1024;
		System.out.println(Integer.toBinaryString(i));
		i >>= 10;
		System.out.println(Integer.toBinaryString(i));
		i = -1;
		System.out.println(Integer.toBinaryString(i));
		i >>>= 10;
		System.out.println(Integer.toBinaryString(i));
		i <<= 1;
		System.out.println(Integer.toBinaryString(i));
		short s = -1;
		s >>>= 10;//s移位后得到的结果在赋值时会强行转为int,所以移位后的s已经是int型
		System.out.println(Integer.toBinaryString(s));
		s = -1;
		System.out.println(Integer.toBinaryString(s>>>10));
	}

}
/*
10000000000
1
11111111111111111111111111111111
1111111111111111111111
11111111111111111111110
11111111111111111111111111111111
1111111111111111111111
*/

六、截尾

p55
将float或double转型为整数值时,总是对数字进行截尾,不会进行四舍五入。如果想要得到舍入的结果可以使用Math.round()

public class CastingNumbers {
	
	public static void main(String[] args) {
		double d = 1.7d;
		int i = (int)d;
		System.out.println(i);
		i = (int) Math.round(d);
		System.out.println(i);
	}
}
//1
//2

第四章(控制执行流程)

一、goto 标签

goto关键词,在java中则是使用标签代替臭名昭著的goto。其实在java中也是最好不要用标签来跳转语句,太伤智商。。
写法
outer:
并放在迭代语句前。
(1)continue 标签
跳出所有循环,并到标签位置的语句,但会重新进入紧接后面的循环里。
(2)break 标签
跳出所有循环,且不再进入紧接后面的循环里。

public class LabeledFor {

	public static void main(String[] args) {
		System.out.println("测试continue————————————");
		label:
		for(int i=0;i<3;i++){
			System.out.println("外部for循环"+i);
			for(int j=0;j<3;j++){
				if(j==1){
					continue label;
				}
				System.out.println("内部循环"+j);
			}
		}
		System.out.println("测试break————————————————");
	    label2:
		for(int m=0;m<3;m++){
			System.out.println("外部for循环"+m);
			for(int n=0;n<3;n++){
				if(n==1){
					break label2;
				}
				System.out.println("内部循环"+n);
			}
		}
	}

}
/*
 测试continue————————————
外部for循环0
内部循环0
外部for循环1
内部循环0
外部for循环2
内部循环0
测试break————————————————
外部for循环0
内部循环0*/

第五章(初始化与清理)

一、重载

p81
重载主要以传入参数及顺序来区别。不能通过返回值来区别
以基本数据类型传入时:自动包装机制
(1)若传入的数据类型小于方法中声明的形式参数类型,实际数据类型就会提升。
byte->short->int->long->float->double
(2)如果传入的实际参数较大,就得通过类型转换执行窄化转换。

二、垃圾回收机制

p91
标记-清扫(Android中使用这个技术)
从堆栈和存储区出发,遍历所有的引用,进而找出所有存活的对象,并给与标记。遍历完成后,清理所有未被标记的对象。
停止-复制
先暂停程序的运行,然后将所有存活的对象复制到另一个堆,而没有复制的是可回收的内存。

三、初始化顺序

p94
所有的变量都会在任何方法(包括构造器)被调用之前得到初始化。
p95
无论创建多少对象,静态数据都只占用一份存储区域,static不能作用于局部变量。且静态初始化动作只执行一次。

四、可变参数列表。

p102
void method(Object... args){}
调用 method();或method(new Object[]{1,2,3,4});

第七章(复用类)

一、toString()的自动调用

p126
有时候编译器会自动帮你调用toString()方法。
"source"+ source;这时候会自动调用source对象的toString方法。

二、构造函数调用顺序

p130
构造函数总是从基类开始。

class Insert{
	Insert(){
		System.out.println("Insert");
	}
	
}

public class Beetle extends Insert{
	
	Beetle(){
		System.out.println("Beetle");
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Beetle b = new Beetle();
	}

}
//Insert
//Beetle

三、final

p140
对于基本类型,final使数值恒定不变;而用于对象引用,final使引用恒定不变,但对象本身是可以改变的。
private 属于 final 方法
static final是属于类属性,即能被类调用,不用实例化
final则需要实例化。

四、组合模式

组合模式可以看做是一颗树,每个枝干都可以长出新的枝干,它们的结构都是相同的。
将枝干抽象为一个类,里面包含链接下一个节点的方法,若需要不断的链接下一个节点只需要继承这个方法并实现。
示例:导航菜单
组合模式,将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性。

public abstract class Tab {
	private String title;

	public Tab(String title) {
		this.title = title;
	}
	
	protected abstract void add(Tab tab);  
	  
    protected abstract void romove(Tab tab);  
}

public class CardTab extends Tab{
	
	public CardTab(String title) {
		super(title);
		// TODO Auto-generated constructor stub
	}

	private List<Tab> tabs;

	@Override
	protected void add(Tab tab) {
		// TODO Auto-generated method stub
		tabs.add(tab);
	}

	@Override
	protected void romove(Tab tab) {
		// TODO Auto-generated method stub
		tabs.remove(tab);
	}

}

public class TabView {
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		CardTab rootTab = new CardTab("roottab");
		CardTab tab1 = new CardTab("tab1");
		CardTab tab2 = new CardTab("tab2");
		CardTab tab3 = new CardTab("tab3");
		rootTab.add(tab1);
		rootTab.add(tab2);
		rootTab.add(tab3);
		CardTab tab4 = new CardTab("tab1-1");
		CardTab tab5 = new CardTab("tab1-2");
		tab1.add(tab4);
		tab1.add(tab5);
	}
}

/**
 * 这样子Tab组成了一个导航列表,这就是一个简单的组合模式.
 */

第八章(多态)

多态是一项让程序员“将改变的事物与未变的事物分离开来”的重要技术。

一、多态缺陷

p156
缺陷1:只有非private方法才可以被覆盖

class Super{
    public int field = 0;
    public int getField(){return field;};
}

class Sub extends Super {
      public int field = 1;
      public int getField() { return field; }
      public int getSuperField() { return super.field; }
    }

public class FiledAccess {
      public static void main(String[] args) {
        Super sup = new Sub(); // Upcast
        System.out.println("sup.field = " + sup.field +
          ", sup.getField() = " + sup.getField());
        Sub sub = new Sub();
        System.out.println("sub.field = " +
          sub.field + ", sub.getField() = " +
          sub.getField() +
          ", sub.getSuperField() = " +
          sub.getSuperField());
      }
} 

输出:
sup.field = 0, sup.getField() = 1
sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0

缺陷2:域和静态方法直接在编译时候进行解析,所以多态不会对其产生作用。

class StaticSuper {
  public static String staticGet() {
    return "Base staticGet()";
  }
  public String dynamicGet() {
    return "Base dynamicGet()";
  }
}

class StaticSub extends StaticSuper {
  public static String staticGet() {
    return "Derived staticGet()";
  }
  public String dynamicGet() {
    return "Derived dynamicGet()";
  }
}

public class StaticPolymorphism {
  public static void main(String[] args) {
    StaticSuper sup = new StaticSub(); // Upcast
    System.out.println(sup.staticGet());
    System.out.println(sup.dynamicGet());
  }
} /* Output:
Base staticGet()
Derived dynamicGet() */

二、断言

p153
@Override作用:
断言,如果我们使用了这种annotation在一个没有覆盖父类方法的方法时,java编译器将以一个编译错误来警示

三、构造器构造顺序

p158
构造器在多态时的构造顺序:

class Meal {
    Meal() {
        P.print("Meal()");
    }
}

class Bread {
    Bread() {
        P.print("Bread()");
    }
}

class Cheese {
    Cheese() {
        P.print("Cheese()");
    }
}

class Lettuce {
    Lettuce() {
        P.print("Lettuce()");
    }
}

class Lunch extends Meal {
    Lunch() {
        P.print("Lunch()");
    }
}

class PortableLunch extends Lunch {
    PortableLunch() {
        P.print("PortableLunch()");
    }
}

public class Sandwich extends PortableLunch {
    private Bread b = new Bread();
    private Cheese c = new Cheese();
    private Lettuce l = new Lettuce();

    public Sandwich() {
        P.print("Sandwich()");
    }

    public static void main(String[] args) {
        new Sandwich();
    }

}

class P {
    public static void print(String s){
        System.out.println(s);
    }
}
输出:
Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()

解释说明:
一个继承类实例化的时候必须要确保所使用的成员已经构建完毕,所以必须先调用基类的构造器,所以当实例化Sandwich对象时先调用其基类的构造方法:
Meal()
Lunch()
PortableLunch()
其次对成员变量进行初始化
Bread()
Cheese()
Lettuce()
最后调用构造器
Sandwich()

三、构造器初始化

p163
初始化的过程:
(1)在所有事物发生之前,将分配给对象的存储空间初始化为二进制的零。
(2)调用基类构造器。
(3)按照声明顺序调用成员的初始化方法。
(4)调用导出类(本体)的构造器主体。

posted @ 2016-06-07 21:49  cpacm  阅读(8495)  评论(0编辑  收藏  举报