枚举
什么是枚举?
所谓的枚举,就是把某个类所有的对象一一罗列出来。
枚举类:这个类的对象是有限的。
定义枚举的语法格式
public enum 枚举类类名{
值1, 值2, 值3, 值4;
}
示例
public enum Season {
SPRING, SUMMER, AUTUMN, WINTER;
}
枚举的使用
枚举一般是配合switch…case使用的。
public class TestEnum {
public static void main(String[] args) {
Season s = Season.SPRING;
switch(s) {
case SPRING:{
System.out.println("春天想去小河边散步");
break;
}
case SUMMER:{
System.out.println("夏天我想逆流而上");
break;
}
case AUTUMN:{
System.out.println("秋天想去拍婚纱照");
break;
}
case WINTER:{
System.out.println("冬天去三亚玩");
break;
}
}
}
}
枚举的剖析
枚举的本质是一个类,这个类继承于Enum。
public final class Season extends Enum
{
public static final Season SPRING;
public static final Season SUMMER;
public static final Season AUTUMN;
public static final Season WINTER;
private static final Season ENUM$VALUES[];
private Season(String s, int i)
{
super(s, i);
}
public static Season[] values()
{
Season aseason[];
int i;
Season aseason1[];
System.arraycopy(aseason = ENUM$VALUES, 0, aseason1 = new Season[i = aseason.length], 0, i);
return aseason1;
}
public static Season valueOf(String s)
{
return (Season)Enum.valueOf(com/lanu/enumeration/Season, s);
}
static
{
SPRING = new Season("SPRING", 0);
SUMMER = new Season("SUMMER", 1);
AUTUMN = new Season("AUTUMN", 2);
WINTER = new Season("WINTER", 3);
ENUM$VALUES = (new Season[] {
SPRING, SUMMER, AUTUMN, WINTER
});
}
}
上述代码是对Season反编译的结果,从反编译的结果可以看出:枚举本质上是一个类。每一个枚举值本质上是一个(public static final)对象。
既然是对象,就可以调用方法,方法要么是自己定义的,要么是继承于父类的方法。
它的父类提供了2个常用的方法:
String name(); //获取枚举值的名字
int ordinal(); //获取枚举值的序号,序号从0开始。
由于枚举本质上是一个类,所以枚举可以添加属性,添加方法,添加构造器等,类所能做的事情,它都可以做。
下面为Season添加属性,方法以及构造器:
public enum Season {
SPRING("春"), SUMMER("夏"), AUTUMN("秋"), WINTER("冬");
private String name;//自定义属性
private Season(String name) {//自定义构造器
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void test() {//自定义方法
System.out.println("hello,my name is " + this.name());
}
}
反编译后的结果如下:
import java.io.PrintStream;
public final class Season extends Enum
{
public static final Season SPRING;
public static final Season SUMMER;
public static final Season AUTUMN;
public static final Season WINTER;
private String name;
private static final Season ENUM$VALUES[];
private Season(String s, int i, String name)
{
super(s, i);
this.name = name;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public void test()
{
System.out.println((new StringBuilder("hello,my name is")).append(name()).toString());
}
public static Season[] values()
{
Season aseason[];
int i;
Season aseason1[];
System.arraycopy(aseason = ENUM$VALUES, 0, aseason1 = new Season[i = aseason.length], 0, i);
return aseason1;
}
public static Season valueOf(String s)
{
return (Season)Enum.valueOf(com/lanu/enumeration/Season, s);
}
static
{
SPRING = new Season("SPRING", 0, "春");
SUMMER = new Season("SUMMER", 1, "夏");
AUTUMN = new Season("AUTUMN", 2, "秋");
WINTER = new Season("WINTER", 3, "冬");
ENUM$VALUES = (new Season[] {
SPRING, SUMMER, AUTUMN, WINTER
});
}
}
枚举的总结
枚举就是一种数据类型,只不过这种数据类型把可能出现的值提前定义好了,用的时候拿类直接访问即可。枚举多用于switch…case和if语句。
单例
什么是单例?
单例是一种设计模式, 指的是单个实例。如果一个类只能创建一个对象,这样的类叫单例类,这个类的对象就是单例。
如何把一个类定义成单例类呢?
- 构造器私有化。(禁用new)。提供一个私有的构造器,这样外界就无法创建对象了。
- 提供一个获取本类对象的静态方法。(方法内部要保证对象的唯一性)。
懒汉模式的单例
所谓懒汉模式,也叫懒加载,也叫延迟加载,即:在首次用到的时候才加载。懒汉模式的好处:节省内存空间,首次用到的时候才创建对象,如果一直没有用到,就一直不创建对象。缺点:代码较多。
public class Singleton {
// 第二步定义一个 本类类型的静态属性。
private static Singleton s = null;
// 第一步 禁用new,让外界无法创建对象
private Singleton() {
}
//第三步 提供一个获取本类对象的方法
public static Singleton sharedSingleton() {
//如果s为空,说明尚未创建对象,如果s不为空,说明已经创建过对象,直接使用对象即可。
if(s == null) {
s = new Singleton();
}
return s;
}
}
上述代码定义了一个单例类,这个类只能创建一个对象(通过Singleton.sharedSingleton()创建对象)。
上述的单例类在单线程模式下是没有问题的,但是在多线程环境中,有可能会出现不止一个对象。在多个线程中同时获取对象时,有可能不止一个对象产生(即执行了多次new)。
可以通过添加同步代码块(或同步方法)的方式来应对多线程环境。
public class Singleton {
// 第二步定义一个 本类类型的静态属性。
private static Singleton s = null;
// 第一步 禁用new,让外界无法创建对象
private Singleton() {
}
// 第三步 提供一个获取本类对象的方法(多线程安全的写法)
public static synchronized Singleton sharedSingleton() {
// 如果s为空,说明尚未创建对象,如果s不为空,说明已经创建过对象,直接使用对象即可。
if (s == null) {
s = new Singleton();
}
return s;
}
}
上述代码是同步代码块的方式解决多线程环境下有可能产生多个对象的问题。
在某个线程执行 Singleton.sharedSingleton()的时候,加锁,这样别的线程就会处于等待状态,从而保证只创建一个对象。但是问题是,唯一的对象创建出来以后,每次调用Singleton.sharedSingleton()获取对象的时候仍然会加锁,无形之中降低了获取单例对象的效率。
改进代码如下:
public class Singleton {
// 第二步定义一个 本类类型的静态属性。
private static Singleton s = null;
// 第一步 禁用new,让外界无法创建对象
private Singleton() {
}
// 第三步 提供一个获取本类对象的方法(多线程安全的写法)
public static Singleton sharedSingleton() {
// 如果s为空,说明尚未创建对象,如果s不为空,说明已经创建过对象,直接使用对象即可。
if (s == null) {
synchronized (Singleton.class) {
if(s == null) {
s = new Singleton();
}
}
}
return s;
}
}
这个代码既保证了创建对象时的唯一性,又提升了获取对象的效率。即创建对象的时候上锁,一旦对象创建完毕以后,获取对象就不会触发上锁的代码。
饿汉模式的单例
在类加载的时候,就把对象创建好,用的时候,直接返回这个类的对象。
public class Singleton2 {
//第二步,创建一个对象
private static Singleton2 s = new Singleton2();
//第一步,禁用new
private Singleton2() {
}
//返回这个类的对象
public static Singleton2 sharedSington2() {
return s;
}
}
饿汉模式的好处:代码少,多线程下是安全的。缺点:只要类一加载,对象就创建出来了,哪怕后面一直没有调用Singleton2.sharedSingleton2();对象也存在于内存中。
面试的时候,面试官想让你写懒汉模式。实际开发中都写饿汉模式。系统的单例类也通常写饿汉模式。饿汉模式的缺点是个伪命题,既然你写了单例类,肯定是项目中要使用单例对象。
变形的懒汉模式
public class Singleton3 {
//第一步 禁用new
private Singleton3() {
}
//第二步 定义一个静态内部类
private static class SingletonLoader {
static Singleton3 s = new Singleton3();
}
//第三步 提供一个获取单例对象的方法
public Singleton3 sharedSingleton3() {
return SingletonLoader.s;
}
}
这种模式结合了懒汉模式的延迟加载优点以及饿汉模式代码简洁以及多线程安全的特点。
单例有什么用?
只能创建一个实例的类有什么用呢?
单例是比较常用的设计模式,主要处理不同类(不同页面)之间传递数据。
单例传值的思路
把要存储的值以及要读取的值都放到单例对象里。只需要给单例对象添加属性以及getter、setter方法即可。存的时候使用单例对象的setter方法,读取的时候,使用单例对象的getter方法。
public class Singleton {
private String privence;//用于存取省份
private String city;//用于存取城市
private String street;//用于存取区
public String getPrivence() {
return privence;
}
public void setPrivence(String privence) {
this.privence = privence;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
// 第二步定义一个 本类类型的静态属性。
private static Singleton s = null;
// 第一步 禁用new,让外界无法创建对象
private Singleton() {
}
// 第三步 提供一个获取本类对象的方法(多线程安全的写法)
public static Singleton sharedSingleton() {
// 如果s为空,说明尚未创建对象,如果s不为空,说明已经创建过对象,直接使用对象即可。
if (s == null) {
synchronized (Singleton.class) {
if(s == null) {
s = new Singleton();
}
}
}
return s;
}
}
public class TestSingleton {
public static void main(String[] args) {
//假定第一个页面要把省份传递给第二个页面。
//不直接传给第二个页面,而是把省份信息放到单例对象里。
Singleton s1 = Singleton.sharedSingleton();
s1.setPrivence("江苏");
//这是第二个页面,我要根据省份来确定显示哪些城市。
Singleton s2 = Singleton.sharedSingleton();
String privence = s2.getPrivence();
//显示该省份的城市列表。
//假定已经选择某个城市,不直接把城市传递给第三个页面,而是存到单例里。
s2.setCity("南京市");
//这是第三个页面,我要根据城市来确定显示哪些区。
Singleton s3 = Singleton.sharedSingleton();
String city = s3.getCity();
s3.setStreet("玄武区");
//显示该城市下的全部区。
//假定已经选择了区,要把选择的省市区都返回到最初的页面。不是把每个页面的数据返回到最初的页面
//而是这些信息放单例里,最初的页面去单例里取就可以了。
//假定这是最初的页面,你要显示用户选择的省市区。
Singleton s4 = Singleton.sharedSingleton();
String p = s4.getPrivence();
String c = s4.getCity();
String s = s4.getStreet();
}
}