Springboot快速入门(七)——巩固知新(一)
2022-10-05 20:51:06

1. Map集合、Collection中的集合与List集合的区别

private Map<String,Object> maps;

Map<Integer, Department>

Collection<Department>

private List<Object> list;

  • Map是一个双列集合,存放形式为键值对(key-value)。元素是成对存在的(理解为夫妻)。每个元素由键与值两部分组成,通过键可以找对所对应的值,每个键只能对应一个值,且不能包含重复的键,值可以重复。

    • HashMap
    • LinkedHashMap
  • Collection中的集合,称为单列集合。元素是孤立存在的(理解为单身),向集合中存储元素采用一个个元素的方式存储。

  • List 接口是继承于 Collection接口并定义 一个允许重复项的有序集合。该接口不但能够对列表的一部分进行处理,还添加了面向位置的操作。

    0cb41b7d632b212b032978fd73633d9a.png

2. List<T> 、List<?>、List<Object>、List<E>、List<U>的区别

List<T>、List<?>、List<Object>这三者都可以容纳所有的对象,但使用的顺序应该是首选List<T>,次之List<?>,最后选择List<Object>

  • List<T>表示的是List集合中的元素都为T类型,具体类型在运行期决定。可以进行诸如add、remove等操作,因为它的类型是固定的T类型,在编码期不需要进行任何的转型操作
  • List<?>表示的是任意类型。是只读类型的,不能进行增加、修改操作,但是却可以删除元素,比如执行remove、clear等方法。List<?>读取出的元素都是Object类型的,需要主动转型,所以它经常用于泛型方法的返回值
  • List<Object>表示List集合中的所有元素为Object类型,Object是所有类的父类,所以List<Object>也可以容纳所有的类类型

List<E> List<T> Map<K, V>

E是Element首字母的缩写,E通常用来表示集合类型中的元素类型

  • 例如List接口的定义:

    1
    public interface List<E> extends Collection<E> 
  • K,V是Key, Value的首字符缩写,通常用来表示Map的键值类型

    1
    public interface Map<K, V>
  • <T> T 表示“返回值”是一个泛型,传递什么类型,就返回什么类型

    1
    2
    3
    4
    5
    6
    private <T> T getListFirst(List<T> data){
    if(data == null || data.size() == 0){
    return null;
    }
    return data.get(0);
    }

3. @AllArgsConstructor和@NoArgsConstructor

  • lombok

    在开发过程中,lombok的使用极大的简化了实体类的开发工作。在通常情况下我们需要手动去建立getter和setter方法,构造函数之类的,而lombok的作用就是能够在我们编译源码的时候自动帮我们生成这些方法,这些都是通过注解的方式:如:@builder, @NoArgsConstructor, @AllArgsConstructor, @Getter, @Setter, @data等。

  • @AllArgsContructor: 会生成一个包含所有变量的构造方法,默认生成的方法是 public 的(有参)

  • @NoArgsConstructor : 生成一个无参数的构造方法(无参)

  • @RequiredArgsConstructor: 会生成一个包含常量,和标识了NotNull的变量的构造方法。生成的构造方法是私有的private。(可能带参数也可能不带参数)

4. @Repository

  • 通过看源代码可以发现,@Controller@Service还是@Repository全部都被@Component注解了。在Spring源码中,注解会一级一级向上递归搜索,搜索所有的注解信息,即被以上注解注解的类在Spring看来都会包含@Component注解。综上所述,本质上@Controller@Service@Repository@Component本质都是@Component即Spring容器中的一个组件

  • 添加这三个注解的主要目的就是为了逻辑分层

    • @Repository——对应数据访问层Bean
    • @Service——业务Bean
    • @Controller——控制层(展示层)Bean
  • @Repository的功能是将数据访问层(Dao层)的类识别并标注为SpringBean,具体方式为直接在DAO类上标注即可,且只能标注在DAO类上。因为该注解的作用不只是将类识别为Bean,同时它还能将所标注的类中抛出的数据访问异常封装为 Spring 的数据访问异常类型。 Spring本身提供了一个丰富的并且是与具体的数据访问技术无关的数据访问异常结构,用于封装不同的持久层框架抛出的异常,使得异常独立于底层的框架。

    1
    2
    3
    4
    5
    //部门dao
    @Repository
    public class DepartmentDao {
    .........
    }
    1
    2
    3
    4
    5
    //员工dao
    @Repository
    public class EmployeeDao {
    ..........
    }

5. 配置Dao层

5.1 模拟数据库中的数据

1
2
3
4
5
6
7
//模拟数据库中的数据
private static Map<Integer, Department> departments=null;
static {
departments=new HashMap<Integer, Department>(); //创建一个部门表
departments.put(101,new Department(101,"教学部"));
..............................................
}
1
2
3
4
5
6
7
8
9
10
11
12
13
//模拟数据库中的数据
private static Map<Integer, Employee> employees=null;

//员工有所属的部门
@Autowired(required = false)
private DepartmentDao departmentDao;

static {
employees=new HashMap<Integer, Employee>(); //创建一个部门表
employees.put(101,new Employee(1001,"AA","A123456789@qq.com",1,new Department(101,"教学部")));
...............................................

}

5.2 static用法

  • static关键字

    • 在类中,用static声明的成员变量为静态成员变量,也成为类变量。类变量的生命周期和类相同,在整个应用程序执行期间都有效。
    • static修饰的成员变量和方法,从属于类;普通变量和方法从属于对象;静态方法不能调用非静态成员,编译会报错。
    • 用途:方便在没有创建对象的情况下进行调用(方法/变量),被static关键字修饰的方法或者变量不需要依赖于对象来进行访问,只要类被加载了,就可以通过类名去进行访问
  • static方法:

    • static方法也成为静态方法,由于静态方法不依赖于任何对象就可以直接访问,因此对于静态方法来说,是没有this的,因为不依附于任何对象,既然都没有对象,就谈不上this了。
    • 并且由于此特性,在静态方法中不能访问类的非静态成员变量和非静态方法,因为非静态成员变量和非静态方法都必须依赖于具体的对象才能被调用。但是在非静态成员方法中是可以访问静态成员方法和静态成员变量。
  • static变量:

    • 称为静态变量。静态变量被所有对象共享,在内存中只有一个副本,在类初次加载的时候才会初始化。
    • 非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
  • static块:

    静态初始化块,用于类的初始化操作,其作用就是提升程序性能。很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。静态初始化块可以置于类中的任何地方,类中可以有多个静态初始化块。在类初次被加载时,会按照静态初始化块的顺序来执行每个块,并且只会执行一次。

    例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    class Person{
    private Date birthDate;

    public Person(Date birthDate) {
    this.birthDate = birthDate;
    }

    boolean isBornBoomer() {
    Date startDate = Date.valueOf("1946");
    Date endDate = Date.valueOf("1964");
    return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
    }
    }

    isBornBoomer是用来这个人是否是1946-1964年出生的,而每次isBornBoomer被调用的时候,都会生成startDate和birthDate两个对象,造成了空间浪费,如果改成这样效率会更好:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Person{
    private Date birthDate;
    private static Date startDate,endDate;
    static{
    startDate = Date.valueOf("1946");
    endDate = Date.valueOf("1964");
    }

    public Person(Date birthDate) {
    this.birthDate = birthDate;
    }

    boolean isBornBoomer() {
    return birthDate.compareTo(startDate)>=0 && birthDate.compareTo(endDate) < 0;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    static {
    departments=new HashMap<Integer, Department>(); //创建一个部门表
    departments.put(101,new Department(101,"教学部"));
    departments.put(102,new Department(102,"市场部"));
    departments.put(103,new Department(103,"教研部"));
    departments.put(104,new Department(104,"运营部"));
    departments.put(105,new Department(105,"后勤部"));
    }

5.3 哈希表

departments = new HashMap<Integer, Department>(); // 创建一个部门

employees = new HashMap<>(); // 创建一个部

  • 什么是哈希表(Hash Table)?

    • 哈希方法

      在对象的存储位置和对象的关键属性(k)之间建立一个特定的对应关系(f),使每个对象与一个唯一的存储位置相对应。在查找时,只要根据待查对象的关键属性 k 计算f(k)的值即可。如果此对象在集合中,则必定在存储位置 f(k)上,因此不需要与集合中的其他元素进行比较,一次存取便能得到所需要的记录。

    • 按照哈希方法思想建立的表为哈希表!

      数组利于元素的查找,链表利于元素的插入和删除,而哈希表既能快速地查找又能高效地插入删除元素。哈希表是由一块地址连续的数组空间构成的,其中每个数组都是一个链表,哈希表可以被认为就是链表的数组,示意图如下:

    • 哈希表是一种数据结构,哈希函数是支撑哈希表的一类函数
  • 什么是Map和HashMap?

    • Map是映射、地图的意思,在java中Map表示一种把 k 映射到 v 的数据类型

    • Map<K, V>是一个以 键值(Key)-数值(Value) 对应形式存储数据的接口。 在数组中我们是通过数组下标来对其内容索引的,而在Map中我们通过对象来对对象进行索引,用来索引的对象叫做key,其对应的对象叫做value

    • HashMap是Map<K, V>的实现类。(Hashtable也是Map<K, V>的实现类) HashMap存储数据采用哈希表结构 ,元素的存取顺序不能保证一致。但键值是唯一、不重复的。

    • HashMap 的 key 与 value 类型可以相同也可以不同,可以是字符串(String)类型的 key 和 value,也可以是整型(Integer)的 key 和字符串(String)类型的 value。

  • HashMap的用法

    参考链接:Java HashMap | 菜鸟教程 (runoob.com)

    • 添加元素

      put()方法

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      // 引入 HashMap 类      
      import java.util.HashMap;

      public class RunoobTest {
      public static void main(String[] args) {
      // 创建 HashMap 对象 Sites
      HashMap<Integer, String> Sites = new HashMap<Integer, String>();
      // 添加键值对
      Sites.put(1, "Google");
      Sites.put(2, "Runoob");
      System.out.println(Sites);
      }
      }

      输出结果:

      1
      {1=Google, 2=Runoob}
    • 创建字符串(String)类型的key和value

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      // 引入 HashMap 类      
      import java.util.HashMap;

      public class RunoobTest {
      public static void main(String[] args) {
      // 创建 HashMap 对象 Sites
      HashMap<String, String> Sites = new HashMap<String, String>();
      // 添加键值对
      Sites.put("one", "Google");
      Sites.put("two", "Runoob");
      }
      }

      输出结果:

      1
      {one=Google, two=Runoob}
    • 访问元素

      get(key)方法来获取 key 对应的 value:

      1
      2
      3
      4
      5
      	// 添加键值对
      Sites.put(1, "Google");
      Sites.put(2, "Runoob");
      Sites.put(3, "Taobao");
      System.out.println(Sites.get(3));

      输出结果:

      1
      Taobao
    • 删除元素

      remove(key)方法来删除 key 对应的键值对(key-value)

      1
      2
      3
      4
      5
      6
      7
      // 添加键值对
      Sites.put(1, "Google");
      Sites.put(2, "Runoob");
      Sites.put(3, "Taobao");
      Sites.put(4, "Zhihu");
      Sites.remove(4);
      System.out.println(Sites);

      输出结果:

      1
      {1=Google, 2=Runoob, 3=Taobao}

      删除所有键值对(key-value)可以使用 clear方法

      1
      2
      3
      4
      5
      6
      7
      // 添加键值对
      Sites.put(1, "Google");
      Sites.put(2, "Runoob");
      Sites.put(3, "Taobao");
      Sites.put(4, "Zhihu");
      Sites.clear();
      System.out.println(Sites);

      输出结果:

      1
      {}
    • 计算大小

      size()方法,计算 HashMap 中的元素数量

      1
      2
      3
      4
      5
      6
      // 添加键值对
      Sites.put(1, "Google");
      Sites.put(2, "Runoob");
      Sites.put(3, "Taobao");
      Sites.put(4, "Zhihu");
      System.out.println(Sites.size());

      输出结果:

      1
      4
    • 迭代HashMap

      for-each 来迭代 HashMap 中的元素

      如果你只想获取 key,可以使用 keySet() 方法,然后可以通过 get(key) 获取对应的 value,如果你只想获取 value,可以使用 values() 方法。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      // 添加键值对
      Sites.put(1, "Google");
      Sites.put(2, "Runoob");
      Sites.put(3, "Taobao");
      Sites.put(4, "Zhihu");
      // 输出 key 和 value
      for (Integer i : Sites.keySet()) {
      System.out.println("key: " + i + " value: " + Sites.get(i));
      }
      // 返回所有 value 值
      for(String value: Sites.values()) {
      // 输出每一个value
      System.out.print(value + ", ");
      }

      输出结果:

      1
      2
      3
      4
      5
      key: 1 value: Google
      key: 2 value: Runoob
      key: 3 value: Taobao
      key: 4 value: Zhihu
      Google, Runoob, Taobao, Zhihu,

6. ViewControllerRegistry和addViewController(首页实现)

ViewControllerRegistry 创建简单的路由

在项目开发过程中,经常会涉及页面跳转问题,而且这个页面跳转没有任何业务逻辑过程,只是单纯的路由过程 ( 点击一个按钮跳转到一个页面 ) 。Spring MVC 中提供了一个方法,可以把类似代码统一管理,减少类似代码的书写(根据项目要求,或者代码规范,不一定非要统一管理页面跳转,有时会把相同业务逻辑的代码放在一个类中)。

常规写法:

1
2
3
4
@RequestMapping("/toview")
public String view(){
return "view";
}

在继承WebMvcConfigurerAdapterDemoMVCConfig类中重载addViewControllers:

1
2
3
4
5
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/toview").setViewName("/view");
//添加更多
}

addViewController

当项目中涉及大量的页面跳转,我们可以使用addViewControllers方法实现无业务逻辑跳转,从而减少控制器代码的编写。addViewControllers方法可以实现将一个请求直接映射为视图,不需要编写控制器来实现,从而简化了页面跳转。

7. 页面国际化

public Locale resolveLocale(HttpServletRequest request) {。。。}

7.1 Locale

Locale locale = Locale.getDefault(); // 如果没有获取到就使用系统默认的,获得此Java虚拟机当前线程默认的语言环境值

Locale对象表示了一个特定的地理,政治或文化区域。需要使用到Locale执行其任务的操作称为区域设置敏感,并使用Locale为用户定制信息。

获取当前Java虚拟机线程默认的国家和语言信息

1
2
3
4
5
6
7
8
9
10
String country = defaultLocale.getCountry();//返回国家地区代码

String language = defaultLocale.getLanguage();//返回国家的语言

String displayCountry = defaultLocale.getDisplayCountry();//返回适合向用户显示的国家信息

String displayLanaguage = defaultLocale.getDisplayLanaguage();//返回适合向用户展示的语言信息

String displayName = defaultLocale.getDisplayName();//返回适合向用户展示的语言环境名

若当前Java虚拟机线程所处的操作系统的区域语言设置为“中文(简体,中国)”,则上边的运行结果将会是:

1
2
3
4
5
6
7
8
9
CN

zh

中国

中文

中文(中国)
  • 使用ResourceBundle读取国际化资源文件

    ResourceBundle资源包包含特定语言环境的对象。使用其可以加载并读取语言环境资源

    • 轻松的本地化或者翻译成不同的语言
    • 一次处理多个语言环境
    • 以后可以轻松的进行修改,一遍支持更多的语言环境

7.2 HttpServletRequest 和HttpServletResponse

Servlet 处理 HTTP 请求的流程如下:

  1. Servlet 容器接收到来自客户端的 HTTP 请求后,容器会针对该请求分别创建一个 HttpServletRequest 对象和 HttpServletReponse 对象。
  2. 容器将 HttpServletRequest 对象和 HttpServletReponse 对象以参数的形式传入 service() 方法内,并调用该方法。
  3. 在 service() 方法中 Servlet 通过 HttpServletRequest 对象获取客户端信息以及请求的相关信息。
  4. 对 HTTP 请求进行处理。
  5. 请求处理完成后,将响应信息封装到 HttpServletReponse 对象中。
  6. Servlet 容器将响应信息返回给客户端。
  7. 当 Servlet 容器将响应信息返回给客户端后,HttpServletRequest 对象和 HttpServletReponse 对象被销毁。

通过以上流程可以看出, HttpServletRequest 和 HttpServletReponse 是 Servlet 处理 HTTP 请求流程中最重要的两个对象。HttpServletRequest 对象用于封装 HTTP 请求信息,HttpServletReponse 对象用于封装 HTTP 响应信息。

7.3 request.getParameter(“l”);

7.4 StringUtils

8.什么时候用setXX(),什么时候用getXX()?什么时候用@RequestMapping(),什么时候用@PostMapping(),什么时候用@GetMapping()?

9. Model model

10. HttpSession

session.invalidate();

11.request.getRequestDispatcher(“/index.html”).forward(request,response);