JAVA基础之-参数传递
cnblogs 2024-08-20 08:09:13 阅读 77
需要格外注意,java的传参是传值,并非传递地址。但某些情况下,也可以理解为传递地址。 由于实际可以传递地址,所以对原有的对象可能影响也可能不影响。
是否会影响,则取决于方法/函数中是否对参数进行重新赋值。如果重新赋值在无法印象参数关联的原来对象。
准备整理一个系列,这是系列的第一篇。
这是一个经典的问题,也是JAVA程序员所必须掌握的。
一、小结论和例子
1.1结论
内容没有多少,可以先说结论:
变量的表示和参数传递
- <li>变量是如何表示,尤其是参数是如何表示的
- 存储则具体看变量是什么类型:类静态、实例变量、方法
- 变量表示-基本类型直接存储,类类型则存储地址
- 值是如何传递的
- 如果是基本类型-则是值的副本
- 如果是类类型-则是指向具体数据的地址的副本
变量通过方法加工后,对原来变量的影响
- 方法对基础类型参数做任何处理,都不会影响到参数关联的变量的值
- 如果方法中对对象类型参数不做重新赋值,那么方法会影响参数关联的变量的值
- 如果方法中对对象类型参数重新赋值,那么方法不会影响参数关联的变量的值
1.2示例代码
本例子是基于JDK17编写:
package study.base.param;
import java.lang.reflect.InvocationTargetException;
import java.util.Random;
import com.alibaba.fastjson2.JSON;
/**
* 本类主要演示以下内容:
* <pre>
* 1.在方法中传递对象
* 2.如何在方法中交换两个对象的值
* 3.通过对象地址验证方法参数被重新赋值后,会指向另外一个对象的地址
* </pre>
*/
public class TestPassObjectParam {
public void testPassint(int x) {
x=x+10;
System.out.println("x=x+10="+x);
}
/**
* 测试-传递字符串,但是对参数整体调整,不会影响外部的变量,
* 因为这会给参数重新赋值,即重新指向另外一个对象的地址,已经不指向原来的对象
* @param s1
* @param s2
*/
public void testPassString(String s1,String s2) {
System.out.println("参数s1,s2在方法中被重新赋值,但不会影响到相关的变量");
s1=s1+"**";
s2=s2.substring(2);
}
/**
* 改变参数的局部值,会改变量本身
* @param dog
*/
public void testPassObject(Dog dog) {
dog.www();
dog.eat();
System.out.printf("参数dog的逻辑地址=%s \n",System.identityHashCode(dog));
}
/**
* 为对象类型参数重新赋值,不会改变变量
* @param dog
*/
public void testPassObjectAndchange(Dog dog) {
dog=new Dog("等级很高", "白", 24);
System.out.printf("参数dog被重新赋值后的逻辑地址=%s\n",System.identityHashCode(dog));
}
public Dog createDog(String name,String color,Integer weight){
Dog dog=new Dog(name,color,weight);
return dog;
}
/**
* 经典的字符串交换例子--这是不可能的,这是因为字符类型是不可变的。
* @param a
* @param b
*/
public void swap(String a,String b) {
String tmp=a;
a=b;
b=tmp;
}
public void swapDog(Dog a,Dog b) {
Dog c=a;
a=b;
b=c;
}
public void swapDog2(Dog a,Dog b) {
//Dog c=a;
String tsa=JSON.toJSONString(a);
String tsb=JSON.toJSONString(b);
a=JSON.to(Dog.class, tsb);
b=JSON.to(Dog.class, tsa);
}
public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
TestPassObjectParam test=new TestPassObjectParam();
// 演示基本类型和对象类型传递
// a.1 传递基本类型。基本类型 的值不会被改变
int x=10;
test.testPassint(x);
System.out.println(x); // 基本类型不会被改变,因为传递的是x的副本
//a.2 传递对象。可能改变变量的值,也可能不会。 这里需要格外小心,尤其是传递String类型的时候。
//在方法中对对象类型进行处理,是否会修改对象,需要格外小心,有时候会修改变量,有时候不会
//大体上可以有3个基本对的结论:
// 1.如果只是对参数的局部属性进行修改,那么变量也会被改变
// 2.如果对参数整体进行处理,或者重新赋值,那么变量不会被改变
// 3.以上2点用String不好理解,最好使用更加复杂的一些类进行测试
String name="lzf";
String address="宇宙银河系太阳系地球中国";
System.out.println("变量name,address被传入方法前的值");
System.out.printf("name=%s,address=%s",name,address);
test.testPassString(name, address);
System.out.println("变量name,address被传入方法后,查看它们的值是否改变");
System.out.printf("name=%s,address=%s \n\r",name,address);
System.out.println("...现在交换字符串变量name,address");
test.swap(name, address);
System.out.println("...name,address交换后的值(事实证明挖法在简单传参的情况下交换两个对象,包括字符串)");
System.out.printf("name=%s,address=%s \n\r",name,address);
System.out.println("---------------------------------------------------------");
// 通过对象的哈希编码验证在方法中对参数的赋值影响=》会被赋予另外一个对象的地址
//a.3 只是修改参数的属性,会影响变量。 演示一个Dog类型的属性变换会影响到变量,因为没有为参数重新赋值
Dog dog=test.createDog(name, "黄",15);
System.out.printf("变量dog传入方法前的逻辑地址=%s \n",System.identityHashCode(dog));
test.testPassObject(dog);
dog.www();
//a.4 参数被重新赋值,不会改变变量
test.testPassObjectAndchange(dog);
dog.www();
//b:简单传参交换两个对象也是不行的
System.out.println("------------------------------------------------------------");
Dog a=test.createDog("ss", "red", 10);
Dog b=test.createDog("ww", "black", 20);
System.out.printf("Dog a,b在交换前的颜色:%s,%s \n",a.getColor(),b.getColor());
String cb=a.getColor();
test.swapDog(a, b);
String ca=a.getColor();
System.out.printf("Dog a,b在交换后的颜色:%s,%s \n",a.getColor(),b.getColor());
if (cb.equals(ca)) {
System.out.println("Dog a,b交换失败(无法通过简单传递来交换两个对象)");
}
System.out.println("Dog a,b通过尝试通过json序列和反序列进行交换");
test.swapDog2(a, b);
System.out.printf("Dog a,b在交换后的颜色:%s,%s \n",a.getColor(),b.getColor());
cb=a.getColor();
if (cb.equals(ca)) {
System.out.println("Dog a,b交换失败(无法通过简单传递来交换两个对象)");
}
System.out.println("通过简单的论证,可以得出结论:两个对象通过一个函数来进行简单的交换属性,是不可行");
System.out.println("在没有特殊的情况下,java不可能再调整为参数复制/变量赋值的方法:先创建值,然后把值的地址赋予类变量/参数");
}
class Dog{
private String name;
private String color;
private Integer weight;
Dog(String name,String color,Integer weight){
this.name=name;
this.color=color;
this.weight=weight;
}
public void eat() {
Random rand=new Random();
int randValue=rand.nextInt(1,10);
int rd=rand.nextInt(10,100);
if (rd>50) {
this.weight+=randValue;
}
else {
this.weight-=randValue;
}
}
public void www() {
System.out.println("有一只"+color+"色,重"+weight.toString()+"斤,它正在吠叫:"+name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public Integer getWeight() {
return weight;
}
public void setWeight(Integer weight) {
this.weight = weight;
}
}
}
注:所谓的简单交换,即经典的交换方法,通过一个临时变量过度。
二、注意事项和其它一些问题
大部分情况下,参数的传递并不是一个问题,这里的注意事项,其实主要就是和字符(String)类型有关。
我们都知道,由于某些原因String本身是final存储的:
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence,
Constable, ConstantDesc {
/**
* The value is used for character storage.
*
* @implNote This field is trusted by the VM, and is a subject to
* constant folding if String instance is constant. Overwriting this
* field after construction will cause problems.
*
* Additionally, it is marked with {@link Stable} to trust the contents
* of the array. No other facility in JDK provides this functionality (yet).
* {@link Stable} is safe here, because value is never null.
*/
@Stable
private final byte[] value;
这就意味着字符对一个字符串变量重新赋值,则必须重新创建一个字符串对象。而一个新的对象必然指向一个新的地址。
当变量/参数被指向新的地址的时候,对原来的对象自然无法产生影响。
对字符串做变更的操作都会导致为创建一个新的对象,并为变量重新赋予新对象的地址。
例如常见的substring,concat,replace都是这样的,如果仅仅是访问字符变量的属性,是不会改变字符的。
所以,如果希望通过一个函数修改一个字符串,那么必须只有两种途径可以影响原来的字符变量:
1.函数返回新的字符串,并把这个新的字符串赋值给原来的变量
2.把字符串包装在某个对象内部,然后在方法中为对象的字符属性重新赋值
上一篇: [HTML]Web前端开发技术25(HTML5、CSS3、JavaScript )JavaScript基础消息对话框告警框确认框提示框命名规范1标识符2关键字3保留字注释标识符和变量——喵喵画网页
下一篇: 解决PHP 7.4安装xdebug出现configure: error: rtnetlink.h is required, please...
本文标签
声明
本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。