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事先定义好的,具有特殊含义的单词",那么我们怎么来用好关键字呢?下面我们对一些常见的关键字进行分析和比较; ...
随机推荐
- MyCat分片规则--笔记(二)
概述 myCat实现分库分表的策略,对数据量的处理带来很大的便利,这里主要整理下MyCat的使用以及常用路由算法,针对MyCat里面的事务.集群后续再做整理:另外内容整理,不免会参考技术大牛的博客,内 ...
- 《ServerSuperIO Designer IDE使用教程》-1.标准Modbus和非标准协议的使用、测试以及驱动开发。附:v4.2发布
ServerSuperIO Designer IDE v4.2版本更新内容: 增加ServerSuperIO.Host运行程序,可以使用IDE进行测试,Host为运行环境. 针对设备驱动增加导入监测点 ...
- zabbix企业级监控dell硬件服务状态
监控dell服务的硬件状态,一般有两种方式 第一,在操作熊上安装OMSA,编写脚本调用omreport命令进行监控 第二.使用IDRAC,只需开启在IARAC上的SNMP,zabbix通过snmp进行 ...
- SVM python代码自实践
import numpy as np import matplotlib.pyplot as plt #创造数据 x = [-2,6,-2,7,-3,3,0,8,1,10,2,12,2,5,3,6,4 ...
- Nginx upstream的5种权重分配方式分享
Nginx负载均衡的分发方式有4种: 1.轮询,默认采取此方式,Nginx会按照请求时间的先后顺序进行轮询分发,若某台Web Server宕机,Nginx自动将其摘掉. 2.weight,权重,即轮询 ...
- centos7系统下配置jdk环境变量
用到的工具, Xshell5, Xftp5 现在的jdk7下载链接不好找, 打开下载界面, 全是jdk8的, 我这里提供了一份百度云的下载地址http://pan.baidu.com/s/1jHF9m ...
- Python 版本管理anaconda
下载安装 下载地址 :anaconda官网 下载后直接命令行安装,默认安装按enter 和yes bash Anaconda3-5.2.0-Linux-x86_64.sh 按照官网上下一步直接用con ...
- Nginx如何对日志文件进行配置?
在我们日常工作开发中,对调试bug最重要的手段就是查看日志和断点调试了. 今天我们来说日志文件,Nginx的日志文件一般保存的是访问日志和错误日志. 1. 用来log_format指令设置日志格式 l ...
- 记一次<iframe>的使用
将jsp拆分frame框架,因为采用了第一种方式,一直在考虑用jquery异步请求获取数据,总是但不到效果, 终于在js写吐的时候选择了第二种方式. //参考网上的使用,大多是下面这个样子,如果涉及静 ...
- unittest生产html测试报告
需要添加HTMLTestRunner.py文件,我用的ubuntu16.04下的python3.5.2,所以我放在/usr/lib/python3.5下 import unittest import ...