精华内容
下载资源
问答
  • Java版本版本变迁2011年02月16日 00:20:00阅读数:8831995年5月23日,Java语言诞生 1996年1月,第一个JDK-JDK1.0诞生 1996年4月,10个最主要的操作系统供应商申明将在其产品中嵌入JAVA技术 1996年9月,约8.3万...

    Java版本的版本变迁

    1995年5月23日,Java语言诞生 
    1996年1月,第一个JDK-JDK1.0诞生 
    1996年4月,10个最主要的操作系统供应商申明将在其产品中嵌入JAVA技术 
    1996年9月,约8.3万个网页应用了JAVA技术来制作 
    1997年2月18日,JDK1.1发布 
    1997年4月2日,JavaOne会议召开,参与者逾一万人,创当时全球同类会议规模之纪录 
    1997年9月,JavaDeveloperConnection社区成员超过十万 
    1998年2月,JDK1.1被下载超过2,000,000次 
    1998年12月8日,JAVA2企业平台J2EE发布 
    1999年6月,SUN公司发布Java的三个版本:标准版、企业版和微型版(J2SE、J2EE、J2ME) 
    2000年5月8日,JDK1.3发布 
    2000年5月29日,JDK1.4发布 
    2001年6月5日,NOKIA宣布,到2003年将出售1亿部支持Java的手机 
    2001年9月24日,J2EE1.3发布 
    2002年2月26日,J2SE1.4发布,自此Java的计算能力有了大幅提升。 
    2004年9月30日18:00PM,J2SE1.5发布,是Java语言的发展史上的又一里程碑事件。为了表示这个版本的重要性,J2SE1.5更名为J2SE5.0 
    2005年6月,JavaOne大会召开,SUN公司公开Java SE 6。此时,Java的各种版本已经更名以取消其中的数字“2”:J2EE更名为Java EE, J2SE更名为Java SE,J2ME更名为Java ME。 
    2006年11月13日,SUN公司宣布Java全线采纳GNU General Public License Version 2,从而公开了Java的源代码。

    已发行的版本:

    版本号

    名称

    中文名

    发布日期

    JDK 1.1.4

    Sparkler

    宝石

    1997-09-12

    JDK 1.1.5

    Pumpkin

    南瓜

    1997-12-13

    JDK 1.1.6

    Abigail

    阿比盖尔--女子名

    1998-04-24

    JDK 1.1.7

    Brutus

    布鲁图--古罗马政治家和将军

    1998-09-28

    JDK 1.1.8

    Chelsea

    切尔西--城市名

    1999-04-08

    J2SE 1.2

    Playground

    运动场

    1998-12-04

    J2SE 1.2.1

    none

    1999-03-30

    J2SE 1.2.2

    Cricket

    蟋蟀

    1999-07-08

    J2SE 1.3

    Kestrel

    美洲红隼

    2000-05-08

    J2SE 1.3.1

    Ladybird

    瓢虫

    2001-05-17

    J2SE 1.4.0

    Merlin

    灰背隼

    2002-02-13

    J2SE 1.4.1

    grasshopper

    蚱蜢

    2002-09-16

    J2SE 1.4.2

    Mantis

    螳螂

    2003-06-26

    J2SE 5.0 (1.5.0)

    Tiger

    老虎

    2004-09-30

    J2SE 5.1 (1.5.1)

    Dragonfly

    蜻蜓

     未发布

    J2SE 6.0 (1.6.0)

    Mustang

    野马

    2006-04

    J2SE 7.0 (1.7.0)

    Dolphin

    海豚

     未发布

    从JDK1.2.2开始,主要版本(如1.3,1.4,5.0)都是以鸟类或哺乳动物来命名的. 而它们的bug修正版本(如1.2.2,1.3.1,1.4.2)都是以昆虫命名的.

    展开全文
  • JDK从1995年发布起至今已历经20多年,从JDK1.0升级到JDK16(2021年8月),本节先一起回顾一下JDK的版本变迁和各版本的新特性 版本变迁 1995年5月23日,Java语言诞生; 1996年1月,第一个JDK-JDK1.0诞生; 1996年4月...

    JDK从1995年发布起至今已历经20多年,从JDK1.0升级到JDK16(2021年8月),本节先一起回顾一下JDK的版本变迁和各版本的新特性

    版本变迁

    • 1995年5月23日,Java语言诞生;
    • 1996年1月,第一个JDK-JDK1.0诞生;
    • 1996年4月,10个最主要的操作系统供应商申明将在其产品中嵌入Java技术;
    • 1996年9月,约8.3万个网页应用了Java技术来制作;
    • 1997年2月18日JDK1.1发布
    • 1997年4月2日,JavaOne会议召开,参与者逾一万人,创当时全球同类会议纪录;
    • 1997年9月,JavaDeveloperConnection社区成员超过十万;
    • 1998年2月,JDK1.1被下载超过2,000,000次;
    • 1998年12月8日Java 2企业平台J2EE发布
    • 1999年6月,SUN公司发布Java三个版本:标准版(J2SE)、企业版(J2EE)和微型版(J2ME)
    • 2000年5月8日,JDK1.3发布
    • 2001年6月5日,Nokia宣布到2003年将出售1亿部支持Java的手机;
    • 2001年9月24日,J2EE1.3发布;
    • 2002年2月26日,J2SE1.4发布,此后Java的计算能力有了大幅提升
    • 2004年9月30日,J2SE1.5发布,成为Java语言发展史上的又一里程碑。为了表示该版本的重要性,J2SE1.5更名为Java SE 5.0;重要新特性:(里程碑版本)
    • 2005年6月,JavaOne大会召开,SUN公司公开Java SE 6。此时,Java的各种版本已经更名,以取消其中的数字“2”:J2EE更名为Java EE,J2SE更名为Java SE,J2ME更名为Java ME;
    • 2006年12月,SUN公司发布JDK6.0;
    • 2009年12月,SUN公司发布Java EE 6;
    • 2010年11月,由于甲骨文对Java社区的不友善,因此Apache扬言将退出JCP;
    • 2011年7月28日,甲骨文发布Java SE 7;
    • 2014年3月18日,甲骨文发表Java SE 8。(重大版本 - LTS)
    • 2017年9月21日,JDK9发布。从JDK9开始固定为每半年一个版本,更新内容相应缩减。
    • 2018年3月21日,JDK10发布。
    • 2018年9月25日,JDK11发布。(长期维护版本 - LTS)
    • 2019年3月19日,JDK12发布。
    • 2019年9月17日,JDK13发布。
    • 2020年3月17日,JDK14发布。
    • 2020年9月15日,JDK15发布。
    • 2021年3月16日,JDK16发布。
    • 2021年9月, JDK17即将发布。(长期维护版本 - LTS)

    版本新特性

    对重要版本的新特性做一下说明。

    JDK1.2

    改进集合框架,如新增HashMap替代旧的HashTable(常用)
    开始提供JIT(Just In Time)编译器;

    JDK1.3

    Dynamic Proxy: 加入了JDK动态代理(常用)

    JDK1.4

    NIO: 在传统BIO的基础上引入了NIO(Non-Blocking IO)(常用)。

    JDK5

    里程碑版本,大部分人也是从该版本开始接触Java。引入了很多意义深远的特性,如下:
    1)自动拆箱/装箱(Auto Inbox/Outbox)(常用)
    2)枚举(Enum)(常用)
    3)静态导入 (static import)
    4)可变参数列表 (var args)(常用)
    5)范型(Generic)(常用)
    6)增强for循环(for-each)(常用)
    7)注解(Annotation)(常用)
    8)协变返回类型:实际返回类型可以是要求返回类型的一个子类
    9)concurrent并发包(Doug leg)(常用)

    JDK6

    1)Compiler API,可以实现动态编译Java源文件,如jsp编译引擎就是动态的,修改后无需重启服务器
    2)对脚本语言的支持:如js, ruby,groovy

    注:因JDK6至JDK7中间间隔了足足5年,所以这个版本是个经典版本,很稳定,官方长期维护,使用了很长时间。

    JDK7

    JDK7版本从JDK6后5年才发布,原因是中间经历了Sun公司被收购。大体发布历程是:

    2009 年 1 月, Sun 开始开发 JDK7 Milestone 1。
    2009 年 11 月,Sun 在 Devoxx 2009 大会上宣布将闭包特性加入 JDK7,并将 JDK7 的发布日期推迟到 2010 年底。
    2010 年 4 月,Oracle 收购 Sun。
    2010 年 9 月,Mark Reinhold 在 JavaOne 大会上宣布 JDK7 将砍去 Lambda、Jigsaw 和部分 Coin 新特性并于 2011 年中发布,其余部分的新特性将于 2012 年底同 JDK8 一同发布。
    2010 年 10 月,IBM 宣布加入 OpenJDK,将与 Oracle 合作共同开发 JDK。此后,Apple 和 SAP 也陆续加入 OpenJDK 社区。
    2011 年 7 月 28 日,Oracle 正式发布 JDK7,并指定 OpenJDK7 为参考实现。

    特性一:一组小的改进
    1)Switch支持String(常用)
    2)try … with … resource(常用)
    3)范型类型自动推断(常用)
    4)多重catch(常用)
    5)数字可用下划线分割

    特性二:G1垃圾回收器(Grabage-First Collector)(常用)
    新出的垃圾回收器,用来替代Concurrent Mark-Sweep Collector(CMS)。目标是减少 Full GC 带来的暂停次数,增加吞吐量。

    特性三:concurrent包改进(常用)
    Doug Lea在此版本引入了轻量级的fork/join框架来支持多核多线程的并发计算。此外,实现了 Phaser 类,它类似于 CyclicBarrier 和 CountDownLatch 但更灵活。最后,ThreadLocalRandom 类提供了线程安全的伪随机数生成器。

    特性四:IO与网络的更新
    NIO2 主要包括了 3 个方面的改进:

    1. 新的文件系统 API 支持大量文件属性的访问、文件系统监控服务、平台相关的 API,如 DosFileAttributes 和 PosixFileAttributes 等,以及一个可插拔文件系统的 SPI。
    2. Socket 和文件的异步 IO。
    3. Socket channel 的功能完善,支持 binding、多播等。

    特性五:JDBC更新(常用)
    支持JDBC4.1和Rowset 1.1

    JDK8

    1)接口默认方法(常用)
    即接口中可以声明一个非抽象的方法作为默认的实现,方法的返回类型前要加上“default”关键字
    2)lambda表达式(常用)

    3)函数式接口(常用)
    指仅仅包含一个抽象方法的接口,需要标记@FunctionalInterface
    4)使用 :: 来传递方法引用,是lambda的简写(常用)

    5)Stream API(常用)

    6)新的日期工具类(常用)

    JDK9

    1)模块化
    即只加载应用运行需要的模块

    2)改进的Java doc
    生成的Java doc符合H5标准,支持API搜索

    3)集合工厂方法,优化集合初始化(常用)

    Set<Integer> ints = Set.of(1,2,3);
    List<String> strings = List.of("first","second");
    

    4)改进 Stream API

     IntStream.iterate(1, i -> i < 100, i -> i + 1).forEach(System.out::println);
    

    5)支持私有接口方法

    6)HTTP/2(常用)
    新的方式处理HTTP调用,用于替换 HttpURLConnection,并提供对WebSocket和HTTP/2的支持

    7)多版本兼容

    JDK10

    1)局部变量类型推断(常用)
    即只能作为局部变量类型,方法参数,返回类型等不可以。常见示例:

    var str = "ABC"; //根据推断为 字符串类型
    var l = 10L;//根据10L 推断long 类型
    var flag = true;//根据 true推断 boolean 类型
    var flag1 = 1;//这里会推断boolean类型。0表示false 非0表示true
    var list = new ArrayList<String>();  // 推断 ArrayList<String>
    var stream = list.stream();          // 推断 Stream<String>
    

    2)并行full gc的G1
    通过并行Full GC, 改善G1的延迟。目前对G1的full GC的实现采用了单线程-清除-压缩算法。JDK10开始使用并行化-清除-压缩算法。

    3)基于实验Java的JIT编译器
    启用基于Java的JIT编译器Graal,它是JDK9中引入的Ahead-of-time(AOT)编译器的基础。

    JDK11

    1)增加一些实用API(常用)

    String str = "woshidage";
    boolean isblank = str.isBlank();  //判断字符串是空白
    boolean isempty = str.isEmpty();  //判断字符串是否为空
    String  result1 = str.strip();    //首尾空白
    String  result2 = str.stripTrailing();  //去除尾部空白
    String  result3 = str.stripLeading();  //去除首部空白
    String  copyStr = str.repeat(2);  //复制几遍字符串
    long  lineCount = str.lines().count();  //行数统计
    

    2)HttpClient增强(常用)
    同时提供了同步调用和异步调用实现。JDK对http对调用支持已经足够强,以前apache提供的httpComponents基本可以去除了。

    4)ZGC
    JDK11最瞩目的特性,但目前是实验性质的。目标是GC暂停时间不会超过10ms,既能处理几百兆的小堆,也能处理几个T的大堆。

    5)完全支持Linux容器(常用)
    JDK10开始,JVM可以识别当前是否在容器中运行,能接受容器设置的内存限制和CPU限制。

    JDK12
    1. Switch Expression
      传统使用switch的方式:
    
    switch (day) {
    	case MONDAY:
    	case FRIDAY:
    	case SUNDAY:
    	System.out.println(6);
    	break;
    case TUESDAY:
    	System.out.println(7);
    	break;
    case THURSDAY:
    case SATURDAY:
    	System.out.println(8);
    	break;
    case WEDNESDAY:
    	System.out.println(9);
    	break;
    }
    

    如今可简化为:

    switch (day) {
    case MONDAY, FRIDAY, SUNDAY -> System.out.println(6);
    case TUESDAY -> System.out.println(7);
    case THURSDAY, SATURDAY -> System.out.println(8);
    case WEDNESDAY -> System.out.println(9);
    }
    
    JDK13
    1. 字符串拼接

    JDK13前:

    String html = "<html>\n" +
                  "    <body>\n" +
                  "        <p>Hello, world</p>\n" +
                  "    </body>\n" +
                  "</html>\n";
    

    现在:

    String html = """
                  <html>
                      <body>
                          <p>Hello, world</p>
                      </body>
                  </html>
                  """;
    
    1. 重写实现旧版套接字API
      使用更简单、更现代的实现替换java.net.Socket和java.net.ServerSocket API使用的底层实现,易于维护和调试。新的实现很容易适应用户模式线程,也就是光纤。之前的底层实现可以追溯到jdk1.0,实现是遗留java和c代码的混合,维护和调试很痛苦,该实现使用线程堆栈作为I/O缓冲区,这种方式需要多次增加默认线程堆栈大小。
    JDK14
    1. switch优化变为最终版
    2. gc相关
      1)删除CMS
      2)弃用 ParallelScavenge + SerialOld GC 的垃圾回收算法组合
      3)将 zgc 垃圾回收器移植到 macOS 和 windows 平台
    展开全文
  • java历史变迁

    2017-09-15 12:05:11
    一、java变迁历史 版本 描述 1991年1月 Sun公司成立了Green项目小组,专攻智能家电的嵌入式控制系统 1991年2月 放弃C++,开发新语言,命名为“Oak” 1991年6月 JamesGosling开发了Oak的解释器 ...

    一、java变迁历史

    版本 描述
    1991年1月 Sun公司成立了Green项目小组,专攻智能家电的嵌入式控制系统
    1991年2月 放弃C++,开发新语言,命名为“Oak”
    1991年6月 JamesGosling开发了Oak的解释器
    1992年1月 Green完成了Green操作系统、Oak语言、类库等开发
    1992年11月 Green计划转化成“FirstPerson”,一个Sun公司的全资母公司
    1993年2月 获得时代华纳的电视机顶盒交互系统的订单,于是开发的重心从家庭消费电子产品转到了电视盒机顶盒的相关平台上。
    1994年6月 FirstPerson公司倒闭,员工都合并到Sun公司。Liveoak计划启动了,目标是使用Oak语言设计出一个操作系统。
    1994年7月 第一个Java语言的Web浏览器WebRunner(后来改名为HotJava),Oak更名为Java。
    1994年10月 VanHoff编写的Java编译器用于Java语言
    1995年3月 在SunWorld大会,Sun公司正式介绍了Java和HotJava。
    1996年1月 JDK1.0发布
    1997年2月 J2SE1.1发布
    1998年12月 J2SE1.2发布
    1999年6月 发布Java的三个版本:J2SE、J2EE、J2ME
    2000年5月 J2SE1.3发布
    2001年9月 J2EE1.3发布
    2002年2月 J2SE1.4发布
    2004年9月 J2SE1.5发布,将J2SE1.5改名JavaSE5.0
    2005年6月 JavaSE6.0发布,J2EE更名为JavaEE,J2SE更名为JavaSE,J2ME更名为JavaME
    2006年12月 JRE6.0发布
    2006年12月 JavaSE6发布
    2009年12月 JavaEE6发布
    2009年4月 Oracle收购Sun
    2011年7月 JavaSE7发布
    2014年3月 JavaSE8发布

    二、java 1.8十大新特性


    (1)、接口的默认方法

    Java 8允许我们给接口添加一个非抽象的方法实现,只需要使用 default关键字即可,这个特征又叫做扩展方法,示例如下:

    复制代码代码如下:

    interface Formula {
        double calculate(int a);

        default double sqrt(int a) {
            return Math.sqrt(a);
        }
    }


    Formula接口在拥有calculate方法之外同时还定义了sqrt方法,实现了Formula接口的子类只需要实现一个calculate方法,默认方法sqrt将在子类上可以直接使用。
    复制代码代码如下:

    Formula formula = new Formula() {
        @Override
        public double calculate(int a) {
            return sqrt(a * 100);
        }
    };

    formula.calculate(100);     // 100.0
    formula.sqrt(16);           // 4.0


    文中的formula被实现为一个匿名类的实例,该代码非常容易理解,6行代码实现了计算 sqrt(a * 100)。在下一节中,我们将会看到实现单方法接口的更简单的做法。

    译者注: 在Java中只有单继承,如果要让一个类赋予新的特性,通常是使用接口来实现,在C++中支持多继承,允许一个子类同时具有多个父类的接口与功能,在其他语言中,让一个类同时具有其他的可复用代码的方法叫做mixin。新的Java 8 的这个特新在编译器实现的角度上来说更加接近Scala的trait。 在C#中也有名为扩展方法的概念,允许给已存在的类型扩展方法,和Java 8的这个在语义上有差别。

    (2)、Lambda 表达式

    首先看看在老版本的Java中是如何排列字符串的:

    复制代码代码如下:

    List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");

    Collections.sort(names, new Comparator<String>() {
        @Override
        public int compare(String a, String b) {
            return b.compareTo(a);
        }
    });


    只需要给静态方法 Collections.sort 传入一个List对象以及一个比较器来按指定顺序排列。通常做法都是创建一个匿名的比较器对象然后将其传递给sort方法。

    在Java 8 中你就没必要使用这种传统的匿名对象的方式了,Java 8提供了更简洁的语法,lambda表达式:

    复制代码代码如下:

    Collections.sort(names, (String a, String b) -> {
        return b.compareTo(a);
    });

    看到了吧,代码变得更段且更具有可读性,但是实际上还可以写得更短:
    复制代码代码如下:

    Collections.sort(names, (String a, String b) -> b.compareTo(a));

    对于函数体只有一行代码的,你可以去掉大括号{}以及return关键字,但是你还可以写得更短点:
    复制代码代码如下:

    Collections.sort(names, (a, b) -> b.compareTo(a));

    Java编译器可以自动推导出参数类型,所以你可以不用再写一次类型。接下来我们看看lambda表达式还能作出什么更方便的东西来:

    (3)、函数式接口

    Lambda表达式是如何在java的类型系统中表示的呢?每一个lambda表达式都对应一个类型,通常是接口类型。而“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。因为 默认方法 不算抽象方法,所以你也可以给你的函数式接口添加默认方法。

    我们可以将lambda表达式当作任意只包含一个抽象方法的接口类型,确保你的接口一定达到这个要求,你只需要给你的接口添加 @FunctionalInterface 注解,编译器如果发现你标注了这个注解的接口有多于一个抽象方法的时候会报错的。

    示例如下:

    复制代码代码如下:

    @FunctionalInterface
    interface Converter<F, T> {
        T convert(F from);
    }
    Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
    Integer converted = converter.convert("123");
    System.out.println(converted);    // 123

    需要注意如果@FunctionalInterface如果没有指定,上面的代码也是对的。

    译者注 将lambda表达式映射到一个单方法的接口上,这种做法在Java 8之前就有别的语言实现,比如Rhino JavaScript解释器,如果一个函数参数接收一个单方法的接口而你传递的是一个function,Rhino 解释器会自动做一个单接口的实例到function的适配器,典型的应用场景有 org.w3c.dom.events.EventTarget 的addEventListener 第二个参数 EventListener。

    (4)、方法与构造函数引用

    前一节中的代码还可以通过静态方法引用来表示:

    复制代码代码如下:

    Converter<String, Integer> converter = Integer::valueOf;
    Integer converted = converter.convert("123");
    System.out.println(converted);   // 123

    Java 8 允许你使用 :: 关键字来传递方法或者构造函数引用,上面的代码展示了如何引用一个静态方法,我们也可以引用一个对象的方法:
    复制代码代码如下:

     converter = something::startsWith;
    String converted = converter.convert("Java");
    System.out.println(converted);    // "J"

    接下来看看构造函数是如何使用::关键字来引用的,首先我们定义一个包含多个构造函数的简单类:
    复制代码代码如下:

    class Person {
        String firstName;
        String lastName;

        Person() {}

        Person(String firstName, String lastName) {
            this.firstName = firstName;
            this.lastName = lastName;
        }
    }


    接下来我们指定一个用来创建Person对象的对象工厂接口:
    复制代码代码如下:

    interface PersonFactory<P extends Person> {
        P create(String firstName, String lastName);
    }

    这里我们使用构造函数引用来将他们关联起来,而不是实现一个完整的工厂:
    复制代码代码如下:

    PersonFactory<Person> personFactory = Person::new;
    Person person = personFactory.create("Peter", "Parker");

    我们只需要使用 Person::new 来获取Person类构造函数的引用,Java编译器会自动根据PersonFactory.create方法的签名来选择合适的构造函数。

    (5)、Lambda 作用域

    在lambda表达式中访问外层作用域和老版本的匿名对象中的方式很相似。你可以直接访问标记了final的外层局部变量,或者实例的字段以及静态变量。

    (6)、访问局部变量

    我们可以直接在lambda表达式中访问外层的局部变量:

    复制代码代码如下:

    final int num = 1;
    Converter<Integer, String> stringConverter =
            (from) -> String.valueOf(from + num);

    stringConverter.convert(2);     // 3


    但是和匿名对象不同的是,这里的变量num可以不用声明为final,该代码同样正确:
    复制代码代码如下:

    int num = 1;
    Converter<Integer, String> stringConverter =
            (from) -> String.valueOf(from + num);

    stringConverter.convert(2);     // 3


    不过这里的num必须不可被后面的代码修改(即隐性的具有final的语义),例如下面的就无法编译:
    复制代码代码如下:

    int num = 1;
    Converter<Integer, String> stringConverter =
            (from) -> String.valueOf(from + num);
    num = 3;

    在lambda表达式中试图修改num同样是不允许的。

    (7)、访问对象字段与静态变量

    和本地变量不同的是,lambda内部对于实例的字段以及静态变量是即可读又可写。该行为和匿名对象是一致的:

    复制代码代码如下:
    class Lambda4 {
        static int outerStaticNum;
        int outerNum;

        void testScopes() {
            Converter<Integer, String> stringConverter1 = (from) -> {
                outerNum = 23;
                return String.valueOf(from);
            };

            Converter<Integer, String> stringConverter2 = (from) -> {
                outerStaticNum = 72;
                return String.valueOf(from);
            };
        }
    }



    (8)、访问接口的默认方法

    还记得第一节中的formula例子么,接口Formula定义了一个默认方法sqrt可以直接被formula的实例包括匿名对象访问到,但是在lambda表达式中这个是不行的。
    Lambda表达式中是无法访问到默认方法的,以下代码将无法编译:
    复制代码代码如下:

    Formula formula = (a) -> sqrt( a * 100);
    Built-in Functional Interfaces

    JDK 1.8 API包含了很多内建的函数式接口,在老Java中常用到的比如Comparator或者Runnable接口,这些接口都增加了@FunctionalInterface注解以便能用在lambda上。
    Java 8 API同样还提供了很多全新的函数式接口来让工作更加方便,有一些接口是来自Google Guava库里的,即便你对这些很熟悉了,还是有必要看看这些是如何扩展到lambda上使用的。

    Predicate接口

    Predicate 接口只有一个参数,返回boolean类型。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非):

    复制代码代码如下:

    Predicate<String> predicate = (s) -> s.length() > 0;

    predicate.test("foo");              // true
    predicate.negate().test("foo");     // false

    Predicate<Boolean> nonNull = Objects::nonNull;
    Predicate<Boolean> isNull = Objects::isNull;

    Predicate<String> isEmpty = String::isEmpty;
    Predicate<String> isNotEmpty = isEmpty.negate();



    Function 接口

    Function 接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法(compose, andThen):

    复制代码代码如下:

    Function<String, Integer> toInteger = Integer::valueOf;
    Function<String, String> backToString = toInteger.andThen(String::valueOf);

    backToString.apply("123");     // "123"



    Supplier 接口

    Supplier 接口返回一个任意范型的值,和Function接口不同的是该接口没有任何参数
    复制代码代码如下:

    Supplier<Person> personSupplier = Person::new;
    personSupplier.get();   // new Person


    Consumer 接口

    Consumer 接口表示执行在单个参数上的操作。
    复制代码代码如下:

    Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
    greeter.accept(new Person("Luke", "Skywalker"));


    Comparator 接口

    Comparator 是老Java中的经典接口, Java 8在此之上添加了多种默认方法:
    复制代码代码如下:

    Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);

    Person p1 = new Person("John", "Doe");
    Person p2 = new Person("Alice", "Wonderland");

    comparator.compare(p1, p2);             // > 0
    comparator.reversed().compare(p1, p2);  // < 0



    Optional 接口

    Optional 不是函数是接口,这是个用来防止NullPointerException异常的辅助类型,这是下一届中将要用到的重要概念,现在先简单的看看这个接口能干什么:

    Optional 被定义为一个简单的容器,其值可能是null或者不是null。在Java 8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null,而在Java 8中,不推荐你返回null而是返回Optional。

    复制代码代码如下:

    Optional<String> optional = Optional.of("bam");

    optional.isPresent();           // true
    optional.get();                 // "bam"
    optional.orElse("fallback");    // "bam"

    optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b"



    Stream 接口

    java.util.Stream 表示能应用在一组元素上一次执行的操作序列。Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。Stream 的创建需要指定一个数据源,比如 java.util.Collection的子类,List或者Set, Map不支持。Stream的操作可以串行执行或者并行执行。

    首先看看Stream是怎么用,首先创建实例代码的用到的数据List:

    复制代码代码如下:

    List<String> stringCollection = new ArrayList<>();
    stringCollection.add("ddd2");
    stringCollection.add("aaa2");
    stringCollection.add("bbb1");
    stringCollection.add("aaa1");
    stringCollection.add("bbb3");
    stringCollection.add("ccc");
    stringCollection.add("bbb2");
    stringCollection.add("ddd1");

    Java 8扩展了集合类,可以通过 Collection.stream() 或者 Collection.parallelStream() 来创建一个Stream。下面几节将详细解释常用的Stream操作:

    Filter 过滤

    过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作,所以我们可以在过滤后的结果来应用其他Stream操作(比如forEach)。forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作,所以我们不能在forEach之后来执行其他Stream操作。

    复制代码代码如下:

    stringCollection
        .stream()
        .filter((s) -> s.startsWith("a"))
        .forEach(System.out::println);

    // "aaa2", "aaa1"



    Sort 排序

    排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。

    复制代码代码如下:

    stringCollection
        .stream()
        .sorted()
        .filter((s) -> s.startsWith("a"))
        .forEach(System.out::println);

    // "aaa1", "aaa2"


    需要注意的是,排序只创建了一个排列好后的Stream,而不会影响原有的数据源,排序之后原数据stringCollection是不会被修改的:
    复制代码代码如下:

    System.out.println(stringCollection);
    // ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1


    Map 映射

    中间操作map会将元素根据指定的Function接口来依次将元素转成另外的对象,下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。
    复制代码代码如下:

    stringCollection
        .stream()
        .map(String::toUpperCase)
        .sorted((a, b) -> b.compareTo(a))
        .forEach(System.out::println);

    // "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"



    Match 匹配

    Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是最终操作,并返回一个boolean类型的值。

    复制代码代码如下:

    boolean anyStartsWithA = 
        stringCollection
            .stream()
            .anyMatch((s) -> s.startsWith("a"));

    System.out.println(anyStartsWithA);      // true

    boolean allStartsWithA = 
        stringCollection
            .stream()
            .allMatch((s) -> s.startsWith("a"));

    System.out.println(allStartsWithA);      // false

    boolean noneStartsWithZ = 
        stringCollection
            .stream()
            .noneMatch((s) -> s.startsWith("z"));

    System.out.println(noneStartsWithZ);      // true

    Count 计数

    计数是一个最终操作,返回Stream中元素的个数,返回值类型是long。

    复制代码代码如下:

    long startsWithB = 
        stringCollection
            .stream()
            .filter((s) -> s.startsWith("b"))
            .count();

    System.out.println(startsWithB);    // 3



    Reduce 规约

    这是一个最终操作,允许通过指定的函数来讲stream中的多个元素规约为一个元素,规越后的结果是通过Optional接口表示的:

    复制代码代码如下:

    Optional<String> reduced =
        stringCollection
            .stream()
            .sorted()
            .reduce((s1, s2) -> s1 + "#" + s2);

    reduced.ifPresent(System.out::println);
    // "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"



    并行Streams

    前面提到过Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。

    下面的例子展示了是如何通过并行Stream来提升性能:

    首先我们创建一个没有重复元素的大表:

    复制代码代码如下:

    int max = 1000000;
    List<String> values = new ArrayList<>(max);
    for (int i = 0; i < max; i++) {
        UUID uuid = UUID.randomUUID();
        values.add(uuid.toString());
    }

    然后我们计算一下排序这个Stream要耗时多久,
    串行排序:
    复制代码代码如下:

    long t0 = System.nanoTime();

    long count = values.stream().sorted().count();
    System.out.println(count);

    long t1 = System.nanoTime();

    long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
    System.out.println(String.format("sequential sort took: %d ms", millis));

    // 串行耗时: 899 ms
    并行排序:

    复制代码代码如下:

    long t0 = System.nanoTime();

    long count = values.parallelStream().sorted().count();
    System.out.println(count);

    long t1 = System.nanoTime();

    long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
    System.out.println(String.format("parallel sort took: %d ms", millis));

    // 并行排序耗时: 472 ms
    上面两个代码几乎是一样的,但是并行版的快了50%之多,唯一需要做的改动就是将stream()改为parallelStream()。

    Map

    前面提到过,Map类型不支持stream,不过Map提供了一些新的有用的方法来处理一些日常任务。

    复制代码代码如下:

    Map<Integer, String> map = new HashMap<>();

    for (int i = 0; i < 10; i++) {
        map.putIfAbsent(i, "val" + i);
    }

    map.forEach((id, val) -> System.out.println(val));
    以上代码很容易理解, putIfAbsent 不需要我们做额外的存在性检查,而forEach则接收一个Consumer接口来对map里的每一个键值对进行操作。

    下面的例子展示了map上的其他有用的函数:

    复制代码代码如下:

    map.computeIfPresent(3, (num, val) -> val + num);
    map.get(3);             // val33

    map.computeIfPresent(9, (num, val) -> null);
    map.containsKey(9);     // false

    map.computeIfAbsent(23, num -> "val" + num);
    map.containsKey(23);    // true

    map.computeIfAbsent(3, num -> "bam");
    map.get(3);             // val33


    接下来展示如何在Map里删除一个键值全都匹配的项:
    复制代码代码如下:

    map.remove(3, "val3");
    map.get(3);             // val33

    map.remove(3, "val33");
    map.get(3);             // null


    另外一个有用的方法:
    复制代码代码如下:

    map.getOrDefault(42, "not found");  // not found

    对Map的元素做合并也变得很容易了:
    复制代码代码如下:

    map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
    map.get(9);             // val9

    map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
    map.get(9);             // val9concat


    Merge做的事情是如果键名不存在则插入,否则则对原键对应的值做合并操作并重新插入到map中。

    (9)、Date API

    Java 8 在包java.time下包含了一组全新的时间日期API。新的日期API和开源的Joda-Time库差不多,但又不完全一样,下面的例子展示了这组新API里最重要的一些部分:

    Clock 时钟

    Clock类提供了访问当前日期和时间的方法,Clock是时区敏感的,可以用来取代 System.currentTimeMillis() 来获取当前的微秒数。某一个特定的时间点也可以使用Instant类来表示,Instant类也可以用来创建老的java.util.Date对象。

    复制代码代码如下:

    Clock clock = Clock.systemDefaultZone();
    long millis = clock.millis();

    Instant instant = clock.instant();
    Date legacyDate = Date.from(instant);   // legacy java.util.Date



    Timezones 时区

    在新API中时区使用ZoneId来表示。时区可以很方便的使用静态方法of来获取到。 时区定义了到UTS时间的时间差,在Instant时间点对象到本地日期对象之间转换的时候是极其重要的。

    复制代码代码如下:

    System.out.println(ZoneId.getAvailableZoneIds());
    // prints all available timezone ids

    ZoneId zone1 = ZoneId.of("Europe/Berlin");
    ZoneId zone2 = ZoneId.of("Brazil/East");
    System.out.println(zone1.getRules());
    System.out.println(zone2.getRules());

    // ZoneRules[currentStandardOffset=+01:00]
    // ZoneRules[currentStandardOffset=-03:00]



    LocalTime 本地时间

    LocalTime 定义了一个没有时区信息的时间,例如 晚上10点,或者 17:30:15。下面的例子使用前面代码创建的时区创建了两个本地时间。之后比较时间并以小时和分钟为单位计算两个时间的时间差:

    复制代码代码如下:

    LocalTime now1 = LocalTime.now(zone1);
    LocalTime now2 = LocalTime.now(zone2);

    System.out.println(now1.isBefore(now2));  // false

    long hoursBetween = ChronoUnit.HOURS.between(now1, now2);
    long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);

    System.out.println(hoursBetween);       // -3
    System.out.println(minutesBetween);     // -239


    LocalTime 提供了多种工厂方法来简化对象的创建,包括解析时间字符串。
    复制代码代码如下:

    LocalTime late = LocalTime.of(23, 59, 59);
    System.out.println(late);       // 23:59:59

    DateTimeFormatter germanFormatter =
        DateTimeFormatter
            .ofLocalizedTime(FormatStyle.SHORT)
            .withLocale(Locale.GERMAN);

    LocalTime leetTime = LocalTime.parse("13:37", germanFormatter);
    System.out.println(leetTime);   // 13:37


    LocalDate 本地日期

    LocalDate 表示了一个确切的日期,比如 2014-03-11。该对象值是不可变的,用起来和LocalTime基本一致。下面的例子展示了如何给Date对象加减天/月/年。另外要注意的是这些对象是不可变的,操作返回的总是一个新实例。

    复制代码代码如下:

    LocalDate today = LocalDate.now();
    LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);
    LocalDate yesterday = tomorrow.minusDays(2);

    LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);
    DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();


    System.out.println(dayOfWeek);    // FRIDAY
    从字符串解析一个LocalDate类型和解析LocalTime一样简单:
    复制代码代码如下:

    DateTimeFormatter germanFormatter =
        DateTimeFormatter
            .ofLocalizedDate(FormatStyle.MEDIUM)
            .withLocale(Locale.GERMAN);

    LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter);
    System.out.println(xmas);   // 2014-12-24



    LocalDateTime 本地日期时间

    LocalDateTime 同时表示了时间和日期,相当于前两节内容合并到一个对象上了。LocalDateTime和LocalTime还有LocalDate一样,都是不可变的。LocalDateTime提供了一些能访问具体字段的方法。

    复制代码代码如下:

    LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59);

    DayOfWeek dayOfWeek = sylvester.getDayOfWeek();
    System.out.println(dayOfWeek);      // WEDNESDAY

    Month month = sylvester.getMonth();
    System.out.println(month);          // DECEMBER

    long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);
    System.out.println(minuteOfDay);    // 1439


    只要附加上时区信息,就可以将其转换为一个时间点Instant对象,Instant时间点对象可以很容易的转换为老式的java.util.Date。
    复制代码代码如下:

    Instant instant = sylvester
            .atZone(ZoneId.systemDefault())
            .toInstant();

    Date legacyDate = Date.from(instant);
    System.out.println(legacyDate);     // Wed Dec 31 23:59:59 CET 2014


    格式化LocalDateTime和格式化时间和日期一样的,除了使用预定义好的格式外,我们也可以自己定义格式:
    复制代码代码如下:

    DateTimeFormatter formatter =
        DateTimeFormatter
            .ofPattern("MMM dd, yyyy - HH:mm");

    LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter);
    String string = formatter.format(parsed);
    System.out.println(string);     // Nov 03, 2014 - 07:13


    和java.text.NumberFormat不一样的是新版的DateTimeFormatter是不可变的,所以它是线程安全的。
    关于时间日期格式的详细信息:http://download.java.net/jdk8/docs/api/java/time/format/DateTimeFormatter.html

    (10)、Annotation 注解

    在Java 8中支持多重注解了,先看个例子来理解一下是什么意思。
    首先定义一个包装类Hints注解用来放置一组具体的Hint注解:

    复制代码代码如下:

    @interface Hints {
        Hint[] value();
    }

    @Repeatable(Hints.class)
    @interface Hint {
        String value();
    }


    Java 8允许我们把同一个类型的注解使用多次,只需要给该注解标注一下@Repeatable即可。

    例 1: 使用包装类当容器来存多个注解(老方法)

    复制代码代码如下:

    @Hints({@Hint("hint1"), @Hint("hint2")})
    class Person {}

    例 2:使用多重注解(新方法)
    复制代码代码如下:

    @Hint("hint1")
    @Hint("hint2")
    class Person {}

    第二个例子里java编译器会隐性的帮你定义好@Hints注解,了解这一点有助于你用反射来获取这些信息:
    复制代码代码如下:

    Hint hint = Person.class.getAnnotation(Hint.class);
    System.out.println(hint);                   // null

    Hints hints1 = Person.class.getAnnotation(Hints.class);
    System.out.println(hints1.value().length);  // 2

    Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);
    System.out.println(hints2.length);          // 2


    即便我们没有在Person类上定义@Hints注解,我们还是可以通过 getAnnotation(Hints.class) 来获取 @Hints注解,更加方便的方法是使用 getAnnotationsByType 可以直接获取到所有的@Hint注解。
    另外Java 8的注解还增加到两种新的target上了:
    复制代码代码如下:

    @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
    @interface MyAnnotation {}


    展开全文
  • 1. 版本变迁: 1996年:JDK1.0 2005年:JDK5.0 2014年:JDK8.0 2018年:JDK11.0 2.环境搭建: 开发环境: 初期:记事本:editplus、notepad++、vsocde、sublime 后期:IDE(集成开发环境): eclipse、idea 运行环境: JDK ...

    1. 版本变迁:

    1996年:JDK1.0
    2005年:JDK5.0
    2014年:JDK8.0 
    2018年:JDK11.0
    

    2.环境搭建:

    开发环境:

    初期:记事本:editplus、notepad++、vsocde、sublime
    后期:IDE(集成开发环境): eclipse、idea
    

    运行环境:

    JDK
    JDK > JRE > JVM
    JDK(java development kit):java开发工具包
    JRE(java runtime environment):java运行时环境
    JVM(java virtual machime):java虚拟机
    
    展开全文
  • 一篇关于java新旧内存模型的文章---文章摘自互联网,感兴趣的可以读一读。
  • JDK版本变迁

    2020-12-18 13:22:27
    作为一名Java程序员,需要对JDK的版本变迁有一定的认识,这有助于在开发项目达到事半功倍的效果。
  • Java版本版本变迁

    千次阅读 2011-02-16 00:20:00
    1995年5月23日,Java语言诞生  1996年1月,第一个JDK-JDK1.0诞生  1996年4月,10个最主要的操作系统供应商申明将在其产品中嵌入JAVA技术  1996年9月,约8.3万个网页应用了JAVA技术来制作  1997年2月18日,...
  • 条评论| 作者程晓明 特别策划 Java Java20周年 程序员电子刊 摘要本文通过介绍Java的新/旧内存模型来展示Java技术的历史变迁 本文通过介绍Java的新/旧内存模型来展示Java技术的历史变迁 旧的Java内存模型 Java使用的...
  • java 面试题 总结

    2009-09-16 08:45:34
    JAVA相关基础知识 1、面向对象的特征有哪些方面 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用...
  • Java内存模型的历史 旧的Java内存模型 Java使用的是共享内存的并发模型,在线程之间共享变量。Java语言定义了线程模型规范,通过内存模型控制线程与变量的交互,从而实现Java线程之间的通信。在JDK5之前,Java一直...
  • Java发展历程

    万次阅读 多人点赞 2019-06-30 19:35:22
    1991年4月,Sun公司成立了Green项目小组,专攻智能家电的嵌入式控制系统。...这个计划的产品就是Java语言的前身:Oak(橡树)。Oak当时在消费品市场上并不算成功,但随着1995年互联网潮流的兴起,Oak迅速找到了...
  • Java版本演进到Java 2,根据不同层面的应用进行了细化,Java 2平台被分为三种版本版本及其说明见 版本 描述 其它 J2EE —企业版,适用于服务器,目前已成为企业运算、电子商务等领域的热门技术 ...
  • JDK Alpha and Beta1995年发布第一个测试版本JDK 1.01996年1月23日,第一个版本发布。随后不久,第一个稳定版本JDK 1.0.2发布JDK1.11997年2月19日,JDK1.1发布,主要特性如下: AWT 内部类 JDBC RMI 反射 JIT ...
  • 而在进一步学习Spring的核心原理之前,有必要和大家一起梳理一下Spring历史版本变迁,知晓一下每一个版本新增了哪些东西,解决了哪些我们开发中的问题,以便我们更清楚的理解这个生态帝国是如何一步一发展壮大的!...
  • 而在进一步学习Spring的核心原理之前,有必要和大家一起梳理一下Spring历史版本变迁,知晓一下每一个版本新增了哪些东西,解决了哪些我们开发中的问题,以便我们更清楚的理解这个生态帝国是如何一步一发展壮大的!...
  • 比如对于“JVM内存模型”和“Java内存模型(JMM)”没有区分,实际上,Java内存模型(JMM)是一种规范,和具体的Java虚拟机的内存结构不是一个概念,不应该把诸如“年轻代“、”老年代”这类关于虚拟机具体实现的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 11,061
精华内容 4,424
关键字:

java版本变迁

java 订阅