1、static修饰符

1.1 成员变量

成员变量被分为类变量和实例变量两种,定义成员变量时不带static修饰的就是实例变量,带static的就是类变量(静态变量)。

类成员变量作为类的一个成员,与类本身共存亡,实例变量则是实例成员的变量,与实例共存亡。

只要类存在,类变量就可以被访问。格式如下:

类.类变量

只要实例存在,就可以访问实例变量。

实例.实例变量

实例变量也可以访问类变量。

实例.类变量

但由于实例并不拥有类变量,因此访问的类变量不属于该实例,而是属于该实例访问的对应的类。如果一个实例修改类变量,其他实例访问的就是被修改的类变量。

对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序依次是(静态变量、静态初始化块)>(变量、初始化块、构造器。)测试代码如下:

public class InitialOrderTest {
// 静态变量
public static String staticField = "静态变量";
// 变量
public String field = "变量";
// 静态初始化块
static {
System.out.println(staticField);
System.out.println("静态初始化块");
}
// 初始化块
{
System.out.println(field);
System.out.println("初始化块");
}
// 构造器
public InitialOrderTest() {
System.out.println("构造器");
}
public static void main(String[] args) {
new InitialOrderTest();
}
}

输入结果:

静态变量
静态初始化块
变量
初始化块
构造器

对static关键字来说,类成员(成员变量、方法、初始化块、内部类和内部枚举)不能访问实例成员。因为类成员是属于类的,可能出现类成员已经初始化完成,但实例成员还不曾初始化的情况。

2、final修饰符

final修饰变量时,表示变量一旦获得了初始值就不能改变,final既可以修饰成员变量(包括类变量和实例变量),也可以修饰局部变量、形参。

2.1 final成员变量

成员变量随类初始化或对象初始化而初始化。当类初始化时系统会为类变量分配内存并分配默认值;当创建对象时,系统会为该对象的实例变量分配内存并分配默认值。

final修饰的成员变量必须由程序员显式地指定初始值。

执行静态初始化块时可以对类变量赋初始值,当执行普通初始化块、构造器时可对实例变量赋初始值。

如果普通初始化块已经为某个实例变量指定了初始值,则不能再在构造器中为该实例变量赋初始值;final修饰的类变量要么在定义时指定初始值,要么在静态初始化块中指定初始值。

final成员变量在显式初始化前不能直接访问,但可以通过方法来访问,这基本可以判断是java设计的缺陷。



class Test {

    int test=10;
    /*构造器执行顺序在初始化块后,输出 10*/
    public Test() {
        System.out.println(test);
    }
    /*调用 showa() 函数输出未定义的final变量,输出 a= 0 ,最早执行*/
    {
        showa();
        a = 5;
        //System.out.println(a); //非法前向引用
    }
    /*上面的初始化块已经执行过,a已经被赋值,输出 a= 5*/
    {
        showa();
    }
    final int a;

    public void showa() {
        System.out.println("a="+a);
    }

}

public class TestDemo {

    public static void main(String[] args) {
        new Test();
    }
}

因为在类初始化的时候,就规定了,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的,只能赋值,不能访问。

系统不会对局部变量进行初始化,局部变量必须由程序员显式初始化。

final 变量的初始化方式:

public class FinalTest1 {
    //-----------------成员变量------------------//
    //初始化方式一,在定义变量时直接赋值
    private final int i = 3;
 
    //初始化方式二,声明完变量后在构造方法中为其赋值
    //如果采用用这种方式,那么每个构造方法中都要有j赋值的语句
    private final int j;
 
    public FinalTest1() {
        j = 3;
    }
 
    //如果取消该构造方法的注释,程序就会报错,因此它没有为j赋值
    /*public FinalTest1(String str) {
    }*/
 
    //为了方便我们可以这样写
    public FinalTest1(String str) {
        this();
    }
 
    //下面的代码同样会报错,因为对j重复赋值
    /*public FinalTest1(String str1, String str2) {
        this();
        j = 3;
    }*/
 
 
    //初始化方式三,声明完变量后在构造代码块中为其赋值
    //如果采用此方式,就不能在构造方法中再次为其赋值
    //构造代码块中的代码会在构造函数之前执行,如果在构造函数中再次赋值,
    //就会造成final变量的重复赋值
    private final int k;
 
    {
        k = 4;
    }
 
    //-----------------类变量(静态变量)------------------//
    //初始化方式一,在定义类变量时直接赋值
    public final static int p = 3;
 
    //初始化方式二,在静态代码块中赋值
    //成员变量可以在构造函数中赋值,但是类变量却不可以。
    //因此成员变量属于对象独有,每个对象创建时只会调用一次构造函数,
    //因此可以保证该成员变量只被初始化一次;
    //而类变量是该类的所有对象共有,每个对象创建时都会对该变量赋值
    //这样就会造成变量的重复赋值。
    public final static int q;
 
    static {
        q = 3;
    }
}

2.2 final修饰基本类型变量与引用类型变量的区别

当使用基本类型变量时,不能对基本类型变量重新赋值,因此基本数据变量不能被改变。

但对于引用变量来说,它仅仅保存了一个引用,final只保证引用的地址不会改变,但是可以改变引用类型变量所引用对象的内容。

2.3 final 方法

final修饰的方法不能被重写,如果不想让子类重写父类某个方法,可以使用final修饰。

使用final修饰一个private访问权限的方法,依然可以在其子类里定义与该方法具有相同方法名、相同形参列表、相同返回值类型的方法。

final修饰的方法仅仅是不能被重写,但是可以被重载。

2.4 final 类

final修饰的类不能被继承。

3、抽象类

java.jpg

3.1 抽象方法和和抽象类

抽象方法和抽象类必须使用abstract修饰符定义,有抽象方法的类只能被定义成抽象类,但是抽象类里可以没有抽象方法。

抽象方法和抽象类规则如下:

  1. 抽象类必须使用abstract修饰,抽象方法一样要用abstract修饰,抽象方法不能有方法体。
  2. 抽象类不能被实例化,不能使用new关键字调用抽象类的构造器创建抽象类的实例。
  3. 抽象类可以包含成员变量、方法(普通和抽象方法都可以)、构造器、初始化块、内部类(接口、枚举)5种成分。抽象类的构造器主要用于被子类调用。
  4. 含有抽象方法的类(包括直接定义了一个抽象方法;或直接继承了一个抽象父类,但没有完全实现父类包含的抽象方法;或实现了一个接口,但没有完全实现接口包含的抽象方法)只能被定义为抽象类。

当使用abstract修饰类时,表明这个类只能被继承,使用abstract修饰方法时,表明这个方法必须被子类实现(重写)。final修饰的类不能被继承,final修饰的方法不能被重写,因此 final 和 abstract 不能同时使用。

当使用static修饰一个方法时,表明这个方法属于该类本身,就是说可以直接通过类名调用这个方法,如果该方法被定义成抽象方法,再使用类名直接调用该方法会出现错误。因此 abstract 和 static 不能同时使用。

abstract不能修饰成员变量,也不能修饰构造器,即没有抽象变量和抽象构造器的说法。

注意:

static 和 abstract不是绝对的互斥,虽然不可以同时修饰同一个方法,但是可以同时修饰一个内部类。

4、 java 接口

抽象类是从多种类中抽象出来的模板,如果将这种抽象进行的更彻底,则可以提炼出一种更加特殊的“抽象类” --接口(interface)。

4.1 接口的定义

Java 9中的接口定义的基本语法如下:

[修饰符] interface 接口名 extends 父接口1,父接口2...
{
    零个到多个常量定义....
    零个到多个抽象方法定义...
    零个到多个内部类、接口、枚举定义...
    零个到多个私有方法、默认方法或类方法定义...
}

说明:

  1. 修饰符可以是 public 或者省略;
  2. 接口名应与类名采用相同命名规则,从语法角度来看,只有符合标记符规范即可,为了增加可读性,应该使用多个有意义的单词连缀而成,每个首字母大写,中间不用分隔符;
  3. 一个接口可以有多个直接父类接口,但接口只能继承接口,不能继承类。

由于接口是一种规范,因此接口里不能包含构造器和初始化块定义。接口里可以包含成员变量(只能是静态常量)、方法(只能是抽象实例方法、类方法、默认方法和私有方法)、内部类(包含内部接口、枚举)定义。

在接口里定义的成员变量,不管是否使用 public static final 修饰符,接口里的成员变量总是使用这三个修饰符来修饰。由于接口里没有 构造器和初始化块,因此接口里定义的成员变量只能在定义时指定默认值。

接口里的方法只能是抽象方法、类方法、默认方法和私有方法,因此如果不是定义默认方法、类方法或私有方法,系统就自动为普通方法加 abstract 修饰符;接口里的普通方法总是使用 public abstract来修饰。

接口里的普通方法不能有方法实现(方法体);但类方法、默认方法、私有方法都必须有方法实现(方法体)。

4.2 接口的继承

接口的继承与类继承不一样,接口的继承支持多继承,即一个接口可以有多个直接父接口;一个类可以实现一个或多个接口,继承使用 extends 关键字,实现则使用 implements 关键字,

一个接口继承多个父接口时,多个父接口排在 extends 关键字后,多个父接口之间用英文逗号(,)隔开。

一个类实现了一个或多个接口后,这个类必须完全实现这些接口里所定义的全部抽象方法(也就是重写这些抽象方法);如果未全部实现父类继承的抽象方法,该类也必须定义成抽象类。

4.3 接口和抽象类

接口可抽象类很像,都具有下面的特牲。

  1. 接口和抽象类都位于继承树的顶端,都不能被实例化,用于被其他类实现和继承。
  2. 接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。

它们在用法上的差别如下:

  1. 接口里只能包含抽象方法、静态方法、默认方法和私有方法,普通方法不能提供方法实现;抽象可以包括普通方法。
  2. 接口里只能定义静态变量;抽象里既可以定义普通变量,也可以定义静态变量。
  3. 接口里不含构造器和初始化块;抽象类里可以包含构造器和初始化块,抽象类里的构造器不是为了创建对象,而是为了给子类调用来完成属于抽象类的初始化操作。
  4. 一个类最多只能有一个直接父类,包括抽象类;但一个类可以实现多个接口,通过实现多个接口弥补Java 单继承的不足。

StackOverflowError

StackOverflowError:栈溢出错误,如果一个线程所需用到栈的大小>配置允许最大的栈大小,那么jvm就会抛出StackOverflow。

一般是由于程序出现死循环导致的。

本文导出文档下载地址:Download

Last modification:November 21st, 2020 at 10:18 pm
如果觉得我的文章对你有用,请随意赞赏