java不是值传递吗?为什么会出现这种情况

如题,按说java是值传递的,可下面的代码能正确运行,getClassSet方法中有一个局部的set变量,在doAddClass方法里对其进行了add但没有返回,但最后却能从getClassSet方法返回的set里获取到元素,这是为什么?代码略长,劳烦大佬了

/**
     * 获取指定包名下的所有类文件,可读取class或jar
     * Enumeration古代迭代器
     */
    public static Set<Class<?>> getClassSet(String packageName) {
        Set<Class<?>> classes = new HashSet<>();
        try {
            //获取文件系统下的资源文件,url路径类似 file:/C:/Users/Kaibo/OneDrive/workplaceForIDEA/SmartFramework/target/classes/com/smar4j/framework/util
            Enumeration<URL> urls = getClassLoader().getResources(packageName.replace(".", "/"));
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                if (null != url) {
                    String protocol = url.getProtocol();
                    //判断url协议,file还是jar
                    if ("file".equals(protocol)) {
                        //替换url中的空格
                        String packagePath = url.getPath().replaceAll("%20", "");
                        addClass(classes, packagePath, packageName);
                    } else if ("jar".equals(protocol)) {
                        //操作jar文件
                        JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
                        if (null != jarURLConnection) {
                            JarFile jarFile = jarURLConnection.getJarFile();
                            if (null != jarFile) {
                                Enumeration<JarEntry> entries = jarFile.entries();
                                while (entries.hasMoreElements()) {
                                    JarEntry jarEntry = entries.nextElement();
                                    String name = jarEntry.getName();
                                    if (name.endsWith(".class")) {
                                        String className = name.substring(0, name.lastIndexOf(".")).replaceAll("/", ".");
                                        doAddClass(classes, className);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return classes;
    }

    private static void addClass(Set<Class<?>> classes, String packagePath, String packageName) {
        File[] files = new File(packagePath).listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
            }
        });
        for (File file : files) {
            String fileName = file.getName();
            //如果不是目录
            if (file.isFile()) {
                String className = fileName.substring(0, fileName.lastIndexOf("."));
                if (StringUtil.isNotEmpty(packageName)) {
                    className = packageName + "." + className;
                    doAddClass(classes, className);
                } else {
                    String subPackagePath = fileName;
                    if (StringUtil.isNotEmpty(packagePath)) {
                        subPackagePath = packagePath + "/" + subPackagePath;
                    }
                    String subPackageName = fileName;
                    if (StringUtil.isNotEmpty(packageName)) {
                        subPackageName = packageName + "." + subPackageName;
                    }
                    addClass(classes, subPackagePath, subPackageName);
                }
            }
        }
    }
    private static void doAddClass(Set<Class<?>> classes, String className) {
        Class<?> cls = loadClass(className, false);
        classes.add(cls);
    }
阅读 3.1k
5 个回答

对于对象来说,传递的只是引用,不是以副本形式(值)传递的,8 种基本类型(非对应的类类型)才是传值。

值传递没错, 但相对于简单类型是这样, 对于类来说,值是类的内存地址, 也就是引用. 如果你改了引用对应对象的内部值, 也没有谁能拦得了你. 网上资料太多, 不重复了.

打个比方说, 集合类型就是书包, 你背上书包进了楼, 相当于是调用了进楼方法, 执行完成了,你出了楼, 你没有变, 书包也没有变. 但你书包里的东西变没变就不好说了.

这种也叫引用传递,传递的是对象的内存地址。也就是说这两个方法里的变量指向的是同一块内存地址。

classes.add(cls);对内存地址里的数据做了修改,也直接反应到getClassSet里的变量,因为它们是同一个对象

HashSet源码是基于HashMap实现的

public class HashSet<E>
    extends AbstractSet<E>
    implements Set<E>, Cloneable, java.io.Serializable
{
    static final long serialVersionUID = -5024744406713321676L;

    private transient HashMap<E,Object> map;
 Set<Class<?>> classes = new HashSet<>();
调用private static void doAddClass(Set<Class<?>> classes, String className)方法 

两个classes引用同时指向new HashSet<>()这个对象,在方法里面执行 classes.add(cls);
也就是调用了map的put方法,修改了这个共同指向对象里面的HashMap成员变量的内容,返回的也是这个对象,所以getClassSet方法返回的set里能获取到元素。

大佬们都把技术问题说了,不过我还是推荐你看一本书,鲍勃大叔的 《clean code》

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题