接口中可以有哪些内用
- JDk1.7以前
抽象方法、常量
2.JDK1.8
默认方法、静态方法
3.JDK1.9
私有方法
接口默认方法的定义和使用
默认方法:使用defaylt修饰,不可省略,供子类调用或者子类重写
默认方法定义格式
public default 返回值类型 方法的名称(参数列表){
方法体
}
默认方法的好处
接口的默认方法,实现类可以不用重写,默认方法可以用于接口升级
/**
* @Auther: yanqi
* @Desc 实现类没有没重写默认方法呢?
* 接口中的抽象方法是必须要重写(实现)
* 接口中的默认方法,不用重写,也可以手动重写,你默认方法存在的意义?
*
* 如果今后项目代码已以经写好,后期可能要扩展,接口中加新的方法(功能),如果你加抽象方法(功能)
* 所有的实现类有影响,所以针对以上后期可能要扩展功能的话,代码的维护性比较差,出现了默认方法
*/
- 定义接口
public interface LiveAble {
//接口的默认方法
public default void fly(){
System.out.println("天上飞");
}
}
- 定义实现类
public class LiveAbleImpl implements LiveAble {
// default 可以选择是否重写,也可以根据实际需求进行重写
/*
@Override
public void fly() {
System.out.println("自由自在的飞");
}
*/
}
- 定义测试类
public class InterfaceDemo {
public static void main(String[] args) {
// 创建子类对象
LiveAble a = new LiveAbleImpl();
// 调用默认方法
a.fly();
}
}
接口静态方法的定义和使用
静态方法:使用static 修饰,供接口直接调用。
静态与.class文件相关,只能使用接口名调用,不可以通过实现类的类名或则实现类的 对象调用
静态方法定义格式
public static 返回值类型 方法名称(参数列表){
方法体
}
静态方法使用
- 定义接口
public interface LiveAble {
//静态方法
public static void show2(){
System.out.println("静态方法-show2");
}
}
- 定义实现类
public class LiveAbleImpl implements LiveAble {
// 无法重写静态方法
}
- 定义测试类
public class InterfaceDemo {
public static void main(String[] args) {
//无法调用
// LiveAble l = new LiveAbleImpl();
// l.show2();
//接口名.静态方法(参数列表)
LiveAble.show2();
}
}
接口私有方法的定义和使用
如果一个接口中有多个默认方法,并且方法中有重复的内容,那么可以抽取出来,封装到方法中,供默认方法去调用。从设计的角度讲,私有的方法是对默认方法和静态方法的辅助。注意这是JDK1.9的新特性
私有方法分类
- 普通私有方法
只有默认方法可以调用
- 私有静态方法
默认方法和静态方法都可以调用
代码演示
- 私有方法
- 定义接口
public interface MyInterfacePrivateA {
//默认方法
public default void methodDefault1() {
System.out.println("默认方法1");
//调用私有方法
methodCommon();
}
//默认方法
public default void methodDefault2() {
System.out.println("默认方法2");
//调用私有方法
methodCommon();
}
//普通-私有方法
private void methodCommon() {
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
}
- 私有静态方法
- 定义接口
public interface MyInterfacePrivateB {
//静态方法
public default void methodStatic1() {
System.out.println("静态方法1");
//调用私有-静态方法
methodStaticCommon();
}
//静态方法
public static void methodStatic2() {
System.out.println("静态方法2");
//调用私有-静态方法
methodStaticCommon();
}
//私有-静态方法
private static void methodStaticCommon() {
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
}
私有方法主要解决的问题是:
默认方法和static方法代码重复的问题,private方法可作为抽取共性
Lambda表达式
什么是Lambda
Lambda 表达式是 Java8 新增的重要特性,Lambda 使 Java 具有了类似函数式编程的风格,其实 Lambda 表达式也是一个
“语法糖”
,其实质也是由编译器根据表达式推断最终生成原始语法的字节码方式。
面向对象思想:
做一件事情,找一个能解决这个事情的对象,调用这个对象的方法,完成事情
函数式编程思想:
只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程
函数式思想:
函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做”
而我们要学习的Lambda表达式就是函数式思想的体现
函数式接口:
函数式接口在java中指的是:有且仅有一个抽象方法的接口当然接口中可以有其他的方法 (默 认方法,静态方法,私有方法)函数式接口的使用: 一般用作方法的参数和返回值类型
体验Lambda
- 案例需求
启动一个线程,在控制台输出一句话:多线程程序启动了
- 实现步骤
- 定义一个类MyRunnable实现Runnable接口,重写run()方法
- 创建MyRunnable类的对象
- 创建Thread类的对象,把MyRunnable的对象作为构造参数传递
- 启动线程
- 代码实现
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("多线程程序启动了");
}
}
- new Thread
//方式一
MyRunnable my = new MyRunnable();
Thread t = new Thread(my);
t.start();
- 匿名内部类方式
//方式二
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("多线程程序启动了");
}
}).start();
- Lambda
//方式三
new Thread( () -> {
System.out.println("多线程程序启动了");
} ).start();
Lambda标准格式
(形式参数) -> { 代码块 }
- 格式解析:
形式参数
:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
->
:由英文中画线和大于符号组成,固定写法。代表指向动作
代码块
:是我们具体要做的事情,也就是以前我们写的方法体内容
Lambda表达式练习
Lambda表达式使用前提
有一个接口
接口中有且仅有一个抽象方法
抽象方法是无参无返回值
- 实现步骤
定义一个接口(Eatable),里面定义一个抽象方法:void eat();
定义一个测试类(EatableDemo),在测试类中提供两个方法
一个方法是:useEatable(Eatable e)
一个方法是主方法,在主方法中调用useEatable方法
- 代码实现
//接口
public interface Eatable {
//抽象方法无参无返回值
void eat();
}
//实现类
public class EatableImpl implements Eatable {
@Override
public void eat() {
System.out.println("一天一苹果,医生远离我");
}
}
/**
* 测试类
* @Auther: yanqi
* @Desc **函数式接口的使用: 一般用作方法的参数和返回值类型**
*/
public class EatableDemo {
public static void main(String[] args) {
//1、普通用法-必须写实现类
Eatable e = new EatableImpl();
e.eat();
//2、普通用法-匿名内部类, 省略实现类
new Eatable() {
@Override
public void eat() {
System.out.println("aaaa");
}
}.eat();
//3、匿名内部类, 把接口当作一个参数来用
useEatable(new Eatable() {
@Override
public void eat() {
System.out.println("bbbb");
}
});
//4、lambda表达式
useEatable(()->
System.out.println("bbbb")
);
}
//定义一个方法,参数用我们定义的函数式接口
private static void useEatable(Eatable e) {
e.eat();
}
}
抽象方法是有参无返回值
- 实现步骤
定义一个接口(Flyable),里面定义一个抽象方法:void fly(String s);
定义一个测试类(FlyableDemo),在测试类中提供两个方法
一个方法是:useFlyable(Flyable f)
一个方法是主方法,在主方法中调用useFlyable方法
- 代码实现
public interface Flyable {
//抽象方法是有参无返回值
void fly(String s);
}
public class FlyableDemo {
public static void main(String[] args) {
//方式一:匿名内部类
useFlyable(new Flyable() {
@Override
public void fly(String s) {
System.out.println(s);
System.out.println("飞机自驾游");
}
});
System.out.println("--------");
//方式二:Lambda表达式
//useFlyable((String s) -> {
useFlyable((s) -> { //类型也可以省略,lambda表达式会自己行判断类型
System.out.println(s);
System.out.println("飞机自驾游");
});
}
private static void useFlyable(Flyable f) {
f.fly("风和日丽,晴空万里");
}
}
抽象方法是有参有返回值
- 操作步骤
定义一个接口(Addable),里面定义一个抽象方法:int add(int x,int y);
定义一个测试类(AddableDemo),在测试类中提供两个方法
一个方法是:useAddable(Addable a)
一个方法是主方法,在主方法中调用useAddable方法
- 代码实现
public interface Addable {
// 抽象方法是有参有返回值
int add(int x,int y);
}
public class AddableDemo {
public static void main(String[] args) {
//方式一:匿名内部类
useAddable(new Addable() {
@Override
public int add(int x, int y) {
return x+y;
}
});
//方式二:lambda
useAddable((int x,int y) -> {
return x + y;
});
//可以省略类型
useAddable((x,y)->{
return x+y;
});
//如果代码块的语句只有一条,可以省略大括号和分号,和return关键字,不建议
useAddable((x,y)->
x+y
);
}
private static void useAddable(Addable a) {
int sum = a.add(10, 20);
System.out.println(sum);
}
}
Lambda表达式的省略模式
省略规则:
参数类型可以省略
。但是有多个参数的情况下,不能只省略一个如果参数有且仅有一个,那么
小括号可以省略
如果代码块的语句只有一条,可以
省略大括号和分号,和return关键字
方法引用
在使用Lambda表达式时,经常会看到双冒号(::)符号的使用,这个符号表示方法引用
静态方法引用
/**
* @Desc: 静态方法引用
*/
public class LambdaDemo1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
//静态方法引用
list.forEach(LambdaDemo1::print);
//等价于上面静态方法引用
list.forEach(s -> LambdaDemo1.print(s));
}
//静态方法
public static void print(String s){
System.out.println(s);
}
}
对象实例方法
/**
* @Desc: 引用特定对象实例方法
*/
public class LambdaDemo2 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
//实例方法引用
list.forEach(new LambdaDemo2()::print);
//等价于上面静态方法引用
list.forEach(s -> new LambdaDemo2().print(s));
}
//普通方法
public void print(String s){
System.out.println(s);
}
}
引用父类
/**
* @Desc:
*/
public class LambdaFather {
public void print(String s ){
System.out.println(s);
}
}
/**
* @Desc: 引用超类(父类)实例方法
*/
public class LambdaDemo3 extends LambdaFather{
public void show(){
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
//实例方法引用
list.forEach(super::print);
}
}
public class LambdaDemo3Test {
public static void main(String[] args) {
//普通调用
new LambdaDemo3().show();
// Supplier 就是一个函数式接口
Supplier<LambdaDemo3> supplier = LambdaDemo3::new;
LambdaDemo3 lambdaDemo3 = supplier.get();
lambdaDemo3.show();
}
}
引用构造器
public class Person {
private String name;
private int age;
//提供 无参有参构造 get set toString方法
}
public interface Inert {
Person show(String name, int age);
}
public class Test {
public static void main(String[] args) {
// 引用构造器-当作参数
getShow(Person::new);
// 引用构造器-直接使用
Supplier<Person> supplier = Person::new;
Person person = supplier.get();
person.setName("路飞");
person.setAge(1);
System.out.println(person);
}
private static void getShow(Inert i) {
Person p = i.show("小面包",22);
System.out.println(p.getName());
}
}
Stream流
Stream流是一个数据流,也就说在流里面包含了一堆数据。
它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。Stream(流)是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
优势:
对数据的操作,一种情况下是使用数据库进行操作,数据库存在磁盘上,对于需要从数据库读取出来的数据,在内存进行高效处理的时候,这个时候就引入了Stream流的概念。集合数据,在去数据库之前,也可以借助于Stream做过滤操作。
获取Stream流的三种方式
- Collection体系集合
使用默认方法stream()生成流, default Stream<E> stream()
- Map体系集合
把Map转成Set集合,间接的生成流
- 数组
通过Stream接口的静态方法of(T... values)生成流
代码演示:
public class StreamDemo {
public static void main(String[] args) {
//Collection体系的集合可以使用默认方法stream()生成流
List<String> list = new ArrayList<String>();
Stream<String> listStream = list.stream();
Set<String> set = new HashSet<String>();
Stream<String> setStream = set.stream();
//Map体系的集合间接的生成流
Map<String,Integer> map = new HashMap<String, Integer>();
Stream<String> keyStream = map.keySet().stream();
Stream<Integer> valueStream = map.values().stream();
Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
//数组可以通过Stream接口的静态方法of(T... values)生成流
String[] strArray = {"hello","world","java"};
Arrays.stream(strArray).forEach(System.out::println);
Arrays.asList(strArray).forEach(System.out::println);
//Stream类的of()方法直接操作数据,获取Stream流对象
Stream<String> strArrayStream = Stream.of(strArray);
Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");
Stream<Integer> intStream = Stream.of(10, 20, 30);
}
}
Stream流中间操作方法
概念
中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作。
一个中间操作链,对数据源的数据进行处理。
多个中间操作可以连接起来形成一个流水线
每次调用完成以后返回一个新的流对象, 可以继续使用,支持链式编程!
常见方法;
Stream流终结操作方法
概念
一旦Stream调用了终结方法,流的操作就全部终结了,不能继续使用,只能创建新的Stream操作。 终结方法: foreach , count.
相关方法:
终结方法: 一旦Stream调用了终结方法,流的操作就全部终结了,不能继续使用,只能创建新的Stream操作。 终结方法: foreach , count
非终结方法: 每次调用完成以后返回一个新的流对象, 可以继续使用,支持链式编程!
Stream流的收集操作
概念
对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中。
常用方法
方法名 | 说明 |
---|---|
R collect(Collector collector) | 把结果收集到集合中 |
- ==工具类Collectors提供了具体的收集方式==
方法名 | 说明 |
---|---|
public static <T> Collectors toList() | 把元素收集到List集合中 |
public static <T> Collectors toSet() | 把元素收集到Set集合中 |
public static Collectors toMap(Function keyMapper,Function valueMapper) | 把元素收集到Map集合中 |