知行合一
Github
顺翔的技术驿站
顺翔的技术驿站
  • README
  • ABOUTME
  • Computer Science
    • 数据结构与算法
      • 位运算以及位图
      • 随机数
      • 递归
      • 经典排序算法
      • 经典查找算法
      • 数组和动态数组
      • 链表
      • 栈和队列
      • 树
      • 哈希表
    • 计算机网络
      • 物理层
      • 数据链路层
      • 网络层
        • TCP
      • 运输层
      • 应用层
      • HTTP
        • HTTPS的原理
        • DNS详解
        • file协议
        • 邮件协议
    • 设计模式
      • 单例模式
      • 建造者模式
      • 原型模式
      • 工厂模式
      • 享元模式
      • 代理模式
      • 装饰者模式
      • 桥接模式
      • 适配器模式
      • 外观模式
      • 组合模式
      • 事件驱动
      • 有限状态机
      • 备忘录模式
      • 模板方法模式
      • 策略模式
      • 迭代器模式
      • 命令模式
      • 解释器模式
    • 加密与解密
      • 数字证书原理
      • cfssl
  • Programming Language
    • 编程语言学习要素
    • Java
      • 集合
        • List
          • ArrayList
          • Vector
          • Stack
          • LinkedList
        • Iterator
        • Set
          • HashSet
          • TreeSet
        • Map
          • HashMap
          • HashTable
          • TreeMap
          • LinkedHashMap
      • 常用API
        • 日期时间处理
        • System
        • Random
        • Arrays
        • Scanner
        • 格式化输出
      • java特性
        • java5特性
        • java8特性
        • java9特性
        • java10特性
        • java11特性
      • 并发编程
        • 线程基础
        • 线程同步:synchronized及其原理
        • 线程同步: volatile
        • 锁机制
        • 锁的分类与对应的Java实现
        • JUC:同步辅助类
        • JUC: AtomicXXX
        • 线程池
        • ThreadLocal详解
      • 测试
        • 使用JMH进行基准测试
      • JVM
        • 强引用、软引用、弱引用、虚引用
        • jvm内存模型
        • jvm优化
        • GC算法与回收器
        • 静态绑定与动态绑定
      • ORM
        • Mybatis
          • IBatis常用操作
      • Web编程
        • Servlet详解(一)
        • Servlet详解(二):request和response对象
        • Servlet详解(三):会话技术与Cookie
        • JSP详解(一):页面构成、EL表达式
        • JSP详解(二):九大内置对象
        • JavaWeb的编码问题
        • Thymeleaf
      • Velocity
      • Java日志框架总结
      • Spring
        • SpringIOC
        • SpringMVC
        • SpringBoot源码
      • 其他
        • Apache Commons Lang使用总结
        • 使用FtpClient进行ftp操作
        • Java PDF操作总结
        • Java使用zip4j进行文件压缩
        • Java解析Excel总结
    • JVM Language
      • Groovy
      • Scala
    • Kotlin
      • 变量和常量
      • 数据类型
        • 基本数据类型
        • 容器类型
        • 函数类型
        • null和null安全
      • 流程控制
      • 包
      • 面向对象
    • Golang
      • 关键字与标识符
      • 变量和常量
      • 数据类型
      • 函数
      • 常用API
        • 时间日期处理
        • 字符串操作
        • 正则表达式
      • 控制语句
      • 包package
      • 面向对象
      • 错误处理
      • 命令行编程
        • Cobra
      • 文件操作
      • 测试
      • 并发编程
        • sync包详解
      • 数据格式与编码
        • 使用encoding包操作xml
        • 使用encoding包操作json
        • 使用magiconair操作properties
        • 使用go-ini操作ini
      • 反射
      • Build Tools
        • Go Module
        • Go Vendor
      • 日志框架
        • zap日志框架
      • Web编程
        • Gin
    • JavaScript
      • 数据类型
      • ECMAScript
        • ECMAScript6
      • NodeJS
    • TypeScript
      • 变量和常量
      • 数据类型
      • 函数
      • 面向对象
      • 泛型
      • Build Tools
        • tsc编译
        • 与webpack整合
    • Python
      • BuildTools
        • requirements.txt
        • Ananconda
    • Swift
      • 变量和常量
    • Script Language
      • Regex
      • BAT
      • Shell
    • Markup Language
      • Markdown
      • Yaml
  • Build Tools
    • CMake
    • Maven
      • 搭建Nexus私服
      • maven使用场景
    • Gradle
  • Version Control
    • Git
      • Git工作流
      • Git分支管理
      • Git Stash
      • Git Commit Message规范
      • .gitttributes文件
    • SVN
  • Distributed
    • 分布式基础理论
      • 互联网架构演变
      • 架构设计思想AKF拆分原则
      • CAP理论
      • BASE理论
    • 一致性
      • 一致性模型
      • 共识算法
        • Paxos
        • Raft
        • ZAB
      • 复制
        • 主从复制
        • Quorum机制
        • Nacos Distro协议
      • 缓存一致性
        • 双写一致性
        • 多级缓存一致性
    • 事务一致性
      • Seata
      • 本地消息表实现方案
      • 关于dpad的事务问题的分析
    • IO
    • RPC协议
    • 序列化
    • Session共享
    • 分布式协调
      • Zookeeper
        • zk集群4节点搭建
    • 服务治理
      • Dubbo分布式治理
    • 分布式ID
      • 分布式ID生成策略总结
    • 分布式锁
    • 应用服务器
      • Tomcat
    • Web服务器
      • Nginx
        • Nginx的基本配置
        • ab接口压力测试工具
        • nginx模块
        • 随机访问页面
        • 替换响应内容
        • 请求限制
        • 访问控制
        • 状态监测
        • nginx应用场景
        • 代理服务
        • 负载均衡
        • 缓存
        • 静态资源服务器和动静分离
        • 附录
      • Kong
    • 缓存中间件
      • Caffeine
      • memcached
      • Redis
        • Centos下安装Redis
        • RatHat下安装Redis
    • 数据库中间件
      • ShardingSphere
      • MyCat2
    • 消息中间件
      • Kafka
      • RocketMQ
  • Microservices
    • 服务发现
      • Nacos注册中心
      • Consul
    • 配置中心
      • Apollo
    • 消息总线
    • 客户端负载均衡
    • 熔断器
    • 服务网关
    • 链路追踪
      • Skywalking
  • Domain-Specific
    • Auth
      • 有关权限设计的思考
      • 认证方式
      • JWT
    • 任务调度
      • QuartzScheduler
      • Elastic-Job
      • XXL-Job
      • PowerJob
    • 工作流
      • BPM
      • Activiti
      • Flowable
    • 规则引擎
      • Drools
  • Architect
    • DDD领域驱动设计
      • 三层架构设计
      • 四层架构设计
    • Cola
    • 代码设计与代码重构
      • 重构改变既有代码设计
      • 枚举规范化
      • 接口幂等
      • 限流
      • 历史与版本
      • 逻辑删除和唯一索引
      • 业务对象设计
    • 单元测试
      • SpringBoot单元测试实践
    • 项目管理
    • APM
      • SkyWalking
      • Arthas
    • 性能优化
      • 接口性能优化
    • 系统设计
      • 流程中台
      • 短信中台
      • 权限中台
        • 智电运维平台组织架构改造二期
  • Database
    • Oracle
      • Docker下安装oracle11g
    • IBM DB2
    • Mysql
      • 安装Mysql
      • 用户与权限管理
      • MySQL的逻辑架构
      • 存储引擎
      • 索引详解
      • MySql的列类型
      • MySql中表和列的设计
      • MySql的SQL详解
      • 锁机制
      • 事务
      • Mysql函数总结
      • MySql存储过程详解
      • MySql触发器详解
      • Mysql视图详解
      • Mysql中Sql语句的执行顺序
      • 配置MySql主从和读写分离
      • MySql的备份策略
      • MySql分库分表解决方案
      • MySql优化总结
      • MySQL实战调优
        • schema与数据类型优化
    • Mongo
  • File System
    • README
    • HDFS
    • FastDFS
    • MinIO
  • Linux
    • 常用的Linux命令
    • vim
    • Linux磁盘管理
    • Linux系统编程
    • RedHat
      • rpm包管理器具体用法
    • Ubuntu
      • Ubuntu下录制屏幕并做成gif图片
      • Ubuntu20.05LiveServe版安装
  • DevOps
    • VM
      • 新建一个新的Linux虚拟机需要配置的东西
      • VMware桥接模式配置centos
      • VMwareFusion配置Nat静态IP
    • Ansible
    • Container
      • Docker
        • Dockerfile详解
        • DockerCompose详解
      • Containerd
    • Kubernetes
      • 安装k8s
        • 使用Minikube安装k8s
        • centos7.x下使用kubeadm安装k8s1.21
        • ubuntu20下使用kubeadm安装k8s1.21
        • centos7.x下使用二进制方式安装k8s1.20
        • 使用DockerDesktop安装K8s(适用M1芯片)
      • 切换容器引擎
      • 使用k8s部署项目的流程
      • 集群维护-备份升级排错
    • Gitlab
      • GitlabCI/CD
    • CI/CD
      • ArgoCD
  • Big-Data
    • Hadoop
    • MapReduce
    • HDFS
  • Front-End
    • Android
      • Log的使用、自定义Log工具类
      • Android倒计时功能实现
      • 解决ViewDrawableLeft左侧图片大小不可控的问题
      • AndroidSQLite基本用法
      • View的生命周期
      • 工具类
      • WebView详解
      • ViewTreeObserver类监听ViewTree
      • 在onCreate中获取控件的宽高等信息的几种方法
      • View的foreground属性
        • MaterialDesign
          • BottomNavigationBar
          • CardView
          • Elevation高度、shadows阴影、clipping裁剪、tint着色
          • TouchFeedbackRipple波纹动画
      • Volley完全解析——使用、源码
      • Android围住神经猫的实现
      • LookLook剖析,架构概述——MVP、Retrofit+RxJava
      • Android性能优化之渲染
    • Browser
      • 浏览器的工作原理
    • HTML
      • DOCTYPE标签、XHTML与HTML的区别
    • CSS
      • CSS的继承性、层叠性、权重
      • CSS浮动float详解(一):标准文档流
      • CSS浮动float详解(二):使用float
      • CSS浮动float详解(三):清除浮动方案
    • Tools Lib
      • JavaScript 文件下载解决方案-download.js
      • js-url 用于url的js开源库
      • jsuri 用于操作url的js开源库
      • window offset
    • React
      • 模块化和组件
      • 组件的三大核心属性
      • 事件处理
      • 表单数据收集
      • 生命周期
      • DOM的diff算法
      • 工程化
        • 脚手架create-react-app
        • 工程结构和模块化
      • 路由
  • Design
    • 产品设计
      • 交互设计
由 GitBook 提供支持
在本页
  • 拆箱和装箱
  • 示例
  • String 也有自动装箱和拆箱
  • 原理
  • 证明
  • 自动装箱的设计
  • foreach循环
  • foreach遍历不能修改数组/集合
  • ConcurrentModificationException 并发修改异常
  • foreach的原理
  • 变长参数 vararg
  • 静态导入 static iomport
  • 泛型
  • 为什么需要泛型?
  • 使用泛型
  • Heap Pollution
  • 什么时候会发生堆污染
  • 为什么会发生堆污染
  • parameterized vararg type 为什么会发生堆污染警告
  • 枚举
  • 定义枚举
  • 枚举的实现原理
  • java.lang.Enum
  • 枚举的几种用法
  • Java的注解
  • 概念
  • 元注解
  • 常用注解
  • 定义注解
  • 底层实现
  • 类型注解
  • 注解的继承
  • 可重复注解(Repeating Annotations)

这有帮助吗?

在GitHub上编辑
  1. Programming Language
  2. Java
  3. java特性

java5特性

拆箱和装箱

装箱(autoboxing)和拆箱(unboxing)

autoboxing: 将基本数据类型用他们响应的引用类包装起来,使其具有对象的性质

unboxing:和装箱相反,将引用数据类型的对象简化为值类型数据

  1. 在表达式中,基本类型的数据值可以和基本数据对象进行运算

  2. 基本数据类型的数组不能实现自动装箱和拆箱,即int[]不能当成Integer[]使用

示例

// jdk5.0之前
Integer i = new Integer(200);
// jdk5.0-自动装箱
Integer j = 200;

// jdk5.0之前
int ii = i.intValue();
// jdk5.0-自动拆箱
int jj = j;

//进行计算时隐含的有自动拆箱
Integer num = 1;
System.out.print(num--);

其它类型类似,就不演示了。

Object o = 1;

上述语句是正确的,1 会被自动装箱为Integer类型,赋值给Object对象。

String 也有自动装箱和拆箱

String str = "sl";
等价于
String str = String.valueOf("sl");

//代替下面的声明方式
String str = new String("sl");

原理

自动装箱时编译器调用 valueOf() 将原始类型值转换成对象,同时自动拆箱时,编译器通过调用类似 intValue(), doubleValue() 这类的方法将对象转换成原始类型值。

即:

Integer i = 200;
等价于
Integer i = Integer.valueOf(200);

int j = i;
等价于
int j = i.intValue();

证明

public class Test {
    public static void main(String[] args){
       Integer  i  =  100; // 装箱
       int  j  = i; // 拆箱
    }
}

使用 javap -c Test 对上述代码的字节码进行反编译。

自动装箱的设计

//在-128~127 之外的数
Integer num1 = 127; Integer num2 = 127;
System.out.println("num1==num2: "+(num1==num2)); // true

// 在-128~127 之内的数
Integer num3 = 128; Integer num4 = 128;
System.out.println("num3==num4: "+(num3==num4)); // false

这是因为Java自动装箱/拆箱使用 享元模式:

享元模式为了加大对简单数字的重用,所以定义:在自动装箱对于值-128到127内的值,他们被装箱为Integer(也包括Long、Character)对象后,会在内存中重用(指向同一个对象),始终指存在一个对象。

如果超过了这个范围(-128~127),被装箱后的Integer对象则不会被重用,所以每次装箱都会创建一个对象。

注意:只有 Long,Integer, Character有享元模式。Double,Float,Byte都没有享元模式

查看Integer源码:

public static Integer valueOf(int i) {
      // 我们发现如果传入的数在这个数组范围内, 则直接返回告诉缓存中的对象,否则创建一个新的对象
      // 这就是【享元模式】,为了性能而有的设计
      if (i >= IntegerCache.low && i <= IntegerCache.high)
          return IntegerCache.cache[i + (-IntegerCache.low)];
          return new Integer(i);
      }

CacheInteger:

    // Integer内部类
    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        // 内部是一个静态的不可变缓存数组
        static final Integer cache[];

    // 这里有一个静态代码块用于进行初始化
        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;
            // 初始化了一个Integer数组,大小为 127 - -128 +1
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }
     }

foreach循环

JDK1.5提供了foreach循环,冒号左边为当前遍历到的元素,右边为遍历的集合/数组。

public static void foreach() {
    String[] strs = new String[]{"a", "b", "c"};
    for (String a : strs) {
        System.out.println(a);
    }
}

foreach遍历不能修改数组/集合

public static void foreach() {
    String[] strs = new String[]{"a", "b", "c"};
    for (String a : strs) {
        a = "1";
    }
    System.out.println(Arrays.toString(strs));
}
// 运行结果 仍然为 [a, b, c]

foreach每次loop的时候,获取的都是对象的值,而不是对象的句柄,所以数组永远不会改变。

ConcurrentModificationException 并发修改异常

遍历集合时,修改集合同样会出现ConcurrentModificationException异常:

for(String language: list){
    list.remove(language);
}

foreach的原理

foreach遍历的原理是基于java.lang.Iterable接口,只要继承该接口,就可以使用foreach对对象进行遍历。

import java.util.*;
public class Test {
    static List<Integer> integers;
    public static void main(String[] args) {
        for(Integer i : integers){
        }
    }
}
  1. 加载this对象到栈

  2. 加载字段包含integers

  3. 调用integers接口iteratable方法iterator,获取Iterator对象

  4. 使用Iterator.hasNext()判断是否有下一个值,如果有调用Iterator.next()获取值,并赋值给变量i

注意,数组并没有实现Iterale接口,数组使用foreach遍历的字节码如下:

public class Test {
    static Integer[] integers = new Integer[]{1,2,3,4,5,6,7};
    public static void main(String[] args) {
        for(Integer i : integers){
        }
    }
}

变长参数 vararg

private String print(Object... values) {
    StringBuilder sb = new StringBuilder();
    for (Object o : values) {
      sb.append(o.toString())
        .append(" ");
    }
    return sb.toString();
}

变长参数 values 实际还是一个数组类型

静态导入 static iomport

静态导入可以导入Class中的所有static变量以及方法:

import static java.lang.System.err;
import static java.lang.System.out;
import java.io.IOException;
import java.io.PrintStream;
public class StaticImporter {
  public static void writeError(PrintStream err, String msg) 
    throws IOException {
   
    // Note that err in the parameter list overshadows the imported err
    err.println(msg); 
  }
  public static void main(String[] args) {
    if (args.length < 2) {
      err.println(
        "Incorrect usage: java com.oreilly.tiger.ch08 [arg1] [arg2]");
      return;
    }
    out.println("Good morning, " + args[0]);
    out.println("Have a " + args[1] + " day!");
    try {
      writeError(System.out, "Error occurred.");
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

泛型

为什么需要泛型?

List list = new ArrayList();
list.add("A");
list.add(100);
for (int i = 0; i < list.size(); i++) {
  String name = (String) list.get(i); //取出Integer时,运行时出现异常
System.out.println("name:" + name);
}

使用泛型

List<String> list = new ArrayList<String>();

通过List,直接限定了list集合中只能含有String类型的元素,集合记住了元素的类型,无需进行强制转换

Heap Pollution

什么是堆污染:

堆污染是一种技术用语,它指的是对象的引用类型不是指向对象类型或者超类类型。

List<A> listOfAs = new ArrayList<>();
List<B> listOfBs = (List<B>)(Object)listOfAs; // 指向as列表,造成堆污染
// 这会导致ClassCastExceptions

所以,当将一个不带泛型的对象赋值给带泛型的变量是,通常会引发堆污染。

什么时候会发生堆污染

把一个不带泛型的对象赋给一个带泛型的变量是,就会发生堆污染. 通常编译期间都会检测出这种情况,并且显示警告: unchecked warning.

List ns = new ArrayList<Number>();
List<String> ls = ns;

为什么会发生堆污染

在Java中,参数化类型(泛型),都是非具体化类型(non-reifiable types)。

non-reifiable types 是指,在runtime时期是不完整的类型,在编译期间,非具体化类型会经过 “类型擦除”(编译器会删除域类型参数相关的信息),所以准确的说,泛型只存在于编译时期。

非具体化类型保证了那些使用JDK1.5(没有泛型)之前的程序的二进制兼容性。

因此,不同泛型的同一类型变量,在运行时具有相同的类或接口实现。所以,当参数化类型的变量( List<String>)指的不是该参数化类型的对象( List<Integer>)时,就发生了堆污染。

Set s = new TreeSet<Integer>();
Set<String> ss = s;            // unchecked warning
s.add(new Integer(42));        // another unchecked warning
Iterator<String> iter = ss.iterator();
while (iter.hasNext())
{
    String str = iter.next();   // ClassCastException thrown
    System.out.println(str);
}

parameterized vararg type 为什么会发生堆污染警告

有如下参数化可变参数类型的方法会发生堆污染警告:

public static <T> void foo(List<T>... bar) {
    for (List<T> ts : bar) {
        System.out.println(ts);
    }
}
// warning: Possible heap pollution from parameterized vararg type

参数化可变参数类型可能发生堆污染,这是因为,可变参数在编译时期,会做出如下转换:

  • public static <T> void foo(List<T>... bar) 函数,被转换为

  • public static <T> void foo(List<T>[] bar),然后再转换为

  • public static <T> void foo(List[] bar)

这样,我们如下调用:

Object[] objectArray = new String[]{};
List<Integer>[] lists = (List<Integer>[]) objectArray;
foo(lists);

就会发生 ClassCastException 异常,(java.lang.String; cannot be cast tojava.util.List)。所以,会出现堆污染警告。

枚举

JDK1.5引入枚举类型,他是一种特殊的数据类型,即使一种类(class)类型但是又比普通的类类型多了些特殊的约束,这些约束造就了枚举类型的简洁性,安全性以及便捷性。

定义枚举

enum Day {
    MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

引用枚举:

Day day =Day.MONDAY;

枚举的实现原理

将上述day类编译并使用反编译得到如下:

//反编译Day.class
// enum 就是继承Enum类型的类
final class Day extends Enum
{
    //编译器为我们添加的静态的values()方法
    public static Day[] values()
    {
        return (Day[])$VALUES.clone();
    }
    //编译器为我们添加的静态的valueOf()方法,注意间接调用了Enum也类的valueOf方法
    public static Day valueOf(String s)
    {
        return (Day)Enum.valueOf(com/zejian/enumdemo/Day, s);
    }
    //私有构造函数
    private Day(String s, int i)
    {
        super(s, i);
    }
     //前面定义的7种枚举实例
    public static final Day MONDAY;
    public static final Day TUESDAY;
    public static final Day WEDNESDAY;
    public static final Day THURSDAY;
    public static final Day FRIDAY;
    public static final Day SATURDAY;
    public static final Day SUNDAY;
    private static final Day $VALUES[];

    static
    {
        //实例化枚举实例
        MONDAY = new Day("MONDAY", 0);
        TUESDAY = new Day("TUESDAY", 1);
        WEDNESDAY = new Day("WEDNESDAY", 2);
        THURSDAY = new Day("THURSDAY", 3);
        FRIDAY = new Day("FRIDAY", 4);
        SATURDAY = new Day("SATURDAY", 5);
        SUNDAY = new Day("SUNDAY", 6);
        $VALUES = (new Day[] {
            MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
        });
    }
}
  1. 编译器在编译enum时,会生成一个final的 Day 类,并且此类继承 java.lang.Enum

  2. 根据枚举元素,生成了七个Day类型的实例对象,并且都是 public static final 修饰,所以,使用enum定义的枚举元素都是一个个Day实例

  3. 此外,编译器还生成了两个静态方法,分别为 values() 和 valueOf()

java.lang.Enum

上面说到,使用enum修饰的枚举类型,在编译期间会自动继承Enum类,下面是Enum类的使用和源码:

首先,Enum是一个抽象类,实现了序列化接口Serializable和比较接口 Comparable,这意味着Enum可以被序列化以及被比较。

构造:

  • Enum只有一个包私有构造器: protected Enum(String name, int ordinal)

从Object继承的方法:

  • protected Object clone() CloneNotSupportedException,enum类型不支持对象克隆

  • protected void finalize() 枚举类不能有 finalize 方法

  • String toString() 返回枚举常量的名称,它包含在声明中

  • int hashCode()

  • boolean equals(Object other) 当指定对象等于此枚举常量时,返回 true

比较方法Compareable:

  • int compareTo(E o)

特有方法:

  • Class<E> getDeclaringClass() 返回枚举常量对应的Class对象

  • int ordinal() 返回枚举常量的序数,它在枚举声明中的位置,其中初始常量序数为零,比如 Day.MONDAY.ordinal() == 0

  • String name() 返回此枚举常量的名称,和toString功能类似

  • static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) 返回带指定名称的指定枚举类型的枚举常量

编译器生成的 values 方法和 valueOf 方法:

  • values()方法和valueOf(String name)方法是编译器生成的static方法

    Day[] days2 = Day.values();
    System.out.println("day2:"+Arrays.toString(days2));
    Day day = Day.valueOf("MONDAY");
    System.out.println("day:"+day);

源码解析:

package java.lang;

import java.io.Serializable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;

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

    // 枚举常量名称
    private final String name;

    // 返回枚举常量的名称
    public final String name() {
        return name;
    }

    // 枚举常量在枚举中的位置
    private final int ordinal;

    // 获取枚举常量的序数
    public final int ordinal() {
        return ordinal;
    }

    // 指定枚举常量名称和序数,构建一个枚举常量
    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }
    // toString和name()别无二致
    public String toString() {
        return name;
    }
    // 使用equals比较的是内存地址
    public final boolean equals(Object other) {
        return this==other;
    }

    // 禁止子类重写hashCode
    public final int hashCode() {
        return super.hashCode();
    }

    // 不支持对象克隆
    protected final Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }
    // 比较方法
    // 1. 比较对象class类型
    // 2. 比较对象的DeclaringClass类型
    // 3. 比较序数
    public final int compareTo(E o) {
        Enum<?> other = (Enum<?>)o;
        Enum<E> self = this;
        if (self.getClass() != other.getClass() && // optimization
            self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;
    }

    @SuppressWarnings("unchecked")
    public final Class<E> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
    }

    //根据类型和名称获取泛型
    public static <T extends Enum<T>> T valueOf(Class<T> enumType,String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null)
            return result;
        if (name == null)
            throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
            "No enum constant " + enumType.getCanonicalName() + "." + name);
    }

    // 使finalize方法无效
    protected final void finalize() { }

    private void readObject(ObjectInputStream in) throws IOException,
        ClassNotFoundException {
        throw new InvalidObjectException("can't deserialize enum");
    }

    private void readObjectNoData() throws ObjectStreamException {
        throw new InvalidObjectException("can't deserialize enum");
    }
}

枚举的几种用法

1. 用作常量

在JDK1.5之前,定义常量需要使用 public static final

在JDK1.5,我们可以这样定义常量:

public enum Color {
    GREEN, RED, ORANGE
}

2. switch语句

JDK1.6之前switch语句只支持 int, char, enum 类型,使用枚举可以让代码可读性更强

enum Signal {
    GREEN, YELLOW, RED
}
public class TrafficLight {
    Signal color = Signal.RED;
    public void change() {
        switch (color) {
        case RED:
            color = Signal.GREEN;
            break;
        case YELLOW:
            color = Signal.RED;
            break;
        case GREEN:
            color = Signal.YELLOW;
            break;
        }
    }
}

3. 添加构造/方法/字段

因为enum本身是一个类,而元素又是enum的对象,所以我们可以添加字段记录一些值,使用构造初始化这些值:

public enum Day2 {
    MONDAY("星期一"),
    TUESDAY("星期二"),
    WEDNESDAY("星期三"),
    THURSDAY("星期四"),
    FRIDAY("星期五"),
    SATURDAY("星期六"),
    SUNDAY("星期日");//记住要用分号结束

    private String desc;//中文描述

    /**
     * 私有构造,防止被外部调用
     * @param desc
     */
    private Day2(String desc){
        this.desc=desc;
    }

    /**
     * 定义方法,返回描述,跟常规类的定义没区别
     * @return
     */
    public String getDesc(){
        return desc;
    }

    public static void main(String[] args){
        for (Day2 day:Day2.values()) {
            System.out.println("name:"+day.name()+
                    ",desc:"+day.getDesc());
        }
    }

    /**
     输出结果:
     name:MONDAY,desc:星期一
     name:TUESDAY,desc:星期二
     name:WEDNESDAY,desc:星期三
     name:THURSDAY,desc:星期四
     name:FRIDAY,desc:星期五
     name:SATURDAY,desc:星期六
     name:SUNDAY,desc:星期日
     */
}

也可以添加一些方法,作为工具类调用。

4. 覆盖toString

public enum Day2 {
    MONDAY("星期一"),
    TUESDAY("星期二"),
    WEDNESDAY("星期三"),
    THURSDAY("星期四"),
    FRIDAY("星期五"),
    SATURDAY("星期六"),
    SUNDAY("星期日");//记住要用分号结束

    private String desc;//中文描述

    /**
     * 私有构造,防止被外部调用
     * @param desc
     */
    private Day2(String desc){
        this.desc=desc;
    }

    /**
     * 覆盖
     * @return
     */
    @Override
    public String toString() {
        return desc;
    }

    public static void main(String[] args){
        for (Day2 day:Day2.values()) {
            System.out.println("name:"+day.name()+
                    ",desc:"+day.toString());
        }
    }
}

5. 定义抽象

package com.zejian.enumdemo;

/**
 * Created by zejian on 2017/5/9.
 * Blog : http://blog.csdn.net/javazejian [原文地址,请尊重原创]
 */
public enum EnumDemo3 {

    FIRST{
        @Override
        public String getInfo() {
            return "FIRST TIME";
        }
    },
    SECOND{
        @Override
        public String getInfo() {
            return "SECOND TIME";
        }
    }

    ;

    /**
     * 定义抽象方法
     * @return
     */
    public abstract String getInfo();

    //测试
    public static void main(String[] args){
        System.out.println("F:"+EnumDemo3.FIRST.getInfo());
        System.out.println("S:"+EnumDemo3.SECOND.getInfo());
        /**
         输出结果:
         F:FIRST TIME
         S:SECOND TIME
         */
    }
}

6. 实现接口

interface food{
    void eat();
}

interface sport{
    void run();
}

public enum EnumDemo2 implements food ,sport{
    FOOD,
    SPORT,
    ; //分号分隔

    @Override
    public void eat() {
        System.out.println("eat.....");
    }

    @Override
    public void run() {
        System.out.println("run.....");
    }
}

我们可能对一组数据进行分类:

public interface Food {
  enum Appetizer implements Food {
    SALAD, SOUP, SPRING_ROLLS;
  }
  enum MainCourse implements Food {
    LASAGNE, BURRITO, PAD_THAI,
    LENTILS, HUMMOUS, VINDALOO;
  }
  enum Dessert implements Food {
    TIRAMISU, GELATO, BLACK_FOREST_CAKE,
    FRUIT, CREME_CARAMEL;
  }
  enum Coffee implements Food {
    BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,
    LATTE, CAPPUCCINO, TEA, HERB_TEA;
  }
}

public class TypeOfFood {
  public static void main(String[] args) {
    Food food = Appetizer.SALAD;
    food = MainCourse.LASAGNE;
    food = Dessert.GELATO;
    food = Coffee.CAPPUCCINO;
  }
}

Java的注解

参考:

概念

Java提供了一种原程序中的元素关联任何信息和任何元数据的途径和方法。(注解就是元数据)

JDK1.5 提供注解特性。

元注解

修饰注解的注解,称为元注解,在JDK中,共有四种元注解:

  • @Target规定注解可以使用的位置,类,方法还是域

  • @Retention表示需要在什么级别保存该注解信息,由RetentionPolicy枚举定义

  • @Documented表示注解会被包含在javaapi文档中

  • @Inherited允许子类继承父类的注解

@Target:

使用ElementType枚举定义,包含以下值:

  • CONSTRUCTOR:构造器的声明

  • FIELD:域声明(包括enum实例)

  • LOCAL_VARIABLE:局部变量声明

  • METHOD:方法声明

  • PACKAGE:包声明

  • PARAMETER:参数声明

  • TYPE:类、接口(包括注解类型)或enum声明

  • ANNOTATION_TYPE:注解声明(应用于另一个注解上)

  • TYPE_PARAMETER:类型参数声明(1.8新加入)

  • TYPE_USE:类型使用声明(1.8新加入)

@Retention:

使用RetentionPolicy枚举定义,包含以下值:

  • SOURCE:注解将被编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)

  • CLASS:注解在class文件中可用,但会被VM丢弃(该类型的注解信息会保留在源码里和class文件里,在执行的时候,不会加载到虚拟机(JVM)中)

  • RUNTIME:VM将在运行期也保留注解信息,因此可以通过反射机制读取注解的信息(源码、class文件和执行的时候都有注解的信息) PS:当注解未定义Retention值时,默认值是CLASS

常用注解

  • java.lang.SuppressWarnings 抑制警告

  • java.lang.Override 方法重写

  • java.lang.Deprecated 已经过时

jdk7新增:

  • @SafeVarargs 堆污染

定义注解

// 定义的注解默认会继承java.lang.annotation.Annotation接口
public @interface 注解名{

}

底层实现

注解的本质实际上是一个Interface,所有注解在反编译后都是继承 java.lang.annotation.Annotation 接口的。

@MyAnnotation // 声明类
public class Test {
}

类型注解

JDK8后,类型注解可以在任何有类型的地方加入注解(包含泛型):

@MyAnnotation
public class Test<@MyAnnotation T> extends @MyAnnotation Fathers implements @MyAnnotation Callable {

    private @MyAnnotation String name = "lisi";

    public @MyAnnotation String test(@MyAnnotation String name) throws @MyAnnotation Exception {
        @MyAnnotation String s = "test";
        @MyAnnotation List<String> list = new @MyAnnotation ArrayList<>();
        Number a = 1;
        Integer i = (@MyAnnotation Integer) a;
        if (list.get(0) == null) {
            throw new @MyAnnotation Exception();
        }
        list.add(s);
        return list.get(0);
    }

    @Override
    public Object call() throws Exception {
        return null;
    }
}

作用:

在8种新增类型注释主要是为了改进Java的程序分析,配合类型检查框架做强类型检查,从而在编译期间确认运行时异常,比如 NullpointException,从而提高代码质量。

比如:

@NonNull Object my = null;

第三方工具会在编译期间自动检测my是否为null,如果为null,抛出异常或者警告

创建类型注解

JDK8 新增 ElementType.TYPE_USE 用来创建类型注解:

@Target({TYPE_USE})
@Retention(RUNTIME)
public @interface MyAnnotation {
}

注意:JDK8还提供了 TYPE_PARAMETER 类型的注解,表示可以修饰类型参数(泛型)的注解。

注解的继承

  1. 首先想让注解可继承,必须增加@Inherited元注解,如果需要反射支持,就需要@Rentention(RetentionPolicy.RUNTIME)标识

  2. JDK文档中,对于注解继承的描述为:只有类上的注解才能被继承

  3. 实际上,如果类中的成员成员变量以及成员方法没有被重写,他们就仍然是父类的那个方法,即使在子类中。故继承自父类的没有被重写的成员,也具有原注解的信息。

  4. 注解的继承不能用在接口上,任何接口上的注解都不能被继承

可重复注解(Repeating Annotations)

在8之前,一个目标只可以打一个注解:

@Test
private String name;

现在JDK8提供了Repeating Annotations,可以在一个目标上打上多个可重复注解:

@Test("a")
@Test("b")
private String name;

创建可重复注解

JDK8中提供了一个新的源注解,用于表示可重复注解:

// 创建可重复注解:指定容器类型为 Sechedules.class
@Repeatable(Schedules.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface Schedule {
    String dayOfMonth() default "first";
    String dayOfWeek() default "Mon";
    int hour() default 12;
}
// 可重复注解的容器注解,用于存放多个可重复注解:
@Retention(RetentionPolicy.RUNTIME)
public @interface Schedules {
    Schedule[] value();
}

获取可重复注解(1.8)

注意:如果想要在运行时获取注解,注解 Retention 必须为 RetentionPolicy.RUNTIME,并且,Schedules与Schedule 注解的保留时期必须都为运行时!

获取可重复注解的方式同获取普通注解没有什么太大的区别:

@Schedule(hour = 9)
@Schedule(hour = 10)
public class Task {

    public static void main(String[] args) throws Exception {
        Schedule[] ats = Task.class.getAnnotationsByType(Schedule.class);
        System.out.println(Arrays.toString(ats));
        // [@test.s.Schedule(hour=9, dayOfMonth=first, dayOfWeek=Mon), @test.s.Schedule(hour=10, dayOfMonth=first, dayOfWeek=Mon)]

        System.out.println(Arrays.toString(Task.class.getAnnotations()));
        // [@test.s.Schedules(value=[@test.s.Schedule(hour=9, dayOfMonth=first, dayOfWeek=Mon), @test.s.Schedule(hour=10, dayOfMonth=first, dayOfWeek=Mon)])]
        //注意:这里获取到的是 Schedules 注解,而不是多个 Schedule 注解
    }
}

可重复注解的原理

使用一个注解来存储重复的注解,编译后的class为:

@Schedules({@Schedule(hour = 9), @Schedule(hour = 10)})
public class Task {
    public Task() {
    }
    public static void main(String[] args) throws Exception {
        Schedule[] ats = (Schedule[])Task.class.getAnnotationsByType(Schedule.class);
        System.out.println(Arrays.toString(ats));
        System.out.println(Arrays.toString(Task.class.getAnnotations()));
    }
}

所谓的可重复注解, 只是编译层面的改动。

上一页java特性下一页java8特性

最后更新于2年前

这有帮助吗?

下面是反编译后: 可以清楚的看到整个步骤:

https://blog.csdn.net/sun_promise/article/details/51315032
https://docs.oracle.com/javase/tutorial/java/annotations/type_annotations.html
https://docs.oracle.com/javase/tutorial/java/annotations/repeating.html
目前具有此项功能的检测框架有
The Check Framework
file
64e8793b
a80370d3