变量以及参数传递
01.变量:作用域以及生命周期
默认值以及可见性
数据类型
C语言:全局变量和局部变量 Global Variable Local Variable
Java语言:
类变量(静态变量):类中用static关键字声明
成员变量(属性, 实例变量)
局部变量(本地变量)在方法、构造器或者块中使用
Scala: val var
Python:
R语言:
02.函数-方法等
基本构成:
参数传递: 根据语言的特点
1. C语言:
在 C 语言中,函数的参数传递方式有两种:值传递与地址传递
01.值传递:参数列表-函数名-返回值
函数参数:使用变量、常量、数组元素
函数体中的操作:将实参的值复制到形参相应的存储单元
函数在调用时,隐含地把实参 a 的值赋值给了参数 x
调用结束后:
形参的存储单元被释放,而形参值的任何变化都不会影响到实参的值
实参的存储单元仍保留并维持数值不变
返回值:
02.地址传递
函数参数:数组名或者指针作为函数参数
传递的是该数组的首地址或指针的值,而形参接收到的是地址
2. Java中
在Java中,方法的传递方式
数据类型: 基本数据类型 以及引用数据类型
赋值操作:
在基本数据类型中
在引用数据类型中
传值
传递的是值的副本。方法中对副本的修改,不会影响到调用方
传引用:
01.传递的是引用的副本,共用一个内存,会影响到调用方。此时,形参和实参指向同一个内存地址。
02.如果对象被重新创建或赋值为null,即new则会重新指向其他对象,不影响其远对象的值
对引用副本本身(对象地址)的修改,如设置为null,重新指向其他对象,不会影响到调用方。
01.传递:
01.基本数据类型,传递的都是副本。在函数中改变了副本的值不会改变原始的值
String
02.对于引用类型(对象、数组),传引用
02.从内存模型来说参数传递:
栈内存(stack)和堆内存(heap)
总结:
01.基本类型(byte,short,int,long,double,float,char,boolean)为传值
02.String、Integer、Double等immutable类型因为类的变量设为final属性,无法被修改,只能重新赋值或生成对象。
当Integer作为方法参数传递时,对其赋值会导致原有的引用被指向了方法内的栈地址,
失去原有的的地址指向,所以对赋值后的Integer做任何操作都不会影响原有值。
03.对象类型(Object,数组,容器)为传引用
总结方式:
01.在方法中,修改一个基础类型的参数永远不会影响原始参数值。
02.在方法中,改变一个对象参数的引用永远不会影响到原始引用。
然而,它会在堆中创建了一个全新的对象(指的是包装类和immutable对象)
03.在方法中,修改一个对象的属性会影响原始对象参数。
04.在方法中,修改集合和Maps会影响原始集合参数。
3.Scala
Scala的解释器在
解析函数参数(function arguments)
传值调用(call-by-value)
传名调用(call-by-name)
具体解释:
传值调用(call-by-value):计算参数表达式的值(reduce the arguments),再应用到函数内部
通常情况下,函数的参数是传值参数;即参数的值在它被传递给函数之前被确定
传名调用(call-by-name):将未计算的参数表达式直接应用到函数内部--惰性求值
使用“按名称传递参数”方式的优点是:1.减少不必要的计算; 2.减少异常
4. Python中
Python传递参数的方式:
01.从表现上:Python参数传递采用的是“传对象引用”的方式
如果函数收到的是一个
不可变对象(比如数字、字符或者元组)的引用,就不能 直接修改原始对象——相当于通过“传值”来传递对象
如果函数收到的是一个
可变对象(比如字典 或者列表)的引用,就能修改对象的原始值——相当于通过“传引用”来传递对象。
可变对象的引用也可以不修改其对象的原始值--这个就要看具体的函数的操作了
02.从内存模型来说参数传递:其主要的关键的地方是了解:
python(对可变对象和不可变对象的)赋值过程中是如何分配内存地址的。
python中,不可变对象是共享的,创建可变对象永远是分配新地址
5. R语言中
函数式编程:
环境和变量作用域--函数可以作为参数
函数可以作为参数传递
函数式编程-函数不修改局部变量
对参数进行重新赋值
代码示例
代码:--01.从具体表现角度-02.从内存模型角度
Python:
x=[6,25,3]
x.sort()
print(x) [3,6,25]
R 语言
sort(x) [3,6,25]
print(x) [6,25,3]
如果想改变x的代码,可以通过对参数x进行重新赋值来解决
即 x <- sort(x)
Java:
public class FunctionTransfer {
public static void main(String[] args){
System.out.println("Hello,Java方法的传递方式-针对基本数据类型");
// 虚拟机栈(VM Stack)--虚拟机栈、本地方法栈、程序计数器是线程独立的内存,每个线程只保存自己的数据。
//用于实现方法调用,每次方法调用就对应栈中的一个栈帧,
// 栈帧包含局部变量表、操作数栈、方法接口等于方法相关的信息。
//栈中存放局部变量,参数,运行中间结果--形参是复制实参的一份拷贝
// 具体--程序在栈内存中开辟了一块地址为AD8500的内存
int num = 20;
System.out.println("基本数据类型-调用前" + num);
// // 栈的高度称为栈的深度,栈深度受栈帧大小影响--栈深度:11114
//虚拟机栈中方法调用的深度超过虚拟机规定的最大深度,则抛出StackOverflowError,特别是方法的递归调用时
//虚拟机栈能够容纳的栈帧数量是有限的,若栈帧不断进栈而不出栈,最终会导致当前线程虚拟机栈的内存空间耗尽,
// 典型如一个无结束条件的递归函数调用
// 虚拟机栈可以动态扩展,如果扩展时没有足够的内存则抛出OutOfMemoryError
//OutOfMemoryError指的是当整个虚拟机栈内存耗尽,并且无法再申请到新的内存时抛出的异常
add(num);
// 程序在栈内存中又开辟了一块地址为AD8600的内存,将num的值30传递进来
// 执行param = 100;后,AD8600中的值变成了100
System.out.println("基本数据类型-调用后" + num);
System.out.println("############################");
System.out.println("Hello,Java方法的传递方式-针对引用数据类型");
//方法区和堆是线程共享的内存
//存储对象实例,java中通new创建的对象实例就保存在堆中
// 对象的创建和存储--堆中分配一块内存来存储对象实例并初始化对象内存空间的值为
// 访问对象通过对象的引用,引用保存在栈中
//String 类型
// String是一个引用类型,但是在作为参数传递的时候表现出来的却是基本类型的特性,
// 即在方法中改变了String类型的变量的值后,不会影响方法外的String变量的值
String s = "hello";
System.out.println("Java方法的传递方式-针对引用数据类型String 前 s=" + s);
changeString(s);
System.out.println("Java方法的传递方式-针对引用数据类型String 后 s=" + s);
System.out.println("############################");
//数组对象作为参数传递 -- 复制引用的拷贝
// 栈内存中开辟了一块地址编号为AD9500内存空间,用于存放array[0]的引用地址,
//里边放的值是堆内存中的一个地址-示例中的值为BE2500,可以理解为有一个指针指向了堆内存中的编号为BE2500的地址
//堆内存中编号为BE2500的这个地址中存放的才是array[0]的值
String[] array = new String[] {"Hello-Do you know"};
System.out.println("调用reset方法前array中的第0个元素的值是:" + array[0]);
//程序在栈内存中又开辟了一块编号为AD9600的内存空间,栈内存AD9500和AD9600
// (即array[0]和param的值)都指向了编号为BE2500的堆内存
reset(array);
//改变对象param的值实际上是改变param这个栈内存所指向的堆内存中的值
System.out.println("调用reset方法后array中的第0个元素的值是:" + array[0]);
//当用对象作为参数传递时,真正的值是放在堆内存中的,传递的是栈内存中的值,
// 而栈内存中存放的是堆内存的地址,所以传递的就是堆内存的地址。这就是它们的区别。
//自定义的类的对象作为参数传递
//对象作为参数传递时,传递的是对象的引用,也就是对象的地址
//java中方法的参数传递机制
//参考:https://www.cnblogs.com/lixiaolun/p/4311863.html
}
public static void add(int param){
param=1000;
}
public static void changeString(String s) {// 改变String型变量的函数
s = "changeString";
}
public static void reset(String[] param) {
param[0] = "hello, world!";
}
}