`
zhong_qm
  • 浏览: 9476 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

关于java中值的传递与改变

阅读更多

关于java中值的传递与改变

 

Java中变量有其作用范围,属性(全局变量)在类中都是其作用范围,方法中定义的变量作用范围是方法,方法中的循环中定义的作用范围就只有在此循环中,若其他的方法或类要对这个变量作出改变或使用,则需要用到传值。

Java中传值一般要用到三个方法:建立一个get的方法,通过类名直接访问属性,构造方法传值。

一:建立一个get的方法

  Get方法中可以从其他类中或本类中得到返回的值。当然,在本类中要得到一个值没有必要特别建立一个get方法,一般时候只要将这个值设置为属性即可,但注意,静态的方法中只能直接得到静态的变量。看下面的例子。

    第1个类:此类是程序的入口

public class GetMethodTran {

//建立一个一般属性值

private int j=15;

//建立一个静态属性值

private static int k=20;

public static void main(String[] args) {

Tran t=new Tran();

//从其他类中得到一个整形值

int i=t.getInOtherClass();

System.out.println("从其他类中得到了值:i="+i);

 

GetMethodTran gmt=new GetMethodTran();

//从本类中得到一个整形值

i=gmt.getInSameClass();

System.out.println("从本类中得到了值:i="+i);

 

//从本类中得到静态属性值

int a=k;

System.out.println("从本类中得到的值:a="+a);

 

//试图得到一个非静态的属性,会报错,因为程序入口main方法是静态的

//int b=j;

 

}

/**

 * 建立的一个get方法

 * @return 一个整形值

 */

public int getInSameClass(){

return 10;

}

}

 

2个类,只有一个get方法,用于测试

public class Tran {

/**

 * 建立的一个get方法

 * @return 一个整形值

 */

public int getInOtherClass(){

return 5;

}

}

 

 

二:通过类名直接访问属性

此方法看似最为简便,那么在其他类中可以访问其他类中什么修饰符修饰下的属性呢?

下面的例子:

1个类,用于得到别的类中的属性

public class GetAttribute {

 

public static void main(String[] args) {

MyAttributes a=new MyAttributes();

//得到一个默认的属性

int i=a.def;

//得到一个受保护的属性

i=a.pro;

//得到一个公有的属性

i=a.pub;

//得到一个静态默认的属性

i=a.sta;

//试图得到一个私有的属性,报错

//i=a.pri;

 

}

}

 

2个类,设置属性,用于测试

public class MyAttributes {

//公有属性

public int pub=0;

//受保护属性

protected int pro=5;

//默认属性,一般认为是default或friendly

 int def=10;

//私有属性

 private int pri=15;

//静态默认属性

 static int sta=20;

 

}

 

在这两个类,我把它们放置在同一个工程的同一个包中,所以才除了私有的属性不能被第一个类访问,当这两个类在不同的包中时会出现不同的结果,原因是java的保护机制,属性的修饰符的原因,修饰符控制了属性的访问范围。此时,了解修饰符很重要。以下是修饰符的简单介绍:

Public :公有,同包或不同的包中的类都可访问,

Protected:    本包中所有类以及包外的子类可访问,

Default/friendly:本包内的所有类可以访问,其他包中类包括子类无法访问,

Private: 只有在类的内部能访问,

当然了,不同的工程中,不管是什么修饰符下的属性也是不可访问的。

 

三:构造方法传值

此方法最为常用,它有固定的格式。看下面的例子。

第一个类,用于传值,开始程序

public class MyConstruction {

 

public static void main(String[] args) {

int i=5;

//实例对象,同时完成了传值

MyConstruction2  mc=new MyConstruction2(i);

 

}

 

}

第二个类,得到第一个类的值,并打印出来

public class MyConstruction2 {

private int i;

/**

 * 有参的用于传值的构造方法

 * @param i 传来的值

 */

 

public MyConstruction2(int i){

this.i=i;

System.out.println("得到传来的值:i="+i);

}

 

}

当然,构造方法中可以一次传递多个值,这个格式如下:

修饰符  类名(第一个值的类型  第一个值的变量名,第二个值的类型  第二个值的变量名,·····,第N个值的类型  第N个值的变量名){}

在有此类构造方法的类中,一般也设置了1~N个属性,在构造方法中的大括号中有N个“this.变量名=变量名;”,如何理解这个表达式呢。“This.变量名”是指本类中的属性,“=变量名”中的变量名是指构造方法格式中的1~N个变量名的某个,是调用了此个构造方法实例对象时传来的值。格式中“this.变量名=变量名;”这两个变量名不一定相同。

注意,当建立了有参构造方法,原有的无参构造方法将会隐藏,要用无参构造方法的话要在类中重新建立一个无参构造方法。

 

我们知道,传值的目的是对传来的值作出一些改变,传值方法的传的值也不仅是八种基本的数据类型,还包括了对象。有的时候,我们想传过去的值的改变不影响原来的值,有时却要传过去的值的改变要改变原来的值,来使原来的值产生一些必要的改变,出现新的样式。这就要理解和区别它们。这里主要讨论一下我对八种基本数据类型,数组,对象,String的一些传值改变的理解。

 

一:关于八种基本数据类型

基本数据类型一般都是值的传递,以int作为例子。

第一个类,程序入口,利用构造方法传值

public class BaseType {

 

public static void main(String[] args) {

//传过去的值是0;

int i=0;

BaseType2  bt=new BaseType2(i);

System.out.println("经过了BaseType2构造方法后的i值:"+i);

change(i);

System.out.println("执行了change(int i)方法后原来的i值:"+i);

}

/**

 * 改变i值的方法

 * @param i 传入的数据

 */

public static void change(int i){

i=10;

System.out.println("执行了change(int i)方法的后来的i值:"+i);

}

 

}

第二个类,对值进行改变,

public class BaseType2 {

private int i;

public BaseType2(int i){

this.i=i;

System.out.println("BaseType传来的i值:"+i+"\n执行了this.i=i;后的BaseType2的i值:"+this.i);

//改变BaseType2的i值

this.i=5;

System.out.println("执行了this.i=5;的BaseType2的i值:"+this.i);

}

 

}

 

 

运行后得到的结果:



 

 

由此个例子可看出基本数据类型是通过值进行传递的,当传值后,将此个值赋给别的值AA的改变不会引起原来的值的改变。

 

二:对象

关于对象的传值,毫无疑问,是地址的传递,这个关系如下:

 



 

我们可以打印这个对象,得到它的内存地址,如此,我们就可以判断出在操作对象传值后出现的改变是否改变了原值。只要地址不变,对这个对象作出的任何操作,都会影响原来的对象。看下面的例子。

第一个类:程序的入口,

public class ObjectTest {

 

public static void main(String[] args) {

ObjectTest2 ot1=new ObjectTest2(20);

System.out.println("未调用任何方法进的age="+ot1.age+"  对象内存地址:"+ot1);

ot1.setNewAge2(ot1);

System.out.println("调用了setNewAge2方法后的age="+ot1.age+"   对象的内存地址:"+ot1);

ot1.setNewAge(ot1);

System.out.println("调用了setNewAge方法后的age="+ot1.age+"   对象的内存地址:"+ot1);

}

 

}

 

第二个类:有两种方法,对传来的对象进行操作,

public class ObjectTest2 {

//设置一个年龄属性

public int age;

public ObjectTest2(int i){

this.age=i;

}

/**

 * 设置一个新的年龄

 * @param i 新的年龄

 */

private ObjectTest2 ot;

public void setNewAge(ObjectTest2 ot){

this.ot=ot;

this.ot.age=25;

System.out.println("调用了setNewAge方法时的age="+this.ot.age+"   对象的内存地址:"+this.ot);

}

/**

 * 设置一个新的年龄

 * @param i 新的年龄

 */

public void setNewAge2(ObjectTest2 ot){

  ot=new ObjectTest2(25);

System.out.println("调用了setNewAge2方法时的age="+ot.age+"   对象的内存地址:"+ot);

}

}

 

运行后的结果是:



 

 

可以看出,调用setNewAge2方法时,原对象并未发生改变,且输出的对象内存地址不同,意味了setNewAge2方法操作的是不同的两个对象。而setNewAge方法中,使原来的对象发生了改变,setNewAge方法中输出的对象内存地址是原对象的内存地址。其实很容易理解,因为在setNewAge2方法中语句: ot=new ObjectTest2(25);使对象内存地址改变了,所以操作了不同的对象。这里我们称对象的内存地址为对象引用。对于setNewAge方法的对象引用理解是:它们指向了同一个内存地址



                                                           

 

 

 

 

 

setNewAge2方法的对象引用理解是:它们指向了不同的内存地址



 

三:数组

关于数组的问题,一般都会问数组是对象还是基本数据类型,因为数组中的元素都是存储八基本数据类型的。一般认为数组是对象,第一,数组的创建使用了new关键字,第二,打印数组的变量名时显示的是内存地址。第三,只是声明而没有创建时,若要使用,必需将其设置为null,而不是其他或它本身就有默认值,可能这是分配内存空间的需要吧。

传递数组时,传递的是对象引用,那么,如果我们要传递后的数组改变不影响原数组怎么办?是否要进行循环,将数组的每个元素分别进行赋值呢。其实不用。看下面的例子。

public class About_ArrayCopy {

 

/**

 * @param args

 */

public static void main(String[] args) {

//最后的一个逗号一向不要

int src[]=new int[]{1,2,3,4,5,};

int[] dest=new int[src.length];

//系统的方法,数组的复制

System.arraycopy(src, 0, dest, 0, src.length);

 

print(src);

print(dest);

System.out.println(src);

System.out.println(dest);

 

}

public static void print(int[] al){

for(int x:al){

System.out.print(x+" ");

}

System.out.println("\n------------------------");

}

 

}

 

得到的结果是:



 

可以看到,数组进行了复制,对象的引用不同。

但有时我们要复制的不是一维数组,而是多维数组怎么做?这个你们可以探究一下。

 

四:关于String

关于String,我们一定用过很多,这个一般会认为这也是一个基本的数据类型,很简单,因为我们打印变量名时,出现的是String的内容,而不是它的内存地址,而且,它的使用也是和基本数据类型一样,传值时的改变也一样。如下面的例子。

 

public class AboutString {

 

public static void main(String[] args) {

String str1="abc";

System.out.println("调用changeStr前的str:"+str1);

changeStr(str1);

System.out.println("调用changeStr后的str:"+str1);

}

/**

 * 改变String的方法 

 * @param str  String的传入

 */

public static void changeStr(String str){

str=str+"def";

System.out.println("调用changeStr方法时的str:"+str);

}

 

}

 

得到的结果是:



 

传值的改变规律也如基本数据类型。

但是,其实String是一个类,尽管它与基本数据类型如此相似。如下面的例子。

public class StringTest {

 

public static void main(String[] args) {

//新建一个String对象

String s=new String("String is Class");

System.out.println(s);

}

}

但是八种基本数据类明显不能通过new关键字来创建,否则会报错,这个或许能证明String的确是一个类吧。

但是,为什么String表现得如此像基本数据类型呢。因为String是一个非可变类,String对象一旦创建,就不能进行修改,String对象修改后,返回的都是它的新的对象。对此,明显对想用这个String的对象在不同的方法或类中进行修改造成困扰。在这介绍两个String子类用于解决这个问题。StringBuilderStringBuffer

如下面的例子。

public class StringTest {

 

public static void main(String[] args) {

//创建StringBuilder对象

StringBuilder  stbui=new StringBuilder("StringBuilder");

System.out.println("StringBuilder对象调用方法前:"+stbui);

changeStringBuilder(stbui);

System.out.println("StringBuilder对象调用方法后:"+stbui);

 

//创建StringBuffer对象

StringBuffer   stbuf=new StringBuffer("StringBuffer");

System.out.println("StringBuffer对象调用方法前:"+stbuf);

changeStringBuffer(stbuf);

System.out.println("StringBuffer对象调用方法后:"+stbuf);

}

/**

 * 改变字符串的方法 1

 * @param str 传入的字符串

 */

public static void changeStringBuilder(StringBuilder str){

//改变字符串

str=str.append(" will change!");

System.out.println("StringBuilder对象调用方法时:"+str);

}

/**

 * 改变字符串的方法 2

 * @param str 传入的字符串

 */

public static void changeStringBuffer(StringBuffer  str){

//改变字符串

str=str.append(" will change too!!");

System.out.println("StringBuffer对象调用方法中:"+str);

 

}

}

 

 



 

<!--EndFragment-->

  • 大小: 34.6 KB
  • 大小: 2.2 KB
  • 大小: 44.2 KB
  • 大小: 6.2 KB
  • 大小: 6.7 KB
  • 大小: 17.9 KB
  • 大小: 20.9 KB
  • 大小: 14.6 KB
0
1
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics