目录
Collectors.groupingBy() 分组之统计分组总值
反射
其实就是动态加载一个指定的类,并获取该类中的所有的内容。而且将字节码文件封装成对象,并将字节码文件中的内容都封装成对象,这样便于操作这些成员。简单说:反射技术可以对一个类进行解刨。
反射的好处:大大增强了程序的扩展性。
反射的基本步骤:
- 获取Class对象,就是获取到指定的名称的字节码文件对象。
- 实例化对象,获得类的属性、方法、构造函数。
- 访问属性、调用方法、调用构造函数创建对象。
获取Class实例的三种方式
- 实例化对象调用getClass()方法。弊端:必须要创建该类对象,才能调用getClass方法。
-
每一个数据类型(基本数据类型和引用数据类型)都有一个静态的属性class。弊端:必须要先明确该类。前两种方式不利于程序的扩展,因为都需要在程序使用具体的类来完成。
-
使用Class类的静态方法forName(),用类的名字获取一个Class实例。
// 1. 根据给定的类名来获得 用于类加载
String classname = "cn.itcast.reflect.Person";// 来自配置文件
Class clazz = Class.forName(classname);// 此对象代表Person.class
// 2. 如果拿到了对象,不知道是什么类型 用于获得对象的类型
Object obj = new Person();
Class clazz1 = obj.getClass();// 获得对象具体的类型
// 3. 如果是明确地获得某个类的Class对象 主要用于传参
Class clazz2 = Person.class;
通过反射获取类属性、方法、构造器
例:Field[] fields = Apple.class.getFields();
方法 | 返回值 | 说明 |
getFields | Field[] | 返回一个类中所有可访问的公共字段 |
getDeclaredFields | Field[] | 返回一个类中全部字段,但只包括该类字段 |
getField("xxx") | field | 根据字段名返回一个公开字段 |
getDeclaredField("xxx") | field | 根据字段名返回一个字段 |
getDeclaredMethods() | Method[] | Methodh获取方式 |
getDeclaredConstructors() | Constructor[] | 获取构造方法 |
field.setAccessible(true) | 可更改属性的访问权限 |
JDK8新特性
lambda表达式
最直接作用就是减少代码,显得非常简洁。
// java7中 筛选产品为nike的
public List<Product> filterProductByColor(List<Product> list){
List<Product> prods = new ArrayList<>();
for (Product product : list){
if ("nike".equals(product.getName())){
prods.add(product);
}
}
return prods;
}
// 使用 lambda
public List<Product> filterProductByPrice(List<Product> list){
return list.stream().filter(p->"nike".equals(p.getName())).collect(Collectors.toList());
}
函数式接口
位于java.util.functaion包下,下面介绍最常用的几个
Predicate
接收一个值返回boolean Predicate p = t->true;
运用详情: https://www.jianshu.com/p/b38ff80e3039
Supplier
无接受参数返回一个值 Supplier<T> s = () -> new T();
运用详情: https://www.jianshu.com/p/1386445719bc
Consumer
接受一个参数无返回值 Consumer<String> c = c -> System.out.println(s);
运用详情: Java 常用函数式接口之Consumer接口 - LeeHua - 博客园
Function<T,R> 接受参数T 返回参数R Function<Long,String> f = c -> String.valueof(c);
其他还有一些 BiFunction,BiConsumer,DoubleSupplier等大家有兴趣自己去阅读下源码
方法引用
静态引用:格式 Class::static_method
List<String> list = Arrays.asList("a","b","c");
list.forEach(str -> System.out.print(str));
list.forEach(System.out::print);
构造器调用 格式:Class::new
List<String> list = Arrays.asList("a","b","c");
List<Test> list.stream().map(Test::new).collect(Collectors.toList());
public class Test{
private final String desc;
public Test(String desc){
this.desc=desc;
}
}
方法调用 格式:instance::method
List<String> list = Arrays.asList("a","b","c");
Test test = new Test();
List<String> list.stream().map(test::toAdd).collect(Collectors.toList());
public class Test{
private final String desc;
public Test(String desc){
this.desc=desc;
}
public String toAdd(String desc){
return desc+"add";
}
}
StreamAPI
// 使用jdk1.8中的Stream API进行集合的操作
@Test
public void test(){
// 循环过滤元素
proList.stream()
.fliter((p) -> "红色".equals(p.getColor()))
.forEach(System.out::println);
// map处理元素然后再循环遍历
proList.stream()
.map(Product::getName)
.forEach(System.out::println);
// map处理元素转换成一个List
proList.stream()
.map(Product::getName)
.collect(Collectors.toList());
}
Collection下的Stream()串行流和parallelStream()并行流
//Arrays 中的 stream() 方法,将数组转成流
Integer[] nums = new Integer[10];
Stream<Integer> stream = Arrays.stream(nums);
//Stream中的静态方法:of()、iterate()、generate()
Stream<Integer> stream = Stream.of(1,2,3,4,5,6);
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 2).limit(6);
stream2.forEach(System.out::println); // 0 2 4 6 8 10
Stream<Double> stream3 = Stream.generate(Math::random).limit(2);
stream3.forEach(System.out::println);
//使用 Pattern.splitAsStream() 方法,将字符串分隔成流
Pattern pattern = Pattern.compile(",");
Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
stringStream.forEach(System.out::println);
流的中间操作
Filter过滤流中某些元素
Limit、skip、distinct、sorted都是有状态操作,这些操作只有拿到前面处理后的所有元素之后才能继续下去。
Limit(n):获取前n个元素
Skip(n):跳过n个元素,配合limit(n)可实现分页
Distinct:通过流中元素的hashCode()和equals()去除重复元素
Stream<Integer> stream = Stream.of(6, 4, 6, 7, 3, 9, 8, 10, 12, 14, 14);
Stream<Integer> newStream = stream.filter(s -> s > 5) //6 6 7 9 8 10 12 14 14
.distinct() //6 7 9 8 10 12 14
.skip(2) //9 8 10 12 14
.limit(2); //9 8
newStream.forEach(System.out::println);
映射
Map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
FlatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
List<String> list = Arrays.asList("a,b,c", "1,2,3");
//去掉字符串中所有的,
List<String> collect = list.stream().map(s -> s.replaceAll(",", "")).collect(Collectors.toList());
System.out.println(collect);
// flatMap 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
Stream<String> stringStream = list.stream().flatMap(s -> {
//将字符串以,分割后得到一个字符串数组
String[] split = s.split(",");
//然后将每个字符串数组对应流返回,flatMap会自动把返回的所有流连接成一个流
Stream<String> stream = Arrays.stream(split);
return stream;
});
System.out.println(stringStream.collect(Collectors.toList()));
排序
Sorted():自然排序,流中元素实现Comparable接口
Sorted(Comparator compatator)定制排序,自定义Comparator排序器
流的终止操作
匹配
allmatch,noneMatch,anyMatch用于对集合中对象的某一个属性值进行判断,
allMatch全部符合该条件返回true,
noneMatch全部不符合该断言返回true
anyMatch 任意一个元素符合该断言返回true
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
boolean allMatch = list.stream().allMatch(e -> e > 10); //false
boolean noneMatch = list.stream().noneMatch(e -> e > 10); //true
boolean anyMatch = list.stream().anyMatch(e -> e > 4); //true
聚合
findFirst:返回流中第一个元素
findAny:返回流中的任意元素
count:返回流中元素的总个数
max:返回流中元素最大值
min:返回流中元素最小值
Java Steam详解_doubleStrongWu的博客-CSDN博客_java steam
Collectors
Collectors 类实现了很多归约操作,例如将流转换成集合和聚合元素。Collectors 可用于返回列表或字符串:
List<String>strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选列表: " + filtered);
String mergedString = strings.stream().
filter(string -> !string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " + mergedString);
Collectors.groupingBy() 分组
/**
* 使用java8 stream groupingBy操作,按城市分组list
*/
public void groupingByCity() {
Map<String,List<Employee>>map=employees.stream().
collect(Collectors.groupingBy(Employee::getCity));
map.forEach((k, v) -> {
System.out.println(k + " = " + v);
});
}
Collectors.groupingBy() 分组之统计分组总值
Map<String, Long> map = employees.stream()
.collect(Collectors.groupingBy(Employee::getCity, Collectors.summingLong(Employee::getAmount)));
Java8 Stream 之groupingBy 分组讲解_在奋斗的大道的博客-CSDN博客_groupingby
接口中的默认方法和静态方法
public interface ProtocolAdaptor {
ProtocolAdaptor INSTANCE = DynamicLoader.findFirst(ProtocolAdaptor.class).orElse(null);
default ProtocolAdaptor proxy() {
return (ProtocolAdaptor)
Proxy.newProxyInstance(ProtocolAdaptor.class.getClassLoader(),
new Class[]{ProtocolAdaptor.class},
(proxy, method, args) -> intercept(method, args));
}
}
Optional
用于处理对象空指针异常,Optrional是一个对象容器,具有以下两个特点:提示用户要注意该对象有可能为null;简化if else代码
1.创建
Optional.empty(): 创建一个空的 Optional 实例
Optional.of(T t):创建一个 Optional 实例,当 t为null时抛出异常
Optional.ofNullable(T t):创建一个 Optional 实例,但当 t为null时不会抛出异常,而是返回一个空的实例
2. 获取:
get():获取optional实例中的对象,当optional 容器为空时报错
3. 判断:
isPresent():判断optional是否为空,如果空则返回false,否则返回true
ifPresent(Consumer c):如果optional不为空,则将optional中的对象传给Comsumer函数
orElse(T other):如果optional不为空,则返回optional中的对象;如果为null,则返回 other 这个默认值
orElseGet(Supplier<T> other):如果optional不为空,则返回optional中的对象;如果为null,则使用Supplier函数生成默认值other
orElseThrow(Supplier<X> exception):如果optional不为空,则返回optional中的对象;如果为null,则抛出Supplier函数生成的异常
4. 过滤:
filter(Predicate<T> p):如果optional不为空,则执行断言函数p,如果p的结果为true,则返回原本的optional,否则返回空的optional
5. 映射:
map(Function<T, U> mapper):如果optional不为空,则将optional中的对象 t 映射成另外一个对象 u,并将 u 存放到一个新的optional容器中。
flatMap(Function< T,Optional<U>> mapper):跟上面一样,在optional不为空的情况下,将对象t映射成另外一个optional
区别:map会自动将u放到optional中,而flatMap则需要手动给u创建一个optional
例子:
日期使用
于原来老旧的日期API一直被人诟病,比如java.util.Date,java.util.Calendar等,并且原来所有的日期类都是可变且线程不安全的,导致许多人要么自己手动封装,要么转去使用Joda Time等这类优秀的第三方工具包。所以,在JDK1.8中,JDK官方在Joda Time等优秀工具包基础上,重新提供了一份相当不错的日期API。并且,在JDK1.8中,java.time包中的类是不可变且线程安全的
- java.time包:JDK8中的基础包,所有常用的基础类都是这个包的一部分,如
LocalDate
,LocalTime
,LocalDateTime
等等,所有这些类都是不可变且线程安全的; - java.time.chrono包:这个包为非ISO的日历系统定义了一些API,我们可以在借助这个包中的一些类扩展我们自己的日历系统;
- java.time.format包:这个包很明显了,格式化和解析日期时间对象,一般java.time包中的类都差不多能满足我们的需求了,如果有需要,可以调用这个包下的类自定义解析方式;
- java.time.temporal包:这个包很有意思,封装了一些获取某个特定日期和时间的接口,比如某月的第一天或最后一天,并且这些方法都是属于特别好认的方法。
- java.time.zone包:这个包就是时区相关的类了。
LocalDate
java.time.LocalDate这个类,是用来表示日期的,也仅包含日期
public static void testDate() {
// 1. 获取当前日期(年月日) -----打印输出-----2018-01-29
LocalDate localDate = LocalDate.now();
System.out.println(localDate.toString());
// 2. 根据年月日构建Date ----打印输出-----2018-01-30
LocalDate localDate1 = LocalDate.of(2018, 01, 30);
// 3. 字符串转换日期,默认按照yyyy-MM-dd格式,也可以自定义格式 -----打印输出-----2018-01-30
LocalDate localDate2 = LocalDate.parse("2018-01-30");
// 4. 获取本月第一天 -----打印输出-----2018-01-01
LocalDate firstDayOfMonth = localDate.with(TemporalAdjusters.firstDayOfMonth());
// 5. 获取本月第二天 -----打印输出-----2018-01-02
LocalDate secondDayOfMonth = localDate.withDayOfMonth(2);
// 6. 获取本月最后一天 -----打印输出-----2018-01-31
LocalDate lastDayOfMonth = localDate.with(TemporalAdjusters.lastDayOfMonth());
// 7. 明天 -----打印输出----- 2018-01-30
LocalDate tomorrowDay = localDate.plusDays(1L);
// 8. 昨天 -----打印输出----- 2018-01-28
LocalDate yesterday = localDate.minusDays(1L);
// 9. 获取本年第12天 -----打印输出----- 2018-04-30
LocalDate day = localDate.withDayOfYear(120);
// 10. 计算两个日期间的天数
long days = localDate.until(localDate1, ChronoUnit.DAYS);
System.out.println(days);
// 11. 计算两个日期间的周数
long weeks = localDate.until(localDate1, ChronoUnit.WEEKS);
System.out.println(weeks);
}
日期计算
名称 | 作用 |
plusDays | 往后加n天,即获取n天后的日期 |
plusWeeks | 往后加n周,即获取n周后的日期 |
plusMonths | 往后加n月,即获取n月后的日期 |
plusYears | 往后加n年,即获取n年后的日期 |
minusDays | 往后减n天,即获取n天前的日期 |
... | minus也有减周、月、年相关的API这里就不写了 |
now | 获取当前日期 |
日期比较
名称 | 作用 |
isBefore | 判断当前日期对象是否在比较的日期对象之前 |
isAfter | 判断当前日期对象是否在比较的日期对象之后 |
isEqual | 判断当前日期对象是否与比较的日期对象(日期)相等 |
日期和字符串转化
LocalDate提供了丰富的API来满足我们对于不同日期格式的需求,最主要的核心方法就是format()和parse(),前者让我们可以实现日期的格式化,后者可以将字符串转为LocalDate对象。
1、日期的格式化(LocalDate对象转字符串)
@Test
public void LocalDateTest(){
//获取当前的日期
LocalDate date1 = LocalDate.now();
String formatDate = date1.format(DateTimeFormatter.ofPattern("MM dd / yyyy"));
System.out.println("格式化后的日期为:" + formatDate);
}
2.字符串转日期
我们一般使用parse()方法来进行字符串转日期的操作,需要注意的是parse()方法默认是按照yyyy-MM-dd的格式来将字符串转换为日期对象的,如果给定的字符串不是这个日期格式的,则需要手动指定解析的日期格式,否则程序会抛异常!!
@Test
public void LocalDateTest(){
// 默认转换的日期格式为 yyyy-MM-dd
LocalDate date = LocalDate.parse("2022-04-03");
System.out.println("date解析后的日期为:" + date);
LocalDatedate2= LocalDate.parse("2022/04/03",DateTimeFormatter.ofPattern("yyyy/MM/dd"));
System.out.println("date2解析后的日期为:" + date2);
}
获取对象上常用的属性
便捷的在日期对象上面获取年月日等属性,方便后续程序的逻辑判断。常用的API如下:
名称 | 作用 |
get(TemporalField field) | 获取指定字段的int值 |
getChronology() | 获取该日期格式,例如:ISO |
getDayOfMonth() | 获取一个月中的第几号,为int类型 |
getDayOfWeek() | 获取星期字段,返回DayOfWeek |
getDayOfYear() | 以 int 格式获取一年中的第几天。它可以返回从 1 到 365 或 366 (闰年)。 |
getEra() | 获取在此日期适用的时代。例如:"CE "从第一年开始,"BCE "从零年开始。 |
LocalDate currentDate = LocalDate.now();
log.info("年份:{}", currentDate.getYear());
log.info("月份:{}", currentDate.getMonth());
//返回月份,从1到12
log.info("年份:{}", currentDate.getMonthValue());
//获取一个月中的第几号,为int类型 from 1 to 31
log.info("年份:{}", currentDate.getDayOfMonth());
log.info(currentDate.getChronology().toString());
log.info("{}", currentDate.get(ChronoField.DAY_OF_YEAR));
INFO [main] (GetDateList.java:47) - 年份:2022
INFO [main] (GetDateList.java:48) - 月份:JUNE
INFO [main] (GetDateList.java:50) - 年份:6
INFO [main] (GetDateList.java:52) - 年份:30
INFO [main] (GetDateList.java:53) - ISO
INFO [main] (GetDateList.java:54) - 181
LocalTime
全是时间,不包含日期
public static void testTime() {
// 1. 获取当前时间,包含毫秒数 -----打印输出----- 21:03:26.315
LocalTime localTime = LocalTime.now();
// 2. 构建时间 -----打印输出----- 12:15:30
LocalTime localTime1 = LocalTime.of(12, 15, 30);
// 3. 获取当前时间,不包含毫秒数 -----打印输出----- 21:01:56
LocalTime localTime2 = localTime.withNano(0);
// 4. 字符串转为时间,还可以有其他格式,比如12:15, 12:15:23.233
// -----打印输出----- 12:15:30
LocalTime localTime3 = LocalTime.parse("12:15:30");
}
LocalDateTime
LocalDateTime就和原先的java.util.Date很像了,既包含日期,又包含时间,它经常和DateTimeFormatter一起使用。
public static void testDateTime() {
// 1. 获取当前年月日 时分秒 -----打印输出----- 2018-01-29T21:23:26.774
LocalDateTime localDateTime = LocalDateTime.now();
// 2. 通过LocalDate和LocalTime构建 ----- 打印输出----- 2018-01-29T21:24:41.738
LocalDateTime localDateTime1 = LocalDateTime.of(LocalDate.now(), LocalTime.now());
// 3. 构建年月日 时分秒 -----打印输出----- 2018-01-29T19:23:13
LocalDateTime localDateTime2 = LocalDateTime.of(2018, 01, 29, 19, 23, 13);
// 4. 格式化当前时间 ----打印输出----- 2018/01/29
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
System.out.println(formatter.format(localDateTime2));
}
使用LocalDateTime的with开头的方法可以设置相应的时间,小时,分钟等,比如:
// 设置分钟数
LocalDateTime localDateTime = LocalDateTime.now().withMinute(23);
TemporalAdjusters
该类是一个计算用的类,提供了各种各样的计算方法。比如某个月的第一天,某个月的最后一天,某一年的第一天,某一年的第几天等各种计算方法。该类内部实现基本上全都是通过JDK8的Lambda表达式来实现的。随便举一些例子,都很简单。
LocalDate localDate = LocalDate.now();
// 1. 本月第一天
LocalDate firstDayOfMonth = localDate.with(TemporalAdjusters.firstDayOfMonth());
// 2. 本月最后一天
LocalDate lastDayOfMonth = localDate.with(TemporalAdjusters.lastDayOfMonth());
// 3. 本年第一天
LocalDate firstDayOfYear = localDate.with(TemporalAdjusters.firstDayOfYear());
// 4. 下个月第一天
LocalDate firstDayOfNextMonth = localDate.with(TemporalAdjusters.firstDayOfNextMonth());
// 5. 本年度最后一天
LocalDate lastDayOfYear = localDate.with(TemporalAdjusters.lastDayOfYear());
System.out.println(firstDayOfMonth);
System.out.println(lastDayOfMonth);
System.out.println(firstDayOfYear);
System.out.println(firstDayOfNextMonth);
System.out.println(lastDayOfYear);
打印输出:
2018-01-01
2018-01-31
2018-01-01
2018-02-01
2018-12-31