摘要: 在云服务业务开发中,善于使用代码新特性,往往能让开发效率大大提升,这里简单介绍下lambad表达式及函数式接口特性。
1.Lambda 表达式
Lambda表达式也被称为箭头函数、匿名函数、闭包。他允许把函数作为一个方法的参数(函数作为参数传递到方法中),体现出轻量级函数式编程思想。
为什么引入lambda?
Model Code as Data,编码及数据,尽可能轻量级的将代码封装为数据。
解决方案:接口&实现类(匿名内部类)
存在问题:语法冗余,this关键字、变量捕获、数据控制等
public static void main (String[] args){
// 1. 传统模式下,新线程的创建
new Thread (new Runnable() {
@Override
public void run() {
System.out.println("threading..." + Thread.currentThread().getId())
}
}).start();
// 2. lambda表达式优化线程模式
new Thread(()->{
System.out.println("lambda threading..." + Thread.currentThread().getId());
})
}
不是解决未知问题的新技术
对现有问题的语义化优化
需要根据实际需求考虑性能问题
2.函数式接口(Functional Interface)
函数式接口就是Java类型系统中的接口,是只包含一个抽象方法的特殊接口(可以有很多非抽象方法)。
语言化检测注解:@FunctionalInterface 检测合法性
java1.8支持接口内包含:抽象方法、默认接口方法、静态接口方法、来自Object继承的方法
/**
* 用户身份认证标记接口
*/
@FunctionalInterface
public interface IUserCredential {
/**
* 通过用户账号,验证用户身份信息的接口
* @param username 要验证的用户账号
* @return 返回身份信息[系统管理员、用户管理员、普通用户]
*/
String verifyUser(String username);
default String getCredential(String username) {
if ("admin".equals(username)) {
return "admin + 系统管理员用户";
} else if("manager".equals(username)){
return "manager + 用户管理员用户";
} else {
return "commons + 普通会员用户";
}
}
String toString();
/**
* 消息合法性验证方法
* @param msg 要验证的消息
* @return 返回验证结果
*/
static boolean verifyMessage(String msg) {
if (msg != null) {
return true;
}
return false;
}
}
// 匿名内部类,实现接口的抽象方法
IUserCredential ic = new IUserCredential() {
@Override
public String verifyUser(String username) {
return "admin".equals(username)?"管理员":"会员";
}
};
// lambda表达式是函数式接口的一种简单实现
IUserCredential ic2 = (username) -> {
return "admin".equals(username)?"lbd管理员": "lbd会员";
};
JDK 1.8 之前已有的函数式接口:
java.lang.Runnable
java.util.concurrent.Callable
java.security.PrivilegedAction
java.util.Comparator
java.io.FileFilter
more
JDK 1.8 新增加的函数接口:
/*
java.util.function提供了大量的函数式接口
Predicate 接收参数T对象,返回一个boolean类型结果
Consumer 接收参数T对象,没有返回值
Function 接收参数T对象,返回R对象
Supplier 不接受任何参数,直接通过get()获取指定类型的对象
UnaryOperator 接口参数T对象,执行业务处理后,返回更新后的T对象
BinaryOperator 接口接收两个T对象,执行业务处理后,返回一个T对象
*/
Predicate pre = (String username) -> {
return "admin".equals(username);
};
System.out.println(pre.test("manager"));
Consumer con = (String message) -> {
System.out.println("要发送的消息:" + message);
};
con.accept("lambda expression.");
Function fun = (String gender) -> {
return "male".equals(gender)?1:0;
};
System.out.println(fun.apply("male"));
Supplier sup = () -> {
return UUID.randomUUID().toString();
};
System.out.println(sup.get());
UnaryOperator uo = (String img)-> {
img += "[100x200]";
return img;
};
System.out.println(uo.apply("原图--"));
BinaryOperator bo = (Integer i1, Integer i2) -> {
return i1 > i2? i1: i2;
};
System.out.println(bo.apply(12, 13));
3.lambda表达式的基本语法
基本语法
声明:就是和lambda表达式绑定的接口类型
参数:包含在一对圆括号中,和绑定的接口中的抽象方法中的参数个数及顺序一致。
操作符:->
执行代码块:包含在一对大括号中,出现在操作符号的右侧
[接口声明] = (参数) -> {执行代码块};
// 没有参数,没有返回值的lambda表达式绑定的接口
interface ILambda1{
void test();
}
// 带有参数,没有返回值的lambda表达式
interface ILambda2{
void test(String name, int age);
}
// 带有参数,带有返回值的lambda表达式
interface ILambda3 {
int test(int x, int y);
}
ILambda1 i1 = () -> System.out.println("hello boys!");
i1.test();
ILambda2 i21 = ( n, a) -> {
System.out.println(n + "say: my year's old is " + a);
};
i21.test("jerry", 18);
ILambda2 i22 = (n, a) ->
System.out.println(n + " 说:我今年" + a + "岁了.");
i22.test("tom", 22);
ILambda3 i3 = (x, y) -> {
int z = x + y;
return z;
};
System.out.println(i3.test(11, 22));
ILambda3 i31 = (x, y) -> x + y;
System.out.println(i31.test(100, 200));
总结:
lambda表达式,必须和接口进行绑定。
lambda表达式的参数,可以附带0个到n个参数,括号中的参数类型可以不用指定,jvm在运行时,会自动根据绑定的抽象方法中的参数进行推导。
lambda表达式的返回值,如果代码块只有一行,并且没有大括号,不用写return关键字,单行代码的执行结果,会自动返回。 如果添加了大括号,或者有多行代码,必须通过return关键字返回执行结果。
变量捕获
// 1. 匿名内部类型中对于变量的访问
String s1 = "全局变量";
public void testInnerClass() {
String s2 = "局部变量";
new Thread(new Runnable() {
String s3 = "内部变量";
@Override
public void run() {
// 访问全局变量
// System.out.println(this.s1);// this关键字~表示是当前内部类型的对象(报错)
System.out.println(s1);
System.out.println(s2);// 局部变量的访问,不能对局部变量进行数据的修改final
// s2 = "hello";
System.out.println(s3);
System.out.println(this.s3);
}
}).start();
}
// 2. lambda表达式变量捕获
public void testLambda() {
String s2 = "局部变量lambda";
new Thread(() -> {
String s3 = "内部变量lambda";
// 访问全局变量
// 不再建立对象域
System.out.println(this.s1);// this关键字,表示的就是所属方法所在类型的对象
// 访问局部变量
System.out.println(s2);
// s2 = "hello";// 不能进行数据修改,默认推导变量的修饰符:final
System.out.println(s3);
s3 = "labmda 内部变量直接修改";
System.out.println(s3);
}).start();
}
总结:Lambda表达式优化了匿名内部类类型中的this关键字,不再单独建立对象作用域,表达式本身就是所属类型对象的一部分,在语法语义上使用更加简洁。
类型检查
对于语法相同的表达式,Jvm在运行的过程中,在底层通过解释及重构,进行类型的自动推导。
@FunctionalInterface
interface MyInterface {
R strategy (T t, R r);
}
public static void test(MyInterface inter) {
List list = inter.strategy("hello", new ArrayList());
System.out.println(list);
}
public static void main(String[] args) {
test(new MyInterface() {
@Override
public List strategy(String s, List list) {
list.add(s);
return list;
}
});
test((x, y) -> {
y.add(x);
return y;
// x.add(y);
// return x;
});
/*
(x,y)->{..} --> test(param) --> param==MyInterface --> lambda表达式-> MyInterface类型
这个就是对于lambda表达式的类型检查,MyInterface接口就是lambda表达式的目标类型(target typing)
(x,y)->{..} --> MyInterface.strategy(T r, R r)--> MyInterface inter
--> T==String R==List --> lambda--> (x, y) == strategy(T t , R r)--> x==T==String y==R==List
*/
方法重载
interface Param1 {
void outInfo(String info);
}
interface Param2 {
void outInfo(String info);
}
// 定义重载的方法
public void lambdaMethod(Param1 param) {
param.outInfo("hello param1 imooc!");
}
public void lambdaMethod(Param2 param) {
param.outInfo("hello param2 imooc");
}
test.lambdaMethod(new Param1() {
@Override
public void outInfo(String info) {
System.out.println(info);
}
});
test.lambdaMethod(new Param2() {
@Override
public void outInfo(String info) {
System.out.println("------");
System.out.println(info);
}
});
/*
lambda表达式存在类型检查-> 自动推导lambda表达式的目标类型
lambdaMethod() -> 方法 -> 重载方法
-> Param1 函数式接口
-> Param2 函数式接口
调用方法-> 传递Lambda表达式-> 自动推导->
-> Param1 | Param2
*/
// 报错 Ambigus Method call
// test.lambdaMethod( (String info) -> {
// System.out.println(info);
// });
总结:出现方法重载的类型中参数都是函数式接口的情况,需使用匿名内部类实现替代lambda表达式。
底层构建原理
public class Test{
public static void main(String args[]){
ITest it = (message) -> System.out.println(message);
it.markUp("lambda!");
// new Test$$Lambda$1().markUp("lambda");
}
}
interface ITest{
void markUp(String msg);
}
javac Test.java
javap -p Test.class (javap反解析工具 -p显示所有类与成员)
java -Djdk.internal.lambda.dumpProxyClasses Test
Compiled from "Test.java"
public class Test {
public Test();
public static void main(java.lang.String[]);
private static void lambda$main$0(java.lang.String){
System.out.println(message);
};
}
finnal class Test$$Lambda$1 implements ITest{
private Test$$Lambda$1(){
}
public void markUp(java.lang.String msg){
Test.lambda$main$0(msg);
}
}
声明一个私有静态方法,对Lambda表达式做一个具体的方法实现
声明一个final内部类型并实现接口
在实现接口后的重写方法中利用外部类调用该私有静态方法
4.方法引用
方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
静态方法引用
实例方法引用
构造方法引用
class Person {
private String name;
private String gender;
private int age;
// 静态方法引用
public static int compareByAge(Person p1, Person p2) {
return p1.getAge() - p2.getAge();
}
}
class PersonUtil {
// 增加一个实例方法
public int comprareByName(Person p1, Person p2) {
return p1.getName().hashCode() - p2.getName().hashCode();
}
interface IPerson {
// 抽象方法:通过指定类型的构造方法初始化对象数据
Person initPerson(String name, String gender, int age);
}
public static void main(String[] args) {
List list = new ArrayList();
list.add(new Person("shuke", "男", 29));
list.add(new Person("tom", "男", 16));
list.add(new Person("jerry", "男", 20));
list.add(new Person("beita", "女", 30));
// 1.匿名内部类实现
Collections.sort(list, new Comparator() {
@Override
public int compare(Person o1, Person o2) {
return o1.getAge() - o2.getAge();
}
});
// 2.lambda表达式实现
Collections.sort(list, (p1, p2) -> p1.getAge() - p2.getAge());
// 3.静态方法引用实现
Collections.sort(list, Person::compareByAge);
// 4.实例方法引用
PersonUtil pu = new PersonUtil();
Collections.sort(list, pu::comprareByName);
list.forEach(System.out::println);
// 5.构造方法引用:绑定函数式接口
IPerson ip = Person::new;
Person person = p1.initPerson("tom", "男", 18);
System.out.println(person);
}
5.Stream
新添加的Stream流—是一个来自数据源的元素队列并支持聚合操作。把真正的函数式编程风格引入到Java中。
不存储数据,也不修改原始源。
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。
// 1. for循环实现 List list = new ArrayList(); for (String s : list) { if (s.length() > 3) { lista.add(s); } } System.out.println(lista);
// 2. 迭代器实现
List listb = new ArrayList<>();
Iterator it = list.iterator();
while(it.hasNext()) {
String s = it.next();
if(s.length() > 3) {
listb.add(s);
}
}
System.out.println(listb);
// 3. stream实现
List listc = list.stream().filter(s->s.length()>3)
.collect(Collectors.toList());
System.out.println(listc);
几者关系
lambda表达式是传统方法的语法糖,简化并且改造传统内部类实现设计方案的另一种实现模式。
方法引用又是lambda基础上的语法糖,和Stream没有关系,简化方法调用的。
Stream是针对数据和集合的强化优化操作,可以和lambda结合起来简化编码过程。
常见API介绍
1.聚合操作
2.Stream的处理流程
3.获取Stream对象
Collection.stream(), 如list.stream()
Collection.parallelstream(), 获得支持并发处理的流
Arrays.stream(T t)
BufferReader.lines()-> stream()
java.util.stream.IntStream.range()..
java.nio.file.Files.walk()..
java.util.Spliterator
Random.ints()
Pattern.spiltAsStream()..
4.中间操作API{intermediate}:
操作结果是一个Stream对象,所以中间操作可有一个或多个连续的中间操作,需要注意的是中间操作只记录操作方式,不做具体执行,直到结束操作发生时,才做数据的最终执行。
中间操作就是业务逻辑处理
操作过程分为有状态和无状态
无状态:即处理数据时,不受前置中间操作的影响
map/filter/peek/parallel/sequential/unordered
有状态:即处理数据时,受前置中间操作的影响
distant/sorted/limit/skip
5.终结操作|结束操作{Terminal}
一个steam对象只能有一个Terminal操作。这个操作不可逆,一旦发生,就会真实处理数据生成对应结果
非短路操作:当前的Stream对象必须处理完集合中所有的数据,才能得到处理结果
forEach/forEachOrdered/toArray/reduce/collect/min/max/count/iterator
短路操作:当前的Stream对象在处理过程中,一旦满足某个条件,就可以得到结果
anyMatch/AllMatch/noneMatch/findfirst/findAny等
short-circuiting : 在无限大的stream 中返回有限大的stream 需要包含短路操作是有必要的
Stream转换
// 1. 批量数据 -> Stream对象
// 多个数据
Stream stream = Stream.of("admin", "tom", "jerry");
// 数组
String [] strArrays = new String[] {"xueqi", "biyao"};
Stream stream2 = Arrays.stream(strArrays);
// 列表
List list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
Stream stream3 = list.stream();
// 集合
Set set = new HashSet<>();
set.add("aaa");
set.add("bbb");
set.add("ccc");
Stream stream4 = set.stream();
// Map
Map map = new HashMap<>();
map.put("tom", 1000);
map.put("jerry", 1200);
map.put("shuke", 1000);
Stream stream5 = map.entrySet().stream();
//2. Stream对象对于基本数据类型的功能封装
//int / long / double
IntStream.of(new int[] {10, 20, 30}).forEach(System.out::println); //只做一次拆箱装箱
IntStream.range(1, 5).forEach(System.out::println);
IntStream.rangeClosed(1, 5).forEach(System.out::println);
// 3. Stream对象 --> 转换得到指定的数据类型
// 数组
Object [] objx = stream.toArray(String[]::new);
// 字符串
String str = stream.collect(Collectors.joining()).toString();
System.out.println(str);
// 列表
//List listx = (List) stream.collect(Collectors.toList());
System.out.println(listx);
// 集合
//Set setx = (Set) stream.collect(Collectors.toSet());
System.out.println(setx);
// Map
//Map mapx = (Map) stream.collect(Collectors.toMap(x->x, y->"value:"+y));
System.out.println(mapx);
Stream常见操作
// Stream中常见的API操作
List accountList = new ArrayList<>();
accountList.add("tom");
accountList.add("jerry");
accountList.add("apha");
accountList.add("beta");
accountList.add("shuke");
// map() 中间操作,map()方法接收一个Functional接口
accountList = accountList.stream().map(x->"name:" + x).collect(Collectors.toList());
// filter() 添加过滤条件,过滤符合条件的用户
accountList = accountList.stream().filter(x-> x.length() > 3).collect(Collectors.toList());
// forEach 增强型循环
accountList.forEach(x-> System.out.println("forEach->" + x));
// peek() 中间操作,迭代数据完成数据的依次处理过程
accountList.stream()
.peek(x -> System.out.println("peek 1: " + x))
.peek(x -> System.out.println("peek 2:" + x))
.forEach(System.out::println);// 合并多个过程 迭代只发生一次
accountList.forEach(System.out::println);
// Stream中对于数字运算的支持
List intList = new ArrayList<>();
intList.add(20);
intList.add(19);
intList.add(7);
intList.add(8);
intList.add(86);
intList.add(11);
intList.add(3);
intList.add(20);
// skip() 中间操作,有状态,跳过部分数据
intList.stream().skip(3).forEach(System.out::println);
// limit() 中间操作,有状态,限制输出数据量
intList.stream().skip(3).limit(2).forEach(System.out::println);
// distinct() 中间操作,有状态,剔除重复的数据
intList.stream().distinct().forEach(System.out::println);
// sorted() 中间操作,有状态,排序
// max() 获取最大值
Optional optional = intList.stream().max((x, y)-> x-y);
System.out.println(optional.get());
// min() 获取最小值
// reduce() 合并处理数据
Optional optional2 = intList.stream().reduce((sum, x)-> sum + x);
System.out.println(optional2.get());
6.案例
问题一:将实例List转化为Map
对于List
来说,我需要将其形变为Map,用如下流处理代码
//Table类
public class DmTable {
private Integer id;
private String tableName;
private String tableComment;
private Integer datasourceId;
private Integer directoryId;
private Boolean partitionFlag;
private Integer columnNum;
// ......
}
tableMap=TableList.stream().collect(Collectors.toMap(Table::getId, b -> b);
// 等效于
tableMap=TableList.stream().collect(Collectors.toMap(Table::getId, Function.identity()));// 静态方法 实现 return t -> t;
问题二:将集合分成若干类别
使用问题一中的Table类,对于List
,我需要将其按照partitionFlag分类,Collector提供两种方法partitioningBy()、groupingBy()。前者分成满足条件与不满足条件两类,后者可按条件分成若干类别的Map。
Map> tablePartition = tableList
.stream().collect(Collectors.partitioningBy(item -> item.getPartitionFlag() == true));
有的时候,我们关注的不光是元素还有元素的个数,流处理可以再进行后期处理。
Map> tablePartition = tableList
.stream().collect(Collectors.partitioningBy(item -> item.getPartitionFlag() == true,Collectors.counting()));
可输出符合要求的个数。
groupingBy()可对字符串长度分组。
List strings=Arrays.asList(“this”,”is”,”a”,”test”);
Map> stringsMap = strings
.stream().collect(Collectors.groupingBy(String::length);
结果输出多分类的map,key值为字符串长度。
注意:如果是从数据库获取数据,务必将分组操作放在数据库中执行,java8新增方法只适合处理内存中的数据。
问题三:从list中得到某个特定的对象
获得List
中columnNum最多的table对象
tableList.stream().sorted(comparingInt(Table::getColumnNum)).collect(Collectors.toList()).get(tableList.size() - 1);
添加中间操作reversed() 可获取最小columnNum的对象
问题四: 得到Map中最大columnNum的table
List> list = new ArrayList(tableMap.entrySet());
Collections.sort(list, (o1, o2) -> (o2.getValue() - o1.getValue()));
list.get(0).getKey();
7.性能与安全
串行Stream的性能小于传统的for循环、 迭代器
并行Stream的性能与传统的for循环、 迭代器差不多,在处理对象(复杂数据类型)的情况下,并行性能最佳
// 整数列表
List lists = new ArrayList();
// 增加数据
for (int i = 0; i < 1000; i++){
lists.add(i);
}
// 串行Stream
List list2 = new ArrayList<>();
lists.stream().forEach(x->list2.add(x));
System.out.println(lists.size());
System.out.println(list2.size());
// 并行Stream 线程不安全 丢失
List list3 = new ArrayList<>();
lists.parallelStream().forEach(x-> list3.add(x));
System.out.println(list3.size());
// collect 当并行执行时可以实例化、填充和合并多个中间结果,以保持可变数据结构的隔离
List list4 = lists.parallelStream().collect(Collectors.toList());
System.out.println(list4.size());
本文分享自华为云社区《如何善用函数式接口简化云服务业务代码开发》,原文作者:luanzhen 。
点击关注,第一时间了解华为云新鲜技术~
你可能感兴趣的:(技术交流,程序员,代码,lambda,函数,函数式编程,数据)
SpringBoot继承JWT token实现权限的验证(从头开始)
CodeGuruInk
spring boot java 后端
目录概述前提:我们需要知道的文件的用处第1步:数据库的连接第2步:定义一个标准化响应对象的类第3步:编写请求数据库数据代码第4步:自定义异常处理第5步:导入依赖第6步:自定义拦截器第7步:配置拦截器第8步:生成token第9步:开始测试代码第10步:vue请求示例扩展:自定义注解AuthAccess总结概述在开发后端接口时,数据的直接暴露可能会导致安全问题。为了应对这种情况,我们需要在访问接口时进
『大模型笔记』AI自动化编程工具汇总[持续更新ING]!
AI大模型前沿研究
大模型笔记 大模型 AI自动化工具 bolt.new Cursor V0
『大模型笔记』AI自动化编程工具汇总!文章目录一.Bolt.new(开源AI驱动全栈Web开发工具)1.1.Bolt.new介绍1.2.编程小白如何打造自己的导航网站二.Cursor(人工智能代码编辑器)2.1.Cursor入门教程2.2.Cursor左侧布局设置和VSCode一样2.3.Cursor效率之道:Agent模式+7大高级技巧详解三.Windsurf(颠覆Cursor的全新工具)3.1
vue2和vue3中是如何进行组件通信
尔嵘
Vue3.0 vue3
前言:Vue2和Vue3都支持多种方式的组件通信,包括:1.Props和Events:父组件向子组件传递数据可以使用props属性。子组件向父组件发送消息可以使用自定义事件(事件名需要使用kebab-case命名规则)。在Vue3中,还可以使用v-model指令来简化双向绑定。2.$emit和on:通过on:通过emit方法在子组件中触发自定义事件,并在父组件中监听该事件使用$on方法来实现组件之
C++实现设计模式---建造者模式 (Builder)
计算机小混子
设计模式 c++ 设计模式 建造者模式
建造者模式(Builder)建造者模式是一种创建型设计模式,它将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。意图将复杂对象的创建过程分步骤完成,并允许以不同方式构建其各部分。提供灵活的方式构造对象,而无需关心其内部细节。使用场景创建复杂对象:当对象的创建需要很多步骤或参数时,使用建造者模式可以简化代码。需要多个构建版本:如同一个对象可能有不同的表示形式,但它们共享相同的构建
使用 Railway 和 Supabase 零成本搭建 n8n 自动化平台
小二上酒8
自动化 系统架构 运维 java 开发语言
在前文使用自动化工作流聚合信息摄入和输出中,我介绍了如何在NAS提供的Docker环境安装n8n,以及n8nworkflow的使用方式。经过3个月的使用,我有了一些新的体会和尝试,重新设计了n8n的部署方案。本文将对这套新的方案进行说明,并分享数据迁移和第三方服务接入的实践。系统架构系统架构图我们所要搭建的这套服务有着如图所示的系统关系。Cloudflare:CDN和Proxy,用于加速网站访问,
C++设计模式——Bridge桥接模式
程序员与背包客_CoderZ
C/C++设计模式 c++ 设计模式 开发语言 c语言 linux 桥接模式
一,桥接模式简介桥接模式是一种结构型设计模式,用于将抽象与实现分离,这里的"抽象"和"实现"都有可能是接口函数或者类。桥接模式让抽象与实现之间解耦合,使得开发者可以更关注于实现部分,调用者(Client)可以更关注于抽象部分。桥接模式可以将一个复杂的类进行拆分为好几个类,开发者可以修改其中任意一个类的实现,而不影响其他类的正常运行,该模式可以降低代码的维护工作量,降低代码风险。桥接模式的核心就是:
计算机毕业设计之jsp影视推荐系统
我的微信bishe911
课程设计 java 开发语言 mysql jsp
随着信息化时代的到来,网络系统都趋向于智能化、系统化,影视推荐系统也不例外,但目前国内的很多行业仍使用人工管理,影视信息量也越来越庞大,人工管理显然已无法应对时代的变化,而影视推荐系统能很好地解决这一问题,轻松的对影视信息进行评分,既能提高用户对影视推荐的了解,又能快捷的查看影视信息,取代人工管理是必然趋势。本影视推荐系统以SSM作为框架,B/S模式以及MySql作为后台运行的数据库。本系统主要包
解锁 Python 与 MySQL 交互密码:全方位技术解析与实战攻略
秋夜Autumn
python MySQL
目录一、引言二、环境准备2.1安装MySQL2.2安装Python及相关库2.2.1使用mysql-connector-python2.2.2使用pymysql三、基本连接与操作3.1连接到MySQL数据库3.2创建游标对象3.3执行SQL查询3.3.1查询单条记录3.3.2查询多条记录3.4插入数据3.5更新数据3.6删除数据3.7关闭连接四、错误处理五、高级操作5.1使用事务5.2处理大型结果
Vue 框架深度剖析:原理、应用与最佳实践
秋夜Autumn
vue.js 前端 javascript
目录一、Vue框架简介二、Vue的安装与基本使用(一)安装(二)基本使用三、Vue组件(一)创建组件(二)组件通信四、Vue模板语法(一)插值(二)指令(三)计算属性和侦听器五、Vue过渡与动画(一)过渡类名(二)过渡组件六、VueRouter(一)安装与配置(二)使用路由七、Vuex(一)核心概念(二)使用Vuex八、Vue项目优化(一)性能优化(二)代码优化九、Vue生态系统(一)UI框架(二
vue3阻止事件冒泡到父元素
我爱加班、、
前端功能实现以及问题解决 前端开发遇到的问题 vue.js javascript 前端
在Vue3中,如果你想在子组件的点击事件中阻止父组件绑定的点击事件触发,可以使用以下方法:1.使用.stop修饰符Vue提供了.stop修饰符,可以阻止事件冒泡到父元素。这是最简单直接的方式。示例代码点击我exportdefault{methods:{handleParentClick(){console.log('父元素被点击');},handleChildClick(){console.log
LGBMRegressor CatBoostRegressor XGBRegressor回归
兔兔爱学习兔兔爱学习
竞赛代码实践 回归 数据挖掘
importpandasaspd#导入csv文件的库importnumpyasnp#进行矩阵运算的库importpolarsaspl#和pandas类似,但是处理大型数据集有更好的性能.#用于对一组元素计数,一个存在默认值的字典,访问不存在的值时抛出的是默认值fromcollectionsimportCounter,defaultdictimportre#用于正则表达式提取fromscipy.st
Lambda离线实时分治架构深度解析与实战
喜欢猪猪
架构
一、引言在大数据技术日新月异的今天,Lambda架构作为一种经典的数据处理模型,在应对大规模数据应用方面展现出了强大的能力。它整合了离线批处理和实时流处理,为需要同时处理批量和实时数据的应用场景提供了成熟的解决方案。本文将对Lambda架构的演变、核心组件、工作原理及痛点进行深度解析,并通过Java代码实现一个实战实例。二、Lambda架构的演变Lambda架构是由Storm的作者NathanMa
“大模型横扫千军”背后的大数据挖掘--浅谈MapReduce
绒绒毛毛雨
大数据挖掘 数据挖掘 mapreduce 人工智能
文章目录O背景知识1数据挖掘2邦费罗尼原则3TF.IDF4哈希函数5分布式文件系统一、MapReduce基本介绍1.Map任务2.按键分组3.Reduce任务4.节点失效处理5.小测验:在一个大型语料库上有100个map任务和若干reduce任务:二、基于MapReduce的基本运算1.选择(Selection)2.交(Intersection)3.并(Union)4.补(Difference)5
什么是vue.js组件开发,我们需要做哪些准备工作?
大懒猫软件
vue.js
Vue.js是一个非常流行的前端框架,用于构建用户界面。组件开发是Vue.js的核心概念之一,通过将界面拆分为独立的组件,可以提高代码的可维护性和复用性。以下是一个详细的Vue.js组件开发指南,包括基础概念、开发流程和代码示例。一、Vue.js组件开发基础1.组件的基本结构Vue.js组件是一个独立的、可复用的UI元素。每个组件都有自己的模板、逻辑和样式。组件的基本结构如下:vue复制{{tit
【贪心算法】洛谷P1106 - 删数问题
仟濹
算法学习笔记 贪心算法 算法 c语言 c++
2025-01-22-第46篇【洛谷】贪心算法题单-【贪心算法】-【学习笔记】作者(Author):郑龙浩/仟濹(CSND账号名)目录文章目录目录P1106删数问题题目描述输入格式输出格式样例#1样例输入#1样例输出#1提示思路代码P1106删数问题题目描述键盘输入一个高精度的正整数nnn(不超过250250250位),去掉其中任意kkk个数字后剩下的数字按原左右次序将组成一个新的非负整数。编程对
Hibernate、JPA、Spring DATA JPA、Hibernate 代理和架构
小蜗牛慢慢爬行
hibernate 架构 java
大家好,今天,我们将讨论Hibernate和JPA架构。在开始我们的文章之前,我想回答一个重要的问题:为什么我们需要使用Hibernate、EclipseLink、EFcore等ORM工具?事实上,这是一个非常好的问题。我们开发人员通常有几个月的时间来创建一个新项目,或者有两周的时间来为我们的业务逻辑添加新功能。当我们考虑这个时间表时,为我们的业务逻辑编写SQL代码需要花费时间;因此,ORM工具的
C++实现设计模式---桥接模式 (Bridge)
计算机小混子
设计模式 c++ 设计模式 桥接模式
桥接模式(Bridge)桥接模式是一种结构型设计模式,它通过将抽象部分与实现部分分离,使它们可以独立变化。桥接模式的核心思想是使用组合(而非继承)来扩展功能。意图将抽象部分与实现部分分离,使它们都可以独立地变化。解决继承层次过深的问题,避免类的爆炸式增长。使用场景系统需要在抽象和实现之间解耦:抽象和实现之间需要独立变化,使用继承会导致代码的复杂性上升。避免类的数量爆炸:系统中有多维度变化的对象(如
解锁 MySQL 数据库的无限潜能:全方位深度解析
秋夜Autumn
数据库 mysql
目录一、MySQL简介二、MySQL安装与配置(一)安装MySQL(二)配置MySQL三、MySQL基础语法(一)数据类型(二)数据库操作(三)表操作(四)数据操作四、MySQL高级特性(一)索引(二)视图(三)存储过程与函数(四)事务处理(五)数据备份与恢复五、MySQL性能优化(一)查询优化(二)服务器配置优化(三)数据库设计优化六、MySQL与其他技术的集成(一)MySQL与Web开发(二)
Python Pandas数据清洗与处理
大数据张老师
Python程序设计 python pandas 开发语言
PythonPandas数据清洗与处理在进行数据分析时,原始数据往往包含了许多不完整、不准确或者冗余的信息。数据清洗与处理的任务就是将这些杂乱无章的数据清理干净,确保数据的准确性和一致性,从而为后续的分析工作打下坚实的基础。Pandas提供了强大的工具来帮助我们清洗和处理数据,尤其是在处理Series和DataFrame时,它能够高效地进行数据的筛选、填充、删除、替换等操作。本节将通过一些常见的数
【数据分享】1929-2024年全球站点的逐年平均降水量(Shp\Excel格式)
小鲨鱼-立方数据学社
立方数据学社 数据分享 气象数据 气象站点 降水数据
气象数据是在各项研究中都经常使用的数据,气象指标包括气温、风速、降水、湿度等指标!说到气象数据,最详细的气象数据是具体到气象监测站点的数据!有关气象指标的监测站点数据,之前我们分享过1929-2024年全球气象站点的逐年平均气温数据、最高气温数据和最低气温数据(均可查看之前的文章获悉详情)。本次我们为大家继续带来具体到气象监测站点的数据——1929-2024年全球气象站点的逐年平均降水量数据!原始
STM32——LCD液晶显示
三只树懒
STM32 单片机 stm32
LCD结构体typedefstruct{vu16LCD_REG;vu16LCD_RAM;}LCD_TypeDef;//使用NOR/SRAM的Bank1.sector4,地址位HADDR[27,26]=11A10作为数据命令区分线//注意设置时STM32内部会右移一位对齐#defineLCD_BASE((u32)(0x6C000000|0x0000007E))#defineLCD((LCD_Type
一、初识操作系统【Lite OS】
阿猿先森
华为LiteOS物联网操作系统 iot os iot
一、为什么要用操作系统从裸机到操作系统是必然发展路线(1)发展阶段1:裸机并且全部代码自写(2)发展阶段2:裸机但调用库函数(3)发展阶段3:裸机加状态机实现简单的多任务(4)发展阶段4:上简单操作系统:RTOS(ucos、LiteoS)(5)发展阶段5:上复杂操作系统:Linux、Windows、Android二、用操作系统的优势(1)轻松实现多任务(2)轻松借用很多第三方组件功能(3)更好实现
Java常用类
ufosuai555
java python 开发语言
包装类包装类定义包装类就是将基本数据类型封装到类中,使基本数据类型具有类的特点,方便操作包装类分类byte->Byteshort->Shortint->Integerlong->Longfloat->Floatdouble->Doublechar->Characterboolean->Boolean基本数据类型与包装类之间的转换在JDK5之前,基本数据类型与包装类之间的转换需要手动编写代码实现,在
Python接口自动化测试框架(实战篇)-- Jenkins持续集成
职说测试
python jenkins ci/cd 自动化测试 接口自动化测试
文章目录一、前言二、[Jenkins](https://www.jenkins.io/)2.1、环境搭建2.2、插件准备2.3、创建job2.4、小结2.5、构建策略2.6、报告展示2.7、扩展三、总结一、前言温馨提示:在框架需要集成jenkins的时候,一定要注意环境切换问题,如果jenkins和开发环境是同样的系统且都有python环境,基本不用太担心代码的移植问题,如果是跨平台了,那么需要注
红黑树实现
黎相思
C++ 算法 c++ 数据结构
目录1.红黑树的概念1.1红黑树的规则1.2红黑树如何确保最长路径不超过最短路径的2倍呢?1.3红黑树的效率编辑2.红黑树的实现2.1红黑树的结构2.2红黑树的插入2.2.1红黑树插入一个值的大概过程2.2.2情况1:变色2.2.3情况2:单旋+变色2.2.4情况3:双旋+变色2.3红黑树的插入代码实现2.4红黑树的查找2.5红黑树的高度2.6红黑树节点个数2.7红黑树的验证2.8红黑树的删除3.
基于大数据的电影数据分析可视化系统设计与应用
AI架构设计之禅
大数据AI人工智能 AI大模型应用入门实战与进阶 计算科学 神经计算 深度学习 神经网络 大数据 人工智能 大型语言模型 AI AGI LLM Java Python 架构设计 Agent RPA
基于大数据的电影数据分析可视化系统设计与应用作者:禅与计算机程序设计艺术1.背景介绍1.1大数据时代的电影行业随着互联网技术和数字化的发展,电影行业已经进入大数据时代。每天都有海量的电影相关数据在各个平台上生成,包括票房数据、评分数据、影评数据等。这些数据蕴含着巨大的价值,如果能够有效地分析和利用,将为电影行业的发展提供重要的决策支持。1.2电影数据分析与可视化的意义1.2.1洞察电影市场趋势通过
python实战项目34:基于flask的天气数据可视化系统1.0
wp_tao
Python副业接单实战项目 flask 信息可视化 python
基于flask的天气数据可视化系统1.0一、效果展示二、flask简介三、图表绘制四、前端页面编写五、完整代码一、效果展示该flask项目相对简单入门,使用了flask框架、bootstrap前端技术,数据使用的是上一篇scrapy爬取城市天气数据中爬取到的数据。二、flask简介Flask是一个基于Python的Web开发框架,它以灵活、微框架著称,基于werkzeug的轻量级web框架,可提高
如何在 Flask 中实现用户认证?
Channing Lewis
Python flask python 后端
在Flask中实现用户认证,可以通过以下方式完成:基础步骤设置用户数据库:存储用户信息(如用户名、密码)。注册功能:允许用户创建账号。登录功能:验证用户输入的凭据。会话管理:使用Flask的session或第三方工具管理登录状态。登出功能:清除用户的登录状态。实现步骤以下是一个完整示例,展示如何实现用户认证功能:1.项目文件结构flask_auth/├──app.py#主应用├──auth/#用户
flask实现重启后需要重新输入用户名而避免浏览器使用之前已经记录的用户名
Channing Lewis
Python flask python 后端
要实现重启后重新输入用户名的功能,关键是确保Flask在应用重启时不会保留之前的用户会话(即清除登录状态)。以下是一些可能的原因和解决方法:1.问题原因:Session数据没有清除Flask使用session来保存用户的登录状态(如用户名),默认存储在浏览器的Cookie中。如果Flask使用了持久化的secret_key,即使重启服务器,客户端的Cookie仍然有效,导致会话数据还存在。2.解决
元数据驱动的设想
吾爱乐享
python python
本文永久更新地址:1.背景针对相似结构的表单,为了提高ui自动化编写效率,减少以减少重复工作,设想是否可以设计一个针对neoUI2.0通过元数据驱动的方式适应不同业务对象的测试框架2.设计元数据模型-字段名-字段类型-是否必填-是否只读-默认值-业务逻辑(可选,后期扩展)3.构建自动化测试框架利用现有的RF框架已实现的功能,通过实体名称打开对应实体的表单关键L进入【进入菜单】关键字进入【点击新建业
knob UI插件使用
换个号韩国红果果
JavaScript jsonp knob
图形是用canvas绘制的
js代码
var paras = {
max:800,
min:100,
skin:'tron',//button type
thickness:.3,//button width
width:'200',//define canvas width.,canvas height
displayInput:'tr
Android+Jquery Mobile学习系列(5)-SQLite数据库
白糖_
JQuery Mobile
目录导航
SQLite是轻量级的、嵌入式的、关系型数据库,目前已经在iPhone、Android等手机系统中使用,SQLite可移植性好,很容易使用,很小,高效而且可靠。
因为Android已经集成了SQLite,所以开发人员无需引入任何JAR包,而且Android也针对SQLite封装了专属的API,调用起来非常快捷方便。
我也是第一次接触S
impala-2.1.2-CDH5.3.2
dayutianfei
impala
最近在整理impala编译的东西,简单记录几个要点:
根据官网的信息(https://github.com/cloudera/Impala/wiki/How-to-build-Impala):
1. 首次编译impala,推荐使用命令:
${IMPALA_HOME}/buildall.sh -skiptests -build_shared_libs -format
2.仅编译BE
${I
求二进制数中1的个数
周凡杨
java 算法 二进制
解法一:
对于一个正整数如果是偶数,该数的二进制数的最后一位是 0 ,反之若是奇数,则该数的二进制数的最后一位是 1 。因此,可以考虑利用位移、判断奇偶来实现。
public int bitCount(int x){
int count = 0;
while(x!=0){
if(x%2!=0){ /
spring中hibernate及事务配置
g21121
Hibernate
hibernate的sessionFactory配置:
<!-- hibernate sessionFactory配置 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<
log4j.properties 使用
510888780
log4j
log4j.properties 使用
一.参数意义说明
输出级别的种类
ERROR、WARN、INFO、DEBUG
ERROR 为严重错误 主要是程序的错误
WARN 为一般警告,比如session丢失
INFO 为一般要显示的信息,比如登录登出
DEBUG 为程序的调试信息
配置日志信息输出目的地
log4j.appender.appenderName = fully.qua
Spring mvc-jfreeChart柱图(2)
布衣凌宇
jfreechart
上一篇中生成的图是静态的,这篇将按条件进行搜索,并统计成图表,左面为统计图,右面显示搜索出的结果。
第一步:导包
第二步;配置web.xml(上一篇有代码)
建BarRenderer类用于柱子颜色
import java.awt.Color;
import java.awt.Paint;
import org.jfree.chart.renderer.category.BarR
我的spring学习笔记14-容器扩展点之PropertyPlaceholderConfigurer
aijuans
Spring3
PropertyPlaceholderConfigurer是个bean工厂后置处理器的实现,也就是BeanFactoryPostProcessor接口的一个实现。关于BeanFactoryPostProcessor和BeanPostProcessor类似。我会在其他地方介绍。
PropertyPlaceholderConfigurer可以将上下文(配置文件)中的属性值放在另一个单独的标准java
maven 之 cobertura 简单使用
antlove
maven test unit cobertura report
1. 创建一个maven项目
2. 创建com.CoberturaStart.java
package com;
public class CoberturaStart {
public void helloEveryone(){
System.out.println("=================================================
程序的执行顺序
百合不是茶
JAVA执行顺序
刚在看java核心技术时发现对java的执行顺序不是很明白了,百度一下也没有找到适合自己的资料,所以就简单的回顾一下吧
代码如下;
经典的程序执行面试题
//关于程序执行的顺序
//例如:
//定义一个基类
public class A(){
public A(
设置session失效的几种方法
bijian1013
web.xml session失效 监听器
在系统登录后,都会设置一个当前session失效的时间,以确保在用户长时间不与服务器交互,自动退出登录,销毁session。具体设置很简单,方法有三种:(1)在主页面或者公共页面中加入:session.setMaxInactiveInterval(900);参数900单位是秒,即在没有活动15分钟后,session将失效。这里要注意这个session设置的时间是根据服务器来计算的,而不是客户端。所
java jvm常用命令工具
bijian1013
java jvm
一.概述
程序运行中经常会遇到各种问题,定位问题时通常需要综合各种信息,如系统日志、堆dump文件、线程dump文件、GC日志等。通过虚拟机监控和诊断工具可以帮忙我们快速获取、分析需要的数据,进而提高问题解决速度。 本文将介绍虚拟机常用监控和问题诊断命令工具的使用方法,主要包含以下工具:
&nbs
【Spring框架一】Spring常用注解之Autowired和Resource注解
bit1129
Spring常用注解
Spring自从2.0引入注解的方式取代XML配置的方式来做IOC之后,对Spring一些常用注解的含义行为一直处于比较模糊的状态,写几篇总结下Spring常用的注解。本篇包含的注解有如下几个:
Autowired
Resource
Component
Service
Controller
Transactional
根据它们的功能、目的,可以分为三组,Autow
mysql 操作遇到safe update mode问题
bitray
update
我并不知道出现这个问题的实际原理,只是通过其他朋友的博客,文章得知的一个解决方案,目前先记录一个解决方法,未来要是真了解以后,还会继续补全.
在mysql5中有一个safe update mode,这个模式让sql操作更加安全,据说要求有where条件,防止全表更新操作.如果必须要进行全表操作,我们可以执行
SET
nginx_perl试用
ronin47
nginx_perl试用
因为空闲时间比较多,所以在CPAN上乱翻,看到了nginx_perl这个项目(原名Nginx::Engine),现在托管在github.com上。地址见:https://github.com/zzzcpan/nginx-perl
这个模块的目的,是在nginx内置官方perl模块的基础上,实现一系列异步非阻塞的api。用connector/writer/reader完成类似proxy的功能(这里
java-63-在字符串中删除特定的字符
bylijinnan
java
public class DeleteSpecificChars {
/**
* Q 63 在字符串中删除特定的字符
* 输入两个字符串,从第一字符串中删除第二个字符串中所有的字符。
* 例如,输入”They are students.”和”aeiou”,则删除之后的第一个字符串变成”Thy r stdnts.”
*/
public static voi
EffectiveJava--创建和销毁对象
ccii
创建和销毁对象
本章内容:
1. 考虑用静态工厂方法代替构造器
2. 遇到多个构造器参数时要考虑用构建器(Builder模式)
3. 用私有构造器或者枚举类型强化Singleton属性
4. 通过私有构造器强化不可实例化的能力
5. 避免创建不必要的对象
6. 消除过期的对象引用
7. 避免使用终结方法
1. 考虑用静态工厂方法代替构造器
类可以通过
[宇宙时代]四边形理论与光速飞行
comsci
从四边形理论来推论 为什么光子飞船必须获得星光信号才能够进行光速飞行?
一组星体组成星座 向空间辐射一组由复杂星光信号组成的辐射频带,按照四边形-频率假说 一组频率就代表一个时空的入口
那么这种由星光信号组成的辐射频带就代表由这些星体所控制的时空通道,该时空通道在三维空间的投影是一
ubuntu server下python脚本迁移数据
cywhoyi
python Kettle pymysql cx_Oracle ubuntu server
因为是在Ubuntu下,所以安装python、pip、pymysql等都极其方便,sudo apt-get install pymysql,
但是在安装cx_Oracle(连接oracle的模块)出现许多问题,查阅相关资料,发现这边文章能够帮我解决,希望大家少走点弯路。http://www.tbdazhe.com/archives/602
1.安装python
2.安装pip、pymysql
Ajax正确但是请求不到值解决方案
dashuaifu
Ajax async
Ajax正确但是请求不到值解决方案
解决方案:1 . async: false , 2. 设置延时执行js里的ajax或者延时后台java方法!!!!!!!
例如:
$.ajax({ &
windows安装配置php+memcached
dcj3sjt126com
PHP Install memcache
Windows下Memcached的安装配置方法
1、将第一个包解压放某个盘下面,比如在c:\memcached。
2、在终端(也即cmd命令界面)下输入 'c:\memcached\memcached.exe -d install' 安装。
3、再输入: 'c:\memcached\memcached.exe -d start' 启动。(需要注意的: 以后memcached将作为windo
iOS开发学习路径的一些建议
dcj3sjt126com
ios
iOS论坛里有朋友要求回答帖子,帖子的标题是: 想学IOS开发高阶一点的东西,从何开始,然后我吧啦吧啦回答写了很多。既然敲了那么多字,我就把我写的回复也贴到博客里来分享,希望能对大家有帮助。欢迎大家也到帖子里讨论和分享,地址:http://bbs.csdn.net/topics/390920759
下面是我回复的内容:
结合自己情况聊下iOS学习建议,
Javascript闭包概念
fanfanlovey
JavaScript 闭包
1.参考资料
http://www.jb51.net/article/24101.htm
http://blog.csdn.net/yn49782026/article/details/8549462
2.内容概述
要理解闭包,首先需要理解变量作用域问题
内部函数可以饮用外面全局变量
var n=999;
functio
yum安装mysql5.6
haisheng
mysql
1、安装http://dev.mysql.com/get/mysql-community-release-el7-5.noarch.rpm
2、yum install mysql
3、yum install mysql-server
4、vi /etc/my.cnf 添加character_set_server=utf8
po/bo/vo/dao/pojo的详介
IT_zhlp80
java BO VO DAO POJO po
JAVA几种对象的解释
PO:persistant object持久对象,可以看成是与数据库中的表相映射的java对象。最简单的PO就是对应数据库中某个表中的一条记录,多个记录可以用PO的集合。PO中应该不包含任何对数据库的操作.
VO:value object值对象。通常用于业务层之间的数据传递,和PO一样也是仅仅包含数据而已。但应是抽象出的业务对象,可
java设计模式
kerryg
java 设计模式
设计模式的分类:
一、 设计模式总体分为三大类:
1、创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。
2、结构型模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。
3、行为型模式(11种):策略模式,模版方法模式,观察者模式,迭代子模式,责任链模式,命令模式,备忘录模式,状态模式,访问者
[1]CXF3.1整合Spring开发webservice——helloworld篇
木头.java
spring webservice CXF
Spring 版本3.2.10
CXF 版本3.1.1
项目采用MAVEN组织依赖jar
我这里是有parent的pom,为了简洁明了,我直接把所有的依赖都列一起了,所以都没version,反正上面已经写了版本
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="ht
Google 工程师亲授:菜鸟开发者一定要投资的十大目标
qindongliang1922
工作 感悟 人生
身为软件开发者,有什么是一定得投资的? Google 软件工程师 Emanuel Saringan 整理了十项他认为必要的投资,第一项就是身体健康,英文与数学也都是必备能力吗?来看看他怎么说。(以下文字以作者第一人称撰写)) 你的健康 无疑地,软件开发者是世界上最久坐不动的职业之一。 每天连坐八到十六小时,休息时间只有一点点,绝对会让你的鲔鱼肚肆无忌惮的生长。肥胖容易扩大罹患其他疾病的风险,
linux打开最大文件数量1,048,576
tianzhihehe
c linux
File descriptors are represented by the C int type. Not using a special type is often considered odd, but is, historically, the Unix way. Each Linux process has a maximum number of files th
java语言中PO、VO、DAO、BO、POJO几种对象的解释
衞酆夼
java VO BO POJO po
PO:persistant object持久对象
最形象的理解就是一个PO就是数据库中的一条记录。好处是可以把一条记录作为一个对象处理,可以方便的转为其它对象。可以看成是与数据库中的表相映射的java对象。最简单的PO就是对应数据库中某个表中的一条记录,多个记录可以用PO的集合。PO中应该不包含任何对数据库的操作。
BO:business object业务对象
封装业务逻辑的java对象