黑马程序员JAVA学习第九节
(视频链接黑马程序员全套Java教程_Java基础入门教程,零基础小白自学Java必备教程_哔哩哔哩 (゜-゜)つロ 干杯~-bilibili)
本节主要内容是函数式接口,Stream流,类加载,反射,模块化。对应视频372-410。
函数式接口
1.函数式接口概述
1)概念 有且仅有一个抽象方法的接口
2)如何检测一个接口是不是函数式接口
@FunctionalInterface 放在接口定义的上方:如果接口是函数式接口,编译通过;如果不是,编译失败
3)注意事项
我们自己定义函数式接口的时候,@FunctionalInterface是可选的,就算我不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口。但是,建议加上该注解
2.函数式接口作为方法的参数
如果方法的参数是一个函数式接口,我们可以使用Lambda表达式作为参数传递。
//Lambda方式
startThread(() -> System.out.println(Thread.currentThread().getName() + "线程启动了"));
3.函数式接口作为方法的返回值
如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回。
private static Comparator<String> getComparator() {
//匿名内部类的方式实现
// return new Comparator<String>() {
// @Override
// public int compare(String s1, String s2) {
// return s1.length()-s2.length();
// }
// };
//Lambda方式实现
return (s1, s2) -> s1.length() - s2.length();
}
4.常用函数式接口之Supplier
1)Supplier 接口
Supplier接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产
什么类型的数据供我们使用。
2)常用方法 只有一个无参的方法
T get() 按照某种实现逻辑(由Lambda表达式实现)返回一个数据
public class SupplierTest {
public static void main(String[] args) {
//定义一个int数组
int[] arr = {19, 50, 28, 37, 46};
int maxValue = getMax(()-> {
int max = arr[0];
for(int i=1; i<arr.length; i++) {
if(arr[i] > max) {
max = arr[i];
}
}
return max;
});
System.out.println(maxValue);
}
//返回一个int数组中的最大值
private static int getMax(Supplier<Integer> sup) {
return sup.get();
}
}
5.常用函数式接口之Consumer
1)Consumer 接口:Consumer接口也被称为消费型接口,它消费(使用)的数据的数据类型由泛型指定
2)方法:
3)代码
//操作一
operatorString("林青霞", s -> System.out.println(s));
//操作二
operatorString("林青霞", s -> System.out.println(new
StringBuilder(s).reverse().toString()));
System.out.println("--------");
//传入两个操作使用andThen完成
operatorString("林青霞", s -> System.out.println(s), s ->
System.out.println(new StringBuilder(s).reverse().toString()));
}
//定义一个方法,用不同的方式消费同一个字符串数据两次
private static void operatorString(String name, Consumer<String> con1,
Consumer<String> con2) {
// con1.accept(name);
// con2.accept(name);
con1.andThen(con2).accept(name);//上两句的改进版本
}
//定义一个方法,消费一个字符串数据
private static void operatorString(String name, Consumer<String> con) {
con.accept(name);
}
6.常用函数式接口之Predicate
1)Predicate 接口 Predicate接口通常用于判断参数是否满足指定的条件
2)方法
3)代码
boolean b1 = checkString("hello", s -> s.length() > 8);
System.out.println(b1);
boolean b2 = checkString("helloworld",s -> s.length() > 8);
System.out.println(b2);
}
//判断给定的字符串是否满足要求
private static boolean checkString(String s, Predicate<String> pre) {
// return !pre.test(s);
return pre.negate().test(s);//与上一句含义相同
}
7. 常用函数式接口之Function
1)Function 接口 Function<T,R>接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值
2)方法
3)代码
//操作一
convert("100",s -> Integer.parseInt(s));
//操作二
convert(100,i -> String.valueOf(i + 566));
//使用andThen的方式连续执行两个操作
convert("100", s -> Integer.parseInt(s), i -> String.valueOf(i + 566));
}
//定义一个方法,把一个字符串转换int类型,在控制台输出
private static void convert(String s, Function<String,Integer> fun) {
// Integer i = fun.apply(s);
int i = fun.apply(s);
System.out.println(i);
}
Stream流
1.Stream 流的好处
1)直接阅读代码的字面意思即可完美展示无关逻辑方式的语义
2)Stream 流把真正的函数式编程风格引入到Java中
2.Stream流的常见生成方式
1)Stream流的思想
2)生成 Stream流的方式
(1)Collection 体系集合
使用默认方法stream()生成流, default Stream stream()
(2)Map 体系集合
把Map转成Set集合,间接的生成流
(3)数组
通过Stream接口的静态方法of(T... values)生成流
3)代码
//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"};
Stream<String> strArrayStream = Stream.of(strArray);
Stream<Integer> intStream = Stream.of(10, 20, 30);
3.Stream流中间操作方法
1)常见方法
4. Stream流终结操作方法
1)概念:终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作
2)
5.Stream流的收集操作
1)概念 对数据使用Stream流的方式操作完毕后,可以把流中的数据收集到集合中
2)
类加载
1.类加载的描述
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成这三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化
2.类的连接
验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
准备阶段:负责为类的类变量分配内存,并设置默认初始化值
解析阶段:将类的二进制数据中的符号引用替换为直接引用
3.类的初始化
在该阶段,主要就是对类变量进行初始化
4.类的初始化步骤
1)假如类还未被加载和连接,则程序先加载并连接该类
2)假如该类的直接父类还未被初始化,则先初始化其直接父类
3)假如类中有初始化语句,则系统依次执行这些初始化语句
注意:在执行第 2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
4.类的初始化时机
1)创建类的实例
2)调用类的类方法
3)访问类或者接口的类变量,或者为该类变量赋值
4)使用反射方式来强制创建某个类或接口对应的 java.lang.Class对象
5)初始化某个类的子类
6)直接使用 java.exe命令来运行某个主类
5.类加载器的作用
负责将 .class文件加载到内存中,并为之生成对应的 java.lang.Class 对象
反射
1.反射的概述
是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展。
2.获取Class类对象的三种方式
1)类名 .class属性
2)对象名 .getClass()方法
3)Class.forName( 全类名)方法
//使用类的class属性来获取该类对应的Class对象
Class<Student> c1 = Student.class;
System.out.println(c1);
Class<Student> c2 = Student.class;
System.out.println(c1 == c2);//true
System.out.println("--------");
//调用对象的getClass()方法,返回该对象所属类对应的Class对象
Student s = new Student();
Class<? extends Student> c3 = s.getClass();
System.out.println(c1 == c3);
System.out.println("--------");
//使用Class类中的静态方法forName(String className)
Class<?> c4 = Class.forName("com.itheima_02.Student");
System.out.println(c1 == c4);
3.反射获取构造方法并使用
1)Class类获取构造方法对象的方法
4.Constructor类用于创建对象的方法
T newInstance(Object...initargs) 根据指定的构造方法创建对象
5.反射获取成员变量并使用
1)Class类获取成员变量对象的方法
模块化
1.模块的基本使用
1)创建模块(按以前讲解的方式创建模块,包,类,定义方法)
在项目中创建两个模块。一个是myOne,一个是myTwo
2)在myOne模块中src目录下,创建module-info.java(描述性文件),该文件专门定义模块名,访问权限,模块依赖等信息,描述性文件中使用模块导出和模块依赖来进行配置并使用
3)
2.模块服务的使用