写点什么

java 之新特性

作者:andy
  • 2022-10-28
    北京
  • 本文字数:3837 字

    阅读完需:约 13 分钟

Java5 新特性


一、可变参数

为了解决传递参数多个的问题,最初的设计方式便是参数为数组,然后进行各个数据处理。但是,使用数组并不标准,不能每次都需要产生一个数组对象,赋值进数组,再进行方法参数传递。Java1.5 之后提供了可变参数的方式。

[ public | protected | private ] 数据类型 方法名 (数据类型 ... 变量)

在方法体中,仍然把可变参数视为数组,然后对数组遍历,进行数据处理。同时,可变参数仍然支持数组作为参数进行传递。对于不传递任务参数,则方法体会自动将该数组定义为非 null 但数组个数为 0 的数组。

实际开发中,一般参数个数是固定的,因此不会使用到可变参数。但是如果编写诸如框架之类通用性的方法时,则一定用到。

二、foreach

对于类集,也包含数组,进行遍历时,希望能够直接进行内容遍历,不需要通过索引取得。为了实现这一目的,jdk1.5 提供了 foreach 方法实现。

三、泛型

Object 类是所有类的父类,可以使用 Object 类进行数据的统一,避免同一功能因数据类型的不同而编写不同类型的代码,造成冗余代码过多。故采用 Object 类方便设计开发。

虽然实现了数据的统一接收,但是在对象向下转型时,需要进行强制转型,而此时,可能会出现非常多的数据类型,而我们需要一一判断。有可能超出了我们所知的数据类型,那这个时候更加难以判断了。同时,可能会出现数据集合中不同类型的数据,这样进行转型一定出错。这也是为什么少用向下转型的原因。

由上,jdk1.5 之后提供了泛型来解决问题。泛型就是一种表示数据类型的标记,数据类型由用户实际使用时定义,这一标记由任意的大写字母组成。同时,这样也可以在具体调用时固定了代码的数据类型,防止类似 ClassCastException 异常产生。

同时,泛型只能使用引用数据类型,不能使用基本数据类型。

当方法的参数类型以泛型修饰的类为主时,为了避免不确定的数据类型造成的赋值错误和向下转型错误问题,可以使用通配符进行方法的参数类型设置,通配符如下:

?:表示不确定数据类型,不能进行数据修改,只能进行数据取出

? extends 类:确定数据类型的上限,即数据类型不能超过父类

? super 类:确定数据类型的下限,即数据类型不能低于子类。该方式在 java.util.Arrays 类中的 public static void sort​(T[] a,Comparator c)方法应用到了。

注意:对于类定义使用了泛型,是对象实例化时,可以不用设置相应的泛型类型,系统默认是 Object 类

四、枚举

枚举是为了方便开发,同样能够实现多例设计模式而引入 Java 的,使用关键字 enum 定义枚举。严格来说,使用 enum 定义的枚举相当于一个继承了 Enum 这个抽象类。

常用方法:

取得所有枚举:values()

这里需要提醒的是,实际定义枚举,语法要求只能使用 enum,而不能补充使用 extends Enum 定义枚举。

五、Annotation

程序开发在进行配置信息设置时经历了三个阶段:

第一阶段:配置信息直接写在程序之中,不方便修改维护

第二极端:配置信息与程序分离,造成配置过多,不利于查错修改

第三阶段:部分的配置信息对开发者而言是无用的,修改后还会容易出错,因此,将部分配置信息写回到代码中,利用一些标记将配置信息与程序区分开。

故 Java 提供了注解的方式解决这一问题。Java 中提供了三个最为常用的注解基础:

@Override:准确的覆写;

@Deprecated:声明过期操作,@Deprecated 是为了兼顾之前的代码功能,而进行保留的说明,但是之后的代码会建议,使用新的方法,故使用此注解声明过期操作;

@SuppressWarnings:压制警告,@SuppressWarnings,对于程序而言,会出现开发明确知道的警告信息,但是开发者又不希望一直显示警告信息,故使用压制警告注解。


Java8 新特性


一、接口定义增强


对于原有的接口设计,只能定义常量和抽象方法。但是,随着接口的子类不断增多,出现了这样一个问题,接口中需要新定义一个方法,这个方法对于各个子类的功能都相同。那么,从原有对于接口的理解,则应该各个子类都得覆写该方法。实际上,这是不现实的。

因此,JDK1.8 推出了新特性,在接口中可以定义普通方法。但是为了和之前 Java 规则有所区别,在接口中定义普通方法使用 default,定义静态方法使用 static。


代码示例:


package org.fuys.ownutil.instance;interface Thing {	public void perform();	default void print(){		System.out.println("JDK1.8 New Character By using dafault");	}	static void println(){		System.out.println("JDK1.8 New Character By using static");	}}class Cup implements Thing {	public static void drink(){		System.out.println("drink water");	}		@Override	public void perform() {		System.out.println("Interface Define");	}}public class NewInterfaceInstance {	public static void main(String[] args) {		Thing thing = new Cup();		thing.perform();		thing.print();		Thing.println();		Cup cup = (Cup)thing;		cup.perform();		Cup.drink();	}}
复制代码


二、Lamda 表达式


对于接口定义,子类需要覆写所有的抽象方法。那么,对于匿名对象而言,就需要在实际调用的时候,覆写所有的抽象方法。但是,写出匿名对象的真正目的在于某一特定抽象方法的调用,而不是其他的部分。所以,Java1.8 引入了函数式编程。

Lamda 语法:

(参数) -> 单行语句;

(参数) -> {单行语句};

(参数) -> 表达式;

代码示例:


package org.fuys.ownutil.instance;interface Lamp {	void turnOn();}interface Sheep extends Lamp {	void turnOff();}public class LamdaInstance {	public static void main(String[] args) {		// original method		lightOn(new Sheep() {			@Override			public void turnOn() {				System.out.println("The sheep light on");			}			@Override			public void turnOff() {				System.out.println("The sheep light off");			}		});		// lamda method		lightOn(() -> System.out.println("The lamp light off"));	}	public static void lightOn(Lamp lamp){		lamp.turnOn();	}}
复制代码


三、Java8 方法引用


对象引用主要是多个引用操作一个对象。方法引用就是对方法其别名,通过别名仍然可以访问方法。对于方法引用,通过接口里定义一个抽象方法,进行转换。Java8 中定义四种形式如下:

引用静态方法:类名称 :: static 方法名称;

引用某个对象的方法:实例化对象 :: 普通方法;

引用特定类型的方法(对象比较中两个对象作为参数的情况):特定类 :: 普通方法;

引用构造方法:类名称 :: new。

定义接口模型如下:


interface MethodReferenceModel<P,R> {	public R function(P p);}
复制代码


方法引用所使用的接口只能定义一个抽象方法,故需要对接口进行限制,不能让其定义多个抽象方法。使用注解 @FunctionalInterface 说明接口只能定义一个抽象方法,是函数式接口。


引用特定类型的方法代码示例:


package org.fuys.ownutil.instance;@FunctionalInterfaceinterface Shoe <T> {	int takeOn(T t1 ,T t2);}public class MethodReferenceInstance {	public static void main(String[] args) {		Shoe<String> shoe = String :: compareTo;		System.out.println(shoe.takeOn("A", "B"));	}}
复制代码


引用构造方法的代码示例:


package org.fuys.ownutil.instance;@FunctionalInterfaceinterface Desk<T> {	T create(String s,double d);}class Phone{		private String name;	private double price;	public String getName() {		return name;	}	public void setName(String name) {		this.name = name;	}	public double getPrice() {		return price;	}	public void setPrice(double price) {		this.price = price;	}	public Phone() {		super();	}	public Phone(String name, double price) {		super();		this.name = name;		this.price = price;	}	@Override	public String toString() {		return "Phone [name=" + name + ", price=" + price + "]";	}}public class MethodReferenceInstance {	public static void main(String[] args) {		// constructor method reference		Desk<Phone> desk = Phone :: new;		System.out.println(desk.create("MEIZU", 1000.0));		}}
复制代码


四、内建函数式接口


对于方法引用,需要建立函数式接口。jdk1.8 中 java.util.function 的包下提供了以下四种核心接口。

1、Function,功能型接口,该接口接受参数,返回结果。

定义接口如下


@FunctionalInterfacepublic interface Function<T,R>{	R apply​(T t);}
复制代码


2、Consumer,消费型接口,负责接收参数,不进行结果返回

定义接口如下:


@FunctionalInterfacepublic interface Consumer<T>{	void accept​(T t);}
复制代码


3、Supplier,供给型接口,不接收参数,但是返回结果

定义接口如下:


@FunctionalInterfacepublic interface Supplier<T>{	T get​();}
复制代码


4、Predicate,断言型接口,接受参数,进行判断

定义接口如下:


@FunctionalInterfacepublic interface Predicate<T>{	boolean test​(T t);}
复制代码


代码示例如下:


// 1,function		Function<String, byte[]> f = String :: getBytes;		System.out.println(Arrays.toString(f.apply("fuyunsong")));				// 3,supplier		Supplier<Date> s = Calendar.getInstance() :: getTime;		System.out.println(s.get());				// 4,predict		Predicate<File> p = File :: exists;		System.out.println(p.test(new File("e:")));				// 2,consumer		Consumer<Integer> c = Runtime.getRuntime() :: exit;		c.accept(1);
复制代码


用户头像

andy

关注

还未添加个人签名 2019-11-21 加入

还未添加个人简介

评论

发布
暂无评论
java之新特性_andy_InfoQ写作社区