家居网购项目(一)

文章目录

    • 1.前置知识
        • 1.项目开发阶段
        • 2.Java经典三层架构
        • 3.项目具体分层(包方案)
        • 4.MVC
    • 2.开发环境搭建
        • 1.新建普通javaweb项目,导入jar包
        • 2.创建项目结构
        • 3.搭建前端页面
    • 3.会员注册前端js校验
        • 1.需求分析
        • 2.代码
          • login.html
        • 3.结果
        • 4.调试阶段
          • 1.验证信息一闪而过
            • 原因:
    • 4.会员注册后端
        • 1.需求分析
        • 2.程序框架图
        • 3.数据库表设计
          • 会员表设计
        • 4.建立javabean与表的映射
          • Member.java
        • 5.引入德鲁伊连接池工具类
          • JDBCUtilsByDruid.java
        • 6.引入BasicDao
          • BasicDao.java
        • 7.编写druid配置文件
          • druid.properties
          • 编写测试类,测试连接
            • JDBCUtilsByDruidTest.java
        • 8.编写MemberDao
          • 1.MemberDao.java
          • 2.MemberDaoImpl.java
          • 3.进行单元测试
            • MemberDaoTest.java
            • 测试结果:
        • 9.编写MemberService
          • 1.MemberService.java
          • 2.MemberServiceImpl.java
          • 3.进行单元测试
            • MemberServiceTest.java
            • 测试结果
        • 10.login.html优化
          • 1.设置base路径
          • 2.修改所有../../为空
          • 3.运行login.html
          • 4.修改前端js验证通过逻辑
        • 11.编写RegisterServlet
          • 1.修改login.html
          • 2.引入两个前端页面
          • 3.修改两个页面的base路径
          • 4.RegisterServlet.java
        • 12.注册功能展示
          • 注册界面
          • 注册成功
          • 注册失败返回首页

1.前置知识

1.项目开发阶段

image-20240204174457829

2.Java经典三层架构

image-20240204181049612

3.项目具体分层(包方案)

image-20240204181314291

4.MVC

image-20240204183543670

image-20240204182252025

2.开发环境搭建

1.新建普通javaweb项目,导入jar包

image-20240204185053583

2.创建项目结构

image-20240204185122184

3.搭建前端页面

image-20240204191613545

3.会员注册前端js校验

1.需求分析

image-20240204193615650

2.代码
login.html
<!--    引入jquery--><script type="text/javascript" src="../../script/jquery-3.6.0.min.js"></script><script>$(function () { //页面加载//绑定注册按钮点击事件$("#sub-btn").click(function () {//获取usernamevar usernameVal = $("#username").val();// alert(usernameVal);//1.编写正则表达式验证用户名var usernamePattern = /^\w{6,10}$/;//验证if (!usernamePattern.test(usernameVal)) {//使用前端给的span展示错误信息$("span[class='errorMsg']").text("用户名格式不对,需要6到10个字符");return false; //不提交}//2.编写正则表达式验证密码//获取密码var passwordVal =  $("#password").val();//正则var passwordPattern = /^\w{6,10}$/;//验证if (!passwordPattern.test(passwordVal)) {//使用前端给的span展示错误信息(普通选择器)$("span.errorMsg").text("密码格式不对,需要6到10个字符");return false;}//3.两次密码需要相同//获取第二次输入的密码var repwdVal = $("#repwd").val();//与第一次的密码进行比较if (repwdVal != passwordVal) {$("span.errorMsg").text("两次密码不相同!");return false;}//4.验证邮箱//得到邮箱var emailVal = $("#email").val();//正则var emailPattern = /^[\w-]+@([a-zA-Z]+\.)+[a-zA-Z]+$/; //java中是两个斜杠,js是一个//验证if(!emailPattern.test(emailVal)) {$("span.errorMsg").text("电子邮件格式不对!");return false;}//暂时先算过关,先不提交$("span.errorMsg").text("验证通过!");return false;})})</script>
3.结果

image-20240204201041909

4.调试阶段
1.验证信息一闪而过
原因:

没有在js中return false,阻止表单提交

4.会员注册后端

1.需求分析

image-20240204201601053

2.程序框架图

image-20240204202530923

3.数据库表设计
会员表设计
-- 创建家具网购数据库表
CREATE DATABASE home_furnishing;USE home_furnishing;-- 创建会员表
CREATE TABLE `member` (
`id` INT PRIMARY KEY auto_increment,
`username` VARCHAR(32) NOT NULL UNIQUE,
`password` VARCHAR(32) NOT NULL,
`email` VARCHAR(64)
)
-- 测试数据
INSERT INTO member VALUES(NULL, 'admin', MD5('admin'), '1721469477@qq.com')
4.建立javabean与表的映射
Member.java
package com.sxs.furns.entity;/*** @author 孙显圣* @version 1.0*/
public class Member {private Integer id;private String username;private String password;private String email;//无参构造方法!!!用于反射调用public Member(){}public Member(Integer id, String username, String password, String email) {this.id = id;this.username = username;this.password = password;this.email = email;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}
}
5.引入德鲁伊连接池工具类
JDBCUtilsByDruid.java
package com.sxs.furns.utils;import com.alibaba.druid.pool.DruidDataSourceFactory;import javax.sql.DataSource;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;/*** @author 孙显圣* @version 1.0*/
public class JDBCUtilsByDruid {//静态数据源引用(jdbc的接口)private static DataSource dataSource;//静态代码块,在类加载时为数据源引用赋值static {//1.读取配置文件Properties properties = new Properties();try {//使用类加载器读取配置文件,配置文件放在resources文件夹//这句话的意思是,使用JDBCUtilsByDruid类读取在resources文件夹下的配置文件,druid.propertiesproperties.load(JDBCUtilsByDruid.class.getClassLoader().getResourceAsStream("druid.properties"));} catch (IOException e) {throw new RuntimeException(e);}//2.使用配置文件,创建德鲁伊数据源对象try {dataSource = DruidDataSourceFactory.createDataSource(properties);} catch (Exception e) {throw new RuntimeException(e);}}//编写getConnection方法public static Connection getConnection() {try {return dataSource.getConnection();} catch (SQLException e) {throw new RuntimeException(e);}}//把Connection对象放回连接池public static void close(ResultSet resultSet, Statement statement, Connection connection) {try {if (resultSet != null) {resultSet.close();}if (statement != null) {statement.close();}if (connection != null) {connection.close();}} catch (SQLException e) {throw new RuntimeException(e);}}}
6.引入BasicDao
BasicDao.java
package com.sxs.furns.dao;import com.sxs.furns.utils.JDBCUtilsByDruid;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.apache.commons.dbutils.handlers.ScalarHandler;import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;/*** @author 孙显圣* @version 1.0* 开发BasicDAO , 是其他DAO的父类*/
public class BasicDao<T> { //泛型指定具体类型private QueryRunner qr =  new QueryRunner();//开发通用的dml方法, 针对任意的表public int update(String sql, Object... parameters) {Connection connection = null;try {connection = JDBCUtilsByDruid.getConnection();int update = qr.update(connection, sql, parameters);return  update;} catch (SQLException e) {throw  new RuntimeException(e); //将编译异常->运行异常 ,抛出} finally {JDBCUtilsByDruid.close(null, null, connection);}}//返回多个对象(即查询的结果是多行), 针对任意表/**** @param sql sql 语句,可以有 ?* @param clazz 传入一个类的Class对象 比如 Actor.class* @param parameters 传入 ? 的具体的值,可以是多个* @return 根据Actor.class 返回对应的 ArrayList 集合*/public List<T> queryMulti(String sql, Class<T> clazz, Object... parameters) {Connection connection = null;try {connection = JDBCUtilsByDruid.getConnection();return qr.query(connection, sql, new BeanListHandler<T>(clazz), parameters);} catch (SQLException e) {throw  new RuntimeException(e); //将编译异常->运行异常 ,抛出} finally {JDBCUtilsByDruid.close(null, null, connection);}}//查询单行结果 的通用方法public T querySingle(String sql, Class<T> clazz, Object... parameters) {Connection connection = null;try {connection = JDBCUtilsByDruid.getConnection();return  qr.query(connection, sql, new BeanHandler<T>(clazz), parameters);} catch (SQLException e) {throw  new RuntimeException(e); //将编译异常->运行异常 ,抛出} finally {JDBCUtilsByDruid.close(null, null, connection);}}//查询单行单列的方法,即返回单值的方法public Object queryScalar(String sql, Object... parameters) {Connection connection = null;try {connection = JDBCUtilsByDruid.getConnection();return  qr.query(connection, sql, new ScalarHandler(), parameters);} catch (SQLException e) {throw  new RuntimeException(e); //将编译异常->运行异常 ,抛出} finally {JDBCUtilsByDruid.close(null, null, connection);}}}
7.编写druid配置文件
druid.properties
#key=value
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/home_furnishing?rewriteBatchedStatements=true
#url=jdbc:mysql://localhost:3306/girls
username=root
password=root
#initial connection Size
initialSize=10
#min idle connecton size
minIdle=5
#max active connection size
maxActive=50
#max wait time (5000 mil seconds)
maxWait=5000
编写测试类,测试连接
JDBCUtilsByDruidTest.java
package com.sxs.furns.test;import com.sxs.furns.utils.JDBCUtilsByDruid;
import org.junit.Test;import java.sql.Connection;/*** @author 孙显圣* @version 1.0*/
public class JDBCUtilsByDruidTest {@Testpublic void getConnection() {//测试连接Connection connection = JDBCUtilsByDruid.getConnection();System.out.println(connection);JDBCUtilsByDruid.close(null, null, connection);}
}

image-20240204210743090

8.编写MemberDao
1.MemberDao.java
package com.sxs.furns.dao;import com.sxs.furns.entity.Member;/*** @author 孙显圣* @version 1.0*/
public interface MemberDao {//1.通过用户名返回Memberpublic Member queryMemberByUsername(String username);//2.保存Member对象到数据库的member表public int saveMember(Member member);
}
2.MemberDaoImpl.java
package com.sxs.furns.dao.impl;import com.sxs.furns.dao.BasicDao;
import com.sxs.furns.dao.MemberDao;
import com.sxs.furns.entity.Member;/*** @author 孙显圣* @version 1.0*/
public class MemberDaoImpl extends BasicDao<Member> implements MemberDao {/*** 通过用户名,返回对应的Member* @param username 用户名* @return 对应的Member对象,如果没有则返回null*/@Overridepublic Member queryMemberByUsername(String username) {//查询单行数据Member member = querySingle("select * from member where username = ?", Member.class, username);return member;}/*** 保存一个会员* @param member 传入Member对象* @return 返回-1就是失败,返回其他的数字就是受影响的行数*/@Overridepublic int saveMember(Member member) {//获取member对象信息String username = member.getUsername();String password = member.getPassword();String email = member.getEmail();//插入到member表中return update("insert into member values(?,?,md5(?),?)", null, username, password, email);}
}
3.进行单元测试
MemberDaoTest.java
package com.sxs.furns.test;import com.sxs.furns.dao.MemberDao;
import com.sxs.furns.dao.impl.MemberDaoImpl;
import com.sxs.furns.entity.Member;
import org.junit.Test;/*** @author 孙显圣* @version 1.0*/
public class MemberDaoTest {private MemberDao memberDao = new MemberDaoImpl();@Testpublic void queryMemberByUsername() {if (memberDao.queryMemberByUsername("admin") == null) {System.out.println("该用户不存在");} else {System.out.println("该用户存在");}}@Testpublic void saveMember() {Member member = new Member(null, "jack", "123", "123@qq.com");if (memberDao.saveMember(member) == 1) {System.out.println("添加成功");} else {System.out.println("添加失败");}}
}
测试结果:

image-20240205095931244

image-20240205095853310

9.编写MemberService
1.MemberService.java
package com.sxs.furns.service;import com.sxs.furns.entity.Member;/*** @author 孙显圣* @version 1.0*/
public interface MemberService {//注册用户public boolean registerMember(Member member);//判断用户名是否存在public boolean isExistsUsername(String username);
}
2.MemberServiceImpl.java
package com.sxs.furns.service.impl;import com.sxs.furns.dao.MemberDao;
import com.sxs.furns.dao.impl.MemberDaoImpl;
import com.sxs.furns.entity.Member;
import com.sxs.furns.service.MemberService;/*** @author 孙显圣* @version 1.0*/
public class MemberServiceImpl implements MemberService {//定义一个MemberDao的属性MemberDao memberDao = new MemberDaoImpl();/*** 将注册用户信息插入到数据库中* @param member 注册用户信息* @return 成功返回true,失败返回false*/@Overridepublic boolean registerMember(Member member) {return memberDao.saveMember(member) == 1 ? true : false;}/*** 判断用户是否存在* @param username 用户名* @return 如果存在,返回true,否则返回false*/@Overridepublic boolean isExistsUsername(String username) {return memberDao.queryMemberByUsername(username) == null ? false : true;}
}
3.进行单元测试
MemberServiceTest.java
package com.sxs.furns.test;import com.sxs.furns.entity.Member;
import com.sxs.furns.service.MemberService;
import com.sxs.furns.service.impl.MemberServiceImpl;
import org.junit.Test;/*** @author 孙显圣* @version 1.0*/
public class MemberServiceTest {private MemberService memberService = new MemberServiceImpl();@Testpublic void registerMember() {Member member = new Member(null, "jack", "123", "123@qq.com");if (!memberService.registerMember(member)) {System.out.println("注册失败");} else {System.out.println("注册成功");}}@Testpublic void isExistsUsername() {if (!memberService.isExistsUsername("jack")) {System.out.println("用户不存在");} else {System.out.println("用户存在");}}
}
测试结果

image-20240205102436637

image-20240205102443364

10.login.html优化
1.设置base路径
<!--    base路径设置    --><base href="http://localhost:8080/jiaju_mail/">

image-20240205104237811

2.修改所有…/…/为空

image-20240205104843033

3.运行login.html

image-20240205104903148

4.修改前端js验证通过逻辑

image-20240205105045231

11.编写RegisterServlet
1.修改login.html

image-20240205105359729

2.引入两个前端页面

image-20240205140855281

3.修改两个页面的base路径

image-20240205140931909

4.RegisterServlet.java
package com.sxs.furns.web;import com.sxs.furns.entity.Member;
import com.sxs.furns.service.MemberService;
import com.sxs.furns.service.impl.MemberServiceImpl;import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;/*** @author 孙显圣* @version 1.0*/
@WebServlet(urlPatterns = "/registerServlet")
public class RegisterServlet extends HttpServlet {//定义属性private MemberService memberService = new MemberServiceImpl();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//接受用户注册信息String username = req.getParameter("username");String password = req.getParameter("password");String email = req.getParameter("email");//判断用户名是否在数据库中if (!memberService.isExistsUsername(username)) { //不在数据库中,可以注册Member member = new Member(null, username, password, email);//注册if (memberService.registerMember(member)) {//请求转发req.getRequestDispatcher("/views/member/register_ok.html").forward(req, resp);} else {//请求转发req.getRequestDispatcher("/views/member/register_fail.html").forward(req, resp);}} else {//返回注册页面req.getRequestDispatcher("/views/member/login.html").forward(req, resp);}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doGet(req, resp);}
}
12.注册功能展示
注册界面

image-20240205141051819

注册成功

image-20240205141105048

注册失败返回首页

image-20240205141249455

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/809767.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode 题目:两个总和

LeetCode 题目&#xff1a;两个总和 描述&#xff1a; 编写一个函数&#xff0c;输入为一个整数数组nums和一个目标整数target&#xff0c;要求找到数组中两个数的和等于target&#xff0c;并返回这两个数的索引。 函数定义&#xff1a; def two_sum(nums: List[int], targe…

Java多线程开发——基础篇

目录 1.基本概念 2.创建线程方式 2.1直接建立线程 2.2实现Runnable接口 3.3实现Callable接口 3.4 了解Future接口 Future模式主要角色及其作用 3.5实例化FutureTask类 3.实现线程安全 3.1定义 3.2不安全原因 3.3解决方案 3.4volatile与synchronized区别 4.极端情…

Docker 学习笔记(三):Centos7 中 Docker 使用,镜像、容器,以及操作等常用命令小结

一、前言 记录时间 [2024-4-7] 前置文章&#xff1a; Docker学习笔记&#xff08;一&#xff09;&#xff1a;入门篇&#xff0c;Docker概述、基本组成等&#xff0c;对Docker有一个初步的认识 Docker学习笔记&#xff08;二&#xff09;&#xff1a;在Linux中部署Docker&#…

(Java)数据结构——排序(第一节)堆排序+PTA L2-012 关于堆的判断

前言 本博客是博主用于复习数据结构以及算法的博客&#xff0c;如果疏忽出现错误&#xff0c;还望各位指正。 堆排序&#xff08;Heap Sort&#xff09;概念 堆排序是一种基于堆数据结构的排序算法&#xff0c;其核心思想是将待排序的序列构建成一个最大堆&#xff08;或最小…

从库延迟案例分析

背景介绍 近来一套业务系统&#xff0c;从库一直处于延迟状态&#xff0c;无法追上主库&#xff0c;导致业务风险较大。从资源上看&#xff0c;从库的CPU、IO、网络使用率较低&#xff0c;不存在服务器压力过高导致回放慢的情况&#xff1b;从库开启了并行回放&#xff1b;在从…

Python —— 简述

Houdini Python | 笔记合集 - 知乎 Houdini内置三大语言&#xff1a; 表达式&#xff0c;主要用于节点参数控制&#xff0c;可实现跨模块控制&#xff1b;vex&#xff0c;速度最快&#xff08;比表达式和Python快一个数量级&#xff09;&#xff0c;非常适合密集型计算环境&…

【React】Ant Design社区扩展库之分割面板:react-resizable-panels

主角&#xff1a;react-resizable-panels 简介&#xff1a;来之Ant Design官方文档社区精选组件 1、效果 2、环境 react-resizable-panels: ^2.0.16next: 14.1.3react: ^18 3、安装 # npm npm install react-resizable-panels# yarn yarn add react-resizable-panels# pnpm …

Docker学习笔记(二):在Linux中部署Docker(Centos7下安装docker、环境配置,以及镜像简单使用)

一、前言 记录时间 [2024-4-6] 前置文章&#xff1a;Docker学习笔记&#xff08;一&#xff09;&#xff1a;入门篇&#xff0c;Docker概述、基本组成等&#xff0c;对Docker有一个初步的认识 在上文中&#xff0c;笔者进行了Docker概述&#xff0c;介绍其历史、优势、作用&am…

《三》按钮---PushButton和信号槽机制

QPushButton按钮用法详解 按钮是 GUI 开发中最常用到的一种控件&#xff0c;作为一款著名的 GUI 开发框架&#xff0c;Qt 提供了很多种按钮&#xff0c;比如 QPushButton&#xff08;普通按钮&#xff09;、QRadioButton&#xff08;单选按钮&#xff09;、QToolButton&#x…

对LSTM的通俗易懂理解--可变权重

RNN的问题&#xff1a;长期依赖&#xff0c;即对短期的数据敏感&#xff0c;对比较远的长期数据不敏感&#xff0c;这是因为RNN隐藏状态权重在不同时刻是共享相同的&#xff0c;随着时间步的增加&#xff0c;梯度会指数级地衰减或者增长&#xff0c;导致梯度消失或者爆炸&#…

【黑马头条】-day06自媒体文章上下架-Kafka

文章目录 今日内容1 Kafka1.1 消息中间件对比1.2 kafka介绍1.3 kafka安装及配置1.4 kafka案例1.4.1 导入kafka客户端1.4.2 编写生产者消费者1.4.3 启动测试1.4.4 多消费者启动 1.5 kafka分区机制1.5.1 topic剖析 1.6 kafka高可用设计1.7 kafka生产者详解1.7.1 同步发送1.7.2 异…

配置vlan和vlan间路由、配置vlan的ip和vrrp、mstp和主次根

简单的通信实验 拓扑图&#xff1a; 1.配置vlan和链路聚合 Sw1 & sw2 undo info-center enable vlan batch 10 20 30 40 int eth-trunk 1 trunkport g 0/0/1 to 0/0/2 port link-type trunk port trunk allow-pass vlan 10 20 30 40 int g0/0/3 port link-type trunk p…

Unity 布局 HorizontalLayoutGroup 多行 换行

演示Gif&#xff1a; 现象: 子元素宽度不同&#xff0c;超出父元素后不会换行 GridLayout则是固定宽度也不能用&#xff0c; 需求 水平排版的同时&#xff0c;超出父级后换行 代码&#xff1a; 催更就展示[狗头]

Linux:Redis7.2.4的简单在线部署(1)

注意&#xff1a;我写的这个文章是以最快速的办法去搭建一个redis的基础环境&#xff0c;作用是为了做实验简单的练习&#xff0c;如果你想搭建一个相对稳定的redis去使用&#xff0c;可以看我下面这个文章 Linux&#xff1a;Redis7.2.4的源码包部署&#xff08;2&#xff09;-…

CSS 基础:设置背景的 5 个属性及简写 background 注意点

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端工具”&#xff0c;可获取 Web 开发工具合集 263篇…

给你的Qt软件加个授权

写在前面 环境&#xff1a; Win11 64位 VS2019 Qt5.15.2 核心思路&#xff1a; 将授权相关信息加密保存到License.txt中&#xff0c;软件运行时获取并解密授权信息&#xff0c;判断是否在限制期限内即可。 加解密部分使用第三方openssl库进行&#xff0c;因此需要手动在…

家庭网络防御系统搭建-虚拟机安装siem/securityonion网络连接问题汇总

由于我是在虚拟机中安装的security onion&#xff0c;在此过程中&#xff0c;遇到很多的网络访问不通的问题&#xff0c;通过该文章把网络连接问题做一下梳理。如果直接把securityonion 安装在物理机上&#xff0c;网络问题则会少很多。 NAT无法访问虚拟机 security onion虚拟…

多目标跟踪 | 基于anchor-free目标检测+ReID的实时一阶多类多目标跟踪算法实现

项目应用场景 面向多目标检测跟踪场景&#xff0c;项目采用 anchor-free 目标检测ReID 的实时一阶段多类多目标跟踪算法实现&#xff0c;效果嘎嘎好。 项目效果 项目细节 > 具体参见项目 README.md (1) 类别支持 1~10 object classes are what we need non-interest-…

SpringCloud学习(9)-GateWay网关-自定义拦截器

GateWay Filter详细配置说明 gateway Filter官网:Spring Cloud Gateway 作用&#xff1a; 请求鉴权异常处理记录接口调用时长统计 过滤器类别 全局默认过滤器&#xff1a;官网&#xff1a;Spring Cloud Gateway&#xff0c;出厂默认已有的&#xff0c;直接用&#xff0c;作…

Qt栅格布局的示例

QGridLayout * layoutnew QGridLayout;for(int i0;i<10;i){for(int j0;j<6;j){QLabel *labelnew QLabel(this);label->setText(QString("%1行%2列").arg(i).arg(j));layout->addWidget(label,i,j);}}ui->widget->setLayout(layout); 这样写程序会崩…