03_Java新特性

第八章 枚举与注解

8.1 枚举

1、枚举(JDK1.5引入的)

枚举类型的对象是有限、固定的几个常量对象。

2、语法格式

//形式一:枚举类型中只有常量对象列表
【修饰符】 enum 枚举类型名{
    常量对象列表
}

//形式二:枚举类型中只有常量对象列表
【修饰符】 enum 枚举类型名{
    常量对象列表;
    
    其他成员列表;
}

说明:常量对象列表必须在枚举类型的首行

回忆:首行

(1)super()或super(实参列表):必须在子类构造器的首行

(2)this()或this(实参列表):必须在本类构造器的首行

(3)package 包; 声明包的语句必须在源文件.java的代码首行

(4)枚举常量对象列表必须在枚举类型的首行

3、在其他类中如何获取枚举的常量对象

//获取一个常量对象
枚举类型名.常量对象名

//获取一个常量对象
枚举类型名.valueOf("常量对象名")
    
//获取所有常量对象
枚举类型名[] all = 枚举类型名.values();

4、枚举类型的特点

(1)枚举类型有一个公共的基本的父类,是java.lang.Enum类型,所以不能再继承别的类型

(2)枚举类型的构造器必须是私有的

(3)枚举类型可以实现接口

interface MyRunnable{
    void run();
}
enum Gender implements MyRunnable{
    NAN,NV;
    public void run(){
        //...
    }
}
//或
enum Gender implements MyRunnable{
    NAN{
        public void run(){
       	 //...
    	}
    },NV{
        public void run(){
        //...
   		}
    };
    
}

5、父类java.lang.Enum类型

(1)构造器

protected Enum(String name, int ordinal):由编译器自动调用

(2)String name():常量对象名

(3)int ordinal():返回常量对象的序号,第一个的序号是0

(4)String toString():返回常量对象名,如果子类想重写,需要手动

(5)int compareTo(Object obj):按照常量对象的顺序比较

8.2 注解

1、注解

它是代码级别的注释

2、标记符号:@

3、系统预定义的三个最基本的注解:

(1)@Override:表示某个方法是重写的方法

它只能用在方法上面,会让编译器对这个方法进行格式检查,是否满足重写的要求

(2)@SuppressWarnings(xx):抑制警告

(3)@Deprecated:表示xx已过时

4、和文档注释相关的注解

(1)文档注释

/**
文档注释
*/

(2)常见的文档注释

@author:作者

@since:从xx版本加入的

@see:另请参考

@param:形参

@return:返回值

@throws或@exception:异常

5、JUnit相关的几个注解

(1)@Test:表示它是一个单元测试方法

这个方法需要是:public void xxx(){}

(2)@Before:表示在每一个单元测试方法之前执行

这个方法需要是:public void xxx(){}

(3)@After:表示在每一个单元测试方法之后执行

这个方法需要是:public void xxx(){}

(4)@BeforeClass:表示在类初始化阶段执行,而且只执行一次

这个方法需要是:public static void xxx(){}

(3)@AfterClass:表示在类的“卸载”阶段执行,而且只执行一次

这个方法需要是:public static void xxx(){}

6、元注解

(1)@Target(xx):用它标记的注解能够用在xx位置

(xx):由ElementType枚举类型的10个常量对象指定,例如:TYPE,METHOD,FIELD等

例如:

@Target(ElementType.TYPE)

@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD})
import static java.lang.annotation.ElementType.*;


@Target({TYPE,METHOD,FIELD})

(2)@Retention(xx):用它标记的注解可以滞留到xx阶段

(xx):由RetentionPolicy枚举类型的3个常量对象指定,分别是:SOURCE,CLASS,RUNTIME

唯有RUNTIME阶段的注解才能被反射读取到

例如:

@Retention(RetentionPolicy.RUNTIME)

(3)@Documentd:用它标记的注解可以读取到API中

(4)@Inherited:用它标记的注解可以被子类继承

7、自定义注解

@元注解
【修饰符】 @interface 注解名{
    
}

@元注解
【修饰符】 @interface 注解名{
    配置参数列表
}

配置参数的语法格式:

数据类型  配置参数名();

或

数据类型  配置参数名() default 默认值;

关于配置参数:

(1)配置参数的类型有要求:

八种基本数据类型、String、枚举、Class类型、注解、它们的数组。

(2)如果自定义注解声明了配置参数,那么在使用这个注解时必须为配置参数赋值,除非它有默认值

@自定义注解名(配置参数名1=值,配置参数名2=值。。。)

//如果配置参数类型是数组,那么赋值时,可以用{}表示数组
@自定义注解名(配置参数名1={},配置参数名2=值。。。)

(3)如果配置参数只有一个,并且名称是value,那么赋值时可以省略value=

(4)如果读取这个注解时,要获取配置参数的值的话,可以当成方法一样来访问

自定义注解对象.配置参数();

第13章 泛型

13.1 泛型的概述

泛型:参数化类型

类型形参:,,,,,。。。。

类型实参:必须是引用数据类型,不能是基本数据类型

​ ,,,<ArrayList>。。。

13.2 形式一:泛型类与泛型接口

1、声明语法格式:

【修饰符】 class 类名/接口<类型形参列表>{
    
}

【修饰符】 class 类名/接口<类型形参1 extends 父类上限>{
    
}
【修饰符】 class 类名/接口<类型形参1 extends 父类上限 & 父接口上限>{
    
}

在类名或接口名后面声明的泛型形参类型,可以在当前类或接口中使用,用作声明成员变量、方法的形参、方法的返回值。

但是不能用于静态成员

2、使用语法格式

在(1)创建泛型类、泛型接口的对象时,为泛型形参指定具体类型

​ (2)在继承泛型类或实现泛型接口时,为泛型形参指定具体类型

示例代码

ArrayList<String> list = new ArrayList<String>();

ArrayList<String> list = new ArrayList<>();//JDK1.7之后可以省略

class MyStringArrayList extends ArrayList<String>{
    
}

class Employee implements Comparable<Employee>{
    public int compareTo(Employee e){
        
    }
}

Arrays.sort(数组,  new  Comparator<泛型实参>(){
    public int compare(泛型实参类型  o1, 泛型实参类型  o2){
        
    }
});

3、泛型如果没有指定,会被擦除,按照最左边的上限处理,如果没有指定上限,按照Object处理

13.3 形式二:泛型方法

1、声明的语法格式

【修饰符】 <泛型形参列表>  返回值类型  方法名(【数据形参列表】)【throws 异常列表】{}
【修饰符】 <泛型形参 extends 父类上限 & 父接口上限>  返回值类型  方法名(【数据形参列表】)【throws 异常列表】{}

(1)在方法返回值类型前面声明的泛型形参类型,只能在当前方法中使用,用于表示形参的类型或返回值类型,或方法局部变量的类型,和别的方法无关。

(2)泛型方法可以是静态方法,也可以是非静态方法

2、 使用

当调用方法,会根据具体的数据的实参的类型,来确定泛型实参的类型。

13.4 通配符?

(1)?:代表任意引用数据类型

(2)? extends 上限:代表上限本身或它的子类

(3)? super 下限:代表下限本身或它的父类

例如:

ArrayList<?>:表示可以接受任意类型

ArrayList<?> list = new ArrayList<String>();
ArrayList<?> list = new ArrayList<Integer>();
ArrayList<?> list = new ArrayList<Animal>();

ArrayList<? extends 上限>:

ArrayList<? extends Person> list = new ArrayList<Person>();
ArrayList<? extends Person> list = new ArrayList<Animal>();//Animal不行,因为Animal是父类
ArrayList<? extends Person> list = new ArrayList<Student>();
ArrayList<? extends Person> list = new ArrayList<Dog>();//Dog也不行

ArrayList<? super 下限>:

ArrayList<? super Person> list = new ArrayList<Person>();
ArrayList<? super Person> list = new ArrayList<Animal>();
ArrayList<? super Person> list = new ArrayList<Student>();//Student,因为Student是子类
ArrayList<? super Person> list = new ArrayList<Dog>();//Dog也不行

ArrayList<?>:不能添加元素,除了null

ArrayList<? extends 上限>:不能添加元素,除了null

ArrayList<? super 下限>:可以添加下限或下限子类的对象

13.5 Collections工具类

java.util.Collections:工具类,操作集合

(1)public static boolean addAll(Collection<? super T> c, T… elements)

添加elements的几个对象到c集合中。T是elements对象的类型,要求Collection集合的元素类型必须是T或T的父类

(2)public static int binarySearch(List<? extends Comparable<? super T>> list,T key)

在list集合中用二分查找key的下标,如果存在返回的是合理的下标,如果不存在返回的是一个负数下标

T是元素的类型,

<? extends Comparable<? super T>>,要求集合的元素必须实现Comparable接口 <? super T>,在实现Comparable接口,可以指定Comparable<类型实参>为T或T的父类。 (3)public static boolean disjoint(Collection<?> c1, Collection<?> c2)

判断c1和c2没有交集就为true

(4)public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)

求coll集合中最大元素

<T extends Object & Comparable<? super T>>:要求T或T的父类实现Comparable接口

因为找最大值需要比较大小

(5)public static <T extends Comparable<? super T>> void sort(List list) 给list集合排序

<T extends Comparable<? super T>>:要求T或T的父类实现Comparable接口

(6)public static Collection synchronizedCollection(Collection c)

以synchronizedXX开头的方法,表示把某种非线程安全集合转为一个线程安全的集合。

(7)public static List unmodifiableList(List<? extends T> list)

以unmodifiableXx开头的方法,表示返回一个“只读”的集合。

第十七章 Java8

17.1 Lambda表达式与函数式接口

Lambda写的好可以极大的减少代码冗余,同时可读性也好过冗长的匿名内部类。

17.1.1 函数式接口

lambda表达式其实就是实现SAM接口的语法糖,所谓SAM接口就是Single Abstract Method,即该接口中只有一个抽象方法需要实现,当然该接口可以包含其他非抽象方法。

其实只要满足“SAM”特征的接口都可以称为函数式接口,但是如果要更明确一点,最好在声明接口时,加上@FunctionalInterface。

之前学过的接口中,是函数式接口的有:Runnable,Comparator,FileFilter

1、消费型接口

这类接口的抽象方法特点:有形参,但是返回值类型是void

接口名抽象方法描述
Consumervoid accept(T t)接收一个对象用于完成功能
BiConsumer<T,U>void accept(T t, U u)接收两个对象用于完成功能
DoubleConsumervoid accept(double value)接收一个double值
IntConsumervoid accept(int value)接收一个int值
LongConsumervoid accept(long value)接收一个long值
ObjDoubleConsumervoid accept(T t, double value)接收一个对象和一个double值
ObjIntConsumervoid accept(T t, int value)接收一个对象和一个int值
ObjLongConsumervoid accept(T t, long value)接收一个对象和一个long值
2、供给型接口

这类接口的抽象方法特点:无参,但是无返回值

接口名抽象方法描述
SupplierT get()返回一个对象
BooleanSupplierboolean getAsBoolean()返回一个boolean值
DoubleSupplierdouble getAsDouble()返回一个double值
IntSupplierint getAsInt()返回一个int值
LongSupplierlong getAsLong()返回一个long值
3、判断型接口

这里接口的抽象方法特点:有参,但是返回值类型是boolean结果。

接口名抽象方法描述
Predicateboolean test(T t)接收一个对象
BiPredicate<T,U>boolean test(T t, U u)接收两个对象
DoublePredicateboolean test(double value)接收一个double值
IntPredicateboolean test(int value)接收一个int值
LongPredicateboolean test(long value)接收一个long值
4、功能型接口

这类接口的抽象方法特点:既有参数又有返回值

接口名抽象方法描述
Function<T,R>R apply(T t)接收一个T类型对象,返回一个R类型对象结果
UnaryOperatorT apply(T t)接收一个T类型对象,返回一个T类型对象结果
DoubleFunctionR apply(double value)接收一个double值,返回一个R类型对象
IntFunctionR apply(int value)接收一个int值,返回一个R类型对象
LongFunctionR apply(long value)接收一个long值,返回一个R类型对象
ToDoubleFunctiondouble applyAsDouble(T value)接收一个T类型对象,返回一个double
ToIntFunctionint applyAsInt(T value)接收一个T类型对象,返回一个int
ToLongFunctionlong applyAsLong(T value)接收一个T类型对象,返回一个long
DoubleToIntFunctionint applyAsInt(double value)接收一个double值,返回一个int结果
DoubleToLongFunctionlong applyAsLong(double value)接收一个double值,返回一个long结果
IntToDoubleFunctiondouble applyAsDouble(int value)接收一个int值,返回一个double结果
IntToLongFunctionlong applyAsLong(int value)接收一个int值,返回一个long结果
LongToDoubleFunctiondouble applyAsDouble(long value)接收一个long值,返回一个double结果
LongToIntFunctionint applyAsInt(long value)接收一个long值,返回一个int结果
DoubleUnaryOperatordouble applyAsDouble(double operand)接收一个double值,返回一个double
IntUnaryOperatorint applyAsInt(int operand)接收一个int值,返回一个int结果
LongUnaryOperatorlong applyAsLong(long operand)接收一个long值,返回一个long结果
BiFunction<T,U,R>R apply(T t, U u)接收一个T类型和一个U类型对象,返回一个R类型对象结果
BinaryOperatorT apply(T t, T u)接收两个T类型对象,返回一个T类型对象结果
ToDoubleBiFunction<T,U>double applyAsDouble(T t, U u)接收一个T类型和一个U类型对象,返回一个double
ToIntBiFunction<T,U>int applyAsInt(T t, U u)接收一个T类型和一个U类型对象,返回一个int
ToLongBiFunction<T,U>long applyAsLong(T t, U u)接收一个T类型和一个U类型对象,返回一个long
DoubleBinaryOperatordouble applyAsDouble(double left, double right)接收两个double值,返回一个double结果
IntBinaryOperatorint applyAsInt(int left, int right)接收两个int值,返回一个int结果
LongBinaryOperatorlong applyAsLong(long left, long right)接收两个long值,返回一个long结果

17.1.2 Lambda表达式语法

Lambda表达式是用来给【函数式接口】的变量或形参赋值用的。

其实本质上,Lambda表达式是用于实现【函数式接口】的“抽象方法”

Lambda表达式语法格式

(形参列表) -> {Lambda体}

说明:

  • (形参列表)它就是你要赋值的函数式接口的抽象方法的(形参列表),照抄
  • {Lambda体}就是实现这个抽象方法的方法体
  • ->称为Lambda操作符(减号和大于号中间不能有空格,而且必须是英文状态下半角输入方式)

优化:Lambda表达式可以精简

  • 当{Lambda体}中只有一句语句时,可以省略{}和{;}
  • 当{Lambda体}中只有一句语句时,并且这个语句还是一个return语句,那么return也可以省略,但是如果{;}没有省略的话,return是不能省略的
  • 当(形参列表)的类型是已知的,那么形参的类型可以省略
  • 当(形参列表)的类型是已知的,并且形参个数只有一个,那么可以把数据类型和()一起省略,但是形参名不能省略
  • 当(形参列表)是空参时,()不能省略

示例代码:

public class TestLambdaGrammer {
	@Test
	public void test1(){
		//用Lambda表达式给Runnable接口的形参或变量赋值
		/*
		 * 确定两件事,才能写好lambda表达式
		 * (1)这个接口的抽象方法长什么样:
		 * 		public void run()
		 * (2)这个抽象方法的实现要干什么事
		 * 		例如:我要打印“hello lambda"
		 */
		Runnable r = () -> {System.out.println("hello lambda");};
	}
	
	@Test
	public void test2(){
		//lambda体省略了{;}
		Runnable r = () -> System.out.println("hello lambda");
	}
	
	@Test
	public void test3(){
		String[] arr = {"hello","Hello","java","chai"};
		
		//为arr数组排序,但是,想要不区分大小写
		/*
		 * public static <T> void sort(T[] a,Comparator<? super T> c)
		 * 这里要用Lambda表达式为Comparator类型的形参赋值
		 * 
		 * 两件事:
		 * (1)这个接口的抽象方法:  int compare(T o1, T o2)
		 * (2)这个抽象方法要做什么事?
		 * 		例如:这里要对String类型的元素,不区分大小写的比较大小
		 */
//		Arrays.sort(arr, (String o1, String o2) -> {return o1.compareToIgnoreCase(o2);});
		
		//省略了{return ;}
//		Arrays.sort(arr, (String o1, String o2) ->  o1.compareToIgnoreCase(o2));
		
		//省略了两个String
		Arrays.sort(arr, (o1, o2) ->  o1.compareToIgnoreCase(o2));
		
		for (String string : arr) {
			System.out.println(string);
		}
	}
	
	@Test
	public void test4(){
		ArrayList<String> list = new ArrayList<>();
		list.add("hello");
		list.add("java");
		list.add("world");
		
		/*
		 * JDK1.8给Collection系列的集合,准确的讲是在Iterable接口中,增加了一个默认方法
		 * 		default void forEach(Consumer<? super T> action) 
		 * 这个方法是用来遍历集合等的。代替原来的foreach循环的。
		 * 
		 * 这个方法的形参是Consumer接口类型,它是函数式接口中消费型接口的代表
		 * 我现在调用这个方法,想要用Lambda表达式为Consumer接口类型形参赋值
		 * 
		 * 两件事:
		 * (1)它的抽象方法:  void  accept(T t)
		 * (2)抽象方法的实现要完成的事是什么
		 * 		例如:这里要打印这个t
		 */
//		list.forEach((String t) -> {System.out.println(t);});
		
		//省略{;}
//		list.forEach((String t) -> System.out.println(t));
		
		//省略String
//		list.forEach((t) -> System.out.println(t));
		
		//可以省略形参()
		list.forEach(t -> System.out.println(t));
	}
}

17.1.3 方法引用与构造器引用

Lambda表达式是可以简化函数式接口的变量与形参赋值的语法。而方法引用和构造器引用是为了简化Lambda表达式的。

1、当Lambda表达式满足一些特殊的情况时,还可以再简化:

(1)Lambda体只有一句语句,并且是通过调用一个对象的/类现有的方法来完成的

例如:System.out对象,调用println()方法来完成Lambda体

​ Math类,调用random()静态方法来完成Lambda体

(2)并且Lambda表达式的形参正好是给该方法的实参

例如:t->System.out.println(t)

​ () -> Math.random() 都是无参

2、方法引用的语法格式:

(1)实例对象名::实例方法

(2)类名::静态方法

(3)类名::实例方法

说明:

  • ::称为方法引用操作符(两个:中间不能有空格,而且必须英文状态下半角输入)

  • Lambda表达式的形参列表,全部在Lambda体中使用上了,要么是作为调用方法的对象,要么是作为方法的实参。

  • 在整个Lambda体中没有额外的数据。

3、构造器引用

(1)当Lambda表达式是创建一个对象,并且满足Lambda表达式形参,正好是给创建这个对象的构造器的实参列表。

(2) 当Lambda表达式是创建一个数组对象,并且满足Lambda表达式形参,正好是给创建这个数组对象的长度

4、构造器引用的语法格式:

  • 类名::new

  • 数组类型名::new

示例代码:

public class TestMethodReference {
	
	//这个方法是模仿HashMap中,把你指定的数组的长度纠正为2的n次方的代码
	//createArray()的作用是,创建一个长度为2的n次方的数组
	public <R> R[] createArray(Function<Integer,R[]> fun,int length){
		int n = length - 1;
        n |= n >>> 1;
        n |= n >>> 2;
        n |= n >>> 4;
        n |= n >>> 8;
        n |= n >>> 16;
        length = n < 0 ? 1 : n + 1;
		return fun.apply(length);
	}
	
	@Test
	public void test6(){
		/*
		 * Function是一个函数式接口,可以用Lambda表达式赋值
		 * Function<T,R>的抽象方法   R apply(T t)
		 * 
		 * createArray这个方法中用的是Function<Integer,R[]> fun。说明T类型已经指定为Integer
		 * 说明
		 */
//		Function<Integer,String[]> f = (Integer len) -> new String[len];
		
		//因为Lambda体是在创建一个数组对象完成的,而且Lambda表达式的形参正好是创建数组用的长度
		//通过构造器引用省略
		Function<Integer,String[]> f = String[]::new;
		String[] array = createArray(f, 10);
		
		System.out.println(array.length);//16
	}
	
	@Test
	public void test5(){
//		Supplier<String> s = () -> new String();//通过供给型接口,提供一个空字符串对象
		
		//构造器引用
		Supplier<String> s = String::new;//通过供给型接口,提供一个空字符串对象
	}
	
	@Test
	public void test4(){
//		Runnable r = () -> System.out.println("hello lambda");
		Runnable r = System.out::println;//打印空行
		
		//不能简化方法引用,因为"hello lambda"这个无法省略
	}
	
	@Test
	public void test3(){
		String[] arr = {"Hello","java","chai"};
//		Arrays.sort(arr, (s1,s2) -> s1.compareToIgnoreCase(s2));
		
		//用方法引用简化
		/*
		 * Lambda表达式的形参,第一个(例如:s1),正好是调用方法的对象,剩下的形参(例如:s2)正好是给这个方法的实参
		 */
		Arrays.sort(arr, String::compareToIgnoreCase);
	}
	
	@Test
	public void test2(){
//		Stream<Double> stream = Stream.generate(() -> Math.random());
		
		//用方法引用简化
		Stream<Double> stream = Stream.generate(Math::random);
	}
	
	@Test
	public void test1(){
		List<Integer> list = Arrays.asList(1,3,4,8,9);
		
		//list.forEach(t -> System.out.println(t));
		
		//用方法再简化
		list.forEach(System.out::println);
	}
}

17.2 StreamAPI

Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。

Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。 使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,负责存储数据,Stream流讲的是计算,负责处理数据!”

注意:

①Stream 自己不会存储元素。

②Stream 不会改变源对象。每次处理都会返回一个持有结果的新Stream。

③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

Stream 的操作三个步骤:

1- 创建 Stream:通过一个数据源(如:集合、数组),获取一个流

2- 中间操作:中间操作是个操作链,对数据源的数据进行n次处理,但是在终结操作前,并不会真正执行。

3- 终止操作:一旦执行终止操作,就执行中间操作链,最终产生结果并结束Stream。

17.2.1 创建Stream

1、创建 Stream方式一:通过集合

Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:

  • public default Stream stream() : 返回一个顺序流

  • public default Stream parallelStream() : 返回一个并行流

2、创建 Stream方式二:通过数组

Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:

  • public static Stream stream(T[] array): 返回一个流

重载形式,能够处理对应基本类型的数组:

  • public static IntStream stream(int[] array):返回一个整型数据流

  • public static LongStream stream(long[] array):返回一个长整型数据流

  • public static DoubleStream stream(double[] array):返回一个浮点型数据流

3、创建 Stream方式三:通过Stream的of()

可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。

  • public static Stream of(T… values) : 返回一个顺序流

4、创建 Stream方式四:创建无限流

可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。

  • public static Stream iterate(final T seed, final UnaryOperator f):返回一个无限流

  • public static Stream generate(Supplier s) :返回一个无限流

17.2.2 中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。

方 法描 述
filter(Predicate p)接收 Lambda , 从流中排除某些元素
distinct()筛选,通过流所生成元素的equals() 去除重复元素
limit(long maxSize)截断流,使其元素不超过给定数量
skip(long n)跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
peek(Consumer action)接收Lambda,对流中的每个数据执行Lambda体操作
sorted()产生一个新流,其中按自然顺序排序
sorted(Comparator com)产生一个新流,其中按比较器顺序排序
map(Function f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
flatMap(Function f)接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流

17.2.3 终结操作

终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void。流进行了终止操作后,不能再次使用。

方法描述
boolean allMatch(Predicate p)检查是否匹配所有元素
boolean anyMatch(Predicate p)检查是否至少匹配一个元素
boolean noneMatch(Predicate p)检查是否没有匹配所有元素
Optional findFirst()返回第一个元素
Optional findAny()返回当前流中的任意元素
long count()返回流中元素总数
Optional max(Comparator c)返回流中最大值
Optional min(Comparator c)返回流中最小值
void forEach(Consumer c)迭代
T reduce(T iden, BinaryOperator b)可以将流中元素反复结合起来,得到一个值。返回 T
U reduce(BinaryOperator b)可以将流中元素反复结合起来,得到一个值。返回 Optional
R collect(Collector c)将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法

Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例。

17.3 Optional类

到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。

Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。

它的API:

1、如何创建Optional对象?或者说如何用Optional来装值对象或null值

(1)static Optional empty() :用来创建一个空的Optional

(2)static Optional of(T value) :用来创建一个非空的Optional

(3)static Optional ofNullable(T value) :用来创建一个可能是空,也可能非空的Optional

2、如何从Optional容器中取出所包装的对象呢?

(1)T get() :要求Optional容器必须非空

T get()与of(T value)使用是安全的

(2)T orElse(T other) :

orElse(T other) 与ofNullable(T value)配合使用,

如果Optional容器中非空,就返回所包装值,如果为空,就用orElse(T other)other指定的默认值(备胎)代替

(3)T orElseGet(Supplier<? extends T> other) :

如果Optional容器中非空,就返回所包装值,如果为空,就用Supplier接口的Lambda表达式提供的值代替

(4) T orElseThrow(Supplier<? extends X> exceptionSupplier)

如果Optional容器中非空,就返回所包装值,如果为空,就抛出你指定的异常类型代替原来的NoSuchElementException

3、其他方法

(1)boolean isPresent() :判断Optional容器中的值是否存在

(2)void ifPresent(Consumer<? super T> consumer) :

判断Optional容器中的值是否存在,如果存在,就对它进行Consumer指定的操作,如果不存在就不做

(3) Optional map(Function<? super T,? extends U> mapper)

判断Optional容器中的值是否存在,如果存在,就对它进行Function接口指定的操作,如果不存在就不做

第十八章 设计模式

18.1 模板设计模式(了解)

1、当解决某个问题,或者完成某个功能时,主体的算法结构(步骤)是确定的,只是其中的一个或者几个小的步骤不确定,要有使用者(子类)来确定时,就可以使用模板设计模式

2、示例代码:计算任意一段代码的运行时间

//模板类
public abstract class CalTime{
    public long getTime(){
        //1、获取开始时间
        long start =  System.currentTimeMills();
        
        //2、运行xx代码:这个是不确定的
        doWork();
        
        //3、获取结束时间
        long end =  System.currentTimeMills();
        
        //4、计算时间差
        return end - start;
    }
    
    protected abstract void doWork();
}

使用模板类:

public class MyCalTime extends CalTime{
    protected void doWork(){
        //....需要计算运行时间的代码
    }
}

测试类

public class Test{
    public static void main(String[] args){
        MyCalTime my = new MyCalTime();
        System.out.println("运行时间:" + my.getTime());
    }
}

18.2 单例设计模式

单例:整个系统中,某个类型的对象只有一个。

1、饿汉式

(1)枚举式

public enum Single{
    INSTANCE
}

(2)形式二

public class Single{
    public static final Single INSTANCE = new Single();
    private Single(){
        
    }
}

(3)形式三

public class Single{
    private static final Single INSTANCE = new Single();
    private Single(){
        
    }
    public static Single getInstance(){
        return INSTANCE;
    }
}

2、懒汉式

(1)内部类形式

public class Single{
    private Single(){}
    
    private static class Inner{
        static final Single INSTANCE = new Single();
    }
    
    public static Single getInstance(){
        return Inner.INSTANCE;
    }
    
}

(2)形式二

public class Single{
    private static Single instance;
    private Single(){}
    
    public static Single getInstance(){
        if(instance == null){
            synchronized(Single.class){
                if(instance == null){
                    instance = new Single();
                }
            }
        }
        return instance;
    }
}

18.3 工厂设计模式

解决的问题:把对象的创建者与对象的使用者分离,把对象的创建统一到一个地方(工厂)

18.3.1 简单工厂模式

示例代码:

interface Car{
	void run();
}

class BMW implements Car{

	@Override
	public void run() {
		System.out.println("让你在宝马车里哭");
	}
	
}
class Benz implements Car{
	@Override
	public void run() {
		System.out.println("奔驰让你坐在引擎盖上哭");
	}
}

class SimpleFactory{
	public static Car getCar(String type){
		switch(type){
		case "宝马":
			return new BMW();
		case "奔驰":
			return new Benz();
		}
		return null;
	}
}

如果有反射:简单工厂模式的工厂类可以优化:

class SimpleFactoryPlus{
	/*
	 * type:类型的全名称
	 */
	public static Car getCar(String type) throws Exception{
		//获取Car的实现类的Class对象
		Class clazz = Class.forName(type);
		//创建Car的实现类的实例对象
		Object obj = clazz.newInstance();
		//强转类并返回车的实例对象
		return (Car)obj;
	}
}

18.3.2 工厂方法模式

示例代码:

interface Car{
	void run();
}

class BMW implements Car{

	@Override
	public void run() {
		System.out.println("让你在宝马车里哭");
	}
	
}
class Benz implements Car{
	@Override
	public void run() {
		System.out.println("奔驰让你坐在引擎盖上哭");
	}
}

//工厂的公共接口
interface Factory{
	Car getCar();
}
class BMWFactory implements Factory{

	@Override
	public BMW getCar() {
		return new BMW();
	}
	
}
class BenzFactory implements Factory{

	@Override
	public Benz getCar() {
		return new Benz();
	}
	
}

18.4 静态代理模式

静态代理类只能替一个主题接口进行代理工作。

如果主题接口不同,代理工作相同,也需要编写两个代理类。

示例代码:

public class TestProxy {
	@Test
	public void test1(){
		TimeProxy tp = new TimeProxy(new GoodsDAO());
		tp.add();
	}
	@Test
	public void test2(){
		TimeProxy tp = new TimeProxy(new UserDAO());
		tp.add();
	}
}
//主题接口
interface DAO{
	void add();
}
//被代理类
class GoodsDAO implements DAO{
	public void add(){
		System.out.println("商品的添加");
	}
}
class UserDAO implements DAO{
	public void add(){
		System.out.println("用户的添加");
	}
}
//代理类
class TimeProxy implements DAO{
	private DAO target;//target表示被代理者对象

	public TimeProxy(DAO target) {
		super();
		this.target = target;
	}

	@Override
	public void add() {
		long start = System.currentTimeMillis();
		target.add();//核心业务逻辑交还给被代理者
		long end = System.currentTimeMillis();
		System.out.println("时间差:" +(end-start));
	}
	
}

18.5 动态代理

步骤:

(1)主题接口

(2)被代理类

(3)动态代理的代理工作处理器

要求必须实现:java.lang.reflect.InvocationHandler接口,重写

Object invoke(Object proxy, Method method,Object[] args)

(4)创建代理类对象

java.lang.reflect.Proxy类型的静态方法

newProxyInstance(ClassLoader loader, Class[] interfaces,InvocationHandler h )

(5)调用对用的方法

示例代码:

 /* 步骤:
 * 1、编写主题接口(和静态代理一样)
 * 2、编写被代理类(和静态代理一样)
 * 3、编写代理工作处理器:即代理类要替被代理类做什么事情
 * 要求:必须实现InvocationHandler,重写
 *   Object invoke(Object proxy, Method method, Object[] args)
 *   第一个参数:代理类对象
 *   第二个参数:被代理类和代理类   要执行的方法
 *   第三个参数:要执行方法的实参列表
 *   
 *   这个invoke方法不是程序员调用,当代理类对象执行对应的代理方法时,自动调用的
 *   
 * 4、创建代理类及其对象
 *   需要:Proxy:提供用于创建动态代理类和实例的静态方法,它还是由这些方法创建的所有动态代理类的超类。
 *   
 *    static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) 
 *    第一个参数:被代理类的类加载器,我们希望被代理和代理类使用同一个类加载器
 *    第二个参数:被代理类实现的接口们
 *    第三个参数:代理工作处理器对象
 *    
 * 5、调用被代理的方法   
 */
public class TestProxy2 {
	@Test
	public void test2(){
		//被代理对象
		YongHuDAO sd = new YongHuDAO();
		
		ClassLoader loader = sd.getClass().getClassLoader();//被代理者的类加载器对象
		Class<?>[] interfaces = sd.getClass().getInterfaces();//被代理者实现的接口们
		TimeInvocationHandler h = new TimeInvocationHandler(sd);//代理工作处理器对象
		
		//创建代理类及其对象
		//proxy是代理类的对象,代理类是编译器自动编译生成的一个类
		Object proxy = Proxy.newProxyInstance(loader, interfaces, h);
		
		//这里强转的目的是为了调用增、删、改、查的方法
		//为什么这里强转可以成功了,因为代理类与被代理类实现了相同的主题接口
		DBDAO d = (DBDAO) proxy;
		d.add();
		d.update();
		d.delete();
		d.select();
	}
	
	@Test
	public void test1(){
		//被代理对象
		ShangPinDAO sd = new ShangPinDAO();
		
		ClassLoader loader = sd.getClass().getClassLoader();
		Class<?>[] interfaces = sd.getClass().getInterfaces();
		TimeInvocationHandler h = new TimeInvocationHandler(sd);
		
		//创建代理类及其对象
		//proxy是代理类的对象,代理类是编译器自动编译生成的一个类
		Object proxy = Proxy.newProxyInstance(loader, interfaces, h);
		
		//这里强转的目的是为了调用增、删、改、查的方法
		//为什么这里强转可以成功了,因为代理类与被代理类实现了相同的主题接口
		DBDAO d = (DBDAO) proxy;
		d.add();
		d.update();
		d.delete();
		d.select();
	}
}
//代理工作处理器
class TimeInvocationHandler implements InvocationHandler{
	private Object target;//被代理对象

	public TimeInvocationHandler(Object target) {
		super();
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		long start = System.currentTimeMillis();
		
		//被代理对象的xx方法被调用
		/*
		 * 没有反射:  被代理对象.xx方法(args实参列表)
		 * 有了反射:  方法对象.invoke(被代理对象,args实参列表)
		 */
		Object returnValue = method.invoke(target, args);
		
		long end = System.currentTimeMillis();
		System.out.println(method.getName() + "方法运行时间:" + (end-start));
		
		return returnValue;
	}
	
}


//主题接口
interface DBDAO{
	void add();
	void update();
	void delete();
	void select();
}
//被代理类1
class ShangPinDAO implements DBDAO{
	public void add(){
		System.out.println("添加商品");
	}

	@Override
	public void update() {
		System.out.println("修改商品");
	}

	@Override
	public void delete() {
		System.out.println("删除商品");
	}

	@Override
	public void select() {
		System.out.println("查询商品");
	}
}
//被代理类2
class YongHuDAO implements DBDAO{
	public void add(){
		System.out.println("添加用户");
	}

	@Override
	public void update() {
		System.out.println("修改用户");
	}

	@Override
	public void delete() {
		System.out.println("删除用户");
	}

	@Override
	public void select() {
		System.out.println("查询用户");
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值