ClassLoaderWrapper

getResourceAsStream

java.lang.reflect.Proxy

MySQL for Excel

  • Import MySQL data into Excel.
  • Export Excel data to MySQL as a new table or append data to an existing table.
  • Modify MySQL data directly from within Excel.

泛型

参考 Comparator 接口的静态方法 comparing

Producer Extends, Consumer Super

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static <T, E extends Comparable<? super E>> Comparator<T> comparing(Function<? super T, ? extends E> keyGenerator) {
Comparator<T> comparator1 = new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
// 假设 keyGenerator 的定义接受一个 T 的父类型, 如 FatherT
// 此时 o1 是 T 类型的,是可以作为 keyGenerator.apply(FatherT fatherT) 的参数的
// 会进行向上转换,是合理有效的,任何父类引用都可以指向子类对象(父类的方法都会被子类继承,所以是合理的)
return keyGenerator.apply(o1).compareTo(keyGenerator.apply(o2));
}
};

// lambda 表达式写法
Comparator<T> comparator = (c1, c2) -> keyGenerator.apply(c1).compareTo(keyGenerator.apply(c2));
return comparator;
}

如果要返回一个 T 类型的 Comparator, 也就是 Comparator, 其负载类型为 T,comparing 是可以传入一个 Functinon<? super T, ? extends E> 的实例的,Functinon 的负载类型的第一个参数类型可以为 T 的任何父类型,也就是此 Functinon 的 apply() 方法可以接受任何 T 的父类对象。

在返回一个 Comparator 对象时,Functinon 函数会接受一个 T 对象实例,此时,会发生向上转换:

1
2
3
4
5
6
7
8
9
10
// 方法签名接受一个 Father 对象
public void printField(Father father) {
System.out.pritln(father.getField());
}

public void test() {
Son = new Son();
// 传入一个子类 Son 对象
printField(son);
}
1
2
3
4
5
6
7
public static void dateTest() {
Function<Circle, Integer> circleFunc = circle -> circle.getR().length();

// 当左边的类型为 CircleSubClass 时, keyGenerator 的负载类型可以是 CircleSubClass 的任何父类型
// 当右边 keyGenerator 的负载类型是 Circle 时,左边的 Comparator 负载类型可以是 Circle 的任何子类
Comparator<CircleSubClass> comparing = comparing(circleFunc);
}

《Java技术内幕》学习笔记

Chapter 6 Java 实现内存管理和并发编程的方式

allocation table - 分配表

stack frame - 栈帧

heap - 堆

reachable object - 可达对象/活性对象

GC Root - 通向可达对象的引用链根部一般称为 GC Root

mark and sweep 标记清除

标记清除,垃圾回收程序需要互斥存取整个堆,因此应用代码一直在运行,会不断创建和修改对象,应用线程会停顿一下(Stop-The-World, STW), 先停止所有应用线程,然后进行垃圾回收

对象的预期生命周期称为

弱代假设 (Weak Generation Hypothesis, WGH), 在这个假设中,对象常常处于少数几个预期生命周期之一

大多数的对象的生命期非常短,不久就会被垃圾回收,这些对象也称为 瞬时对象

筛选回收 Evacuation

将堆内存分成多个独立的内存空间,每次回收垃圾时,只为活性对象分配空间,并将这些对象移动到另一个内存空间。清理整个内存空间,供以后重复使用。

Eden 区 / Nursery 区

使用筛选回收程序的话,每个线程都可以单独分配内存,每个应用线程都有一块连续的内存-线程私有的分配缓冲区,专门供这个线程分配对象。为对象分配内存时,只需把指针指向分配缓冲区。

HotSpot 引入了 Survivor 区,用于保存前一次回收新生对象后存活下来的对象。筛选回收程序会在多个 Survivor 区之间来回复制存活下来的对象,直到超过 保有阈值, 再推给老年代

• 并行回收程序
使用多个线程执行回收操作的垃圾回收程序
• 并发回收程序
可以和应用线程同时运行的垃圾回收程序

HotSpot 堆

堆空间:新生代和老年代;新生代由三个区组成: Eden 区、两个 Survivor 区,老年代只有一个内存空间

多次回收循环后存活下来的对象,最终会推给老年代。

默认情况下,老年代使用的也是并行标记清除回收程序,并且回收程序会整理老年代,清除内存碎片

批量重命名文件

批量重命名或移动文件

  1. 利用 find 命令和 mv 命令,编写简单脚本
1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
# 重命名文件

count=1
for img in `find . -maxdepth 1 -type f -iname "*.jpg" -o -iname "*.jpeg"`
do
picName=image-$count.${img##*.}
echo "rename $img to $picName"
mv $img $picName
((count++))
done
  1. 利用 rename 命令
1
2
3
4
5
# reanme 利用了 perl 的正则表达式来进行替换 substitute
rename ‘s/\.jpg$/.jpeg/’ *.jpg

# 文件名大小变小写
rename 'y/A-Z/a-z/' *
  1. find 结合 rename 实现递归重命名
1
find -type f -exec rename 's/-/_/' {} \;
  1. 清理工作

文本处理

让文本飞

vim 中显示行号

1
2
3
vim /etc/vim/vimrc 
set number
set nonumber

提示符

1
2
3
4
5
6
7
59 if [ "$color_prompt" = yes ]; then
60 PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
61 else
62 PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '


force_color_prompt=yes

反转字符串

1
2
# reverse
echo "Hello World" | rev

字符串比较相等

1
2
# 等号两边的空格一定不能省略
[ $var1 = $var2 ]

1 压缩(删除)多余的空行

  • 使用 tr 转换
1
2
# tr -s for --squeeze-repeats
cat text.txt | tr -s '\n'
  • 使用 sed 转换
1
2
3
# sed 默认一次只处理一行(涉及sed的处理方式,hold space),是以 \n 作为行分隔符的,但是提供的一个 option -z, 以 NUL 字符作为行分隔符来解析

sed -zr 's/\n{2,}/\n/g' text.txt
  • sed 的另一种用法
1
sed '/^$/d' text.txt

2 计算连续数字的加法

  • 使用 tr
1
seq 1 10 | echo $[ $(tr '\n' '+') 0 ]

3 进入指定目录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 函数 return 为函数的返回,exit 为 shell 的结束
function to()
{
if [ $# -ne 1 ];
then
echo "Useage is: $0 argument"
echo "avaliable arguments can be znrt, zgrt, szrt"
return -1
fi
target=`echo -e $1 | tr '[a-z]' '[A-Z]'`
basePath=/opt

case $target in
"ZNRT") cd "$basePath/$target" && pwd;;
"ZGRT") cd "$basePath/$target" && pwd;;
"SZRT") cd "$basePath/$target" && pwd;;
*) echo "invalid argument";;
esac
}

查找出最近一个小时目录内有修改过的文件

modified.sh

1
2
find "/mnt/d/Markdown 文档" -type f -mmin -60 |
awk 'BEGIN { "date"| getline; print $0 } { print "modified:"$0 } END { print "\n" }'

备份:

1
2
3
4
5
6
7
8
9
10
11
12
#!/bin/bash
# copy source to target
baseDir="/mnt/e/SoftConfigBackup"
awk -F"=" '!/^\s*#/ \
{ cmd0=bd"/modified.sh "$1" >> modified.log"; \
system(cmd0); \
targetDir=bd"/"$2; \
cmd1="mkdir -p "targetDir; \
cmd2="cp -r " $1 " " targetDir; \
system(cmd1); \
system(cmd2) }' \
bd=$baseDir "$baseDir/backup.properties"

备份配置文件:

1
2
3
# software-config-dir=target-dit-name
/mnt/c/Users/guo/AppData/Roaming/Typora/themes=Typora
/mnt/c/Users/guo/Desktop=Desktop

WSL 服务开机自启

startup 设置 vbs 脚本

1
2
Set ws = CreateObject("Wscript.Shell")
ws.run "C:\Users\guo\AppData\Local\Microsoft\WindowsApps\ubuntu1804.exe run sudo /etc/init.wsl", vbhide

/etc/init.wsl

1
2
3
4
#! /bin/sh
/etc/init.d/ssh restart
/etc/init.d/mysql restart
/etc/init.d/cron restart

/etc/sudoers

1
%sudo ALL=NOPASSWD: /etc/init.wsl