water men

今天,被一位先生问到了一个Java的问题,下面的代码应该输出什么:

Class Demo{
	public static void main(String[] args){
		String s = "0";
		change(s);
		System.out.println(s);
	}

	static void change(String s){
		s = "123";
	}
}

说实话,当这位先生问我问题的时候,我还是比较紧张的,毕竟他曾是某牛逼公司的牛逼架构师。

而且,往往人们遇到自认为简单的问题,往往就觉得这个问题不简单,举个例子就是:

当被问到一加一等于几的时候,人们往往会想:这个问题肯定不像我想象的这么简单,说不定有个坑正等着我去跳。

所以对于上面这道题,我脑子里面经历了下面几个过程:

1.原始想法

s是一个对象,而且不是基础类型数据,所以参数因该传递的是引用,那么在函数中改变了引用中某个属性的值(比如),所以main函数中s的值肯定也就改变了。

2.转折

但是,不会这么简单吧,他为什么要问我一个类似于一加一等于几的问题?很大可能是打印“0”,但这与我的推断结果并不相符。

3.转折

难道这道题就打印的是“123”?万一这道题就是考验人的心理素质呢?

所以:

我就坚定不移的回答了“123”。当这位先生问我两次“你确定?”之后,我依然认为他是在对我进行心理上的考验。

结果证明:我错大发了!

当这位先生把结果给我展现的时候,我觉得他一定在想:哥们儿,你当时面试怎么过的呀,进这公司不是走后门儿的吧?!(哈哈。。。)

所以我当时就更紧张了,同时:我的大脑进行飞速的运转,思考着各种各样会导致这种输出的原因。

一.第一步进阶

现在我需要研究一下Java的函数调用到底是传值还是传引用?

之前我一直认为:除了Java的基础数据类型(例如int,long等等),其余的数据类型(例如String,自定义对象等)都是引用传递。结果是:

通过查阅各种资料获知,Java中只存在值传递,值传递的时候会创建副本。比如下面这个例子:

class Demo{
	public static void main(String[] args){
		User u = new User();
		u.name = "123";
		changeName(u);
		System.out.println(u.name);
	}
	void changeName(User u){
		u = new User();
		u.name = "0";
	}
}
class User{
	public Strin name;
}

结果肯定是打印123,而不是打印0.现在引用就相当于房卡,对象就相当于房间。这个例子中传递的就是房卡,在changeName方法中,对房卡进行了修改,本来该房卡是打开1001这间房的,但是现在这个房卡已经执行1002号房间了。

那么main方法中的房卡是不是也就应该指向1002号房间了呢?答案是否定的,因为main方法调用changeName方法的时候,只不过是将房卡复制了一份,交给changeName(注意这里不是讲1001号房间复制一份交给changeName)。

所以这里传递的是引用的值,引用同样是一种数据结构。like that:

因为Java中没有指针,对内存的操作也很难下手,所以通过C语言相似的例子似乎更能够说明问题,如下所示:

#include<stdio.h>
#include<malloc.h>
#include<string.h>
void getSpace(char* str)
{
	str = (char*)malloc(sizeof(char)*10);
}
int main(int argc,char** argv)
{
	char* str;
	getSpace(str);
	strcpy(str,"nihao");
	printf("%s\n",str);
}

这个小程序就是main函数中声明一个字符串指针,然后通过将该指针以参数的形式传入到函数中进行空间分配。

空间分配完成后将一个字符串copy到该空间。并且打印该空间中的内容。

该程序会在运行期间异常终止。这里的函数形参是一个指针,就相似于Java中的引用。

虽然我们在getSpace函数中开辟了一块空间,并且让该指针指向开辟的新的空间,但是main函数中的指针是没有变的,main函数传递的只是指针值的一个副本。同样是一个copy后的房卡,main函数中的房卡仍然是指向原来的房间。

二.第二步进阶

回到最初的例子:

Class Demo{
	public static void main(String[] args){
		String s = "0";
		change(s);
		System.out.println(s);
	}
	static void change(String s){
		s = "123";
	}
}

通过第一步进阶得到的结论:main函数中保留的是String s的引用,传递到change函数中的是s的引用副本,但他们都指向堆中的同一片区域:“123”。

所以如果我们在change函数中改变了s所指向的堆中“123”的值,那么按道理来说,main函数中s所指向的值也就会发生改变。

但是问题来了,String并没有提供改变自身value的方法,所以s = “123” 就相当于 s = new String("123");!!!!!!!!!!!!!!!!!!!!!!!!,所以s所指向的堆中“123”这片区域没有发生改变。

这里发生改变的同样是让房卡副本指向了1002号房间,main函数中的房卡仍然指向1001号房间。

所以对于之前提到的那位先生问我的问题,应该输出0,而非123.

三.第三步进阶

第二步进阶的说明是错误的!俗话说:温故而知新,当我再一次查看第二步进阶的时候我想到了以前在C语言中遇到的一个问题,如下所示:

#include<stdio.h>
int main(int argc,char** argv)
{
	char* str="hello";
	str[1] = 'z';
	printf("%s\n",str);
}

这个程序很简单,就是先让一个指针指向一个字符串“hello”,再将“hello”中的‘e‘替换为‘z’。但是在运行时,该程序会异常退出,原因在于:

对于指针str我们并没有分配空间,而是让其直接指向了常量池(方法区)中的“Hello”字符串,常量池中的内容是不能够被改变的,就相当于4=5这样的赋值操作是不被允许的。

通过下面这个例子更加可以说明问题:

#include<stdio.h>
int main(int argc,char** argv)
{
	char* str1="hello";
	char* str2="hello";
}

C调试的时候相对于Java的好处就在于:可以很容易的看到变量所在的内存地址。如下图所示:

嗦噶,上面这些话都是为了引入下面的主题:对于Java来说是不是也是这样呢?String s = “123” 与 String s = new String("123")是否有区别呢? 答案是肯定的!

我们通过下面两个程序示例可以看到效果:

示例代码一:

String a = "123";
String b = "123";
a==b; //true
a.equals(b);//true

示例代码二:

String a = new String("123");
String b = new String("123");
a==b; //false
a.equals(b);//true

如下图所示:

所以对于第二步进阶中说明的main中的s与change中的s指向堆中同一片内容的说法是错误的,他们应该是指向方法区中的同一片内容。

后记:

我在这里非常诚恳的感谢这位先生对我的教导。自从学Java以来,整天都是各种框架满天飞,各种新技术不断地尝试,却忽略了根本。

多看,多想,多写!

时间: 2024-11-18 13:01:52

water men的相关文章

Water Problem

water problem 发布时间: 2015年10月10日 15:34   时间限制: 1000ms   内存限制: 256M 描述 题意很简单 给你N个数, Q个查询 每次查询给你一个区间[L, R] 你要找出 [L, R] 这个区间里面取模M后的最大值. 输入 第一行一个T,表示测试数据组数.第二行两个整数N, M (1<=N<=10^5, 1<=M<=10^9).第三行给你N个整数 整数范围在1到10^9之间.第四行给你一个整数Q. ( 1<=Q<=10^5)

leetcode -eleven:Container With Most Water

Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a containe

Container With Most Water ——解题笔记

[题目] Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a con

hdu 4974 A simple water problem(数学题)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4974 Problem Description Dragon is watching competitions on TV. Every competition is held between two competitors, and surely Dragon's favorite. After each competition he will give a score of either 0 or

[poj 2331] Water pipe ID A*迭代加深搜索(dfs)

Water pipe Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 2265 Accepted: 602 Description The Eastowner city is perpetually haunted with water supply shortages, so in order to remedy this problem a new water-pipe has been built. Builders s

407. Trapping Rain Water II

Given an m x n matrix of positive integers representing the height of each unit cell in a 2D elevation map, compute the volume of water it is able to trap after raining. Note: Both m and n are less than 110. The height of each unit cell is greater th

LeetCode11:Container With Most Water

Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a containe

dutacm.club Water Problem(矩阵快速幂)

Water Problem Time Limit:3000/1000 MS (Java/Others)   Memory Limit:163840/131072 KB (Java/Others)Total Submissions:1228   Accepted:121 [Submit][Status][Discuss] Description 函数 f:Z+→Z .已知 f(1),f(2) 的值,且对于任意 x>1,有 f(x+1)=f(x)+f(x?1)+sin(πx2) . 求 f(n) 的

【LeetCode】11. Container With Most Water

题目: Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a cont