java入门(3)

函数和方法

如果我们经常要进行一些相似的处理过程,就可以把这个处理过程封装为函数

函数可以被多次重复调用,从而实现代码重用和隔离的目的。

在面向对象的语言中,函数经常和对象绑定在一起,为区分起见,这时它被称为方法

因为java是完全面向对象的,函数必须从属于某个类。所以java中的函数都被称为方法。

如果方法前以 static 修饰,则称为静态方法,可以粗略地认为,它与 c 语言的函数的概念大致相等了。

方法可以在其括号中列出调用它时需要准备的参数,叫形式参数

方法也可以有一个返回值(或没有,此时写void)。

下面,我们调用方法 f,求两个整数的末位是否相同。

1 1 public class A0403 2 2 { 3 3 static boolean f(int a, int b){ 4 4 int a1 = a % 10; 5 5 int b1 = b % 10; 6 6 return a1==b1; 7 7 } 8 8 9 9 public static void main(String[] args){ 1010 System.out.println(f(101,12)); 1111 System.out.println(f(65432,12)); 1212 } 1313 }

这个方法接受两个参数,都是整数,它进行一系列的处理后,返回一个布尔值。

return 语句结束 f 方法的执行,并返回一个指定的值。

在遇到 return 语句后,f 方法的其它代码就不再执行,而是要返回调用它的那个方法中。当然,在此方法中,return 语句后恰好没有更多的语句了。

如果一个方法定义了返回值,它在结束执行之前就一定会遇到 return 语句,否则会引发编译错误。

形参独立原理

如果在被调函数中改变了形参的值,会不会影响调用的一方呢? 不会!

我们看这个例子:

1 1 public class A0404 2 2 { 3 3 static void f(int x){ 4 4 int sum = x % 10; 5 5 x /= 10; 6 6 sum = sum * 10 + x % 10; 7 7 x /= 10; 8 8 sum = sum * 10 + x % 10; 9 9 System.out.println(sum); 1010 System.out.println("x=" + x); 1111 } 1212 1313 public static void main(String[] args){ 1414 int a = 368; 1515 f(a); 1616 System.out.println("a=" + a); 1717 } 1818 }

这里的函数f的功能是:把传给它的3位数 x 的数位反转并输出。

为了观察,我们额外输出了 a 的值,以及 x 的值。

通过结果可以看到,虽然 x 的值在计算过程中发生了变化,但这并不会影响 a 的值。

实际上,在调用 f 之前,要为它准备需要的参数,这些参数必须新创建,所以才称为形式参数。

也就是说,在没有调用 f 函数的时候,这些形参变量是不存在的,在多次调用 f 的时候,这些形参就会被创建多次。

创建形参后,把实参的值(这里就是 a 的值)拷贝给它,然后才开始 f 的执行。

当 f  执行结束后,形参变量会被自动释放掉。

从更底层的机制看,这个形参的分配与释放的过程是通过栈来完成的。

函数调用前,要把返回的位置,需要的参数等信息压栈,在函数执行完毕后,自动弹栈,恢复执行前的样子。

如果被调用的函数还会去调用其它函数,这个过程还会继续上演。

这样,栈可能就会越涨越高,但函数的执行总有结束的时候,那时栈就会落回来。

如果由于某种设计失误,导致函数调用一直没能正确返回,而是不断地调用其它的函数,就会导致栈的溢出,这是很常见的程序错误。

从参数传递的原理上我们看到,实参与形参是各自独立的变量,除了开始的时候,实参拷贝给形参外,它们再不会有任何联系。这叫做

"形参独立原理"。

这种设计为我们省去了许多麻烦,但有时我们可能会想让被调方与主调方共享一个变量,而不是各自独立,这怎么办呢?

答案是传指针,在java中叫做:引用。

引用作为参数

引用的本质是持有另一个对象的地址。

如果对一个引用进行复制,只不过是两个引用指向了同一个对象,并没复制对象本身。

引用与面向对象的体系紧密联系在一起,所以要等到学了初步的面向对象的知识后我们才能更好地理解它。

但这里,我们可以先看看数组的行为,来窥其端倪。

数组就是一种对象类型,我们定义的数组变量,实际上是指向实际数组对象的指针,或说:引用。

1 1 public class A0404 2 2 { 3 3 static void f(int[] x){ 4 4 for(int i=0; i<x.length; i++){ 5 5 if(x[i] < 0) x[i] = -x[i]; 6 6 } 7 7 } 8 8 9 9 public static void main(String[] args){ 1010 int[] a = {5, -3, 6, 10, 0, -15}; 1111 f(a); 1212 System.out.println(java.util.Arrays.toString(a)); 1313 } 1414 }

这里的形参 x,实参 a 都不是数组本身,它们是指向数组的引用。

虽然  x 和 a 也是按照同样的规则,不会相互影响,但由于它们指向了同一个对象,这就引起了复杂的现象。

从结果上,我们可以观察到,在 f 方法中修改了数组对象的值,在主调方打印数组时也看到了这些变化。

其原理图:

引用是一种在主调函数和被调函数间共享数据的常用手段。

在上面的这个例子中,引用变量本身并没有发生变化。发生变化的是引用所指向的对象,所以“形参独立性原理”并没有因此而破坏。

我们可能会看到某些材料上写着: java 有两种传递参数的方式,一种是传值(传拷贝),另一种是传引用。

这可能会产生一些误导。实际上Java只有一种传递参数的方式,就是传值。

而这个被传递的值有可能会恰好是一个引用类型,就会引起一些类似共享变量的效果,其实并没有什么特殊的秘密,也不应该算是“另一种”传递方式。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Java日期时间API系列31

  时间戳是指格林威治时间1970年01月01日00时00分00秒起至现在的总毫秒数,是所有时间的基础,其他时间可以通过时间戳转换得到。Java中本来已经有相关获取时间戳的方法,Java8后增加新的类Instant等专用于处理时间戳问题。 1获取时间戳的方法和性能对比1.1获取时间戳方法Java8以前

JS 对象数组Array 根据对象object key的值排序sort,很风骚哦

有个js对象数组varary\{id:1,name:"b"},{id:2,name:"b"}\需求是根据name或者id的值来排序,这里有个风骚的函数函数定义:function keysrt(key,desc) {  return function(a,b){    return desc ? ~~(ak