目录
- 建议六:覆写变长方法要遵循规矩
- 建议七:警惕自增带来的陷阱
- 建议八:不要被旧语法困扰
- 建议九:少用静态导入
- 建议十:不要再本类中覆盖静态导入的变量和方法
建议六:覆写变长方法要遵循规矩
在Java中子类覆写父类中的方法很常见,这样做既可以修正BUG也可以提供扩展业务功能,同时还符合开闭原则(Open-Closed Principle)
- 重新方法的权限不能缩小访问权限
- 参数列表必须与被重写方法相同
- 返回类型必须与被重写方法的相同或是其子类
- 重写方法不能抛出新的异常,或者超出父类范围异常,但是可以抛出更少、更有限的异常,或不抛出异常。
public Class Test{
public static void main(String[] args){
//向上转型
Base base = new Sub();
base.fun(100,50);
//不转型
Sub sub = new Sub();
sub.fun(100,50);
}
}
class Base{
void fun(int price,int... dusciybts){
System.out.println("Base");
}
}
class Sub extends Base{
@Override
void fun(int price,int[] dusciybts){
System.out.println("Sub");
}
}
该程序编译不通过
sub.fun(100,50) 这句报错提示找不到fun(int,int)方法 这就奇怪了:子类继承父类所有属性和方法,不管私有的还是公开的访问权限,同样的参数、同样的方法名,通过父类调用没有任何问题,通过子类调用却编译不过。
事实上,base对象是吧子类对象Sub做了上转型,形参列表时由父类决定的由于时边长参数,在编译时"base.fun(100,50)"中"50"这个参数会被编译器猜测为{50}数组,再由子类Sub执行,但是子类直接调用是并不会把"50"类型转换,因为数组本身也是一个对象,编译器不能再两个没有继承关系的类之间转换。
- 注意 覆写方法参数与父类相同,不仅仅时类型、数量、还包括显示形式。
建议七:警惕自增带来的陷阱
记得刚开始学自增时候十在大学一年级学习C语言的时候,自增有两种形式i++,++i,
i++表示的是先赋值再自增1,++i是先自增1后赋值,这样理解了很长时间也没有问题,知道遇到了一个特殊代码,我才怀疑自己的理解是否有误:
public class Client{
public static void main(String[] args){
int number = 0;
for(int i=0;i<10;i++){
number = number ++;
}
System.out.println("number="+number);
}
}
这个程序输出的count等于几?刚开始看的时候我会自信的回答10!但是可以肯定的说运行结果是0
count++是一个有返回值的表达式,它的返回值就是count自加前的值,java对自加是这样处理的:
- JVM先把number值(值是0)拷贝到临时百年两区
- number值加1,这时候number值是1
- 返回临时变量区的值,注意这个值是0,没修改过
- 返回值赋值给number,此时number值被重置为0
相当于以下的执行语句
int tem = number;
number = number + 1;
return tem;
所以你会发现最终结果是0
建议八:小心被旧语法困扰
之前找到一个除了原码以外什么都没有的项目,没需求,没文档,没设计。只能通过阅读源码来进行维护,期间看到依据特别神奇的代码
public static void main(String[] args){
// 数据定义与初始化
int fee = 200;
// 其他业务处理
saveDefalut:save(fee);
// 其他业务处理
}
这句神奇的代码出现而且还没报错,之后查询很多资料才找到,这是C语言里被java抛弃的goto语句.
Java中虽然没了goto但是扩展了break 与 continue关键字所以建议用较新的语法而非被java抛弃的语法。
建议九:少用静态导入
Java 5 引入了静态导入语法(import static),其目的是为了减少字符输入两,提高代码的可阅读性,以便更好地理解程序。
public class MathTest{
public static double calArea(double r){
return Math.PI * r * r;//这样写入Math过多的话有些多余
}
}
// -------------------------------------
import static java.lang.Math.PI;
class MathTest1{
public static double calArea(double r){
return PI * r * r;//这样写入Math过多的话有些多余
}
}
静态导入的作用就是把Math类中的PI常量引入到本类中更易阅读。但是滥用静态导入会是程序更难阅读,更难维护。
所以对于静态导入,一定要遵循两个规则
- 不使用*通配符,除非是导入静态常量类(只包含常量的类或接口)
- 方法名是具有明确、清晰表象意义的工具类
建议十:不要再本类中覆盖静态导入的变量和方法
如果一个类中的方法及属性与静态导入的方法及属性重名会出现一系列问题
import static java.lang.Math.PI;
import static java.lang.Math.abs;
public class Test{
// 常量名与静态导入的PI相同时
public final static String PI = "祖冲之";
// 方法名与静态导入的相同
public static int abs(int abs){
return 0;
}
public static void main(String args[]){
System.out.println("PI="+PI);
System.out.println("abs(100)="+abs(-100));
}
}
以上代码编译器没有报错,我们不知道哪个属性和哪个方法被调用了,因为常常变量名和方法名相同,到底调用了哪一个方法
PI=祖冲之
abs(100)=0
很明显是本地属性和方法被引用为什么不是Math类中属性和方法?
因为编译器的一个"最短路径"
原则:如果能够在苯类中查找到的变量、常量、方法,就不会跑到其他包或父类、接口中查找,以确保本类属性、方法优先。
因此,如果要变更一个被静态导入的方法,最好的办法是在原始类中重构,而非本类覆盖。