Java中有一些或常用,或不常用,但却不得不知关键字,本篇文章将讨论这些关键字的作用。

transient

transient关键字可能用的不是那么频繁,但却是一个很重要的关键字,它的作用是在对象序列化过程中体现的。如果一个类的变量被transient修饰,那么这个对象在序列化过程中,不会序列化这个变量,同时,在反序列化过程中,也不会去反序列这个变量。

笔者有时候会遇到这样一种情况,可能是因为笔者经验不足。在使用JPA进行数据库操作的时候,某些对象在序列化到数据库的时候,会有一些变量并不想要去存到数据库中,这个时候就可以用transient来修饰变量,解决这个问题。

instanceof

这个关键字就比较常用了,尤其是在一些大量采用反射的框架中,在需要判断某一个对象是否是某一类型(可以是接口,父类,父类的父类等)的时候,可以采用这个关键字。比如说,判断UserServiceImpl是否是UserService接口的类型,可以这样做:

if(userServiceImpl instanceof UserService){
//do some thing
}

  

笔者在自己的IOC框架中就用到了这个关键字,在将进行接口依赖注入的时候,使用该关键字判断容器中是否有相应接口的实例,然后将实例注入。

final

final关键字可以用来修饰变量、类、方法。

final修饰的变量,一旦在被赋值之后,将不能再次赋值,也就是说这个变量在后续的使用过程中,只能采取读的方式,变量具有不可变性。这里需要做一下区别,就是基本数据类型和引用数据类型在被final修饰后的情况。基本数据类型的不可变性往往体现在变量的值永远不会再变化,而引用类型则不是,引用类型的不变性是体现在引用的不变性。引用类型的变量一旦为一个引用数据类型赋值,那么变量就会指向对象在内存中的地址,final关键字修饰过后,变量所指向的地址就不能再被改变,但对象本身的状态还是可以改变的,可以看一下下面这段代码:

final int i = 0;

i = 1; //error

final int[] j = new int[10];

j = new int[20]; //error

j[0] = 1; //right

  

final修饰过的变量有一个好处,即它会是线程安全的。在Java中,final变量会进行指令重排序,确保所有线程在访问该变量的时候,变量已经被初始化过了。虽然在旧的版本中,会出现对象引用在构造函数中“逸出”的情况,但自从jsr133增强了final的内存语义之后,所有线程在看到final变量时,看到的都是已经初始化之后的值。final变量初始化之后又不会再改变,所以它是线程安全的。

final修饰的类,将具有不可继承性,即不能有子类。

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

static

static关键字可以用来修饰变量,方法。static修饰的变量和方法,将只属于类,可以通过类名.变量名(或方法名)的方式来引用,使用方式如下:

public class Demo{
public static int i = 0;
public static void hello(){
System.out.println(i);
}
}
//可以这么访问
int j = Demo.i;
Demo.hello();

  

其中static变量将会常驻内存中,不会在垃圾回收的过程中被回收掉,甚至会成为垃圾回收中的GC Root。static变量的初始化只能在static代码块中执行,它先于构造函数执行,使用方式如下:

 
public class Demo{
public static int i;
static{
i=0;
}
}

  

volatile

这个关键字是一个很重要的关键字,可以修饰变量。如果要进行多线程编程,那么这个关键字将会是一个重点。因为它有两个特性:可见性和原子性。

可见性是指,对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。volatile变量再被修改的时候,如果有其他线程读取了该变量,它会通知其他线程变量已经实现,重新读取最新的值。

原子性是指,对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种符合操作不具有原子性。其原理是因为volatile具有一定程度上的锁的语义,但并没有像synchronized那么重量级。

它的使用方式如下,以最常见的单例模式为示例。一般来说,我们比较喜欢使用double-check的方式实现单例模式,因为它既可以做延迟初始化又可以保证线程的安全,代码如下:

public class Demo{
private static Demo instance;
private Demo(){ }
public static Demo getInstance(){
if(instance == null){ //第一步,判断instance是否为null
synchronized(Demo.class){ //第二步,加锁
if(instance == null){ //第三步,再判断instance是否为null
instance = new Demo(); //第四步,实例化
}
}
}
return instance;
}
}

  

这段代码看似线程安全,但却存在一个很大的缺陷。如果有两个线程,两个线程都执行到了第二步,一个线程在拿到锁并进行实例化之后,另一个线程继续进入同步代码块,这个线程读到的instance可能还是null,之后就会导致线程安全问题。为什么会这样呢?其实在Java中,对象的实例化可以分为以下三个子阶段:

memory = allocate();  // 1:分配对象的内存空间
ctorInstance(memory); // 2:初始化对象
instance = memory;  // 3:设置instance指向刚分配的内存地址

  

由于Java会对指令进行重排序,可能会导致3和2的指令顺序相反,即

memory = allocate();  // 1:分配对象的内存空间
instance = memory;  // 3:设置instance指向刚分配的内存地址
ctorInstance(memory); // 2:初始化对象

  

问题就出现在这里,如果线程一的还没有执行完初始化对象这个子阶段,另一个线程将会认为这个实例为空,将会导致线程安全。要解决这个问题,可以加上volatile关键字,代码如下:

public class Demo{
private volatile static Demo instance;
private Demo(){ }
public static Demo getInstance(){
if(instance == null){ //第一步,判断instance是否为null
synchronized(Demo.class){ //第二步,加锁
if(instance == null){ //第三步,再判断instance是否为null
instance = new Demo(); //第四步,实例化
}
}
}
return instance;
}
}

  

volatile会禁止指令重排序,使得这个操作变得具有原子性,这样线程就可以读取到变量最新的状态,保证了线程的安全。

synchronized

synchronized可以说是一个非常常见的关键字了,在jdk1.6之前,它被称为重量级锁,不过jdk1.6之后经过优化和升级,已经没有那么夸张了。通过使用synchronized可以使代码同步,解决多线程环境下的一些问题。一般来说,它可以使用下面三种形式的锁:

  • 对于普通同步方法,锁是当前实例对象。
  • 对于静态同步方法,锁是当前类的Class对象。
  • 对于同步方法块,锁是synchronized括号里配置的对象。

当一个线程试图去访问同步代码块时,它必须先得到锁,退出或抛出异常时必须释放锁。其底层原理是通过Monitor对象来实现方法同步和代码块同步。代码块同步是使用monitorenter和monitorexit指令实现的,方法同步是使用另一种方法实现的。

monitorenter方法是在编译后插入到同步代码块的开始位置,而monitorexit是插入到方法结束处和异常处,JVM要保证每个monitorenter都有一个monitorexit与之配对。任何对象都有一个monitor与之关联,当且一个monitor被持有后,它将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor所有权,即尝试获得对象的锁。

 

Java中需要知道的关键字的更多相关文章

  1. Java中的两个关键字——super、this

    Java中的两个关键字——super.this 神话丿小王子的博客主页 一.super super 是java中方的一个关键字,用它可以引用父类中的成员: super可用于访问父类中定义的属性 sup ...

  2. Java中的50个关键字

    form:http://blog.csdn.net/luoweifu/article/details/6776240 Java中的50个关键字 关键字也称为保留字,是指java语言中规定了特定含义的标 ...

  3. 【每周一译】愚蠢的指标:Java中使用最多的关键字

    此翻译纯属个人爱好,由于水平所限,翻译质量可能较低.网络上可能存在其它翻译版本,原文地址:http://blog.jooq.org/2013/08/26/silly-metrics-the-most- ...

  4. Java中的标识符和关键字

    1.标识符 含义:标识符用于给程序中的类.变量.方法命名的符号. 组成:数字(0-9).字母(a-z)(A-Z).下划线(_).美元符号$. 命名规则:1.数字不能够作为命名符号的开头 2.不能够使用 ...

  5. java中this和super关键字的使用

    这几天看到类在继承时会用到this和super,这里就做了一点总结,与各位共同交流,有错误请各位指正~ this this是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针. this ...

  6. Java中this和super关键字

    今天练习到Java中的this和super关键字,我有如下总结: 1.子类继承父类,子类初始化对象,必须先调用父类构造方法,因为随时有可能要使用父类的成员变量. 2.get和set方法只是对成员变量进 ...

  7. 1.3 Java中的标识符和关键字

    1.标识符 含义:标识符用于给程序中的类.变量.方法命名的符号. 组成:数字(0-9).字母(a-z)(A-Z).下划线(_).美元符号$. 命名规则:1.数字不能够作为命名符号的开头 2.不能够使用 ...

  8. java中的标识符、关键字、保留字

    Java中关键字(keyword)和保留字(reservedword) Keyword :Java的关键字对java的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序的结构等. Reserv ...

  9. 简析--Java中常见的一些关键字的解析

    在Java开发中我们经常会用到一些关键字,关键字的定义很有意思"Java事先定义好的,具有特殊含义的单词",那么我们怎么来用好关键字呢?下面我们对一些常见的关键字进行分析和比较; ...

随机推荐

  1. C# Conversion Keywords

    主要是解决类与其他不同数据类型的转换 类于类的显式转换: explicit  public static explicit operator B(A a) { return new B(){a.... ...

  2. 利用js实现 禁用浏览器后退

    现在很多的内部系统,一些界面,都是用户手动点击退出按钮的.但是为了避免,用户误操作 点击浏览器后退,或者用鼠标手势后退什么的.容易出现误操作.        所以在有些页面上,适当的禁用浏览器的后退, ...

  3. Python2.7和3.7区别

    区别一:print语法使用 Python2.7   print语法使用   >>> print "Hello Python" Python3.7   print语 ...

  4. Gradle 下载的依赖包在什么位置?

    Mac系统默认下载到:/Users/(用户名)/.gradle/caches/modules-2/files-2.1Windows系统默认下载到:C:\Users\(用户名)\.gradle\cach ...

  5. HeadFirst学习笔记-1. 设计模式入门

    1.概念 在开始学习前,我们先了解一些概念,方便我们接下来的学习. OO基础 抽象 继承 多态 封装 OO原则 封装变化 多用组合,少用继承 针对接口编程,不针对实现编程 设计模式 设计模式(Desi ...

  6. [原创]大数据:布隆过滤器C#版简单实现。

    public class BloomFilter { public BitArray _BloomArray; public Int64 BloomArryLength { get; } public ...

  7. MonggoDB(二)

    分组聚合 如果你有数据存储在MongoDB中,你想做的可能就不仅仅是将数据提取出来这么简单,可能需要对数据进行分析并加以利用. 聚合框架:可以使用多个构件创建一个管道,上一个构件的结果传给下一个构件. ...

  8. CheckedTextView文字不居中的问题

    问题:CheckedTextView设置了android:gravity="center",但是不居中 解决方法:添加属性android:textAlignment="c ...

  9. Relativelayout和LinearLayout对比分析

    分析之前先了解下View的绘制流程 首先view在windows中的布局样式如下图: view绘制在windows,windows与DecoverView的交互在VIewRoot中进行. view绘制 ...

  10. APIO2018 被屠记

    占坑 day0 10:40才起床 感觉一点也不好 下午去了趟80中拿牌子然而没有到,白浪费我颓废时间. day0.5 早上第一课讲二分凸优化,有点瞌睡 第二课讲匹配相关,感觉这篇文章涵盖了大部分内容 ...