建议31 在接口中不要存在实现代码
建议32 静态变量一定要先声明后赋值
这要从静态变量的诞生说起了,静态变量是类加载时被分配到数据区(Data Area)的,它在内存中只有一个拷贝,不会被分配多次,其后的所有赋值操作都是值改变,地址则保持不变。我们知道JVM初始化变量是先声明空间,然后再赋值的,也就是说:
int i=100;
在JVM中是分开执行,等价于:
int i; //分配地址空间
i=100; //赋值
静态变量是在类初始化时首先被加载的,JVM会去查找类中所有的静态声明,然后分配空间,注意这时候只是完成了地址空间的分配,还没有赋值,之后JVM会根据类中静态赋值(包括静态类赋值和静态块赋值)的先后顺序来执行。对于程序来说,就是先声明了int类型的地址空间,并把地址传递给了i,然后按照类中的先后顺序执行赋值动作。
建议33 不要覆写静态方法
见文章 Java-static方法
建议34 构造函数尽量简化
子类是如何实例化的?子类实例化时,会首先初始化父类(注意这里是初始化,可不是生成父类对象),也就是初始化父类的变量,调用父类的构造函数,然后才会初始化子类的变量,调用子类自己的构造函数,最后生成一个实例对象。
建议35 不要在构造函数中声明初始化其他类
建议36 使用构造代码精炼程序
在Java中一共有四种类型的代码块:
(1)普通代码块
就是在方法后面使用“{}”括起来的代码片段,它不能单独执行,必须通过方法名调用执行。
(2)静态代码块
在类中使用static修饰,并使用“{}”括起来的代码片段,用于静态变量的初始化或对象创建前的环境初始化。
(3)同步代码块
使用synchronized关键字修饰,并使用“{}”括起来的代码片段,它表示同一时间只能有一个线程进入到该方法块中,是一种多线程保护机制。
(4)构造代码块
在类中没有任何的前缀或后缀,并使用“{}”括起来的代码片段。
构造代码块会在每个构造函数内首先执行(需要注意的是:构造代码块不是在构造函数之前运行的,它依托于构造函数的执行)。例如:
public class Client {
{
//构造代码块
System.out.println("执行构造代码块");
}
public Client(){
System.out.println("执行无参构造");
}
public Client(String _str){
System.out.println("执行有参构造");
}
}
等价于:
public class Client {
public Client(){
System.out.println("执行构造代码块");
System.out.println("执行无参构造");
}
public Client(String _str){
System.out.println("执行构造代码块");
System.out.println("执行有参构造");
}
}
因此构造代码块应用到如下场景中:
(1)初始化实例变量(Instance Variable)
如果每个构造函数都要初始化变量,可以通过构造代码块来实现。当然也可以通过定义一个方法,然后在每个构造函数中调用该方法来实现,没错,可以解决,但是要在每个构造函数中都调用该方法,而这就是其缺点,若采用构造代码块的方式则不用定义和调用,会直接由编译器写入到每个构造函数中,这才是解决此类问题的绝佳方式。
(2)初始化实例环境
一个对象必须在适当的场景下才能存在,如果没有适当的场景,则就需要在创建对象时创建此场景,例如在JEE开发中,要产生HTTP Request必须首先建立HTTP Session,在创建HTTP Request时就可以通过构造代码块来检查HTTP Session是否已经存在,不存在则创建之。
建议37 构造代码块会想你所想
上一个人建议说编译器会把构造代码块插入到每一个构造函数中,但是
(1)若遇到this关键字(即构造函数调用自身其他构造函数时)则不插入构造代码块,即只执行一次构造代码
(2)而super方法并没有这个特殊,只是编译器把构造代码块插入super方法之后执行
建议38 使用静态内部类提高封装性
public class Person{
//姓名
private String name;
//家庭
private Home home;
//构造函数设置属性值
public Person(String _name){
name = _name;
}
/* home、name的getter/setter方法省略 */
public static class Home{
//家庭地址
private String address;
//家庭电话
private String tel;
public Home(String _address,String _tel){
address = _address;
tel = _tel;
}
/* address、tel的getter/setter方法省略 */
}
}
public static void main(String[] args) {
//定义张三这个人
Person p = new Person("张三");
//设置张三的家庭信息
p.setHome(new Person.Home("上海","021"));
}
静态内部类的优点:
(1)提高封装性。从代码位置上来讲,静态内部类放置在外部类内,其代码层意义就是:静态内部类是外部类的子行为或子属性,两者直接保持着一定的关系。
(2)提高代码的可读性。相关联的代码放在一起,可读性当然提高了。
(3)形似内部,神似外部。静态内部类虽然存在于外部类内,而且编译后的类文件名也包含外部类(格式是:外部类+$+内部类),但是它可以脱离外部类存在,也就是说我们仍然可以通过new Home()声明一个Home对象,只是需要导入“Person.Home”而已。
静态内部类与普通内部类的区别如下:
(1)静态内部类不持有外部类的引用
在普通内部类中,我们可以直接访问外部类的属性、方法,即使是private类型也可以访问,这是因为内部类持有一个外部类的引用,可以自由访问。而静态内部类,则只可以访问外部类的静态方法和静态属性(如果是private权限也能访问,这是由其代码位置所决定的),其他则不能访问。
(2)静态内部类不依赖外部类
普通内部类与外部类之间是相互依赖的关系,内部类实例不能脱离外部类实例,也就是说它们会同生同死,一起声明,一起被垃圾回收器回收。而静态内部类是可以独立存在的,即使外部类消亡了,静态内部类还是可以存在的。
(3)普通内部类不能声明static的方法和变量
普通内部类不能声明static的方法和变量,注意这里说的是变量,常量(也就是final static修饰的属性)还是可以的,而静态内部类形似外部类,没有任何限制。
建议39 使用匿名类的构造函数
List l2 = new ArrayList(){{}};
类似于:
//定义一个继承ArrayList的内部类
class Sub extends ArrayList{
{
//初始化块
}
}
//声明和赋值
List l3 = new Sub();
因为匿名类没有名字,初始化块就是它的构造函数。当然,一个类中的构造函数块可以没有,也可以是多个,也就是说可以出现如下代码:
List l1 = new ArrayList(){};
List l2 = new ArrayList(){{}};
List l3 = new ArrayList(){{}{}{}{}{}};
建议40 匿名类的构造函数很特殊
一般类(也就是具有显式名字的类)的所有构造函数默认都是调用父类的无参构造,而匿名类在初始化时直接调用父类的同参数构造,然后再调用自己的代码块。