整理自《Java开发之道》明日科技、张振坤、李钟尉、陈丹丹等编著,电子工业出版社出版
1. for循环中删除数组的所有元素
for(int i=0;i<l.size();i++){
l.remove(i);
}
在这个循环中,由于每次循环都删掉了数组中的元素,导致 l.size()变化,不能达到最初的目的。
2. 浮点数的比较和相减会因为精度的原因导致很大的误差,需要小心对待。
解决方案:
1)用BigDecimal类的subtract()方法来实现相减
2)将浮点数转化为整数后进行计算
3. static类型的变量属于类,当一个实例对static类型变量的值进行改变时,同时会改变其他实例中的该变量的值。
4. long类型
1)long l= 9999999999;(超过了int类型的取值范围,编译错误)
2)long l=9999999*10000;(虽然结果超出了int类型的取值范围,但需要在运行的时候发现,所以编译正确,由于结果溢出了,l 会被赋值为溢出后截断的结果)
3)long l=9999999L*10000;(编译正确,结果正确)
5. 复合赋值操作
1)
short n1=30000;
int n2=35000;
n1+=n2;
System.out.println(n1);
编译通过,运行的结果是-536。由于n1+=n2,计算结果的值超出了short类型的取值范围,所以自动将高位截掉了。
2)
short n1=30000;
int n2=35000;
n1=n1+n2; //报错
System.out.println(n1);
编译出错,n1+n2计算时,自动将结果转化为int类型,int类型不能强制转化为short类型,所以报错。如果改成 n1=(short)n1+n2; 编译通过,得到的结果也是 -536
6. StringBuffer的构造函数只有如下三个,没有char型入口参数的构造方法,如果用一个字符作为其构造方法的入口参数,则等同于传递了一个int型的入口参数,这个值就是该字符在Unicode表中的位置值,也就是说创建了一个具有参数指定的初试容量,但不带初试字符的字符串缓冲区对象。
StringBuffer()
StringBuffer( int capacity )
StringBuffer( String str )
7. static变量的放置顺序
import java.util.Calendar;
public class Outer{
public static final Outer o= new Outer();
public static final int C=currentYear();
public int work;
private static int currentYear() {
return Calendar.getInstance().get(Calendar.YEAR);
}
private Outer(){
work = C-2005;
}
public int getWorkages(){
return work;
}
public static void main(String[] args){
System.out.println(o.getWorkages());
}
}
程序原本是想计算当前的年份减掉2005的数值,但是该程序的结果是-2005。
原因是:首先根据static的放置顺序初始化静态的static变量。先new一个Outer对象o,调用构造函数private Outer() ,此时static类型的C没有初始化,默认值为0,所以在构造函数中得到work的值是-2005,然后再初始化下一个static变量C,赋值为当前年份,不会影响work的值,所以在main函数中,输出的值为-2005.
如果想实现原本的目的,只需要将两个static变量的定义语句换位置就可以了。
import java.util.Calendar;
public class Outer{
public static final int C=currentYear();
public static final Outer o= new Outer();
public int work;
private static int currentYear() {
return Calendar.getInstance().get(Calendar.YEAR);
}
private Outer(){
work = C-2005;
}
public int getWorkages(){
return work;
}
public static void main(String[] args){
System.out.println(o.getWorkages());
}
}
static变量的声明与初始值分开时,初始值的顺序与变量的位置有关,与声明的位置无关。(不太准确)
import java.util.Calendar;
public class Outer{
public static int n1;
public static int n2;
public static int n3;
Outer() throws InterruptedException{
n2=Calendar.getInstance().get(Calendar.SECOND);
Thread.sleep(3000);
n3=Calendar.getInstance().get(Calendar.SECOND);
Thread.sleep(3000);
n1=Calendar.getInstance().get(Calendar.SECOND);
Thread.sleep(3000);
}
int getn1(){
return n1;
}
int getn2(){
return n2;
}
int getn3(){
return n3;
}
public static void main(String[] args){
Outer a1;
try {
a1 = new Outer();
System.out.println(a1.getn1());
System.out.println(a1.getn2());
System.out.println(a1.getn3());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
程序运行结果:
57
51
54
从结果可以看出,程序是先赋值n2,再赋值n3,最后赋值n1.其实这个结果是不难理解的,类中的成员变量在声明时,如有初始值就赋初始值,没有初始值就赋默认值,程序中n1,n2,n3在定义时就已经赋值为0,0,0,在构造函数中顺序执行程序,最后的结果当然是n2先赋值,其次n3,最后n1.
8. 无论在什么情况下,如果需要重写对象的equals()方法和hashCode()方法,一定要同时重写这两个方法,不能只重写其中的一个方法,并且在重写对象的equals()方法时,一定要注意该方法的形参类型必须要是Object类型的,而不能是其他类型的,如果改成了其他类型就不是重写,而是重载equals()方法了。
9. Java编译器中自动生成的构造方法的访问修饰符与所在类的修饰符一致,也就是说,如果类是public的,默认构造方法的修饰符就是public的,如果类无修饰符,默认构造方法也就没有修饰符。在内部类中,类的修饰符可能是private和protected,那么相应的构造方法的修饰符就是private或protected。
10. 装箱与拆箱
Integer num1=80;
Integer num2=80;
if(num1==num2){
System.out.println("num1==num2");
}else{
System.out.println("num1!=num2");
}
程序运行结果:
num1==num2
但是将数值80都改成180后,运行的结果就变成了“ num1!=num2 ”,这是因为在进行自动装箱时,对于整数值-128~127范围内的数,可以在放在常量区,而对于不在这个范围这个范围的数,就会存放在堆中,如果将条件表达式换成 num1.equals(num2),程序就可以输出正确的结果 “ num1==num2 ”
11. 不能在for...each循环中改变集合的元素
String [] s={"1","2","3"};
for(String ss:s){
ss="4";
}
for(String ss:s){
System.out.println(ss);
}
程序编译正确,运行结果是
1
2
3
12. ==与equals
1)对于Java中已经定义好的几种引用数据类型,如Byte,Short,Integer,Long,Float,Double,String,使用“==”比较对象时,比较的是对象的引用是否相同,如果对象的引用相同,则比较的结果就是true,否则就是false。
而对于equals(),比较的是对象的内容是否相同,如果对象的内容相同,则比较的结果就是true,否则就是false。
2)对于简单类型,如byte,short,int,long,float,double,使用“==”比较时,比较的是内容。
而对于字符串常量,由于字符串常量存放在堆栈中,所以当两个字符串的内容相同时,两个字符串引用会同时指向堆栈中该字符串存放的内存位置,即位置相同,使用“==”比较时,由于内容也一样,两者相同,结果为true。
3)equals()方法是Object类中的方法,在没有重写该方法时,equals()方法是用来比较对象的引用是否相同,Java中的几种引用数据类型可以用equals()来判断内容是否相同,是因为这些类型重写了equals()方法,所以,当需要比较其他对象时,必须重写Object类的equals()方法,并最好也重写hashCode()方法,以免在集合中出现错误。
Integer a=new Integer(10);
Integer b=new Integer(10);
System.out.println(a==b);
System.out.println(a.equals(b));
String s1=new String("Java");
String s2=new String("Java");
System.out.println(s1==s2);
System.out.println(s1.equals(s2));
String ss1= "Java";
String ss2="Java";
System.out.println(ss1==ss2);
System.out.println(ss1.equals(ss2));
程序运行结果:
false
true
false
true
true
true
13. 向上转型和向下转型
1)向上转型是指从子类对象到父类对象的类型转换,这种转换由系统自动完成。当子类对象向上转型为父类对象后,就不能再通过父类对象的引用来访问子类对象新增的属性和方法了,但是可以访问子类重写的父类中的属性和方法。
2)向下转型是指从父类对象到子类对象的类型转换,在进行向下转型(显式类型转换 或 强制类型转换)时,不可以直接将父类对象创建的引用显式转换为子类对象,这样做会出错,而是应该将子类对象创建的父类对象的引用显式地转换为子类对象。为避免向下转型时出错,可以通过instanceof运算符判断父类对象是否可以转换为子类对象。
14. 初始化块
1)若初始化块写在成员变量之前,并且定义成员变量时,没有用赋值运算符为成员变量指定初始值
public class Outer{
{
num=10;
}
private int num;
public static void main(String[] args){
Outer o= new Outer();
System.out.println(o.num);
}
}
程序运行结果:
10
2)若初始化块写在成员变量后面,定义成员变量时,没有用赋值运算符为成员变量指定初始值
public class Outer{
private int num;
{
num=10;
}
public static void main(String[] args){
Outer o= new Outer();
System.out.println(o.num);
}
}
程序运行结果:
10
3)若初始化块写在成员变量前面,定义成员变量时,用赋值运算符为成员变量指定了初始值
public class Outer{
{
num=10;
}
private int num=20;
public static void main(String[] args){
Outer o= new Outer();
System.out.println(o.num);
}
}
程序运行结果:
20
4)若初始化块写在成员变量后面,定义成员变量时,用赋值运算符为成员变量指定了初始值
public class Outer{
private int num=20;
{
num=10;
}
public static void main(String[] args){
Outer o= new Outer();
System.out.println(o.num);
}
}
程序运行结果:
10
解释:Java类初始化顺序是:静态成员、静态初始化块——>成员变量、普通初始化块——>构造函数
其中静态成员和静态初始化块中的变量的初始顺序和初始出现的顺序相同,成员变量和普通初始化块的变量的初试顺序和初始出现的顺序相同。
当普通成员变量定义后没有马上初始化的现象,不能认为初始化为默认值,只有当类中没有初始该变量时,才会用默认值初始化成员变量。所以,对于只定义而没有初始化的成员变量,如果初始化块或构造函数中对该变量进行了初始化,则该成员变量的初始值就是初始化块或构造函数中的初始值。对于既存在初始化块赋值,并在定义时也进行了初始化,则该变量的值会根据初始的顺序依次被赋予不同的值。
可参考 点击打开链接
对于static变量,和普通变量的初始化情况类似。