学习JAVA视频时 发现的 值传递问题,引发的思考

学习IO流视频时,发现老师讲解的在mian函数里调用递归函数,但是递归函数没有返回值,但是他的传值居然有改变。

具体代码如下:(main方法 getFiles(dir,filter,list)  递归调用处)

package cn....test;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/*
 * 
 * 获取指定目录下,指定扩展名的文件(包含子目录中的)
 * 这些文件的绝对路径写入到一个文本文件中。
 * 
 * 简单说,就是建立一个指定扩展名的文件的列表。 
 * 
 * 思路:
 * 1,必须进行深度遍历。
 * 2,要在遍历的过程中进行过滤。将符合条件的内容都存储到容器中。
 * 3,对容器中的内容进行遍历并将绝对路径写入到文件中。 
 * 
 *
 */
public class Test {

	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(String[] args) throws IOException {
			
		File dir = new File("e:\\java0331");
		
		FilenameFilter filter = new FilenameFilter(){
			@Override
			public boolean accept(File dir, String name) {
				
				return name.endsWith(".java");
			}			
		};
		
		List<File> list = new ArrayList<File>();
		
		getFiles(dir,filter,list);
		
		File destFile = new File(dir,"javalist.txt");
		
		write2File(list,destFile);
		
	}
	/**
	 * 对指定目录中的内容进行深度遍历,并按照指定过滤器,进行过滤,
	 * 将过滤后的内容存储到指定容器List中。
	 * @param dir
	 * @param filter
	 * @param list
	 */
	public static void getFiles(File dir,FilenameFilter filter,List<File> list){
		
		File[] files = dir.listFiles();
		
		for(File file : files){
			if(file.isDirectory()){
				//递归啦!
				getFiles(file,filter,list);
			}else{
				//对遍历到的文件进行过滤器的过滤。将符合条件File对象,存储到List集合中。 
				if(filter.accept(dir, file.getName())){
					list.add(file);
				}
			}
		}
		
	}
	
	public static void write2File(List<File> list,File destFile)throws IOException{
		
		BufferedWriter bufw = null;
		try {
			bufw = new BufferedWriter(new FileWriter(destFile));
			for(File file : list){
				bufw.write(file.getAbsolutePath());
				bufw.newLine();
				bufw.flush();
			}
			
			
		} /*catch(IOException e){
			
			throw new RuntimeException("写入失败");
		}*/finally{
			if(bufw!=null)
				try {
					bufw.close();
				} catch (IOException e) {
					
					throw new RuntimeException("关闭失败");
				}
		}
	}

}

让我想起了VB函数里重要的 ByRef 和 ByVal。对于用过VB,或者C#,或者delphi的人来说,对于这个参数传值并不陌生,但是在JAVA里,好像没有显式的传参类型让人设置。

ByRef 是对地址或者说是指针的直接引用,ByVal 是针对值的复制引用。用法其实就是


' 久未写VB,大致语法这样吧

Sub from_load(.....)

  String x = "x"
  String y = "y"
  change(x,y)
  Msgbox(x + "," + y)
  // 弹窗结果是 x,b 因为ByVal是复制值,ByRef是指向内存地址。

End Sub

' 注意参数的显示传值类型
Sub change(ByVal String X , ByRef string y)
    x = "a"
    y = "b"
End Sub 

现在因为JAVA无这种方式,起码我未找到关于ByRef 和 ByVal的方法。

我找了几篇博客,大致解释如下,传参的结果是否发生变化根据数据类型来区分。

值传递,分为引用数据类型值传递和基本数据类型值传递,
前者会对传值进行改变,后者不会,
前者只有接口,自定义类,数组,后者为8种基本类型和其延伸。
如果是基本数据类型,运行时参数只会复制传递进来的值,类似VB里的ByVal(By value)。
如果是引用数据类型,运行时参数会直接指向传参的内存地址,类似VB里的ByRef。
这样一来复制的值怎么操作都不会对原参数产生影响,而直接指向内存地址的修改行为,自然会对同样指向内存地址的原参数造成影响。

复制博客的图片如下:

è¿éåå¾çæè¿°

图片引用自:深入理解--Java按值传递和按引用传递

 

四类 八种 基本数据类型

1. 整型 byte(1字节) short (2个字节) int(4个字节) long (8个字节)

2.浮点型 float(4个字节) double(8个字节)

3.逻辑性 boolean(八分之一个字节)

4.字符型 char(2个字节,一个字符能存储下一个中文汉字)

整理起来就是 byte short int long float double char boolean,包括他的包装类型

String类型:String类型不是基本数据类型,而是引用数据类型,但是因为有特殊的用法,常量池的存在,所以对于String类型来说,和基本类型类似。它每一次赋新值,都会在常量池中寻找是否已存在这个新值,如果存在则直接将String变量指向常量池已存在的地址,如果不存在则会在常量池创建这个地址,再指向新创建的地址。当然,无用的地址会被自动清理掉。但是对于StringBuffer和StringBuilding来说,内置了Append等等直接对自身进行修改的方法,不会创建新的常量池,直接对这个值进行改变。所以demo2,会修改成功,是使用了内置的方法,直接对内存地址对应的值进行了改变,而demo1中包括上面的对于String的各种修改,都只是用=来创建一个新的常量池地址值并指过去,不会对旧地址造成任何影响。

关于String类型与常量池可参考大神博客链接:从底层彻底搞懂String,StringBuilder,StringBuffer的实现

两种类型的区别原因

值类型/引用类型,是用于区分两种内存分配方式,值类型在调用栈上分配,引用类型在堆上分配。
理论来自于:Java中的值传递,具体的可参考此大神博客,分析的更详细。

如下是我测试结果:

public class Main {
    public static void main(String[] args) {
        String s = "asd";
        changeString(s);
        System.out.println("s:" + s);

        int i = 1;
        changeInt(i);
        System.out.println("i:" + i);

        Integer integer = 55;
        changeInteger(integer);
        System.out.println("integer:" + integer);

        //demo1
        String str= new String("hello");
        char []chs={'w','o','r','l','d'};
        change(str, chs);
        System.out.println(str+" "+new String(chs));

        //-------------------------------------------------
        //demo2
        StringBuffer sb=new StringBuffer("hello");
        change(sb);
        System.out.println(sb);

        //对abc通过调用方法进行修改
        StringBuilder a=new StringBuilder("A");
        StringBuilder b=new StringBuilder("B");
        StringBuilder c=new StringBuilder("C");
        method3(a,b,c);
        System.out.println(a);
        System.out.println(b);
        System.out.println(c);
    }

    private static void changeString(String s) {
        s = "789";
    }

    private static void changeInt(int i) {
        i = 5;
    }

    private static void changeInteger(Integer integer) {
        integer = 99;
    }

    public static void change(StringBuffer sb)
    {
        sb.append(" world");
      sb.deleteCharAt(0);
    }

    public static void change(String str,char[]chs)
    {
        str.replace('h', 'H');
        chs[0]='W';
    }

    public static void method3(StringBuilder a,StringBuilder b,StringBuilder c){
        // 将a原值添加了A(a=AA;b=B;c=C)
        a.append("A");
        // 将b换了个新对象,原b对象不动(a=AA;b=B;新的b=BB;c=C)
        b=new StringBuilder("BB");
        // 将新对象添加了B(a=AA;b=B;新的b=BBB;c=C)
        b.append("B");
        // 将C重新换了指定的新对象,原c对象不动(a=AA;b=B;新的b=BBB;c=AA)
        c=a;
        // c现在指定的是a所以是将a添加了C(a=AAC;b=B;新的b=BBB;c=AAC)
        c.append("C");
    }
}

运行结果如下所示:


s:asd
i:1
integer:55
hello World
ello world
AAC
B
C

进程已结束,退出代码0

到此地步算是初步解开了我的疑问,算是基本了解了一点浅薄的参数传递的知识,虽然对于以前用到的各种语言来说,不是很方便。

如果有错误,请回复指正,望大神指点。

补充:到后面测试过程里,才发现,好像只有 静态的方法才能起到效果。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要调用一个类的公有数据并传递到另一个类的函数,需要以下几个步骤。 首先,确保要访问的类的公有数据已经在该类定义。公有数据通常在类的定义使用public访问修饰符进行声明,这样其他类就可以访问和使用这些数据。 接下来,在要调用该类的公有数据的地方,实例化该类的对象。通过创建该类的对象,我们就可以访问该类的公有数据和函数。 然后,要将这个类的公有数据传递到另一个类的函数,需要先创建另一个类的对象。通过创建另一个类的对象,我们就可以调用该类的函数。 在调用另一个类的函数,将要传递的类的公有数据作为参数传递给该函数。确保参数的类型与函数的参数类型匹配。 最后,调用另一个类的函数并传递类的公有数据作为参数。在函数内部,可以使用参数来处理和操作这个数据。 总结起来,要调用一个类的公有数据并传递到另一个类的函数,需要实例化这两个类的对象,将公有数据作为参数传递给函数,并在函数内部处理和操作这个数据。这样就可以实现将一个类的公有数据传递到另一个类的函数。 ### 回答2: 要将一个类的公有数据传递到另一个类的函数,我们可以使用以下步骤: 1. 首先,在要传递数据的类定义一个公有的数据成员。假设这个类的名称是ClassA,而我们要传递的公有数据成员的名称是data。 2. 接下来,在要接收数据的类定义一个函数。假设这个类的名称是ClassB,而我们要定义的函数的名称是getData,并且这个函数需要接收一个参数,用于接收从ClassA传递过来的数据。函数的定义可以如下: def getData(self, data): # 在这里对接收到的数据进行操作 pass 3. 在ClassB类的对象,通过对象来调用getData函数,并将ClassA类的对象的data成员作为参数传递给getData函数。假设ClassA对象的名称是objectA,而ClassB对象的名称是objectB,调用的代码可以如下: objectB.getData(objectA.data) 通过以上步骤,我们可以将ClassA类的公有数据传递到ClassB类的函数进行操作。在ClassB类的getData函数,我们可以对传递过来的数据进行任何所需的操作,例如赋值给ClassB类的成员变量、进行计算或输出结果等。这样,数据的传递就完成了。 ### 回答3: 要将一个类的公有数据传递到另一个类的函数,可以按照以下步骤操作: 1. 首先,在类A创建一个公有数据成员,并为其赋值。例如,我们可以在类A创建一个公有整型数据成员num,并将其赋值为10。 2. 接下来,在类B定义一个接收类A类型对象作为参数的公有函数。在函数定义,可以使用类A的对象作为参数,类型为类A的引用或常量引用。例如,我们可以在类B定义一个公有函数foo,它的参数类型为类A的引用,命名为obj。 3. 在类B的函数foo,可以使用obj访问到类A的公有数据成员num,并进行相应的操作。例如,可以将num的值加倍,并打印结果。 4. 最后,在类B的调用函数创建一个类A的对象,并将其作为参数传递给类B的函数foo。例如,我们可以在主函数创建一个类A的对象a,然后调用类B的函数foo,并将a作为参数传递进去。 综上所述,以上步骤描述了如何在类A和类B之间调用一个类的公有数据传递到另一个类的函数。通过将类A的对象作为参数传递给类B的函数,可以在类B访问到类A的公有数据成员并进行相应的操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值