枚举类,初识集合

Day15

一、枚举类型

枚举类型的引入

概念:枚举(enum)全称为enumeration,是JDK1.5中引入的新特性。

语法:

public enum Season{
	
	//注意:枚举类的第一行必须声明对象
	
	//spring;//底层实现 - public static final Season spring = new Season();
	//spring();//底层实现 - public static final Season spring = new Season();
	
	spring("春天","春雨绵绵"),//底层实现 - public static final Season spring = new Season("春天","春雨绵绵");
 winter("冬天","白雪皑皑");

枚举的应用场景:
该类有固定的几个对象,就使用枚举组织起来 — 枚举是用来组织常量的。

本质:枚举看上去像是一种新的数据类型,但本质上枚举是一种受限制的类,并且具有自己的办法。创建自己的enum类时,这个类继承自java.lang.Enum。

public abstract class Enum<E extends Enum <E>> implements Comparable<E>, Serializable{
 ...
}

特点:

1.枚举就是一个受限制的类(该类只能有固定的几个对象,不能让外界创建对象)

2.枚举不能有继承关系(显示继承)

3.自定义枚举类隐式继承Enum类 – public class Season extends Enum{}

4.枚举类的第一行必须声明对象

优势:

1.增强代码可读性

2.枚举型可直接与数据库交互

3.switch语句优势

4.将常量组织起来统一管理

5.取出equals两者判断,由于常量值地址唯一,可以直接用“==”进行对比

6.编译优势,枚举类编译时,没有把常量值编译到代码中,即使常量值发生改变,也不会影响引用常量的类。

枚举的switch优势

自己的代码:

package com.qf.enum03;

public enum Season{
	
	spring("春天","春雨绵绵"),
	summer("夏天","烈日炎炎"),
	autumn("秋天","硕果累累"),
	winter("冬天","白雪皑皑");
	
	private String name;
	private String info;
	
	private Season() {
	}

	private Season(String name, String info) {
		this.name = name;
		this.info = info;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getInfo() {
		return info;
	}

	public void setInfo(String info) {
		this.info = info;
	}
	
	@Override
	public String toString() {
		return name + " -- " + info;
	}
}

switch类型处理枚举类型的底层源码:

//使用final修饰自定义枚举类很好的说明了自定义枚举类是不能被继承的
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 String info;
	private static final Season[] ENUM$VALUES;//该枚举类的对象数组
 
 static {
     //				   (枚举名,枚举编号,属性name,属性info)
		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});
	}

	//private Season(){}
	private Season(String s, int i){
		super(s, i);
	}

 //private Season(String s, int i, String name, String info){
	private Season(String s, int i, String name, String info){
		super(s, i);
		this.name = name;
		this.info = info;
	}

	public String getName()
	{
		return name;
	}

	public void setName(String name)
	{
		this.name = name;
	}

	public String getInfo()
	{
		return info;
	}

	public void setInfo(String info)
	{
		this.info = info;
	}

	public String toString()
	{
		return (new StringBuilder(String.valueOf(name))).append(" -- ").append(info).toString();
	}

	public static Season[] values(){
		Season[] aseason = ENUM$VALUES;//[spring,summer,autumn,winter]
		int i = aseason.length;//4
		Season[] aseason1 = new Season[i];//new Season[4];
     
     //将aseason数组从下标为0的位置开始的元素复制到aseason1数组从下标为0的位置开始,赋值4个元素
		System.arraycopy(aseason, 0, aseason1 , 0, i);
     
     //[spring,summer,autumn,winter]
		return aseason1;
	}

 //通过字符串获取枚举对象
	public static Season valueOf(String s)
	{
     //利用Enum类的静态方法去实现 -- 反射
		return (Season)Enum.valueOf(com/qf/enum03/Season, s);
	}

	
}

从创建一个枚举类的源码中可以看到:

1.会创造一个ENUM$VALUES的数组,用于获取该枚举类的对象。

2.构造方法中会默认添加两个参数,分别是String s,int i,代表枚举名和枚举编号。

3.会创造一个values()方法,该方法会将ENUM$VALUES数组中的对象复制一遍到一个新创建的对象数组,目的是为了防止在外部改变原有的枚举对象。

4.创造一个valueOf方法,利用父类Enum的valueOf静态方法实现反射。

自己的主方法代码:

package com.qf.enum03;

public class Test01 {
	/**
	 * 知识点:枚举的switch优势
	 */
	public static void main(String[] args) {

		switch (Season.spring) {
		case spring:
			System.out.println("aaa");
			break;
		case summer:
			System.out.println("bbb");
			break;
		case autumn:
			System.out.println("ccc");
			break;
		case winter:
			System.out.println("ddd");
			break;
		}
	}
}

底层源码:

public class Test01{

 //[1,2,3,4]
	private static int $SWITCH_TABLE$com$qf$enum03$Season[];

	public static void main(String args[]){
		
     //$SWITCH_TABLE$com$qf$enum03$Season() --> [1,2,3,4] -> ai[0] -> 1
     switch ($SWITCH_TABLE$com$qf$enum03$Season()[Season.spring.ordinal()]){
		
     case 1://$SWITCH_TABLE$com$qf$enum03$Season()[Season.spring.ordinal()] -> 1
			System.out.println("aaa");
			break;

		case 2://$SWITCH_TABLE$com$qf$enum03$Season()[Season.summer.ordinal()] -> 2
			System.out.println("bbb");
			break;

		case 3://$SWITCH_TABLE$com$qf$enum03$Season()[Season.autumn.ordinal()] -> 3
			System.out.println("ccc");
			break;

		case 4://$SWITCH_TABLE$com$qf$enum03$Season()[Season.winter.ordinal()] -> 4
			System.out.println("ddd");
			break;
		}
	}

	static int[] $SWITCH_TABLE$com$qf$enum03$Season(){
		//int[] ai = new int[4]; -- [0,0,0,0]
		int ai[] = new int[Season.values().length];
     
		try{
         //ai[0] = 1; -- ai-> [1,0,0,0]
			ai[Season.spring.ordinal()] = 1;
		}catch (NoSuchFieldError ) { }
     
		try{
         //ai[1] = 2; -- ai-> [1,2,0,0]
			ai[Season.summer.ordinal()] = 2;
		}catch (NoSuchFieldError ) { }
     
     try{
         //ai[2] = 3; -- ai-> [1,2,3,0]
			ai[Season.autumn.ordinal()] = 3;
		}catch (NoSuchFieldError ) { }
     
		try{
         //ai[3] = 4; -- ai-> [1,2,3,4]
			ai[Season.winter.ordinal()] = 4;
		}catch (NoSuchFieldError ) { }
     
		return $SWITCH_TABLE$com$qf$enum03$Season = ai;
	}
}

1.会声明一个int数组变量 S W I T C H T A B L E SWITCH_TABLE SWITCHTABLEcom q f qf qfenum03$Season,这个数组变量用来存放枚举对象中元素的位置。

2.创建一个 S W I T C H T A B L E SWITCH_TABLE SWITCHTABLEcom q f qf qfenum03$Season()的静态方法,用于获得这个int类型的数组变量。

3.再进行switch语句,此时每个case判断的不是元素,而是用静态方法获取数组变量,再用枚举类型的ordinal()方法获取该元素在对象数组中的次序,即最终判断的是int类型的值,而不是String类型的值。

扩展知识点:switch如何判断String

判断的实际是字符串的哈希地址。

/**
		 * 研究String的hash码如何计算出来:
		 * 
		 * "abc" --> ['a','b','c'] --> [97,98,99]
		 * 	
		 * 	 public int hashCode() {
		        int h = hash;//h - 0
		        if (h == 0 && value.length > 0) {
		        
		            char val[] = value;
		
					//h - 96354
		            for (int i = 0; i < value.length; i++) {
		                h = 31 * h + val[i];
		            }
		            hash = h;
		        }
		        return h;
 		}
 		
 		System.out.println("abc".hashCode());//96354
 		
 		注意:两个不一样的字符串,hash值有可能相同
 		
 		System.out.println("Aa".hashCode()); //2112
			System.out.println("BB".hashCode()); //2112
		 */


写的代码:

switch ("Aa") {//2112
			case "Aa"://2112
				System.out.println("Aa");
				break;
			case "BB"://2112
				System.out.println("BB");
				break;
				
			case "CC"://2144
				System.out.println("CC");
				break;
		}

底层源码:

     String s;
     switch ((s = "Aa").hashCode())//2112
     {
         default:
             break;

         case 2112: 
             if (!s.equals("Aa")){
                 if (s.equals("BB"))
                     System.out.println("BB");
             } else{
                 System.out.println("Aa");
             }
             break;

         case 2144: 
             if (s.equals("CC"))
                 System.out.println("CC");
             break;
     }

由此,为应对不同的字符串有相同的哈希码情况,switch判断String类型是依据hash值 + equals()。

枚举的常用方法

//获取该枚举类所有的对象
		Season[] values = Season.values();
//通过字符串获取到枚举类的对象
		Season season1 = Season.valueOf("spring");
//或
		Season season2 = Enum.valueOf(Season.class, "spring");

枚举案例 之 状态机

//信号灯
public enum Signal {

	RED, YELLOW, GREEN;
}

public static void main(String[] args) {

		Scanner scan = new Scanner(System.in);
		System.out.println("请选择信号灯:RED, YELLOW, GREEN");
		String str = scan.next();

		Signal signal = Signal.valueOf(str);
		String trafficInstruct = getTrafficInstruct(signal);
		System.out.println(trafficInstruct);

		scan.close();
	}

	public static String getTrafficInstruct(Signal signal) {
		String trafficInstruct = "信号灯故障";
		switch (signal) {
		case RED:
			trafficInstruct = "红灯停";
			break;
		case YELLOW:
			trafficInstruct = "黄灯请注意";
			break;
		case GREEN:
			trafficInstruct = "绿灯行";
			break;
		}
		
		return trafficInstruct;
	}

注意:

1.枚举类虽然不能创建对象,但是可以用.valueOf(str)方法把获取的字符串转换为对应的对象。

2.创造了一个getTrafficInstruct方法用来判断获取的枚举类并返回一个字符串来表示状态。

枚举案例 之 错误码

public enum AddCode {

	ERR01(-1,"添加失败 - 学生信息不合法"),
	ERR02(-2,"添加失败 - 没有该学生"),
	OK(1,"添加成功");
	
	private int code;
	private String message;
	
	private AddCode(int code, String message) {
		this.code = code;
		this.message = message;
	}

	public int getCode() {
		return code;
	}

	public void setCode(int code) {
		this.code = code;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
	
	@Override
	public String toString() {
		return code + " -- " + message;
	}
}

public class Test01 {
	/**
	 * 知识点:枚举案例 之 错误码
	 */
	public static void main(String[] args) {
		
		System.out.println(AddCode.ERR01);
		System.out.println(AddCode.ERR02);
		System.out.println(AddCode.OK);
		
	}
}

枚举案例 之 组织枚举

应用场景:可以将一个一个的错误码使用类或接口组织在一起

注意:
使用类组织枚举,枚举默认使用static修饰
使用接口组织枚举,枚举默认使用public static修饰
经验:使用接口组织众多枚举

public interface Code {

	enum AddCode {

		ERR01(-1,"添加失败 - 学生信息不合法"),
		ERR02(-2,"添加失败 - 有重复学生"),
		OK(1,"添加成功");
		
		private int code;
		private String message;
		
		private AddCode(int code, String message) {
			this.code = code;
			this.message = message;
		}

		public int getCode() {
			return code;
		}

		public void setCode(int code) {
			this.code = code;
		}

		public String getMessage() {
			return message;
		}

		public void setMessage(String message) {
			this.message = message;
		}
		
		@Override
		public String toString() {
			return code + " -- " + message;
		}
	}

	enum RemoveCode {

		ERR01(-1,"删除失败 - 学生信息不合法"),
		ERR02(-2,"删除失败 - 没有该学生"),
		OK(1,"删除成功");
		
		private int code;
		private String message;
		
		private RemoveCode(int code, String message) {
			this.code = code;
			this.message = message;
		}

		public int getCode() {
			return code;
		}

		public void setCode(int code) {
			this.code = code;
		}

		public String getMessage() {
			return message;
		}

		public void setMessage(String message) {
			this.message = message;
		}
		
		@Override
		public String toString() {
			return code + " -- " + message;
		}
	}

	enum UpdateCode {

		ERR01(-1,"修改失败 - 学生信息不合法"),
		ERR02(-2,"修改失败 - 没有该学生"),
		ERR03(-3,"修改失败 - 修改数据不合法"),
		ERR04(-4,"修改失败 - 目标班级上有学生"),
		ERR05(-5,"修改失败 - 目标学号上有学生"),
		OK(1,"修改成功");
		
		private int code;
		private String message;
		
		private UpdateCode(int code, String message) {
			this.code = code;
			this.message = message;
		}

		public int getCode() {
			return code;
		}

		public void setCode(int code) {
			this.code = code;
		}

		public String getMessage() {
			return message;
		}

		public void setMessage(String message) {
			this.message = message;
		}
		
		@Override
		public String toString() {
			return code + " -- " + message;
		}
	}

}
public static void main(String[] args) {
		
		System.out.println(Code.AddCode.ERR01);
	}

注意:

1.组织枚举本质上就是创建一个接口或者类,然后在这个接口或类里面创建几个内部枚举类,分别表示不同情况下的错误码。

2.通常情况下使用接口,因为接口组织枚举在其他包内可以调用(public static),而类不行(static)。

枚举案例 之 策略枚举

需求:计算工资

按照员工类别:讲师、人力
讲师:基本工资 + 绩效 + 课时费*课时
人力:基本工资 + 绩效

按照部门类别:Java(讲师)、Python(讲师)、HTML(讲师)、总经办(人力)、财务(人力)

//获取工资的枚举
public enum Salary {
	//按照部门创建工资对象
	java(StaffType.teacher),python(StaffType.teacher),HTML(StaffType.teacher),
	GeneralManagerOffice(StaffType.humanResources),finance(StaffType.humanResources);
	
	private StaffType staffType;
	
	private Salary(StaffType staffType) {
		this.staffType = staffType;
	}

	public double getSalary(double basicSalary, double performance, double classFees, int classHours){
		return staffType.calculateSalary(basicSalary, performance, classFees, classHours);
	}

	//员工类别的枚举
	//public class StaffType{}
	enum StaffType{
		
		//public class Salary$StaffType$1 extends StaffType{}
		//StaffType teacher = new Salary$StaffType$1();
		//teacher是匿名内部类的对象
		teacher {
			@Override
			public double calculateSalary(double basicSalary, double performance, double classFees, int classHours) {
				
				BigDecimal big1 = new BigDecimal(String.valueOf(basicSalary));
				BigDecimal big2 = new BigDecimal(String.valueOf(performance));
				BigDecimal big3 = new BigDecimal(String.valueOf(classFees));
				BigDecimal big4 = new BigDecimal(String.valueOf(classHours));
				
				double salary = big3.multiply(big4).add(big1).add(big2).doubleValue();
				return salary;
			}
		},
		//public class Salary$StaffType$2 extends StaffType{}
		//StaffType humanResources = new Salary$StaffType$2();
		//humanResources是匿名内部类的对象
		humanResources {
			@Override
			public double calculateSalary(double basicSalary, double performance, double classFees, int classHours) {
				BigDecimal big1 = new BigDecimal(String.valueOf(basicSalary));
				BigDecimal big2 = new BigDecimal(String.valueOf(performance));
				
				double salary = big1.add(big2).doubleValue();
				return salary;
			}
		}
		;
		
		public abstract double calculateSalary(double basicSalary, double performance, double classFees,int classHours);
		
	}
}

public static void main(String[] args) {
		
		double salary1 = Salary.java.getSalary(1800, 200, 88, 20);
		System.out.println(salary1);
		
		double salary2 = Salary.GeneralManagerOffice.getSalary(50000, 130000, 0, 0);
		System.out.println(salary2);
	}

解读:

1.每个部门的枚举对象都有一个对应的属性,这个属性决定了这个对象属于哪一类(相当于通过制定另外一个枚举类来进行分支),因此还创建了一个StaffType的枚举类。

2.由于要通过指定StaffType类来作为部门类的属性,所以要创建一个有参构造,该有参构造中用的是StaffType类的对象,但是在定义枚举常量(本质上是定义匿名内部类创建的对象)时用StaffType.teacher体现了多态的思想(StaffType是创建teacher时创建的匿名内部类的父类)。

3.创造了一个getSalary方法,该方法调用了StaffType类的静态方法用于计算工资。

4.在StaffType类中,通过两个枚举,分别重写了StaffType类的静态方法用于计算不同类别的工资。

5.编写顺序:构建Salary类->构建StaffType类->在StaffType类中创建抽象方法->构建StaffType枚举常量并实现抽象方法->构建Salary枚举常量(用有参构造)->定义方法(利用StaffType类的方法)

二、初识集合

在这里插入图片描述

集合

含义

  1. 集合是Java API所提供的一系列类,可以用于动态存放多个对象 (集合只能存对象)
  2. 集合与数组的不同在于,集合是大小可变的序列,而且元素类型可以不受限定,只要是引用类型。(集合中不能放基本数据类型,但可以放基本数据类型的包装类)
  3. 集合类全部支持泛型,是一种数据安全的用法。

集合与数组的不同

数组:一旦初始化后长度不可变,元素类型受限定(String类型的数组只能装String的数据),数组可以存储基本数据类型

集合:长度可变的序列,元素类型不受限定(一个集合可以存储多个数据类型的元素),集合只能存储引用数据类型

Collection家族

List接口

特点:有序且可重复(因为List接口中添加了许多针对下标操作的方法)

实现类:

  1. ArrayList
  2. LinkedList
  3. Vector
  4. Stack

Set接口

特点:无序且不可重复

实现类:

  1. HashSet
  2. LinkedHashSet
  3. TreeSet

Map家族

实现类:

  1. HashMap
  2. LinkedHashMap
  3. Hashtable
  4. ConcurrentHashMap
  5. TreeMap
  6. Properties

泛型

含义:数据安全的做法

泛型限定:

?表示什么类型都可以

? extends A 表示元素必须是A类或A的子类

? super A 表示元素必须是A类或A的父类

迭代器

含义:遍历集合中的数据

分类:Iterator 和 ListIterator

Iterator 和 ListIterator 区别

Iterator :Collection接口下所有的实现类都可以获取的迭代器,可以在遍历时删除元素

ListIterator :List接口下所有的实现类可以获取的迭代器,可以在遍历时删除、替换、添加元素,也可以指定下标开始遍历,还可以倒叙遍历

比较器接口

作用:排序时使用

分类:

​ 内置比较器:Comparable - compareTo()

​ 外置比较器:Comparator - compare()

使用场景:

​ 内置比较器:对象要想存入TreeSet、TreeMap中,对象所属的类必须要实现内置比较器

​ 外置比较器:当内置比较的规则不满足现在的需求,但又不能改动内置比较器规则时

优先级别:外置比较器 > 内置比较器

注意

  1. Collection 与 Map的区别

Collection 存单个值,可以获取迭代器进行遍历

Map存两个值(Key-Value),不可以获取迭代器,不能遍历(Map可以间接遍历)

  1. 理解Set为什么是无序

无序:存入顺序和取出顺序不一致,无序不等于随机

  1. ArrayList 与 LinkedList的区别

使用上的区别:

​ LinkedList添加了

​ 队列模式-先进先出(removeFirst())

​ 栈模式-先进后出(removeLast())

效率上的区别:

ArrayList底层数据结构是一维数组

LinkedList底层数据结构是双向链表

​ 添加 - 不扩容的情况:ArrayList快

​ 添加 - 扩容的情况:LinkedList快

​ 删除:LinkedList快

​ 查询:ArrayList快

​ 修改:ArrayList快

​ 注意:工作中常用ArrayList,因为很多需求都需要使用查询功能,ArrayList查询更快

  1. 各种集合的应用场景

ArrayList:存数据,线程不安全

LinkedList:队列模式、栈模式,线程不安全

Vector:弃用,线程安全

Stack:弃用,线程安全

HashSet:去重+无序,线程不安全

LinkedHashSet:去重+有序,线程不安全

TreeSet:排序,线程不安全

HashMap:存key+value,key去重,无序,线程不安全

LinkedHashMap:存key+value,key去重,有序,线程不安全

Hashtable:弃用,存key+value,key去重,无序,线程安全,方法加锁-效率低

ConcurrentHashMap:存key+value,key去重,无序,线程安全,局部加锁、CAS-效率高

TreeMap:存key+value,针对于Key排序

Properties:配置文件

  1. 。。。

ArrayList

使用

ArrayList list = new ArrayList<>();

list.add(“abc”) - 添加数据

list.add(1,“xxx”) - 在指定下标上添加数据

list.addAll(newList); - 将指定集合添加到集合的末尾

list.remove(1); - 删除指定下标的元素

list.remove(“abc”) - 删除指定元素

list.removeAll(newList); - 将list中有newList的元素全部删除(删除交集)

list.retainAll(newList); - 将list中有newList的元素保留,删除其他元素(保留交集)

list.set(0, “小康”); - 在指定位置重置数据

String str = list.get(1); - 获取指定下标上的元素

int size = list.size(); - 获取元素的个数

list.clear(); - 清空集合里所有的元素

list.contains(“abc”) - 判断集合里是否包含指定元素,返回的是一个布尔值

list.containsAll(newList); - 判断集合里是否包含指定集合,返回的是一个布尔值

list.indexOf(“abc”); - 获取元素在集合中第一次出现的下标

list.lastIndexOf(“abc”) ;- 获取元素在集合中最后一次出现的下标

list.isEmpty(); - 判断集合里有没有元素,返回的是一个布尔值

List subList = list.subList(1,4) - 从开始(包含)下标处截取到结束(排他)下标处,返回新的集合

Object[] array1 = subList.toArray();-将集合转换为数组

String[] array2 = subList.toArray();- 将集合转换为指定类型的数组

遍历集合 – for循环

for(int i=0;i<list.size();i++){
    System.out.println(list.get(i));
}

遍历集合–foreach

for(String element:list){
    System.out.println(element);
}

遍历集合–Iterator

Iterator<String> it = list.iterator()
while(it.hasNext()){
    String next = it.next();
    System.out.println(next);
}

遍历集合 – ListIterator

ListIterator<String> listIterator = list.listIterator()
while(listIterator.hasNext()){
    String next = listIterator.next();
    System.out.println(next);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值