文章目录
![在这里插入图片描述](https://img-blog.csdnimg.cn/074a414fe4564be89dca15e3d8ebe61f.jpeg#pic_center)
一、方法、参数、返回值
许多编程语言都使用函数这个术语来命名子程序,而在Java完全面向对象的编程中,我们把子程序称为方法,方法存在于类中,是对象具有的一种行为,是对外提供服务的接口。方法一般具有返回类型、方法名、返回值。如下结构。
ReturnType methodName(/*Argument List*/){
//方法体
}
- 方法名:用来标识对象可以具备的行为,也是其他对象通知该对象要做的事情。一般方法名取名要具有标识性,能看出这个方法提供怎样的一个方法。
- 参数:参数是方法在执行的过程中需要的必要信息,可以通过参数将信息传递给方法。
- 返回值:方法体执行完毕后,可提供一个返回值,这个值可以是基本数据类型,也可以是引用类型。若返回的是空类型,那么return就只有结束方法体的功能。
二、参数传递过程中的底层原理
(1)创建对象过程中发生的事
1.对象创建并存储
上一节我们讲到对象在创建的过程中,会产生一个引用,一个数据,引用存放于栈内存中,对象数据存放于堆内存中。通过以下几个例子将说明这几个存放过程。
Type type;
这句代码的意思是创建了一个引用,这个引用用来存放Type类型的对象的地址,但是现在还没对引用进行赋值,因此此时这个引用是空的。
Type type2=new Type();
在这句代码中,我们使用了new来创建对象,此时,计算机首先在栈空间创建了一个引用“type2”,并且在堆空间中分配了一个区域用来存储type2的数据,然后将引用和对象设置对应关系,此后无论这个对象在堆空间的哪里,这个引用都和这个对象的数据存储位置绑定了。
type=type2;
这一句在执行的时候,实质上是将type2对象存储的地址赋给了type,我们后续无论是使用type2,还是type访问对象,操作的都是同一个对象。
(2)基本类型作为形参传递——值传递
基本类型作为形参传递的时候,传递的是值,在函数体内修改基本类型的值是不会改变原本的函数体外的变量的值。
public class MyCode {
public static void main(String[] args) {
int a=1,b=2;
MyCode myCode=new MyCode();
myCode.change(a,b);
System.out.println(a+" "+b);
}
public void change(int a,int b){
int temp;
temp=a;
a=b;
b=temp;
}
}
1 2
可以看到,我们在函数体内修改了a,b的值,但是函数体外的值并没有受到影响,因此,基本类型在作为形参传递的时候,只是将值传递进去,并没有将变量传进去。
(3)引用类型作为形参传递——本质也是值传递
引用类型在作为形参传递的时候,很多人认为这是一个地址传递,因为引用类型在传入函数体后,我们操作引用的对象,对象的数据是改变了的,因此很多人认为这是一个地址传递,但引用和地址存在着本质的区别。我们来看下面这个例子。
public class Mycode2 {
public static void main(String[] args) {
Node node=new Node("A");
Node node2=new Node("B");
System.out.println(node.name+"的原始地址为"+node);
System.out.println(node2.name+"的原始地址为"+node2);
Mycode2 mycode2=new Mycode2();
mycode2.change(node,node2);
System.out.println(node.name+"方法体处理后地址"+node);
System.out.println(node2.name+"方法体处理后地址"+node2);
}
public void change(Node node,Node node2){
System.out.println(node.name+"刚传进来的地址为"+node);
System.out.println(node2.name+"刚传进来的地址为"+node2);
node=new Node("C");
node2.name="D";
System.out.println(node.name+"方法体内的地址"+node);
System.out.println(node2.name+"方法体内的地址"+node2);
}
}
class Node{
public String name;
public Node(String name){
this.name=name;
}
}
运行结果:
A的原始地址为wttext3.Node@1540e19d
B的原始地址为wttext3.Node@677327b6
A刚传进来的地址为wttext3.Node@1540e19d
B刚传进来的地址为wttext3.Node@677327b6
C方法体内的地址wttext3.Node@14ae5a5
D方法体内的地址wttext3.Node@677327b6
A方法体处理后地址wttext3.Node@1540e19d
D方法体处理后地址wttext3.Node@677327b6
通过这个例子我们可以看出,结合内存模型,我们看一下刚创建对象发生的事,创建两个栈区内存,两个堆区内存,建立了映射关系。
然后我们来看传入方法体内发生了什么,很明显,传入的是引用,引用本身存的是一个堆区地址。
再看方法体内发生了什么,我们通过new创建了新的对象,这个对象命名为C,我们把方法体内A的引用变为C,所以此时它指向的是C的数据区,然后,我们通过传入的B的引用修改了堆区的内容
接下来方法体结束后的操作:我们可以看到,在函数体内将Node A的内容修改为指向C,方法体结束后,并没有使栈区中的Node A指向C,但是,我们通过传入的B的引用,修改了B中数据,数据在堆区中被修改了,方法体结束后B中的数据已经修改为了D。
由此我们可以看出,Java中的引用传递,本质上还是值传递,只不过这个时候我们传递的是引用的值而已(也可以理解为对应的堆区的数据的地址) 因此我们在方法体结束后,我们并不能修改引用,但是在方法体内我们可以通过传入的引用去修改堆区内的内容。
(4)Java中的引用与C++中引用、指针的区别,
java中的引用是分配内存的,用来存储对象数据域的地址,并且这个数值会随着对象地址改变而改变,Java中的引用类似C++的指针,但是与C++的指针又有所不同,C++的指针不会随着对象的地址改变而改变,并且C++中的指针是可以运算的。而C++中的引用,可以理解为是为对象取了一个别名,这个引用是没有占用内存的。
三、特殊类型的参数传递(包装类)
1.包装类
Java是一个完全面向对象的语言,对于七种基本数据类型都有一个包装类,以及一个String类,这些包装类的对象在作为参数传递的过程中是作为引用类型的。
2.特殊的类作为参数传递时具有值传递特性
包装类在进行参数传递时,也可以将其看做基本数据类型,在方法体内改变他们的值是无法改变方法体外的值的。例如下面这个例子。
public class MyCode {
public static void main(String[] args) {
String a="Java";
MyCode myCode=new MyCode();
myCode.change(a);
System.out.println(a);
}
public void change(String a){
a="C语言";
}
}
在类内改变这个数据部分,类外的a并没有改变值,这是因为在函数体内的a="C语言"这句话其实相当于String a=new String(“C语言”);这相当于创建了一个新的对象,然后将新对象的引用传给了a,这个同上面那个例子一样,引用的传递是值传递,所以a在方法体结束后并没有指向新的对象“C语言”。