JAVASE学习笔记(十三)
Java7新特性 - AutoCloseable
1. 概述
JDK在1.7之后出现了自动关闭类的功能,该功能的出现为各种关闭资源提供了相当大的帮助,这里我们谈一谈自动关闭类。
JDK1.7之后出现了一个重要的接口,以及改造了一个重要的方法结构:
- AutoCloseable自动关闭接口
- try(){}–catch{}–finally{}
相应的 一些资源也实现了该接口,如PreparedStatement、Connection、InputStream、OutputStream等等资源接口。
2. 应用场景
示例
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyLockDemo {
static Lock lock = new ReentrantLock();
public static void main(String[] args) {
try {
lock.lock();
System.out.println("1-----加锁成功!!!");
System.out.println("2-----开始执行业务逻辑");
Thread.sleep(3000);
System.out.println("3-----业务执行完毕");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
System.out.println("4----释放锁资源");
}
}
}
如上,在开发中锁是释放必须要做得事情,所以就把放在finally中来执行。但是在开发中往往很多的开发中,都会忘记释放锁或者忘记把锁的释放放入finally中,就造成死锁现象,这个很危险的操作和行为。
如何解决
- 使用
AutoCloseable
接口覆盖close方法
实现
AutoCloseable
接口,
覆盖close
方法,
把原来要写finally
中释放资源的动作,放入到close方法中去执行,
而这个执行是jvm自己去执行.
在有try(){}–catch{}–finally{}的时候去实现
- 接口的实现类要重写close()方法,
- 将要关闭的资源定义在try()中,这样当程序执行完毕之后,资源将会自动关闭。
- 自定义类如果要进行自动关闭,只需要实现AutoCloseable接口重写close()方法即可,
同时也只有实现了AutoCloseable接口才能将自定义类放入到try()块中,否则编译不能通过
改进
- 定义类MyLock类
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyLock implements AutoCloseable{
Lock lock = new ReentrantLock();
// 加锁
public void lock() {
lock.lock();
}
// 释放锁
public void unlock() {
lock.unlock();
}
@Override
public void close() throws Exception {
unlock();
System.out.println("4----释放锁资源");
}
}
改进
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class MyLockDemo {
public static void main(String[] args) {
try (MyLock lock = new MyLock();){
lock.lock();
System.out.println("1-----加锁成功!!!");
System.out.println("2-----开始执行业务逻辑");
Thread.sleep(3000);
System.out.println("3-----业务执行完毕");
} catch (Exception e) {
e.printStackTrace();
}// finally {
// // 不需要定义了.因为会自动去释放资源
// lock.unlock();
// }
}
}
1-----加锁成功!!!
2-----开始执行业务逻辑
3-----业务执行完毕
4----释放锁资源 -- 这里的执行就是AutoCloseable中的close方法自动执行的
注意
若是有多个类需要上锁释放锁
在try{}中用“;”隔开
释放锁有顺序,先上锁的最后放
Java8新特性 - Stream流
1. 概述
概念:Stream 是Java8 提出的一个新概念,不是输入输出的 Stream
流,而是一种用函数式编程方式在集合类上进行复杂操作的工具。简而言之,是以内部迭代的方式处理集合数据的操作,内部迭代可以将更多的控制权交给集合类。Stream和 Iterator 的功能类似,只是 Iterator 是以外部迭代的形式处理集合数据的操作。
总结:Stream 是Java8 的新特性,它是基于Lambda表达式在集合数组等进行一系列的优化和操作的工具集。解决集合中一些像过滤,排序,分组,聚合,改造等一系列的问题。
在Java8以前,对集合的操作需要写出处理的过程,如在集合中筛选出满足条件的数据,需要一 一遍历集合中的每个元素,再把每个元素逐一判断是否满足条件,最后将满足条件的元素保存返回。而Stream 对集合筛选的操作提供了一种更为便捷的操作,只需将实现函数接口的筛选条件作为参数传递进来,Stream会自行操作并将合适的元素同样以Stream 的方式返回,最后进行接收即可。
- 集合和数组在遍历元素的时候十分冗余,受到函数式编程以及流水线思想的启发,我们可以将常见的操作封装成比较简单的方法,比如遍历的时候直接调用一个方法即可,而不用写冗余的循环程序。这就是Stream流对象的由来。
- Stream是一个接口,有两种方式来进行创建流对象。
一是调用 Stream.接口中的of方法。
二是调用集合或者数组中的strain方法来获取 Stream.流对象 - Stream对象中常用的方法有:遍历元素,筛选元素,跳过元素,截取元素,计数,把流对象拼接,对流对象中的数据元素进行转换。
2. Stream的API
流是javaAPI中的新的成员,它可以让你用声明式的方式处理集合,简单点说,可以看成遍历数据的一个高级点的迭代器,也可以看做一个工厂,数据处理的工厂,当然,流还天然的支持并行操作;也就不用去写复杂的多线程的代码,下面是Stream的接口定义
在jdk1.8的java.util.stream.Stream接口,里面定义流的API的方法,jdk集合Collection.java顶级集合,利用默认方法进行了实现,具体的实现是通过:StreamSupport.java实现的。
Stream是处理集合的一套高级的API的解决方案
public interface Stream<T> extends BaseStream<T, Stream<T>> {
Stream<T> filter(Predicate<? super T> predicate);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);
Stream<T> distinct();
Stream<T> sorted();
Stream<T> sorted(Comparator<? super T> comparator);
Stream<T> peek(Consumer<? super T> action);
Stream<T> limit(long maxSize);
Stream<T> skip(long n);
void forEach(Consumer<? super T> action);
void forEachOrdered(Consumer<? super T> action);
Object[] toArray();
<A> A[] toArray(IntFunction<A[]> generator);
T reduce(T identity, BinaryOperator<T> accumulator);
Optional<T> reduce(BinaryOperator<T> accumulator);
<U> U reduce(U identity, BiFunction<U, ? super T, U> accumulator, BinaryOperator<U> combiner);
<R> R collect(Supplier<R> supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
<R, A> R collect(Collector<? super T, A, R> collector);
Optional<T> min(Comparator<? super T> comparator);
Optional<T> max(Comparator<? super T> comparator);
long count();
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
Optional<T> findFirst();
Optional<T> findAny();
public static <T> Builder<T> builder() {
return new Streams.streamBuilderImpl<>();
}
public static <T> Stream<T> empty() {
return StreamSupport.stream(Spliterators.<T> emptySpliterator(), false);
}
public static <T> Stream<T> of(T t) {
return StreamSupport.stream(new Streams.streamBuilderImpl<>(t), false);
}
@SafeVarargs
@SuppressWarnings("varargs") // Creating a Stream from an array is safe
public static <T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
public static <T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
Objects.requireNonNull(f);
final Iterator<T> iterator = new Iterator<T>() {
@SuppressWarnings("unchecked")
T t = (T) Streams.NONE;
@Override
public boolean hasNext() {
return true;
}
@Override
public T next() {
return t = (t == Streams.NONE) ? seed : f.apply(t);
}
};
return StreamSupport.stream(
Spliterators.spliteratorUnknownSize(iterator, Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
}
public static <T> Stream<T> generate(Supplier<T> s) {
Objects.requireNonNull(s);
return StreamSupport.stream(new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s),
false);
}
public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
Objects.requireNonNull(a);
Objects.requireNonNull(b);
@SuppressWarnings("unchecked")
Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>((Spliterator<T>) a.spliterator(),
(Spliterator<T>) b.spliterator());
Stream<T> Stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
return Stream.onClose(Streams.composedClose(a, b));
}
public interface Builder<T> extends Consumer<T> {
@Override
void accept(T t);
default Builder<T> add(T t) {
accept(t);
return this;
}
Stream<T> build();
}
}
3. Stream的认识
通过上述接口定义,可以看到,抽象方法,有30多个方法,里面还有一些其他的接口;
上述方法可分为三类:
(1)Stream-流的常用创建方法
面试题
stream()
和parallelStream()
两种流化方法的区别
stream流使用步骤
- 流化
- 中间操作
- 终端操作
准备工作
//1.创建集合
List<User> users = new ArrayList<>();
users.add(new User("月色",18,0));
users.add(new User("夜雨",16,1));
users.add(new User("摇光",18,0));
users.add(new User("流苏",19,1));
//2.流化
Stream<User> stream = users.stream();
Stream<User> userStream = users.parallelStream();
List<User> collect = Stream.of(new User("月色", 18, 0), new User("夜雨", 16, 1)).collect(Collectors.toList());
1.1 集合转流:使用Collection下的 stream() 和 parallelStream() 方法
List<String> list = new ArrayList<>();
Stream<String> Stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流
1.2 数组转流:使用Arrays 中的 stream() 方法
Integer[] nums = new Integer[10];
Stream<Integer> Stream = Arrays.stream(nums);
1.3 使用Stream中的静态方法:of()、iterate()、generate()
List<User> collect = Stream.of(new User("月色", 18, 0), new User("夜雨", 16, 1)).collect(Collectors.toList());
Stream.of:把多个对象进行流化处理
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);
1.4 使用 BufferedReader.lines() 方法,将每行内容转成流
BufferedReader reader = new BufferedReader(new FileReader("F:\\test_Stream.txt"));
Stream<String> lineStream = reader.lines();
lineStream.forEach(System.out::println);
1.5 使用 Pattern.splitAsStream() 方法,将字符串分隔成流
Pattern pattern = Pattern.compile(",");
Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
stringStream.forEach(System.out::println);
(2)中间操作
intermediate operation 中间操作:中间操作的结果是刻画、描述了一个Stream,并没有产生一个新集合,这种操作也叫做惰性求值方法。
对应的方法如下:
这是所有Stream中间操作的列表:
过滤()==>filter()//将满足条件的过滤出来
地图==>map()//映射结果,改造数据,有返回值
转换()==>flatMap()
不同()==>distinct()
排序()==>sorted()
窥视()==>peek()//映射结果,改造数据,无返回值
限制()==>limit()
跳跃()==>skip()
叠加()==>reduce()
1. 过滤:filter
语法:
Stream<T> filter(Predicate<? super T> predicate);
写法
userList1.stream().filter(new Predicate<User>() {
@Override
public boolean test(User user) {
System.out.println("11111");
return false;
}
});
userList1.stream().filter(u-> {
return u.getAge() > 16;
});
userList1.filter(u -> u.getAge() > 16)
是过滤,把条件满足的过滤匹配处理,如下
public class StreamDemo01 {
public static void main(String[] args) {
// 1:创建一个集合
List<User> userList1 = new ArrayList<>();
userList1 .add(new User("月色",18,0));
userList1 .add(new User("夜雨",16,1));
userList1 .add(new User("摇光",18,0));
userList1 .add(new User("流苏",19,1));
// 3: 结束操作
List<User> collect = userList1.stream()
.filter(u -> u.getAge() > 16) // 把满足条件的筛选出来
.collect(Collectors.toList());
for (User user : collect) {
System.out.println(user);
}
}
}
2. 映射改造:peek / map
属于:映射 ,它可以改变集合中的对象的属性值
Stream<T> peek(Consumer<? super T> action);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
- peek操作案例:
把集合中年龄大于16岁的用户的年龄+1岁,如下
List<User> collect = userList1.stream() // 1: stream流化操作
.filter(u -> u.getAge() > 16) // // 2:中间操作 filter 把满足条件的筛选出来
.peek(u->u.setAge(u.getAge() + 1))// 3:中间操作 peek 把每个用户的年龄加1岁
.collect(Collectors.toList()); // 4: 结束操作
for (User user : collect) {
System.out.println(user);
}
- map操作案例:
List<User> collect = userList1.stream() // 1: stream流化操作
.filter(u -> u.getAge() > 16) // // 2:中间操作 filter 把满足条件的筛选出来
.map(u -> {
u.setAge(u.getAge() + 1);
return u;
})// 3:中间操作 peek 把每个用户的年龄加1岁
.collect(Collectors.toList()); // 4: 结束操作
peek和map的区别是什么?
- peek和map区别:peek无返回值,map有返回值(可以改变返回值)
- peek你前面集合流化对象返回的是什么就是什么?
map可以把stream返回值进行改变。我可以把集合中的对象改成map在返回。或者我只需要集合中某个一列的值。
public class StreamDemo02 {
public static void main(String[] args) {
// 1:创建一个集合
List<User> userList1 = new ArrayList<>();
userList1 .add(new User("月色",18,0));
userList1 .add(new User("夜雨",16,1));
userList1 .add(new User("摇光",18,0));
userList1 .add(new User("流苏",19,1));
// user--nickname
List<String> nicknames = userList1.stream().map(u -> {
return u.getUsername();
}).collect(Collectors.toList());
for (String nickname : nicknames) {
System.out.println(nickname);
}
// user---map
List<Map<String, Object>> collect = userList1.stream().map(u -> {
Map<String, Object> map = new HashMap<>();
map.put("id", u.getId());
map.put("username", u.getUsername());
map.put("sex", u.getSex());
return map;
}).collect(Collectors.toList());
for (Map<String, Object> stringObjectMap : collect) {
System.out.println(stringObjectMap);
}
// user--->uservo
List<UserVo> collect1 = userList1.stream().map(u -> {
UserVo userVo = new UserVo();
userVo.setId(u.getId());
userVo.setUsername(u.getUsername());
userVo.setSex(u.getSex() == 1 ? "男" : "女");
return userVo;
}).collect(Collectors.toList());
for (UserVo userVo : collect1) {
System.out.println(userVo);
}
}
}
3. 排序:sorted
public class StreamDemo03 {
public static void main(String[] args) {
// 1:创建一个集合
List<User> userList1 = new ArrayList<>();
userList1 .add(new User("月色",18,0));
userList1 .add(new User("夜雨",16,1));
userList1 .add(new User("摇光",18,0));
userList1 .add(new User("流苏",19,1));
// List<User> sortUserList = userList1.stream().sorted(new Comparator<User>() {
// @Override
// public int compare(User o1, User o2) {
// return o2.getAge() - o1.getAge();
// }
// }).collect(Collectors.toList());
// List<User> sortUserList = userList1.stream().sorted((User o1, User o2) ->{
// return o2.getAge() - o1.getAge();
// }).collect(Collectors.toList());
List<User> sortUserList = userList1.stream().sorted((o1, o2) -> o2.getAge() - o1.getAge()).collect(Collectors.toList());
for (User user : sortUserList) {
System.out.println(user);
}
}
}
4. distanct
去重这里有个坑:去重是通过equal
来比较两者值是否相等来实现去重。但是问题来了:
类似
Integer
这种类型源码已经重写了equal
方法,但是像我们自定义的一些类型,对象,它并没有重写equal
方法,所以它依旧在比较地址以至于不能完成去重功能。所以我们要使用distabct
的时候注意是否需要重写equal
- 如果是对象,必须满足eqauls是true ,并且hashcode要相同
public class User {
// 身份id
private Integer id;
// 姓名
private String username;
// 密码
private String password;
// 芳龄
private Integer age;
// 性别 0 女 1 男
private Integer sex;
// 身家
private Double money;
public User(){
}
public User(Integer id, String username, String password, Integer age, Integer sex, Double money) {
this.id = id;
this.username = username;
this.password = password;
this.age = age;
this.sex = sex;
this.money = money;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getSex() {
return sex;
}
public void setSex(Integer sex) {
this.sex = sex;
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", password='" + password + '\'' +
", age=" + age +
", sex=" + sex +
", money=" + money +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id) && Objects.equals(username, user.username) && Objects.equals(password, user.password) && Objects.equals(age, user.age) && Objects.equals(sex, user.sex) && Objects.equals(money, user.money);
}
@Override
public int hashCode() {
return Objects.hash(id, username, password, age, sex, money);
}
}
public class StreamDemo04 {
public static void main(String[] args) {
// 1:创建一个集合
List<User> userList1 = new ArrayList<>();
User user1 = new User(1, "小文", "123456", 16, 1, 20000d);
User user2 = new User(1, "小文", "123456", 16, 1, 20000d);
userList1.add(user1);
userList1.add(user2);
userList1.add(new User(2, "老季", "123456", 22, 1, 100000d));
userList1.add(new User(3, "怪咖", "123456", 13, 1, 89557d));
userList1.add(new User(4, "小六", "123456", 26, 1, 78000d));
userList1.add(new User(5, "小刘", "123456", 46, 1, 58000d));
System.out.println(user1.equals(user2));
List<User> userList = userList1.stream().distinct().collect(Collectors.toList());
for (User user : userList) {
System.out.println(user);
}
// List<Integer> userList1 = new ArrayList<>();
// userList1.add(1);
// userList1.add(2);
// userList1.add(3);
// userList1.add(1);
//
// List<Integer> collect = userList1.stream().distinct().collect(Collectors.toList());
// System.out.println(collect);
}
}
转换()==>flatMap()
叠加()==>reduce()
5. 集合分页:skip / limit
public class StreamDemo03 {
public static void main(String[] args) {
// 1:创建一个集合
List<User> userList1 = new ArrayList<>();
userList1 .add(new User("月色",18,0));
userList1 .add(new User("夜雨",16,1));
userList1 .add(new User("摇光",18,0));
userList1 .add(new User("流苏",19,1));
int pageSize =2;// 每页显示多少条
int pageno = 1; // 当前页
List<User> userpage = pageUser(pageNo,pageSize);
}
public static List<User> pageUser(int pageNo,int pageSize){
int skip = (pageNo - 1) * pageSize;
return userList1.stream().sorted((o1, o2) -> o2.getAge() - o1.getAge())
.skip(skip) // (pageno-1) * pageSize
.limit(pageSize) // pageSize
.collect(Collectors.toList());
//类似分页
for(User user : sortUserList){
system.out.println(user);
}
}
}
匿名内部类作用
中间操作经常用
Lambda
表达式简化代码,使用lambda表达式会派生一个接口类,好处是:
把匿名内部类(接口,抽象类)当做方法的参数传递以后,它可以达到一个效果就是可以把方法中的一部分逻辑抽离到匿名内部类的方法中去处理。
userList1.stream().filter(new Predicate<User>() {
@Override
public boolean test(User user) {
System.out.println("11111");
return false;
}
});
userList1.stream().filter(u-> {
return u.getAge() > 16;
});
userList1.filter(u -> u.getAge() > 16)
(3)终止操作
terminal operation 终止操作:最终会从Stream中得到值。说白了:就是可以直接得到结果
循环()==>foreach()
count():返回流中元总个数素
收集()==>collect()---放
andMatch():接受一个`predicate`函数,只要流中有一个元素符合该断言返回true,否则返回false
noneMatch():接受一个`predicate`函数,当流中每个元素都不符合该断言返回true,否则返回false
allMatch():接受一个`predicate`函数,当流中每个元素都符合该断言返回true,否则返回false
min():返回流中元素最小值
max():返回流中元素最大值
findFirst& findAny
- 两者在stream(串行)的情况下,两者其实都返回的第一个元素
- 如果是parallelStream(并行)情况下findAny就造成随机返回
findAny()返回的元素是不确定的,对于同一个列表多次调用findAny()有可能会返回不同的值。使用findAny()是为了更高效的性能。
如果是数据较少,串行地情况下,一般会返回第一个结果,
如果是并行的情况,那就不能确保是第一个。
总结
steram流一旦调用终止方法就代表流已经操作完毕,不能进行其他操作了
若要重新进行操作
将结果再次转换为流即可
- 如何区分这2种操作
可以根据操作的返回值类型判断:
返回值是Stream
,则该操作是中间操作
返回值是其他值或者为空,则该操作是终止操作。
如下图的前2个操作是中间操作,只有最后一个操作是终止操作。
可以形象地理解Stream的操作是对一组粗糙的工艺品原型(即对应的 Stream
数据源)进行加工成颜色统一的工艺品(即最终得到的结果),第一步筛选出合适的原型(即对应Stream的 filter
的方法),第二步将这些筛选出来的原型工艺品上色(对应Stream的map方法),第三步取下这些上好色的工艺品(即对应Stream的
collect(toLis t())方法)。在取下工艺品之前进行的操作都是中间操作,可以有多个或者0个中间操作,但每个Stream数据源只能有一次终止操作,否则程序会报错
4. 使用场景
场景:查询用户信息的时候为了安全起见,需要把集合中每个用户对象的敏感信息赋值null,在进行返回。
- 版本8之前:新建list
List<User> userList = new ArrayList();
List<User> newList = new ArrayList();
for(User user : userList){
user.setPassword = null;
newList.add(user);
}
-
版本8之后:stream流化
-
peek解决:
List<User> userList = userList1.stream().peek(u -> u.setPassword("")).collect(Collectors.toList());
userList.forEach(System.out::println);
- map解决
List<User> userList2 = userList1.stream().map(u -> {
u.setPassword("");
return u;
}).collect(Collectors.toList());
userList2.forEach(System.out::println);