深入了解Java几个常见的关键字:private public protect final static this super
this和super:
- this是指向当前对象的一个引用,可以使用this访问当前对象的属性和方法,一般用在形参和属性名相同的时候使用,this()标示调用当前类中的构造方法
- super是指向当前类父类对象的一个引用,可以使用super访问父类的属性和方法(由于继承的存在,父类的属性和方法可以直接在子类中访问),super()表示调用父类的构造方法
- this(参数)代表调用本类对应的构造方法;
- super(参数)代表调用父类对应的构造方法;
- super()和this()需要放在构造方法的第一行;
- 同一个构造函数不能同时出现super和this,因为this调用其他的构造函数,其他的构造函数至少会默认调用父类无参的构造函数,这样不仅重复而且也会编译错误;
private public和protect
- 三者用来控制属性和方法的可见性,其中三者中只有public可以用于外部类的修饰符,内部类可以使用private
- private修饰的属性和方法只有本类成员可见,在类的外部即使类的对象都无法访问;
- protect修饰的属性和方法只对本类和子类可见;
- public修饰的属性和方法对所有能使用类的地方都可以访问该类的属性和方法;
final和static
要理解final和static变量,先了一个类中个部分的加载顺序:
- 调用构造方法时,从上到下,从父类到子类,执行static修饰的语句(static变量和static块),static执行完之后是构造代码块,最后是构造函数;
- static块:static{}
- 构造代码块:没有static修饰,直接{}
其次要了解jvm加载类的过程:
Java代码在编译后会生成jvm能够识别的二进制字节流文件,.class文件,jvm吧class文件中的类描述数据从文件加载到jvm内存,并对数据进行校验,解析和初始化,
最终得到jvm能够使用的Java数据,这就是jvm的类加载机制。
类从class文件加载到jvm内存到使用完毕卸载经历七个生命周期:加载,验证,准备,解析,初始化,使用,卸载。类加载包含了前五个生命周期;
类的加载,注意这里的加载是指生命周期的第一个阶段不是指类的加载机制。此阶段jvm完成三件事:
- 通过类的全名获取类的class文件,获取方式可以从jar包中,网络等
- 将字节流代表的静态存储结构转化为“方法区”运行时的数据结构,这里只对数据结构进行转化没有赋值。方法区存放已加载的类的信息常量和静态变量,代码运行的内存区域;
- 在内存中生成一个对应此类的Java.lang.Class对象,作为方法区内的变量方法的访问入口,注意此对象存储在方法区而不是堆内存中;
验证,准备和解析统称为类的连接,此阶段jvm做的事:
- 验证被加载的类是否具有正确的数据结构,同时也进行安全性校验
- 为类的静态变量在方法区分配内存,并赋给默认值,0或null;这里需要注意:一般成员变量不会在此配内存,而是随对象实例化分配到堆内存中。
- 对于静态常量static final此步会直接赋给程序中指定的值而不是先赋值为默认值;
- 解析是将类的二进制引用转换为直接引用;
类的初始化是类加载的最后一步,主要的工作是为静态变量赋给程序指定的值。
需要注意的是类的初始化并不是自动进行仅仅在满足以下五种情况下才会进行类的初始化:
- 使用new关键字创建类的实例对象,或者访问类的静态变量或者调用类的静态方法时;
- 反射调用时;
- 当初始化子类的时候会对父类进行初始化;
- 包含main方法的类在被加载时;
- 以下情况不会触发类的初始化:
- 在子类中引用父类的静态变量,这只会触发父类的初始化,不会触发子类初始化
- 声明类的数组时;
- 访问类的静态常量时,由于静态常量在准备阶段就已经分配在方法区而且赋了程序制定的值,不需要初始化类
引申:类加载的第一步中从文件获取类的class文件是通过类加载器完成的,类加载器分为三类:
- BootstrapClassLoader 启动类加载器或者根类加载器,加载Java的核心类库,底层是由C++实现的
- ExtentionClassLoader 扩展类加载器,加载Java扩展类库,ext下;
- SystemClassLoader或者APPClassLoader 加载classPath指定jar包
类的加载模式是双亲委派,当一个加载器收到加载请求时会将此请求转交给父类加载器请求加载,父类也会一直向上请求,只有父类加载不了时才会尝试自己加载。
了解类的加载机制后再来看final和static关键字:
final单独使用时可以用来修饰类,属性和方法以及方法参数:
- 修饰类,表示此类不可以被继承,final类中的所有方法默认都为final类型
- 修饰方法,该方法不可以被子类覆盖;
- 修饰属性和变量,表示此变量为常量,只能被赋值一次,赋值后无法再被改变;
- 修饰方法参数,可以在方法中使用此参数,但无法改变此参数;
- final修饰的变量表示只能赋值一次,而且必须是在初始化时进行赋值,因此可以重载构造函数灵活使用
1 public class AClass { 2 3 private final int a; 4 5 public AClass(int b) { 6 a = b; 7 } 8 9 public AClass(int b, int c) {10 a = b + c;11 }12 13 public void print() {14 System.out.println(a);15 }16 17 public static void main(String[] args) {18 AClass a = new AClass(2);19 a.print();20 21 AClass b = new AClass(2, 2);22 b.print();23 }24 }
输出结果:
24
static用来修饰成员变量和方法和代码块(除内部类外不能用来修饰类)
- static修饰的成员变量和方法是独立于类的对象存在的,只要这个类被加载,这些成员变量和方法就可以被访问(通过类名)
- public static修饰的成员变量或方法全局只存在一份,不会在声明对象时产生副本;
- private static修饰的成员变量或方法只能在本类的静态方法静态块或非静态方法中使用,无法通过类名在外部访问(private的限制)
- static修饰代码块,可以存在多个,jvm会按照顺序加载
static和final一起使用表示静态常量,静态常量赋值后不可再改变,可以全部访问(前提是不是private),对于String和基本数据类型不能被改变,对象,集合等引用不改变但值可以改变;
根据上述类加载的过程可以看出final和static关键字能够提高程序的运行效率。