0%

说明

  • 上篇主要搭建了servlet,本次主要加入jsp的应用,功能包括增删改查
  • 本次java 版本
1
2
3
4
C:\Users\Administrator>java -version
java version "1.8.0_381"
Java(TM) SE Runtime Environment (build 1.8.0_381-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.381-b09, mixed mode)

简单实例

  • webapp下新增index.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>主页</title>
</head>
<body>
<%
String user=(String)session.getAttribute("user");
if (user != null) {
// 用户已登录
out.println("<a href=\"homePage.jsp\">");
out.println("登录成功主页");
out.println("</a>");
} else {
// 用户未登录
out.println("<a href=\"login.jsp\">");
out.println("登录界面");
out.println("</a>");
}
%>
</body>
</html>
  • 当sesssion数据不存在,可点击进入登录界面

image-20231201173302810

  • webapp/login.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h1>登录</h1>
<form action="login" method="post">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required><br><br>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required><br><br>
<input type="submit" value="登录">
</form>
</body>
</html>
  • 登录的servlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package xyz.shi.servlet;
...
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpSession;

@WebServlet("/login")
public class LoginServlet extends HttpServlet{
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String username = request.getParameter("username");
String password = request.getParameter("password");

// 验证用户名和密码
if (username.equals("admin") && password.equals("123456")) {
// 登录成功,创建会话
HttpSession session = request.getSession();
session.setAttribute("username", username);
response.sendRedirect("homePage.jsp");
} else {
// 登录失败,返回登录页面
request.setAttribute("error", "用户名或密码错误");
request.getRequestDispatcher("login.jsp").forward(request, response);
}
}
}
  • webapp/homePage.jsp
1
2
3
4
5
6
7
8
9
10
11
12
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>主页</title>
</head>
<body>
<h1>欢迎登录,${username}!</h1>
<a href="logout">注销</a>
</body>
</html>
  • 注销的Servlet
1
2
3
4
5
6
7
8
9
10
11
package xyz.shi.servlet;

...
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
HttpSession session = request.getSession();
session.invalidate();
response.sendRedirect("login.jsp");
}
}

image-20231201173645669

结合jdbc

  • 基础知识参考这篇文章

  • 首先pom.xml中加入本次需要的依赖文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/javax.servlet/jstl -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>

jstl主要是在jsp中使用的标签语法

三层架构

  • 表示层:主要是指与用户交互的界面。用于接收用户输入的数据和显示处理后用户需要的数 据。

  • 业务逻辑层:UI层(表示)和DAL层(数据访问)之间的桥梁。实现业务逻辑。业务逻辑具体包含:验证、计算、业务规则等等。

  • 数据访问层:与数据库打交道。主要实现对数据的增、删、改、查。将存储在数据库中的数据提交给业务层,同时将业务层处理的数据保存到数据库。

  • 实体层:它不属于三层中的任何一层,贯穿于三层,在三层之间传递数据;

实体层

  • xyz.shi.entity.User
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package xyz.shi.entity;
public class User {
private int id;
private String name;
private String password;
public int getId() {
return id;
}
// 因为QueryRunner创建Bean对象的过程使用的是newInstance()方法,该方法只能调用无参构
public User () {

}
public User(int id,String name, String password) {
this.name = name;
this.id = id;
this.password = password;
}
public void setId(int id) { this.id = id;}
public String getName() { return name;}
public void setName(String name) { this.name = name;}
public void setPassword(String password) {this.password = password;}
public String getPassword() {return password;}

}

数据访问层

  • 分为接口和实现接口,代码放在xyz.shi.dao 包下

  • 接口层xyz.shi.dao.UserDao

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package xyz.shi.dao;

import xyz.shi.entity.User;

import java.sql.SQLException;
import java.util.List;

public interface UserDao {
//返回所有对象
List<User> queryAllList(String sql);
// 查找多条记录
List<User> queryList(String sql,Object ...params);
// 查找一条记录
User queryOne(String sql, Object... parameters);
// 修改(删除)一条记录
int update(String sql, Object... parameters) ;
}

  • 实现接口层xyz.shi.dao.impl.UserDaoImpl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package xyz.shi.dao.impl;

import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import xyz.shi.dao.UserDao;
import xyz.shi.entity.User;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import xyz.shi.util.jdbcUtil;
public class UserDaoImpl implements UserDao {
private QueryRunner qr = new QueryRunner();
// 返回所有对象
@Override
public List<User> queryAllList(String sql) {
Connection connection = null;
try {
connection =jdbcUtil.getConnection();
return qr.query(connection, sql, new BeanListHandler<User>(User.class));
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
jdbcUtil.closeResource(null,null, connection);
}
}
// 返回多个对象(即查询的结果是多行)
@Override
public List<User> queryList(String sql, Object... params) {
Connection connection = null;
try {
connection =jdbcUtil.getConnection();
return qr.query(connection, sql, new BeanListHandler<User>(User.class), params);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
jdbcUtil.closeResource(null,null, connection);
}
}
// 查询单行结果
@Override
public User queryOne(String sql, Object... parameters) {
Connection connection = null;

try {
connection =jdbcUtil.getConnection();
return qr.query(connection, sql, new BeanHandler<User>(User.class), parameters);

} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
jdbcUtil.closeResource(null,null, connection);
}

}
// 修改(增删改通用)
@Override
public int update(String sql, Object... parameters) {
Connection connection = null;
try {
connection =jdbcUtil.getConnection();
return qr.update(connection,sql,parameters);
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
jdbcUtil.closeResource(null,null, connection);
}
}
}

业务逻辑层

  • 分为接口和实现接口,代码放在xyz.shi.service 包下

  • 接口层xyz.shi.service.UserService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package xyz.shi.service;
import xyz.shi.entity.User;
import java.util.List;
public interface UserService {
//根据userId查询一个user
User getUser(String id);
//根据name和password查询一个user
Boolean ableLogin (String name , String password);
//查找多条user对象
List<User> getUserList(Object... parameters);
//查找所有user对象
List<User> getAllUser();
//增加一个user
int addUser(Object... parameters);
//删除一个user
int deleteUser(int userId);
//修改一个user的信息
int updateUser(Object... parameters);
}

  • xyz.shi.service.impl.UserServiceImpl 实现接口,这里就是编写具体的逻辑
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
package xyz.shi.service.impl;

import xyz.shi.dao.UserDao;
import xyz.shi.dao.impl.UserDaoImpl;
import xyz.shi.entity.User;
import xyz.shi.service.UserService;

import java.sql.SQLException;
import java.util.List;

public class UserServiceImpl implements UserService {
//创建UserDaoImpl对象
private UserDao userDao = new UserDaoImpl();
//查询所有
@Override
public List<User> getAllUser() {
String sql ="select * from `users`";
try {
return userDao.queryAllList(sql);
} catch (Exception e) {
System.out.println(e);
return null;
}
}
//根据userId查询一个user
@Override
public User getUser(String userId) {
String sql = "select * from `users` where id = ? ";
try {
return userDao.queryOne(sql,userId);
} catch (Exception e) {
System.out.println(e);
return null;
}
}
//根据userId和password查询一个user
@Override
public Boolean ableLogin (String name , String password) {
String sql ="select * from `users` where name = ? and password = ? ";
User user = userDao.queryOne(sql, name, password);

return user != null ? true : false;

}
//根据条件查询多个user
@Override
public List<User> getUserList(Object... parameters) {
String sql ="select * from `users` where ? = ? ";
try {
return userDao.queryList(sql,parameters);
} catch (Exception e) {
System.out.println(e);
return null;
}
}
//增加一个user
@Override
public int addUser(Object... parameters) {
String sql ="insert into `users` (name, password) values (?, ?)";
return userDao.update(sql, parameters);
}
//删除一个user
@Override
public int deleteUser(int userId) {
String sql ="delete from `users` where id = ? ";
return userDao.update(sql,userId);
}
//修改一个user的信息
@Override
public int updateUser(Object... parameters) {
String sql ="update `users` set name = ?,password = ? where id = ? ";
return userDao.update(sql,parameters);
}

}

表示层

  • 也就是用户界面层

  • xyz.shi.servlet 层,主要放jsp文件传过来的数据,然后把服务器数据传给jsp页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package xyz.shi.servlet;

import xyz.shi.entity.User;
import xyz.shi.service.UserService;
import xyz.shi.service.impl.UserServiceImpl;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpSession;

@WebServlet("/login")
public class LoginServlet extends HttpServlet{
// 注意这里get的写法,当其他页面新增或者修改成功后,跳转到/login页面,然后自动读取用户列表数据
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String session = (String) req.getSession().getAttribute("username");
if (session != null) {
UserService userService = new UserServiceImpl();
List<User> users = userService.getAllUser();
req.setAttribute("userList", users);
req.getRequestDispatcher("homePage.jsp").forward(req, resp);;

}
}

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
UserService userService = new UserServiceImpl();
String username = request.getParameter("username");
String password = request.getParameter("password");
boolean login = userService.ableLogin(username, password);
// 验证用户名和密码
if (login) {
// 登录成功,创建会话
HttpSession session = request.getSession();
List<User> users = userService.getAllUser();
request.setAttribute("userList", users);

session.setAttribute("username", username);
System.out.println("登录成功");
request.getRequestDispatcher("homePage.jsp").forward(request, response);;
} else {
// 登录失败,返回登录页面
System.out.println("登录失败");
request.setAttribute("error", "用户名或密码错误");
request.getRequestDispatcher("login.jsp").forward(request, response);

}
}
}
  • 默认打开的是index.jsp界面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>主页</title>
</head>
<body>
<%
String user=(String)session.getAttribute("username");
if (user != null) {
// 用户已登录
out.println("<a href=\"homePage.jsp\">");
out.println("登录成功主页");
out.println("</a>");
} else {
// 用户未登录
out.println("<a href=\"login.jsp\">");
out.println("登录界面");
out.println("</a>");
}
%>
</body>
</html>
  • 登录界面login.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>登录</title>
</head>
<body>
<h1>登录</h1>
<form action="login" method="post">
<label for="username">用户名:</label>
<input type="text" id="username" name="username" required><br><br>
<label for="password">密码:</label>
<input type="password" id="password" name="password" required><br><br>
<input type="submit" value="登录">
</form>
</body>
</html>
  • 登录后的界面homepage.jsp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!DOCTYPE html>
<%@ page contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>

<html>
<head>
<meta charset="UTF-8">
<title>主页</title>
</head>
<body>
<h1>欢迎登录,${username}!</h1>
<a href="add.jsp">添加</a>
<table>
<tr>
<td>id</td>
<td>用户名</td>
<td>状态</td>
<td>操作</td>
</tr>
<c:forEach var="item" items="${requestScope.userList}">
<tr>
<td>${item.id}</td>
<td>${item.name}</td>
<td><a href="userFind?id=${item.id}">修改</a>
<td><a href="<%=request.getContextPath()%>/userDel?id=${item.id}">删 除</a></td>
</tr>
</c:forEach>

</table>
<a href="logout">注销</a>
</body>
</html>

登录成功后LoginServlet把用户列表传给了homePage.jsp,后续的修改,删除逻辑也是一样

servlet层

  • 比如下面这是修改用户名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package xyz.shi.servlet;

import xyz.shi.entity.User;
import xyz.shi.service.UserService;
import xyz.shi.service.impl.UserServiceImpl;
import xyz.shi.utils.GetUsersUtils;

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;
import java.util.List;

@WebServlet("/userUpdate")
public class UpdateUserServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
UserService userService = new UserServiceImpl();
int userId = Integer.parseInt(request.getParameter("id"));
String username = request.getParameter("username");
String password = request.getParameter("password");
User user = new User(userId, username, password);
int flag = userService.modify(user);
if (flag > 0) {
System.out.println("修改数据成功");
GetUsersUtils.GetUsers(request, response);
} else {
// 登录失败,返回登录页面
System.out.println("修改数据失败");
request.setAttribute("message", "failed");

}
}
}

说明

  • 本编开始学习JavaWeb开发的内容,环境信息如下:IDEA,Maven,win10,java version "1.8.0_381"

搭建Servlet

在IDEA中新建webapp

image-20231129101815913

  • 注意maven设置为本地

image-20231129102658617

  • 在根目录新建:src\main\javasrc\main\resourcessrc\main\webapp

  • 在pom.xml中加入依赖文件

1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
  • 手动新建src\main\webapp\WEB-INF\web.xml
1
2
3
4
5
6
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
</web-app>
  • src-mian-java 新建一个包名为xyz.shi.servlet , 然后在报名下新建MyServlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package xyz.shi.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import javax.servlet.annotation.WebServlet;

@WebServlet("/hello")
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("get请求");
resp.getWriter().write("hello world");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("post请求");
resp.getWriter().write("你好");

}
}


  • @WebServlet()会根据某些GET请求会生效, 然后里面写的 /hello,表示当请求的url是 /hello 的请求才会生效
  • 重写了父类中的get和post方法
  • 虽然打war 复制到Tomcat的webapps里面运行是一种方法,但是这种方法比较麻烦.所以我更推荐使用Smart Tomcat

image-20231130163237555.png

  • 打开编辑配置

image-20231130163329491

  • 并选择本地tomcat的根目录,设置路径等,端口不用改

image-20231201154747879

  • 点击运行按钮

image-20231130163627514

  • 出现如下信息说明启动成功
1
2
3
30-Nov-2023 16:36:32.793 信息 [main] org.apache.coyote.AbstractProtocol.start 开始协议处理句柄["http-nio-8080"]
30-Nov-2023 16:36:32.807 信息 [main] org.apache.catalina.startup.Catalina.start [594]毫秒后服务器启动
http://localhost:8080/WebTest
  • 浏览器访问http://localhost:8080/WebTest/hello

image-20231130163758294

  • MyServlet中的WebServlet("/hello") 的注解去掉
1
2
3
4
5
6
7
8
9
10
11
12
ackage xyz.shi.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import javax.servlet.annotation.WebServlet;

public class MyServlet extends HttpServlet {
@Override
....
  • 在web.xml文件下,配置Servlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<!--servlet命名-->
<servlet-name>MyServlet</servlet-name>
<!--servlet类的全限定名(路径+文件名)-->
<servlet-class>xyz.shi.servlet.MyServlet</servlet-class>
</servlet>
<!--定义Servlet的请求映射-->
<servlet-mapping>
<!--要映射的servlet名,与上方定义的servlet-name一致-->
<servlet-name>MyServlet</servlet-name>
<!--请求映射url,必须以/开头-->
<!--之后通过项目上下文路径+该路径,就能访问FirstServlet类-->
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>

  • 重写运行,在浏览器中访问http://localhost:8080/WebTest/hello

总结

有两种运行Servlet的方式

  • 第一种是在类中直接加上WebServlet("/hello")的标识,然后启动
  • 第二种是在web.xml中进行配置,推荐第二种方式更加方便管理

说明

  • 之前用go写过一篇爬虫的实例,本次用java来写
  • 需要掌握http请求的编写

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Craw {
public static void main(String[] args) throws Exception {
for(int i=2;i<3;i++) {
String url= "http://www.52desktop.cn/html/DLZM/KPBZ/20191205/15898_" +Integer.toString(i) + ".html";
String pageStr = String.valueOf(GetPageStr(url));
String regImg = "http://i.52desktop.cn:81/upimg/allimg/[^\\\"]+?(\\.(jpg|png|jpeg|gif|bmp))";
Pattern pattern = Pattern.compile(regImg);
Matcher matcher = pattern.matcher(pageStr);
if (matcher.find()) {
System.out.println(matcher.groupCount());
System.out.println(matcher.group(0));
System.out.println(matcher.group(1));
if(matcher.group(0).contains("20191204")) {
downloadImg("d:\\img\\", matcher.group(0));
}
}

}
}
private static StringBuffer GetPageStr(String url) throws Exception {
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
// 发送get请求
con.setRequestMethod("GET");
// 通过con.getInputStream() 获取到与服务器建立的输入流,即服务端返回的数据流
//InputStreamReader将字节流转换为字符流,而BufferedReader提供了缓冲和高效读取的功能,常常用来包装InputStreamReader以提供更好的读取性能。
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
String inputLine;
StringBuffer response = new StringBuffer();
//不断地调用 BufferedReader 的 readLine() 方法,每次读取一行数据,并将其加入到 StringBuffer 中。
// 当 readLine() 方法返回 null 时,表示已经读取完了所有的数据,循环结束
while ((inputLine=in.readLine()) != null) {
response.append(inputLine);
}
in.close();

// 打印服务器的响应
System.out.println("GET Response: " + response.toString());
return response;
}
private static void downloadImg(String savePath, String url) throws IOException {
URL imageUrl = new URL(url);
HttpURLConnection con = (HttpURLConnection) imageUrl.openConnection();
// 发送get请求
con.setRequestMethod("GET");
try(InputStream inputStream= con.getInputStream()) {
// 获取当前图片名称
String fileName = url.substring(url.lastIndexOf("/") +1);
// 获取保存图片路径
Path filePath = Paths.get(savePath, fileName);
// 将输入流中的内容复制到指定的目标文件
Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING);
System.out.println(fileName + " download is successful");
}

}
}

如上代码的确可以运行,但是发现目标地址基本上无法访问了

  • 爬其他目标网站的实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;


public class Craw {
public static void main(String[] args) throws Exception {
String url= "https://sj.zol.com.cn/bizhi/detail_12078_130945.html";
// 读取html页面的内容
String pageStr = String.valueOf(GetPageStr(url));
// 用Jsoup转换为dom格式进行过滤
Document document = Jsoup.parse(pageStr);
// css的语法
Elements elements = document.select("ul#showImg li");
// 循环读取img
for(Element li: elements) {
Element img = li.selectFirst("img");
String imgUrl;
if (img.attr("src").length() > 0) {
imgUrl = img.attr("src");
} else {
imgUrl = img.attr("srcs");
}
System.out.println(imgUrl);
downloadImg("d:\\img\\", imgUrl);

}
}
private static StringBuffer GetPageStr(String url) throws Exception {
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
// 发送get请求
con.setRequestMethod("GET");
// 通过con.getInputStream() 获取到与服务器建立的输入流,即服务端返回的数据流
//InputStreamReader将字节流转换为字符流,而BufferedReader提供了缓冲和高效读取的功能,常常用来包装InputStreamReader以提供更好的读取性能。
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
String inputLine;
StringBuffer response = new StringBuffer();
//不断地调用 BufferedReader 的 readLine() 方法,每次读取一行数据,并将其加入到 StringBuffer 中。
// 当 readLine() 方法返回 null 时,表示已经读取完了所有的数据,循环结束
while ((inputLine=in.readLine()) != null) {
response.append(inputLine);
}
in.close();

// 打印服务器的响应
System.out.println("GET Response: " + response.toString());
return response;
}
private static void downloadImg(String savePath, String url) throws IOException {
URL imageUrl = new URL(url);
HttpURLConnection con = (HttpURLConnection) imageUrl.openConnection();
// 发送get请求
con.setRequestMethod("GET");
try(InputStream inputStream= con.getInputStream()) {
// 获取当前图片名称
// String fileName = url.substring(url.lastIndexOf("/") +1);
// 生成唯一格式图片
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
String fileName = now.format(formatter) + ".jpg";
// 获取保存图片路径
Path filePath = Paths.get(savePath, fileName);
// 将输入流中的内容复制到指定的目标文件
Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING);
System.out.println(fileName + " download is successful");
}

}
}

本次目标网站采用jsoup的读取图片路径,然后用http下载到本地

需要在pom.xml中加入依赖文件

org.jsoup jsoup 1.14.3
  • 下面代码再次改进,采用多线程的方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class ThreadCraw {
public static void main(String[] args) throws Exception {
String url= "https://sj.zol.com.cn/bizhi/detail_12078_130945.html";
// 读取html页面的内容
String pageStr = String.valueOf(GetPageStr(url));
// 用Jsoup转换为dom格式进行过滤
Document document = Jsoup.parse(pageStr);
// css的语法
Elements elements = document.select("ul#showImg li");
// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(elements.size());
// 循环读取img
for(Element li: elements) {
Element img = li.selectFirst("img");
String imgUrl;
if (img.attr("src").length() > 0) {
imgUrl = img.attr("src");
} else {
imgUrl = img.attr("srcs");
}
// downloadImg("d:\\img\\", imgUrl);
// 创建并启动下载线程
ImageDownloader downloader = new ImageDownloader("d:\\img\\", imgUrl);
executor.execute(downloader);
}
// 关闭线程池
executor.shutdown();
}
private static StringBuffer GetPageStr(String url) throws Exception {
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
// 发送get请求
con.setRequestMethod("GET");
// 通过con.getInputStream() 获取到与服务器建立的输入流,即服务端返回的数据流
//InputStreamReader将字节流转换为字符流,而BufferedReader提供了缓冲和高效读取的功能,常常用来包装InputStreamReader以提供更好的读取性能。
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream(), "UTF-8"));
String inputLine;
StringBuffer response = new StringBuffer();
//不断地调用 BufferedReader 的 readLine() 方法,每次读取一行数据,并将其加入到 StringBuffer 中。
// 当 readLine() 方法返回 null 时,表示已经读取完了所有的数据,循环结束
while ((inputLine=in.readLine()) != null) {
response.append(inputLine);
}
in.close();

// 打印服务器的响应
System.out.println("GET Response: " + response.toString());
return response;
}
private static void downloadImg(String savePath, String url) throws IOException {

URL imageUrl = new URL(url);
HttpURLConnection con = (HttpURLConnection) imageUrl.openConnection();
// 发送get请求
con.setRequestMethod("GET");
try(InputStream inputStream= con.getInputStream()) {
// 获取当前图片名称
// String fileName = url.substring(url.lastIndexOf("/") +1);
// 生成唯一格式图片
LocalDateTime now = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
String fileName = now.format(formatter) + ".jpg";
// 获取保存图片路径
Path filePath = Paths.get(savePath, fileName);
try {
// 将输入流中的内容复制到指定的目标文件
Files.copy(inputStream, filePath, StandardCopyOption.REPLACE_EXISTING);
System.out.println(fileName + " download is successful");
} catch (FileAlreadyExistsException e) {

}

}
}

static class ImageDownloader extends Thread {

private String savePath;
private String imgUrl;

public ImageDownloader(String savePath, String imgUrl) {
this.savePath = savePath;
this.imgUrl = imgUrl;
}

@Override
public void run() {
try {
downloadImg(savePath, imgUrl);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}

关键代码

  • 创建线程池: ExecutorService executor = Executors.newFixedThreadPool(elements.size());

  • 最后执行线程

1
2
ImageDownloader downloader = new ImageDownloader("d:\\img\\", imgUrl);
executor.execute(downloader);
  • 其实更优雅的方式应该是这种
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public static void main(String[] args) throws Exception {
String url = "https://sj.zol.com.cn/bizhi/detail_12078_130945.html";
// 读取html页面的内容
String pageStr = String.valueOf(GetPageStr(url));
// 用Jsoup转换为dom格式进行过滤
Document document = Jsoup.parse(pageStr);
// css的语法
Elements elements = document.select("ul#showImg li");

// 创建线程池
ExecutorService executor = Executors.newFixedThreadPool(elements.size());
List<Future<Void>> futures = new ArrayList<>();

// 循环读取img
for (Element li : elements) {
Element img = li.selectFirst("img");
String imgUrl;
if (img.attr("src").length() > 0) {
imgUrl = img.attr("src");
} else {
imgUrl = img.attr("srcs");
}

// 提交下载任务到线程池
Callable<Void> downloadTask = () -> {
downloadImg("d:\\img\\", imgUrl);
return null;
};
Future<Void> future = executor.submit(downloadTask);
futures.add(future);
}

// 等待所有任务完成
for (Future<Void> future : futures) {
future.get();
}

// 关闭线程池
executor.shutdown();
}

说明

  • 进程和线程的关系就是:一个进程可以包含一个或多个线程,但至少会有一个线程。

  • 多进程的优点:多进程稳定性比多线程高,因为在多进程的情况下,一个进程崩溃不会影响其他进程,而在多线程的情况下,任何一个线程崩溃会直接导致整个进程崩溃。

  • 场景场景:一个进程中至少一个线程,一个进程中多个线程,多个进程中多个线程

  • Java语言内置了多线程支持。当Java程序启动的时候,实际上是启动了一个JVM进程,然后,JVM启动主线程来执行main()方法。在main()方法中,我们又可以启动其他线程

继承Thread

  • Thread派生一个自定义类,然后覆写run()方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class Main {
public static void main(String[] args) {
//创建线程
MyThread t01 = new MyThread();
MyThread t02 = new MyThread();
MyThread t03 = new MyThread("线程03");
t01.start();
t02.start();
t03.start();

//设置线程名(补救的设置线程名的方式)
t01.setName("线程01");
t02.setName("线程02");
// 设置主线程名称
// Thread.currentThread().setName("主线程");
for(int i=0;i<50;i++) {
// Thread.currentThread() 获取当前正在执行线程的对象
System.out.println("主线程" + Thread.currentThread().getName() + ":" + i);
}


}
}
class MyThread extends Thread {

public MyThread() {
}
public MyThread(String name) {
super(name);
}
//run方法是每个线程运行过程中都必须执行的方法
@Override
public void run() {
for (int i = 0; i < 50; i++) {
System.out.println("子线程" + this.getName() + ":" + i);
}
}
}

  • 打印结果发现:主线程50个,子线程有150个(3*50),可以看出来,主线程在和子线程抢占CPU的过程中,交替打印结果

此处最重要的为start()方法。单纯调用run()方法不会启动线程,不会分配新的分支栈。

start()方法的作用是:启动一个分支线程,在JVM中开辟一个新的栈空间,这段代码任务完成之后,瞬间就结束了。线程就启动成功了。

启动成功的线程会自动调用run方法(由JVM线程调度机制来运作的),并且run方法在分支栈的栈底部(压栈)。

run方法在分支栈的栈底部,main方法在主栈的栈底部。run和main是平级的。

单纯使用run()方法是不能多线程并发的。

实现Runnable

  • 实现Runnable接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class demo {

public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
//创建Thread对象,并把MyRunnable对象作为Tread类构造方法的参数传递进去
//创建Thread对象,并把MyRunnable对象作为Tread类构造方法的参数传递进去
Thread tread = new Thread(runnable);
tread.start();

//通过匿名内部类的方式创建线程
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println( "主线程:" + i);
}
}
}).start();
}
}

class MyRunnable implements Runnable {

@Override
public void run() {
for (int i = 1; i <= 50; i++) {
System.out.println( "子线程:" + i);
}
}
}

实现Callable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

public class TestThread {

public static void main(String[] args) throws ExecutionException, InterruptedException {
FutureTask<String> futureTask = new FutureTask<>(()->{
for (int i = 0; i < 50; i++) {
System.out.println("子线程1执行中:"+i);
}
return "线程1执行完毕";
});

FutureTask<String> futureTask2 = new FutureTask<>(()->{
for (int i = 0; i < 50; i++) {
System.out.println("子线程2执行中:"+i);
}
return "线程2执行完毕";
});

new Thread(futureTask).start();
new Thread(futureTask2).start();
//get 获取线程最终返回的值
System.out.println(futureTask.get());
System.out.println(futureTask2.get());
}
}

线程池

  • Java中创建线程池很简单,只需要调用Executors中相应的便捷方法即可,比如Executors.newFixedThreadPool(int nThreads),但是便捷不仅隐藏了复杂性,也为我们埋下了潜在的隐患(OOM,线程耗尽)。

  • Executors创建线程池便捷方法列表:

方法名 功能
newFixedThreadPool(int nThreads) 创建固定大小的线程池
newSingleThreadExecutor() 创建只有一个线程的线程池
newCachedThreadPool() 创建一个不限线程数上限的线程池,任何提交的任务都将立即执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.FutureTask;

public class TestThread {

public static void main(String[] args) throws ExecutionException, InterruptedException {
testFixedThreadPool();
}

private static void testFixedThreadPool() {
ExecutorService fixThread = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
int finalI = i;
fixThread.execute(new Runnable() {
@Override
public void run() {
//每次只能三个一起执行
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
Thread.currentThread().setName("线程");
System.out.println(Thread.currentThread().getName() + " " + finalI);
}
});
}
}

}

  • 后续实践和分析可以看此文章

总结

  • 看了以上四种分析,我们可以清晰的发现了java中其实创建线程的方式就只有一种就是利用Thread+Runnable来实现多线程。其他多有方式都是对这个实现方式的变种。
  • 当然操作线程还有更多的知识,比如设置线程的一些状态、设置线程同步等,更加详细知识参考这里
1
2
3
4
5
6
setPriority  # 设置线程优先级
sleep # 让线程进入阻塞状态
join # 当一个线程调用join方法时,该线程会强制抢占cpu资源,直到该线程执行完毕其他线程才会继续执行
yield # 释放cpu资源,让当前线程进入就绪状态,礼让操作,重新和其他线程竞争cpu资源,可能不一定成功
interrupt # 中断线程
setDaemon # 将线程设置为守护线程,用来守护用户线程的,虚拟机只会保证用户线程的执行完毕,守护线程会随着虚拟机的关闭而关闭。

添加依赖

  • 首先需要在pom.xml添加依赖文件,本次用的阿里提供的解析json的包
1
2
3
4
5
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.42</version>
</dependency>
  • 常见用法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
package org.example;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;

public class Main {
public static void main(String[] args) {
String jsonString = "{\"name\":\"Tom\",\"age\":20,\"address\":{\"street\":\"123 Main St\",\"city\":\"New York\",\"state\":\"NY\"},\"phoneNumbers\":[{\"type\":\"home\",\"number\":\"123-456-7890\"},{\"type\":\"work\",\"number\":\"111-222-3333\"}]}";

// 将JSON字符串解析为JSONObject对象
JSONObject jsonObj = JSON.parseObject(jsonString);
// 新增
jsonObj.put("id", 1001);
// 新增一个数组
String[] strings = {"哈哈","忸怩"};
jsonObj.put("test",strings);
// ...{"id":1001,"test":["哈哈","忸怩"]}
System.out.println(jsonObj);

// 从JSONObject中获取属性值
String name = jsonObj.getString("name");
int age = jsonObj.getIntValue("age");
// map里面嵌套map
JSONObject address = jsonObj.getJSONObject("address");
String street = address.getString("street");
String city = address.getString("city");
String state = address.getString("state");
// 循环json中的数组
JSONArray phoneNumbers = jsonObj.getJSONArray("phoneNumbers");
for (int i = 0; i < phoneNumbers.size(); i++) {
JSONObject phone = phoneNumbers.getJSONObject(i);
String type = phone.getString("type");
String number = phone.getString("number");
System.out.println(type + ": " + number);
}
}
}

还有其他更多用法,比如删除、替换、查询是否存在等,不在演示

构建json对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.example;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;

public class JsonToBean {
public static void main(String[] args) {
// Json对象中添加的是键值对
JSONObject jsonObject = new JSONObject();
jsonObject.put("1","a");
jsonObject.put("2","b");
jsonObject.put("3","c");
// JSONArray中添加的是Json对象
JSONArray JsonArray = new JSONArray();

jsonObject.put("key", "value");//JSONObject对象中添加键值对
JsonArray.add(jsonObject);//将JSONObject对象添加到Json数组中

System.out.println(jsonObject);
System.out.println(JsonArray);
}
}

结果如下:

{“1”:”a”,”2”:”b”,”3”:”c”,”key”:”value”}
[{“1”:”a”,”2”:”b”,”3”:”c”,”key”:”value”}]

JavaBean转Json

  • user.class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package org.example;

public class User {
private int id;
private String name;
private String sex;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getSex() {
return sex;
}
@Override
public String toString() {
return "user:id" + id +"-name:" + name + "-sex:" + sex;
}
}

  • JsonToBean.class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package org.example;
import com.alibaba.fastjson2.JSONObject;

public class JsonToBean {
public static void main(String[] args) {
User user = new User();
user.setSex(null);
user.setName("行三");
user.setId(1);
JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(user));
System.out.println(jsonObject);
}
}

结果

{“id”:1,”name”:”行三”}

sex为null丢失了

Json转JavaBean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package org.example;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;

import java.util.List;

public class JsonToBean {
public static void main(String[] args) {
String jsonString = "{\"name\":\"Tom\",\"sex\":\"男\"}";
// 前面是JSON字符串 后面是java对象类型
User user=JSONObject.parseObject(jsonString,User.class);
// name: Tom---sex: 男
System.out.println("name: "+user.getName()+"---"+"sex: "+user.getSex());
String listStr = "[{\"name\":\"Tom\",\"sex\":\"男\"},{\"name\":\"jay\",\"sex\":\"女\"}]";

// 转Json List
JSONArray jsonArray = JSONArray.parseArray(listStr);
// [{"name":"Tom","sex":"男"},{"name":"jay","sex":"女"}]
System.out.println(jsonArray);
// 转具体的List<实体类> 然后循环
List<User> userList = JSONArray.parseArray(listStr, User.class);
for (User item : userList){
System.out.println(item.toString());
System.out.println(item.getName());
}

}
}

说明

  • 本次主要针对jdbc的知识点进行练习,用到了maven,mysql,idea等知识点

  • 本次的java版本如下

1
2
3
4
C:\Users\Administrator>java -version
java version "1.8.0_381"
Java(TM) SE Runtime Environment (build 1.8.0_381-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.381-b09, mixed mode)

配置

  • 在IDEA里新建一个maven项目,项目名叫做:MavenMysqlJdbc

  • 本地win搭建好mysql服务,本次搭建的的版本为:8.0.13

  • 打开src下的pom.xml文件, 在里面添加Mysql的jdbc包的引用,代码如下

1
2
3
4
5
6
7
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
</dependencies>
  • 自动下载依赖文件中,可能出现卡死,需要maven的内存:-Xms1024m -Xmx2048m修改如下:

image-20231115174256836

反射properties

  • 新建数据库配置文件,获取配置文件信息后,再注册数据库驱动,在src——main——resources目录下,新建db.properties文件
1
2
3
4
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai
user=root
password=123456
  • 新建util包,然后在里面创建JdbcUtil类,利用反射获取db.properties文件信息,最后返回数据库连接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package util;

import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

public class JdbcUtil {
private static String driver;
private static String url;
private static String user;
private static String pwd;

static {
try{
Properties properties = new Properties();
// 通过反射,新建字符输入流,读取db.properties文件
InputStream inputStream = JdbcUtil.class.getClassLoader().getResourceAsStream("db.properties");
// 输入流中读取到的属性,加载到properties属性集对象中
properties.load(inputStream);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
user = properties.getProperty("user");
pwd = properties.getProperty("password");


} catch (Exception e) {
e.printStackTrace();
}
}
public static Connection getConnection() {
try {
// 注册数据库驱动
Class.forName(driver);
//获取数据库连接(里面内容依次是:主机名和端口、用户名、密码),返回数据库链接
return DriverManager.getConnection(url,user, pwd);
} catch (ClassNotFoundException | SQLException e) {
throw new RuntimeException(e);
}
}
}

  • 在java(src-main-java)目录下创建LinkMysql类,调用JdbcUtil类返回的数据库连接操作数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

import util.JdbcUtil;
public class LinkMysql {
public static void main(String[] args) throws SQLException {
// 获取数据库链接
Connection connection = JdbcUtil.getConnection();
//需要执行的sql语句
String sql = "insert into class(name,sex) values(?,?)";
///获取预处理对象,并给参数赋值
PreparedStatement statement = connection.prepareCall(sql);
statement.setString(1, "刘亦菲");
statement.setString(2, "女");
//执行sql语句(插入了几条记录,就返回几)
int num = statement.executeUpdate();
System.out.println(num);

// 删除数据
String sql1 = "delete from class where id=17";
PreparedStatement statement1 = connection.prepareCall(sql1);
int num1 = statement1.executeUpdate();
System.out.println(num1);
// 关闭sql链接
statement.close();
connection.close();
}
}


ResourceBundle类获取properties

  • 在util包里面创建创建JdbcUtil2类,ResourceBundle类获取db.properties文件信息,最后返回数据库连接
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package util;

import java.sql.*;
import java.util.ResourceBundle;

public class JdbcUtil2 {
private static String url;
private static String user;
private static String pwd;

static {
try {
ResourceBundle resourceBundle = ResourceBundle.getBundle("db");
url = resourceBundle.getString("url");
pwd = resourceBundle.getString("password");
user = resourceBundle.getString("user");
} catch (Exception e) {
throw new RuntimeException(e);
}
}


public static Connection getConnection() {
try {
//获取数据库连接(里面内容依次是:主机名和端口、用户名、密码),返回数据库链接
return DriverManager.getConnection(url,user, pwd);
} catch (SQLException e) {
throw new RuntimeException(e);
}
}

// 关闭结果集
public static void closeResultSet(ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
// 关闭预处理对象
public static void closeStatement(Statement statement) throws SQLException {
if (statement != null) {
statement.close();
}
}
// 关闭数据库连接
public static void closeConnection(Connection connection) throws SQLException {
if (connection != null) {
connection.close();
}
}
// 关闭如上三个
public static void closeResource(ResultSet resultSet, Statement statement, Connection connection) throws SQLException {
closeResultSet(resultSet);
closeStatement(statement);
closeConnection(connection);
}
}

  • 在java目录下创建LinkMysql2类,调用JdbcUtil2类返回的数据库连接操作数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import util.JdbcUtil2;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class LinkMysql2 {
public static void main(String[] args) throws SQLException {
// 获取数据库链接
Connection connection = JdbcUtil2.getConnection();
//需要执行的sql语句
String sql = "insert into class(name,sex) values(?,?)";
///获取预处理对象,并给参数赋值
PreparedStatement statement = connection.prepareCall(sql);
statement.setString(1, "刘亦菲");
statement.setString(2, "女");
//执行sql语句(插入了几条记录,就返回几)
int num = statement.executeUpdate();
System.out.println(num);

// 删除数据
String sql1 = "delete from class where id=17";
PreparedStatement statement1 = connection.prepareCall(sql1);
int num1 = statement1.executeUpdate();
System.out.println(num1);
// 关闭sql链接
JdbcUtil2.closeResource(null, statement, connection);
}
}

DButils工具包

  • 打开src下的pom.xml文件, 在里面添加DButils的引用,代码如下
1
2
3
4
5
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.5</version>
</dependency>
  • 创建src-java-main-Dbutils类,往数据库内进行增删改查据,代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayHandler;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
import util.JdbcUtil2;

import java.sql.Array;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;

public class Dbutils {

public static void main(String[] args) throws SQLException {
//创建dbUtils里面的QueryRunner对象
QueryRunner queryRunner = new QueryRunner();
String sql = "insert into class(name,sex) values(?,?)";
Object[] object = {"自然", "男"};
Connection connection = JdbcUtil2.getConnection();
// 增加
int num = queryRunner.update(connection,sql,object);

System.out.println("新增数据行:" + num);

String updateSql = "update class set name=? where id=?";
Object[] object2 = {"自然2", 20};
// 修改
int num2 = queryRunner.update(connection,updateSql,object2);
System.out.println("修改数据行:" + num2);

String delSql = "delete from class where id=?";
Object[] object3 = {20};
// 删除
int num3 = queryRunner.update(connection,delSql,object3);
System.out.println("删除行:"+num3);

String sqlSelect = "select * from class";
// 执行查询,并以数组的形式返回查询结果(new ArrayHandler()只会返回第一条记录)
Object[] objects = queryRunner.query(connection,sqlSelect,new ArrayHandler());
System.out.println("单行查询结果:" + Arrays.toString(objects));
System.out.println("\n");
//执行查询,并以数组的形式返回查询结果(new ArrayListHandler()返回所有查询到的记录)
List<Object[]> lists = queryRunner.query(connection,sqlSelect,new ArrayListHandler());
for(Object[] item: lists) {
System.out.println("多行查询:" + Arrays.toString(item));
}
connection.close();
}
}

转换为实体

  • 新建一个表的实体类,src-java-main-Classs(当初我新建这个表瞎编的。。。)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Classs {
private int id;
private String name;
private String sex;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
public void setSex(String sex) {
this.sex = sex;
}
public String getSex() {
return sex;
}
@Override
public String toString() {
return "class:id" + id +"name:" + name + "sex:" + sex;
}
}

  • 新建一个src-main-java-DbutilsBean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.ArrayHandler;
import org.apache.commons.dbutils.handlers.ArrayListHandler;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import util.JdbcUtil2;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.List;

public class DbutilsBean {
public static void main(String[] args) throws SQLException {
//创建dbUtils里面的QueryRunner对象
QueryRunner queryRunner = new QueryRunner();
Connection connection = JdbcUtil2.getConnection();

String sqlSelect = "select * from class";
// 执行查询,并以数组的形式返回查询结果(new BeanHandler()只会返回第一条记录,并转成对象)
Classs classs = queryRunner.query(connection,sqlSelect,new BeanHandler<Classs>(Classs.class));
System.out.println("单行查询结果:" + classs);

System.out.println("\n");
//执行查询,并以数组的形式返回查询结果(new BeanListHandler()返回查询到的所有记录,并转成对象)
List<Classs> lists = queryRunner.query(connection,sqlSelect,new BeanListHandler<Classs>(Classs.class));
for(Classs item: lists) {
System.out.println("多行查询:" + item.getName());
}
System.out.println(lists);
connection.close();
}
}

  • 得到结果
1
2
3
4
多行查询:综艺一般
多行查询:五年一班
多行查询:王大炮
多行查询:刘大师
  • 更多用法
1
2
3
4
5
6
7
8
9

String sql = "select * from stu where age>?";
...
Object[] params = {16};
// 会返回结果中指定的列,比如返回name
List<Object> strs = queryRunner.query(connection,sql, new ColumnListHandler<>("name"),params);
//查询单数据
int age = queryRunner.query(connection,sql, new ScalarHandler<>(),params);
System.out.println(age);

连接池

  • 在scr——pom.xml文件里,引入c3p0包
1
2
3
4
5
<dependency>
<groupId>c3p0</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.1.2</version>
</dependency>
  • 在src——main——resources下增加c3p0-config.mxl文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
<?xml version="1.0" encoding="UTF-8"?>
<c3p0-config>
<default-config>
<property name="driverClass">com.mysql.jdbc.Driver</property>
<property name="jdbcUrl">jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai</property>
<property name="user">root</property>
<property name="password">123456</property>
<!-- 扩展配置 -->
<!-- 获取连接超时设置,默认是一直等待,单位毫秒 -->
<property name="checkoutTimeout">30000</property>
<!--每多少秒检查所有连接池中的空闲连接。Default: 0 -->
<property name="idleConnectionTestPeriod">30</property>
<!-- 初始化连接池的连接数 -->
<property name="initialPoolSize">10</property>
<!--最大空闲时间,多少秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 -->
<property name="maxIdleTime">30</property>
<!--连接池中保留的最大连接数。Default: 15 -->
<property name="maxPoolSize">100</property>
<!-- 连接池中保留的最小连接数 -->
<property name="minPoolSize">10</property>
<!--JDBC的标准参数,用以控制数据源内加载的PreparedStatements数量。但由于预缓存的statements属于单个connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素。
如果maxStatements与maxStatementsPerConnection均为0,则缓存被关闭。Default:0 -->
<property name="maxStatements">200</property>
<!--maxStatementsPerConnection定义了连接池内单个连接所拥有的最大缓存statements数。Default: 0 -->
<property name="maxStatementsPerConnection">200</property>
<!--c3p0是异步操作的,缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能 通过多线程实现多个操作同时被执行。Default:3 -->
<property name="numHelperThreads">3</property>
</default-config>
</c3p0-config>
  • 在src——main——java——util里添加DataSourceUtils类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package util;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import javax.sql.DataSource;

public class DataSourceUtils {
private static ComboPooledDataSource dataSource = new ComboPooledDataSource();

/**
* 获取数据源
* @return 连接池
*/
public static DataSource getDataSource() {
return dataSource;
}
}

  • 新建src-main-java-C3p0Select类,用数据库连接池的方式查询
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import util.DataSourceUtils;

import java.sql.SQLException;
import java.util.List;

public class C3p0Select {

public static void main(String[] args) throws SQLException {
// 创建dbUtils里面的QueryRunner对象,并获取数据库连接
QueryRunner queryRunner = new QueryRunner(DataSourceUtils.getDataSource());
// String sqlSelect = "select * from class where id>?";
String sqlSelect = "select * from class";
Object[] params = {16};

//执行查询,并以数组的形式返回查询结果(new BeanListHandler()返回查询到的所有记录,并转成对象)
// List<Classs> lists = queryRunner.query(sqlSelect,new BeanListHandler<Classs>(Classs.class), params);
List<Classs> lists = queryRunner.query(sqlSelect,new BeanListHandler<Classs>(Classs.class));
for(Classs item: lists) {
System.out.println("多行查询:" + item.getName());
}
System.out.println(lists);
}
}

  • 得到结果
1
2
3
4
多行查询:综艺一般
多行查询:五年一班
多行查询:王大炮
多行查询:刘大师

注意

  • 此练习代码主要来自这里

配置

  • 打开官网 下载bin.zip压缩文件,解压到本地后,设置环境变量
1
2
3
4
5
6
C:\Users\Administrator>mvn -version
Apache Maven 3.9.5 (57804ffe001d7215b5e7bcb531cf83df38f93546)
Maven home: E:\exe\apache-maven-3.9.5
Java version: 1.8.0_381, vendor: Oracle Corporation, runtime: E:\app\Java\jdk-1.8\jre
Default locale: zh_CN, platform encoding: GBK
OS name: "windows 10", version: "10.0", arch: "amd64", family: "windows"
  • 在文件中我们要在文件中对下载相对于的jar包进行镜像配置,一般是使用阿里云镜像。
1
2
3
4
5
6
7
8
// apache-maven-3.9.5\conf\setting.xml
<!-- 拉去阿里镜像-->
<mirror>
<id>nexus-aliyun</id>
<mirrorOf>*</mirrorOf>
<name>Nexus aliyun</name>
<url>http://maven.aliyun.com/nexus/content/groups/public</url>
</mirror>
  • 们还需要在里面配置本地的Maven仓库。下面这句话就是我的本地仓库位置。
1
<localRepository>D:\Maven\mm</localRepository>
  • 打开Idea然后选择File =》New =》 Project 得到下面的界面

image-20231114165504799

  • 在文件的创建过程中我们要选择修改Maven的仓库为本地的仓库

image-20231114170122121

  • 一个使用Maven管理的普通的Java项目,它的目录结构默认如下,如果没有就手动新建
1
2
3
4
5
6
7
8
9
10
testMvn
├── pom.xml
├── src
│ ├── main
│ │ ├── java
│ │ └── resources
│ └── test
│ ├── java
│ └── resources
└── target
  • pom.xml就是引用依赖包

  • 存放Java源码的目录是src/main/java

  • 存放资源文件的目录是src/main/resources

  • 存放测试源码的目录是src/test/java,存放测试资源的目录是src/test/resources

  • 最后,所有编译、打包生成的文件都放在target目录里

分析pom

  • 分析pom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.example</groupId> <!--类似于Java的包名,通常是公司或组织名称 -->
<artifactId>testMvn</artifactId> <!--类似于Java的类名,通常是项目名称 -->
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>

</project>
  • 一个Maven工程就是由groupIdartifactIdversion作为唯一标识。我们在引用其他第三方库的时候,也是通过这3个变量确定。例如,依赖commons-logging
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>org.example</groupId>
<artifactId>testMvn</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
</project>

使用<dependency>声明一个依赖后,Maven就会自动下载这个依赖包并把它放到classpath中

在D:\Maven\mm 本地仓库中可以看到自动下载的依赖文件

依赖关系

Maven定义了几种依赖关系,分别是compiletestruntimeprovided

scope 说明 示例
compile 编译时需要用到该jar包(默认) commons-logging
test 编译Test时需要用到该jar包 junit
runtime 编译时不需要,但运行时需要用到 mysql
provided 编译时需要用到,但运行时由JDK或某个服务器提供 servlet-api
  • 其中,默认的compile是最常用的,Maven会把这种类型的依赖直接放入classpath。

  • test依赖表示仅在测试时使用,正常运行时并不需要。最常用的test依赖就是JUnit:

1
2
3
4
5
6
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.3.2</version>
<scope>test</scope>
</dependency>

搜索第三方组件

后一个问题:如果我们要引用一个第三方组件,比如okhttp,如何确切地获得它的groupIdartifactIdversion?方法是通过search.maven.org搜索关键字,找到对应的组件后,直接复制:

image-20231114174716157

常用命令

  • mvn clean:清理所有生成的class和jar;

  • mvn clean compile:先清理,再执行到compile

  • mvn clean test:先清理,再执行到test,因为执行test前必须执行compile,所以这里不必指定compile

  • mvn clean package:先清理,再执行到package

更多详解参考这里,后续这里还有很多关于maven的知识等能用到的时候回头继续学习

socker

  • Socket又称“套接字”,应用程序通常通过“套接字”向网络发出请求或者应答网络请求
  • 常用的Socket类型有两种:流式Socket和数据报式Socket,流式是一种面向连接的Socket,针对于面向连接的TCP服务应用,数据报式Socket是一种无连接的Socket,针对于无连接的UDP服务应用
  • TCP:比较靠谱,面向连接,比较慢
  • UDP:不是太靠谱,比较快
  • 举个例子:TCP就像货到付款的快递,送到家还必须见到你人才算一整套流程。UDP就像某快递快递柜一扔就走管你收到收不到,一般直播用UDP。

TCP编程

server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import java.net.*;
import java.io.*;
public class GreetingServer extends Thread {
private ServerSocket serverSocket;
public GreetingServer(int port) throws IOException {
serverSocket = new ServerSocket(port);
// serverSocket.setSoTimeout(1000);

}
public void run() {
try {
System.out.println("等待远程连接,端口号为:" + serverSocket.getLocalPort() + "...");
// 连接服务器
Socket server = serverSocket.accept();
System.out.println("客户端已连接:" + server.getInetAddress());
//构建IO流
while(true){
// 接受客户端
BufferedReader br = new BufferedReader(new InputStreamReader(server.getInputStream()));
String mess = br.readLine();
System.out.println("接受客户端:" + mess);

// 发送信息到客户端
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(server.getOutputStream()));
//如果客户端发送内容q,那么服务器就断开连接
if (mess.equals("q")) {
String str1 = "服务器返回>>bye\n";
bw.write(str1);
bw.flush();
System.out.println("服务器关闭");

break;
} else {
String str = "服务器返回>>"+mess+"\n";
bw.write(str);
bw.flush();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
try{
Thread t = new GreetingServer(6666);
t.run();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}


如果客户端发送内容q,那么服务器就断开连接

client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 文件名 GreetingClient.java

import java.net.*;
import java.io.*;
import java.util.Scanner;

public class GreetingClient
{
public static void main(String [] args)
{

try {
Socket s = new Socket("127.0.0.1",6666);
System.out.println("客户端IP:"+s.getLocalAddress()+"端口"+s.getPort());
//构建IO流
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));

//建立键盘输入:
Scanner scanner = new Scanner(System.in);
while(true){
System.out.println("请输入发送消息内容:");
bw.write(scanner.nextLine()+"\n");
bw.newLine();
bw.flush();
BufferedReader br = new BufferedReader(new InputStreamReader(s.getInputStream()));
//读取服务器返回的消息数据
String ser_msg = br.readLine();
System.out.println(ser_msg);
System.out.println(s.getInetAddress().getLocalHost()+":"+s.getPort()+">>"+ser_msg);
// 服务器发送bye的内容后,服务器就主动断开链接,客户端也要关闭
if (ser_msg.contains("bye")) {
System.out.println("客户端关闭");
break;
}
}

} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}

}
}

客户端输入q后,接收到服务器发送bye的内容后,服务器就主动断开链接,客户端也要关闭

http

server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpServer;

public class SimpleHttpServer {
public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8000), 0);
server.createContext("/get", new GetHandler());
server.createContext("/post", new PostHandler());
server.setExecutor(null); // creates a default executor
server.start();
System.out.println("Server is listening on port 8000");
}

// get请求处理
static class GetHandler implements HttpHandler {
// 重写handle方法
@Override
public void handle(HttpExchange exchange) throws IOException {
String requestParamValue = null;
if ("GET".equals(exchange.getRequestMethod())) {
requestParamValue = handleGetRequest(exchange);
}
String response = "GET request received with parameter: " + requestParamValue;
sendResponse(exchange, response);
}
}
// post请求处理
static class PostHandler implements HttpHandler {
@Override
public void handle(HttpExchange exchange) throws IOException {
String requestParamValue = null;
if ("POST".equals(exchange.getRequestMethod())) {
requestParamValue = handlePostRequest(exchange);
}
String response = "POST request received with JSON data: " + requestParamValue;
sendResponse(exchange, response);
}
}
// 获取get请求的参数
private static String handleGetRequest(HttpExchange exchange) {
return exchange.getRequestURI().getQuery();
}

// 获取post请求的参数
private static String handlePostRequest(HttpExchange exchange) throws IOException {
// 读取客户端发送的JSON数据
StringBuilder sb = new StringBuilder();
int i;
while ((i = exchange.getRequestBody().read()) != -1) {
sb.append((char) i);
}
return sb.toString();
}
// 发送请求到客户端
private static void sendResponse(HttpExchange exchange, String response) throws IOException {
exchange.sendResponseHeaders(200, response.length());
OutputStream os = exchange.getResponseBody();
os.write(response.getBytes());
os.close();
}
}

client

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;

public class SimpleHttpClient {
public static void main(String[] args) throws Exception {
sendGetRequest();
sendPostRequest();
}

private static void sendGetRequest() throws Exception {
String url = "http://localhost:8000/get?param1=value1&param2=value2";
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
// 发送get请求
con.setRequestMethod("GET");
// 通过con.getInputStream() 获取到与服务器建立的输入流,即服务端返回的数据流
//InputStreamReader将字节流转换为字符流,而BufferedReader提供了缓冲和高效读取的功能,常常用来包装InputStreamReader以提供更好的读取性能。
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
//不断地调用 BufferedReader 的 readLine() 方法,每次读取一行数据,并将其加入到 StringBuffer 中。
// 当 readLine() 方法返回 null 时,表示已经读取完了所有的数据,循环结束
while ((inputLine=in.readLine()) != null) {
response.append(inputLine);
}
in.close();

// 打印服务器的响应
System.out.println("GET Response from server: " + response.toString());
}

private static void sendPostRequest() throws Exception {
String url = "http://localhost:8000/post";
URL obj = new URL(url);
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/json");

String jsonInputString = "{\"name\":\"John\", \"age\":30, \"city\":\"New York\"}";

con.setDoOutput(true);
try (OutputStream os = con.getOutputStream()) {
byte[] input = jsonInputString.getBytes("utf-8");
os.write(input, 0, input.length);
}

BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();

while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
in.close();

// 打印服务器的响应
System.out.println("POST Response from server: " + response.toString());
}
}

  • BufferedReader 和InputStreamReader用法,一般都是BufferedReader 包装InputStreamReader。

  • StringBuffer 是一个可变字符串,作用是在处理大量字符串拼接操作时提供更高效的性能

  • 运行服务端和客户端代码得到结果:
1
2
GET Response from server: GET request received with parameter: param1=value1&param2=value2
POST Response from server: POST request received with JSON data: {"name":"John", "age":30, "city":"New York"}

场景

  • 本次java 版本
1
2
3
4
C:\Users\Administrator>java -version
java version "1.8.0_381"
Java(TM) SE Runtime Environment (build 1.8.0_381-b09)
Java HotSpot(TM) 64-Bit Server VM (build 25.381-b09, mixed mode
  • 泛型的本质是为了将类型参数化,数据类型被设置为一个参数,在使用时再从外部传入一个数据类型;而一旦传入了具体的数据类型后,传入变量(实参)的数据类型如果不匹配,编译器就会直接报错

  • 在 ArrayList 集合中,可以放入所有类型的对象,假设现在需要一个只存储了 String 类型对象的 ArrayList 集合

1
2
3
4
5
6
7
ArrayList list = new ArrayList();
list.add("aaa");
list.add("bbb");
list.add("ccc");
for (int i = 0; i < list.size(); i++) {
System.out.println((String)list.get(i));
}
  • 但如果在添加 String 对象时,不小心添加了一个 Integer 对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import java.util.ArrayList;

public class Main {
public static void main(String args[]) {
ArrayList list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add(111); //新增数字
for (int i = 0; i < list.size(); i++) {
System.out.println((String)list.get(i));
}
}
}



  • 上述代码在编译时没有报错,但在运行时却抛出了一个 ClassCastException 异常,其原因是 Integer 对象不能强转为 String 类型
1
2
   Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at Main.main(Main.java:11)
  • 使用泛型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.ArrayList;

public class Main {
public static void main(String args[]) {
ArrayList<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
list.add("ccc");
list.add(111); // 编译阶段,编译器会报错
for (int i = 0; i < list.size(); i++) {
System.out.println((String)list.get(i));
}
}
}

image-20231113153841330

  • 因此,当具体的数据类型确定后,泛型又提供了一种类型安全检测机制,只有数据类型相匹配的变量才能正常的赋值,否则编译器就不通过。所以说,泛型一定程度上提高了软件的安全性,防止出现低级的失误。

  • 泛型有三种使用方式,分别为:泛型类、泛型接口、泛型方法

泛型类

  • 定义
1
2
3
4
5
6
7
class 类名称 <泛型标识> {
private 泛型标识 /*(成员变量类型)*/ 变量名;
.....

}
}

  • 尖括号 <> 中的 泛型标识被称作是类型参数,用于指代任何数据类型
  • 泛型标识是任意设置的(如果你想可以设置为 Hello都行),Java 常见的泛型标识以及其代表含义如下:
1
2
3
4
5
6
T :代表一般的任何类。
E :代表 Element 元素的意思,或者 Exception 异常的意思。
K :代表 Key 的意思。
V :代表 Value 的意思,通常与 K 一起配合使用。
S :代表 Subtype 的意思,文章后面部分会讲解示意。

  • 举例如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Generic<T> { 
// key 这个成员变量的数据类型为 T, T 的类型由外部传入
private T key;
// 泛型构造方法形参 key 的类型也为 T,T 的类型由外部传入
public Generic(T key) {
this.key = key;
}

// 泛型方法 getKey 的返回值类型为 T,T 的类型由外部指定
public T getKey(){
return key;
}
}

  • 泛型类中的静态方法和静态变量不可以使用泛型类所声明的类型参数
1
2
3
4
5
6
7
public class Test<T> {    
public static T one; // 编译错误
public static T show(T one){ // 编译错误
return null;
}
}

  • 而静态变量和静态方法在类加载时已经初始化,直接使用类名调用;在泛型类的类型参数未确定时,静态成员有可能被调用,因此泛型类的类型参数是不能在静态成员中使用的。
  • 泛型类不只接受一个类型参数,它还可以接受多个类型参数
1
2
3
4
5
6
7
8
9
10
11
12
13
public class MultiType <E,T> {
E value1;
T value2;

public E getValue1(){
return value1;
}

public T getValue2(){
return value2;
}
}

  • 看如下实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Generic<T> {
private T key;
public Generic(T key) {
this.key = key;
}
public T getKey() {
return key;
}
}


public class Main {
public static void main(String args[]) {
Generic<String> generic = new Generic<>("hello");
System.out.println(generic.getKey());

// // <> 中什么都不传入,等价于 Generic<Object> generic = new Generic<>();
Generic generic1 = new Generic("word");
System.out.println(generic1.getKey());
}
}

泛型接口

  • 定义
1
2
3
4
5
6
7
8
9
public interface 接口名<类型参数> {
...
}

public interface Inter<T> {
public abstract void show(T t) ;
}


  • 在泛型接口中,静态成员也不能使用泛型接口定义的类型参数
1
2
3
4
5
6
7
8
9
10
interface IUsb<U, R> {
int n = 10;
U name;// 报错! 接口中的属性默认是静态的,因此不能使用类型参数声明
R get(U u);// 普通方法中,可以使用类型参数
void hi(R r);// 抽象方法中,可以使用类型参数
default R method(U u) {
return null;
}
}

  • 定义一个接口 IA 继承了 泛型接口 IUsb,在 接口 IA 定义时必须确定泛型接口 IUsb 中的类型参数
1
2
3
4
//IA.java

// 在继承泛型接口时,必须确定泛型接口的类型参数
interface IA extends IUsb<String, Double> { }
  • AA继承了IA的接口,而IA接口继承了IUsb并指定了特定的泛型参数,因此AA其实就是实现了IUsb接口的方法get和hi方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 当去实现 IA 接口时,因为 IA 在继承 IUsu 接口时,指定了类型参数 U 为 String,R 为 Double
// 所以在实现 IUsb 接口的方法时,使用 String 替换 U,用 Double 替换 R
class AA implements IA {
public Double get(String s) {
System.out.println("AA 的Double get=" + s);
return null;
}
public void hi(Double d) {
System.out.println("AA hi=" +d);

}
public void test() {
System.out.println("test");

}
}
  • 运行代码
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Main {
public static void main(String args[]) {
AA aa = new AA();
aa.get("哈哈");
aa.hi(10.01);
aa.test();
}
}

// 结果
AA 的Double get=哈哈
AA hi=10.01
test
  • 当然也可以直接用:定义一个类 BB 实现了 泛型接口 IUsb,在 类 BB 定义时需要确定泛型接口 IUsb 中的类型参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 实现接口时,需要指定泛型接口的类型参数
// 给 U 指定 Integer, 给 R 指定了 Float
// 所以,当我们实现 IUsb 方法时,会使用 Integer 替换 U, 使用 Float 替换 R
class BB implements IUsb<Integer, Float> {
@Override
public Float get(Integer integer) {
System.out.println("BB Float get=" + integer);
return 10.11F;
}
@Override
public void hi(Float afloat) {
System.out.println("BB hi=" +afloat);

}
}
  • 定义一个类 CC 实现了 泛型接口 IUsb 时,若是没有确定泛型接口 IUsb 中的类型参数,则默认为 Object
1
2
3
4
5
6
7
8
9
10
11
12
13
public class CC implements IUsb{
@Override
public Object get(Object o) {
System.out.println("CC object get=" + o.toString());
return null;
}
@Override
public void hi(Object o) {
System.out.println("cc object hi=" + o.toString());

}
}

  • 定义一个类 DD 实现了 泛型接口 IUsb 时,若是没有确定泛型接口 IUsb 中的类型参数,也可以将 DD 类也定义为泛型类,其声明的类型参数必须要和接口 IUsb 中的类型参数相同
1
2
3
4
5
6
// DD 类定义为 泛型类,则不需要确定 接口的类型参数
// 但 DD 类定义的类型参数要和接口中类型参数的一致
class DD<U, R> implements IUsb<U, R> {
...
}

泛型方法

  • 当在一个方法签名中的返回值前面声明了一个 < T > 时,该方法就被声明为一个泛型方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public <类型参数> 返回类型 方法名(类型参数 变量名) {
...
}

public class Test<U> {
// 该方法只是使用了泛型类定义的类型参数,不是泛型方法
public void testMethod(U u){
System.out.println(u);
}

// <T> 真正声明了下面的方法是一个泛型方法
public <T> T testMethod1(T t){
return t;
}
}

  • 实现了普通方法和泛型方法
1
2
3
4
5
6
7
8
9
10
public class Test<T> {
// 是泛型类中的普通方法
public void testMethod(T t) {
System.out.println(t);
}
// 是一个泛型方法
public <T> T testMethod1(T t) {
return t;
}
}
  • 运行
1
2
3
4
5
6
7
8
9
public class Main {
public static void main(String args[]) {
Test test = new Test<Integer>();
test.testMethod("112");
String tt = (String) test.testMethod1("你好");
System.out.println(tt);
}
}

  • 当然泛型方法也可用到普通类中
1
2
3
4
5
6
7
8
9
10
public class Test {
// 普通方法
public void testMethod(String t) {
System.out.println(t);
}
// 是一个泛型方法
public <T> T testMethod1(T t) {
return t;
}
}
  • 在调用泛型方法的时候,可以显式地指定类型参数,也可以不指定
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Test {

// 这是一个简单的泛型方法
public static <T> T add(T x, T y) {
return y;
}

public static void main(String[] args) {
// 一、不显式地指定类型参数
//(1)传入的两个实参都是 Integer,所以泛型方法中的<T> == <Integer>
int i = Test.add(1, 2);

//(2)传入的两个实参一个是 Integer,另一个是 Float,
// 所以<T>取共同父类的最小级,<T> == <Number>
Number f = Test.add(1, 1.2);

// 传入的两个实参一个是 Integer,另一个是 String,
// 所以<T>取共同父类的最小级,<T> == <Object>
Object o = Test.add(1, "asd");

// 二、显式地指定类型参数
//(1)指定了<T> = <Integer>,所以传入的实参只能为 Integer 对象
int a = Test.<Integer>add(1, 2);

//(2)指定了<T> = <Integer>,所以不能传入 Float 对象
int b = Test.<Integer>add(1, 2.2);// 编译错误

//(3)指定<T> = <Number>,所以可以传入 Number 对象
// Integer 和 Float 都是 Number 的子类,因此可以传入两者的对象
Number c = Test.<Number>add(1, 2.2);
}
}

泛型类,在创建类的对象的时候确定类型参数的具体类型;
泛型方法,在调用方法的时候再确定类型参数的具体类型。

总结

  • 后续还有更多高级的用法,比如类型擦除、通配符等有空继续按照此博主内容进行学习

说明

本次笔记主要记录面向对象中的继承、重载重写、多态、抽象、封装、接口、包等概念

java版本为:java version “1.8.0_381”

继承

  • 继承格式有两种,方式一:
1
2
3
4
5
class 父类 {
}

class 子类 extends 父类 {
}
  • 方式二
1
2
3
4
5
6
7
8
9
10
11
public interface A {
public void eat();
public void sleep();
}

public interface B {
public void show();
}

public class C implements A,B {
}
  • 代码实例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// 动物类为父类主要包的动作为吃、睡觉、介绍等方法
public class Animal {
private String name;
private int id;
// 重载,主要用来初始化name 和id
public Animal(String myName, int myId) {
name = myName;
id = myId;
}
public void eat(){
System.out.println(name+"正在吃");
}
public void sleep(){
System.out.println(name+"正在睡");
}
public void introduction() {
System.out.println("大家好!我是" + id + "号" + name + ".");
}
}
// 继承父类
public class Mouse extends Animal{
public Mouse(String myName,int myId) {
// 调用的就是Animal父类的重载函数Animal(String myName, int myId)
super(myName, myId);
}
public void eat() {
System.out.println("子类:我吃完了");
}
public void eatTest() {
// 调用父类方法
super.eat();
// this指向子类本身
this.eat();

}
}

//super用来调用父类方法
// this指向子类本身

  • 得到结果
1
2
3
4
5
6
7
8
9
10

public class Main {
public static void main(String args[]) {
Mouse mose = new Mouse("老鼠",1);
mose.eatTest();
}
}
----
老鼠正在吃
子类:我吃完了

重写

  • 重写是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// 动物类为父类主要包的动作为吃、睡觉、介绍等方法
public class Animal {
private String name;
private int id;
// 重载,主要用来初始化name 和id
public Animal(String myName, int myId) {
name = myName;
id = myId;
}
public void eat(){
System.out.println(name+"正在吃");
}
public void sleep(){
System.out.println(name+"正在睡");
}
public void introduction() {
System.out.println("大家好!我是" + id + "号" + name + ".");
}
}

//子类
public class Mouse extends Animal{
private String Name;
private int Id;
public Mouse(String myName,int myId) {
// 调用的就是Animal父类的重载函数Animal(String myName, int myId)
super(myName, myId);
this.Name = myName;
this.Id = myId;
}
public void eat() {
System.out.println(this.Name+ ":我吃完了");
}
// 自定义方法
public void dark() {
System.out.println("老鼠可以在叫");
}
}


import java.io.*;

public class Main {
public static void main(String args[]) {
Animal a = new Animal("我是动物父类",1001);
Animal b = new Mouse("老鼠",1);
a.eat(); // 调用父类的eat方法,结果为:我是动物父类正在吃
b.eat(); // 尽管b属于Animal类型,但是它运行的是 Mouse子类的eat,这里重写了父类的eat方法,结果为:老鼠:我吃完
b.sleep(); //同时b调用分类本身的sleep
// 总结:当分类指向子类时
// 1.若子类和父类使用相同方法eat,那么子类中的方法eat重写了父类中的方法eat
// 2.父类可直接调用父类中方法
// 3.子类中的方法在父类中不存在,那么指定调用子类中的方法报错

// 这里标红报错了
b.dark();

}
}

当需要在子类中调用父类的被重写方法时,要使用 super 关键字。继承中已经介绍

重载

  • 重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Overloading {
public int test(){
System.out.println("test1");
return 1;
}

public void test(int a){
System.out.println("test2");
}

//以下两个参数类型顺序不同
public String test(int a,String s){
System.out.println("test3");
return "returntest3";
}

public String test(String s,int a){
System.out.println("test4");
return "returntest4";
}

public static void main(String[] args){
Overloading o = new Overloading();
System.out.println(o.test());
o.test(1);
System.out.println(o.test(1,"test3"));
System.out.println(o.test("test4",1));
}
}

实例中重载方法为test

多态

  • 多态存在的三个条件:继承、重写、父类引用指向子类对象,在重写实例中刚好包含了这三要素。

抽象

抽象类

  • 如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。

  • 抽象类除了不能实例化对象之外,类的其它功能依然存在,抽象类必须被继承,才能被使

  • 一个类只能继承一个抽象类,而一个类却可以实现多个接口(留作后续)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// 动物类为父类主要包的动作为吃、睡觉、介绍等方法
public abstract class Animal {
private String name;
private int id;
// 重载,主要用来初始化name 和id
public Animal(String myName, int myId) {
name = myName;
id = myId;
}
public void eat(){
System.out.println(name+"正在吃");
}
public void sleep(){
System.out.println(name+"正在睡");
}
public void introduction() {
System.out.println("大家好!我是" + id + "号" + name + ".");
}
}

// 子类
public class Mouse extends Animal{
private String Name;
private int Id;
public Mouse(String myName,int myId) {
// 调用的就是Animal父类的重载函数Animal(String myName, int myId)
super(myName, myId);
this.Name = myName;
this.Id = myId;
}
public void eat() {
System.out.println(this.Name+ ":我吃完了");
}
// 自定义方法
public void dark() {
System.out.println("老鼠可以在叫");
}
}


public class Main {
public static void main(String args[]) {
// 这样不行报错了,不可用直接实例化
// Animal a = new Animal("我是动物父类",1001);
Mouse mouse = new Mouse("老鼠", 1001);
Animal a = new Mouse("老鼠2号", 1002);
a.eat(); //老鼠2号:我吃完了
mouse.eat(); // 老鼠:我吃完了
mouse.sleep();
// 子类中的方法在父类中不存在,调用子类中的方法报错
//a.dark();
}
}

  • 和重写的那个实例非常接近,唯一区别就是不用实例化父类
  • 根据本实例发现既然无法实例化父类,那么父类中的里具体逻辑存在没有必要,只要存在一个方法名就可以了

抽象方法

  • 如果你想设计这样一个类,该类包含一个特别的成员方法,该方法的具体实现由它的子类确定,那么你可以在父类中声明该方法为抽象方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// 分类
public abstract class Animal {
// 重载,主要用来初始化name 和id
public abstract void eat();
public abstract void sleep(String myName);
public abstract String test();
}

// 子类
public class Mouse extends Animal{
private String Name;
private int Id;
public Mouse(String myName,int myId) {
this.Name = myName;
this.Id = myId;
}
public void eat() {
System.out.println(this.Name+ ":我吃完了");
}
public void sleep(String myName) {
System.out.println(myName+ "在睡觉");
}
public void dark() {
System.out.println("老鼠可以在叫");
}
public String test() {
return "我是一只老鼠";
}
}


public class Main {
public static void main(String args[]) {
Mouse mouse = new Mouse("老鼠", 1001);
Animal a = new Mouse("老鼠2号", 1002);
a.eat(); //老鼠2号:我吃完了
mouse.eat(); // 老鼠:我吃完了
String ts = a.test();
System.out.println(ts); // 我是一只老鼠
a.sleep("我是老鼠啊");// 我是老鼠啊在睡觉
mouse.dark();
// a.dark(); 子类中的方法在父类中不存在,调用子类中的方法报错
}
}
  • 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
  • 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能
  • 抽象类的子类必须给出抽象类中的抽象方法的具体实现,除非该子类也是抽象类

接口

  • 接口和抽象类很像,他们是有区别的

  • 接口和抽象类区别

    • 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行

    • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的

    • 一个类只能继承一个抽象类,而一个类却可以实现多个接口

    • 接口是隐式抽象的,当声明一个接口和方法的时候,不必使用abstract关键字

  • 实战代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 定义接口
interface Animal {
public void eat();
public void sleep(String myName);
public String test();
}

// 实现接口
public class Mouse implements Animal{
private String Name;
private int Id;
public Mouse(String myName,int myId) {
this.Name = myName;
this.Id = myId;
}
public void eat() {
System.out.println(this.Name+ ":我吃完了");
}
public void sleep(String myName) {
System.out.println(myName+ "在睡觉");
}
public void dark() {
System.out.println("老鼠可以在叫");
}
//如果不实现接口中的test方法,那么就报错了
public String test() {
return "我是一只老鼠";
}
}


public class Main {
public static void main(String args[]) {
Mouse mouse = new Mouse("老鼠", 1001);
mouse.eat(); // 老鼠:我吃完了
String ts = mouse.test();
System.out.println(ts); // 我是一只老鼠
mouse.sleep("我是老鼠啊");// 我是老鼠啊在睡觉
mouse.dark(); // 老鼠可以在叫
}
}

当类实现接口的时候,类要实现(重写)接口中所有的方法,不然报错。否则,类必须声明为抽象的类

接口继承

  • 感觉这里太复杂,不做详细介绍,做个简单笔记。
  • 一个接口能继承另一个接口,和类之间的继承方式比较相似。接口的继承使用extends关键字,子接口继承父接口的方法。

下面的Sports接口被Hockey和Football接口继承:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 文件名: Sports.java
public interface Sports
{
public void setHomeTeam(String name);
public void setVisitingTeam(String name);
}

// 文件名: Football.java
public interface Football extends Sports
{
public void homeTeamScored(int points);
public void visitingTeamScored(int points);
public void endOfQuarter(int quarter);
}

// 文件名: Hockey.java
public interface Hockey extends Sports
{
public void homeGoalScored();
public void visitingGoalScored();
public void endOfPeriod(int period);
public void overtimePeriod(int ot);
}

在Java中,类的多继承是不合法,但接口允许多继承。

在接口的多继承中extends关键字只需要使用一次,在其后跟着继承接口。 如下所示

1
public interface Hockey extends Sports, Event