学习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

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值