JAVA8-新特性

JAVA作为常年占据编程语言排行第一的语言,在大数据时代,JAVA是否还有其一席之地?俗话说一个编程语言流不流行就看它的更新速度快不快,JAVA作为最流行的编程语言之一自然更新速度也很快,尤其是sun公司被Oracle收购后,JAVA语言迎来了一个新的时代,相信JAVA在甲骨文的领导下会越走越远。JDK8作为被Oracle公司收购后推出的第一个JAVA版本,自然有着非常重要的意义。

最近学了些JDK8的新特性,所以写篇博客来记录自己的心得,有理解不对的地方希望各位能够指出。

我认为JDK8与JDK7差异最大的有三个地方,下面我一个个说明。

1.接口给中的默认方法和静态方法

我们知道在JDK8以前的版本,在接口中是不允许有实现方法的,这样不好的地方就是使得接口太僵化。如果我们需要向一个很顶层的接口添加一个很好的方法,那么我们就需要对实现该接口的所有子类进行修改。这样就很不方便。所以在JDK8中,Oracle公司为我们接口开放了一个默认方法。默认方法就是接口也能实现,如果接口的子类有必要可以去覆盖,如果没必要,该接口的子类就可以使用接口的默认方法,这东西有点类似于抽象类。

默认方法就像一个普通Java方法,只是方法用default关键字修饰。

在使用默认方法的时候需要注意的点:

  1. 选择父类中的方法。如果一个父类提供了具体的实现方法,那么接口中具有相同名称和参数的默认方法会被忽略。
  2. 接口冲突。如果一个父接口提供了一个默认方法,而另一个接口也提供了具有相同名称和参数类型的方法(不管该方法是否是默认方法),那么必须通过覆盖方法来解决。
  3. 记住一个原则,就是“类优先”,即当类和接口都有一个同名方法时,只有父类中的方法会起作用。“类优先”原则可以保证与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的使用流程有三步:

  1. 创建一个Stream。
  2. 在一个或多个步骤中,将初始Stream转化到另一个Stream的【中间操作】。中间操作方法有:map、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered
  3. 使用一个【终止操作】来产生一个结果。该操作会强制他之前的延迟操作立即执行。在这之后,该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();
	}

}

 

 

猜你喜欢

转载自blog.csdn.net/qq_38166944/article/details/82557418