jdk5ITeye - 乐橙lc8

jdk5ITeye

2019-01-11 22:11:24 | 作者: 海白 | 标签: 代码,一个,如下 | 浏览: 436

投稿:junjie 字体:[增加 减小] 类型:转载 本教程将Java8的新特新逐个列出,并将运用简略的代码示例来辅导你怎样运用默许接口办法,lambda表达式,办法引证以及多重Annotation,之后你将会学到最新的API上的改善,比方流,函数式接口,Map以及全新的日期API

“Java is still not dead—and people are starting to figure that out.”

本教程将用带注释的简略代码来描绘新特性,你将看不到大片吓人的文字。

一、接口的默许办法
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的这个在语义上有不同。
二、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表达式还能作出什么更便利的东西来:
三、函数式接口
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。

四、办法与结构函数引证
前一节中的代码还能够经过静态办法引证来表明:

仿制代码 代码如下:
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办法的签名来挑选适宜的结构函数。

五、Lambda 效果域
在lambda表达式中拜访外层效果域和老版其他匿名目标中的办法很类似。你能够直接拜访标记了final的外层局部变量,或许实例的字段以及静态变量。

六、拜访局部变量

咱们能够直接在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相同是不答应的。
七、拜访目标字段与静态变量

和本地变量不同的是,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);
        };
    }
}


八、拜访接口的默许办法
还记得第一节中的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()

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 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中。

九、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

十、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 {}
关于Java 8的新特性就写到这了,必定还有更多的特性等候开掘。JDK 1.8里还有许多很有用的东西,比方Arrays.parallelSort, StampedLock和CompletableFuture等等。

 

 

 

 

 

版权声明
本文来源于网络,版权归原作者所有,其内容与观点不代表乐橙lc8立场。转载文章仅为传播更有价值的信息,如采编人员采编有误或者版权原因,请与我们联系,我们核实后立即修改或删除。

猜您喜欢的文章