Java数据结构之枚举

Posted ZIYE_190

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java数据结构之枚举相关的知识,希望对你有一定的参考价值。

目录

1 枚举

1.1 背景及定义

枚举是在JDK1.5以后引入的。主要用途是:将一组常量组织起来

场景:错误状态码,消息类型,颜色的划分,状态机等等…
本质:是 java.lang.Enum 的子类,也就是说,自己写的枚举类,就算没有显示的继承 Enum ,但是其默认继承了这个类

1.2 枚举的使用

1.switch语句

public enum TestEnum 
	RED,BLACK,GREEN,WHITE;
	public static void main(String[] args) 
		TestEnum testEnum2 = TestEnum.BLACK;
		switch (testEnum2) 
			case RED:
				System.out.println("red");
				break;
			case BLACK:
				System.out.println("black");
				break;
			case WHITE:
				System.out.println("WHITE");
				break;
			case GREEN:
				System.out.println("black");
				break;
			default:
				break;
		
	

  1. 常用方法
方法说明
values()以数组形式返回枚举类型的所有成员
ordinal()获取枚举成员的索引位置
valueOf()将普通字符串转换为枚举实例
compareTo()比较两个枚举成员在定义时的顺序

示例1:

public enum TestEnum 
	RED,BLACK,GREEN,WHITE;
	public static void main(String[] args) 
		TestEnum[] testEnum2 = TestEnum.values();
		for (int i = 0; i < testEnum2.length; i++) 
			System.out.println(testEnum2[i] + " " + testEnum2[i].ordinal());
		 
		System.out.println("=========================");
		System.out.println(TestEnum.valueOf("GREEN"));
	

示例2:

public enum TestEnum 
	RED,BLACK,GREEN,WHITE;
	public static void main(String[] args) 
		//拿到枚举实例BLACK
		TestEnum testEnum = TestEnum.BLACK;
		//拿到枚举实例RED
		TestEnum testEnum21 = TestEnum.RED;
		System.out.println(testEnum.compareTo(testEnum21));
		System.out.println(BLACK.compareTo(RED));
		System.out.println(RED.compareTo(BLACK));
	

示例3:

public enum TestEnum 
	RED("red",1),BLACK("black",2),WHITE("white",3),GREEN("green",4);
	private String name;
	private int key;
	/**
	* 1、当枚举对象有参数后,需要提供相应的构造函数
	* 2、枚举的构造函数默认是私有的 这个一定要记住
	* @param name
	* @param key
	*/
	private TestEnum (String name,int key) 
		this.name = name;
		this.key = key;
	
	public static TestEnum getEnumKey (int key) 
		for (TestEnum t: TestEnum.values()) 
			if(t.key == key) 
				return t;
			
		 
		return null;
	
	public static void main(String[] args) 
		System.out.println(getEnumKey(2));
	

枚举的构造方法默认是私有的

1.3 枚举优点缺点

优点

  1. 枚举常量更简单安全 。
  2. 枚举具有内置方法 ,代码更优雅

缺点

  1. 不可继承,无法扩展

1.4 枚举与反射


查看反射创建类实例的newInstance方法源码可以发现,枚举在这里被过滤了,不能通过反射获取枚举类的实例!

Java之枚举注解反射

枚举

什么是枚举?

枚举的字面意思就是 一一列举出来
在数学和计算机科学理论中,一个集的枚举是列出某些有穷序列集的所有成员的程序,或者是一种特定类型对象的计数。这两种类型经常(但不总是)重叠。是一个被命名的整型常数的集合,枚举在日常生活中很常见,例如表示星期的SUNDAY、MONDAY、TUESDAY、WEDNESDAY、THURSDAY、FRIDAY、SATURDAY就是一个枚举。再比如线程的状态:创建、运行、阻塞、等待、消亡,这也是一个枚举。
在java中的枚举也是类似的:
枚举是在JDK1.5以后引入的。主要用途是:将一组常量组织起来。
当需要定义一组常量的时候就可以使用枚举。枚举的对象是有限的、确定的。
枚举本身就是一个类,其构造方法默认为私有的。
如果枚举类的对象只有一个,则可以作为单例模式的一种实现方式。

枚举类的实现

自定义实现枚举类

jdk1.5之前使用自定义的方式实现枚举类的定义

package enumDemo;

/**
 * user:ypc;
 * date:2021-06-22;
 * time: 18:21;
 * 自定义实现枚举类
 */
public class EnumDemo1 {
    public static void main(String[] args) {
        Season spring = Season.SPRING;
        System.out.println(spring);
    }
}

//自定义实现季节的枚举类
class Season {
    //1.首先要求枚举类是常量,就可以使用 private final 来修饰
    private final String seasonName;
    private final String seasonDesc;

    //2.常量肯定要赋值,提供私有的构造方法来初始化常量
    private Season(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    //3.枚举的对象是有限个的,确定的。所以要提供对应的枚举对象
    public static final Season SPRING = new Season("春天", "春暖花开");
    public static final Season SUMMER = new Season("夏天", "夏日炎炎");
    public static final Season AUTUMN = new Season("秋天", "秋高气爽");
    public static final Season WINTER = new Season("冬天", "冰天雪地");

    //提供获取枚举的属性的方法
    public String getSeasonName() {
        return seasonName;
    }

    public String getSeasonDesc() {
        return seasonDesc;
    }

    @Override
    public String toString() {
        return "Season{" +
                "seasonName='" + seasonName + '\\'' +
                ", seasonDesc='" + seasonDesc + '\\'' +
                '}';
    }
}

在这里插入图片描述

使用关键字enum定义枚举类

jdk1.5引入enum关键字

package enumDemo;

import org.junit.Test;

/**
 * user:ypc;
 * date:2021-06-22;
 * time: 18:40;
 * 使用enum关键字来定义枚举类
 */
public class EnumDemo2 {
    @Test
    public void test(){
        SeasonEnum spring = SeasonEnum.SPRING;
        System.out.println(spring);

        //枚举类的父类是
        System.out.println("枚举类的父类是:"+SeasonEnum.class.getSuperclass().getName());
    }
}
enum SeasonEnum{
    //提供当前枚举类的对象,之间使用","隔开,最后一个对象使用";"隔开
    SPRING("春天","春暖花开"),
    SUMMER("夏天","夏日炎炎"),
    AUTUMN("秋天","秋高气爽"),
    WINTER("冬天","冰天雪地");


    private final String seasonName;
    private final String seasonDesc;
    private SeasonEnum(String seasonName,String seasonDesc){
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }
}

在这里插入图片描述

Enum的常用方法

package enumDemo;

import org.junit.Test;

/**
 * @author ypc
 * @create 2021-06-22;
 * 枚举类常用的方法
 */
public class EnumDemo3 {
    @Test
    public void test() {
        //toString方法
        System.out.println(SeasonEnum.SPRING.toString());
        System.out.println("****************************");

        //values方法.
        SeasonEnum[] seasonEnums = SeasonEnum.values();
        System.out.println("季节枚举类中的对象:");
        for (SeasonEnum seasonenum : seasonEnums) {
            System.out.println(seasonenum);
        }
        System.out.println("****************************");

        //线程状态中的所有的枚举的对象
        Thread.State[] states = Thread.State.values();
        System.out.println("线程状态枚举类中的对象:");
        for (int i = 0; i < states.length; i++) {
            System.out.println(states[i]);
        }
        System.out.println("****************************");
        //valueOf方法
        SeasonEnum winter = SeasonEnum.valueOf("WINTER");
        System.out.println(winter);
    }
}

在这里插入图片描述

实现接口的枚举类

实现接口,在枚举类中实现抽象方法

interface info {
    void show();
}

enum SeasonEnumImplementsInterface1 implements info {
    //提供当前枚举类的对象,之间使用","隔开,最后一个对象使用";"隔开
    SPRING("春天", "春暖花开"),
    SUMMER("夏天", "夏日炎炎"),
    AUTUMN("秋天", "秋高气爽"),
    WINTER("冬天", "冰天雪地");


    private final String seasonName;
    private final String seasonDesc;

    private SeasonEnumImplementsInterface1(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

    @Override
    public void show() {
        System.out.println("枚举类实现了接口的抽象方法");
    }
}

在这里插入图片描述

让接口的每个枚举对象都实现各自的方法

//枚举的各个对象实现了接口的抽象方法
enum SeasonEnumImplementsInterface2 implements info {
    //提供当前枚举类的对象,之间使用","隔开,最后一个对象使用";"隔开
    SPRING("春天", "春暖花开") {
        @Override
        public void show() {
            System.out.println("春天来了");
        }
    },
    SUMMER("夏天", "夏日炎炎") {
        @Override
        public void show() {
            System.out.println("夏天到了");

        }
    },
    AUTUMN("秋天", "秋高气爽") {
        @Override
        public void show() {
            System.out.println("秋天来了");
        }
    },
    WINTER("冬天", "冰天雪地") {
        @Override
        public void show() {
            System.out.println("冬天来了");

        }
    };


    private final String seasonName;
    private final String seasonDesc;

    private SeasonEnumImplementsInterface2(String seasonName, String seasonDesc) {
        this.seasonName = seasonName;
        this.seasonDesc = seasonDesc;
    }

}

在这里插入图片描述

public class EnumDemo4 {
    @Test
    public void test1(){
        SeasonEnumImplementsInterface1[] SeasonEnumImplementsInterface1s = SeasonEnumImplementsInterface1.values();
        System.out.println("季节枚举类中的对象:");
        for (SeasonEnumImplementsInterface1 seasonEnumImplementsInterface1:SeasonEnumImplementsInterface1s) {
            System.out.println(seasonEnumImplementsInterface1);
            seasonEnumImplementsInterface1.show();
        }

    }
    @Test
    public void test2(){
        SeasonEnumImplementsInterface2[] SeasonEnumImplementsInterface2s = SeasonEnumImplementsInterface2.values();
        System.out.println("季节枚举类中的对象:");
        for (SeasonEnumImplementsInterface2 seasonEnumImplementsInterface2:SeasonEnumImplementsInterface2s) {
            System.out.println(seasonEnumImplementsInterface2);
            seasonEnumImplementsInterface2.show();
        }

    }
}

注解

注解概述

在Java中,注解(Annotation)引入始于Java5,用来描述Java代码的元信息,通常情况下注解不会直接影响代码的执行,尽管有些注解可以用来做到影响代码执行。
Annotation 其实就是代码里的特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理。通过使用 Annotation,程序员可以在不改变原逻辑的情况下, 在源文件中嵌入一些补充信息。

Annotation可以像修饰符一样使用,可以用来修饰包、类、构造器、方法、成员变量、参数、局部变量的声明,这些信息被保存在Annotation的“name = value”对中。
在JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。
一定的程度上可以说框架 = 注解 + 反射机制 + 设计模式

常见的注解

1.生成文档相关的注解
2.在编译的时候进行格式的检查
3.跟踪代码的依赖性,实现代替配置文件的功能

package AnnotationDemo;

/**
 * 1.生成文档相关的注解
 * @author ypc
 * @create 2021-06-22;
 */
public class AnnotationDemo1 {

}
class B implements A{
    //2.在编译的时候进行格式的检查
    @Override
    public void test() {

    }
}
interface A{
    void test();
}
/**
 * 3.跟踪代码的依赖性,实现代替配置文件的功能
 * 未使用注解的时候
  <servlet>
    <servlet-name>reg</servlet-name>
    <servlet-class>service.RegServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>reg</servlet-name>
    <url-pattern>/reg</url-pattern>
  </servlet-mapping>
 使用注解之后:
 @WebServlet("/reg")
 */

反射

什么是反射?

反射是指在程序运行期间,可以通过Reflection Api提供方法可以获取任何类的内部的信息,并能直接操作任意类的方法和属性。反射被视为动态语言的关键。

//在反射之前可以做的事情
    @Test
    public void Test1() {
        //创建Person类的对象
        Person person = new Person("name", 78);
        //通过对象调用其内部的方法和属性
        person.setAge(20);
        System.out.println(person.toString());
        person.show();
        //在Person类的外部,不能通过对象调用其内部的私有的结构
    }

    //在反射之后可以做的事情
    @Test
    public void Test2() throws Exception {
        //通过反射创建Person类的对象
        Class classPerson = Person.class;
        Constructor constructor = classPerson.getConstructor(String.class, int.class);
        Object object = constructor.newInstance("Tom", 13);
        Person person = (Person) object;
        System.out.println(person.toString());
        //通过反射获取Person内部的属性和方法
        Field name = classPerson.getField("name");
        name.set(person, "Jack");
        System.out.println(person.toString());

        //调方法

        Method show = classPerson.getDeclaredMethod("show");
        show.invoke(person);

        //调用私有的构造方法

        Constructor constructor1 = classPerson.getDeclaredConstructor(String.class);
        constructor1.setAccessible(true);
        Person person1 = (Person) constructor1.newInstance("Marry");

        System.out.println(person1);

        //调用私有的方法
        Method showNation = classPerson.getDeclaredMethod("showNation", String.class);
        showNation.setAccessible(true);
        showNation.invoke(person1, "中国");

    }

结果:
未使用反射
在这里插入图片描述
使用反射:
在这里插入图片描述

Person类

package reflection;

/**
 * user:ypc;
 * date:2021-06-20;
 * time: 13:55;
 */
public class Person {
    public String name;
    private int age;

    public String getName() {
        return name;
    }

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

    public Person() {
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Person(String name, int age) {
        this.age = age;
        this.name = name;

    }

    private Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\\'' +
                ", age=" + age +
                '}';
    }

    public void show() {
        System.out.println("I am a person");
    }

    private String showNation(String nation) {
        return nation;
    }


}

反射的用途

1、在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法 。
2、反射最重要的用途就是开发各种通用框架,比如在spring中,我们将所有的类Bean交给spring容器管理,无论是XML配置Bean还是注解配置,当我们从容器中获取Bean来依赖注入时,容器会读取配置,而配置中给的就是类的信息,spring根据这些信息,需要创建那些Bean,spring就动态的创建这些类。
Java程序中许多对象在运行时会出现两种类型:运行时类型(RTTI)和编译时类型,例如Person p = new Student();这句代码中p在编译时类型为Person,运行时类型为Student。程序需要在运行时发现对象和类的真实信息。而通过使用反射程序就能判断出该对象和类属于哪些类。

在类被加载完成之后,就会在堆区,产生一个Class对象,这个对象就包含了存这个类的全部的结构信息。我们就可以通过这个对象看到这个类的全部的信息。
这个对象就像一面镜子,通过这个镜子可以看到这个类的全部的信息结构。所以称之为反射。
正常的方式:通过引入需要导入的包类的名称---->通过new来实例化---->得到实例化的对象
反射的方式:实例化对象---->通过getClass()方法---->得到完整的包类的名称

反射的具体作用

在运行的时候判断任意的一个对象所属的类
在运行的时候构造任意一个类的对象
在运行 的时候判断任意一个类的成员变量和方法
在运行的时获取泛型的信息
在运行的时候调用任意一个类的成员变量和方法
在运行的时候处理注解
生成动态代理

反射的主要API

类名 用途
Class类 代表类的实体,在运行的Java应用程序中表示类和接口
Field类 代表类的成员变量/类的属性
Method类 代表类的方法
Constructor类 代表类的构造方法

通过直接new 的方式和反射 都可以直接调用公共的结构,在开发的时候应该使用哪一个呢?
建议:使用new 的方式来创建对象。
什么时候使用反射呢?
反射的特性:动态性。就是在编译的时候不知道要创建什么样的对象的时候,可以使用反射方式来创建对象。比如在后端部署的服务器,前端传来的时登录的请求的话,就创建登录对应的对象。前端传来的是注册所对应的请求的话,就创建登录所对应的对象,这就是反射的动态特性。
反射的机制和封装是不矛盾的呢?
封装是告诉你不要调,反射可以调。

Class类

在Object类中定义了以下的方法,此方法将被所有子类继承:

public final Class getClass()

以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即可以通过对象反射求出类的名称。
对象使用反射后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构( class/interface/enum/annotation/primitive type/void/[])的有关信息。也就是这些类型可以有Class对象:class 成员内部类、 静态内部类、 局部内部类 、 匿名内部类、接口、数组、枚举、注解、基本的数据类型、void等。
注意:
Class本身也是一个类
Class对象只能由系统建立对象
一个加载的类在JVM中只会有一个Class实例
一个Class对象对应的是一个加载到JVM中的一个.class文件
每个类的实例都会记得自己是由哪个Class实例所生成
通过Class可以完整地得到一个类中的所有被加载的结构
Class类是Reflection的根源,针对任何你想动态加载、运行的类,只有先获得相应的Class对象,才能继续下去。
关于java.lang.Class的理解:
类的加载过程:
程序通过javac.exe命令,生成一个或多个字节码文件。接着使用java.exe命令来对某个字节码文件来解释运行。将字节码文件加载到内存中,这个过程称为类的加载。加载到内存中的类,就被称为运行时类,此运行使类就称为Class 的一个实例。
Class 实例就对应着一个运行时类,加载到内存中的运行时类,会缓存一段时间。在此时间之内,可以通过不同的方式来获取运行时类。

获取Class实例的四种方式

    //     Class 实例就对应着运行时类
    @Test
    public void test3() throws ClassNotFoundException {
        //方式1 调用运行时类的属性:.class
        Class clazz1 = Person.class;
        System.out.println(clazz1);

        //方式2 通过运行时类的对象来调用

        Person person = new Person();
        Class clazz2 = person.getClass();
        System.out.println(clazz2);


        //方式3 通过Class的静态方法 forName(类的全路径名称)
        Class clazz3 = Class.forName("reflection.Person");
        System.out.println(clazz3);

        //方式4 通过类加载器:ClassLoader
        ClassLoader classLoader = ReflectionDemo1.class.getClassLoader();
        Class clazz4 = classLoader.loadClass("reflection.Person");
        System.outjava笔记JVM(java虚拟机)之JVM的组成部分及运行时数据区

Java杂记之JVM内存模型

JVM之JVM的体系结构

JVM之基本结构

JVM之内存结构

Java之枚举注解反射