Java中三个非常重要、的修饰符:static、final 和 abstract
static:静态修饰符
能够修饰属性、方法、初始化代码快、内部类,不能修饰局部变量和类
1)本类所有对象共享
2)可以直接通过 类名.静态属性 访问
实际编程中应尽量用类名直接调用静态属性,这样能把静态变量和实例变量加以区分,从而提高代码的可读性。
1)全类公有的方法
2) 可以使用类名.方法名直接调用
3)静态方法与对象无关,不依赖对象所以无法使用this或super关键字
4)静态方法只能访问静态成员(非静态方法中,无论方法和属性是否是静态的都能够访问)
5)静态方法/属性可以被继承
6)静态方法只能被静态方法覆盖且没有多态
使用多态时:子类对象给父类引用赋值 ,运行时会调用子类覆盖的方法,但是当对一个引用调用静态方法时,等同于对这个引用类型调用静态方法。
例:
Super sub = new Sub();
sub.m1();
//由于m1()方法为静态方法,所以当使用 引用.方法名调用时 sub.m1() 会自动被替换为 引用类型.方法名 Super.m1();
类加载的时机与过程:
1)当JVM第一次使用某个类时(new 对象、调用静态成员、使用子类)需要通过ClassPath查找该类的.class文件
2)将.class文件中保存的该类信息(属性、方法….)加载到内存中(内存中的模板)
3)加载时会初始化该类的必要信息,初始化静态属性、静态方法、执行该类静态初始化代码块等。
加载时机1:访问静态成员
加载时机2:创建对像
加载时机3:创建子类对象或加载子类时
注意:
1)一次运行中类加载只会执行一次
2)只声明引用,不会进行类加载
作用:在类加载时对静态属性进行初始化的工作
1)如果有需要先加载父类(初始化父类静态属性、执行父类静态初始化代码块)
2)初始化本类静态属性,执行本类静态初始化代码块
final属性能够修饰变量、方法 和类。变量 包括属性、局部变量
用final修饰的变量和属性一般称之为常量。
定义:一旦赋值,其值不能改变的变量
对于基本类型的常量而言,所谓的不改变妹纸的是不能改变变量的值。
对于对象类型的常量而言,保存的是对象的地址,一旦指向某个对象以后,不能指向其他对像,但是完全可以改变这个对象的属性。
对于final属性而言,虚拟机在分配完空间以后,将不会为其赋予默认值,,从而把为final属性赋值的机会留给程序员。
但Java语言规定,一旦对象创建完成,则对像的final类型的属性就不能赋值了,故,对于 final 类型的实例变量,能够赋值的时机,就是在分配空间之后,对象创建完成之前。
赋值机会:
1)声明同时赋值(如果该属性不是static修饰的则除了浪费空间外无任何意义)
2)构造方法赋值(如果有多个构造方法,则多个构造方法都得对final属性赋值,这样就保证了当一个对象被创建的时候,无论调用的是哪一个构造方法,final属性都被正确地赋值了。)
3)初始化代码块,静态初始化代码块。
final修饰的方法不允许被覆盖。
final修饰方法意义在于:防止子类在定义新方法时,造成不经意的重写。
Eg:
class Super{
public void m1();
public final void m2(){}
}
class Sub extends Super{
public void m1(){} //能够覆盖父类方法
public void m2(){} //编译出错!无法覆盖父类方法
}
上面的代码中,Super类中有两个方法,m1是普通方法,能被子类覆盖,m2方法被final修饰,故子类方法不能够覆盖父类方法。
用final修饰类表示这个类不能被继承。
Eg:
final class MyClass{}
class Sub extends MyClass{}
//编译出错,无法继承一个final类
final修饰方法和final修饰类的区别:
1)一个类有final方法,这个类能够被继承,但是无法覆盖final方法
2)一个类如果本身就是final的,则这个类无法被继承,且它所有的方法都无法被覆盖。
注意:
1)常用 public static final (公开静态常量) 用于保存一些常用且不可变的值。
2)final 修饰基本类型变量- -值不可变
3)final 修饰对象引用- - 地址不可变
4)final static 可以连用,private、static、final 均不能与abstract连用。、
5)JDK中一些基础类库被定义为final,例如:String、Math、Integer、Double等
6)static final修饰的成员变量称为常量,必须声明同时初始化,不可被改变。
static final 常量会在编译期被替换。
Eg:
class Foo{
public static final int NUM = 100;
}
class Goo{
public static void main(String[] args){
System.out.println(Foo.NUM);
//编译代码时,JVM会自动替换为:System.out.println(Foo.NUM);
}
}
// static final 常量Foo.NUM在编译时会被常量100替换,在运行Goo类时,Foo类不需要被载入
修饰 类 、方法
abstract修饰类,则这个类就成为了一个抽象类,抽象类的特点是:
抽象类只能用来声明引用,不能用来创建对象。
虽然抽象类不能创建对象,但是抽象类可以被继承,才从而创建子类对象。
abstract class MyAbstract{
public void m(){ }
}
class MySubClass extends MyAbstract{ }
public class TestAbtract1{
public static void main(String[] args){
MyAbstract ma; //可以声明抽象类的引用类型
// ma = new MyAbtract(); 错误的代码
ma = new MySubClass(); //但是可以创建子类对象
}
}
//写抽象类的目的就是:为了能够让子类继承
用 abstract 修饰的方法称为抽象方法。
定义一个方法分两个部分:方法的声明,方法的实现。
抽象方法指的是:一个只有声明,没有实现的方法。对于抽象方法来说,方法的实现部分用一个分号来代替。
Eg:
class MyAbstract{
public abstract void m1(); // 抽象方法
public void m2(){} //普通方法
//抽象方法的实现留给子类来完成
注意
1)抽象类可以没有抽象方法,有抽象方法的类一定是抽象类
2)子类覆盖父类的抽象方法时,并不是用一个特殊的实现替换一个一般的实现,而是在父类没有方法实现的情况下,子类给出一个方法的实现。
3)子类继承抽象类,要么也成为抽象类,要么就必须实现抽象类中所有的抽象方法。
1)使用子类实现父类中所有的抽象方法
2)使用多态创建对象
方法的声明是共性,方法的实现是特性。
使用抽象方法,就可以让我们把方法的声明放在父类中,把方法的实现留在子类中。这样一方面,能够很好的体现出“共性放在父类”这个基本的原则,另一方面,用父类类型的引用又可以调用这些方法,并不影响多态语法的正常使用。