JAVA作为常年占据编程语言排行第一的语言,在大数据时代,JAVA是否还有其一席之地?俗话说一个编程语言流不流行就看它的更新速度快不快,JAVA作为最流行的编程语言之一自然更新速度也很快,尤其是sun公司被Oracle收购后,JAVA语言迎来了一个新的时代,相信JAVA在甲骨文的领导下会越走越远。JDK8作为被Oracle公司收购后推出的第一个JAVA版本,自然有着非常重要的意义。
最近学了些JDK8的新特性,所以写篇博客来记录自己的心得,有理解不对的地方希望各位能够指出。
我认为JDK8与JDK7差异最大的有三个地方,下面我一个个说明。
1.接口给中的默认方法和静态方法
我们知道在JDK8以前的版本,在接口中是不允许有实现方法的,这样不好的地方就是使得接口太僵化。如果我们需要向一个很顶层的接口添加一个很好的方法,那么我们就需要对实现该接口的所有子类进行修改。这样就很不方便。所以在JDK8中,Oracle公司为我们接口开放了一个默认方法。默认方法就是接口也能实现,如果接口的子类有必要可以去覆盖,如果没必要,该接口的子类就可以使用接口的默认方法,这东西有点类似于抽象类。
默认方法就像一个普通Java方法,只是方法用default关键字修饰。
在使用默认方法的时候需要注意的点:
- 选择父类中的方法。如果一个父类提供了具体的实现方法,那么接口中具有相同名称和参数的默认方法会被忽略。
- 接口冲突。如果一个父接口提供了一个默认方法,而另一个接口也提供了具有相同名称和参数类型的方法(不管该方法是否是默认方法),那么必须通过覆盖方法来解决。
- 记住一个原则,就是“类优先”,即当类和接口都有一个同名方法时,只有父类中的方法会起作用。“类优先”原则可以保证与Java 7的兼容性。如果你再接口中添加了一个默认方法,它对Java 8以前编写的代码不会产生任何影响。
2.函数式接口和Lambda表达式
函数式接口(Functional Interface)是只包含一个抽象方法的接口。
比如Java标准库中的java.lang.Runnable,java.util.Comparator<T>就是典型的函数式接口。
在Java 8中通过@FunctionalInterface注解,将一个接口标注为函数式接口,该接口只能包含一个抽象方法。
@FunctionalInterface注解不是必须的,只要接口只包含一个抽象方法,虚拟机会自动判断该接口为函数式接口。
一般建议在接口上使用@FunctionalInterface注解进行声明,以免他人错误地往接口中添加新方法,如果在你的接口中定义了第二个抽象方法的话,编译器会报错。
函数式接口是为Java 8中的lambda表达式而设计的,lambda表达式的方法体其实就是函数接口的实现。其语法如下:
(parameters) -> expression 或者 (parameters) -> {statements;}
其中(parameters)是参数,expression是表达式,{statements;}是代码块,你问函数名去哪了,因为只有一个函数要被实现,所以就可以省略不写。
括号里的参数可以省略其类型,编译器会根据上下文来推导参数的类型,你也可以显式地指定参数类型,如果没有参数,括号内可以为空。
方法体,如果有多行功能语句用大括号括起来,如果只有一行功能语句则可以省略大括号。
下面是我写的一个测试例子
package lambda;
import java.text.Collator;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class SortCollection {
private static List<String> list = Arrays.asList("d","c","a","f","z","k");
public static void sortString(){
/*Collections.sort(list,new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return Collator.getInstance().compare(o1, o2);
}
});*/
Collections.sort(list, (o1, o2)->Collator.getInstance().compare(o1, o2));
System.out.println(list);
}
public static void main(String[] args) {
sortString();
}
}
3.Stream API
Stream作为JDK8的一大亮点,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作,或者大批量数据操作 。
Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行(Stream)和并行(parallelStream)两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势。
上文提到的聚合操作,即类似于关系数据库中的分组聚合函数,如求平均,求最大,求总数等。。另外大批量数据操作,即字面意义上的大数据量(百万级甚至千万级)这是奠定JAVA在大数据时代不会落寞很重要的一个能力。
参考文献 文献:https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/
Stream的使用流程有三步:
- 创建一个Stream。
- 在一个或多个步骤中,将初始Stream转化到另一个Stream的【中间操作】。中间操作方法有:map、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered
- 使用一个【终止操作】来产生一个结果。该操作会强制他之前的延迟操作立即执行。在这之后,该Stream就不会在被使用了。终止操作方法有:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
注意:要在Stream中调用方法可以采用lambda表达式“方法引用”的方式,如下:
使用“::”操作符将方法名和对象或类的名字分隔开来。以下是四种使用情况:
对象::实例方法
类::静态方法
类::实例方法
类::new
下面是我用Steam对集合操作的几个Demo
package stream;
import java.util.Arrays;
public class Stream01 {
public static String[] strArr = {"java8","New","Feature","Stream","API"};
public static void oldForEach(){
for(String str:strArr){
System.out.println(str);
}
}
public static void newForEach(){
//中间操作,终结操作 这里只需要使用终结操作 .stream()获取到流).forEach调用forEach方法
//str 相当于原来forEach的str
//Arrays.asList(strArr).stream().forEach(str->System.out.println(str));
//还可以更简便 ::专门在lambda表达式中用来访问方法 用System调用out静态属性,然后调用println方法。
Arrays.asList(strArr).stream().forEach(System.out::println);
}
public static void main(String[] args) {
//oldForEach();
newForEach();
}
/*例如:调用自己写的方法 可以这样调用方Arrays.asList(strArr).stream().forEach(Stream01::myPrintln);
* 这就是lambda表达式的规范 再 forEach中只可以这样调用方法
*/
public static void myPrintln(String str){
System.out.println(str);
}
}
package stream;
import java.text.Collator;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Stream02 {
public static void createStream(){
//构造stream方式
Stream stream = Stream.of("a","b","c");
//数组构造Stream
String[] strArr = {"a","b","c"};
stream = Stream.of(strArr); // 等价 stream = Arrays.stream(strArr)
//最主流的 集合构造出stream
List<String> list = Arrays.asList(strArr);
stream = list.stream();
}
//中间操作 map 1:1转化,将 input stream按照map中的规则转换output stream
//终结操作collect 能将Stream的结果以指定的格式返回
public static void streamMiddleMap(){
List<String> wordList = Arrays.asList("one","two","three","four","five") ;
List<String> output = wordList.stream().map(String::toUpperCase)
.collect(Collectors.toList());//collect(Collectors.toList()) 终结操作 转化回List
output.stream().forEach(System.out::println);
}
//中间操作 filter 对输入的Stream添加过滤条件,只有符合条件的集合内容才能被输出
//终结操作forEach
public static void streamMiddleFilter(){
String[] strArr = {"java8","New","Feature","Stream","API"};//找出字符串长度为3的内容
Stream.of(strArr).filter(str->str.length()==3).
forEach(System.out::println);
}
//中间操作 sorted,对stream进行排序
//终结操作forEach
public static void streamMiddleSorted(){
String[] strArr = {"a","c","d","e"};
Stream.of(strArr)
.sorted((String o1,String o2) -> Collator.getInstance().compare(o1, o2))
.forEach(System.out::print);
}
public static void main(String[] args) {
streamMiddleSorted();
}
}
下边是结合JDBC的一个练习
1.JDBCUtil工具类
package utils;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
* JDBC高级工具类 --- 高级在查询 通过反射查询
* @author Administrator
*
*/
public class JDBCUtil {
/**
* 数据库URL
*/
public static final String URL = "jdbc:mysql://localhost:3306/jdbc";
public static final String USERNAME = "root";
public static final String PASSWORD = "123456";
public static final String DRIVER = "com.mysql.jdbc.Driver";
/**
* 构造方法私有化
*/
private JDBCUtil(){
}
/**
* 获取链接
* @return Connection
*/
public static Connection getConnection(){
try {
//1.加载驱动
Class.forName(DRIVER);
return DriverManager.getConnection(URL, USERNAME, PASSWORD);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
/**
* 关闭资源
* @param con
* @param st
* @param rs
*/
public static void close(Connection con ,Statement st ,ResultSet rs){
try{
try{
if(rs != null){
rs.close();
}
}finally{
try{
if(st != null){
st.close();
}
}finally{
if(con != null){
con.close();
}
}
}
}catch (Exception e) {
e.printStackTrace();
}
}
/**
* ctrl+o 快速查找方法
* ctrl+l 快速定位行
* ctrl+shift+r 快速查找文件
* 新增,修改,删除方法
* @param sql
* @param args
* @return
*/
public static int update(String sql,Object ... args){
//1.加载驱动
//2.获取链接
Connection con = null;
PreparedStatement ps = null;
//3.创建Statement对象
try {
con = getConnection();
ps = con.prepareStatement(sql);
//设置参数
if(args != null){
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1, args[i]);
}
}
//4.执行SQL语句
int i = ps.executeUpdate();
//如果增删改成功,返回受影响的函数
return i;
} catch (SQLException e) {
e.printStackTrace();
}finally{
close(con, ps, null);
}
return 0;
}
/**
* 查询,返回一个集合,实体类中的属性名字必须和表中字段名一样
* T 是类原型,传入的类原型是什么 T就是什么 例如 如果传入的是Student.class T 就是Student的类原型
* @param sql
* @param clz 返回结果的泛型
* @param args sql参数
* @return
*/
public static <T> List<T> query(String sql,Class<T> clz ,Object ... args){
Connection connection =null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
connection = getConnection();
ps = connection.prepareStatement(sql);
if(args != null){
for (int i = 0; i < args.length; i++) {
//设置条件
ps.setObject(i+1, args[i]);
}
}
rs = ps.executeQuery();
List<T> list = new ArrayList<T>();
ResultSetMetaData rsmd = rs.getMetaData(); //获得数据库中所有字段的对象的集合
int count = rsmd.getColumnCount();//获得总共多少个字段(列)
while(rs.next()){ //遍历每一行数据
//根据类创建对象
T obj = clz.newInstance(); // T->传入的类 通过类原型实例化出一个对象
//User: id,name,password
for (int i = 0; i < count; i++) { //遍历列(字段)
//获取数据库中字段名
String columnName = rsmd.getColumnLabel(i+1);//获得列名
//获取类所有已定义的属性
Field [] fields = clz.getDeclaredFields();//获得属性名数组
for (int j = 0; j < fields.length; j++) { //遍历属性,比较列名和属性名,依次来用对应列中单元格的数据来给属性赋值
//获取单个字段对象
Field field = fields[j];
//获取属性名字
String fieldName = field.getName();//获得单个属性名
if(fieldName.equalsIgnoreCase(columnName)){//属性名和列名做不分大小写比对
//获取set方法名 name--方法名: setName 构造出一个set方法
String methodName = "set"+(fieldName.charAt(0)+"").toUpperCase()+fieldName.substring(1, fieldName.length());
Method method = clz.getMethod(methodName, field.getType());
Object value = rs.getObject(columnName);
//oralce需要判断value类型
if(value != null){
//最后相当于调用了一个setId方法
method.invoke(obj, value);
}
}
}
}
list.add(obj);
}
return list;
} catch (Exception e) {
e.printStackTrace();
}finally{
close(connection, ps, rs);
}
return null;
}
/**
*
* 查询,一个对象 就是用query方式,然后把第一个元素拿出来
* @param sql
* @param clz 返回结果的泛型
* @param args sql参数
* @return
*/
public static <T> T queryForObject(String sql,Class<T> clz,Object ...args){
List<T> list = query(sql, clz, args);
if(list != null && list.size()>0){
return list.get(0);
}
return null;
}
/**
* 查询,返回集合,使用map表示对象
*
* map.put("name","水淋");
* map.put("sex","女");
* 用MAP表示对象,key就是对象的属性名,value是对象属性值
* @param sql
* @param args
* @return
*/
public static List<Map<String, Object>> queryForList(String sql,Object ... args){
Connection connection = null;
PreparedStatement ps = null;
ResultSet rs = null;
try{
connection = getConnection();
ps = connection.prepareStatement(sql);
if(args != null){
for (int i = 0; i < args.length; i++) {
ps.setObject(i+1, args[i]);
}
}
rs = ps.executeQuery();
List<Map<String, Object>> list = new ArrayList<Map<String,Object>>();
//获取结果集元信息(字段名字,字段类型,一共有多少字段)
ResultSetMetaData rsmd = rs.getMetaData();
//统计结果集中的字段个数
int count = rsmd.getColumnCount();
while(rs.next()){
//一个map表示一行数据或一个对象
Map<String, Object> map = new HashMap<String, Object>();
for (int i = 0; i < count; i++) {
String columnName = rsmd.getColumnLabel(i+1);
Object columnValue = rs.getObject(columnName);
map.put(columnName, columnValue);
}
list.add(map);
}
return list;
}catch (Exception e) {
e.printStackTrace();
}finally{
close(connection, ps, rs);
}
return null;
}
/**
* 查询,返回一个map
* @param sql
* @param args
* @return
*/
public static Map<String, Object> queryForMap(String sql,Object ... args){
List<Map<String, Object>> list = queryForList(sql, args);
if(list != null && list.size()>0){
return list.get(0);
}
return null;
}
}
2.两个测试用的实体
package entity;
public class Student {
private int id;
private String name;
private String sex;
private int birth;
private String department;
private String address;
//下面省略Get/Set方法,有参无参构造方法,toString方法
}
package entity;
public class Score {
private int id;
private int stu_id;
private String c_name;
private int grade;
//下面省略Get/Set方法,有参无参构造方法,toString方法
}
3.两张数据库表结构
4.测试主程序
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import entity.Score;
import entity.Student;
import utils.JDBCUtil;
public class main {
/* 为了降低难度,将student表中的birth字段类型改成int类型。
* 作业:
* 1、用JDBCUtil查询学生表所有数据,用Lambda表达式实现Comparator接口排序规则是出生年份倒序,用Stream遍历输出结果
* 2、将第一题中的排序改用Stream的sorted中间 操作完成,其余保持不变
* 3、用JDBCUtil工具类查询学生姓名、系别、选修的课程、成绩。用Stream给计算机成绩大于90分的学生添加一个字段remark,内容为“good”。
* 4、用JDBCUtil工具类查询成绩表,用Stream做分组运算: **
* 4.1、按学号统计总成绩
* 4.2、按课程统计平均成绩
* 4.3、按课程统计选修人数
*/
/**
* 第一题
*/
public static void queryAllStudent(){
String sql = "select * from student";
List<Student> stus = JDBCUtil.query(sql, Student.class);
/*Collections.sort(stus, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getBirth() - o2.getBirth();
}
});*/
/*Collections中的sort排序括号中的是lambda表达式 o2-o1代表第二个元素减去第一个元素
* 如果为1则继续递进,把新结点与下一层的结点进行比较;
* 如果为0则该属性值不足以决定两结点位置区别,再定义其他属性的比较算法来进一步比较两对象;
* 如果为-1则新结点优先级大于当前层,往上一层比较;
* */
Collections.sort(stus, (o1,o2) -> o2.getBirth() - o1.getBirth());
stus.stream().forEach(System.out::println);
}
/**
* 第二题
*/
public static void queryAllStudentByStream(){
String sql = "select * from student";
List<Student> stus = JDBCUtil.query(sql, Student.class);
stus.stream().sorted((o1,o2)->o2.getBirth() - o1.getBirth()).forEach(System.out::println);
}
// * 3、用JDBCUtil工具类查询学生姓名、系别、选修的课程、成绩。用Stream给计算机成绩大于90分的学生添加一个字段remark,内容为“good”。
/**
* 第三题
*/
public static void query3(){
String sql = "select student.`name` name ,student.department,score.c_name,score.grade from student INNER JOIN score ON student.id = score.stu_id";
List<Map<String,Object>> list = JDBCUtil.queryForList(sql);
list.stream()
.filter(a -> a.get("c_name").equals("计算机"))
.filter(a->(int)a.get("grade")>90)
.map(a->{a.put("remark", "good");return a;})
.forEach(System.out::println);
//.collect(Collectors.toList());//使用collect操作返回指定格式,使用Collectors的toList方法将查出计算机成绩大于90的同学返回成一个list
/*.map((a)->a.put("remark", "good"))
//.collect(Collections.h)
.forEach(System.out::println);*/
/*for (Map<String, Object> map : stu) {
map.put("remark", "good");
}
System.out.println(stu);*/
}
/*4、用JDBCUtil工具类查询成绩表,用Stream做分组运算: **
* 4.1、按学号统计总成绩
* 4.2、按课程统计平均成绩
* 4.3、按课程统计选修人数*/
public static void query4(){
String sql = "select * from score";
List<Score> sc = JDBCUtil.query(sql, Score.class);
//通过collect终结方法获得一个map,使用Collectors.groupingBy对stu_id进行分组
Map<Integer, Integer> map1 = sc.stream().collect(Collectors.groupingBy(Score::getStu_id,Collectors.summingInt(Score::getGrade)));
Set<Integer> keys = map1.keySet();
System.out.println("===================按学号统计总成绩======================");
for (Integer key : keys) {
System.out.println(key+":"+map1.get(key));
}
System.out.println("===================按课程统计平均成绩======================");
Map<String, Double> map2 = sc.stream().collect(Collectors.groupingBy(Score::getC_name,Collectors.averagingDouble(Score::getGrade)));
Set<String> keys2 = map2.keySet();
for (String key : keys2) {
System.out.println(key+":"+map2.get(key));
}
System.out.println("===================按课程统计选修人数======================");
Map<String, Long> map3 = sc.stream().collect(Collectors.groupingBy(Score::getC_name,Collectors.counting()));
Set<String> keys3 = map3.keySet();
for (String key : keys3) {
System.out.println(key+":"+map3.get(key));
}
}
public static void main(String[] args) {
//queryAllStudent();
//queryAllStudentByStream();
//query3();
query4();
}
}