俺又来了,今天给大家分享的东西比较杂,本来打算只分享java中new出来对象当作一个参数传递给其他对象后,被改变后,那么这个对象也会改变(具体怎么表达我也不太知道如何表达,等下用具体例子来给大家演示),然后在找资料的时候,发现了很多讲String、Integer和int中三者比较的是关系;然后索性今天这篇文章把这也给大家分享下吧,好了,废话少说,直接上干货。
周四见项目代码中有一段代码表示在A方法中new出来一个对象,这个对象当作一个参数传递给B方法,然后在B方法中将该参数对象进行修改,注意哈:修改后该对象并没有返回给A方法,B方法返回的是void,然后在A方法中将该参数打印出来,惊奇发现该对象值发生改变,对象值打印是B方法中修改后对象值。当时就一脸懵,然后自己又重新写了下测试例子,还真是发生了改变,下面我分析出如下结果:
先说下代码例子:
A方法代码如下:
@GetMapping("/testA") private void testA() { People people = new People(); people.setName("ygl"); people.setAge(18); //将people对象传递给B方法 testService.testB(people); //打印people对象值 System.out.println("People:" + people.toString()); }
B方法代码如下所示:
public void testB(People people){ people.setName("ly"); people.setAge(99); }
打印结果为:
People:People(name=ly, age=99, address=null);
java中A方法new出来对象后,将该对象存放进java堆中,然后该对象名存放在栈中,栈中该对象名指向堆中地址;然后将该对象当作参数传递给B方法,然后B中该参数名指向堆中地址,然后B方法中对其对象修改,则是修改堆中对象,因此所有指向该对象的对象名都会修改,也就是出现了以上B方法中未返回给A方法该对象,但是A中对象却发生更改。
下面图片更容易理解:
通过上面解释,大家应该很容易理解上面出现的问题了。
String是java中引用类型,它可不属于java中八大基本类型,这一点大家千万要注意。
刚刚在上一个段落中分析的是new出来实体的对象出现的上面结果,我就想到测试下String类型来试试看会出现那种结果呢,测试代码如下所示:
A方法:
@GetMapping("/testA") private void testA() { String str = "ygl"; //将people对象传递给B方法 testService.testB(str); //打印people对象值 System.out.println("Str:" + str); }
B方法:
public void testB(String str){ str = "ly"; }
打印结果如下所示:
Str:ygl
大家可以很明显的看出,没有出现和上面new对象一样结果呀,String也是引用类型,这是为什么呢?大家不要急,我接下来给大家分析下(大家先等下我哦,我先下楼去买点水果,嘻嘻,等我哦,很快的)。
String有两种情况,一种是String str = new String(“abc”);另外一种为String str = “abc”。
第一种String str = new String(“abc”);这种都是在堆中创建字符串对象,然后变量名str是指向堆中该变量地址;
第二种String str = “abc”;这种通过字面量赋值创建字符串,会先在常量池中查找是否有相同的字符串,如果存在,则将栈中的引用直接指向该字符串;如果不存在,则在常量池中生成该字符串,然后栈中引用再指向该字符串。
注意:String是不可变对象,因此在每次改变String的时候,都是生成一个新的String对象,然后再将栈指向新的堆内存或者常量池。因为String源代码中修饰符为final,因此会出现这种结果。
源代码如图所示:
接下来我给大家解释下在上面例子中为什么出现那种结果,直接用图给大家解释下:
谈到String都到这种地步了,这个时候如果我不给大家聊聊String类型的各种比较的话,那么我就太不够意思了,那接下来来和大家聊聊,先上代码:
private void testNew() { String str1 = "str"; String str2 = "str"; System.out.println(str1 == str2); //true String str3 = new String("str"); String str4 = new String("str"); System.out.println(str3 == str4); //false System.out.println(str1 == str3); //false }
打印结果都在后面注释中写着的,在这里我就不单独给大家再写了。
分析结果:
str1与str2结果比较:创建str1属性时,会先在常量池中查找是否有相同属性值,这时发现常量池中没有该常量,则会在常量池中创建str1的属性值str
,然后将str1属性指向常量池中值str
的地址;创建str2属性时,同样先在常量池中查找是否有str
的值,这时发现拥有该值,那么不再重新创建str
值,直接将str2属性指向常量池中str
值。因此str1在与str2的双等号比较其地址时,两个属性指向的是同一个属性值地址,因此两者比较结果为true
。
str3与str4结果比较:创建str3时,在堆内存中创建new String("str")
,然后str3变量名存放在栈中,栈中str3
指向堆内存的str3刚刚创建的地址;创建str4时,在堆内存中又创建一个new String("str")
,然后str4变量名存放在栈中,栈中str4指向堆内存中刚刚创建的地址;一次两个变量指向地址是不一样的,因此两个变量在比较时,结果为false。含义如下图所示:
str1与str3结果比较:str1变量指向的是常量池中(常量池在堆内存中,独立开辟出个内存空间),str3变量指向堆内存中的地址,因此两者在比较地址的时候会出现false结果。
int是基本类型,Integer是int的包装类型,可以自动拆箱和装箱,这里我就不做过多解释这方面内容,大家可以自行百度哦,我这里就写如下代码来进行分析与研究。
a:分析下面一段代码:
private void testNew() { //-128 ~ 127之间结果比较为true;不在这个范围内则为false Integer i1 = 127; Integer i2 = 127; System.out.println("1:" + (i1 == i2)); //true Integer i3 = 999; Integer i4 = 999; System.out.println("2:" + (i3 == i4)); //false }
代码中打印结果我在后面注释中已经标注。
Integer直接进行赋值时,如果数值在-128 ~ 127
范围内的话,则会自动拆箱为int类型,数值存放在常量池中,因此i1 和 i2的结果在比较中结果为true
;当数值超过这个范围时,它会自动装箱,例如:Integer = 999
时,内部实际运行代码和new Integer(999)
一样,在堆内存中存放,因此i3和i4结果为false
。
b:分析下面一段代码:
private void testNew() { Integer i5 = new Integer(12); Integer i6 = new Integer(12); System.out.println("1:" + (i5 == i6)); //false }
这里采用的new Integer(12)
,其运行原理是在每new一次,那么就会在堆内存中创建其对象,栈中变量名指向其堆内存地址,因此上面代码段中i5与i6指向堆内存地址是不一样的,所以其比较结果为false
。注意哈,这里不论其数值,也就是数值无论在不在-128 ~ 127之间,其原理都会在堆内存中创建对象,因此不论其数值,比较结果都为false。
c:分析下面一段代码:
private void testNew() { int int1 = 990; int int2 = 990; System.out.println("1:" + (int1 == int2)); //true }
上面代码采用是int
直接进行赋值,也就是变量名存放在栈中,值存放在常量池中,每创建一个变量是,先去常量池中去查找该值是否存在,如果存在则直接指向,如果不存在则会在常量池中进行创建。因此上述代码中两个指向的是同一个地址,比较结果为true
。
好了,上面就是给大家分享的知识点了,如果有那些不对的地方还恳请大家指出,我进行改正。
大家记得转发、点赞和评论哦。拜拜,这周末又过完了,明天要上班喽,加油干饭人。