一、static、final、abstract三者的使用场景与注意事项
static关键字可用于修饰成员变量和成员函数,想要实现对象中的共性数据的对象共享,可以将这个数据进行静态修饰,被静态修饰的成员可以直接被类名调用,静态随着类的加载而加载,而且优先于对象存在。静态方法只能访问静态成员(静态方法和静态变量),不可以访问非静态成员,这是因为静态方法加载时,优先于对象存在,所以没有办法访问对象中的成员。
静态方法中不能使用this和super关键字,因为this代表本类对象,super代表父类对象,而静态内容执行时,有可能因为对象内容不存在(如对象还未创建),所以this和super无法使用。
final关键字可用于修饰类,方法,变量(成员变量内,局部变量,静态变量),被final修饰的类是一个最终类,不可以被继承。被final修饰的方法是一个最终方法,不可以被覆盖,但是可以被继承。被final修饰的变量只能是一个常量,只能赋值一次。内部类被定义在类中的局部位置上时,只能访问局部被final修饰的局部变量。
abstract关键字只能用于修饰类和方法,不能修饰变量。抽象方法只能定义在抽象类中,抽象方法和抽象类必须由abstract修饰,抽象方法只定义方法声明,不定义方法实现。抽象类不可以被实例化(创建对象),只有通过子类继承抽象类并覆盖抽象类中的所有抽象方法后,该子类才可以被实例化,否则该子类还是一个抽象类。抽象类中有构造函数用于给子类对象进行初始化,同时抽象类中可以含有非抽象方法。
注意事项:abstract关键字不可以与final、private、static关键字共存,因为被final修饰的方法不可以被重写,意味着子类不可以重写该方法,如果abstract和final共同修饰父类中的方法,子类要实现抽象方法(abstract的作用),而final又不让该方法重写,这相互矛盾。如果private和abstract共同修饰父类中的方法,private修饰则该方法不可以被子类访问,但是abstract修饰需要子类去实现,两者产生矛盾。如果static和abstract共同修饰父类中的方法,static表示是静态的方法,随着类的加载而加载,则该方法不需要在子类中去实现,这与abstract关键字矛盾。
二、static 修饰属性、方法和代码块
1.static修饰属性
类名.属性名”来访问,也就是使这个属性成为本类的类变量,为本类对象所共有。这个属性就是全类公有。类加载的过程,类本身保存在文件中(字节码文件保存着类的信息)的,java会通过I/O流把类的文件(字节码文件)读入JVM(java虚拟机),这个过程称为类的加载过程。
JVM(java虚拟机)会通过类路径(CLASSPATH)来找字节码文件。
类变量,会在加载时自动初始化,初始化规则和实例变量相同。
注意:
类中的实例变量是在创建对象时被初始化的,被static修饰的属性(类变量),是在类加载时被创建并进行初始化,类加载的过程是进行一次。也就是类变量只会被创建一次。
2.static修饰方法(静态方法)
类名.方法名”访问。
"类名.属性名"以及"类名.方法"访问举例:
测试类 :
注意:
(1)static修饰的方法,不直接能访问本类中的非静态(static)成员(包括方法和属性),本类的非静态方法可以访问本类的静态成员(包括方法和属性)静态方法要慎重使用(原因是静态变量会一直存在,占用资源),且在静态方法中不能出现this关键字。
(2)父类中是静态方法,子类中不能覆盖为非静态方法;在符合覆盖规则的前提下,在父子类中,父类中的静态方法可以被子类中的静态方法覆盖,但无多态。(在使用对象调用静态方法时,实则是调用编译时类型的静态方法)
(3)父子类中,静态方法只能被静态方法覆盖,父子类中,非静态方法只能被非静态方法覆盖。
例:java中的main方法必须写成static的,因为在类加载时无法创建对象,静态方法可以不通过对象调用所以在类的main方法。
所在在类加载时就可以通过main方法入口来运行程序。
3.static修饰初始代码块
由static修饰的代码块就叫做静态初始代码块,该代码块只在类加载时被执行一次。
通常情况我们初始代码块,写在类体中的“{}”,这个代码块是在生成对象的初始化属性时运行,这种代码块叫动态初始代码块。
也可以用静态初始代码块初始化一个类,如:
类在什么时候会被加载,创建对象时会加载类,调用类中静态方法或访问静态属性也是会加载类的。在加载子类时必会先加载父类,类加载会有延迟加载原则,只有在必须加载时才会加载。
三、final修饰变量、方法和类
1.final修饰变量
变量被final修饰,就会变成常量(常量应大写),一旦赋值不能改变(可以在初始化时直接赋值,也可以在构造方法里也可以赋值,只能在这两种方法里二选一,必须为常量赋值);final的常量不会有默认初始值,对于直接在初始化是赋值时final修饰符常和static修饰符一起使用。
2.final修饰方法
被final修饰的方法将不能被其子类覆盖,保持方法的稳定不能被覆盖。
3.final修饰类
被final修饰的类将不能被继承。final类中的方法也都是final的。
注意一:
final,不能用来修饰构造方法,在父类中如果有常量属性,在子类中使用常量属性时是不会进行父类的类加载。
对象一旦创建属性就不会改变。用final修饰属性,也用final修饰类(强不变模式),用final修饰属性(弱不变模式)。
如:java.lang.String类,不变模式可以实现对象的共享(可以用一个对象实例赋值给多个对象变量)池化的思想,把需要共享的数据放在池中(节省资源空间,共享数据)
注意二:
只有String类可以用“”中的字面值创建对象。在String类中,以字面值创建时,会到Java方法空间的串池空间中去查找,如果有就返回串池中字符串的地址,并把这个地址付给对象变量。如果没有则会在串池里创建一个字符串对象,并返回其地址付购对象变量,当另一个以字面时,创建对象时则会重复上述过程。如果是new在堆空间中创建String类的对象,则不会有上述的过程。
String类中的intern()方法会将在堆空间中创建的String类对象中的字符串和串池中的比对,如果有相同的串就返回这个串的串池中的地址。不变模式在对于对象进行修改,添加操作是相当麻烦的,它会产生很多的中间垃圾对象。创建和销毁的资源的开销是相当大的。这就是推荐大量使用StringBuffer,而不建议使用String的原因!
String类在字符串连接时会先的效率很低,就是因为它所产生的对象的属性不能被修改的,当连接字符串时也就是只能创建新的对象。造成很多无用的资源开销和浪费!
一句话概述:final修饰的东西不能被修改,不能被继承。
四、abstract修饰类和方法
1.abstract修饰类
使这个类成为一个抽象类,类将不能生成对象实例,但可以做为对象变量声明的类型,也就是编译时类型,抽象类就像当于一类的半成品,需要子类继承并覆盖其中的抽象方法。
2.abstract修饰方法
使这个方法变成抽象方法,即只有声明(定义)而没有实现,实现部分以";"代替。需要子类继承实现(覆盖)。
注意:
(1)有抽象方法的类一定是抽象类。但是抽象类中不一定都是抽象方法,也可以全是具体方法。abstract修饰符在修饰类时必须放在类名前。
abstract修饰方法就是要求其子类覆盖(实现)这个方法。调用时可以以多态方式调用子类覆盖(实现)后的方法,即抽象方法必须在其子类中实现,除非子类本身也是抽象类。
(2)父类是抽象类,其中有抽象方法,那么子类继承父类,并把父类中的所有抽象方法都实现(覆盖)了,子类才有创建对象的实例的能 力,否则子类也必须是抽象类。抽象类中可以有构造方法,是子类在构造子类对象时需要调用的父类(抽象类)的构造方法。
(3)不能放在一起的修饰符:final和abstract,private和abstract,static和abstract,因为abstract修饰的方法是必须在其子类中实现(覆盖),才能以多态方式调用,以上修饰符在修饰一个方法时它的子类都修改不了这个方法,final是不可以覆盖,private是不能够继承,所以也就不能覆盖,static是可以覆盖的,但是在调用时会调用编译时类型的方法,因为调用的是父类的方法,而父类的方法又是抽象的方法,又不能够调用,所以上述的修饰符不能放在一起。
抽象类的目的就是为了让其子类实现它本身的方法!