Java基础:关键字

在平时编码时,我们可能只注意到static、final、volatile等关键字的使用,而忽略了它们更深层次的意义。

static关键字

概述

当static修饰类的属性或者方法时,那么就可以在没有创建对象的情况下使用该属性或方法。

静态块也是static的一个应用,用于初始化类时的一些操作。

 

静态方法和静态变量

被static修饰后的属性或者方法,使用时不需要new一个类,用类.属性名或方法名访问。

比如java.lang.Math就存放了很多静态资源,可以直接使用Math.random()来获取随机数。

注:

1、非静态方法是可以直接访问静态资源的,静态方法是不能直接引用非静态资源的。

 

2、.静态属性和方法可以通过.属性名或方法名,而且,该类的对象也是访问静态属性和变量的。

 

3、Java的语法规定,static不能修饰局部变量。没有为什么,这就是规定。

 

示例:

编译时就会报错,main方法是静态方法,变量i是非静态的。解决方案:一是将变量i加上static修饰,二是如上采用实例访问非静态属性。

  

为什么非静态方法可以访问静态资源,而静态方法不能访问非静态资源呢?

 从类加载机制上讲,静态资源是类初始化的时候加载的,然后非静态资源是new一个该类的对象的时候加载的。

 

这就带来一个疑问:

加载类时默认先加载静态资源的,当new一个对象之后,才会加载其他资源,所以在new对象之前,静态资源是不知道类有哪些非静态资源的,

但是当对象new出来之后,该类的所有属性和方法都知道。

 

静态块

静态块也是在类加载的时候执行,而且只执行一次。

静态块注意要点:

  1. 静态资源的加载顺序严格按照静态资源的定义顺序加载的;
  2. 静态块,对于定义在它之后的静态变量,可以赋值但不能访问。

示例:

package main.keyword;


public class StaticDemoTest2 extends ParentClass{
    public static String subStaticField = "子类静态变量";
    public String subField = "子类非静态变量";
    public static StaticClass staticClass = new StaticClass("子类");

    static {
        System.out.println("子类 静态块初始化");
    }

    {
        System.out.println("子类 [非]静态块初始化");
    }

    public StaticDemoTest2(){
        System.out.println("子类构造器初始化");
    }
    public static void main(String[] args) {
        new StaticDemoTest2();
    }
}

class ParentClass{
    public static String parentStaticField = "父类静态变量";
    public String parentField = "父类[非]惊天变量";
    public static StaticClass staticClass = new StaticClass("父类");

    static {
        System.out.println("父类 静态块初始化");
    }

    {
        System.out.println("父类 [非]静态块初始化");
    }

    public ParentClass(){
        System.out.println("父类 构造器初始化");
    }
}

class StaticClass{
    public StaticClass(String name){
        System.out.println(name+" 静态变量加载");
    }
}

 

执行结果:

父类 静态变量加载
父类 静态块初始化
子类 静态变量加载
子类 静态块初始化
父类 [非]静态块初始化
父类 构造器初始化
子类 [非]静态块初始化
子类构造器初始化

 

 

 下面是总结类加载流程,可以对照着这个流程,可以再重新看一下上面的例子,会有新的理解。

1. 加载父类静态
    1.1 为静态属性分配存储空间并赋初始值
    1.2 执行静态初始化块和静态初始化语句(从上至下)

2. 加载子类静态
    2.1 为静态属性分配存储空间
    2.2 执行静态初始化块和静态初始化语句(从上至下)

3. 加载父类非静态
    3.1 为非静态块分配空间  
    3.2 执行非静态块

4. 加载子类非静态
    4.1 为非静态块分配空间  
    4.2 执行非静态块

5. 加载父类构造器
    5.1 为实例属性分配存数空间并赋初始值
    5.2 执行实例初始化块和实例初始化语句
    5.3 执行构造器内容

6. 加载子类构造器
    6.1 为实例属性分配存数空间并赋初始值
    6.2 执行实例初始化块和实例初始化语句
    6.3 执行构造器内容

 

 示例2:

package main.keyword;

public class StaticDemoTest3 {
    Person person = new Person("TestStaticLoad");//4
    static{
         System.out.println("TestStaticLoad static");//1
    }

     public StaticDemoTest3() {
         System.out.println("TestStaticLoad constructor");//5 
     }

     public static void main(String[] args) {
         new God();
     }
}
 class Person{
     static{
        System.out.println("person static");//3
     }
     public Person(String str) {
         System.out.println("person "+str);//4 -TestStaticLoad  6-God
    }
 }

class God extends StaticDemoTest3 {
     Person person = new Person("God"); //6
     static{
         System.out.println("God static");//2
     }

     public God() {
         System.out.println("God constructor");//7
     }
 }

 

执行结果:

TestStaticLoad static
God static
person static
person TestStaticLoad
TestStaticLoad constructor
person God
God constructor

 

 

一步一步地解析:

  • 在StaticDemoTest3的main方法中,执行了new God(),那就就会去加载God类,在这之前会先加载它的父类:TestStaticLoad
  • 第一步:加载父类静态,执行System.out.println("TestStaticLoad static");  输出:TestStaticLoad static,
  • 第二步:加载子类静态,执行System.out.println("God static");,输出God static
  • 第三步:加载父类非静态,Person person = new Person("TestStaticLoad");,这里实例化了Person 对象,那就会去加载Person类。
  • 第四步:加载Person类,首先看有没有父类,没有。好,加载静态块,执行System.out.println("person static");输出person static
  • 第五步:Pernson类静态块加载完毕,加载构造器,new一个Person对象,输出person TestStaticLoad。这时TestStaticLoad 类非静态块加载完毕
  • 第六步:加载God 父类(TestStaticLoad )构造器,输出TestStaticLoad constructor
  • 第七步:God父类全部加载完毕,加载God的非静态块,Person person = new Person("God");这时又会去加载Person类,需要注意的是,static块只加载一次,因为之前在父类已经加载过了,这时只加载构造器,输出person God
  • 最后一步:加载本类God 的构造器,输出God constructor。

 static关键字总结:

  • static关键字 可以再没有创建对象的时候进行调用类的元素
  • static 可以修饰类的方法 以及类的变量, 以及静态代码块
  • 被static修饰的成为静态方法,静态方法是没有this的,静态方法不能访问同一个类中的非静态方法和静态变量,但是非静态方法 可以可以访问静态变量
  • 类的构造器 也是静态的
  • 静态变量被所有的内存所有的对象共享,在内存中只有一个副本。非静态变量是是在创建对象的时候初始化的,存在多个副本,每个副本不受影响。
  • static 静态代码块,static 代码块可以放在类中的任何地方,类加载的时候会按照static代码块的顺序来加载代码块,并且只会执行一次。
  • 静态成员变量虽然独立于对象,但是不代表不可以通过对象去访问,所有的静态方法和静态变量都可以通过对象访问。
  • static不可以修饰局部变量(java语法规定)

 

 final关键字

总结:

  • 在java中final可以用来修饰类、方法、和变量(包括成员变量和局部变量)
  • final修饰类的时候,这个类将永远不会被继承,类中的成员方法也会被隐式的修饰为final(尽量不要用final修饰类)
  • final修饰的方法不可重写,final修饰的属性不能继承
  • 如果不想方法被继承,可以用final修饰,private也会隐式的将方法指定为final
  • final修饰变量的时候,如果是基本类型的变量,那么他的值在初始化之后就不能更改
  • final在修饰对象的时候,在其初始化之后就不能指向其他对象
  • 被static和final修饰的变量,将会占据一段不能改变的存储空间,将会被看做编译期常量
  • 不可变的是变量的引用而非引用指向对象的内容

 示例:

public class TestFinal {
    public static void main(String args[]){
        String a = "test1";
        final String b = "test";
        String d = "test";
        String c = b + 1; 
        String e = d + 1;
        System.out.println((a == c));
        System.out.println((a.equals(e)));
    }
}

 

执行结果:

 

true
true

 

 因为final变量是基本类型以及String时,在编译期的时候就把它当做常量来使用,不需要在运行时候使用。“==”是对比两个对象基于内存引用,如果两个对象的引用完全相同,则返回true,所以这里b是用访问常量的方式去访问,d是链接的方式,所以a的内存引用和c的内存引用是相等的,所以结果为true,a和e两个对象的值是相等的,所以结果为true。

 

示例:

在编译的时候,或报错, 不能指向一个final对象。

 

volatile关键字

具体定义请看这里

 应用

线程池(ThreadPoolExecutor)中一些变量的定义:

 

   private volatile ThreadFactory threadFactory;

    private volatile RejectedExecutionHandler handler;

    private volatile long keepAliveTime;

    private volatile boolean allowCoreThreadTimeOut;

    private volatile int corePoolSize;

    private volatile int maximumPoolSize;

 

 

可以看到线程工厂threadFactory,拒绝策略handler,没有任务时的活跃时间keepAliveTime,keepAliveTime的开关allowCoreThreadTimeOut,核心池大小corePoolSize,最大线程数maximumPoolSize

都是被volatile修饰中,因为在线程池中有若干个线程,这些变量必需保持对线程可见性,不然会引起线程池运行不正确。

 

assert关键字

assert断言

在目前的java编码中,是不推荐使用的,这里只是稍微了解一下:

使用方式:

 
1、assert <boolean表达式>
如果<boolean表达式>为true,则程序继续执行。
如果为false,则程序抛出AssertionError,并终止执行。
 
2、assert <boolean表达式> : <错误信息表达式>
如果<boolean表达式>为true,则程序继续执行。
如果为false,则程序抛出java.lang.AssertionError,并输入<错误信息表达式>。
 
如果要开启断言检查,则需要用开关-enableassertions或-ea来开启,java中IDE工具默认支持开启-ea
 
示例:
public class AssertDemoTest {
    public static void main(String[] args) {
        assert true;
        System.out.println("断言1成功执行");
        System.out.println("-----------");
        assert false:"error";
        System.out.println("断言2成功执行");
    }
}

 执行结果:

断言1成功执行
-----------
断言2成功执行

 

synchronized关键字

关于锁关键字,有以下几个总结:

  • 无论synchronized关键字加在方法上还是对象上,如果它作用的对象是非静态的,则它取得的锁是对象;如果synchronized作用的对象是一个静态方法或一个类,则它取得的锁是对类,该类所有的对象同一把锁。
  • 每个对象只有一个锁(lock)与之相关联,谁拿到这个锁谁就可以运行它所控制的那段代码。
  • 实现同步是要很大的系统开销作为代价的,甚至可能造成死锁,所以尽量避免无谓的同步控制。 

 

transient关键字

 Java中,一个类想要序列化,可以通过实现Serilizable接口的方式来实现,实现该接口之后,该类所有属性和方法都会自动序列化。

但是如果属性或方法被transient修饰,那么将不会被序列化。

 

转载于:https://www.cnblogs.com/dudu2mama/p/11083536.html

评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符 “速评一下”
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页