StackOverflow 周报 - 这些高关注的问题你是否都会

预测StackOverflow问题标签的机器学习实践 多标签分类是指在输入数据可能与多个标签相关联的情况下,对数据进行标签分配的过程。例如,在图像识别任务中,一张图片可能同时包含“猫”、“户外”和“日落”等标签。与传统的单标签分类相比,多标签分类能够提供更丰富的信息,这在许多实际应用中非常有用,如情感分析、图像标注、基因功能注释等。数据增强通常在数据集相对较小的情况下使用,尤其是图像、声音和文本数据处理中。它可以通过特定技术增加数据的多样性,帮助模型更好地捕捉数据中的关键信息,从而提模型的泛化能力。数据增强的常见方法包括:图像数据增强。 阅读详情

我从 Stack Overflow 上找的了一些高关注度且高赞的问题。这些问题可能平时我们遇不到,但既然是高关注的问题和高点赞的回答说明是被大家普遍认可的,如果我们提前学到了以后不管工作中还是面试中处理起来就会更得心应手。本篇文章是第一周的内容,一共 5 个题目。我每天都会在公众号发一篇,你如果觉得这个系列对你有价值,欢迎文末关注我的公众号。

DAY1.  复合运算符中的强制转换

今天讨论的问题是“符合运算符中的强制转换”。以 += 为例,我编写了如下代码,你可以先考虑下为什么会出现下面这种情况。

int i = 5;
long j = 10;

i += j; //正常
i = i+j; //报错,Incompatible types.

这个问题可以从 “Java 语言手册” 中找到答案,原文如下:

A compound assignment expression of the form E1 op= E2 
is equivalent to E1 = (T) ((E1) op (E2)), 
where T is the type of E1, except that E1 is evaluated only once.

翻译一下:形如 E1 op= E2 的复合赋值表达式等价于 E1 = (T)((E1) op (E2)), 其中,T 是 E1 的类型。所以,回到本例,i+j 的结果会强制转换成 int 再赋值给 i。

其实验证也比较容易,我们看下编译后的 .class 文件就知道做了什么处理。

从 .class 文件可以看出,有两处强制转换。第一处是 i+j 时,由于 j 是 long 类型,因此 i 进行类型提升,强转为 long, 这个过程我们比较熟悉。第二处是我们今天讨论的内容,i+j 的结果强转成了 int 类型。
这里面我们还可以在进一步思考,因为在这个例子中强转可能会导致计算结果溢出,那你可以想想为什么 Java 设计的时候不让它报错呢?
我的猜想是这样的,假设遇到这种情况报错,我们看看会有什么样的后果。比如在 byte 或者 short 类型中使用 += 运算符。

byte b = 1;
b += 1;

按照我们的假设,这里就会报错,因为 i+1 返回的 int 类型。然而实际应用场景中这种代码很常见,因此,假设成立的话,将会严重影响复合赋值运算符的应用范围,最终设计出来可能就是一个比较鸡肋的东西。所以,为了普适性只能把判断交给用户,让用户来保障使用复合赋值运算符不会发生溢出。我们平时应用时一定要注意这个潜在的风险。

原文地址

DAY2.  生成随机数你用对了吗

在 Java 中如何生成一个随机数?如果你的答案是 Random 类,那就有必要继续向下看了。Java 7 之前使用 Random 类生成随机数,Java 7 之后的标准做法是使用 ThreadLocalRandom 类,代码如下:

ThreadLocalRandom.current().nextInt();

既然 Java 7 要引入一个新的类取代之前的 Random 类,说明之前生成随机数的方式存在一定的问题,下面就结合源码简单介绍一下这两个类的区别。
Random 类是线程安全的,如果多线程同时使用一个 Random 实例生成随机数,那么就会共享同一个随机种子,从而存在并发问题导致性能下降,下面看看 next(int bits) 方法的源码:

protected int next(int bits) {
    long oldseed, nextseed;
    AtomicLong seed = this.seed;
    do {
        oldseed = seed.get();
        nextseed = (oldseed * multiplier + addend) & mask;
    } while (!seed.compareAndSet(oldseed, nextseed));
    return (int)(nextseed >>> (48 - bits));
}

看到代码并不复杂,其中,随机种子 seed 是 AtomicLong 类型的,并且使用 CAS 方式更新种子。
接下来再看看 ThreadLocalRandom 类,多线程调用 ThreadLocalRandom.current() 返回的是同一个 ThreadLocalRandom 实例,但它并不存在多线程同步的问题。看下它更新种子的代码:

final long nextSeed() {
    Thread t; long r; // read and update per-thread seed
    UNSAFE.putLong(t = Thread.currentThread(), SEED,
                   r = UNSAFE.getLong(t, SEED) + GAMMA);
    return r;
}

可以看到,这里面不存在线程同步的代码。猜测代码中使用了Thread.currentThread() 达到了  ThreadLocal 的目的,因此不存在线程安全的问题。使用 ThreadLocalRandom 还有个好处是不需要自己 new 对象,使用起来更方便。如果你的项目是 Java 7+ 并且仍在使用 Random 生成随机数,那么建议你切换成 ThreadLocalRandom。由于它继承了 Random 类,因此不会对你现有的代码造成很大的影响。

原文地址

DAY3.  InputStream转String有多少种方法

Java 中如果要将 InputStream 转成 String,你能想到多少种方法?

String str = "测试";
InputStream inputStream = new ByteArrayInputStream(str.getBytes());

1. 使用 ByteArrayOutputStream 循环读取

/** 1. 使用 ByteArrayOutputStream 循环读取 */
BufferedInputStream bis = new BufferedInputStream(inputStream);
ByteArrayOutputStream buf = new ByteArrayOutputStream();
int tmpRes = bis.read();
while(tmpRes != -1) {
    buf.write((byte) tmpRes);
    tmpRes = bis.read();
}
System.out.println(buf.toString());

2. 使用 InputStreamReader 批量读取

/** 2. 使用 InputStreamReader 批量读取 */
final char[] buffer = new char[1024];
final StringBuilder out = new StringBuilder();
Reader in = new InputStreamReader(inputStream);
for (; ; ) {
    int rsz = in.read(buffer, 0, buffer.length);
    if (rsz < 0) {
        break;
    }
    out.append(buffer, 0, rsz);
}
System.out.println(out.toString());

3. 使用 JDK Scanner

/** 3. 使用 JDK Scanner */
Scanner s = new Scanner(inputStream).useDelimiter("\\A");
String result = s.hasNext() ? s.next() : "";
System.out.println(result);

4. 使用 Java 8 Stream API

/** 4. 使用 Java 8 Stream API */
result = new BufferedReader(new InputStreamReader(inputStream))
        .lines().collect(Collectors.joining("\n"));
System.out.println(result);

5. 使用 IOUtils StringWriter

/** 5. 使用 IOUtils StringWriter */
StringWriter stringWriter = new StringWriter();
IOUtils.copy(inputStream, stringWriter);
System.out.println(stringWriter.toString());

6. 使用 IOUtils.toString 一步到位

/** 6. 使用 IOUtils.toString 一步到位 */
System.out.println(IOUtils.toString(inputStream));

这里我们用了 6 种方式实现,实际还会有更多的方法。简单总结一下这几个方法。
第一种和第二种方法使用原始的循环读取,代码量比较大。第三和第四种方法使用了 JDK 封装好的 API 可以明显减少代码量, 同时 Stream API 可以让我们将代码写成一行,更方便书写。最后使用 IOUtils 工具类(commons-io 库), 听名字就知道是专门做 IO 用的,它也提供了两种方式,第五种框架提供了更加开放,灵活的方式叫做 copy 方法,也就是说除了 copy 到 String 还可以 copy 到其他地方。第六种就完全的定制化,就是专门用来转 String 的,当然定制化的结果就是不灵活,但对于单纯转 String 这个需求来说却是最方便、最省事的。其实我们平时编程也是一样,对于一个产品需求有时候不需要暴露太多的开放性的选择,针对需求提供一个简单粗暴的实现方式也许是最佳选择。
最后补充一句,我们平时可以多关注框架,用到的时候直接拿过来省时省力,减少代码量。当然有兴趣的话我们也可以深入学习框架内部的设计和实现。

原文地址

DAY4.  面试官:写个内存泄漏的例子

我们都是知道 Java 自带垃圾回收机制,内存泄漏这事好像跟 Java 程序员关系不大。所以,写 Java 程序一般会比 C/C++ 程序轻松一些。记得前领导写 C++ 代码时说过一句话,“写 C++ 程序一定会漏的,只不过是能不能被发现而已”。所以看来 C/C++ 程序员还是比较苦逼的,虽然他们经常鄙视 Java 程序员,哈哈~~。
尽管 Java 程序出现出现内存泄漏的可能性较少,但不代表不会出现。如果你哪天去面试,面试官让你用 Java 写一个内存泄漏的例子,你有思路吗?下面我就举一个内存泄漏的例子。

public final class ClassLoaderLeakExample {
    static volatile boolean running = true;
    /**
     * 1. main 函数,逻辑比较简单只是创建一个 LongRunningThread 线程,并接受停止的指令
     */
    public static void main(String[] args) throws Exception {
        Thread thread = new LongRunningThread();
        try {
            thread.start();
            System.out.println("Running, press any key to stop.");
            System.in.read();
        } finally {
            running = false;
            thread.join();
        }
    }

    /**
     * 2. 定义 LongRunningThread 线程,该线程做的事情比较简单,每隔 100ms 调用 loadAndDiscard 方法
     */
    static final class LongRunningThread extends Thread {
        @Override public void run() {
            while(running) {
                try {
                    loadAndDiscard();
                } catch (Throwable ex) {
                    ex.printStackTrace();
                }
                try {
                    Thread.sleep(100);
                } catch (InterruptedException ex) {
                    System.out.println("Caught InterruptedException, shutting down.");
                    running = false;
                }
            }
        }
    }

    /**
     * 3. 定义一个 class loader - ChildOnlyClassLoader,它在我们的例子中至关重要。
     * ChildOnlyClassLoader 专门用来装载 LoadedInChildClassLoader 类,
     * 逻辑比较简单,读取 LoadedInChildClassLoader 类的 .class 文件,返回类对象。
     */
    static final class ChildOnlyClassLoader extends ClassLoader {
        ChildOnlyClassLoader() {
            super(ClassLoaderLeakExample.class.getClassLoader());
        }

        @Override protected Class<?> loadClass(String name, boolean resolve)
                throws ClassNotFoundException {
            if (!LoadedInChildClassLoader.class.getName().equals(name)) {
                return super.loadClass(name, resolve);
            }
            try {
                Path path = Paths.get(LoadedInChildClassLoader.class.getName()
                        + ".class");
                byte[] classBytes = Files.readAllBytes(path);
                Class<?> c = defineClass(name, classBytes, 0, classBytes.length);
                if (resolve) {
                    resolveClass(c);
                }
                return c;
            } catch (IOException ex) {
                throw new ClassNotFoundException("Could not load " + name, ex);
            }
        }
    }

    /**
     * 4. 编写 loadAndDiscard 方法的代码,也就是在 LongRunningThread 线程中被调用的方法。
     * 该方法创建 ChildOnlyClassLoader 对象,用来装载 LoadedInChildClassLoader 类,将结果赋值给 childClass 变量,
     * childClass 调用 newInstance 方法来创建 LoadedInChildClassLoader 对象。
     * 每次调用 loadAndDiscard 方法,都会加载一次 LoadedInChildClassLoader 类并创建其对象。
     */
    static void loadAndDiscard() throws Exception {
        ClassLoader childClassLoader = new ChildOnlyClassLoader();
        Class<?> childClass = Class.forName(
                LoadedInChildClassLoader.class.getName(), true, childClassLoader);
        childClass.newInstance();
    }

    /**
     * 5. 定义 LoadedInChildClassLoader  类
     * 该类中定义了一个 moreBytesToLeak 字节数组,初始大小比较大是为了尽快模拟出内存泄漏的结果。
     * 在类的构造方法调用 threadLocal 的 set 方法存储对象本身的引用。
     */
    public static final class LoadedInChildClassLoader {
        static final byte[] moreBytesToLeak = new byte[1024 * 1024 * 10];

        private static final ThreadLocal<LoadedInChildClassLoader> threadLocal
                = new ThreadLocal<>();

        public LoadedInChildClassLoader() {
            threadLocal.set(this);
        }
    }
}

这是完整的例子, 可以按照注释中的序号的顺序阅读代码。最后运行代码,在 ClassLoaderLeakExample 类所在的目录下执行以下命令

javac ClassLoaderLeakExample.java
java -cp . ClassLoaderLeakExample

运行后会打印 "Running, press any key to stop." 等一分钟左右就会报内存不足的错误 "java.lang.OutOfMemoryError: Java heap space" 。
简单梳理一下逻辑,loadAndDiscard 方法会不断地被调用,每次被调用在该方法中都会加载一次 LoadedInChildClassLoader 类,每加载一次类就会创建一个新的threadLocal 和 moreBytesToLeak 属性。虽然创建的 LoadedInChildClassLoader 对象是局部变量,但退出 loadAndDiscard  方法后该对象仍然不会被回收,因为 threadLocal 保存了该对象的引用,对象保存了对类的引用,而类保存了对类加载器的引用,类加载器反过来保存对它已加载的类的引用。因此虽然退出 loadAndDiscard 方法,该对象对我们不可见了,但是它永远不会被回收。随着每次加载的类越来越多,创建的 moreBytesToLeak 越来越多并且内存得不到清理,会导致 OutOfMemory 错误。

为了对比你可以去掉自定义类加载器这个参数,loadAndDiscard 方法中的代码修改如下:

Class<?> childClass = Class.forName(
                LoadedInChildClassLoader.class.getName(), true, childClassLoader);
//改为:
Class<?> childClass = Class.forName(
                LoadedInChildClassLoader.class.getName());

再运行就不会出现 OOM 的错误。修改之后,无论 loadAndDiscard 方法被调用多少次都只会加载一次 LoadedInChildClassLoader 类,也就是说只有一个 threadLocal 和 moreBytesToLeak 属性。当再次创建 LoadedInChildClassLoader 对象时,threadLocal 会设置成当前的对象,之前 set 的对象就没有任何变量引用它,因此之前的对象会被回收。

原文地址

DAY5.  为什么密码用 char[] 存储而不用String

周五,放松一下。一起来看一个无需写代码的问题“为什么 Java 程序中用 char[] 保存密码而不用 String”。既然提到密码,我们用脚指头想想也知道肯定是出于安全性的考虑。具体的是为什么呢?我这里提供两点答案供你参考。

先说第一点,也是最重要的一点。String 存储的字符串是不可变的,也就是说用它存储密码后,这块内存是无法被人为改变的。并且只能等 GC 将其清除。如果有其他进程恶意将内存 dump 下来,就可能会造成密码泄露。
然而使用 char[] 存储密码对我们来说就是可控的,我们可以在任何时候将 char[] 的内容设置为空或者其他无意义的字符,从而保证密码不会长期驻留内存。相对使用 String 存储密码来说更加安全。

再说说第二点,假设我们在程序中无意地将密码打印到日志中了。如果使用 String 存储密码将会被明文输出,而使用 char[] 存储密码只会输出地址不会泄露密码。
这两点都是从安全性的角度出发。

第一点更侧重防止密码驻留内存不安全,第二点则侧重防止密码驻留外存。虽然第二点发生的概率比较低,但也给了我们一个新的视角。

以上便是 Stack Overflow 的第一周周报,希望对你有用,后续会继续更新,如果想看日更内容欢迎关注公众号。

欢迎关注公众号「渡码」,分享更多高质量内容

【搜索/提问必备】如何正确的在Stack Overflow提问 在我们写程序的时候,经常会遇到各色各样的问题,在国内,小伙伴们经常去知乎、CSDN、博客园、思否、安卓巴士等地方提问并获得答案。 这些地方汇集了很多优秀的、爱分享的国内资源。小编比较自豪的一件事情就是:当初学习dubbo期间,因为一个数据关闭错误一直找不到正确的解决方式,就顺手把自己解决问题的步骤写下,并附上参考资料中的方法,算是把那类问题做了一个增强版的总结,没想过几个月后,有位粉丝专门找上来感... 阅读详情

相关推荐

File IO(NIO.2):读、写并创建文件

简介 本页讨论读,写,创建和打开文件的细节。有各种各样的文件I / O方法可供选择。为了帮助理解API,下图以复杂性排列文件I / O方法 在图的最左侧是实用程序方法readAllBytes,readAllLines和write方法,为简单的常见情况设计。右边是用于迭代流或文本行的方法,例如newBufferedReader,newBufferedWriter,然后是newInputSt...

weixin_30412013的博客 399

StackOverflow 周报 -关注问题过过招(Java

本篇文章是 Stack Overflow 周报的第二周,共收集了 4 道关注问题和对应的赞回答。公众号「渡码」为日更,欢迎关注。 DAY1.serialVersionUID 的重要性 关注: 2820,最赞: 2152 这篇文章介绍一下 Java 中 serialVersionUID 属性的含义以及重要性。从属性可以看出它与序列化有关系,所以在 java...

weixin_30371469的博客 222

java线程重复bug_实际导致Stack Overflow错误的原因是什么? [重复]

是否有其他方法可以发生堆栈溢出,而不仅仅是通过递归?挑战接受:) StackOverflowError 没有递归(挑战失败,见评论):public class Test{final static int CALLS = 710;public static void main(String[] args){final Functor[] functors = new Functor[CALLS];...

weixin_36051064的博客 281

Stackoverflow上有哪些声望or值得关注的国人

Stackoverflow中国用户排行榜 Stackoverflow中国用户排名 Stackoverflow上有哪些声望/值得关注的国人?

极客学长Bravo | 突破信息差 | 副业生财有术 2796

python contains类似函数_2018年StackOverflow上最受关注的10个Python问题

作为一个开发者,除了Github,你最应该上的网站就是StackOverflow(以下简称SO)- 编程领域的问答网站,在SO上可以找到最正确和最容易理解的答案。如果你是一个有经验的并且愿意分享的人,还可以通过回答问题或者其他用户的赞同而获得'声望值',现在基本互联网公司招聘时如果你的SO账号的声望值比较是一个很正面的加分项。去年这个时候我们盘点过爱湃森:StackOverflow上最受关注的1...

weixin_39795845的博客 70

stackoverflow_如何获得10,000StackOverflow信誉

stackoverflow 一切如何开始 2014年Spring,我启动了Hibernate Master Class项目,重点关注最佳实践和完善的使用模式。 然后,我意识到我以前的所有Hibernate经验还不足以完成此任务。 我还需要更多。 Hibernate的学习曲线非常陡峭,每天都会询问数十个新的StackOverflow问题 。 有这么多问题等待解决,我意识到这是一个很好的机会,...

danpoxiang7784的博客 303

Stackoverflow 最受关注的 10Java 问题

Stack Overflow 是一个大型的编程知识库。在 Stack Overflow 中已经有数以百万计的问题,并且很多答案有着很的质量。这就是为什么 Stack Overflow 的答案经常位于 Google 搜索结果的顶部。尽管 Stack Overflow 上很多问题已经有了答案,每天还是会有很多问题被提出,很多问题仍然没有被回答或者没有获得满意的答案。那么当 Stack Overflo...

weixin_30344795的博客 125

StackOverflow 第四周周报及19年就业情况分析

这是 Stack Overflow 第四周周报,两篇 Java、两篇 Python。公众号「渡码」为日更,欢迎关注。另外,我搜集了今年的就业数据,对招聘情况和岗位情况做了简单总结,想了解的朋友点这里。 DAY1.枚举对象 == 和 equals 区别 我们在比较枚举常量时可以使用 == 运算符或者 equals 方法,如: public class EnumEquals { ...

weixin_30371469的博客 798

偶遇StackOverflow,很香

学而不思则罔,思而不学则殆 ---孔子《论语》 首先需要澄清的是,我个人也是最近才开始接触StackOverflow,有讲的不好的地方,还希望大佬们多多批评指点;说来也很惭愧,从事IT行业已经四五年了,第一次了解到StackOverflow,不过瞬间就被香到了,所以在这里简单的写一写,个人还未完全的熟练的使用它,不过已经感受到了它的吸引力。把它推荐给大家,希望大家及早接触了解,及早的成长。 先说说StackOverflow的历史 StackOverflow由JeffAtwood和JoelSpo.

weixin_35933684的博客 818

StackOverflow 上面最流行的 7 个 Java 问题

StackOverflow发展到目前,已经成为了全球开发者的金矿。它能够帮助我们找到在各个领域遇到的问题的最有用的解决方案,同时我们也会从中学习到很多新的东西。 这篇文章是在我们审阅了StackOverflow上最流行的Java问题以及答案后从中挑出来的。即使你是一个有丰富经验的开发者,也能从中学到不少东西。 一、分支预测 问题链接: https://stackoverflow.com/que...

Java技术栈,分享最主流的Java技术 1190

StackOverflow 周报 - 第四周质量问题的问答(Java、Python)

这是 Stack Overflow 第三周周报,由于本周周四外出,所以只有三篇内容。两篇 Java、一篇 Python。公众号「渡码」为日更,欢迎关注。 DAY1.枚举对象 == 和 equals 区别 我们在比较枚举常量时可以使用 == 运算符或者 equals 方法,如: 先考虑第一个问题:为什么可以用 == 判断枚举常量相等? 我们都知道判断对象的相等应该用 equ...

weixin_30371469的博客 799

Study-如何科学使用StackOverflow

Study-如何科学使用 Stack Overflow.md tags: StackOverFlow 2019 年 12 月 搜索 必备知识 翻墙 Stack OverFalow作为程序员的帮助论坛,Stack OverFalow 和 Google 远远优于百度 Stack OverFalow 简介 作为初学者,如何有效使用 Stack Overflow? 程序员的一个帮助论坛,您可以在其中发布问...

Scfan的博客天涯 973

为什么 StackOverflow 上的代码片段会摧毁你的项目?

本文转载自 InfoQ 在 StackOverflow 上你会惊奇地发现,上面分享的一些解决常见问题的代码居然存在安全漏洞。 现在,StackOverflow 已经成为许多程序员的救星。我们中的大部分人甚至从未访问过 StackOverflow 的主页。我们访问该网站的唯一方式,是通过谷歌搜索某个我们遇到的问题或 bug,然后直接进入该问题的讨论区。 尽管去 StackOverflow 上寻找问题解决办法的习惯很实用,但与此同时我们可能也会无意中使用一些包含严重 bug 或者安全缺陷的代码。众所周.

m0_46163918的博客 5124
上一篇: 程序员修炼之道-注重实效
下一篇: 19年就业形势怎么样
渡码
博客等级 码龄14年 31粉丝 22原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值