Java 值传递/引用传递以及final/static问题

参数传递:

问:当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 
  答:是值传递。Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,参数的值就是该对象的引用一个副本。指向同一个对象,对象的内容可以在被调用的方法中改变,但对象的引用(不是引用的副本)是永远不会改变的。

  Java参数,不管是原始类型还是引用类型,传递的都是副本(有另外一种说法是传值,但是说传副本更好理解吧,传值通常是相对传址而言)。

  如果参数类型是原始类型,那么传过来的就是这个参数的一个副本,也就是这个原始参数的值,这个跟之前所谈的传值是一样的。如果在函数中改变了副本的值不会改变原始的值.

  如果参数类型是引用类型,那么传过来的就是这个引用参数的副本,这个副本存放的是参数的地址。如果在函数中没有改变这个副本的地址,而是改变了地址中的 值,那么在函数内的改变会影响到传入的参数。如果在函数中改变了副本的地址,如new一个,那么副本就指向了一个新的地址,此时传入的参数还是指向原来的 地址,所以不会改变参数的值。

基本类型参数传递:不改变值

       

引用类型参数传递:改变值

 

 

无论是基本类型作为参数传递,还是对象作为参数传递,实际上传递的都是值,只是值的的形式不用而已。第一个示例中用基本类型作为参数传递时,将栈内存中的值30传递到了add方法中。第二个示例中用对象作为参数传递时,将栈内存中的值BE2500传递到了reset方法中。当用对象作为参数传递时,真正的值是放在堆内存中的,传递的是栈内存中的值,而栈内存中存放的是堆内存的地址,所以传递的就是堆内存的地址。这就是它们的区别。

补充一下,在Java中,String是一个引用类型,但是在作为参数传递的时候表现出来的却是基本类型的特性,即在方法中改变了String类型的变量的值后,不会影响方法外的String变量的值。关于这个问题,可以参考如下两个地址:

http://freej.blog.51cto.com/235241/168676

http://dryr.blog.163.com/blog/static/58211013200802393317600/

我觉得是这两篇文章中提到的两个原因导致的,一个是String实际上操作的是char[],可以理解为String是char[]的包装类。二是给String变量重新赋值后,实际上没有改变这个变量的值,而是重新new了一个String对象,改变了新对象的值,所以原来的String变量的值并没有改变。

参考:https://www.cnblogs.com/lixiaolun/p/4311863.html

 

 在Java中,final关键字可以用来修饰类、方法和变量(包括成员变量和局部变量)。下面就从这三个方面来了解一下final关键字的基本用法。

1.修饰类

  当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final类中的成员变量可以根据需要设为final,但是要注意final类中的所有成员方法都会被隐式地指定为final方法。

2.修饰方法

  下面这段话摘自《Java编程思想》第四版第143页:

  “使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升。在最近的Java版本中,不需要使用final方法进行这些优化了。“

  因此,如果只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。

  注:类的private方法会隐式地被指定为final方法。

  3.修饰变量

  修饰变量是final用得最多的地方,也是本文接下来要重点阐述的内容。首先了解一下final变量的基本语法:

  对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。

 

1.类的final变量和普通变量有什么区别?

  当用final作用于类的成员变量时,成员变量(注意是类的成员变量,局部变量只需要保证在使用之前被初始化赋值即可)必须在定义时或者构造器中进行初始化赋值,而且final变量一旦被初始化赋值之后,就不能再被赋值了。

2.被final修饰的引用变量指向的对象内容可变吗?

  在上面提到被final修饰的引用变量一旦初始化赋值之后就不能再指向其他的对象,那么该引用变量指向的对象的内容可变吗?看下面这个例子:

1

2

3

4

5

6

7

8

9

10

11

public class Test {

    public static void main(String[] args)  {

        final MyClass myClass = new MyClass();

        System.out.println(++myClass.i);

 

    }

}

 

class MyClass {

    public int i = 0;

}

  这段代码可以顺利编译通过并且有输出结果,输出结果为1。这说明引用变量被final修饰之后,虽然不能再指向其他对象,但是它指向的对象的内容是可变的。

3.final和static

  很多时候会容易把static和final关键字混淆,static作用于成员变量用来表示只保存一份副本,而final的作用是用来保证变量不可变。看下面这个例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

public class Test {

    public static void main(String[] args)  {

        MyClass myClass1 = new MyClass();

        MyClass myClass2 = new MyClass();

        System.out.println(myClass1.i);

        System.out.println(myClass1.j);

        System.out.println(myClass2.i);

        System.out.println(myClass2.j);

 

    }

}

 

class MyClass {

    public final double i = Math.random();

    public static double j = Math.random();

}

  运行这段代码就会发现,每次打印的两个j值都是一样的,而i的值却是不同的。从这里就可以知道final和static变量的区别了。

4.匿名内部类中使用的外部局部变量为什么只能是final变量?

  这个问题请参见上一篇博文中《Java内部类详解》中的解释,在此处不再赘述。

5.关于final参数的问题

使用final修饰方法参数的目的是防止修改这个参数的值,同时也是一种声明和约定,强调这个参数是不可变的

参考:https://www.cnblogs.com/dolphin0520/p/3736238.html

 

 

个人总结:

关于参数传递:

基本类型的传递为值传递,所有传递的基本类型为原始值得copy,所做的操作也仅限于所传递的操作。

对象的传递为引用传递,所传递的为原始对象存储值的地址,作为引用的传递,对于对象引用的修改实为对指向的原始值的修改,所以会影响到原始对象(因为指向同一块内存地址)。

注:String除外,虽为对象,实为基本类型,且String的重新赋值相当于新的对象new出来,已经不是原有的对象引用。

所以对于不希望发生更改的对象参数传递,一般可以加上final字段,用于约定和声明该变量。

关于final和static

这里主要是针对final,对于值类型的定义,必须初始化后,且无法被重新赋值。对于引用类型代表其引用的地址不可变更,但是其引用地址所指向的值(堆内存)并不受影响。

final表明变量本身不可变,而static表明变量本身作为副本一直存在。

对于类成员变量(final修饰的),在对象实例后,成员变量初始化,所以要么定义时初始化,要么构造方法中初始化。

对于局部变量(用于方法内部的),可以在声明后实例,但仅可初始化一次。

转载于:https://my.oschina.net/slightScenery/blog/1919792

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值