(一)、枚举类:
关键字:enum
语法:public enum 枚举类名{ 对象}
适用场景:当对象的个数固定,且有限。
例子:
public enum Sex{
MAlE,
FEMALE;
public void test(){};//枚举中定义方法,MALE,FEMALE对象都有这个方法
private string cnName;
Sex(String cnName){this.cnName=cnName};//构造方法,不能定义成public,对象有限。
}
测试类中:获取枚举类的序号:Sex.MALE.ordinal()。
把枚举对象转为字符串:Sex.MALE.name();
把字符串转为枚举对象Sex.valueof();
虚拟机中的枚举对象只会有一份,可以用==来比较,定义几个对象就有几个。
获取枚举中的所有对象:Sex.values();返回值Sex[]数组。
枚举类中也可以有方法,构造方法,属性,但是构造方法不能是公共的,枚举类型也不能继承。
代码实现:
public enum Sex {
//枚举类,对象固定且有限
MALE("男"),
FEMALE("女");
//构造方法不能加public
//因为对象数是固定的有限个
private String chName;
Sex(String chName){
this.chName=chName;
}
public String getChName() {
return chName;
}
//枚举类中可以定义方法,
public void test(){
System.out.println("我是枚举类中的test方法");
}
}
public class SexTestEnum {
public static void main(String[] args) {
int i1= Sex.MALE.ordinal();//返回此枚举常量的序号
int i2=Sex.FEMALE.ordinal();
System.out.println(i1);
System.out.println(i2);
Sex.MALE.test();//调用方法
Sex.FEMALE.test();
System.out.println(Sex.MALE.name());//把枚举对象转为字符串
System.out.println(Sex.FEMALE.toString());//把枚举对象转为字符串
System.out.println(Sex.MALE.getChName());
Sex sex = Sex.valueOf(Sex.MALE.name());//把字符串转为枚举对象
System.out.println(sex);
Sex[] sexs= Sex.values();//获取枚举类型中的所有对象
for(Sex sex1:sexs){
System.out.println(sex1);
}
}
}
(二)、设计模式((Design pattern)重要)
定义:就是编程中的一些套路;目的让我们的代码实现特定的目的,结构上更加优秀。
GOF(group of Four)—总结出了23种设计模式;
1.单例模式(singleton)
(1).定义:虚拟机中这个类只有一个实例(一个对象);
(2).饿汉式单例模式:(一开始就创建好了)
第一步:让构造方法私有,别人没办法创建此类对象;
第二步:自己创建这个实例对象;//定义成私有的,静态的
第三步:定义静态方法,获取唯一实例;
代码如下:
//设计模式之饿汉单例模式
public class Singleton1 {
//第一步:让构造方法私有
private void Singleton1(){}
//第二步:自己创建这个类的实例对象
private static final Singleton1 st1=new Singleton1();
//第三步:定义静态方法,获取唯一实例
public static Singleton1 getInstance(){
return st1;
}
}
测试类:
public class Singleton1Test {
public static void main(String[] args) {
Singleton1 st1= Singleton1.getInstance();
Singleton1 st2=Singleton1.getInstance();
System.out.println(st1==st2);//true证明只有一个对象
}
}
(2).懒汉式单例模式:(用到实例对象时才创建,不像饿汉式那样随着类的加载就创建)
第一步:构造方法私有;
第二步:只声明有实例对象,不创建;
第三步:在获取实例方法中创建对象;注意要进行判断对象是不是刚开始的null,
第一次才创建对象,后面就不创建了,注意在方法上也要加上锁synchronized,保证在多线程下单例的安全。
注:静态方法上的synchronized锁住类对象,普通方法锁住this对象;
//设计模式之懒汉单例模式
public class Singleton2 {
//第一步私有化构造函数
private Singleton2(){}
//第二步只声明实例对象,不实现
private static Singleton2 ST;
//第三步定义获取实例方法,并实现实例对象创建
//在多线程时 为了保证安全,需加锁Syncthronize
public synchronized static Singleton2 getInstance(){
//进行判断,只有第一次时才创建对象
if(ST==null){
ST=new Singleton2();
}
return ST;
}
}
(3).用枚举类实现单例 (属于饿汉式单例);
//枚举类型创建单例模式
public enum Singleton3 {
ST;
}
(4).懒汉式单例改进:
在(2)中的懒汉式模式中,在多线程中,每次调用方法都要加锁,锁很浪费资源,所以现在有了一个改进方法,采用静态内部类, 在多线程中第一次枷锁,第二次及以后不加锁;
第一步:私有化构造;
第二步:用静态内部类创建唯一实例;(类只加载一次,所以只创建了一次对象)
第三步:获取唯一实例;
代码如下:
//懒汉式单例模式的改进,采用静态内部类
public class Singleton4 {
//第一步私有化构造
private Singleton4(){}
//第二步用静态内部类创建唯一实例
static class Inner{
static Singleton4 ST=new Singleton4();
}
//第三步获取实例
public static Singleton4 getInstance(){
return Inner.ST;
}
}
2.享元模式 flyweight
(1).提倡重用已有的对象,而不是创建新的对象;
Integer的享元范围:-128到127;
后面学习连接池时也会用到享元模式:对数据库连接对象进行重用。
public class Flyweight {
public static void main(String[] args) {
//Integer类的源码就是享元模式,享元范围是-128到127
System.out.println(Integer.valueOf(1)==Integer.valueOf(1));//true
System.out.println(Integer.valueOf(127)==Integer.valueOf(127));//true
System.out.println(Integer.valueOf(200)==Integer.valueOf(200));//false
}
}
3.原型模式prototype
(1).根据已有对象来创建新的对象,克隆;
(2).要求:需要实现一个接口:Cloneable,并且需要重写其父类Object中的c lone()方法。
代码如下:
public class Prototype implements Cloneable{
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
private String name;
private int age;
private Date 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;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
测试类:
public class PrototypeTest1 {
public static void main(String[] args) throws CloneNotSupportedException {
copy();//浅拷贝
}
private static void copy() throws CloneNotSupportedException {
Prototype pt = new Prototype();
pt.setName("小明");
pt.setAge(18);
pt.setBirthday(new Date());
System.out.println(pt.getName());
System.out.println(pt.getAge());
System.out.println(pt.getBirthday());
System.out.println("==================");
//克隆的另一个对象
Prototype pt2= ( Prototype )pt.clone();
System.out.println(pt==pt2);//false
pt2.setName("小红");
System.out.println("新克隆用户的名字:"+pt2.getName());
System.out.println("小明的名字:"+pt.getName());
pt2.getBirthday().setDate(15);
//新克隆用户的生日改变了,随之也把小明的生日改变了
//克隆对于引用数据类型仅仅复制的是地址,内容没有复制,
// 同一个地址中,内容改变了,其他引用此地址的对象的内容随之改变。
System.out.println("新克隆用户的生日:"+pt2.getBirthday());
System.out.println("小明的生日:"+pt.getBirthday());
}
}
(3).使用场景:当对象属性非常多时,希望新的对象的大部分属性是从原对象复制过来的。
(4)常遇到的问题:浅拷贝与深拷贝
刚才的Cloneable是浅拷贝,也就是说对象的属性仅仅是复制了地址,没有把内容
复制一份。当更改克隆出来的引用数据类型对象时,也会把原来对象的值也给改变(基本类型用浅拷贝可以完全拷贝,引用数据类型要用深拷贝)。
深拷贝是指所有的内容都得是全新的,深拷贝可以用反序列化来完成。
代码如下:
//深拷贝,采用反序列化流,需要实现Serializable接口
public class Prototype2 implements Cloneable,Serializable {
@Override
protected Object clone() {
// return super.clone();不再直接使用父类的克隆方法了
//定义了一个字节数组流
ByteArrayOutputStream bos=new ByteArrayOutputStream();
try {
//定义一个序列化流
ObjectOutputStream os=new ObjectOutputStream(bos);
os.writeObject(this);//把当前对象写入字节数组流
//取出字节数组
byte[] bytes=bos.toByteArray();
//反序列化为对象
ByteArrayInputStream bis=new ByteArrayInputStream(bytes);
ObjectInputStream ois=new ObjectInputStream(bis);
return ois.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
private String name;
private int age;
private Date 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;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
}
测试类同浅拷贝一样,只是输出结果中新克隆用户的生日改变了,小明的生日将不再改变了。
4.建造器模式:(Builder)
问题:person
String sex ; string name; Integer weight;
目的:让创建对象的过程更为灵活;适用于一步一步创建更为复杂的某些属性。
代码如下:
public class Person {
private String name;
private String sex;
private Integer weight;
private Integer height;
public Person(String name,String sex,Integer weight,Integer height){
this.name=name;
this.sex=sex;
this.weight=weight;
this.height=height;
}
//建造器
public static class PersonBuilder{
private String name;
private String sex;
private Integer weight;
private Integer height;
//返回值是构造器本身
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;
}
//定义一个方法,搜集信息,返回类型为Person类
public Person build(){
return new Person(name,sex,weight,height);
}
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", sex='" + sex + '\'' +
", weight=" + weight +
", height=" + height +
'}';
}
}
测试类:
public class PersonTest {
public static void main(String[] args) {
//可以打点连调是因为返回的类型就是PersonBuilder类,
//这就是建造器中的方法为什么要返回PersonBulider类型的原因。
Person p1=new Person.PersonBuilder()
.name("小明")
.sex("男")
.weight(60).build();
System.out.println(p1);
Person p2=new Person.PersonBuilder()
.name("小红")
.height(160)
.build();
System.out.println(p2);
}
}
5.迭代器模式(iterator)
(1).定义:以一种一致的方式对集合内的元素进行遍历,而不在乎集合的数据结构。
(2)for(Object o:list)的底层就是迭代器模式。
6.策略模式(strategy)
(1)Java集合或者数组的排序算法;
基本数据类型:双基点的快速排序;
对象类型:TimSort(早期的JDK使用归并排序);
规模小:插入排序;
(2)排序的算法是固定的,排序的规则能否固定-------不能。
把排序的规则抽出来, 形成比较器接口(Comparator),不同比较器的实现称之为策略。
(3)Open close 开闭原则;
算法不能更改-----体现的是close原则;
比较器可以改------体现的是open原则;
代码如下:
public class StrategyStudent {
public static void main(String[] args) {
Student s1=new Student("Zhangsan",12);
Student s2=new Student("Lisi",19);
Student s3=new Student("Bob",10);
Student s4=new Student("Wangming",21);
Student s5=new Student("Tommy",16);
Student s6=new Student("Tommy",19);
List<Student> list=new ArrayList<>();
list.add(s1);
list.add(s2);
list.add(s3);
list.add(s4);
list.add(s5);
list.add(s6);
//按年龄从小到大顺序输出
Collections.sort(list, ( o1, o2) -> {return o1.getAge()-o2.getAge(); });
System.out.println(list);
//按年龄从大到小顺序输出
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return -o1.getAge()-o2.getAge();
}
});
System.out.println(list);
//按姓名字母的字典顺序输出
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName());
}
});
System.out.println(list);
//先按名字再按年龄排
Collections.sort(list,(o1,o2)->{
int b=o1.getName().compareTo(o2.getName());
if(b==0){
return o1.getAge()-o2.getAge();
}else{
return b;
}
});
System.out.println(list);
}
}
Student类如下:
public class Student {
private String name;
private int age;
public Student(String name,int age){
this.name=name;
this.age=age;
}
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
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}