实体类分类、Lombok、List集合、匿名内部类、lambda
Java中实体类的分类
POJO(Plain Ordinary Java Object)
就是一个普通的java类对象,可以转化为VO、DTO、PO
VO(View Object)
表示层对象,一般在Controller层使用,对应页面显示的数据对象。
DTO(Data Transfer Object)
数据传输对象,一般在Service层使用,如PO(Entity)有30个属性,页面VO只显示10个,那么DTO就也传输10个。
Entity(实体类)
持久化对象,一般在Dao层使用,它跟数据表形成一对一的映射关系。
Lombok(以及常用注解)
是一个在Java开发过程中用注解的方式,简化了 JavaBean 的编写,就是一个插件,让编写的类更加简洁。
@Data
自动生成set/get方法,toString方法,equals方法,hashCode方法,不带参数的构造方法
@NoArgsConstructor
无参构造
@AllArgsConstructor
全参构造
Maven依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
List集合
概念:List首先是一个接口下面有两个实现类ArrayList和LinkedList
ArrayList
1.ArrayList是List接口的一个实现类,它是程序中最常见的一种集合类;
2.在ArrayList内部封装了一个数组对象,初始长度缺省为10,当存入的元素超过数组长度时,ArrayList会在内存中分配一个更大的数组来重新容纳这些元素,因此可以将ArrayList集合看作一个长度可变的数组;
3.ArrayList集合类的大部分方法都是从父类Collection和List继承过来的,其中add()方法和get()方法用于实现元素的添加和读取。
LinkedList
1.List接口的另一个实现类LinkedList,克服了ArrayList集合在增删元素时效率较低的局限性。但是LinkedList集合的查询效率低(不支持随机访问),要查询第n个元素,必须从第一个元素开始,逐一的向后遍历,直到第n个元素。
2.该集合内部维护了一个双向链表,链表中的每个元素都通过对象引用来记住它的前一个元素和后一个元素,从而将所有的元素彼此连接起来。
3.当插入一个新元素时,只需要修改元素之间的引用关系即可,删除一个节点也是如此。
遍历List集合
普通for循环
增强for循环
迭代器
List自身迭代器
Lambda表达式
Stream流
package day01;
import day01.service.MenuInfoService;
import day01.service.impl.MenuInfoServiceImpl;
import day01.vo.MenuInfoVO;
import org.junit.Before;
import org.junit.Test;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class Test01List {
MenuInfoService menuInfoService;
@Before
public void init(){
menuInfoService = new MenuInfoServiceImpl();
}
@Test
public void test01(){
// menuInfoService = new MenuInfoServiceImpl();
List<MenuInfoVO> list = menuInfoService.queryMenuInfo();
System.out.println("list="+list);
}
//对List集合操作
@Test
public void test普通for循环(){
List<MenuInfoVO> list = menuInfoService.queryMenuInfo();
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
}
@Test
public void test增强for循环(){
List<MenuInfoVO> list = menuInfoService.queryMenuInfo();
for (MenuInfoVO mv:list) {
System.out.println(mv);
}
}
@Test
public void test迭代器(){
List<MenuInfoVO> list = menuInfoService.queryMenuInfo();
Iterator<MenuInfoVO> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
@Test
public void test自身迭代器(){
List<MenuInfoVO> list = menuInfoService.queryMenuInfo();
ListIterator<MenuInfoVO> it = list.listIterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
@Test
public void testLambda表达式(){
List<MenuInfoVO> list = menuInfoService.queryMenuInfo();
list.forEach(e -> System.out.println(e));
}
@Test
public void testStream流(){
List<MenuInfoVO> list = menuInfoService.queryMenuInfo();
list.stream().forEach(e -> System.out.println(e));
}
}
删除List集合元素
package day01;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Iterator;
/**
* 遍历List集合的方式
*/
public class Test01List删除 {
ArrayList<Integer> list = new ArrayList<>();
@Before
public void init(){
list.add(1);
list.add(3);
list.add(3);
list.add(5);
list.add(7);
list.add(9);
}
@Test
public void test普通for循环删除() {
for (int i = 0; i < list.size(); i++) {
if (list.get(i) == 3) {
list.remove(i);
i--; //不加i-- 就只能删除一个3
}
}
System.out.println(list);//[1, 3, 5, 7, 9]
}
@Test
public void test增强for循环(){
for (int mv:list) {
if(mv==3){
list.remove(mv);//ConcurrentModificationException 并发修改异常
}
}
}
@Test
public void test迭代器(){
Iterator<Integer> it = list.iterator();
while(it.hasNext()){
if(it.next()==3){
list.remove(it.next());//ConcurrentModificationException 并发修改异常
// it.remove();//删除成功
}
}
System.out.println(list);
}
}
匿名内部类
package day01;
/*
* 匿名内部类:没有名字的内部类
* 前提 :
* 要求要先存在一个类(可以是普通类,抽象类,接口)
*语法:
* new 接口名或者类名(){重写方法};
*注意:
* 匿名内部类只针对重写一个方法的时候再去使用
*如果有多个方法要重写,最好用有名字的内部类
*/
public class Test03匿名内部类 {
public static void main(String[] args) {
Outer4 out = new Outer4();
out.method();//print
}
}
//1.要求要先存在一个类(可以是普通类,抽象类,接口)
interface Inter{//接口
void print();//接口中的抽象方法
void show();//接口中的抽象方法
}
//外部类
class Outer4{
//定义有名字的内部类实现Inter接口并重写它的抽象方法
// class Inner4 implements Inter{
// @Override
// public void print() {
// System.out.println("print");
// }
// }
//调用print方法
public void method() {
//通过有名字的内部类,去调用print方法
// Inner4 in = new Inner4();
// in.print();
// new Inner4().print();
//直接通过匿名内部类去调用print方法
// new Inter();//接口不能实例化
//变相的对接口进行实例化,相当于创建接口的子类对象
//其实就是{}匿名的类实现了Inter接口
//父类引用指向子类对象(而这里的子类对象没有名字)
Inter i = new Inter(){
@Override
public void print() {
System.out.println("print");
}
@Override
public void show() {
System.out.println("show");
}
//注意:如果子类对象中有独有的方法
public void Dy() {
System.out.println("Inter子类对象独有的方法.....");
}
};//因为匿名内部类没有名字,所以直接new出来以后,直接调就是了
i.print();
i.show();
// 因为子类对象没有名字,{},所以匿名内部类在多态中,是无法向下造型
// i.Dy();//Dy成员方法,是编译看=左边(父类),父类没有Dy所以报错
//思考一个问题:如果Inter接口中有2个方法,该如何调用???
//步骤:1.在Inter里面追加一个show方法
// 2.在Outer外部类的成员方法method里面再new匿名内部类并调用show方法
/*这种写法很明显很不合理
* new Inter(){
@Override
public void print() {
System.out.println("print");
}
@Override
public void show() {
System.out.println("show");
}
}.show();*/
}
}
匿名内部类练习
package day01;
/*
* 匿名内部类练习
*/
public class Test04匿名内部类练习 {
public static void main(String[] args) {
// 如何调用CatDemo中的method方法
CatDemo cd = new CatDemo();
//要求匿名内部类的方式来调用method()
Cat c = new Cat() {
@Override
public void eat() {
System.out.println("猫吃饭");
}};
cd.method(c);//c就是匿名内部类的对象
//使用lambda表达式,
//但是这里lambda表达式的父类应该是一个接口,不能是抽象类
/*cd.method(()->{
System.out.println("猫吃饭");
});*/
}
}
//父类
abstract class Cat {
// 抽象方法
public abstract void eat();
}
class CatDemo {
public void method(Cat c) {// 父类引用Cat c做参数 Cat c = new 匿名内部类;
c.eat();
}
}
Lambda表达式
package basic.day16;
import java.util.Timer;
import java.util.TimerTask;
/*
由来:
* 在了解Lambda之前,我们先回顾一下Java的方法。
* java方法格式:
修饰符 返回值类型 方法名(参数类型 参数名1,参数类型 参数名2...){
方法体语句;
return 返回值;
}
而Lambda表达式中我们只需要关心:参数列表 方法体,也就是修饰符,返回值类型,方法名都可以不要。
为什么呢?先了解什么是函数式接口
要了解Lambda表达式,首先需要了解什么是函数式接口,
函数式接口定义:一个接口有且只有一个抽象方法
比如Runnable接口就只有一个run方法
而我们之前写的Timer定时器里面的TimerTask则不是函数式接口,因为它是一个抽象类,里面不止一个方法
注意:
如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口
如果我们在某个接口上声明了 @FunctionalInterface 注解,
那么编译器就会按照函数式接口的定义来要求该接口,
这样如果有两个抽象方法,程序编译就会报错的。
所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解。
加上就会编译器就会自动进行检测的。
语法:
基本语法: (parameters) -> expression
或
(parameters) ->{ statements; }
paramaters
类似方法中的形参列表,这里的参数是函数式接口里的参数。
这里的参数类型可以明确的声明也可不声明而由JVM隐含的推断。
另外当只有一个推断类型时可以省略掉圆括号。
->
可理解为“被用于”的意思
方法体
可以是表达式也可以代码块,是函数式接口里方法的实现。
代码块可返回一个值或者什么都不反回,这里的代码块块等同于方法的方法体。
如果是表达式,也可以返回一个值或者什么都不反回。
案例如下:
*/
public class Demo01Lambda表达式 {
public static void main(String[] args) {
//===============================常规写法======================================
/*Cat cat = new Cat();
//创建 Thread 对象,把 cat 对象(实现了 Runnable ),放入了Thread
Thread t = new Thread(cat);
t.start();//调用start方法启动线程
*/
//===============================简写======================================
/*new Thread(new Runnable() {
@Override
public void run() {
while(true) {
System.out.println("瞄...");
//休眠1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();*/
//===============================Lambda写法======================================
//既然是匿名方法,把new Runnable都可以不要了
/*new Thread(()->{
while(true) {
System.out.println("瞄...");
//休眠1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();*/
}
}
class Cat implements Runnable{ //通过实现Runnable接口来实现
@Override
public void run() {
while(true) {
System.out.println("瞄...");
//休眠1秒
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
package basic.day16;
/*
Lambda注意:
如果一个接口只有一个抽象方法,那么该接口就是一个函数式接口
如果我们在某个接口上声明了 @FunctionalInterface 注解,
那么编译器就会按照函数式接口的定义来要求该接口,
这样如果有两个抽象方法,程序编译就会报错的。
所以,从某种意义上来说,只要你保证你的接口中只有一个抽象方法,你可以不加这个注解。
加上编译器就会自动进行检测的。
java方法分类:
无参无返回值
无参有返回值
有参无返回值
有参有返回值
函数式编程接口:
无参无返回值
无参有返回值
有参无返回值
有参有返回值
Lambda表达式的语法还可以精简,显得非常有逼格,但是可读性就非常差。
参数类型可以省略,如果需要省略,每个参数的类型都要省略。
参数的小括号里面只有一个参数,那么小括号可以省略
如果方法体当中只有一句代码,那么大括号可以省略
如果方法体中只有一条语句,其是return语句,那么大括号可以省略,且去掉return关键字
*/
public class Demo02Lambda表达式 {
public static void main(String[] args) {
//早期这里怎么写?要么,创建Dog的子类,要么用匿名对象
/*Dog dog = new Dog(){
@Override
public void eat() {
System.out.println("狗吃饭");
}
};*/
//无参无返回值
//现在用Lambda表达式写,既然是匿名,new Dog就都可以不要了
Dog dog = ()->{System.out.println("狗吃饭");};
dog.eat();//狗吃饭
//无参有返回值
Person p = ()->{return 23;};
int age = p.getAge();
System.out.println("age="+age);//age=23
//有参无返回值
// Girl g = (name)->{
Girl g = name->{//参数的小括号里面只有一个参数,那么小括号可以省略
//如果方法体当中只有一句代码,那么大括号可以省略
System.out.println("我的名字叫"+name);
};
g.say("Andy");
//有参有返回值
// Sum b = (int x,int y)->{//参数类型可以省略,如果需要省略,每个参数的类型都要省略。
Sum b = (x,y)->{
return x+y;
};
int sum = b.sum(2, 3);
System.out.println("sum="+sum);//sum=5
b = (x,y)->x+y;//如果方法体中只有一条语句,其是return语句,那么大括号可以省略,且去掉return关键字
sum = b.sum(100, 200);//300
System.out.println("sum="+sum);//sum=300
}
}
//函数式编程接口:
//无参无返回值
@FunctionalInterface
interface Dog{
//public可以省略不写,为什么???因为接口中都是抽象方法,public就可以不写
void eat();
// public void sleep();//打开,Dog接口就会报错
}
//无参有返回值
@FunctionalInterface
interface Person{
int getAge();
}
//有参无返回值
@FunctionalInterface
interface Girl {
void say(String name);
}
//有参有返回值
interface Sum{
int sum(int x,int y);
}
package basic.day16;
/*
* Lambda 表达式中存在变量捕获 ,了解了变量捕获之后,我们才能更好的理解Lambda 表达式的作用域 。
* Java当中的匿名类中,会存在变量捕获。
* Lambda的变量捕获:
* Lambda的变量捕获,同样也是不能捕获放生改变的,如果发生改变就会报错。
*/
public class Demo03变量捕获 {
public static void main(String[] args) {
// Outer o = new Outer();
// o.method();//a=10
int age = 23;
Man man = ()->{
// age = 18;//Lambda的变量捕获,同样也是不能捕获放生改变的,如果发生改变就会报错。
return age;
};
int ManAge = man.getAge();
System.out.println("ManAge="+ManAge);//ManAge=23
}
}
//1.要求要先存在一个类(可以是普通类,抽象类,接口)
interface Inter {// 接口
public void print();// 接口中的抽象方法
}
class Outer {
public void method() {
int a = 10;
Inter i = new Inter() {
//如果不修改tmp就可以运行,有的书上会说匿名内部中只能是常量。
//其实是在运行过程中没有发生改变的量!
//a = 20;//匿名内部类中:一定是程序在运行的过程当中没有发生改变的量
@Override
public void print() {
System.out.println("a="+a);
}
};
i.print();
}
}
@FunctionalInterface
interface Man{
int getAge();
}
Lambda操作List
package day02;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
/*
* 为了能够让Lambda和Java的集合类集更好的一起使用,集合当中,
* 也新增了部分接口,以便与Lambda表达式对接。
* 要用Lambda遍历集合就一定要看懂源码。
* Collection
* removeif() spliterator() stream() parllelStream() forEach()
* List
* replaceAll() sort()
* Map
* getorDefault()forEach()replaceAll()putIfAbsent()remove()replace()
* computeIfAbsent() computeIfPresent()compute()merge()
* 总结
Lambda表达式的优点很明显,在代码层次上来说,使代码变得非常的简洁。缺点也很明显,代码不易读
优点:
代码简洁,开发迅速
方便函数式编程
非常容易进行并行计算
Java 引入 Lambda,改善了集合操作
缺点:
代码可读性变差
在非并行计算中,很多计算未必有传统的 for 性能要高
不容易进行调试
*/
public class Demo04Lambda操作List {
/*
* List的forEach方法遍历集合,先得看一下源码。
* 如果要打印元素,它需要的实现Consumer接口,同时要实现重写accept()方法,
* 它会把数组里的每一个元素都交给,accept()方法。
*/
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("刘备");
list.add("关羽");
list.add("张飞");
list.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
System.out.println("=====================================================================================");
//因为上面的Conumer是一个函数式接口,所以可以使用lambda简写
list.forEach(s-> System.out.println(s));
}
}