什么状况下使用枚举类?
有的时候一个类的对象是有限且固定的,这种状况下咱们使用枚举类就比较方便。java
public static final int SEASON_SPRING = 1; public static final int SEASON_SUMMER = 2; public static final int SEASON_FALL = 3; public static final int SEASON_WINTER = 4;
枚举类更加直观,类型安全。使用常量会有如下几个缺陷:算法
3.枚举类入门
先看一个简单的枚举类数组
package enumcase; public enum SeasonEnum { SPRING,SUMMER,FALL,WINTER; }
1.enum和class、interface的地位同样
2.使用enum定义的枚举类默认继承了java.lang.Enum,而不是继承Object类。枚举类能够实现一个或多个接口。
3.枚举类的全部实例都必须放在第一行展现,不需使用new 关键字,不需显式调用构造器。自动添加public static final修饰。
4.使用enum定义、非抽象的枚举类默认使用final修饰,不能够被继承。
5.枚举类的构造器只能是私有的。安全
4.枚举类介绍
枚举类内也能够定义属性和方法,但是是静态的和非静态的。数据结构
package enumcase; public enum SeasonEnum { SPRING("春天"),SUMMER("夏天"),FALL("秋天"),WINTER("冬天"); private final String name; private SeasonEnum(String name) { this.name = name; } public String getName() { return name; } }
实际上在第一行写枚举类实例的时候,默认是调用了构造器的,因此此处须要传入参数,由于没有显式申明无参构造器,只能调用有参数的构造器。
构造器需定义成私有的,这样就不能在别处申明此类的对象了。枚举类一般应该设计成不可变类,它的Field不该该被改变,这样会更安全,并且代码更加简洁。因此咱们将Field用private final修饰。框架
5.switch语句里的表达式能够是枚举值dom
package enumcase; public class SeasonTest { public void judge(SeasonEnum s) { switch(s) { case SPRING: System.out.println("春天适合踏青。"); break; case SUMMER: System.out.println("夏天要去游泳啦。"); break; case FALL: System.out.println("秋天必定要去旅游哦。"); break; case WINTER: System.out.println("冬天要是下雪就好啦。"); break; } } public static void main(String[] args) { SeasonEnum s = SeasonEnum.SPRING; SeasonTest test = new SeasonTest(); test.judge(s); } }
1、Java中的日期概述
日期在Java中是一块很是复杂的内容,对于一个日期在不一样的语言国别环境中,日期的国际化,日期和时间之间的转换,日期的加减运算,日期的展现格式都是很是复杂的问题。
在Java中,操做日期主要涉及到一下几个类:
一、java.util.Date
类 Date 表示特定的瞬间,精确到毫秒。从 JDK 1.1 开始,应该使用 Calendar 类实现日期和时间字段之间转换,使用 DateFormat 类来格式化和分析日期字符串。Date 中的把日期解释为年、月、日、小时、分钟和秒值的方法已废弃。
二、java.text.DateFormat(抽象类)
DateFormat 是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并分析日期或时间。日期/时间格式化子类(如 SimpleDateFormat)容许进行格式化(也就是日期 -> 文本)、分析(文本-> 日期)和标准化。将日期表示为 Date 对象,或者表示为从 GMT(格林尼治标准时间)1970 年,1 月 1 日 00:00:00 这一刻开始的毫秒数。
三、java.text.SimpleDateFormat(DateFormat的直接子类)
SimpleDateFormat 是一个以与语言环境相关的方式来格式化和分析日期的具体类。它容许进行格式化(日期 -> 文本)、分析(文本 -> 日期)和规范化。
SimpleDateFormat 使得能够选择任何用户定义的日期-时间格式的模式。可是,仍然建议经过 DateFormat 中的 getTimeInstance、getDateInstance 或 getDateTimeInstance 来新的建立日期-时间格式化程序。
四、java.util.Calendar(抽象类)
Calendar 类是一个抽象类,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日历字段之间的转换提供了一些方法,并为操做日历字段(例如得到下星期的日期)提供了一些方法。瞬间可用毫秒值来表示,它是距历元(即格林威治标准时间 1970 年 1 月 1 日的 00:00:00.000,格里高利历)的偏移量。
与其余语言环境敏感类同样,Calendar 提供了一个类方法 getInstance,以得到此类型的一个通用的对象。Calendar 的 getInstance 方法返回一个 Calendar 对象,其日历字段已由当前日期和时间初始化。
五、java.util.GregorianCalendar(Calendar的直接子类)
GregorianCalendar 是 Calendar 的一个具体子类,提供了世界上大多数国家使用的标准日历系统。
GregorianCalendar 是一种混合日历,在单一间断性的支持下同时支持儒略历和格里高利历系统,在默认状况下,它对应格里高利日历创立时的格里高利历日期(某些国家是在 1582 年 10 月 15 日创立,在其余国家要晚一些)。可由调用方经过调用 setGregorianChange() 来更改起始日期。
代码清单13.1 使用Instant来计时一项操做函数
import java.time.Duration; import java.time.Instant; public class InstantDemo1{ public static void main(String[] args){ Instant start=Instant.now(); System.out.println("Hello World"); Instant end=Instant.now(); System.out.println(Duration.between(start,end).toMillis()); } }
LocalDate类建模了没有时间部分的日期。
代码清单13.2 LocalDate示例性能
import java.time.LocalDate; import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; public class LocalDateDemo1{ public static void main(String[] args){ LocalDate today=LocalDate.now(); LocalDate tomorrow=today.plusDays(1); LocalDate oneDecadeAgo=today.minus(1, ChronoUnit.DECADES); System.out.println("Day of month:" +today.getDayOfMonth()); System.out.println("Today is"+today); System.out.println("Tomorrow is"+tomorrow); System.out.println("A decade ago was"+oneDecadeAgo); System.out.println("Year:" +today.get(ChronoField.YEAR)); System.out.println("Day of year:"+today.getDayOfYear()); } }
一、java.util.Date的API简介
类 java.util.Date 表示特定的瞬间,精确到毫秒。提供了不少的方法,可是不少已通过时,不推荐使用,下面仅仅列出没有过期的方法:
构造方法摘要
-------------
Date()
分配 Date 对象并用当前时间初始化此对象,以表示分配它的时间(精确到毫秒)。
Date(long date)
分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。
方法摘要
-------------
boolean after(Date when)
测试此日期是否在指定日期以后。
boolean before(Date when)
测试此日期是否在指定日期以前。
Object clone()
返回此对象的副本。
int compareTo(Date anotherDate)
比较两个日期的顺序。
boolean equals(Object obj)
比较两个日期的相等性。
long getTime()
返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。
int hashCode()
返回此对象的哈希码值。
void setTime(long time)
设置此 Date 对象,以表示 1970 年 1 月 1 日 00:00:00 GMT 之后 time 毫秒的时间点。
String toString()
把此 Date 对象转换为如下形式的 String: dow mon dd hh:mm:ss zzz yyyy 其中:
dow 是一周中的某一天 (Sun, Mon, Tue, Wed, Thu, Fri, Sat)。
mon 是月份 (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec)。
dd 是一月中的某一天(01 至 31),显示为两位十进制数。
hh 是一天中的小时(00 至 23),显示为两位十进制数。
mm 是小时中的分钟(00 至 59),显示为两位十进制数。
ss 是分钟中的秒数(00 至 61),显示为两位十进制数。
zzz 是时区(并能够反映夏令时)。标准时区缩写包括方法 parse 识别的时区缩写。若是不提供时区信息,则 zzz 为空,即根本不包括任何字符。
yyyy 是年份,显示为 4 位十进制数。测试
import java.util.Date; /** * Created by IntelliJ IDEA. * User: leizhimin * Date: 2007-11-30 * Time: 8:45:44 * 日期测试 */ public class TestDate { public static void main(String args[]) { TestDate nowDate = new TestDate(); nowDate.getSystemCurrentTime(); nowDate.getCurrentDate(); } /** * 获取系统当前时间 * System.currentTimeMillis()返回系统当前时间,结果为1970年1月1日0时0分0秒开始,到程序执行取得系统时间为止所通过的毫秒数 * 1秒=1000毫秒 */ public void getSystemCurrentTime() { System.out.println("----获取系统当前时间----"); System.out.println("系统当前时间 = " + System.currentTimeMillis()); } /** * 经过Date类获取当前日期和当前时间 * date.toString()把日期转换为dow mon dd hh:mm:ss zzz yyyy */ public void getCurrentDate() { System.out.println("----获取系统当前日期----"); //建立并初始化一个日期(初始值为当前日期) Date date = new Date(); System.out.println("如今的日期是 = " + date.toString()); System.out.println("自1970年1月1日0时0分0秒开始至今所经历的毫秒数 = " + date.getTime()); } } 运行结果: ----获取系统当前时间---- 系统当前时间 = 1196413077278 ----获取系统当前日期---- 如今的日期是 = Fri Nov 30 16:57:57 CST 2007 自1970年1月1日0时0分0秒开始至今所经历的毫秒数 = 1196413077278
二、java.text.DateFormat抽象类的使用
DateFormat 是日期/时间格式化子类的抽象类,它以与语言无关的方式格式化并分析日期或时间。日期/时间格式化子类(如 SimpleDateFormat)容许进行格式化(也就是日期 -> 文本)、分析(文本-> 日期)和标准化。将日期表示为 Date 对象,或者表示为从 GMT(格林尼治标准时间)1970 年,1 月 1 日 00:00:00 这一刻开始的毫秒数。
DateFormat 提供了不少类方法,以得到基于默认或给定语言环境和多种格式化风格的默认日期/时间 Formatter。格式化风格包括 FULL、LONG、MEDIUM 和 SHORT。方法描述中提供了使用这些风格的更多细节和示例。
DateFormat 可帮助进行格式化并分析任何语言环境的日期。对于月、星期,甚至日历格式(阴历和阳历),其代码可彻底与语言环境的约定无关。
要格式化一个当前语言环境下的日期,可以使用某个静态工厂方法:
myString = DateFormat.getDateInstance().format(myDate);
若是格式化多个日期,那么得到该格式并屡次使用它是更为高效的作法,这样系统就没必要屡次获取有关环境语言和国家约定的信息了。
DateFormat df = DateFormat.getDateInstance();
for (int i = 0; i < myDate.length; ++i) {
output.println(df.format(myDate[i]) + "; ");
}
要格式化不一样语言环境的日期,可在 getDateInstance() 的调用中指定它。
DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, Locale.FRANCE);
还可以使用 DateFormat 进行分析。
myDate = df.parse(myString);
使用 getDateInstance 来得到该国家的标准日期格式。另外还提供了一些其余静态工厂方法。使用 getTimeInstance 可得到该国家的时间格式。使用 getDateTimeInstance 可得到日期和时间格式。能够将不一样选项传入这些工厂方法,以控制结果的长度(从 SHORT 到 MEDIUM 到 LONG 再到 FULL)。确切的结果取决于语言环境,可是一般:
SHORT 彻底为数字,如 12.13.52 或 3:30pm
MEDIUM 较长,如 Jan 12, 1952
LONG 更长,如 January 12, 1952 或 3:30:32pm
FULL 是彻底指定,如 Tuesday, April 12, 1952 AD 或 3:30:42pm PST。
若是愿意,还能够在格式上设置时区。若是想对格式化或分析施加更多的控制(或者给予用户更多的控制),能够尝试将从工厂方法所得到的 DateFormat 强制转换为 SimpleDateFormat。这适用于大多数国家;只是要记住将其放入一个 try 代码块中,以防遇到特殊的格式。
还可使用借助 ParsePosition 和 FieldPosition 的分析和格式化方法形式来:逐步地分析字符串的各部分。 对齐任意特定的字段,或者找出字符串在屏幕上的选择位置。
DateFormat 不是同步的。建议为每一个线程建立独立的格式实例。若是多个线程同时访问一个格式,则它必须保持外部同步
三、java.util.Calendar(抽象类)
java.util.Calendar是个抽象类,是系统时间的抽象表示,它为特定瞬间与一组诸如 YEAR、MONTH、DAY_OF_MONTH、HOUR 等 日历字段之间的转换提供了一些方法,并为操做日历字段(例如得到下星期的日期)提供了一些方法。瞬间可用毫秒值来表示,它是距历元(即格林威治标准时间 1970 年 1 月 1 日的 00:00:00.000,格里高利历)的偏移量。
与其余语言环境敏感类同样,Calendar 提供了一个类方法 getInstance,以得到此类型的一个通用的对象。Calendar 的 getInstance 方法返回一个 Calendar 对象,其日历字段已由当前日期和时间初始化。
一个Calendar的实例是系统时间的抽象表示,从Calendar的实例能够知道年月日星期月份时区等信息。Calendar类中有一个静态方法get(int x),经过这个方法能够获取到相关实例的一些值(年月日星期月份等)信息。参数x是一个产量值,在Calendar中有定义。
Calendar中些陷阱,很容易掉下去:
一、Calendar的星期是从周日开始的,常量值为0。
二、Calendar的月份是从一月开始的,常量值为0。
三、Calendar的每月的第一天值为1
说明:对于以上的框架图有以下几点说明
1.全部集合类都位于java.util包下。Java的集合类主要由两个接口派生而出:Collection和Map,Collection和Map是Java集合框架的根接口,这两个接口又包含了一些子接口或实现类。
Collection接口是处理对象集合的根接口,其中定义了不少对元素进行操做的方法。Collection接口有两个主要的子接口List和Set,注意Map不是Collection的子接口,这个要牢记
**1.List接口*
List集合表明一个有序集合,集合中每一个元素都有其对应的顺序索引。List集合容许使用重复元素,能够经过索引来访问指定位置的集合元素。
List接口继承于Collection接口,它能够定义一个容许重复的有序集合。由于List中的元素是有序的,因此咱们能够经过使用索引(元素在List中的位置,相似于数组下标)来访问List中的元素,这相似于Java的数组。
List接口为Collection直接接口。List所表明的是有序的Collection,即它用某种特定的插入顺序来维护元素顺序。用户能够对列表中每一个元素的插入位置进行精确地控制,同时能够根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。实现List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。
(1)ArrayList
ArrayList是一个动态数组,也是咱们最经常使用的集合。它容许任何符合规则的元素插入甚至包括null。每个ArrayList都有一个初始容量(10),该容量表明了数组的大小。随着容器中的元素不断增长,容器的大小也会随着增长。在每次向容器中增长元素的同时都会进行容量检查,当快溢出时,就会进行扩容操做。因此若是咱们明确所插入元素的多少,最好指定一个初始容量值,避免过多的进行扩容操做而浪费时间、效率。 size、isEmpty、get、set、iterator 和 listIterator 操做都以固定时间运行。add 操做以分摊的固定时间运行,也就是说,添加 n 个元素须要 O(n) 时间(因为要考虑到扩容,因此这不仅是添加元素会带来分摊固定时间开销那样简单)。 ArrayList擅长于随机访问。同时ArrayList是非同步的。
(2)LinkedList
一样实现List接口的LinkedList与ArrayList不一样,ArrayList是一个动态数组,而LinkedList是一个双向链表。因此它除了有ArrayList的基本操做方法外还额外提供了get,remove,insert方法在LinkedList的首部或尾部。 因为实现的方式不一样,LinkedList不能随机访问,它全部的操做都是要按照双重链表的须要执行。在列表中索引的操做将从开头或结尾遍历列表(从靠近指定索引的一端)。这样作的好处就是能够经过较低的代价在List中进行插入和删除操做。 与ArrayList同样,LinkedList也是非同步的。若是多个线程同时访问一个List,则必须本身实现访问同步。一种解决方法是在建立List时构造一个同步的List:
List list = Collections.synchronizedList(new LinkedList(...))
2.Set接口
Set是一种不包括重复元素的Collection。它维持它本身的内部排序,因此随机访问没有任何意义。与List同样,它一样容许null的存在可是仅有一个。因为Set接口的特殊性,全部传入Set集合中的元素都必须不一样,同时要注意任何可变对象,若是在对集合中元素进行操做时,致使e1.equals(e2)==true,则一定会产生某些问题。Set接口有三个具体实现类,分别是散列集HashSet、链式散列集LinkedHashSet和树形集TreeSet。
Set是一种不包含重复的元素的Collection,无序,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。须要注意的是:虽然Set中元素没有顺序,可是元素在set中的位置是由该元素的HashCode决定的,其具体位置实际上是固定的。
3.Hash Set
HashSet 是一个没有重复元素的集合。它是由HashMap实现的,不保证元素的顺序(这里所说的没有顺序是指:元素插入的顺序与输出的顺序不一致),并且HashSet容许使用null 元素。HashSet是非同步的,若是多个线程同时访问一个哈希set,而其中至少一个线程修改了该set,那么它必须保持外部同步。 HashSet按Hash算法来存储集合的元素,所以具备很好的存取和查找性能。
HashSet的实现方式大体以下,经过一个HashMap存储元素,元素是存放在HashMap的Key中,而Value统一使用一个Object对象。
HashSet使用和理解中容易出现的误区:
a.HashSet中存放null值
HashSet中是容许存入null值的,可是在HashSet中仅仅可以存入一个null值。
b.HashSet中存储元素的位置是固定的
HashSet中存储的元素的是无序的,这个没什么好说的,可是因为HashSet底层是基于Hash算法实现的,使用了hashcode,因此HashSet中相应的元素的位置是固定的。
c.必须当心操做可变对象(Mutable Object)。若是一个Set中的可变元素改变了自身状态致使Object.equals(Object)=true将致使一些问题。
4.Map接口
Map与List、Set接口不一样,它是由一系列键值对组成的集合,提供了key到Value的映射。同时它也没有继承Collection。在Map中它保证了key与value之间的一一对应关系。也就是说一个key对应一个value,因此它不能存在相同的key值,固然value值能够相同。
1.HashMap
以哈希表数据结构实现,查找对象时经过哈希函数计算其位置,它是为快速查询而设计的,其内部定义了一个hash表数组(Entry[] table),元素会经过哈希转换函数将元素的哈希地址转换成数组中存放的索引,若是有冲突,则使用散列链表的形式将全部相同哈希地址的元素串起来,可能经过查看HashMap.Entry的源码它是一个单链表结构。
5.Iterator
1.Iterator
Iterator的定义以下:
public interface Iterator
Iterator是一个接口,它是集合的迭代器。集合能够经过Iterator去遍历集合中的元素。Iterator提供的API接口以下:
boolean hasNext():判断集合里是否存在下一个元素。若是有,hasNext()方法返回 true。
Object next():返回集合里下一个元素。
void remove():删除集合里上一次next方法返回的元素。
使用示例:
public class IteratorExample { public static void main(String[] args) { ArrayList<String> a = new ArrayList<String>(); a.add("aaa"); a.add("bbb"); a.add("ccc"); System.out.println("Before iterate : " + a); Iterator<String> it = a.iterator(); while (it.hasNext()) { String t = it.next(); if ("bbb".equals(t)) { it.remove(); } } System.out.println("After iterate : " + a); } } 输出结果以下: Before iterate : [aaa, bbb, ccc] After iterate : [aaa, ccc]
1.什么是泛型
泛型,即“参数化类型”。一提到参数,最熟悉的就是定义方法时有形参,而后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,相似于方法中的变量参数,此时类型也定义成参数形式(能够称之为类型形参),而后在使用/调用时传入具体的类型(类型实参)。看着好像有点复杂,首先咱们看下上面那个例子采用泛型的写法。
1 public class GenericTest { 2 3 public static void main(String[] args) { 4 /* 5 List list = new ArrayList(); 6 list.add("qqyumidi"); 7 list.add("corn"); 8 list.add(100); 9 */ 10 11 List<String> list = new ArrayList<String>(); 12 list.add("qqyumidi"); 13 list.add("corn"); 14 //list.add(100); // 1 提示编译错误 15 16 for (int i = 0; i < list.size(); i++) { 17 String name = list.get(i); // 2 18 System.out.println("name:" + name); 19 } 20 } 21 }
采用泛型写法后,在//1处想加入一个Integer类型的对象时会出现编译错误,经过List
结合上面的泛型定义,咱们知道在List
1 public interface List<E> extends Collection<E> { 2 3 int size(); 4 5 boolean isEmpty(); 6 7 boolean contains(Object o); 8 9 Iterator<E> iterator(); 10 11 Object[] toArray(); 12 13 <T> T[] toArray(T[] a); 14 15 boolean add(E e); 16 17 boolean remove(Object o); 18 19 boolean containsAll(Collection<?> c); 20 21 boolean addAll(Collection<? extends E> c); 22 23 boolean addAll(int index, Collection<? extends E> c); 24 25 boolean removeAll(Collection<?> c); 26 27 boolean retainAll(Collection<?> c); 28 29 void clear(); 30 31 boolean equals(Object o); 32 33 int hashCode(); 34 35 E get(int index); 36 37 E set(int index, E element); 38 39 void add(int index, E element); 40 41 E remove(int index); 42 43 int indexOf(Object o); 44 45 int lastIndexOf(Object o); 46 47 ListIterator<E> listIterator(); 48 49 ListIterator<E> listIterator(int index); 50 51 List<E> subList(int fromIndex, int toIndex); 52 }
咱们能够看到,在List接口中采用泛型化定义以后,
天然的,ArrayList做为List接口的实现类,其定义形式是:
1 public class ArrayList<E> extends AbstractList<E> 2 implements List<E>, RandomAccess, Cloneable, java.io.Serializable { 3 4 public boolean add(E e) { 5 ensureCapacityInternal(size + 1); // Increments modCount!! 6 elementData[size++] = e; 7 return true; 8 } 9 10 public E get(int index) { 11 rangeCheck(index); 12 checkForComodification(); 13 return ArrayList.this.elementData(offset + index); 14 } 15 16 //...省略掉其余具体的定义过程 17 18 }
由此,咱们从源代码角度明白了为何//1处加入Integer类型对象编译错误,且//2处get()到的类型直接就是String类型了。
自定义泛型接口、泛型类和泛型方法
从上面的内容中,你们已经明白了泛型的具体运做过程。也知道了接口、类和方法也均可以使用泛型去定义,以及相应的使用。是的,在具体使用时,能够分为泛型接口、泛型类和泛型方法。
自定义泛型接口、泛型类和泛型方法与上述Java源码中的List、ArrayList相似。以下,咱们看一个最简单的泛型类和方法定义:
1 public class GenericTest { 2 3 public static void main(String[] args) { 4 5 Box<String> name = new Box<String>("corn"); 6 System.out.println("name:" + name.getData()); 7 } 8 9 } 10 11 class Box<T> { 12 13 private T data; 14 15 public Box() { 16 17 } 18 19 public Box(T data) { 20 this.data = data; 21 } 22 23 public T getData() { 24 return data; 25 } 26 27 }
在泛型接口、泛型类和泛型方法的定义过程当中,咱们常见的如T、E、K、V等形式的参数经常使用于表示泛型形参,因为接收来自外部使用时候传入的类型实参。那么对于不一样传入的类型实参,生成的相应对象实例的类型是否是同样的呢?
1 public class GenericTest { 2 3 public static void main(String[] args) { 4 5 Box<String> name = new Box<String>("corn"); 6 Box<Integer> age = new Box<Integer>(712); 7 8 System.out.println("name class:" + name.getClass()); // com.qqyumidi.Box 9 System.out.println("age class:" + age.getClass()); // com.qqyumidi.Box 10 System.out.println(name.getClass() == age.getClass()); // true 11 12 } 13 14 }
由此,咱们发现,在使用泛型类时,虽然传入了不一样的泛型实参,但并无真正意义上生成不一样的类型,传入不一样泛型实参的泛型类在内存上只有一个,即仍是原来的最基本的类型(本实例中为Box),固然,在逻辑上咱们能够理解成多个不一样的泛型类型。
究其缘由,在于Java中的泛型这一律念提出的目的,致使其只是做用于代码编译阶段,在编译过程当中,对于正确检验泛型结果后,会将泛型的相关信息擦出,也就是说,成功编译事后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。
对此总结成一句话:泛型类型在逻辑上看以当作是多个不一样的类型,实际上都是相同的基本类型。
statistics.sh脚本运行结果的截图