一.枚举

认识枚举类

枚举是一种特殊的类

枚举的格式:

修饰符  enmu   枚举类名{

名称1,名称2;

其它成员

}

//枚举类
public enum A {
//枚举类的第一列必须是罗列枚举对象的名称
X,Y,Z;
private String name; public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

枚举类的构造器是私有的,也就是说,我们不能通过外部类来使用new关键字实例化枚举类,但是可以通过:枚举类 . 对象 ;拿取内部的对象

如拿取对象X

 public static void main(String[] args) {
A a=A.X;
System.out.println(a);
}

枚举类有以下特点

  • 枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举类的一个对象
  • 枚举类的构造器都是私有的(无论是否显式表达,都是私有的),因此枚举类不能对外创建对象
  • 枚举类都是最终类,不可以被继承
  • 枚举类中,从第二行开始,可以定义类的其它各种成员
  • 编译器为枚举类新增了几个方法,并且枚举类都是继承:Java.lang.Enum类的,从Enum类中也会继承到一些方法

枚举类的一些方法

拿取全部对象:

//拿取到全部对象
A[] values = A.values();
for (A value : values) {
System.out.println(value);
}

通过方法拿对象

//通过方法拿对象,传入对象名
A z = A.valueOf("Z");
System.out.println(z);

抽象枚举

抽象枚举指的是在枚举类中,写一些抽象方法,在抽象枚举类中的对象都需要实现这个方法

//抽象枚举
public enum B {
X(){
@Override
public void fun() {
System.out.println("对象X实现抽象方法");
}
},Y("msf"){
@Override
public void fun() {
System.out.println(getName()+"对象Y实现抽象方法");
}
}; public static void X() {
} //抽象方法
public abstract void fun();
private String name; //私有的,无修饰符,且不可更改
B() { } B(String name) {
this.name = name;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} }

抽象枚举的操作:

//拿取抽象对象,使用实现的方法方法
B x= B.X;
System.out.println(x);
x.fun();

枚举的应用场景

一般用于数据标注和分类,用于解决原来区分参数时,传入变量值是不可控的情况

如,我们一些APP在做认证的时候,需要分别男,女然后分别推送相应的信息,那么传输的关键字肯定是性别

我们试着写如下代码,查看有什么问题?

public static void main(String[] args) {
check(1);
}
public static void check(int sex){
switch (sex){
//女
case 0:
System.out.println("此用户为女,展示一些化妆品,包包~");
break;
//男
case 1:
System.out.println("此用户为男,展示一些游戏,美女信息~");
break;
}
}

首先,这样写识别身份是肯定没有问题的,如果开发者或用户都选择从正常的角度去进入,那么没有问题

但是,往往这些事情都是不可控,在调用check()方法的时候,传递参数完全是不可控的,也就是,如果我传递的是-1,也能运行只是身份识别不了,违背了程序的初衷,

有些人解决的方式是利用常量来解决的,也就是限定为传入的参数为常量

//专门构造一个存放常量的类
public class staticVar {
//男
public static final int Boy=1;
//女
public static final int Girl=0;
}

然后传递参数的时候就是传递一个常量:

    public static void main(String[] args) {
check(staticVar.Boy);
}
public static void check(int sex){
switch (sex){
//女
case staticVar.Girl:
System.out.println("此用户为女,展示一些化妆品,包包~");
break;
//男
case staticVar.Boy:
System.out.println("此用户为男,展示一些游戏,美女信息~");
break;
}
}

当我们能构思到常量的时候,我们就离枚举类很近了,

回忆一下枚举类的第一列是什么?------枚举类的第一行只能罗列一些名称,这些名称都是常量,并且每个常量记住的都是枚举类的一个对象

这不是匹配上了嘛,所以继续改造,编写一个枚举类,用来存储这些标记

public enum StaticEnum {
Boy,Girl;
}

传递枚举类对象作为区分男,女的标志

public static void main(String[] args) {
check(StaticEnum.Girl);
}
public static void check(StaticEnum sex){
switch (sex){
//女
case Girl:
System.out.println("此用户为女,展示一些化妆品,包包~");
break;
//男
case Boy:
System.out.println("此用户为男,展示一些游戏,美女信息~");
break;
}
}

使用枚举类作为数据标记分类的优点在于,用户或后面实现的程序员都只能传递此枚举类的参数

此枚举类的对象又被我们限定好了,且枚举类没有公共构造器,不能自己构造,参数就被限定为开发者设定的那几组内部了

总结:

但是要知道静态变量也是可以使用的,那么枚举类和静态变量做数据标注和分类的区别在哪呢?

  • 一般游戏搭建里使用枚举类比较多,因为它的参数就是那些固定参数,如,上,下,左,右键,鼠标事件等固定的案件
  • 在项目开发里,使用静态变量就比较多了,他需要从初始变量拿取值的,这些值有可能还是字符串,所以是有类型的,和枚举类的特点不符

二.泛型

认识泛型

在定义方法,接口,类的时候,同时声明了一个或多个类的变量(如:E),称为泛型方法,泛型接口,泛型类,它们统称为泛型

泛型的一般格式:

public  class  ArrayList<E>{

......

}

泛型的作用:泛型提供了在编译操作的时候所提供的数据类型,并自动检测的能力,这样可以避免强制类型的转换,及其可能出现的异常

泛型的本质:把具体的数据类型作为参数传递给类变量

在使用Java自定义的一些类的时候,它们的数据类型大多都是泛型,所以在进行值传递是都是Object类型,返回的值也是Object,在存放数据的时候可以将任意一种对象放入,但是去数据就很麻烦了

第一,有可能你根本不知道这个Object中到底存放的是那些数据

第二,即使你知道它是那个对象的类型,但是要进行操作时,必须要强转成它原本的类型

所以,我们可以使用将数据类型作为参数传递给类变量的方式来控制泛型

如下,使用Java自带的ArrayList类来做测试:

public class Main2 {
public static void main(String[] args) {
//直接new的ArrayList
ArrayList list1 = new ArrayList<>();
list1.add("java");
list1.add("mysql");
list1.add(new Animal());
}
}
class Animal{ }

在为进行泛型约束的时候,默认是什么都可以往里面装的,

在拿取的时候也是拿取的Object类型

如下,我们在取里面的值的时候:

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

在拿取对象的时候报了一个错误,它的意思就是Animal不是String类型,肯定不是啊,他们属于两个不同的类对象,如果强转成Animal,又会报错String听不是Animal类型

发生这种事情的根本原因就是传递在添加数据的时候设定的两个数据类型都可以,所以我们要尽量避免这种情况发生

现在我们使用将类型作为参数传递给类,约束泛型:

ArrayList<String> list2 = new ArrayList<>();
list2.add("java");
list2.add("mysql");

将数据类型约束成了字符串,再来添加一个对象试试:

我们可以看到报了一个错误,这个错误的意思是需要将这个对象转为String类型才能添加

这样就可以约束到泛型的参数类型了

自定义泛型类

//泛型类<E> E默认表示Object,在实例化的时候通过参数传递进来
public class MyArrayList<E> {
//存放数据的Object数组
private Object[] o = new Object[20];
//计数器,记录数据存放到哪里了
private int size=0;
//添加数据的方法
public void add(E e){
o[size++]=e;
}
//获取数据的方法
public E get(int index){
return (E)o[index];
}
//拿取当前位置长度
public int getSize() {
return size;
}
}

此时的E默认是Object,当我们在构造泛型的时候传递一个数据类型给类接收到后,就会将E 替换为你传递进来的类型

如:

//自定义的ArrayList
MyArrayList<String> list3 = new MyArrayList<>();

也就是将E替换为String,我们的自定义泛型类就会做出如下改变:

public class MyArrayList<String> {
//存放数据的Object数组
private Object[] o = new Object[20];
//计数器,记录数据存放到哪里了
private int size=0;
//添加数据的方法
public void add(String e){
o[size++]=e;
}
//获取数据的方法
public String get(int index){
return (String)o[index];
}
//拿取当前位置长度
public int getSize() {
return size;
}
}

如上,E都被替换成为了String类型

这就是对泛型类型的约束

测试自定义泛型类:

//自定义的ArrayList
MyArrayList<String> list3 = new MyArrayList<>();
list3.add("msf");
list3.add("maming");
for (int i = 0; i < list3.getSize(); i++) {
System.out.println(list3.get(i));
}

多泛型类定义:

//多泛型类
public class MyArrayList2 <E,T>{
public void add(E e,T t){ }
public E getE(String name){
return null;
}
public T getT(String name){
return null;
}
}

实例化:

//自定一ArrayList2
MyArrayList2<String, Animal> list4 = new MyArrayList2<>();

泛型类型的继承定义:

//泛型E是继承Animal类的
public class MyArrayList3<E extends Animal> {
public void add(E e){ }
public E getE(String name){
return null;
}
}

这种类的泛型类型只能是Animal类本身或继承Animal类的类

如:

MyArrayList3<Animal> list5 = new MyArrayList3<>();

错误情况,如其为String类时:

如上,当不为Animal类或其继承类时,会报错,且不能运行程序

泛型接口

泛型接口的定义和泛型类的定义方式是差不多的,只是用途不一样,因为类和接口的适用范围本来就是不一样的

在实现泛型接口的类中,也可以携带数据类型进行实现接口,这样接口中泛型T都会被替换成被传递的参数类型

//泛型接口
public interface MyInterface<T>{
//默认Object
void add(T e);
T get(String name);
}

我们再来看看实现类将数据类型传入后实现:

实现类一:

//实现类的操作数据都是String
public class MyStringImp implements MyInterface<String>{
//变化:T -》String
@Override
public void add(String e) {
}
//变化:T -》String
@Override
public String get(String name) {
return null;
}
}

实现类二:

//实现类的操作数据类型都是Integer
public class MyIntImp implements MyInterface<Integer>{
//变化:T -> Integer
@Override
public void add(Integer e) { }
//变化:T -> Integer
@Override
public Integer get(String name) {
return 0;
}
}

泛型方法

定义格式

修饰符  <类变量,类型变量.....>   返回值类型  方法名(形参列表){

//语句

}

此类方法和泛型类是一样的使用方式,都是用于解决参数类型不知道的情况下做的适配,

泛型方法的泛型参数都是自定义的,这是一个基本点,在泛型类中也有泛型变量,但是泛型类中的方法都不是泛型方法,因为泛型变量是泛型类定义的,而不是方法自定义的

    //泛型方法
public static <T> void test(T t){ }
//不是泛型方法
public E get(int index){
return E;
}

泛型方法示例:

    public static void main(String[] args) {
//ArrayList
ArrayList<String> list = new ArrayList<>();
list.add("java");
list.add("mysql");
//调用方法
String rs = test(list);
System.out.println(rs);
}
//使用自定义泛型变量
public static <T> T test(ArrayList<T> arr){
return arr.get(1);
}

我们在使用已经定义好的泛型的时候可以 不用自定义泛型,而是使用通配符?代替

通配符 ?可以代替一切类型

如我们熟知的ArrayList<>就是一个典型的泛型对象,我们可以使用自定义方法,让自定义泛型变量作为参数传入ArrayList<>中,也可以使用通配符?

自定义方法:

    //使用自定义泛型变量
public <T> T test(ArrayList<T> arr){
return arr.get(1);
}

ArrayList<>对象本身就是定义好的泛型对象,使用的时候没必要在自定义一个泛型变量做适配,所以推荐使用通配符,自定义泛型是基于没有写好的泛型对象的基础上,可以自定义一个来解决参数类型不确定的情况

通配符解决:

    //使用通配符
public <T> T test(ArrayList<?> arr){
return arr.get(1);
}

对已经定义好的泛型对象,ArrayList<?>使用通配符是最常用的

泛型的上下限

泛型变量出现就是为了解决,未知的数据类型,但是它也有一个缺点,所谓得于斯者,毁于斯,正因为,他包含很多种数据类型可以通配

我们在使用泛型方法的时候,就不能限制不是我们需要的参数类型进入方法计算,如我们泛型方法不能处理字符型的数据,但是还是传入了字符数据,泛型方法就不能解决它

所以需要用到泛型的上下限来解决

泛型的上限,E extend  Animal ,即只能有Animal对象或者它的子类进行传入方法,其它的参数类型是不能传入此泛型方法的,相当于Animal就是此方法的上限

 public static <T extends Animal>  T test(T t){
return t;
}

相同的还有泛型的下限,E  super Animal,即只能有Animal对象或者它的父类传入方法,其它的类型是传入不了此方法的

    public static <T>  void test(ArrayList<? super Animal> t){

    }

注意点

  • 泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了,这就是泛型擦除
//.java文件
public static void main(String[] args) {
//ArrayList
ArrayList<String> list = new ArrayList<>();
list.add("java");
list.add("mysql");
//调用方法
String rs = test(list);
System.out.println(rs);
}
//.class文件
public static void main(String[] args) {
ArrayList<String> list = new ArrayList();
list.add("java");
list.add("mysql");
String rs = (String)test(list);
System.out.println(rs);
}

可以看到.class文件中没有了泛型,都是用的强转来进行数据操作的

  • 泛型不支持基本数据类型,只支持对象类型(引用类型)

如上,

对于基本数据类型int,编译器则提示需要转为int的封装类型Interger

JavaImprove--Lesson01--枚举类,泛型的更多相关文章

  1. Java基础(七)泛型数组列表ArrayList与枚举类Enum

    一.泛型数组列表ArrayList 1.在Java中,ArrayList类可以解决运行时动态更改数组的问题.ArrayList使用起来有点像数组,但是在添加或删除元素时,具有自动调节数组容量的功能,而 ...

  2. Java基础15:深入剖析Java枚举类

    更多内容请关注微信公众号[Java技术江湖] 这是一位阿里 Java 工程师的技术小站,作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux ...

  3. Kotlin 枚举类

    枚举类最基本的用法是实现一个类型安全的枚举. 枚举常量用逗号分隔,每个枚举常量都是一个对象. enum class Color{ RED,BLACK,BLUE,GREEN,WHITE } 枚举初始化 ...

  4. JAVA常用基础知识点[继承,抽象,接口,静态,枚举,反射,泛型,多线程...]

    类的继承 Java只支持单继承,不允许多重继承- 一个子类只能有一个父类- 一个父类可以派生出多个子类这里写图片描述子类继承了父类,就继承了父类的方法和属性.在子类中,可以使用父类中定义的方法和属性, ...

  5. 程序猿的日常——Java基础之抽象类与接口、枚举、泛型

    再次回顾这些基础内容,发现自己理解的又多了一点.对于一些之前很模糊的概念,渐渐的清晰起来. 抽象类与接口 抽象类通常是描述一些对象的通用方法和属性,并且默认实现一些功能,它不能被实例化.接口仅仅是描述 ...

  6. Kotlin——中级篇(五):枚举类(Enum)、接口类(Interface)详解

    在上一章节中,详细的类(class)做了一个实例讲解,提到了类(class)的实例化.构造函数.声明.实现方式.和Java中类的区别等.但是对于Kotlin中的类的使用还远远不止那些.并且在上文中提到 ...

  7. AJPFX关于枚举,泛型详解

    枚举类型是JDK5.0的新特征.Sun引进了一个全新的关键字enum来定义一个枚举类.下面就是一个典型枚举类型的定义:public enum Color{RED,BLUE,BLACK,YELLOW,G ...

  8. 夯实Java基础系列14:深入理解Java枚举类

    目录 初探枚举类 枚举类-语法 枚举类的具体使用 使用枚举类的注意事项 枚举类的实现原理 枚举类实战 实战一无参 实战二有一参 实战三有两参 枚举类总结 枚举 API 总结 参考文章 微信公众号 Ja ...

  9. Java枚举类和注解梳理

    1. 枚举类 1. 枚举类的使用 枚举类的理解:类的对象只有有限个,确定的.我们称此类为枚举类. 当需要定义一组常量时,强烈建议使用枚举类. 如果枚举类中只有一个对象,则可以作为单例模式的实现方式. ...

  10. Java笔记---枚举类和注解

    Java笔记---枚举类和注解 一.枚举类 自定义枚举类 方式一:JDK5.0之前自定义枚举类 class Seasons { //1. 声明Seasons对象的属性 private final St ...

随机推荐

  1. mac应用已损坏无法打开

    sudo xattr -r -d com.apple.quarantine /User/name/yourapp # '/User/name/yourapp' 替换成你自己要安装的 mac 应用地址 ...

  2. 我在前端写Java SpringBoot项目

    前言 玩归玩,闹归闹,别拿 C端 开玩笑! 这里不推荐大家把Node服务作为C端服务,毕竟它是单线程多任务 机制. 这一特性是 Javascript 语言设计之初,就决定了它的使命 - Java &g ...

  3. 随身wifi 救砖过程记录

    7,8块钱买了个随身wifi,准备刷机玩的,后来不知道刷错了boot还是啥,加电后灯都不亮了,前期没备份,于是网上找了各种教程,下面记录下: 变砖后有个底层的9008驱动协议可以刷机,下面的过程都是基 ...

  4. js排序算法--冒泡排序

    <!DOCTYPE html> <html> <head> <title></title> </head> <body&g ...

  5. 【日常收支账本】【Day03】完成编辑账本界面的新增动账记录功能——通过ElementTree加XPath实现

    一.项目地址 https://github.com/LinFeng-BingYi/DailyAccountBook 二.新增 1. 解析xml文件 1.1 功能详述 解析所设计的xml文件格式,并将所 ...

  6. jenkins原理篇——成员权限管理

    大家好,我是蓝胖子,前面几节我讲述了jenkins的语法以及我是如何使用jenkins对测试和正式环境进行发布的.但正式环境使用jenkins还有一点很重要,那就是权限管理.正式环境的权限往往不能对所 ...

  7. STM32CUBEIDE中 Debug 和 Release 的作用/区别/使用场景

    基本主流IDE都有该功能选项例如Keil MDK, IAR, Eclipse, VS等, 这里使用STM32CUBEIDE来举例 创建STM32CUBEIDE工程后默认有2个目标选项 Debug / ...

  8. 一、Linux发展史

    一.Linux发展史及红帽认证 红帽授权培训合作伙伴 木兰宽松许可证 1. Linux系统发展史 1. Unix发展历程 上世纪六十年代贝尔实验室(Bell).麻省理工学院(MIT)以及通用电气(GE ...

  9. classpath 和 classpath* 的区别

    classpath 和 classpath* 的区别 classpath 和 classpath* 是两种不同的类路径搜索模式,它们在寻找资源文件时有所不同: classpath:classpath ...

  10. GOF23--23种设计模式(一)

    一.什么是设计模式 设计模式(Design  Pattern)是前辈们对代码开发经验的总结,是解决一系列特定问题的套路. 它不是语法规定,而是一套用来提高代码复用性,可读性,可维护性,稳健性,安全性的 ...