这篇文章将为大家详细讲解有关Java5、Java6、Java7、Java8的新特性是什么,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。
引用泛型之后,允许制定集合里元素的类型,免去了强制类型转换,并且能在编译时刻进行类型检查的好处。
Parameterized Type 作为参数和返回值, Generic是vararg、annotation、enumeration、collection的基石。
抛弃List、Map,使用List<T>、Map<K,V>,给它们添加元素或者使用Iterator<T>遍历时,编译期就可以给你检查出类型错误
抛弃List、Map,使用List<T>、Map<K,V>
List<String> list = new ArrayList<String>(); String str = list.get(i);
?
假设一个打印List<T>
中元素的方法printList
,我们希望任何类型T的List<T>都可以被打印, 代码:
public void printList(List<?> list,PrintStream out)throws IOException{ for(Iterator<?> i=list.iterator();i.hasNext();){ System.out.println(i.next.toString()); } }
如果通配符?让我们的参数类型过于广泛,我们可以把List<?>、Iterator<?> 修改为List<? Extends Number>、Iterator<? Extends Number>限制一下它。
简单的说是类型自动转换。
自动装包:基本类型自动转为包装类(int ——Integer)
自动拆包:包装类自动转为基本类型(Integer——int)
参数类型相同时,把重载函数合并到一起了。
如:
public void test(object... objs){ for(Object obj:objs){ System.out.println(obj); } }
指出某个method覆盖了superclass 的method当你要覆盖的方法名拼写错时编译不通过
指出某个method或element类型的使用是被阻止的,子类将不能覆盖该方法
关闭class、method、field、variable 初始化的编译期警告,比如:List没有使用 Generic,则@SuppressWarnings("unchecked")去掉编译期警告。
public @interface Marked{}
或者说annotation的annotation
四种标准的meta-annotation全部定义在java.lang.annotaion包中:
指定所定义的annotation可以用在哪些程序单元上,如果Target没有指定,则表示该annotation可以使用在任意程序单元上
@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD, ElementType.METHOD }) public @interface RejectEmpty { String value() default ""; } @Retention(RetentionPolicy.RUNTIME) @Target( { ElementType.FIELD, ElementType.METHOD }) public @interface AcceptInt { int min() default Integer.MIN_VALUE; int max() default Integer.MAX_VALUE; String hint() default ""; }
指出Java编译期如何对待annotation,annotation可以被编译期丢掉,或者保留在编译过的class文件中,在annotation被保留时,它也指定是否会在JVM加载class时读取该annotation
代码
@Retention(RetentionPolicy.SOURCE) // Annotation会被编译期丢弃 public @interface TODO1 {} @Retention(RetentionPolicy.CLASS) // Annotation保留在class文件中,但会被JVM忽略 public @interface TODO2 {} @Retention(RetentionPolicy.RUNTIME) // Annotation保留在class文件中且会被JVM读取 public @interface TODO3 {}
指出被定义的annotation被视为所熟悉的程序单元的公开API之一。被@Documented标注的annotation会在javadoc中显示,这在annotation对它标注的元素被客户端使用时有影响时起作用
该meta-annotation应用于目标为class的annotation类型上,被此annotattion标注的class会自动继承父类的annotation
我们发现java.lang.Class有许多与Annotation的反射相关的方法,如getAnnotations、isAnnotationpresent。我们可以利用Annotation反射来做许多事情,比如自定义Annotation来做Model对象验证
代码
@Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.FIELD, ElementType.METHOD }) public @interface RejectEmpty { String value() default ""; } @Retention(RetentionPolicy.RUNTIME) @Target( { ElementType.FIELD, ElementType.METHOD }) public @interface AcceptInt { int min() default Integer.MIN_VALUE; int max() default Integer.MAX_VALUE; String hint() default ""; }
使用@RejectEmpty和@AcceptInt标注我们的Model的field,然后利用反射来做Model验证
formatter.format("Remaining account balance: $%.2f", balance);
HashMap的替代者ConcurrentHashMap和ArrayList的替代者CopyOnWriteArrayList
在大并发量读取时采用java.util.concurrent包里的一些类会让大家满意BlockingQueue、Callable、Executor、Semaphore...
String s = "test"; switch (s) { case "test" : System.out.println("test"); case "test1" : System.out.println("test1"); break ; default : System.out.println("break"); break ; }
final List<Integer> piDigits = [ 1,2,3,4,5,8 ];
File System.getJavaIoTempDir() // IO临时文件夹 File System.getJavaHomeDir() // JRE的安装目录 File System.getUserHomeDir() // 当前用户目录 File System.getUserDir() // 启动java进程时所在的目录5
Boolean Booleans.negate(Boolean booleanObj) True => False , False => True, Null => Null boolean Booleans.and(boolean[] array) boolean Booleans.or(boolean[] array) boolean Booleans.xor(boolean[] array) boolean Booleans.and(Boolean[] array) boolean Booleans.or(Boolean[] array) boolean Booleans.xor(Boolean[] array)
boolean Character.equalsIgnoreCase(char ch2, char ch3)
int Math.safeToInt(long value) int Math.safeNegate(int value) long Math.safeSubtract(long value1, int value2) long Math.safeSubtract(long value1, long value2) int Math.safeMultiply(int value1, int value2) long Math.safeMultiply(long value1, int value2) long Math.safeMultiply(long value1, long value2) long Math.safeNegate(long value) int Math.safeAdd(int value1, int value2) long Math.safeAdd(long value1, int value2) long Math.safeAdd(long value1, long value2) int Math.safeSubtract(int value1, int value2)
java8的新特新逐一列出,并将使用简单的代码示例来指导你如何使用默认接口方法,lambda表达式,方法引用以及多重Annotation,之后你将会学到最新的API上的改进,比如流,函数式接口,Map以及全新的日期API
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。新的Java8的这个特新在编译器实现的角度上来说更加接近Scala的trait。在C#中也有名为扩展方法的概念,允许给已存在的类型扩展方法,和Java 8的这个在语义上有差别。
首先看看在老版本的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));
看到了吧,代码变得更段且更具有可读性,但是实际上还可以写得更短:
Collections.sort(names, (String a, String b) -> b.compareTo(a));
对于函数体只有一行代码的,你可以去掉大括号{}以及return关键字,但是你还可以写得更短点:
Collections.sort(names, (a, b) -> b.compareTo(a));
Java编译器可以自动推导出参数类型,所以你可以不用再写一次类型。
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表达式中访问外层作用域和老版本的匿名对象中的方式很相似。你可以直接访问标记了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 接口只有一个参数,返回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 接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法(compose, andThen):
Function<String, Integer> toInteger = Integer::valueOf; Function<String, String> backToString = toInteger.andThen(String::valueOf); backToString.apply("123"); // "123"
Supplier 接口返回一个任意范型的值,和Function接口不同的是该接口没有任何参数
Supplier<Person> personSupplier = Person::new; personSupplier.get(); // new Person
Consumer 接口表示执行在单个参数上的操作。
Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName); greeter.accept(new Person("Luke", "Skywalker"));
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 不是函数是接口,这是个用来防止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"
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操作:
过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于中间操作,所以我们可以在过滤后的结果来应用其他Stream操作(比如forEach)。forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作,所以我们不能在forEach之后来执行其他Stream操作。
stringCollection .stream() .filter((s) -> s.startsWith("a")) .forEach(System.out::println); // "aaa2", "aaa1"
排序是一个中间操作,返回的是排序好后的Stream。如果你不指定一个自定义的Comparator则会使用默认排序。
stringCollection .stream() .sorted() .filter((s) -> s.startsWith("a")) .forEach(System.out::println); // "aaa1", "aaa2"
关于Java5、Java6、Java7、Java8的新特性是什么就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。