首页 > 试题广场 >

以下代码输出的是

[单选题]
以下代码输出的是:
public class SendValue{
	public String str="6";
	public static void main(String[] args) {
		SendValue sv=new SendValue();
		sv.change(sv.str);
		System.out.println(sv.str);
	}
	public void change(String str) {
		str="10";
	}
}

  • 6
  • 10
  • 都不对
  • 16

补充Java内存管理知识:

1. 内存分配策略

按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的。

静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编译时就可以给他们分配固定的内存空间。这种分配策略要求程序代码中不允许有可变数据结构(比如可变数组)的存在,也不允许有嵌套或者递归的结构出现,因为它们都会导致编译程序无法计算准确的存储空间需求。

栈式存储分配也可称为动态存储分配,是由一个类似于堆栈的运行栈来实现的。和静态存储分配相反,在栈式存储方案中,程序对数据区的需求在编译时是完全未知的,只有到运行的时候才能够知道,但是规定在运行中进入一个程序模块时,必须知道该程序模块所需的数据区大小才能够为其分配内存。和我们在数据结构所熟知的栈一样,栈式存储分配按照先进后出的原则进行分配。

静态存储分配要求在编译时能知道所有变量的存储要求,栈式存储分配要求在过程的入口处必须知道所有的存储要求,而堆式存储分配则专门负责在编译时或运行时模块入口处都无法确定存储要求的数据结构的内存分配,比如可变长度串和对象实例。堆由大片的可利用块或空闲块组成,堆中的内存可以按照任意顺序分配和释放。

2. JVM中的堆和栈

JVM是基于堆栈的虚拟机。JVM为每个新创建的线程都分配一个堆栈,也就是说,对于一个Java程序来说,它的运行就是通过对堆栈的操作来完成的。堆栈以帧为单位保存线程的状态。JVM对堆栈只进行两种操作:以帧为单位的压栈和出栈操作。

java把内存分两种:一种是栈内存,另一种是堆内存

栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。

栈(stack):是一个先进后出的数据结构,通常用于保存方法(函数)中的参数,局部变量。

堆(heap):是一个可动态申请的内存空间(其记录空闲内存空间的链表由操作系统维护),是一个运行时数据区,C中的malloc语句所产生的内存空间就在堆中。

3. 堆和栈优缺点比较

栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享,详见第3点。

堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。

4. Java中的数据类型有两种

一种是基本类型

共有8种,即int, short, long, byte, float, double, boolean, char(注意,并没有string的基本类型)。

这种类型的定义是通过诸如int a = 3; long b = 255L;的形式来定义的,称为自动变量。值得注意的是,自动变量存的是字面值,不是类的实例,即不是类的引用,这里并没有类的存在。如int a = 3; 这里的a是一个指向int类型的引用,指向3这个字面值。这些字面值的数据,由于大小可知,生存期可知(这些字面值固定定义在某个程序块里面,程序块退出后,字段值就消失了),出于追求速度的原因,就存在于栈中。

另外,栈有一个很重要的特殊性,就是存在栈中的数据可以共享。假设我们同时定义:

int a = 3;
int b = 3;
编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找有没有字面值为3的地址,没找到,就开辟一个存放3这个字面值的地址,然后将a指向3的地址。接着处理int b = 3;在创建完b的引用变量后,由于在栈中已经有3这个字面值,便将b直接指向3的地址。这样,就出现了a与b同时均指向3的情况。

特别注意的是,这种字面值的引用与类对象的引用不同。假定两个类对象的引用同时指向一个对象,如果一个对象引用变量修改了这个对象的内部状态,那么另一个对象引用变量也即刻反映出这个变化。相反,通过字面值的引用来修改其值,不会导致另一个指向此字面值的引用的值也跟着改变的情况。如上例,我们定义完a与b的值后,再令a=4;那么,b不会等于4,还是等于3。在编译器内部,遇到a=4;时,它就会重新搜索栈中是否有4的字面值,如果没有,重新开辟地址存放4的值;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。

另一种是包装类数据

如Integer, String, Double等将相应的基本数据类型包装起来的类。这些类数据全部存在于堆中,Java用new()语句来显示地告诉编译器,在运行时才根据需要动态创建,因此比较灵活,但缺点是要占用更多的时间。

String是一个特殊的包装类数据。即可以用String str = new String(“abc”);的形式来创建,也可以用String str = “abc”;的形式来创建(作为对比,在JDK 5.0之前,你从未见过Integer i = 3;的表达式,因为类与字面值是不能通用的,除了String。而在JDK 5.0中,这种表达式是可以的!因为编译器在后台进行Integer i = new Integer(3)的转换)。前者是规范的类的创建过程,即在Java中,一切都是对象,而对象是类的实例,全部通过new()的形式来创建。Java中的有些类,如DateFormat类,可以通过该类的getInstance()方法来返回一个新创建的类,似乎违反了此原则。其实不然。该类运用了单例模式来返回类的实例,只不过这个实例是在该类内部通过new()来创建的,而getInstance()向外部隐藏了此细节。那为什么在String str = “abc”;中,并没有通过new()来创建实例,是不是违反了上述原则?其实没有。

 5.String在内存中的存放

String是一个特殊的包装类数据,可以用用以下两种方式创建:

String str = new String(“abc”);第一种创建方式是用new()来新建对象的,它会存放于堆中。每调用一次就会创建一个新的对象。

String str = “abc”;  第二种创建方式先在栈中创建一个对String类的对象引用变量str,然后在栈中查找有没有存放值为”abc”的地址,如果没有,则开辟一个存放字面值为”abc”的地址,接着创建一个新的String类的对象o,并将o的字符串值指向这个地址,而且在栈中这个地址旁边记下这个引用的对象o。如果已经有了值为”abc”的地址,则查找对象o,并返回o的地址,最后将str指向对象o的地址。

值得注意的是,一般String类中字符串值都是直接存值的。但像String str = “abc”;这种场合下,其字符串值却是保存了一个指向存在栈中数据的引用!

6.数组在内存中的存放

int x[] 或者int []x 时,在内存栈空间中创建一个数组引用,通过该数组名来引用数组。

x = new int[5] 将在堆内存中分配5个保存int型数据的空间,堆内存的首地址放到栈内存中,每个数组元素被初始化为0。

7.static变量在内存中的存放

用 static的修饰的变量和方法,实际上是指定了这些变量和方法在内存中的“固定位置”-static storage。既然要有“固定位置”那么他们的 “大小”似乎就是固定的了,有了固定位置和固定大小的特征了,在栈中或堆中开辟空间那就是非常的方便了。如果静态的变量或方法在不出其作用域的情况下,其引用句柄是不会发生改变的。

8. java中变量在内存中的分配

1、类变量(static修饰的变量)

在程序加载时系统就为它在堆中开辟了内存,堆中的内存地址存放于栈以便于高速访问。静态变量的生命周期一直持续到整个”系统”关闭

2、实例变量

当你使用java关键字new的时候,系统在堆中开辟并不一定是连续的空间分配给变量(比如说类实例),然后根据零散的堆内存地址,通过哈希算法换算为一长串数字以表征这个变量在堆中的”物理位置”。 实例变量的生命周期–当实例变量的引用丢失后,将被GC(垃圾回收器)列入可回收“名单”中,但并不是马上就释放堆中内存

3、局部变量

局部变量,由声明在某方法,或某代码段里(比如for循环),执行到它的时候在栈中开辟内存,当局部变量一但脱离作用域,内存立即释放

发表于 2017-10-27 13:45:47 回复(35)
发表于 2016-08-10 22:15:23 回复(22)
Java中String类型变量是immutable(不可变的)。
尽管 change()方法中的str与sv.str都是新的对象实例成员变量值"6"的引用, 由于String类型的 不可变 性,change()方法中的str="10"语句实际上是将传入的str副本引用指向了一个值为“10”的新的内存地址, 原数据引用 sv.str的引用值(也就是“6”的内存地址) 并没有发生改变,因此sv.str指向的值仍旧为6.

参考博客:http://www.cnblogs.com/alexlo/p/3493755.html
编辑于 2016-01-05 20:15:19 回复(11)
昨天刚看的这个。java中的方法传递都是值传递,java中的数据类型有基本类型和引用类型,他们都是值传递方式。基本类型传递的是它的值,因此方法中的改变参数的值,不会影响方法外。引用类型传递的是一个地址,因为引用类型在生成对象实例时,里面的值是一个地址,指向了对象实例。在传值的时候实际上传的是一个地址,他们指向了同一块地址,所以在方法内的改变会影响方法外的参数。 这里比较乱人心的是包装类型,因为包装类型也是引用类型,这里应该就是和包装类型的实现有关了,在包装类型中,比如Integer a=1,有一个自动装箱的操作。其实a=1,如果现在令a=2,不会令2覆盖1(即1本身是不会变的),真正改变的是a被赋给了一个新地址,这个地址指向了2。因此方法内的改变包装类型的值就相当于改变了形参里面的地址,相当于重新new了一遍。而方法外面的实参仍旧指向含1的那个地址,一次方法内的改变不会影响方法外的实参。
发表于 2015-12-01 09:12:54 回复(15)
package com.qtm.q04z;
public class chengfabiao { 
public String str="6";
    public static void main(String[] args) {
    chengfabiao sv=new chengfabiao();
   
        sv.change(sv.str);
        System.out.println(sv.str);
    }
    public void change(String str) {
        str="10";
       this.str="10";
       System.out.println("str"+str);
    }
}
首先执行的是change里面的,然后才执行main方法里面的,个人认为change里面的str=“10”时候并没有把值带过去,结果传的还是str="6"这个值,所以打印出来显示的是6,只有通过this.str才可以修改
发表于 2015-10-19 20:39:24 回复(0)
change方法里面是将传入的参数修改成10,但是并没有对SendValue的属性str就行修改,要修改必须指明this.str="10",所有答案还是6
编辑于 2015-10-13 19:27:19 回复(7)
我觉得要说清这个问题有两点要知道: 1.java中的string是不可变的 2.java中函数参数传递方式是引用传递 具体是: 函数change的形参str最初指向的是“6”这块地址,当将“10”赋给str时由于string的不可变性jvm会分配新对象给str,而原来的“6”内存空间并不变
发表于 2016-08-05 11:15:36 回复(4)
参数传递问题分两类:值传递和引用传递

值传递:传递基本数据类型。这道题属于值传递

可以看出change()方法的调用开辟了新的栈空间change方法栈中的str的改变并不会影响main方法栈帧中的str

引用传递:(改一下这道题)



发表于 2017-11-14 13:53:50 回复(1)
参数传递分为两种 a.基本类型 b.引用类型

                    基本类型:形式参数的改变不影响实际参数

                   引用类型:形式参数的改变直接影响实际参数

                   String 作为参数传递,效果和基本类型作为参数传递一样的

                   基本类型是值传递,引用类型是引用传递

                   值传递不改变原来的值,引用传递会改变原来的值。

发表于 2017-04-21 14:42:49 回复(3)

这段代码是起迷惑作用他把6传进去但10并没有返回出来,他最后输出的还是6

发表于 2015-10-13 10:02:51 回复(0)
1.别再说这是值传递了,所有的对象都是引用传递,包括包装类和String类
2.至于为什么它们产生了不可变的效果,就像值传递一样,String的底层是被final修饰的char数组,Integer类的value属性也是被final修饰的
3.假设有一个基本数据类型int a=3,如果我们再进行a=2的操作,它会在原本存放3的位置,把3变成2。
而String和Integer这种则不同,如果有个Integera=1和String a="hello",这时进行a =2和a="hi"操作后,输出a后确实发现值也同样向上面的基本数据类型一样被更改了。但是!底层原理是不同的,Integer和String并没有在原理1和"hello"的位置直接进行更改,而是重新指向了一个新地址,这个地址存放的分别是2和"hi",所以你打印出来的是2和"hi"
4.String和Integer传参你可以理解为。一个箱子有一把钥匙a,它可以打开宝库A。里面有一个苹果,这时a当做参数传给了一个方法产生呃形参b,这时形参b也是一把钥匙,它同样指向宝库A(重点,因为这样就是引用传递了,因为指向的地址是一样的),这时当我们企图让b打开的宝库是梨子时,你不可以把宝库A里面的苹果换成梨子,因为Integer和String告诉你我们是不可变的。编译器则会重新建成一个宝库B里面放上梨子,让b钥匙变成一把打开B宝库的钥匙。所以最后,a钥匙打开的会变成梨子吗?当然不会,所以值是没有改的,但其实它确实是引用传递而不是值传递
发表于 2021-12-04 11:02:08 回复(0)
在java核心技术卷1上已经明确说明,参数是按照值传递的,不管是基本类型还是引用类型;
发表于 2016-09-28 21:04:21 回复(4)

题目加一个this答案就是10

发表于 2018-10-18 15:28:55 回复(1)
change方法里面是将传入的参数修改成10,但是并没有对SendValue的属性str就行修改,要修改必须指明this.str="10",所有答案还是6
发表于 2016-07-01 21:57:29 回复(1)
记住不会改变就可以了
发表于 2016-01-08 14:55:17 回复(1)
答案是6,双引号String存放在常量池中,应该算是值传递吧
发表于 2015-10-10 15:01:29 回复(0)
这题考点应该是: 方法里没加this! str仅是局部变量,对象的成员变量丝毫没被影响。

关于String的不变性,由老杜课上讲的是:String不可变不是说str引用的地址不可变,而是说的双引号里的对象不可变,而str并没有被final修饰,str所保存的对象的内存地址是可变的。

若有错误,还望大佬们指正。
编辑于 2021-12-04 12:29:08 回复(0)
高赞的那个回答有问题,这里和String没有关系。把String换成其他类型都是不会改变输出的值的。希望大家尝试一下
发表于 2018-06-07 09:51:52 回复(0)
package Wrong_topic;

public class SendValue{
    public String str="6";
    public static void main(String[] args) {
        SendValue sv=new SendValue();
        sv.change(sv.str);
        System.out.println(sv.str);
    }
//    public void change(String str) {
//        this.str="10";
//    }//结果为10
public void change(String str) {
        str="10";
    }//结果为6
}


发表于 2022-10-16 21:37:04 回复(0)
这个题我理解的是,参数传递过程中:
对于基本数据类型数据的传递,传递的是值,所以方法内的参数的改变不会改变变量的值;
对于引用类型数据的传递,传递的也是值(地址值),即对象引用的副本,那么在方法内对参数内容的改变只能是改变该引用的副本所指向的对象的内容;
所以本题中,方法调用sv.change(sv.str);使得引用副本指向了其他引用,这样没有改变原引用sv.str,它指向对象的还是"6"
编辑于 2020-03-04 17:10:01 回复(0)