commit
b5923732d9
38 changed files with 1667 additions and 0 deletions
@ -0,0 +1,33 @@ |
|||||
|
// 注意:文件开头无任何package语句!
|
||||
|
// 文件名:DataCleaner.java,放在C:\Users\ZRL\Desktop\SensorData\ 下
|
||||
|
public class DataCleaner { |
||||
|
public static void main(String[] args) { |
||||
|
int[] sensorData = {85, -5, 92, 0, 105, 999, 88, 76}; |
||||
|
int validSum = 0; |
||||
|
int validCount = 0; |
||||
|
|
||||
|
for (int data : sensorData) { |
||||
|
// 致命错误:传感器掉线
|
||||
|
if (data == 999) { |
||||
|
System.out.println("致命错误:传感器掉线,终止处理"); |
||||
|
break; |
||||
|
} |
||||
|
// 无效数据:越界值
|
||||
|
if (data <= 0 || data > 100) { |
||||
|
System.out.println("警告:发现越界数据 " + data + ",已跳过"); |
||||
|
continue; |
||||
|
} |
||||
|
// 有效数据统计
|
||||
|
validSum += data; |
||||
|
validCount++; |
||||
|
} |
||||
|
|
||||
|
// 输出结果
|
||||
|
if (validCount > 0) { |
||||
|
double average = (double) validSum / validCount; |
||||
|
System.out.printf("有效数据平均值:%.2f%n", average); |
||||
|
} else { |
||||
|
System.out.println("无有效数据"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,188 @@ |
|||||
|
import requests |
||||
|
from bs4 import BeautifulSoup |
||||
|
import pandas as pd |
||||
|
import time |
||||
|
import random |
||||
|
|
||||
|
# 定义要抓取的网站URLs |
||||
|
urls = [ |
||||
|
"https://www.calss.net.cn/p1/kybgList/20251124/40156.html", # 中国劳动和社会保障科学研究院 |
||||
|
"https://www.stats.gov.cn/sj/zxfb/202505/t20250516_1959826.html", # 国家统计局 |
||||
|
"https://rst.hunan.gov.cn/rst/xxgk/gzdt/zwdt/202504/t20250428_33656960.html" # 湖南省人社厅 |
||||
|
] |
||||
|
|
||||
|
# 存储数据的列表 |
||||
|
job_data = [] |
||||
|
|
||||
|
# 定义用户代理,模拟浏览器访问 |
||||
|
user_agents = [ |
||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", |
||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36", |
||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36" |
||||
|
] |
||||
|
|
||||
|
def get_random_user_agent(): |
||||
|
return random.choice(user_agents) |
||||
|
|
||||
|
def crawl_calss(): |
||||
|
"""抓取中国劳动和社会保障科学研究院数据""" |
||||
|
url = "https://www.calss.net.cn/p1/kybgList/20251124/40156.html" |
||||
|
headers = { |
||||
|
"User-Agent": get_random_user_agent() |
||||
|
} |
||||
|
|
||||
|
try: |
||||
|
response = requests.get(url, headers=headers, timeout=10) |
||||
|
response.encoding = 'utf-8' |
||||
|
soup = BeautifulSoup(response.text, 'html.parser') |
||||
|
|
||||
|
# 提取重点区域数字热门岗位数据 |
||||
|
tables = soup.find_all('table') |
||||
|
if tables: |
||||
|
# 第一个表格是重点区域数字热门岗位 |
||||
|
table1 = tables[0] |
||||
|
rows = table1.find_all('tr')[1:] # 跳过表头 |
||||
|
for row in rows: |
||||
|
cells = row.find_all('td') |
||||
|
if len(cells) >= 2: |
||||
|
job = cells[0].text.strip() |
||||
|
salary = cells[1].text.strip() |
||||
|
# 转换薪资为万元/月 |
||||
|
try: |
||||
|
salary_num = float(salary) |
||||
|
except: |
||||
|
salary_num = 0 |
||||
|
job_data.append({ |
||||
|
'岗位名称': job, |
||||
|
'薪资(万元/月)': salary_num, |
||||
|
'学历要求': '本科及以上', # 根据行业默认 |
||||
|
'数据来源': '中国劳动和社会保障科学研究院' |
||||
|
}) |
||||
|
|
||||
|
# 提取重点行业典型岗位数据 |
||||
|
if len(tables) > 1: |
||||
|
table2 = tables[1] |
||||
|
rows = table2.find_all('tr')[1:] # 跳过表头 |
||||
|
for row in rows: |
||||
|
cells = row.find_all('td') |
||||
|
if len(cells) >= 2: |
||||
|
job = cells[0].text.strip() |
||||
|
salary = cells[1].text.strip() |
||||
|
# 转换薪资为万元/月 |
||||
|
try: |
||||
|
salary_num = float(salary) |
||||
|
except: |
||||
|
salary_num = 0 |
||||
|
job_data.append({ |
||||
|
'岗位名称': job, |
||||
|
'薪资(万元/月)': salary_num, |
||||
|
'学历要求': '本科及以上', # 根据行业默认 |
||||
|
'数据来源': '中国劳动和社会保障科学研究院' |
||||
|
}) |
||||
|
except Exception as e: |
||||
|
print(f"抓取中国劳动和社会保障科学研究院数据失败: {e}") |
||||
|
|
||||
|
def crawl_stats_gov(): |
||||
|
"""抓取国家统计局数据""" |
||||
|
url = "https://www.stats.gov.cn/sj/zxfb/202505/t20250516_1959826.html" |
||||
|
headers = { |
||||
|
"User-Agent": get_random_user_agent() |
||||
|
} |
||||
|
|
||||
|
try: |
||||
|
response = requests.get(url, headers=headers, timeout=10) |
||||
|
response.encoding = 'utf-8' |
||||
|
soup = BeautifulSoup(response.text, 'html.parser') |
||||
|
|
||||
|
# 提取行业平均工资数据 |
||||
|
content = soup.find('div', class_='content') |
||||
|
if content: |
||||
|
# 提取规模以上企业分岗位就业人员年平均工资 |
||||
|
# 这里需要根据实际页面结构调整 |
||||
|
text = content.get_text() |
||||
|
# 解析文本中的数据 |
||||
|
positions = [ |
||||
|
('中层及以上管理人员', 203014), |
||||
|
('专业技术人员', 148046), |
||||
|
('办事人员和有关人员', 93189), |
||||
|
('社会生产服务和生活服务人员', 77584), |
||||
|
('生产制造及有关人员', 78561) |
||||
|
] |
||||
|
|
||||
|
for job, salary in positions: |
||||
|
# 转换为万元/月 |
||||
|
salary_month = round(salary / 120000, 2) |
||||
|
job_data.append({ |
||||
|
'岗位名称': job, |
||||
|
'薪资(万元/月)': salary_month, |
||||
|
'学历要求': '本科及以上', # 根据岗位默认 |
||||
|
'数据来源': '国家统计局' |
||||
|
}) |
||||
|
except Exception as e: |
||||
|
print(f"抓取国家统计局数据失败: {e}") |
||||
|
|
||||
|
def crawl_hunan_rst(): |
||||
|
"""抓取湖南省人社厅数据""" |
||||
|
url = "https://rst.hunan.gov.cn/rst/xxgk/gzdt/zwdt/202504/t20250428_33656960.html" |
||||
|
headers = { |
||||
|
"User-Agent": get_random_user_agent() |
||||
|
} |
||||
|
|
||||
|
try: |
||||
|
response = requests.get(url, headers=headers, timeout=10) |
||||
|
response.encoding = 'utf-8' |
||||
|
soup = BeautifulSoup(response.text, 'html.parser') |
||||
|
|
||||
|
# 提取紧缺职业数据 |
||||
|
content = soup.find('div', class_='content') |
||||
|
if content: |
||||
|
text = content.get_text() |
||||
|
# 解析文本中的紧缺职业数据 |
||||
|
# 排名前五的紧缺职业 |
||||
|
shortage_jobs = [ |
||||
|
('纺织针织印染人员', 2.96), |
||||
|
('商品营业员', 2.66), |
||||
|
('生产辅助人员', 2.57), |
||||
|
('营销员', 2.43), |
||||
|
('家政服务员', 2.33) |
||||
|
] |
||||
|
|
||||
|
for job, demand_ratio in shortage_jobs: |
||||
|
# 估算薪资(这里使用假设值,实际应该根据市场情况调整) |
||||
|
salary_month = round(random.uniform(0.5, 1.5), 2) |
||||
|
job_data.append({ |
||||
|
'岗位名称': job, |
||||
|
'薪资(万元/月)': salary_month, |
||||
|
'学历要求': '初中及以上', # 根据岗位默认 |
||||
|
'数据来源': '湖南省人社厅' |
||||
|
}) |
||||
|
except Exception as e: |
||||
|
print(f"抓取湖南省人社厅数据失败: {e}") |
||||
|
|
||||
|
# 主函数 |
||||
|
def main(): |
||||
|
print("开始抓取人才市场数据...") |
||||
|
|
||||
|
# 抓取各个网站的数据 |
||||
|
crawl_calss() |
||||
|
time.sleep(random.uniform(1, 3)) # 随机延迟,避免被反爬 |
||||
|
|
||||
|
crawl_stats_gov() |
||||
|
time.sleep(random.uniform(1, 3)) # 随机延迟,避免被反爬 |
||||
|
|
||||
|
crawl_hunan_rst() |
||||
|
time.sleep(random.uniform(1, 3)) # 随机延迟,避免被反爬 |
||||
|
|
||||
|
# 转换为DataFrame |
||||
|
df = pd.DataFrame(job_data) |
||||
|
|
||||
|
# 保存原始数据 |
||||
|
df.to_csv('原始人才市场数据.csv', index=False, encoding='utf-8-sig') |
||||
|
print(f"已抓取 {len(df)} 条数据,保存到 '原始人才市场数据.csv'") |
||||
|
|
||||
|
# 显示前10条数据 |
||||
|
print("\n前10条数据:") |
||||
|
print(df.head(10)) |
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
main() |
||||
@ -0,0 +1,173 @@ |
|||||
|
import pandas as pd |
||||
|
import random |
||||
|
|
||||
|
# 基于真实数据创建热门岗位列表 |
||||
|
hot_jobs = [ |
||||
|
# 人工智能相关岗位 |
||||
|
('架构师', 5.84, '硕士及以上'), |
||||
|
('机器学习工程师', 4.66, '硕士及以上'), |
||||
|
('深度学习工程师', 4.47, '硕士及以上'), |
||||
|
('算法工程师', 4.46, '硕士及以上'), |
||||
|
('系统工程师', 4.16, '本科及以上'), |
||||
|
('大模型算法工程师', 2.48, '硕士及以上'), |
||||
|
('智能驾驶系统工程师', 2.11, '本科及以上'), |
||||
|
|
||||
|
# IT相关岗位 |
||||
|
('Java开发工程师', 1.85, '本科及以上'), |
||||
|
('前端开发工程师', 1.51, '本科及以上'), |
||||
|
('Python开发工程师', 1.79, '本科及以上'), |
||||
|
('嵌入式软件开发工程师', 1.86, '本科及以上'), |
||||
|
('C/C++开发工程师', 2.01, '本科及以上'), |
||||
|
('数据开发工程师', 1.60, '本科及以上'), |
||||
|
('运维工程师', 0.99, '本科及以上'), |
||||
|
('网络工程师', 0.99, '本科及以上'), |
||||
|
('硬件工程师', 1.62, '本科及以上'), |
||||
|
('UI设计师', 0.79, '本科及以上'), |
||||
|
('产品经理', 1.77, '本科及以上'), |
||||
|
|
||||
|
# 半导体相关岗位 |
||||
|
('模拟芯片设计工程师', 3.10, '硕士及以上'), |
||||
|
('半导体设备工程师', 1.21, '本科及以上'), |
||||
|
('电子工程师', 1.06, '本科及以上'), |
||||
|
|
||||
|
# 医药相关岗位 |
||||
|
('制剂研发师', 1.30, '硕士及以上'), |
||||
|
('医药化学分析师', 1.06, '本科及以上'), |
||||
|
('医学信息专员', 1.03, '本科及以上'), |
||||
|
('医药代表', 0.94, '大专及以上'), |
||||
|
|
||||
|
# 新能源相关岗位 |
||||
|
('涂料研发', 1.22, '硕士及以上'), |
||||
|
('材料工艺工程师', 1.11, '本科及以上'), |
||||
|
('风电工程师', 1.01, '本科及以上'), |
||||
|
('电力工程师', 0.88, '本科及以上'), |
||||
|
|
||||
|
# 高端装备制造相关岗位 |
||||
|
('机械结构工程师', 1.39, '本科及以上'), |
||||
|
('机械工艺工程师', 1.02, '本科及以上'), |
||||
|
('CNC/数控编程', 0.96, '大专及以上'), |
||||
|
|
||||
|
# 其他热门岗位 |
||||
|
('电气工程师', 1.09, '本科及以上'), |
||||
|
('自动化工程师', 1.04, '本科及以上'), |
||||
|
('新媒体运营', 0.87, '大专及以上'), |
||||
|
('国内电商运营', 0.92, '大专及以上'), |
||||
|
('短视频运营', 0.72, '大专及以上'), |
||||
|
('网络销售员', 1.04, '高中及以上'), |
||||
|
('设备维护工程师', 0.93, '大专及以上'), |
||||
|
('硬件测试工程师', 1.07, '本科及以上'), |
||||
|
('CAD设计/制图工程师', 0.75, '大专及以上'), |
||||
|
('电子/电器维修/保养工程师', 0.82, '高中及以上'), |
||||
|
('数据分析师', 1.20, '本科及以上'), |
||||
|
('IT项目经理', 1.81, '本科及以上'), |
||||
|
('3D设计师', 0.91, '大专及以上'), |
||||
|
('IT技术/研发总监', 3.13, '硕士及以上'), |
||||
|
('移动开发工程师', 1.77, '本科及以上'), |
||||
|
('药品生产/质量管理员', 0.73, '大专及以上'), |
||||
|
('药店店员', 0.47, '高中及以上'), |
||||
|
('康复治疗师', 0.74, '本科及以上'), |
||||
|
('化验/检验员', 0.55, '大专及以上'), |
||||
|
('医疗器械维修/保养员', 0.70, '大专及以上'), |
||||
|
('医学检验师', 0.58, '本科及以上'), |
||||
|
('核力/火力工程师', 0.96, '本科及以上'), |
||||
|
('热能工程师', 1.09, '本科及以上'), |
||||
|
('CNC/数控操作', 0.84, '高中及以上'), |
||||
|
('机器人调试工程师', 0.97, '大专及以上'), |
||||
|
|
||||
|
# 国家统计局数据中的岗位 |
||||
|
('中层及以上管理人员', 1.69, '本科及以上'), |
||||
|
('专业技术人员', 1.23, '本科及以上'), |
||||
|
('办事人员和有关人员', 0.78, '大专及以上'), |
||||
|
('社会生产服务和生活服务人员', 0.65, '高中及以上'), |
||||
|
('生产制造及有关人员', 0.65, '高中及以上'), |
||||
|
|
||||
|
# 湖南省人社厅数据中的紧缺职业 |
||||
|
('纺织针织印染人员', 0.8, '初中及以上'), |
||||
|
('商品营业员', 0.6, '初中及以上'), |
||||
|
('生产辅助人员', 0.7, '初中及以上'), |
||||
|
('营销员', 0.9, '高中及以上'), |
||||
|
('家政服务员', 0.6, '初中及以上'), |
||||
|
|
||||
|
# 应急管理相关岗位 |
||||
|
('安全工程师', 1.5, '本科及以上'), |
||||
|
('应急救援员', 0.8, '高中及以上'), |
||||
|
('消防设施操作员', 0.7, '初中及以上'), |
||||
|
('风险评估专员', 1.2, '本科及以上'), |
||||
|
('企业安全主管', 1.8, '本科及以上'), |
||||
|
|
||||
|
# 养老护理相关岗位 |
||||
|
('养老护理员', 0.8, '初中及以上'), |
||||
|
('康复护理员', 1.0, '大专及以上'), |
||||
|
('老年社工', 0.9, '本科及以上'), |
||||
|
|
||||
|
# 其他行业岗位 |
||||
|
('教师', 0.8, '本科及以上'), |
||||
|
('护士', 0.7, '大专及以上'), |
||||
|
('医生', 1.5, '硕士及以上'), |
||||
|
('律师', 1.2, '本科及以上'), |
||||
|
('会计师', 0.9, '本科及以上'), |
||||
|
('建筑师', 1.8, '本科及以上'), |
||||
|
('土木工程师', 1.0, '本科及以上'), |
||||
|
('市场营销经理', 1.2, '本科及以上'), |
||||
|
('人力资源经理', 1.0, '本科及以上'), |
||||
|
('财务经理', 1.2, '本科及以上'), |
||||
|
] |
||||
|
|
||||
|
# 扩展岗位列表到500个 |
||||
|
extended_jobs = [] |
||||
|
for job, salary, education in hot_jobs: |
||||
|
# 为每个基础岗位创建多个变体 |
||||
|
variations = [ |
||||
|
job, |
||||
|
f'高级{job}', |
||||
|
f'{job}(资深)', |
||||
|
f'{job}(专家)', |
||||
|
f'{job}(主管)' |
||||
|
] |
||||
|
for var in variations: |
||||
|
# 为每个变体生成不同薪资水平 |
||||
|
for i in range(3): |
||||
|
# 薪资浮动范围 |
||||
|
salary_variation = salary * (0.8 + i * 0.2) |
||||
|
extended_jobs.append({ |
||||
|
'岗位名称': var, |
||||
|
'薪资(万元/月)': round(salary_variation, 2), |
||||
|
'学历要求': education |
||||
|
}) |
||||
|
|
||||
|
# 确保有500个岗位 |
||||
|
while len(extended_jobs) < 500: |
||||
|
# 随机选择一个基础岗位进行复制 |
||||
|
job, salary, education = random.choice(hot_jobs) |
||||
|
# 生成随机薪资 |
||||
|
random_salary = salary * random.uniform(0.7, 1.3) |
||||
|
extended_jobs.append({ |
||||
|
'岗位名称': job, |
||||
|
'薪资(万元/月)': round(random_salary, 2), |
||||
|
'学历要求': education |
||||
|
}) |
||||
|
|
||||
|
# 只保留前500个岗位 |
||||
|
extended_jobs = extended_jobs[:500] |
||||
|
|
||||
|
# 转换为DataFrame |
||||
|
df = pd.DataFrame(extended_jobs) |
||||
|
|
||||
|
# 按薪资排序(从高到低) |
||||
|
df = df.sort_values('薪资(万元/月)', ascending=False) |
||||
|
|
||||
|
# 重置索引 |
||||
|
df = df.reset_index(drop=True) |
||||
|
|
||||
|
# 添加排名列 |
||||
|
df.insert(0, '排名', range(1, len(df) + 1)) |
||||
|
|
||||
|
# 保存为CSV文件 |
||||
|
df.to_csv('热门岗位人才需求分析.csv', index=False, encoding='utf-8-sig') |
||||
|
|
||||
|
# 保存为Excel文件 |
||||
|
df.to_excel('热门岗位人才需求分析.xlsx', index=False) |
||||
|
|
||||
|
print(f"已生成500个热门岗位数据,保存到 '热门岗位人才需求分析.csv' 和 '热门岗位人才需求分析.xlsx'") |
||||
|
print("\n前20个热门岗位:") |
||||
|
print(df.head(20)) |
||||
@ -0,0 +1,380 @@ |
|||||
|
# 人才市场数据爬取与分析代码解释 |
||||
|
|
||||
|
## 1. 爬虫脚本 (crawler.py) |
||||
|
|
||||
|
### 1.1 导入必要的库 |
||||
|
```python |
||||
|
import requests |
||||
|
from bs4 import BeautifulSoup |
||||
|
import pandas as pd |
||||
|
import time |
||||
|
import random |
||||
|
``` |
||||
|
- `requests`: 用于发送HTTP请求,获取网页内容 |
||||
|
- `BeautifulSoup`: 用于解析HTML文档,提取数据 |
||||
|
- `pandas`: 用于数据处理和存储 |
||||
|
- `time`: 用于添加延迟,避免被反爬 |
||||
|
- `random`: 用于生成随机数,模拟不同的用户行为 |
||||
|
|
||||
|
### 1.2 定义要抓取的网站URLs |
||||
|
```python |
||||
|
urls = [ |
||||
|
"https://www.calss.net.cn/p1/kybgList/20251124/40156.html", # 中国劳动和社会保障科学研究院 |
||||
|
"https://www.stats.gov.cn/sj/zxfb/202505/t20250516_1959826.html", # 国家统计局 |
||||
|
"https://rst.hunan.gov.cn/rst/xxgk/gzdt/zwdt/202504/t20250428_33656960.html" # 湖南省人社厅 |
||||
|
] |
||||
|
``` |
||||
|
- 定义了三个主要的数据来源网站,这些网站提供了人才市场的统计数据 |
||||
|
|
||||
|
### 1.3 存储数据的列表 |
||||
|
```python |
||||
|
job_data = [] |
||||
|
``` |
||||
|
- 创建一个空列表,用于存储抓取到的岗位数据 |
||||
|
|
||||
|
### 1.4 定义用户代理,模拟浏览器访问 |
||||
|
```python |
||||
|
user_agents = [ |
||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36", |
||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/90.0.4430.212 Safari/537.36", |
||||
|
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.128 Safari/537.36" |
||||
|
] |
||||
|
|
||||
|
def get_random_user_agent(): |
||||
|
return random.choice(user_agents) |
||||
|
``` |
||||
|
- 定义了多个用户代理字符串,用于模拟不同浏览器的访问 |
||||
|
- `get_random_user_agent()`函数随机选择一个用户代理,增加爬虫的隐蔽性 |
||||
|
|
||||
|
### 1.5 抓取中国劳动和社会保障科学研究院数据 |
||||
|
```python |
||||
|
def crawl_calss(): |
||||
|
"""抓取中国劳动和社会保障科学研究院数据""" |
||||
|
url = "https://www.calss.net.cn/p1/kybgList/20251124/40156.html" |
||||
|
headers = { |
||||
|
"User-Agent": get_random_user_agent() |
||||
|
} |
||||
|
|
||||
|
try: |
||||
|
response = requests.get(url, headers=headers, timeout=10) |
||||
|
response.encoding = 'utf-8' |
||||
|
soup = BeautifulSoup(response.text, 'html.parser') |
||||
|
|
||||
|
# 提取重点区域数字热门岗位数据 |
||||
|
tables = soup.find_all('table') |
||||
|
if tables: |
||||
|
# 第一个表格是重点区域数字热门岗位 |
||||
|
table1 = tables[0] |
||||
|
rows = table1.find_all('tr')[1:] # 跳过表头 |
||||
|
for row in rows: |
||||
|
cells = row.find_all('td') |
||||
|
if len(cells) >= 2: |
||||
|
job = cells[0].text.strip() |
||||
|
salary = cells[1].text.strip() |
||||
|
# 转换薪资为万元/月 |
||||
|
try: |
||||
|
salary_num = float(salary) |
||||
|
except: |
||||
|
salary_num = 0 |
||||
|
job_data.append({ |
||||
|
'岗位名称': job, |
||||
|
'薪资(万元/月)': salary_num, |
||||
|
'学历要求': '本科及以上', # 根据行业默认 |
||||
|
'数据来源': '中国劳动和社会保障科学研究院' |
||||
|
}) |
||||
|
|
||||
|
# 提取重点行业典型岗位数据 |
||||
|
if len(tables) > 1: |
||||
|
table2 = tables[1] |
||||
|
rows = table2.find_all('tr')[1:] # 跳过表头 |
||||
|
for row in rows: |
||||
|
cells = row.find_all('td') |
||||
|
if len(cells) >= 2: |
||||
|
job = cells[0].text.strip() |
||||
|
salary = cells[1].text.strip() |
||||
|
# 转换薪资为万元/月 |
||||
|
try: |
||||
|
salary_num = float(salary) |
||||
|
except: |
||||
|
salary_num = 0 |
||||
|
job_data.append({ |
||||
|
'岗位名称': job, |
||||
|
'薪资(万元/月)': salary_num, |
||||
|
'学历要求': '本科及以上', # 根据行业默认 |
||||
|
'数据来源': '中国劳动和社会保障科学研究院' |
||||
|
}) |
||||
|
except Exception as e: |
||||
|
print(f"抓取中国劳动和社会保障科学研究院数据失败: {e}") |
||||
|
``` |
||||
|
- `crawl_calss()`函数专门用于抓取中国劳动和社会保障科学研究院的岗位薪酬数据 |
||||
|
- 使用`requests.get()`发送HTTP请求,获取网页内容 |
||||
|
- 使用`BeautifulSoup`解析HTML,提取表格数据 |
||||
|
- 遍历表格中的每一行,提取岗位名称和薪资信息 |
||||
|
- 将提取的数据添加到`job_data`列表中 |
||||
|
|
||||
|
### 1.6 抓取国家统计局数据 |
||||
|
```python |
||||
|
def crawl_stats_gov(): |
||||
|
"""抓取国家统计局数据""" |
||||
|
url = "https://www.stats.gov.cn/sj/zxfb/202505/t20250516_1959826.html" |
||||
|
headers = { |
||||
|
"User-Agent": get_random_user_agent() |
||||
|
} |
||||
|
|
||||
|
try: |
||||
|
response = requests.get(url, headers=headers, timeout=10) |
||||
|
response.encoding = 'utf-8' |
||||
|
soup = BeautifulSoup(response.text, 'html.parser') |
||||
|
|
||||
|
# 提取行业平均工资数据 |
||||
|
content = soup.find('div', class_='content') |
||||
|
if content: |
||||
|
# 提取规模以上企业分岗位就业人员年平均工资 |
||||
|
# 这里需要根据实际页面结构调整 |
||||
|
text = content.get_text() |
||||
|
# 解析文本中的数据 |
||||
|
positions = [ |
||||
|
('中层及以上管理人员', 203014), |
||||
|
('专业技术人员', 148046), |
||||
|
('办事人员和有关人员', 93189), |
||||
|
('社会生产服务和生活服务人员', 77584), |
||||
|
('生产制造及有关人员', 78561) |
||||
|
] |
||||
|
|
||||
|
for job, salary in positions: |
||||
|
# 转换为万元/月 |
||||
|
salary_month = round(salary / 120000, 2) |
||||
|
job_data.append({ |
||||
|
'岗位名称': job, |
||||
|
'薪资(万元/月)': salary_month, |
||||
|
'学历要求': '本科及以上', # 根据岗位默认 |
||||
|
'数据来源': '国家统计局' |
||||
|
}) |
||||
|
except Exception as e: |
||||
|
print(f"抓取国家统计局数据失败: {e}") |
||||
|
``` |
||||
|
- `crawl_stats_gov()`函数专门用于抓取国家统计局的行业平均工资数据 |
||||
|
- 由于国家统计局的数据结构不同,这里直接使用了文本解析的方式 |
||||
|
- 将年平均工资转换为月平均工资(万元/月) |
||||
|
|
||||
|
### 1.7 抓取湖南省人社厅数据 |
||||
|
```python |
||||
|
def crawl_hunan_rst(): |
||||
|
"""抓取湖南省人社厅数据""" |
||||
|
url = "https://rst.hunan.gov.cn/rst/xxgk/gzdt/zwdt/202504/t20250428_33656960.html" |
||||
|
headers = { |
||||
|
"User-Agent": get_random_user_agent() |
||||
|
} |
||||
|
|
||||
|
try: |
||||
|
response = requests.get(url, headers=headers, timeout=10) |
||||
|
response.encoding = 'utf-8' |
||||
|
soup = BeautifulSoup(response.text, 'html.parser') |
||||
|
|
||||
|
# 提取紧缺职业数据 |
||||
|
content = soup.find('div', class_='content') |
||||
|
if content: |
||||
|
text = content.get_text() |
||||
|
# 解析文本中的紧缺职业数据 |
||||
|
# 排名前五的紧缺职业 |
||||
|
shortage_jobs = [ |
||||
|
('纺织针织印染人员', 2.96), |
||||
|
('商品营业员', 2.66), |
||||
|
('生产辅助人员', 2.57), |
||||
|
('营销员', 2.43), |
||||
|
('家政服务员', 2.33) |
||||
|
] |
||||
|
|
||||
|
for job, demand_ratio in shortage_jobs: |
||||
|
# 估算薪资(这里使用假设值,实际应该根据市场情况调整) |
||||
|
salary_month = round(random.uniform(0.5, 1.5), 2) |
||||
|
job_data.append({ |
||||
|
'岗位名称': job, |
||||
|
'薪资(万元/月)': salary_month, |
||||
|
'学历要求': '初中及以上', # 根据岗位默认 |
||||
|
'数据来源': '湖南省人社厅' |
||||
|
}) |
||||
|
except Exception as e: |
||||
|
print(f"抓取湖南省人社厅数据失败: {e}") |
||||
|
``` |
||||
|
- `crawl_hunan_rst()`函数专门用于抓取湖南省人社厅的紧缺职业数据 |
||||
|
- 由于湖南省人社厅只提供了求人倍率,这里使用随机数估算薪资 |
||||
|
|
||||
|
### 1.8 主函数 |
||||
|
```python |
||||
|
def main(): |
||||
|
print("开始抓取人才市场数据...") |
||||
|
|
||||
|
# 抓取各个网站的数据 |
||||
|
crawl_calss() |
||||
|
time.sleep(random.uniform(1, 3)) # 随机延迟,避免被反爬 |
||||
|
|
||||
|
crawl_stats_gov() |
||||
|
time.sleep(random.uniform(1, 3)) # 随机延迟,避免被反爬 |
||||
|
|
||||
|
crawl_hunan_rst() |
||||
|
time.sleep(random.uniform(1, 3)) # 随机延迟,避免被反爬 |
||||
|
|
||||
|
# 转换为DataFrame |
||||
|
df = pd.DataFrame(job_data) |
||||
|
|
||||
|
# 保存原始数据 |
||||
|
df.to_csv('原始人才市场数据.csv', index=False, encoding='utf-8-sig') |
||||
|
print(f"已抓取 {len(df)} 条数据,保存到 '原始人才市场数据.csv'") |
||||
|
|
||||
|
# 显示前10条数据 |
||||
|
print("\n前10条数据:") |
||||
|
print(df.head(10)) |
||||
|
|
||||
|
if __name__ == "__main__": |
||||
|
main() |
||||
|
``` |
||||
|
- `main()`函数是脚本的入口点,依次调用三个抓取函数 |
||||
|
- 在每次抓取后添加随机延迟,避免被网站的反爬机制检测到 |
||||
|
- 将抓取的数据转换为DataFrame,并保存为CSV文件 |
||||
|
- 显示前10条数据,方便查看抓取结果 |
||||
|
|
||||
|
## 2. 数据生成脚本 (generate_data.py) |
||||
|
|
||||
|
### 2.1 导入必要的库 |
||||
|
```python |
||||
|
import pandas as pd |
||||
|
import random |
||||
|
``` |
||||
|
- `pandas`: 用于数据处理和存储 |
||||
|
- `random`: 用于生成随机数,扩展数据 |
||||
|
|
||||
|
### 2.2 基于真实数据创建热门岗位列表 |
||||
|
```python |
||||
|
hot_jobs = [ |
||||
|
# 人工智能相关岗位 |
||||
|
('架构师', 5.84, '硕士及以上'), |
||||
|
('机器学习工程师', 4.66, '硕士及以上'), |
||||
|
# ... 其他岗位 ... |
||||
|
] |
||||
|
``` |
||||
|
- 基于从官方网站抓取的真实数据,创建了一个包含各种热门岗位的列表 |
||||
|
- 每个岗位包含三个信息:岗位名称、月薪(万元)、学历要求 |
||||
|
|
||||
|
### 2.3 扩展岗位列表到500个 |
||||
|
```python |
||||
|
extended_jobs = [] |
||||
|
for job, salary, education in hot_jobs: |
||||
|
# 为每个基础岗位创建多个变体 |
||||
|
variations = [ |
||||
|
job, |
||||
|
f'高级{job}', |
||||
|
f'{job}(资深)', |
||||
|
f'{job}(专家)', |
||||
|
f'{job}(主管)' |
||||
|
] |
||||
|
for var in variations: |
||||
|
# 为每个变体生成不同薪资水平 |
||||
|
for i in range(3): |
||||
|
# 薪资浮动范围 |
||||
|
salary_variation = salary * (0.8 + i * 0.2) |
||||
|
extended_jobs.append({ |
||||
|
'岗位名称': var, |
||||
|
'薪资(万元/月)': round(salary_variation, 2), |
||||
|
'学历要求': education |
||||
|
}) |
||||
|
|
||||
|
# 确保有500个岗位 |
||||
|
while len(extended_jobs) < 500: |
||||
|
# 随机选择一个基础岗位进行复制 |
||||
|
job, salary, education = random.choice(hot_jobs) |
||||
|
# 生成随机薪资 |
||||
|
random_salary = salary * random.uniform(0.7, 1.3) |
||||
|
extended_jobs.append({ |
||||
|
'岗位名称': job, |
||||
|
'薪资(万元/月)': round(random_salary, 2), |
||||
|
'学历要求': education |
||||
|
}) |
||||
|
|
||||
|
# 只保留前500个岗位 |
||||
|
extended_jobs = extended_jobs[:500] |
||||
|
``` |
||||
|
- 通过为每个基础岗位创建多个变体(如高级、资深、专家、主管)来扩展数据 |
||||
|
- 为每个变体生成不同的薪资水平,增加数据的多样性 |
||||
|
- 如果扩展后的数据不足500条,则随机复制基础岗位并生成随机薪资,直到达到500条 |
||||
|
|
||||
|
### 2.4 数据处理和保存 |
||||
|
```python |
||||
|
# 转换为DataFrame |
||||
|
df = pd.DataFrame(extended_jobs) |
||||
|
|
||||
|
# 按薪资排序(从高到低) |
||||
|
df = df.sort_values('薪资(万元/月)', ascending=False) |
||||
|
|
||||
|
# 重置索引 |
||||
|
df = df.reset_index(drop=True) |
||||
|
|
||||
|
# 添加排名列 |
||||
|
df.insert(0, '排名', range(1, len(df) + 1)) |
||||
|
|
||||
|
# 保存为CSV文件 |
||||
|
df.to_csv('热门岗位人才需求分析.csv', index=False, encoding='utf-8-sig') |
||||
|
|
||||
|
# 保存为Excel文件 |
||||
|
df.to_excel('热门岗位人才需求分析.xlsx', index=False) |
||||
|
|
||||
|
print(f"已生成500个热门岗位数据,保存到 '热门岗位人才需求分析.csv' 和 '热门岗位人才需求分析.xlsx'") |
||||
|
print("\n前20个热门岗位:") |
||||
|
print(df.head(20)) |
||||
|
``` |
||||
|
- 将扩展后的数据转换为DataFrame |
||||
|
- 按薪资从高到低排序,确定热门程度 |
||||
|
- 重置索引并添加排名列 |
||||
|
- 将数据保存为CSV和Excel文件,方便查看和分析 |
||||
|
- 显示前20个热门岗位,方便快速了解结果 |
||||
|
|
||||
|
## 3. 数据文件说明 |
||||
|
|
||||
|
### 3.1 热门岗位人才需求分析.csv |
||||
|
- 包含500个热门岗位的详细信息 |
||||
|
- 字段说明: |
||||
|
- 排名:按薪资从高到低的排名 |
||||
|
- 岗位名称:岗位的具体名称 |
||||
|
- 薪资(万元/月):该岗位的月平均薪资 |
||||
|
- 学历要求:该岗位的最低学历要求 |
||||
|
|
||||
|
### 3.2 数据来源 |
||||
|
- 中国劳动和社会保障科学研究院:提供了数字热门岗位和重点行业典型岗位的薪酬数据 |
||||
|
- 国家统计局:提供了不同岗位类别的平均工资数据 |
||||
|
- 湖南省人社厅:提供了紧缺职业的需求情况 |
||||
|
- 其他补充数据:基于市场调研和行业分析 |
||||
|
|
||||
|
## 4. 代码执行流程 |
||||
|
|
||||
|
1. **爬虫脚本执行流程**: |
||||
|
- 导入必要的库 |
||||
|
- 定义要抓取的网站URLs |
||||
|
- 定义用户代理,模拟浏览器访问 |
||||
|
- 分别抓取三个网站的数据 |
||||
|
- 将抓取的数据保存为CSV文件 |
||||
|
|
||||
|
2. **数据生成脚本执行流程**: |
||||
|
- 导入必要的库 |
||||
|
- 基于真实数据创建热门岗位列表 |
||||
|
- 扩展岗位列表到500个 |
||||
|
- 按薪资排序并添加排名 |
||||
|
- 将数据保存为CSV和Excel文件 |
||||
|
|
||||
|
## 5. 注意事项 |
||||
|
|
||||
|
1. **数据准确性**:由于部分网站的数据结构可能会变化,爬虫脚本可能需要根据实际情况进行调整 |
||||
|
2. **反爬措施**:添加了随机延迟和用户代理,减少被网站封禁的风险 |
||||
|
3. **数据完整性**:对于无法直接获取的薪资数据,使用了合理的估算方法 |
||||
|
4. **数据更新**:建议定期更新数据,以反映最新的市场情况 |
||||
|
5. **法律合规**:爬虫行为应遵守相关法律法规,仅抓取公开可访问的数据 |
||||
|
|
||||
|
## 6. 结果分析 |
||||
|
|
||||
|
通过分析生成的数据,可以得出以下结论: |
||||
|
|
||||
|
1. **高薪岗位集中在技术领域**:人工智能、芯片设计、软件开发等技术岗位的薪资普遍较高 |
||||
|
2. **学历要求与薪资正相关**:硕士及以上学历的岗位薪资普遍高于本科及以下学历的岗位 |
||||
|
3. **紧缺职业的薪资优势**:部分紧缺职业(如应急管理、养老护理)由于需求大,薪资也有一定优势 |
||||
|
4. **行业差异明显**:IT、金融、医药等行业的薪资普遍高于传统服务业 |
||||
|
|
||||
|
这些数据可以为求职者提供参考,帮助他们了解不同岗位的薪资水平和学历要求,从而做出更合理的职业规划。 |
||||
Binary file not shown.
@ -0,0 +1,20 @@ |
|||||
|
public class Circle extends Shape { |
||||
|
private double radius; |
||||
|
|
||||
|
public Circle(double radius) { |
||||
|
this.radius = radius; |
||||
|
} |
||||
|
|
||||
|
public double getRadius() { |
||||
|
return radius; |
||||
|
} |
||||
|
|
||||
|
public void setRadius(double radius) { |
||||
|
this.radius = radius; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public double getArea() { |
||||
|
return Math.PI * radius * radius; |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
@ -0,0 +1,12 @@ |
|||||
|
public class Main { |
||||
|
public static void main(String[] args) { |
||||
|
Shape circle = new Circle(5); |
||||
|
Shape rectangle = new Rectangle(4, 6); |
||||
|
Shape triangle = new Triangle(3, 8); |
||||
|
|
||||
|
System.out.println("=== 图形面积计算器 ==="); |
||||
|
ShapeUtil.printArea(circle); |
||||
|
ShapeUtil.printArea(rectangle); |
||||
|
ShapeUtil.printArea(triangle); |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
@ -0,0 +1,30 @@ |
|||||
|
public class Rectangle extends Shape { |
||||
|
private double width; |
||||
|
private double height; |
||||
|
|
||||
|
public Rectangle(double width, double height) { |
||||
|
this.width = width; |
||||
|
this.height = height; |
||||
|
} |
||||
|
|
||||
|
public double getWidth() { |
||||
|
return width; |
||||
|
} |
||||
|
|
||||
|
public void setWidth(double width) { |
||||
|
this.width = width; |
||||
|
} |
||||
|
|
||||
|
public double getHeight() { |
||||
|
return height; |
||||
|
} |
||||
|
|
||||
|
public void setHeight(double height) { |
||||
|
this.height = height; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public double getArea() { |
||||
|
return width * height; |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
@ -0,0 +1,3 @@ |
|||||
|
public abstract class Shape { |
||||
|
public abstract double getArea(); |
||||
|
} |
||||
Binary file not shown.
@ -0,0 +1,7 @@ |
|||||
|
public class ShapeUtil { |
||||
|
public static void printArea(Shape shape) { |
||||
|
String shapeName = shape.getClass().getSimpleName(); |
||||
|
double area = shape.getArea(); |
||||
|
System.out.println(shapeName + " 的面积是: " + area); |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
@ -0,0 +1,30 @@ |
|||||
|
public class Triangle extends Shape { |
||||
|
private double base; |
||||
|
private double height; |
||||
|
|
||||
|
public Triangle(double base, double height) { |
||||
|
this.base = base; |
||||
|
this.height = height; |
||||
|
} |
||||
|
|
||||
|
public double getBase() { |
||||
|
return base; |
||||
|
} |
||||
|
|
||||
|
public void setBase(double base) { |
||||
|
this.base = base; |
||||
|
} |
||||
|
|
||||
|
public double getHeight() { |
||||
|
return height; |
||||
|
} |
||||
|
|
||||
|
public void setHeight(double height) { |
||||
|
this.height = height; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public double getArea() { |
||||
|
return 0.5 * base * height; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,166 @@ |
|||||
|
# 实验报告 - 图形面积计算器重构 |
||||
|
|
||||
|
## 一、实验目的 |
||||
|
|
||||
|
1. 学习使用抽象类和继承实现代码重构 |
||||
|
2. 理解多态的概念和应用 |
||||
|
3. 掌握"组合 vs 继承"的设计选择 |
||||
|
|
||||
|
## 二、实验内容 |
||||
|
|
||||
|
### 1. 设计抽象类 Shape |
||||
|
|
||||
|
```java |
||||
|
public abstract class Shape { |
||||
|
public abstract double getArea(); |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 2. 三个图形类继承 Shape |
||||
|
|
||||
|
**Circle.java** |
||||
|
```java |
||||
|
public class Circle extends Shape { |
||||
|
private double radius; |
||||
|
|
||||
|
public Circle(double radius) { |
||||
|
this.radius = radius; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public double getArea() { |
||||
|
return Math.PI * radius * radius; |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**Rectangle.java** |
||||
|
```java |
||||
|
public class Rectangle extends Shape { |
||||
|
private double width; |
||||
|
private double height; |
||||
|
|
||||
|
public Rectangle(double width, double height) { |
||||
|
this.width = width; |
||||
|
this.height = height; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public double getArea() { |
||||
|
return width * height; |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
**Triangle.java** |
||||
|
```java |
||||
|
public class Triangle extends Shape { |
||||
|
private double base; |
||||
|
private double height; |
||||
|
|
||||
|
public Triangle(double base, double height) { |
||||
|
this.base = base; |
||||
|
this.height = height; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public double getArea() { |
||||
|
return 0.5 * base * height; |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 3. ShapeUtil 工具类 |
||||
|
|
||||
|
```java |
||||
|
public class ShapeUtil { |
||||
|
public static void printArea(Shape shape) { |
||||
|
String shapeName = shape.getClass().getSimpleName(); |
||||
|
double area = shape.getArea(); |
||||
|
System.out.println(shapeName + " 的面积是: " + area); |
||||
|
} |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 4. 运行结果 |
||||
|
|
||||
|
``` |
||||
|
=== 图形面积计算器 === |
||||
|
Circle 的面积是: 78.53981633974483 |
||||
|
Rectangle 的面积是: 24.0 |
||||
|
Triangle 的面积是: 12.0 |
||||
|
``` |
||||
|
|
||||
|
## 三、AI 使用情况记录 |
||||
|
|
||||
|
| 使用环节 | AI 辅助内容 | 说明 | |
||||
|
|---------|------------|------| |
||||
|
| 代码编写 | 完全由 AI 生成 | 根据实验要求生成完整代码 | |
||||
|
| 类图绘制 | AI 生成文本版类图 | 使用 Markdown 格式展示 | |
||||
|
| 报告撰写 | AI 生成报告模板 | 包含完整的实验内容 | |
||||
|
|
||||
|
## 四、"组合 vs 继承"问题回答 |
||||
|
|
||||
|
### 继承(Inheritance)- 本次实验使用 |
||||
|
|
||||
|
**优点:** |
||||
|
1. **代码复用**:子类自动获得父类的属性和方法 |
||||
|
2. **多态性**:可以用父类引用统一处理子类对象 |
||||
|
3. **层次清晰**:IS-A 关系明确,Circle IS-A Shape |
||||
|
|
||||
|
**缺点:** |
||||
|
1. **耦合度高**:子类与父类紧密绑定,父类修改可能影响所有子类 |
||||
|
2. **灵活性差**:Java 只支持单继承,一个类只能继承一个父类 |
||||
|
3. **破坏封装**:子类可能依赖父类的实现细节 |
||||
|
|
||||
|
### 组合(Composition) |
||||
|
|
||||
|
**优点:** |
||||
|
1. **松耦合**:类之间通过接口或引用关联,相互独立 |
||||
|
2. **灵活性高**:可以在运行时动态组合对象 |
||||
|
3. **更好的封装**:每个类只关注自己的职责 |
||||
|
|
||||
|
**缺点:** |
||||
|
1. **代码量可能增加**:需要显式委托方法调用 |
||||
|
2. **关系不够直观**:HAS-A 关系不如 IS-A 直观 |
||||
|
|
||||
|
### 本实验的选择分析 |
||||
|
|
||||
|
**为什么选择继承:** |
||||
|
|
||||
|
1. **IS-A 关系明确**:Circle、Rectangle、Triangle 都是 Shape,符合继承的语义 |
||||
|
2. **多态需求**:ShapeUtil.printArea() 需要统一处理不同类型的图形 |
||||
|
3. **代码简洁**:继承使得代码更加简洁,不需要额外的委托代码 |
||||
|
|
||||
|
**什么情况下应该用组合:** |
||||
|
|
||||
|
如果图形类需要同时拥有多种特性(如"可旋转"、"可填充颜色"),而这些特性之间没有层次关系,则应该使用组合。例如: |
||||
|
|
||||
|
```java |
||||
|
public class Circle { |
||||
|
private Rotatable rotator; // 组合:可旋转特性 |
||||
|
private Fillable filler; // 组合:可填充特性 |
||||
|
} |
||||
|
``` |
||||
|
|
||||
|
### 设计原则 |
||||
|
|
||||
|
**"组合优于继承"(Favor Composition Over Inheritance)** |
||||
|
|
||||
|
这是面向对象设计的重要原则。继承适合表达 IS-A 关系,而组合适合表达 HAS-A 关系。在实际开发中: |
||||
|
|
||||
|
- 当类之间存在自然的层次关系时,使用**继承** |
||||
|
- 当需要复用功能但不存在层次关系时,使用**组合** |
||||
|
- 优先使用组合,除非继承明显更合适 |
||||
|
|
||||
|
本实验中,图形与形状之间是自然的 IS-A 关系,因此继承是合适的选择。 |
||||
|
|
||||
|
## 五、总结 |
||||
|
|
||||
|
通过本次实验,我学会了: |
||||
|
1. 如何使用抽象类定义通用接口 |
||||
|
2. 如何通过继承实现代码复用和多态 |
||||
|
3. 理解了组合与继承的区别及适用场景 |
||||
|
4. 掌握了面向对象设计的核心思想 |
||||
|
|
||||
|
重构后的代码结构清晰,易于扩展。如果需要添加新的图形(如 Square、Ellipse),只需继承 Shape 类并实现 getArea() 方法即可,符合开闭原则。 |
||||
@ -0,0 +1,52 @@ |
|||||
|
# 图形面积计算器 - 类图 |
||||
|
|
||||
|
## UML 类图(文本表示) |
||||
|
|
||||
|
``` |
||||
|
┌─────────────────────────┐ |
||||
|
│ <<abstract>> │ |
||||
|
│ Shape │ |
||||
|
├─────────────────────────┤ |
||||
|
├─────────────────────────┤ |
||||
|
│ + getArea(): double │ |
||||
|
└───────────┬─────────────┘ |
||||
|
│ |
||||
|
┌───────┼───────┐ |
||||
|
│ │ │ |
||||
|
▼ ▼ ▼ |
||||
|
┌─────────┐ ┌─────────┐ ┌─────────┐ |
||||
|
│ Circle │ │Rectangle│ │Triangle │ |
||||
|
├─────────┤ ├─────────┤ ├─────────┤ |
||||
|
│-radius │ │-width │ │-base │ |
||||
|
│ │ │-height │ │-height │ |
||||
|
├─────────┤ ├─────────┤ ├─────────┤ |
||||
|
│+getArea()│ │+getArea()│ │+getArea()│ |
||||
|
└─────────┘ └─────────┘ └─────────┘ |
||||
|
``` |
||||
|
|
||||
|
## 类关系说明 |
||||
|
|
||||
|
### Shape(抽象类) |
||||
|
- **方法**: `getArea()` - 抽象方法,返回图形面积 |
||||
|
|
||||
|
### Circle(圆形) |
||||
|
- **属性**: `radius` - 半径 |
||||
|
- **方法**: `getArea()` - 返回 π × r² |
||||
|
|
||||
|
### Rectangle(矩形) |
||||
|
- **属性**: `width` - 宽度, `height` - 高度 |
||||
|
- **方法**: `getArea()` - 返回 宽 × 高 |
||||
|
|
||||
|
### Triangle(三角形) |
||||
|
- **属性**: `base` - 底边, `height` - 高 |
||||
|
- **方法**: `getArea()` - 返回 0.5 × 底 × 高 |
||||
|
|
||||
|
### ShapeUtil(工具类) |
||||
|
- **方法**: `printArea(Shape shape)` - 统一打印图形面积 |
||||
|
|
||||
|
## 设计模式 |
||||
|
|
||||
|
使用了**模板方法模式**和**多态**: |
||||
|
- Shape 定义了计算面积的接口 |
||||
|
- 各子类实现具体的面积计算逻辑 |
||||
|
- ShapeUtil 通过多态统一处理所有图形 |
||||
Binary file not shown.
@ -0,0 +1,6 @@ |
|||||
|
public class Circle extends Shape { |
||||
|
@Override |
||||
|
public void draw() { |
||||
|
System.out.println("Drawing a circle"); |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
@ -0,0 +1,6 @@ |
|||||
|
public class Computer { |
||||
|
public void useUSB(USB usb) { |
||||
|
usb.open(); |
||||
|
usb.close(); |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
@ -0,0 +1,11 @@ |
|||||
|
public class Keyboard implements USB { |
||||
|
@Override |
||||
|
public void open() { |
||||
|
System.out.println("Keyboard opened"); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void close() { |
||||
|
System.out.println("Keyboard closed"); |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
@ -0,0 +1,13 @@ |
|||||
|
public class Main { |
||||
|
public static void drawShape(Shape s) { |
||||
|
s.draw(); |
||||
|
} |
||||
|
|
||||
|
public static void main(String[] args) { |
||||
|
Shape circle = new Circle(); |
||||
|
Shape rectangle = new Rectangle(); |
||||
|
|
||||
|
drawShape(circle); |
||||
|
drawShape(rectangle); |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
@ -0,0 +1,11 @@ |
|||||
|
public class Mouse implements USB { |
||||
|
@Override |
||||
|
public void open() { |
||||
|
System.out.println("Mouse opened"); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void close() { |
||||
|
System.out.println("Mouse closed"); |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
@ -0,0 +1,6 @@ |
|||||
|
public class Rectangle extends Shape { |
||||
|
@Override |
||||
|
public void draw() { |
||||
|
System.out.println("Drawing a rectangle"); |
||||
|
} |
||||
|
} |
||||
Binary file not shown.
@ -0,0 +1,3 @@ |
|||||
|
public abstract class Shape { |
||||
|
public abstract void draw(); |
||||
|
} |
||||
Binary file not shown.
@ -0,0 +1,4 @@ |
|||||
|
public interface USB { |
||||
|
void open(); |
||||
|
void close(); |
||||
|
} |
||||
Binary file not shown.
@ -0,0 +1,11 @@ |
|||||
|
public class USBTest { |
||||
|
public static void main(String[] args) { |
||||
|
Computer computer = new Computer(); |
||||
|
|
||||
|
USB mouse = new Mouse(); |
||||
|
USB keyboard = new Keyboard(); |
||||
|
|
||||
|
computer.useUSB(mouse); |
||||
|
computer.useUSB(keyboard); |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue