`

《细说Java》读书笔记

阅读更多

国人写的, 感觉是一个Java编程注意事项的汇编(涉及到JDK5以后的特性比较少), 适合Java入门不久, 对一些细节了解不深入的人看, 当然老鸟也可以翻翻用来查缺补漏, 巩固所学所用. 如果高阶的可以看看<effictive java>

===============我是读书笔记的分割线===================
如果操作数是byte, char或者short类型(或者为三者的混合运算)时, 结果为int

负号"-"也是一个运算符, 因此假设存在一操作数x,x可以是byte, char, short, 那么-x就不再是以前的x类型, 而是int

对于编译时的整数常量, 如果将其赋值给比int类型低的类型(byte, short, char), 而此常量的值又没有超过该变量类型的取值范围, 那么编译器就可以做隐式类型转换(比如: short s = 20), 但是这里仅限于整型常量赋值, 而对于方法调用并不适用.
比如:
private void invoke(){call(5);}
private void call(byte b){...}
private void call(long l){...}
这里常数5既可以转换为long类型, 也可以转换byte, 两种转换都可以隐式进行, 这样系统将无法选择.

在表达式中, 几个操作数都是int类型的常量, 而若干个int类型的计算结果即使超出了其所有能表示的数值范围, 也不会自动进行向更高的数据类型转化(如自动转换为long), 即结果仍然为int类型, 最终导致溢出. 如long l=70*60*24*365; 的结果为-1718527296, 正确的写法: long l = 70L*60*24*365

在使用浮点数时, 不要在数量级相差非常大的数之间进行加减运算, 最好的做法是不使用浮点数进行计算.

进行移位运算, 左操作数是int类型时, 右侧的操作数只有低于5位是有效的(低于5位的十进制最大值为31), 因为int类型只有32位, 这样就可以避免将数据全部移除而失去意义. 同样对于左侧操作数为long类型的移位运算, 右侧最大数必须低于6位.如:
27<<40
首先取40的补码(低5位):
101000
取低5位:
01000
这就是十进制的8, 因此相当于:
27<<8

在异或运算中, 对于两个变量x,y, 有这样一个性质:x^y^y=x, 利用异或这个性质可以进行简单的加密功能.

由于局部变量是在栈上分配的, 而类的实例变量是在堆上分配的, 而对局部变量的访问速度要快于对于堆中变量的访问, 因此当在循环中多次访问某个实例变量时, 可以先将实例变量赋值给一局部变量, 然后对局部变量进行访问.

子类可以继承父类的成员, 但是却不能继承父类的构造函数, 当子类被实例化时, 在子类构造函数中会自动地调用父类的构造函数, 如果不显示地调用, 系统会自动调用父类无参的构造函数. 在子类构造函数中, super语句必须是第一条语句, 因为构造器是用来对类的成员进行初始化, 而子类的某些成员可能要依赖父类的成员来初始化, 如果父类的初始化不能正常进行, 就没法保证子类能够正确的初始化. 如:
class Ball{public Ball(int radius){...}}
class Football extends Ball{}
这里编译无法通过, 因为子类Football没有声明构造函数, 则系统会自动创建一个默认构造器, 而该构造器会自动调用父类无参构造器
public Football(){super();}
而父类中根本没有无参构造函数, 所以出错.

如果只是声明了类的引用, JVM是不会载入类的. JVM只有在需要某个类时才载入该类, 这种需要可以是使用该类的静态成员变量, 调用该类的静态方法, 或者生成了该类的实例, 同时如果加载了子类, 父类也会加载.

如果有可能尽量不要使用成员变量而使用局部变量, 因为成员变量要等对象销毁时才会随之销毁, 如果有可能使用参数传递共享数据, 这样有利于垃圾的清理, 使内存空间能及时释放.

垃圾收集只会回收对象所占的内存, 也就是使用new在堆上创建的对象, 而对于本地方法来说, 方法的实现方式是平台相关的, 其内部并非使用java代码, 而是类似于c等其他语言, 如果在本地方法内申请并占用某些资源, 那么垃圾收集器将无可奈何. 例如在程序中操作文件, 如果要显示的回收这种资源, 需要调用IO类的close方法来清理本地方法占用的资源.

对于基本类型的包装类来说, 其对象是不可变, 对于某些频繁使用的值, 系统提供了包装类的缓冲(即预先创建了经常使用的包装类对象, 可以参考包装类的valueOf方法), 其值包括:
boolean的true和false
byte的-128~127
char的0~127
short的-128~127
int的-128~127
long的-128~127
对于float和double的包装类没有缓存

对于instanceof操作符来说, 如果左侧是null, 那么不管右侧是什么类型, 一律返回false, 因为null不是任何类型的对象.

因为数组类型也是类型, instanceof右侧的操作数类型也可以数组类型. 如:
Type[] type = new Type[3];
type instanceof Type[]

重载方法的调用是在编译时确定的, 与引用所指向的具体对象类型无关, 即重载方法关心的是引用的具体类型, 而不是对象的具体类型.如:
print(Cat cat){print("cat");}
print(Dog dog){print("dog");}
print(Animal animal){print("animal");}
void main(){
	Cat cat = new Cat();
	print(cat);
	Dog dog = new Dog();
	print(dog);
	Animal animal = cat;
	print(animal); // 输出是"animal", 而不是cat
}


如果子类重写了父类的方法, 则通过父类的引用调用的是子类的方法. 如果子类隐藏了父类的方法(静态方法的覆盖), 则通过父类的引用调用的仍然是父类的方法. 实例方法的调用是动态绑定的, 运行时根据对象真正的类型来决定调用哪个方法, 而静态方法的调用是静态绑定的, 是根据引用的类型来决定调用哪个方法.

子类不能重写父类中的成员变量, 只能隐藏父类中的成员变量(不论是静态的还是实例的)

接口中的成员变量默认是public static final, 即接口的所有成员都是public.

由于匿名类没有名字, 所以匿名类无法声明构造器, 如果需要对匿名类的成员进行初始化, 可以使用初始化块的方式.如:
interface Center{}
class Circle{public Center getCenter(int x, int y){return new Center(){
		int int i;
		int j; 
			{
				i = x; 
				j =y;
			}
		}
	}
}


要区分包装类的Class对象与其对应的基本类型的Class独享, 二者并不相等:
Integer.TYPE == int.class
Integer.class != Integer.TYPE

循环的优化: 次数少的放外层, 多的放内层
int v1 = 10000;
int v2 = 1000;
int v3 = 10;
for(int i = 0; i < v3; i++){
	for(int j = 0; j < v2; j++){
		for(int k = 0; k < v3; k ++){
			...
		}
	}
}
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics