方法的参数传递机制与String类的理解

这篇文章讲的是方法的参数传递机制与String类的理解
我觉得这两个知识点我说的还是比较清楚的

方法的参数传递机制

先来看一段代码,main方法中有五种类型的数据:int类型,String类型,Integer类型,int[]数组类型,Mydata引用类型。
change方法中,传入了这五种类型的数据,并做了修改,那么经过change方法后,输出这五个变量的值会是多少呢?

package com.test;

import java.util.Arrays;

public class Test {

    public static void main(String[] args) {
        int i = 1;
        String str = "hello";
        Integer num = 200;
        int[] arr = {1,2,3,4,5};
        MyData my = new MyData();

        change(i, str, num, arr, my);

        System.out.println(i);
        System.out.println(str);
        System.out.println(num);
        System.out.println(Arrays.toString(arr));
        System.out.println(my.a);
    }

    public static void change(int j, String s, Integer n, int[] a, MyData m){
        j += 1;
        s += "world";
        n += 1;
        a[0] += 1;
        m.a += 1;
    }
}

class MyData{
    int a = 10;
}

这里主要用到的知识点就是

  1. 方法的参数传递机制
  2. String、包装类等对象的不可变性

程序运行的结果为:

1
hello
200
[2, 2, 3, 4, 5]
11

方法的参数传递机制
1、形参是基本数据类型,实参传递给形参的是“数据值”,形参的修改和实参无关。
2、形参是引用数据类型,实参传递给形参的是“地址值”,如果通过形参修改对象的属性值,那么就相当于实参对象在修改。
注意:
如果在方法中,形参指向了“新对象”,那么就和实参无关了。
那什么时候,形参会在指向“新对象"呢?引用数据类型有特殊的类型,比如String类型、包装类等,这些类型是不可变的,即修改都会产生新对象,所以形参的修改和实参无关

分析

那根据这道题,来分析一下具体的流程,体会一下方法的参数传递机制

第一步

在main方法中创建了这5个变量并赋值后,在JVM虚拟机中的分布情况

int i = 1,直接在虚拟机栈中存在

String str = “hello”,这种字面量赋值的方式,str保存的是字符串常量池中hello的引用地址

Integer num = 200,200会在堆内存中开辟空间,num保存的是200的引用地址

int[] arr = {1, 2, 3, 4, 5},数组会在堆内存中开辟空间,arr保存的是数组的引用地址

MyData my = new MyData(),新建一个对象,会在堆内存为这个对象开辟空间,my保存的是这个对象的引用地址

画张图说明一下,图中数据的内存地址是假定的
在这里插入图片描述
字符串常量池实际是在堆内存中的
JDK6的时候,字符串常量池是在永久代中的,JDK7开始,就转移至了堆内存中了

第二步

调用change方法,change方法入虚拟机栈,change方法中的对形参的操作,修改了形参的值形参j,直接在虚拟机栈中存在并赋值

形参s,保存的是字符串常量池中helloworld的引用地址,这里体现了字符串的不可变性,s += “world”,world在字符串常量池中并不存在,所以会在字符串常量池中创建一个world字符串常量,s += "world"后,s是helloworld,依然会在字符串常量池中创建一个helloworld字符串常量

形参n,保存的是堆内存中int类型的数据201的引用地址,Integer类型的数据也是不可变的,n传入的值为200,n += 1后,n变为了201,此时会在堆内存中,为这个Integer 201开辟空间,n保存的是201的引用地址

形参a,保存的是堆内存中数组类型的数据{2,2,3,4,5}的引用地址

形参m,保存的是堆内存中MyData类型的对象的引用地址
在这里插入图片描述

第三步

在change方法执行完后,change方法出栈

此时在main方法中,int i还是等于1;
String str还是指向hello;
Integer num还是指向200;
int[] arr还是指向原数组的内存地址,但是此时数组中第一个元素发生了变化;
MyData my还是指向int a的内存地址,但是a的值发生了变化
在这里插入图片描述
这就是方法的传递机制,包含了字符串和包装类的不可变性的知识点

String类的理解

String字符串,有两种声明方式

  1. String str = “hello”; //字面量的定义方式,“hello”保存在字符串常量池中
  2. String str = new String(“hello”);// “hello”保存在字符串常量池中,new String在堆内存中分配空间

看String类的源码:
String是声明为final的,不可被继承
String实现了Serializable接口,是支持序列化的;实现了Comparable接口,是可以比较大小的
在这里插入图片描述
还有个很重要的一点
在JDK8及以前,内部定义的是final char[] value来存储字符串数据;JDK9开始改为byte[]
StringBuffer和StringBuilder以及HostSpot虚拟机中与String相关的部分,都进行了同步修改和更新
在http://openjdk.java.net/jeps/254中提供了相关说明

String类的几个容易困惑的问题

  1. test方法中,hello字符串存在于字符串常量池中,字符串常量池中的字符串是唯一的
    每次new String()会在堆内存中分配空间并创建对象,s1和s2保存的是不同String对象的引用地址,所以System.out.println(s1 == s2)会输出false
public void test(){
    String s1 = new String("hello");
    String s2 = new String("hello");
    System.out.println(s1 == s2);
}
  1. test1方法中,hello字符串存在于字符串常量池中,而字符串常量池中的字符串是唯一的,s1和s2保存的都是字符串常量池中hello字符串的引用地址,所以System.out.println(s1 == s2)会输出true
public void test1(){
    String s1 = "hello";
    String s2 = "hello";
    System.out.println(s1 == s2);
}
  1. test2方法中,字符串常量的值在编译的时候就确定了,hello和world都是字符串常量,所以s2在编译的时候就已经确定了,s2保存的是字符串常量池中helloworld字符串的引用地址,所以System.out.println(s1 == s2)会输出true
public void test2(){
    String s1 = "helloworld";
    String s2 = "hello" + "world";
    System.out.println(s1 == s2);
}
  1. test3方法中,s4由s2和s3这两个字符串类型的变量相加得到,注意是变量的相加,所以不能在编译期确定下来,所以s4会在堆内存中创建一个String对象,s4保存这个String对象的引用地址,而s1保存的是字符串常量池中helloworld字符串的引用地址,所以System.out.println(s1 == s2)会输出false
public void test3(){
    String s1 = "helloworld";
    String s2 = "hello";
    String s3 = "world";
    String s4 = s2 + s3;
    System.out.println(s1 == s4);
}
  1. test4方法中,s2和s3这两个字符串类型的变量被final关键字修饰,程序中使用s2和s3遍历的地方,编译器会直接用该变量的值进行替代。所以s4的值在编译期就确定为helloworld,s4保存的是字符串常量池中helloworld字符串的引用地址,而s1保存的也是字符串常量池中helloworld字符串的引用地址,所以System.out.println(s1 == s2)会输出true
public void test4(){
    String s1 = "helloworld";
    final String s2 = "hello";
    final String s3 = "world";
    String s4 = s2 + s3;
    System.out.println(s1 == s4);
}
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值