Java中需要知道的关键字
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中需要知道的关键字的更多相关文章
- Java中的两个关键字——super、this
Java中的两个关键字——super.this 神话丿小王子的博客主页 一.super super 是java中方的一个关键字,用它可以引用父类中的成员: super可用于访问父类中定义的属性 sup ...
- Java中的50个关键字
form:http://blog.csdn.net/luoweifu/article/details/6776240 Java中的50个关键字 关键字也称为保留字,是指java语言中规定了特定含义的标 ...
- 【每周一译】愚蠢的指标:Java中使用最多的关键字
此翻译纯属个人爱好,由于水平所限,翻译质量可能较低.网络上可能存在其它翻译版本,原文地址:http://blog.jooq.org/2013/08/26/silly-metrics-the-most- ...
- Java中的标识符和关键字
1.标识符 含义:标识符用于给程序中的类.变量.方法命名的符号. 组成:数字(0-9).字母(a-z)(A-Z).下划线(_).美元符号$. 命名规则:1.数字不能够作为命名符号的开头 2.不能够使用 ...
- java中this和super关键字的使用
这几天看到类在继承时会用到this和super,这里就做了一点总结,与各位共同交流,有错误请各位指正~ this this是自身的一个对象,代表对象本身,可以理解为:指向对象本身的一个指针. this ...
- Java中this和super关键字
今天练习到Java中的this和super关键字,我有如下总结: 1.子类继承父类,子类初始化对象,必须先调用父类构造方法,因为随时有可能要使用父类的成员变量. 2.get和set方法只是对成员变量进 ...
- 1.3 Java中的标识符和关键字
1.标识符 含义:标识符用于给程序中的类.变量.方法命名的符号. 组成:数字(0-9).字母(a-z)(A-Z).下划线(_).美元符号$. 命名规则:1.数字不能够作为命名符号的开头 2.不能够使用 ...
- java中的标识符、关键字、保留字
Java中关键字(keyword)和保留字(reservedword) Keyword :Java的关键字对java的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序的结构等. Reserv ...
- 简析--Java中常见的一些关键字的解析
在Java开发中我们经常会用到一些关键字,关键字的定义很有意思"Java事先定义好的,具有特殊含义的单词",那么我们怎么来用好关键字呢?下面我们对一些常见的关键字进行分析和比较; ...
随机推荐
- java servlet的域对象
在进行网络编程中的项目时 经常用到的域对象主要包括以下三种: 1. ServletContext 作用范围比较大 代码如下: //一个请求代码: ServletContext sc = reques ...
- IBOS二次开发之视图创建(PHP技术)
在 views 文件夹,我们创建一个跟控制器名称一样的文件夹list,新建一个index.php的视图文件. 我们讨论过多次,最后决定IBOS的视图机制还是以高效为主.因此我们没有使用模板,而是直接使 ...
- Esptouch移植xamarin记要
目标: 1.支持最新esptouch库更新 2.android和IOS双端混合UI开发App 解决思路: 到esptouch官方开源项目库(android,ios)编译jar及ios标准库到xamar ...
- 关于LP Wizard生成Allegro封装无焊盘的解决方案
最近在学Allegro,安装了软件后看网上说LP Wizard可以一键生成Allegro封装,就想去尝尝鲜.毕竟一直都是手动做封装,没怎么用过向导.但是按照网上教程用LP生成了一个封装,发现打开时没有 ...
- JSTree如何实现第二级菜单异步从数据库读取。
参考文档: https://www.cnblogs.com/luozhihao/p/4679050.html http://jsfiddle.net/vakata/2kwkh2uL/5/ 核心的关键点 ...
- 我的第一个chrome浏览器扩展 5分钟学习搞定
注意: 文件名必须是 manifest, ,注意扩展名是json, 新建一个文件夹,然后创建一个文本文件,作为这个扩展程序的配置文件,所以文件名是manifest.json, 感谢https://ww ...
- nodejs, 阿里oss上传下载图片
const archiver = require('archiver')const send = require('koa-send')const oss = require('ali-oss').W ...
- LOJ 6092
这个题也很没意思 发现q那么大没有用, 不重复的询问有26*n种 所以记录一下就好了 #include<bits/stdc++.h> using namespace std; #defin ...
- [zt]C++二维数组讲解、二维数组的声明和初始化
定义: int *pia = new int[10]; // array of 10 uninitialized ints 此 new 表达式分配了一个含有 10 个 int 型元素的数组,并返回指向 ...
- zookeeper使用和原理探究
转:http://www.blogjava.net/BucketLi/archive/2010/12/21/341268.html zookeeper介绍 zookeeper是一个为分布式应用提供一致 ...