枚举
枚举是JDK1.5版本新增的特性(泛型、For-each等如今被广泛应用的特性也是由JDK1.5时所新增的),另外到了JDK1.6后switch语句支持枚举类型;
- 枚举的使用情况:
有的时候一个类的对象是有限且固定的,这种情况下我们使用枚举类就比较方便;
枚举就是将所有的有限的类对象先定义好; - 语法
public enum 枚举类名{
//枚举的所有对象都必须在第一行定义好
}
比如:
public enum Sex {
// 男性
MALE("男"),
// 女性
FEMALE("女");
private String cnName;
//可以定义方法
public String cnName() {
return this.cnName;
}
// 构造方法不能是公共的
Sex(String cnName) {
this.cnName = cnName;
}
}
public enum Week {
MON, TUE, WED, THU, FRI, SAT, SUN
}
枚举类内也可以定义属性和方法,可是是静态的和非静态的。
但是构造方法必须为私有的;在定义枚举类型时我们使用的关键字是enum,与class关键字类似,只不过前者是定义枚举类型,后者是定义类类型。枚举类型Day中分别定义了从周一到周日的值,这里要注意,值一般是大写的字母,多个值之间以逗号分隔。同时我们应该知道的是枚举类型可以像类(class)类型一样,定义为一个单独的文件,当然也可以定义在其他类内部,更重要的是枚举常量在类型安全性和便捷性都很有保证,如果出现类型问题编译器也会提示我们改进,但务必记住枚举表示的类型其取值是必须有限的,也就是说每个值都是可以枚举出来的,比如上述描述的一周共有七天。
同时枚举也给我们提供了更多的方法操作所定义对象;
以上面性别枚举类举例;
//获取枚举对象的序号,序号从0开始
System.out.println(Sex.MALE.ordinal());
System.out.println(Sex.FEMALE.ordinal());
// 把枚举对象转为字符串
System.out.println(Sex.MALE.name());
// 把字符串转为枚举对象, 虚拟机中的枚举对象只会有一份, 可以用== 来比较
System.out.println(Sex.valueOf("MALE") == Sex.MALE);
// 打印所有的枚举对象
for (Sex value : Sex.values()) {
System.out.println(value);
}
设计模式
定义:编程中的一些套路,让我们的代码实现特定的目的,结构上更加优秀;
- 单例模式
单例模式(Singleton Pattern)
定义:虚拟机中这个类只有一个实例(一个对象);
特点:
1、单例类只能有一个实例。
2、单例类必须自己创建自己的唯一实例。
3、单例类必须给所有其他对象提供这一实例。
单例顾名思义,就只能有一个实例;因此它需要私有,然后自己去创建出这一个唯一实例;同时它又不是说就只能自己使用,它还需要给所有其他对象提供这个实例,供他们使用,因此简单单例的代码就有了大致模板;
public class Singleton1 {
/**
* 让构造方法私有,别人就没法创建此类的实例了
*/
private Singleton1() {
}
/**
* 自己创建这个实例
*/
private static final Singleton1 ME = new Singleton1();
/**
* 获取唯一实例
*/
public static Singleton1 getInstance() {
return ME;
}
}
上面我们就看出来,我们先创建了私有的构造方法,然后自行和创建他的实例;最后给一个公有方法返回我们创造的对象实例;供其他对象调用;
但是我们想一下,这种方法在一开始就创建出了唯一的单例了,可是如果我们很久不用呢?这种方法就叫饿汉式的单例模式,就不管别人用不用,在一开始就创建好实例;这种方法就不太好;
所以我们也可以在真正需要实例的时候再创建;这种方式叫懒汉式单例;
但是懒汉式单例就会出现一个问题,即如果在多线程的环境下,就不一定能保证单例;
比如上面的例子如果我们的实例方法这样写
public class Singleton1 {
private Singleton1() {
}
private static final Singleton1 ME = new Singleton1();
public static Singleton2 getInstance() {
if (ME == null) {
ME = new Singleton2();
}
return ME;
}
在我们还没有创建好 ME = new Singleton2();时,又新进来的线程判断ME还时null,这时他就会再创建一个实例;这就不是单例了,所以我们必须加锁;
普通方法加锁,就需要声明加锁的synchronized关键字;
public static synchronized Singleton2 getInstance() {
// 当一次调用时ME == null为真, 当后续调用时ME == null为假,就不会执行创建对象的操作了
if (ME == null) {
ME = new Singleton2();
}
return ME;
}
这样就保证了单例;
同时懒汉式的单例也有更好的实现方式;我们可以用静态内部类的方式,在我们调用方法的时候才实现单例的创建;
public class Singleton4 {
static{
System.out.println("Singleton4类被加载了");
}
private Singleton4() {
}
// holder 拥有, 由静态内部类创建了他的唯一实例
private static class Holder {
static{
System.out.println("Holder类被加载了");
}
static Singleton4 ME = new Singleton4();
}
public static Singleton4 getInstance() {
return Holder.ME;
}
public static void test() {
System.out.println("Singleton4其它方法");
}
}
这里我们用一个测试类看看静态内部类的单例创建效果;
public class TestSingle {
public static void main(String[] args) {
Singleton4.test();
System.out.println("========================");
Singleton4.getInstance();
Singleton4.getInstance();
Singleton4.getInstance();
}
}
从上面的结果我们就能看出来,在没有调用获取单例的方法时,单例并没有被创建,holder拥有创建单例的功能,那部分的代码也没有执行,在我们调用后,先用houlder,执行了它的静态内部类,创建出了唯一单例;这就是懒汉模式单例的更优方式;
- 享元模式
享元模式是提倡重用已有的对象,而不是创建新的对象。
在Integer的valueOf方法中就用到了享元模式。
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
这段代码是指如果数字在一个范围内则会返回一个存储在cache数组中的对象。即实现了对象的重复利用。
- 原型模式 prototype
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
根据已有对象来创建新的对象, 克隆;
使用场景,当对象属性很多,希望新的对象的大部分属性从原有对象复制而来。
public class User implements Cloneable {
private String name;
private int age;
private Date birthday;
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
浅拷贝和深拷贝
刚才的Cloneable实现的是浅拷贝,也就是说,对象的属性仅仅是复制了地址,没有把内容新复制一份
深拷贝是指所有的内容都得是全新的。
使用序列化流来进行深拷贝
protected Object clone() throws CloneNotSupportedException {
//内存操作流
ByteArrayOutputStream os = new ByteArrayOutputStream();
try {
// 把自己(当前对象)写入输出流
new ObjectOutputStream(os).writeObject(this);
// 拿到字节数组
byte[] bytes = os.toByteArray();
// 内存操作流
ByteArrayInputStream is = new ByteArrayInputStream(bytes);
// 对象输入流
ObjectInputStream ois = new ObjectInputStream(is);
return ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
- 建造器模式(Builder)
通过建造起模式可以使创建对象时调用构造方法更加灵活。
适用于有多个可省参数的构造方法。
例如有一个Person类,他有以下的属性
String sex 性别
String name 名字
Integer weight; 体重
Integer height; 身高
在调用构造方法时,所有属性都是可选的。如果使用一般的模式编写,则需要为所有可能都写一个构造方法。
如果改用建造器模式,代码量就会变得简单。
public class Person {
private String name;
private String sex;
private Integer weight;
private Integer height;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", weight=" + weight +
", height=" + height +
'}';
}
// 建造器
public static class PersonBuilder{
private String name;
private String sex="男";
private Integer weight=50;
private Integer height;
// 返回值类型不再是void 而是建造器类型本身
public PersonBuilder name(String name) {
this.name = name;
return this;
}
public PersonBuilder sex(String sex) {
this.sex = sex;
return this;
}
public PersonBuilder weight(Integer weight) {
this.weight = weight;
return this;
}
public PersonBuilder height(Integer height) {
this.height = height;
return this;
}
public Person build() {
return new Person(this.name,this.sex,this.weight,this.height);
}
}
private Person(String name, String sex, Integer weight, Integer height) {
this.name = name;
this.sex = sex;
this.weight = weight;
this.height = height;
}
public String getName() {
return name;
}
public String getSex() {
return sex;
}
public Integer getWeight() {
return weight;
}
public Integer getHeight() {
return height;
}
}
public class TestBuilder {
// new Person.PersonBuilder() 0 170 "张三" "男"
public static void main(String[] args) {
Person person = new Person.PersonBuilder()
.sex("男")
.name("张三")
.height(170)
.build();
System.out.println(person);
}
}
使用构造器可以让调用构造方法变得简单。
- 迭代器模式(iterator)
提供顺序访问聚合对象元素的方法,并且不暴露其底层表现;
定义:以一种一致的对集合内的元素进行遍历,而不用在乎集合内的数据结构
ArrayList 数组
LinkedList 链表
HashSet 数组+链表
TreeSet 二叉搜索树-》红黑树
for(Object o : 集合)
Iterator iter = 集合.iterator();
while(iter.hasNext()) {
iter.next();
}
- 策略模式 (Strategy)
在策略模式(Strategy Pattern)中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
java 集合或数组的排序算法
Collections.sort
Arrays.sort
基本类型 双基点快速排序
对象类型 TimSort (早期使用归并排序)
规模小 插入排序
把排序的规则抽取出来,形成比较器接口(Comparator),不同比较器的实现就称为策略
open close 开闭原则
算法不能改-- 体现的是close原则
比较器可以改 – 体现的是open原则
Collections.sort(list, (a, b) -> a.getAge() - b.getAge() );
System.out.println(list);
// 按名字拍
Collections.sort(list, (a, b) -> a.getName().compareTo(b.getName()) );
System.out.println(list);