正则表达式:
概念:
正(确的)(规)则表达式,又称规则表达式。(英语:Regular Expression,在代码中常简写为regex、regexp或RE),计算机科学的一个概念。
作用:
正则表通常被用来判断、检索、替换那些符合某个模式(规则)的文本。
入门案例:判断输入的数字是否是六位数字。
package regex;
import java.util.Scanner;
/**
* @author shihao
* @create 2020-07-12 9:04
*/
public class regex {
public static void main(String[] args) {
//判断用户输入的是否是六位数字
Scanner scanner = new Scanner(System.in);
String s = scanner.nextLine();
boolean result = isvalid(s);
//正则表达式判断输入的是否是六位数字
boolean result2 = s.matches("\\d{6}");
//1.最基本的功能就是 匹配一个跟你给定的字符串一模一样的 字符串
boolean result3 = s.matches("123");
//2.*匹配最近的一个字符
boolean result4 = s.matches("zo*");
//3.可匹配空字符串
boolean result5 = s.matches("(zo)*");
//google,gooooooogle
boolean result6 = s.matches("go{2,}gle");
boolean result7 = s.matches("z|food");
boolean result8 = s.matches("(z|f)ood");
//表示只匹配一个,abc中任意一个
boolean result9 = s.matches("[abc]");
//表示匹配a-z中任意一个
boolean result10 = s.matches("[a-z]");
//表示不匹配a-z中任意一个
boolean result11 = s.matches("[^a-z]");
//如果有[],^表示取反
//如果没有[],^表示开始的位置,$表示结束的位置
//也就是说下面这个例子表示的是只能匹配abc
boolean result12 = s.matches("^abc&");
boolean result13 = s.matches("\\\\");
//如果想匹配.就需要使用\来转义
boolean result14 = s.matches("www\\.cskaoyan\\.com");
System.out.println(result14);
}
/**
* 判断是否是有效的六位数字
*
* @param s
* @return
*/
private static boolean isvalid(String s) {
if (s.length() != 6) {
return false;
}
char[] chars = s.toCharArray();
//chars.for,然后回车,会直接写成for循环的格式
for (char aChar : chars) {
if (aChar < '0' || aChar > '9') {
return false;
}
}
return true;
}
}
正则基本规则:
- 次数限定符
- 字符集
- 特殊字符
- 其他
注:在java中得用2个\才表示1个\。
练习:
package regex;
import java.util.Scanner;
/**
* @author shihao
* @create 2020-07-12 10:34
*/
public class exercise {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String s = scanner.nextLine();
//匹配身份证号码 15位、18位、17位+X
boolean result1 = s.matches("\\d{15}|\\d{18}|\\{17}X");
//匹配手机号
//13[0-9]、14[4,8,9]、15[0-9]、168、177、18[0-9]
boolean result2 = s.matches("(13[0-9]|14[489]|15[0-9]|168|177|18[0-9])\\d{8}");
System.out.println(result1);
}
}
ThreadLocal
MainDemo:
/**
* @author shihao
* @create 2020-07-12 16:11
*/
public class MainDemo {
public static void main(String[] args) {
ThreadLocal threadLocal = new ThreadLocal();
threadLocal.set("张三");
String o = (String) threadLocal.get();
System.out.println(o);
new MyThread(threadLocal).start();
}
}
MyThread:
/**
* @author shihao
* @create 2020-07-12 16:17
*/
public class MyThread extends Thread {
ThreadLocal threadLocal = new ThreadLocal();
public MyThread(ThreadLocal threadLocal) {
this.threadLocal = threadLocal;
}
@Override
public void run() {
super.run();
String o = (String) threadLocal.get();
System.out.println(o);
}
}
结果输出的结果是:
为什么调用MyThread后从ThreadLocal中get到的值是null呢?
可能是线程隔离的原因。
看一下threadLocal的set和get方法源码
也就是说,在main线程中set了只有在main线程中才能get到,而在mythread线程中get是没法儿get到的。
threadLocal存取数据具有线程隔离性。相同的线程,总可以取出来数据。
一个请求到来,service和dao是不是一个线程?是。
service可以取出来,dao可以取出来?可以。
多用户并发访问threadLocal里面的数据会不会交叉?不会交叉。
threadLocal可以用在事务中吗?必须可以。
事务:
DruidUtils (复制自前面做项目一种的DruidUtils ,并新增了开启事务的功能):
/**
* MallUser: zsquirrel
* Date: 2020/4/21
* Time: 10:59 上午
*/
package com.cskaoyan.transfer.utils;
import com.alibaba.druid.pool.DruidDataSourceFactory;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
public class DruidUtils {
private static DataSource dataSource;
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
static {
//要用类加载器的一个API来获取绝对路径,可以和servletContext解耦
// c3p0-config.xml文件放在哪的? src目录 不需要做任何配置,它就可以读取到
InputStream inputStream = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
Properties properties = new Properties();
try {
properties.load(inputStream);
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
public static DataSource getDataSource() {
return dataSource;
}
public static Connection getConnection() throws SQLException {
return dataSource.getConnection();
}
/**
* @param transactional 是否开启事务
* @return
* @throws SQLException
*/
public static Connection getConnection(boolean transactional) throws SQLException {
//如果要开启事务
if (transactional) {
//从threadLocal中get以下看下有没有
//第一次get肯定是没有的,没有就调用无参的getConnection方法,并在threadLocal中设置这个connection
//第二次调用这个方法时就能从threadLocal中get到了
Connection connection = threadLocal.get();
if (connection == null) {
connection = getConnection();
//set的时候是在当前线程中set一个connection,而TransferServiceImpl在用完之后又close
//close就是将其放回数据库连接池
//而当前线程中放的还是这个connection,但这个connection已经放回到数据库连接池了,
//此时这个connection就不能直接使用了
//Tomcat在处理请求时采用的是线程池,那可能下一个请求到来的时候Tomcat就会分配相同的线程给它
//那再调用getConnection(boolean transactional)方法不就能直接get到connection了
//但是这个connection已经被放回数据库连接池了,所以这个时候就不能使用connection了
//所以就有了releaseConnection方法。
threadLocal.set(connection);
}
}
return getConnection();
}
/**
* 把当前线程中的connection清空
*/
public static void releaseConnection() {
threadLocal.set(null);
}
}
TransferServlet :
package com.cskaoyan.transfer.controller;
import com.cskaoyan.transfer.service.TransferService;
import com.cskaoyan.transfer.service.TransferServiceImpl;
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 shihao
* @create 2020-07-12 11:42
*/
@WebServlet("/transfer")
public class TransferServlet extends HttpServlet {
private TransferService transferService = new TransferServiceImpl();
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String borrow = request.getParameter("borrow");
String lend = request.getParameter("lend");
String money = request.getParameter("money");
//校验
transferService.transfer(borrow, lend, money);
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
}
TransferServiceImpl :
package com.cskaoyan.transfer.service;
import com.cskaoyan.transfer.dao.TransferDao;
import com.cskaoyan.transfer.dao.TransferDaoImpl;
import com.cskaoyan.transfer.utils.DruidUtils;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @author shihao
* @create 2020-07-12 15:22
*/
public class TransferServiceImpl implements TransferService {
private TransferDao transferDao = new TransferDaoImpl();
/**
* 事务的代码应该写在service层,dao层其实就是一个个操纵数据库的方法
* service和dao不是同一个connection,
* 两个dao方法也不是同一个connection
* 所以service和dao应当是同一个connection才可以谈事务
* @param borrow
* @param lend
* @param money
*/
@Override
public void transfer(String borrow, String lend, String money) {
//这两个方法,DataSource会取两个connection,没法保障事务
//所以必须让其在一个connection中执行这两个方法
Connection connection = null;
try {
//开启事务
connection = DruidUtils.getConnection(true);
connection.setAutoCommit(false);
transferDao.transfer(borrow, Double.parseDouble(money));
transferDao.transfer(lend, -Double.parseDouble(money));
//没出错就提交
connection.commit();
} catch (SQLException e) {
//出错就rollback
e.printStackTrace();
if (connection != null) {
try {
connection.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
}
} finally {
//关闭流
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
}
TransferDaoImpl :
package com.cskaoyan.transfer.dao;
import com.cskaoyan.transfer.utils.DruidUtils;
import org.apache.commons.dbutils.QueryRunner;
import java.sql.SQLException;
/**
* @author shihao
* @create 2020-07-12 15:26
*/
public class TransferDaoImpl implements TransferDao {
@Override
public void transfer(String borrow, Double money) {
//QueryRunner runner = new QueryRunner(DruidUtils.getDataSource());
//不传DataSource了,在runner.update中传入connection
QueryRunner runner = new QueryRunner();
try {
runner.update(DruidUtils.getConnection(true),
"update account set money = money - ? where name = ?",
money, borrow);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
index.jsp:
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2020/7/12
Time: 11:05
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<form action="<%=request.getContextPath()%>/transfer" method="post">
转入方:<input type="text" name="borrow"><br>
转出方:<input type="text" name="lend"><br>
金额:<input type="number" name="money"><br>
<input type="submit">
</form>
</body>
</html>
这又遇见了一个bug,老是报NullPointerException,于是我对DruidUtils的静态代码块中的代码进行了如下改动,就发现了报错原因:
static {
//要用类加载器的一个API来获取绝对路径,可以和servletContext解耦
// c3p0-config.xml文件放在哪的? src目录 不需要做任何配置,它就可以读取到
try {
FileInputStream fis = new FileInputStream(DruidUtils.class.getResource("/").getPath() + "druid.properties");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
//找不到指定文件
//E:\WangDao\JavaEE\regex_transfer\transfer\target\transfer-1.0-SNAPSHOT\WEB-INF\classes\druid.properties
//然后我将druid.properties复制到上述目录下,问题解决
//执行到这步的时候老是报NullPointerException,于是我写了上述代码看看是不是没找到文件
InputStream inputStream = DruidUtils.class.getClassLoader().getResourceAsStream("druid.properties");
Properties properties = new Properties();
try {
properties.load(inputStream);
dataSource = DruidDataSourceFactory.createDataSource(properties);
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}