java通过映射修改String的值

本文深入探讨Java中String类的不可变性原理,通过反射机制展示如何看似改变String值,并解析背后机制,包括字符串常量池、堆内存中的对象引用及substring方法的工作方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

字符串String被设计成immutable(不可改变)的,String的value值也是final类型,如何改变值呢?

package com.hanshimeng.other;
import java.lang.reflect.Field;
public class Test {
	public static void main(String[] args) throws Exception {
		String str1 = "hello world";
		String str2 = "hello world";
		String str3 = str1.substring(6);
		System.out.println(str1);
		System.out.println(str2);
		System.out.println(str3);
		
		System.out.println("----------------");
		
		Field field = String.class.getDeclaredField("value");
		field.setAccessible(true); // 设置访问权限不检查
		char[] value = (char[])field.get(str1);
		value[6] = '1';
		value[7] = '2';
		value[8] = '3';
		value[9] = '4';
		value[10] = '5';
		System.out.println(str1);
		System.out.println(str2);
		System.out.println(str3);
	}
}

输出结果:
hello world
hello world
world
----------------
hello 12345
hello 12345
world

修改str1的值str2也会改变,说明他们引用的是同一个对象;那为何str3没有改变呢,咱先来看一下substring的方法实现:

// 该版本是JDK7,至于7之前的版本可能有不一样的情况
public String substring(int beginIndex) {
    if (beginIndex < 0) {
        throw new StringIndexOutOfBoundsException(beginIndex);
    }
    int subLen = value.length - beginIndex;
    if (subLen < 0) {
        throw new StringIndexOutOfBoundsException(subLen);
    }
    return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}

以上代码解释:
当beginIndex不为0时,返回的是new的String对象;当beginIndex为0时,返回的是原对象本身;
我们都知道new对象后是在堆中创建的,是一个新的对象,与str1和str2的值没有关系。

再看以下的代码就更有意思了:

System.out.println("hello world" == str1);
System.out.println("hello world".equals(str1));
	
System.out.println("hello world".toString());	
	
System.out.println("hello 12345" == str1);
System.out.println("hello 12345".equals(str1));

输出结果:
true
true
hello 12345
false
true

String 的变量值是存放在 JVM 的常量池中的,同时指向 “hello world” 的引用的 s即 String 对象,
存放在堆区,我们通过反射区修改的值并不是常量池中的 “hello world” ,而是 堆区中的对象 s 的值,
所以我们输出 s的时候就是hello 12345,那么为什么初始值比较修改后的值的时候会出现 true 呢?

我们看看 System.out.println(“hello world”.toString()); 这行代码,输出结果是 hello 12345,这是因为
“hello world”.toString() 的时候,会去常量池中去找 "hello world"的引用,它的引用就是 s ,所以输出的
是 s 的值,即 “hello 12345”,所以,到这里,我们就知道了,为什么 str1 == “hello world” 和 str1.equals(“hello world”) 返回 true 了

如需转载,请注明作者出处
作者:hanshimeng
出处:https://blog.youkuaiyun.com/hanshimeng

<think>在Java修改入口映射路径通常涉及Web应用的配置,特别是在使用Servlet或Spring框架时。以下是针对不同场景的配置方法: ### 1. 使用Servlet的web.xml配置 在传统的Servlet应用中,可以在`web.xml`文件中通过`<servlet-mapping>`修改URL映射: ```xml <servlet> <servlet-name>MyServlet</servlet-name> <servlet-class>com.example.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyServlet</servlet-name> <!-- 修改映射路径为 /newPath --> <url-pattern>/newPath</url-pattern> </servlet-mapping> ``` ### 2. 使用Spring MVC的注解配置 在Spring Boot中,可通过`@RequestMapping`或`@GetMapping`等注解直接修改控制器方法的映射路径: ```java @RestController public class MyController { // 原始路径 /oldPath @GetMapping("/newPath") // 修改为/newPath public String handleRequest() { return "Hello World"; } } ``` ### 3. 全局路径前缀配置(Spring Boot) 若需为所有控制器添加统一前缀,可在`application.properties`中配置: ```properties spring.mvc.servlet.path=/api ``` 此时所有控制器路径都会自动添加`/api`前缀(如`/newPath`变为`/api/newPath`)[^4]。 ### 4. 使用过滤器重定向路径 通过`Filter`实现路径重定向: ```java public class RedirectFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; if ("/oldPath".equals(request.getRequestURI())) { ((HttpServletResponse) res).sendRedirect("/newPath"); } else { chain.doFilter(req, res); } } } ``` 并在`web.xml`中配置该过滤器。 ### 注意事项 - **上下文路径(Context Path)**:通过`server.servlet.context-path=/myapp`可设置整个应用的基础路径[^4]。 - **静态资源路径**:在Spring Boot中可通过`spring.web.resources.static-locations`修改静态文件映射。 > 提示:具体方法取决于项目架构,Servlet应用侧重`web.xml`,Spring Boot推荐注解属性配置。 §§相关问题§§ 1. 如何在Spring Boot中自定义静态资源的访问路径? 2. Servlet的`url-pattern`支持哪些匹配规则? 3. 如何通过Java配置类(非XML)修改Spring MVC的路径映射?</think>### Java 修改入口映射路径配置方法 在 Java修改入口映射路径主要涉及 Web 应用的请求路由配置,以下是不同框架的实现方法: #### 1. **Servlet 容器配置(web.xml)** ```xml <!-- 修改 Servlet 映射路径 --> <servlet> <servlet-name>MyServlet</servlet-name> <servlet-class>com.example.MyServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>MyServlet</servlet-name> <!-- 修改入口路径 --> <url-pattern>/newPath/*</url-pattern> <!-- 原始路径为 /oldPath --> </servlet-mapping> ``` **原理**:通过修改 `<url-pattern>` 改变请求到 Servlet 的映射关系[^1]。 #### 2. **Spring Boot 注解配置** ```java @RestController public class MyController { // 原始路径 @GetMapping("/old") @GetMapping("/new") // 修改为 /new public String entryPoint() { return "New entry path"; } } ``` **特性**:通过注解直接声明映射路径,无需 XML 配置。 #### 3. **全局路径前缀配置(Spring Boot)** 在 `application.properties` 中添加: ```properties # 设置全局路径前缀 spring.mvc.servlet.path=/api ``` 此时所有控制器路径自动添加 `/api` 前缀(如 `/new` → `/api/new`)[^4]。 #### 4. **Filter 实现路径重定向** ```java @WebFilter("/*") public class PathRedirectFilter implements Filter { @Override public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) req; String path = request.getRequestURI(); if (path.startsWith("/oldPath")) { // 动态修改路径 String newPath = path.replace("/oldPath", "/newPath"); request.getRequestDispatcher(newPath).forward(req, res); } else { chain.doFilter(req, res); } } } ``` #### 5. **动态反射修改(高级用法)** 通过反射修改已注册的映射: ```java // 获取 Spring 的 HandlerMapping RequestMappingHandlerMapping handlerMapping = applicationContext.getBean(RequestMappingHandlerMapping.class); // 获取原方法映射 HandlerMethod oldHandler = ... // 创建新路径映射 RequestMappingInfo newMapping = new RequestMappingInfo( RequestMethod.GET, PathPatternParser.defaultInstance.parse("/newPath"), ... // 其他参数 ); // 动态注册新路径 handlerMapping.registerMapping(newMapping, bean, oldHandler.getMethod()); ``` > **关键点**: > - Servlet 容器通过 `web.xml` 或注解配置 > - Spring Boot 支持全局前缀注解级修改 > - Filter 适用于请求转发场景 > - 反射修改适用于运行时动态调整[^1][^4]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值