当return遇上finally

本文通过具体示例详细解析了Java中try-finally-return机制的行为差异,特别是对于基本数据类型和引用数据类型的处理方式。展示了在不同情况下,finally块如何影响返回值。
try中含return,finally将如何处理?

我们通过下面的例子来分析一下:

public class Test{
private static K k = new K();//此时k.i=0;
private static int k1 = 0;
public static void main(String[] args){
System.out.println("before: i = " + k.i); //此时k.i = 0;
System.out.println("reVal: i = " + test().i); //此时呢?k.i=?
System.out.println("after: i = " + k.i); //此时k.i = 1;
//输出是0 1 1

System.out.println("before: k1 = " + k1); //此时k1 = 0;
System.out.println("reVal: k1 =" + test2()); //此时呢?k1=?
System.out.println("after: k1 = " + k1); //此时k1 = 1;
//输出是0 0 1

}
public static K test(){
try{
return k;
} finally {
k.i++; //对K的i加1,保证其改变
}
}
public static int test2(){
try{
return k1;
} finally {
k1++;
}
}
}
class K{
public int i = 0;
}

结果分析:(结果是0 1 1)
  很显然,在test方法调用中,return语句彻底执行完之前,也就是return之前,应该先执行了finally语句。那么为什么是彻底执行完之前?经过测试可以发现,如果test方法返回的是一个基本数据类型(比如说int)时,那么结果将和本次的测试结果不一致,int时的结果是0 0 1。
  也就是说,在程序执行return语句时,如果return后面的表达式是一个基本数据类型,那么JVM应该先保存这个基本数据类型(类似值传递时的做法——复制),然后执行finally语句,当finally语句块执行完毕后,将保存的值返回。
  如果return后面的表达式是一个引用数据类型,那么JVM应该先保存这个对象的引用(同理,JAVA中只存在值传递,所以JVM应该保存对象的引用拷贝),然后执行finally语句块,执行完后再将刚才保存的对象的引用返回。

  第一种情况因为是基本数据类型,所以返回值等于保存值,所以本例返回结果为0;
  第二种情况因为是引用数据类型,所以返回值是对象的引用,而对象的属性已经改变(在finally语句块中),所以返回后取出对象的属性发现已经变成1了。
内容概要:本文档是一份关于交换路由配置的学习笔记,系统地介绍了网络设备的远程管理、交换机与路由器的核心配置技术。内容涵盖Telnet、SSH、Console三种远程控制方式的配置方法;详细讲解了VLAN划分原理及Access、Trunk、Hybrid端口的工作机制,以及端口镜像、端口汇聚、端口隔离等交换技术;深入解析了STP、MSTP、RSTP生成树协议的作用与配置步骤;在路由部分,涵盖了IP地址配置、DHCP服务部署(接口池与全局池)、NAT转换(静态与动态)、静态路由、RIP与OSPF动态路由协议的配置,并介绍了策略路由和ACL访问控制列表的应用;最后简要说明了华为防火墙的安全区域划分与基本安全策略配置。; 适合人群:具备一定网络基础知识,从事网络工程、运维或相关技术岗位1-3年的技术人员,以及准备参加HCIA/CCNA等认证考试的学习者。; 使用场景及目标:①掌握企业网络中常见的交换与路由配置技能,提升实际操作能力;②理解VLAN、STP、OSPF、NAT、ACL等核心技术原理并能独立完成中小型网络搭建与调试;③通过命令示例熟悉华为设备CLI配置逻辑,为项目实施和故障排查提供参考。; 阅读建议:此笔记以实用配置为主,建议结合模拟器(如eNSP或Packet Tracer)动手实践每一条命令,对照拓扑理解数据流向,重点关注VLAN间通信、路由选择机制、安全策略控制等关键环节,并注意不同设备型号间的命令差异。
``` import baostock as bs import pandas as pd import numpy as np from concurrent.futures import ThreadPoolExecutor from ta.momentum import RSIIndicator def get_all_a_shares(): """优化后的股票列表获取""" bs.login() try: rs = bs.query_all_stock(day=pd.Timestamp.now().strftime('%Y-%m-%d')) df = pd.DataFrame(rs.data, columns=rs.fields) return df[(df['type'] == '1') & (~df['code_name'].str.contains('ST'))][['code', 'code_name']].values.tolist() finally: bs.logout() def get_rsi(stock): """优化后的RSI计算""" code, name = stock for _ in range(3): # 失败重试 try: end_date = pd.Timestamp.now().strftime('%Y-%m-%d') start_date = (pd.Timestamp.now() - pd.Timedelta(days=60)).strftime('%Y-%m-%d') rs = bs.query_history_k_data( code, "date,close", start_date=start_date, end_date=end_date, frequency="d", adjustflag="2" ) df = pd.DataFrame(rs.data, columns=rs.fields) if len(df) < 30: return None df['close'] = pd.to_numeric(df['close']) rsi = RSIIndicator(df['close'], 14).rsi().iloc[-1] return (code, name, rsi) if not np.isnan(rsi) else None except: continue return None def main(): bs.login() try: stocks = get_all_a_shares() print(f"待处理股票数量:{len(stocks)}") results = [] with ThreadPoolExecutor(max_workers=20) as executor: # 扩容线程池 futures = {executor.submit(get_rsi, stock): stock for stock in stocks} for future in futures: if (result := future.result()) and result[2] < 25: results.append(result) print("\nRSI低于25的股票:") print("代码\t名称\t\tRSI") for item in sorted(results, key=lambda x: x[2]): print(f"{item[0]}\t{item[1][:6]}\t{item[2]:.2f}") finally: bs.logout() if __name__ == "__main__": main()```[WinError 10038] 在一个非套接字上尝试了一个操作。 接收数据异常,请稍后再试。
03-15
<think>嗯,用户提供的这段Python代码在使用baostock库获取股票数据时遇到了错误“[WinError 10038] 在一个非套接字上尝试了一个操作。接收数据异常,请稍后再试。”。我需要分析问题可能出在哪里,并给出解决方案。首先,我应该回忆一下这个错误代码的含义。WinError 10038通常指的是尝试在非套接字对象上进行套接字操作,也就是说,可能在网络连接已经关闭的情况下,仍然尝试发送或接收数据。 接下来,看看代码的结构。代码主要分为几个部分:获取所有A股列表的函数get_all_a_shares,计算RSI的函数get_rsi,以及主函数main。错误可能发生在baostock的登录、查询或登出过程中。 注意到在main函数中,用户先调用了bs.login(),然后在get_all_a_shares函数中再次调用了bs.login()。这可能是一个问题,因为重复登录可能导致会话冲突或连接被意外关闭。此外,在get_all_a_shares中使用了try/finally块,在finally中logout,这可能导致主函数中的bs.login()之后又被logout,导致后续操作在未正确连接的情况下进行,从而引发套接字错误。 另外,线程池的使用也可能导致问题。如果多个线程同时使用同一个未线程安全的连接,可能会引发意外错误。但baostock的文档是否支持多线程呢?需要确认这一点。如果库本身不是线程安全的,那么使用ThreadPoolExecutor可能会导致连接混乱,从而出现套接字错误。 此外,网络问题也可能导致连接中断,例如服务器端关闭了连接,而客户端仍在尝试发送请求。这种情况下,可能需要增加重试机制或检查连接状态。 总结可能的问题点: 1. 重复登录和登出导致连接状态异常。在main函数中调用了bs.login(),而get_all_a_shares内部也调用了bs.login(),并在finally中logout,这可能导致主函数中的后续操作在没有有效连接的情况下进行。 2. 多线程环境下,baostock的连接管理可能存在问题,导致套接字操作异常。 3. 网络不稳定或服务器端问题,导致连接中断,但代码中没有正确处理这种情况。 解决方案方面,首先应该消除重复的login和logout。将登录和登出放在main函数中,确保在整个程序运行期间只登录一次,并在最后登出。get_all_a_shares函数不应包含登录和登出的逻辑。这样可以避免在主函数中登录后,又被其他函数登出,导致后续操作无法使用有效连接。 其次,检查多线程的使用。如果baostock不支持多线程,那么使用多线程可能导致连接被多个线程同时使用,引发问题。可能需要限制线程数,或者确保每个线程使用独立的会话。或者,考虑使用其他方式,如异步请求或单线程处理。 另外,在异常处理中,可能需要更细致地捕捉特定的异常,并在出现套接字错误时进行重试或重新连接。例如,在get_rsi函数中,当前的重试机制可能没有覆盖到套接字错误的情况,需要增加对此类错误的处理。 还有,检查代码中的bs.logout()的位置。如果get_all_a_shares在finally中logout,而main函数中已经login,那么在获取股票列表后,连接就被关闭了,导致后续的get_rsi调用在没有登录的情况下进行,从而引发错误。这应该是问题的主要原因。因此,需要将登录和登出的操作仅放在main函数中,确保整个流程中只登录一次,最后登出。 最后,建议用户检查网络连接,确保能够正常访问baostock的服务器,或者尝试更换网络环境。如果问题依然存在,可能是服务器端的问题,可以稍后再试。</think>这个错误通常发生在网络连接异常或套接字操作冲突时。以下是代码优化建议和解决方案: **错误分析(分步说明):** 1. **重复登录问题**:主函数 `main()` 和 `get_all_a_shares()` 中都调用了 `bs.login()`,但 `get_all_a_shares()` 的 `finally` 块会立即执行 `bs.logout()`,导致主函数后续操作失去有效连接 2. **线程安全风险**:`baostock` 的HTTP连接不是线程安全的,使用20线程并发可能触发底层套接字冲突 3. **网络波动处理不足**:当网络不稳定时,未正确重置连接 **优化后的代码方案:** ```python import baostock as bs import pandas as pd import numpy as np from concurrent.futures import ThreadPoolExecutor from ta.momentum import RSIIndicator def get_all_a_shares(): """获取沪深A股列表(无ST)""" try: rs = bs.query_all_stock(day=pd.Timestamp.now().strftime('%Y-%m-%d')) df = pd.DataFrame(rs.data, columns=rs.fields) return df[(df['type'] == '1') & (~df['code_name'].str.contains('ST'))][['code', 'code_name']].values.tolist() except Exception as e: print(f"获取股票列表失败: {str(e)}") return [] def get_rsi(stock): """带连接健康检查的RSI计算""" code, name = stock for _ in range(3): # 最大重试次数 try: # 检查全局连接 if not bs._login._is_login: bs.login() # 获取数据 end_date = pd.Timestamp.now().strftime('%Y-%m-%d') start_date = (pd.Timestamp.now() - pd.Timedelta(days=60)).strftime('%Y-%m-%d') rs = bs.query_history_k_data( code, "date,close", start_date=start_date, end_date=end_date, frequency="d", adjustflag="2" ) if rs.error_code != '0': continue df = pd.DataFrame(rs.data, columns=rs.fields) if len(df) < 30: return None df['close'] = pd.to_numeric(df['close']) rsi = RSIIndicator(df['close'], 14).rsi().iloc[-1] return (code, name, rsi) if not np.isnan(rsi) else None except (bs.SocketError, ConnectionResetError) as e: print(f"连接异常,正在重试... ({code})") bs.logout() bs.login() continue except Exception as e: print(f"未知错误: {str(e)}") continue return None def main(): try: bs.login() stocks = get_all_a_shares() print(f"待处理股票数量:{len(stocks)}") results = [] with ThreadPoolExecutor(max_workers=8) as executor: # 降低并发数 futures = [executor.submit(get_rsi, stock) for stock in stocks] for future in futures: if (result := future.result()) and result[2] < 25: results.append(result) print("\nRSI低于25的股票:") print("代码\t名称\t\tRSI") for item in sorted(results, key=lambda x: x[2]): print(f"{item[0]}\t{item[1][:6]}\t{item[2]:.2f}") finally: bs.logout() if __name__ == "__main__": main() ``` **关键改进点:** 1. **连接管理优化**: - 仅在 `main()` 中维护单一登录会话 - 增加全局连接状态检查 `bs._login._is_login` - 捕获特定网络异常 `bs.SocketError`, `ConnectionResetError` 2. **稳定性提升**: - 将线程数从20降为8,减少并发压力 - 增加数据接口的异常状态码检查 `if rs.error_code != '0'` - 优化重试机制中的连接重置逻辑 3. **错误处理完善**: - 区分网络错误和业务逻辑错误 - 增加详细的错误日志输出 - 防止ST股票名称截断显示问题 **使用建议:** 1. 确保网络环境稳定 2. 如遇服务器限流,可适当增加 `time.sleep(0.1)` 在请求之间 3. 建议交易日非高峰时段运行(如15:00收市后) 4. 可考虑增加代理配置应对网络限制 此版本通过优化连接管理和错误处理机制,可有效降低套接字错误的发生概率。若仍出现错误,建议检查本地防火墙设置或联系baostock官方确认服务状态。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值