Java 1.8/8 New Features

Content:

  1. Version numbers
  2. Release
  3. General
  4. Date and time
  5. Parallel array sort
  6. Base64 encoding
  7. Functional interfaces and lambda
  8. Interface default methods and static methods
  9. Bulk data operations (streams)
  10. No perm generation
  11. Nashorn
  12. JavaFX

Version numbers:

This version is officially known as Java SE 8, but often called Java 1.8. Unlike previous versions it does not have code name.

Java version Official name Code name Release date
1.0 JDK 1.0 January 23rd 1996
1.1 JDK 1.1 February 19th 1997
1.2 J2SE 1.2 Playground December 8th 1998
1.3 J2SE 1.3 Kestrel May 8th 2000
1.4 J2SE 1.4 Merlin February 6th 2002
1.5/5 Java SE 5 Tiger September 30th 2004
1.6/6 Java SE 6 Mustang December 11th 2006
1.7/7 Java SE 7 Dolphin July 7th 2011
1.8/8 Java SE 8 March 18th 2014

Release:

Java 1.8 was released March 18th 2014.

It was originally scheduled for release in September 2013, but Oracle had to use many resources around that time to close security vulnerabbilities in Java applets and that delayed release.

General:

Version 1.8 has several major changes - significant more changes than in 1.6 and 1.7.

Many of the changes are obviously inspired by C# and .NET.

Date and time:

java.util.Date and java.util.Calendar has been criticized for many years and Java 1.8 has a replacement for those via a bunch of new classes in java.util.time package.

Some of the most relevant classes are:

LocalDateTime
Time without time zone
ZonedDateTime
Time with time zone
Instant
Wrapper for integer representation of time
DateTimeFormatter
Formating and parsing of time

Java 7 code:

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.TimeZone;

public class DT7 {
    public static void main(String[] args) {
        DateFormat df = new SimpleDateFormat("HH:mm");
        Date now = new Date();
        Calendar nowplus2 = new GregorianCalendar();
        nowplus2.setTime(now);
        nowplus2.add(Calendar.HOUR, 2);
        System.out.println(df.format(now));
        System.out.println(df.format(nowplus2.getTime()));
        df.setTimeZone(TimeZone.getTimeZone("GMT"));
        System.out.println(df.format(now));
        System.out.println(df.format(nowplus2.getTime()));
    }
}

Java 8 code:

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;

public class DT8 {
    public static void main(String[] args) {
        DateTimeFormatter dtf = DateTimeFormatter.ofPattern("HH:mm");
        LocalDateTime now = LocalDateTime.now();
        LocalDateTime nowplus2 = now.plusHours(2)  ;
        System.out.println(dtf.format(now));
        System.out.println(dtf.format(nowplus2));
        ZonedDateTime nowgmt = now.atZone(ZoneId.of("GMT"));
        ZonedDateTime nowplus2gmt = nowplus2.atZone(ZoneId.of("GMT"));
        System.out.println(dtf.format(nowgmt));
        System.out.println(dtf.format(nowplus2gmt));
    }
}

It is obviously good that Oracle has listened to users and done something about a (perceived) problem. But personally I did not have a problem with the old classes.

I expect many to use the new classes. They should after having complained about the old classes for years.

Parallel array sort:

java.util.Arrays now besides the sort methods also have have the parallelSort methods that performs sort via multiple threads.

And it works. On a 4 core with hyperthreading parallelSort is 5 times faster than sort.

Java 7 code:

import java.util.Arrays;
import java.util.Random;

public class PS7 {
    public static void main(String[] args) {
        Random rng = new Random()   ;
        int[] a = new int[100000000];
        for(int i = 0; i < a.length; i++) a[i] = rng.nextInt();
        long t1 = System.currentTimeMillis();
        Arrays.sort(a);
        long t2 = System.currentTimeMillis();
        System.out.println(t2 - t1);
    }
}
import java.util.Arrays;
import java.util.Random;

public class PS8 {
    public static void main(String[] args) {
        Random rng = new Random()   ;
        int[] a = new int[100000000];
        for(int i = 0; i < a.length; i++) a[i] = rng.nextInt();
        long t1 = System.currentTimeMillis();
        Arrays.parallelSort(a);
        long t2 = System.currentTimeMillis();
        System.out.println(t2 - t1);
    }
}

I don't see this as an important change.

And I do not expect much use - it is simply too rare to have to sort very large arrays.

Java 8 code:

Base64 encoding:

Java EE has had it forever via Java Mail, but sometimes one need base64 support in Java SE and including Java Mail just for that is not a nice solution.

There is an even less nice solution of using an internal undocumented SUN class for it - and some has been using that.

In Java 6 came documented methods for it, but they were part of JAXB (Java XML Binding) and far from a natural place to look for the functionality.

But Java 8 now got a solution in java.util for it.

Java pre-6 via Java Mail code:

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import javax.mail.MessagingException;
import javax.mail.internet.MimeUtility;

public class B64old {
    public static String b64encode(byte[] b) throws MessagingException, IOException  {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        OutputStream b64os = MimeUtility.encode(baos, "base64");
        b64os.write(b);
        b64os.close();
        return new String(baos.toByteArray(), 0, baos.toByteArray().length - 1);
    }
    public static byte[] b64decode(String s) throws MessagingException, IOException  {
        ByteArrayInputStream bais = new ByteArrayInputStream(s.getBytes());
        InputStream b64is = MimeUtility.decode(bais, "Base64");
        byte[] tmp = new byte[s.length()];
        int n = b64is.read(tmp);
        byte[] res = new byte[n];
        System.arraycopy(tmp, 0, res, 0, n);
        return res;
    }
    public static void main(String[] args) throws Exception {
        byte[] b = { 1, 2, 3, 4 };
        String s = b64encode(b);
        System.out.println(s);
        byte[] b2 = b64decode(s);
        System.out.println(b[0] + " " + b[3]);
    }
}

Java 6 and 7 via JAXB code:

import javax.xml.bind.DatatypeConverter;

public class B647 {
    public static void main(String[] args) throws Exception {
        byte[] b = { 1, 2, 3, 4 };
        String s = DatatypeConverter.printBase64Binary(b);
        System.out.println(s);
        byte[] b2 = DatatypeConverter.parseBase64Binary(s);
        System.out.println(b[0] + " " + b[3]);
    }
}

Java 8 code:

import java.util.Base64;

public class B648 {
    public static void main(String[] args) throws Exception {
        byte[] b = { 1, 2, 3, 4 };
        String s = Base64.getEncoder().encodeToString(b);
        System.out.println(s);
        byte[] b2 = Base64.getDecoder().decode(s);
        System.out.println(b[0] + " " + b[3]);
    }
}

I don't see this as an important change.

And I do not expect much use - it is simply too rare to have to work with base64.

Functional interfaces and lambda:

Yes - after more than 5 years of discussions Java finally got lambda.

The implementation og lambda in Java 8 is very similar with implementation in C#. But the syntax in Java is less elegant.

Examples of lambda expressions:

One reference to non-anonymous methods with:

One declare a type by creating an interface with a single method and an annotation: FunctionalInterface.

For execution the method of the interface must be called explicit.

Example:

import java.util.function.Consumer;
import java.util.function.Function;

public class Lambda {
    public static void test1(Function<Integer, Integer> f, int x) {
        System.out.println(f.apply(x));
    }
    public static void test2(FuncSig f, int a, int b) {
        System.out.println(f.eval(a, b));
    }
    public static void test3(Consumer<String> f, String s) {
        f.accept(s);
    }
    public static void main(String[] args) {
        test1(x -> x+x, 5);
        test1(x -> x*x, 5);
        test2((a,b) -> a+b, 123, 456);
        FuncSig fs = (a,b) -> a+b;
        test2(fs, 123, 456);
        test3(System.out::println, "ABC");
    }
}

@FunctionalInterface
interface FuncSig {
    public int eval(int a, int b);
}

This is a very important change. Functional programming is hot. And now Java get (a little bit) on the train.

I am sure this will be used a lot. Lambda was a huge success in C# and it will be a huge success in Java as well.

Interface default methods and static methods:

In Java 8 interfaces can have default methods, that classes not implementing the method inherit.

Java 8 defaults are added for the same raeson that C# got extension methods. It makes it easier to add methods to an interface, because existing classes implementing the interface will continue to compile and work.

But the way it works in Java and C# is very different and has very different characteristica:

C# extension method Java 8 interface default method
both interfaces and classes only interfaces
separate class so everybody can add interface itself so only interface owner can add
separate class so one need to search for it interface itself so traceable via inheritance tree
not polymorph polymorph
no reflection support reflection support

Syntax wise one just add a normal method with added default keyword to the interface.

Simple example:

public class Default {
    public static void main(String[] args) {
        Test o = new TestImpl();
        o.m1();
        o.m2();
    }
}

interface Test {
    public void m1();
    public default void m2() {
        System.out.println("m2");
    }
}

class TestImpl implements Test {
    public void m1() {
        System.out.println("m1");
    }
}

Example that shows the polymorph characteristica:

public class DefaultPolymorph {
    public static void main(String[] args) {
        I1 o1 = new C();
        o1.m();
        I2 o2 = new C();
        o2.m();
        C o3 = new C();
        o3.m();
    }
}

interface I1 {
    public default void m() {
        System.out.println("I1.m");
    }
}

interface I2 extends I1 {
    public default void m() {
        System.out.println("I2.m");
    }
}

class C implements I2 {
    public void m() {
        System.out.println("C.m");
    }
}

The last example should be compared to the C# code:

using System;

namespace E
{
    public interface I1
    {
    }
    public interface I2 : I1
    {
    }
    public class C : I2
    {
    }
    public static class MyExtensions 
    {
        public static void M(this I1 o)
        {
            Console.WriteLine("I1.M");
        }
        public static void M(this I2 o)
        {
            Console.WriteLine("I2.M");
        }
        public static void M(this C o)
        {
            Console.WriteLine("C.M");
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            I1 o1 = new C();
            o1.M();
            I2 o2 = new C();
            o2.M();
            C o3 = new C();
            o3.M();
        }
    }
}

That gives a completely different result.

Interfaces can also implement static methods.

Note: interface default methods is not support for multiple implementation inheritance - there are strict restrictions on what can be done.

Note: interface default methods does not suffer from diamond of death problem due to the same restrictions.

This is a very important change. It is necessary to support streams without breaking existing code. But it is also a huge benefit for those working with API's defined via interfaces.

It will certainly be used a lot implicit via standard Java library. But as people start to realize its potential, then I believe that it will also be used outside Java library..

Bulk data operations (streams):

Java 8 bulk data operations aka Java 8 streams is Java's version of .NET LINQ.

The new interface java.util.stream.Stream<T> natches .NET System.Collections.Generic.IEnumerabble<T>.

And just like lambda and extension methods was required for LINQ in .NET then lambda and interface default methods are required for streams in Java.

Java does not have a SQL like DSL like .NET though.

The above could give the impression that if one is good to C# LINQ, then streams in Java would be easy. But I don't think that is the case. Everything is sligtly different. And a lot of the Java stuff is not very intuitive.

Simple examples:

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class S8basic {
    public static void main(String[] args) {
        List<Data> lst = new ArrayList<>();
        lst.add(new Data(2, "BB"));
        lst.add(new Data(1, "A"));
        lst.add(new Data(4, "DDDD"));
        lst.add(new Data(3, "CCC"));
        for(Data d : lst) {
            System.out.println(d);
        }
        lst.stream().forEach(d -> System.out.println(d));
        lst.stream().sorted().forEach(d -> System.out.println(d));
        System.out.println(lst.stream().count());
        System.out.println(lst.stream().mapToInt(d -> d.getIv()).sum());
        lst.stream().filter(d -> d.getIv() > 2).forEach(d -> System.out.println(d));
        Object[] da = lst.stream().filter(d -> d.getIv() > 2).toArray();
        System.out.println(da.length);
        List<Data> dlst = lst.stream().filter(d -> d.getIv() > 2).collect(Collectors.toList());
        System.out.println(dlst.size());
        Map<Integer, String> dmp = lst.stream().collect(Collectors.toMap(Data::getIv, Data::getSv));
        System.out.println(dmp.size());
        System.out.println(lst.stream().map(d -> d.getIv()).reduce(0, (a, b) -> a + b));
        System.out.println(lst.stream().map(d -> d.getIv()).reduce(1, (a, b) -> a * b));
    }
}

class Data implements Comparable<Data> {
    private int iv;
    private String sv;
    public Data(int iv, String sv) {
        this.iv = iv;
        this.sv = sv;
    }
    public int getIv() {
        return iv;
    }
    public void setIv(int iv) {
        this.iv = iv;
    }
    public String getSv() {
        return sv;
    }
    public void setSv(String sv) {
        this.sv = sv;
    }
    @Override
    public String toString() {
        return "{" + iv + "," + sv + "}";
    }
    @Override
    public int compareTo(Data o) {
        return iv - o.iv;
    }
}

But one thing is made very easy - switch between sequentia and parallel execution is completely transparent:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class S8parallel {
    private static final int N = 10000000;
    public static void main(String[] args) {
        Random rng = new Random();
        List<Double> lst = new ArrayList<Double>();
        for(int i = 0; i < N; i++) lst.add(rng.nextDouble());
        double sum;
        long t1, t2;
        for(int i = 0; i < 3; i++) {
            t1 = System.currentTimeMillis();
            sum = lst.stream().mapToDouble(x -> Math.sqrt(x)).sum();
            t2 = System.currentTimeMillis();
            System.out.printf("%10.1f : %d\n", sum, t2-t1);
            t1 = System.currentTimeMillis();
            sum = lst.parallelStream().mapToDouble(x -> Math.sqrt(x)).sum();
            t2 = System.currentTimeMillis();
            System.out.printf("%10.1f : %d\n", sum, t2-t1);
            t1 = System.currentTimeMillis();
            sum = lst.stream().parallel().mapToDouble(x -> Math.sqrt(x)).sum();
            t2 = System.currentTimeMillis();
            System.out.printf("%10.1f : %d\n", sum, t2-t1);
            t1 = System.currentTimeMillis();
            sum = lst.parallelStream().sequential().mapToDouble(x -> Math.sqrt(x)).sum();
            t2 = System.currentTimeMillis();
            System.out.printf("%10.1f : %d\n", sum, t2-t1);
        }
    }
}

Stream support has also been added to NIO:

No perm generation:

Perm gen is removed in Oracle Java 8.

Perm gen was an area in Java heap used for class definitions and a few other things.

So this is no longer necesarry for applications loading a lot of classes: -XX:MaxPermSize=256m

Class definitions are now in metaspace. Metaspace is not in Java heap but in native memory. And there is no limit on it by default.

If one want to limit it then the option is: -XX:MaxMetaspaceSize=256m

This seems like a very practical change.

Nashorn:

The built in JavaScript engine Rhino has been replaced by a new engine Nashorn.

I don't see this as an important change as it does not provide new functionality but only a new implementation.

JavaFX:

JavaFX is part of Java 8. It used to be a separate downloadable library, but now it is included with Java.

Note: JavaFX is part of Oracle Java SE 8 implementation, but it is not part of Java SE 8 standard. So other Java implementations does not have it (including IBM Java). It may become part of Java SE 9 standard.

JavaFX is a new GUI framework that replaces Swing.

I see this as a very important change. Swing is getting old and JavaFX is a new GUI framework similar to Adobe Flex and Microsoft WPF.

I do not expect to see much use - very few fat client GUI's are done in Java and many will just stick to Swing.

UPDATE: I am actually seeing some interest for JavaFX in the market. Job ads are listing JavaFX as a requirement, so somebody are using it.

For detailed information about JavaFX see this article.

Article history:

Version Date Description
1.0 July 27th 2013 Initial version (in Danish) published on Eksperten.dk
2.0 August 3rd 2016 Translation to English and complete reformatting and publishing here
2.1 October 8th 2016 Add content overview
2.2 March 23rd 2017 Add release dates

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj