static是java语言中的一个关键字,表示一个静态修饰符,修饰符比较容易理解,静态修饰符又有什么特点呢,首先程序中的任何变量或者代码都是在编译时,由系统自动分配内存来存储的,而静态的特点就是指,在编译后所分配的内存会一直存在,直到程序退出是才会释放这个空间。计算机语言是这样描述的,那程序语言又应该怎样去解释这个东西呢,以java语言为例:java语言中所有的东西都可以理解为对象(java是一门面向对象的编程语言),对象就可以理解为我们常说的类,类中通常都会有一些成员变量或者成员方法,在不考虑访问权限的情况下,需要先实例化对象(通俗一点就是先new 对象),然后通过对象的引用,就可以访问这些成员变量或者方法。然而有一种情况是例外的——用static修饰符修饰的变量或者方法。因为一个变量或者有方法,用static修饰时,在类加载的是时候,就已经分配了内存空间,且在程序就是前一直存在,所有jvm虚拟机不需要穿件类的实力对象就可以找到该内存区域,调用相应的方法或者属性。所以,再运用静态的方法时也就有了一些限制。
· 它们仅能调用其他的static 方法,
· 它们只能访问static数据。
· 它们不能以任何方式引用this 或super。
原因很简单,类加载就产生的东西也只能调用类加载的时候就产生了的东西。
然后再来谈谈与static息息相关的东西——单例模式。
简单来说,单例模式可以理解为:在任何时刻,确保一个类只有一个实例,并提供一个全局的访问点。首先要知道对象是通过什么样的途径创建的,然后才能想办法确保只产生一个类实例。创建类对象,既——调用类的无参构造方法,创建类对象(这个说法可能并不全面),也就是常说的new 对象();因为要创建一个任何情况都只有一个类实例,所以我们就要先控制这个无参构造方法的访问权限为私有的,只有在类自己的内部参能调用这个构造方法。然而,类不出创建,又如何调用无参的构造方法来创建对象呢?那么就是凭借单例模式的核心:static。写一个静态方法(不需要创建对象,再类加载之后就可以调用),在这个静态方法内部,调用构造方法,来创建对象。方法返回值为这个类对象。一个简单的单例模式就完成了
public class Singleton {
private Singleton(){
}
public static Singleton getInstance(){
return new Singleton();
}
}
单例模式确实是实现了,私有的构造方法,公有的静态方法返回类实例,然而在有些时候会出现一些问题:并发。电脑由cup执行程序,而java是多线程的语言。所以当两个线程同时调用静态方法去得到实例的时候,还是有极小的可能出现问题,得到两个类实例。所以,这个单例模式需要一点点修改。
public class Singleton {
private static Singleton singleton =new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return singleton;
}
}
这样写,在类加载的时候就产生了类实例,所以就保证了线程安全,但是这样还有一个缺点,就是失去了延迟加载的特性,如果程序中有1w个单例,不管用没用到,在类加载的时候就全部产生实例,会造成不必要的冗余和负担,这样做并不好。所以,需要用到另外一个方法——同步,在用同步的时候,需要考虑另外一个问题,就是效率的问题,因为在使用synchronized的时候,一个线程在执行synchronized中的代码的时候如果别的线程也要执行其中的代码就要等到之前的线程运行完之后在才能够进入。
public class Singleton {
private static Singleton singleton ;
private Singleton(){
}
public static Singleton getInstance(){
if(singleton==null){
synchronized (Singleton.class) {
if(singleton==null){
singleton=new Singleton();
}
}
}
return singleton;
}
}
首先判断,对象是否已经创建,如果没有创建,则进入同步代码中,进入之后再判断是否已经创建,排除第一次创建时就发生并发的问题,如果已经创建则结束同步代码,如果为创建,则创建对象。其中同步锁用类的字节码也就是Singleton.class。这样实现单例模式的时候,只有第一次创建类实例的时候进入了同步锁中。极大的避免了同步所带来的等待问题。锁内和锁外两次判断也确保了类实例的唯一性。