1.classpath环境变量
使用1.5版本以上的JDK时,不推荐设置classpath环境变量
2.大小写
Java语言是严格区分大小写的语言,在编写Java程序时,必须严格注意Java程序中每个单词的大小写,
例如:main() 和 Main()是两个完全不同的方法,Java虚拟机只会选择从main方法开始执行。
3.推荐修改JDK的安装路径
使用JDK的默认安装路径,可能会引发各种莫名奇妙的问题。
4.使用二进制整数
byte binVal2 = 0B01101001;
可以编译通过
byte binVal2 = 0B11101001;
无法编译通过,
5.将浮点数直接赋给float变量
float a = 5.6
//因为浮点数默认是一个double类型的浮点数,因此将5.6赋值给一个float类型将导致错误,必须使用强制类型转换才可以。
float a = (float)5.6
6.除以0异常
5 / 0
5 % 0
都会引发除以0异常
7.== 和 = 混用
==:等于
=:赋值运算符
8.if分支语句省略花括号
即使条件执行体只有一行代码,也保留花括号会有更好的可读性,
而且保留花括号会减少发生错误的可能。
nt b = 5;
if (b > 4)
// 如果b>4,执行下面的执行体,只有一行代码作为代码块
System.out.println("b大于4");
else
// 否则,执行下面的执行体,只有一行代码作为代码块
b--;
// 对于下面代码而言,它已经不再是条件执行体的一部分,因此总会执行。
System.out.println("b不大于4");
9.if...else语句的执行顺序问题
会从上至下顺序执行,如果有一个条件成立,则后面的条件不再进行判断
int age = 45;
if (age > 20)
{
System.out.println("青年人");
}
else if (age > 40)
{
System.out.println("中年人");
}
else if (age > 60)
{
System.out.println("老年人");
}
本意想输出中年人,结果输出的却是青年人
因此,当使用if...else语句进行流程控制时,一定不要忽略了else所带的隐含条件。
为了避免出现上面的错误,在使用if...else语句时有一条基本规则:总是优先把包含范围
小的条件放在前面处理。如age>60和age>20两个条件,明显age>60的范围更小,所以
应该先处理age>60的情况。
10.switch语句中的各个case标签后忘记添加break语句
char score = 'C';
// 执行swicth分支语句
switch (score)
{
case 'B':
System.out.println("良好");
break;
case 'C':
System.out.println("中");
case 'D':
System.out.println("及格");
case 'F':
System.out.println("不及格");
}
运行结果:
中
及格
不及格
成绩输入错误
11.死循环
// 下面是一个死循环
// int count = 0;
// while (count < 10)
// {
// System.out.println("不停执行的死循环 " + count);
// count--;
// }
// System.out.println("永远无法跳出的循环体");
使用while循环时,一定要保证循环条件有变成false的时候,否则这个循环将成为死循环,
永远无法结束这个循环。
12.while循环的循环条件后误加了一个分号,变成了死循环
int count = 0;
// while后紧跟一个分号,表明循环体是一个分号(空语句)
while (count < 10);
// 下面的代码块与while循环已经没有任何关系
{
System.out.println("------" + count);
count++;
}
在Java程序中,一个单独的分号表示一个空语句,不做任何事情的空语句,
这意味着这个while 循环的循环体是空语句,当Java反复执行这个循环体
时,循环条件的返回值没有任何改变,这就成了一个死循环。
13.do...while循环的循环条件后必须有一个分号,这个分号表明循环结束。
// 定义变量count2
int count2 = 20;
// 执行do while循环
do
// 这行代码把循环体和迭代部分合并成了一行代码
System.out.println(count2++);
while (count2 < 10);
System.out.println("循环结束!");
14.不要在循环体内修改循环变量(也叫循环计数器)的值
// 循环的初始化条件,循环条件,循环迭代语句都在下面一行
for (int count = 0 ; count < 10 ; count++)
{
System.out.println(count);
// 再次修改了循环变量
count *= 0.1;
}
System.out.println("循环结束!");
该程序在循环体内修改了count变量的值,并且把这个变量的值乘以0.1,
导致count的值永远都不能超过10,因此上面的程序是一个死循环。
因此建议不要在循环体内修改循环变量(也叫循环计数器)的值,否则会增加程序出错的可能性。
万一程序真的需要访问,修改循环变量的值,建议重新定义一个临时变量,先将循环变量的值
赋给临时变量,然后队对临时变量的值进行修改。
15.假象:一个数组里可以存放多种数据类型
实际上一个数组里只能存储一种数据类型的数据,而不能存储多种数据类型的数据。
假象造成的原因可能是因为数组初始化时使用了该类的子类造成的。
16.假象:数组的长度可以改变
一旦数组的初始化完成,数组在内存中所占的空间将被固定下来,因此数组的
长度将不可改变。
假象造成的原因可能是因为引用变量指向了新的对象造成的。
11.Java构造器写错
public 构造器名(形参列表){ //正确写法
//执行体
}
public void 构造器名(形参列表){ //错误写法,Java会把这个所谓的构造器当成方法来处理,不再是构造器
//执行体
}
Java语法规定:构造器既不能定义返回值类型,也不能用void声明
构造器没有返回值,而且也不能在构造器里显式使用return来返回当前类
的对象,因为构造器的返回值是隐式的
12.使用==运算符比较两个包装类对象
两个包装类的实例进行比较
两个包装类的实例进行比较时比较复杂,因为包装类的实例实际上是引用类型,
只有两个包装类引用指向同一个对象时,才会返回true。
System.out.println("比较2个包装类的实例是否相等:"
+ (new Integer(2) == new Integer(2))); // 输出false
但JDK1.5后支持自动装箱功能,在这种情况下可能会出现一些BUG:
// 通过自动装箱,允许把基本类型值赋值给包装类的实例
Integer ina = 2;
Integer inb = 2;
System.out.println("两个2自动装箱后是否相等:"
+ (ina == inb)); // 输出true
Integer biga = 128;
Integer bigb = 128;
System.out.println("两个128自动装箱后是否相等:"
+ (biga == bigb)); // 输出false
同样是两个int类型的数值自动装箱成Integer实例后,如果是两个2自动装箱
后就相等;但如果是两个128自动装箱后就不相等,这是为什么呢?
这与Java的Integer类的设计有关,
java.lang.Ingeger的源码
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
可总结为:
//定义一个长度为256的Integer数组
static final Integer[] cache = new Integer[-(128)+127+1];
static{
//执行初始化,创建-128到127的Integer实例,并放入cache数组
for(int i = 0; i < cache.length; i++)
cache[i] = new Integer(i - 128);
}
从上面代码可以看出,系统把一个-128~127之间的整数自动装箱成Integer实例,
并放入了一个名为cache的数组中缓存起来。如果以后把一个-128~127之间的
整数自动装箱成一个Ingeger实例时,实际上是直接指向对应的数组元素,
因此-128~127之间的同一个正数自动装箱成Integer实例时,永远都是引用
cache数组的同一个数组元素,所以它们全部相等;
但如果把一个不在-128~127范围内的正数自动装箱成Integer实例时,系统
总是重新创建一个Integer实例。
13.在子类重写父类成员变量可能会引发的问题
ex:
public class Bullet extends Fly{
int speed = 0; //改行应删除
public Bullet(int x, int y, int speed, Image img) {
super(x, y, speed, img);
// this.x = x;
// this.y = y;
// this.speed = speed;
// this.img = img;
}
当创建Bullet对象时,先调用lang包下的构造器,在调用Fly的构造器,此时speed初始化的是父类的speed变量,
接着执行本类的构造器,最后初始化本类的成员变量,当本类定义的变量与父类相同时,会隐藏父类的变量,
此时,本类的speed是0.
所以不推荐在子类中重写父类的变量。若是重写了父类的变量,
则不推荐在本类构造器中使用super(x, y, speed, img);调用父类构造器
14.子类无法重写被private修饰的父类方法
对于一个private方法,因为它仅在当前类中可见,其子类无法访问该方法,
所以子类无法重写该方法--如果子类中定义一个与父类private方法有相同的
方法名,相同形参列表,相同返回值类型的方法,也不是方法重写,知识重新
定义了一个新的方法。
public class PrivateFinalMethodTest
{
private final void test(){}
}
class Sub extends PrivateFinalMethodTest
{
// 下面方法定义将不会出现问题
public void test(){}
}
15.创建不可变类时,包含一个引用类型的成员变量
当创建一个不可变类时,如果它包含成员变量的类型是可变的(引用类型),那么其对象
的成员变量的值依然是可改变的--这个不可变类其实是失败的。
因此,如果需要设计一个不可变类,尤其要注意其引用类型的成员变量,如果引用类型的成员
变量的类是可变的,就必须采取必要的措施来保护该成员变量所引用的对象不会被修改,这样次啊能创建真正
的不可变类
16.抽象方法后加了花括号
抽象方法根本没有方法体,即方法定义后面没有一对花括号;
ex:
public abstract void test();
错误写法:
public abstract void test(){
}
17.注意:Calendar.MONTH字段表示月份,月份的起始值不是1,而是0,
所以要设置8月时,用7而不是8.
c.set(2003 , 10 , 23 , 12, 32, 23); //2003-11-23 12:32:23
System.out.println(c.getTime());
18.注意:创建BigDeciaml对象时,不要直接使用double浮点数作为
构造器参数来调用BigDecimal构造器,否则同样会发生精度丢失
的问题。
BigDecimal f1 = new BigDecimal("0.05");
BigDecimal f2 = BigDecimal.valueOf(0.01);
BigDecimal f3 = new BigDecimal(0.05);
System.out.println("使用String作为BigDecimal构造器参数:");
System.out.println("0.05 + 0.01 = " + f1.add(f2));
System.out.println("0.05 - 0.01 = " + f1.subtract(f2));
System.out.println("0.05 * 0.01 = " + f1.multiply(f2));
System.out.println("0.05 / 0.01 = " + f1.divide(f2));
System.out.println("使用double作为BigDecimal构造器参数:");
System.out.println("0.05 + 0.01 = " + f3.add(f2));
System.out.println("0.05 - 0.01 = " + f3.subtract(f2));
System.out.println("0.05 * 0.01 = " + f3.multiply(f2));
System.out.println("0.05 / 0.01 = " + f3.divide(f2));
运行结果:
使用String作为BigDecimal构造器参数:
0.05 + 0.01 = 0.06
0.05 - 0.01 = 0.04
0.05 * 0.01 = 0.0005
0.05 / 0.01 = 5
使用double作为BigDecimal构造器参数:
0.05 + 0.01 = 0.06000000000000000277555756156289135105907917022705078125
0.05 - 0.01 = 0.04000000000000000277555756156289135105907917022705078125
0.05 * 0.01 = 0.0005000000000000000277555756156289135105907917022705078125
0.05 / 0.01 = 5.000000000000000277555756156289135105907917022705078125
.....后续会逐渐更新