我的个人博客

哪有什么胜利可言,挺着就代表着一切


  • 首页

  • 归档

vscode的快捷键

发表于 2019-04-03 | 分类于 工具的使用

转载vscode: Visual Studio Code 常用快捷键

主命令框

1
2
3
4
5
6
7
8
9
10
11
F1 或 Ctrl+Shift+P : 打开命令面板。在打开的输入框内,可以输入任何命令,例如:
按一下 Backspace 会进入到 Ctrl+P 模式
在 Ctrl+P 下输入 > 可以进入 Ctrl+Shift+P 模式
在 Ctrl+P 窗口下还可以:
-直接输入文件名,跳转到文件

? 列出当前可执行的动作
! 显示 Errors或 Warnings,也可以 Ctrl+Shift+M
: 跳转到行数,也可以 Ctrl+G 直接进入
@ 跳转到 symbol(搜索变量或者函数),也可以 Ctrl+Shift+O 直接进入
@ 根据分类跳转 symbol,查找属性或函数,也可以 Ctrl+Shift+O 后输入:进入

根据名字查找 symbol,也可以 Ctrl+T

编辑器与窗口管理

1
2
3
4
5
6
7
8
9
打开一个新窗口: Ctrl+Shift+N
关闭窗口: Ctrl+Shift+W
同时打开多个编辑器(查看多个文件)
新建文件 Ctrl+N
文件之间切换 Ctrl+Tab
切出一个新的编辑器(最多 3 个) Ctrl+\,也可以按住 Ctrl 鼠标点击 Explorer 里的文件名
左中右 3 个编辑器的快捷键 Ctrl+1 Ctrl+2 Ctrl+3
3 个编辑器之间循环切换 Ctrl+
编辑器换位置, Ctrl+k然后按 Left或 Right

代码编辑

1
2
3
4
5
6
7
代码行缩进: Ctrl+[ 、 Ctrl+]
Ctrl+C 、 Ctrl+V 复制或剪切当前行/当前选中内容
代码格式化: Shift+Alt+F,或 Ctrl+Shift+P 后输入 format code
上下移动一行: Alt+Up 或 Alt+Down
向上向下复制一行: Shift+Alt+Up 或 Shift+Alt+Down
在当前行下边插入一行: Ctrl+Enter
在当前行上方插入一行 Ctrl+Shift+Enter

光标相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
移动到行首: Home
移动到行尾: End
移动到文件结尾: Ctrl+End
移动到文件开头: Ctrl+Home
移动到定义处: F12
定义处缩略图:只看一眼而不跳转过去 Alt+F12
移动到后半个括号: Ctrl+Shift+]
选择从光标到行尾: Shift+End
选择从行首到光标处: Shift+Home
删除光标右侧的所有字: Ctrl+Delete
扩展/缩小选取范围: Shift+Alt+Left 和 Shift+Alt+Right
多行编辑(列编辑):Alt+Shift+鼠标左键, Ctrl+Alt+Down/Up
同时选中所有匹配: Ctrl+Shift+L
Ctrl+D 下一个匹配的也被选中 (在 sublime 中是删除当前行,后面自定义快键键中,设置与 Ctrl+Shift+K 互换了)
回退上一个光标操作: Ctrl+U
选中所有匹配词批量编辑:鼠标高亮选中需要查找的词,按下 Ctrl + Shift + L键,即可快速选中当前文件中所有匹配的词,并在每一个词后面有一个编辑光标,可批量同步编辑

重构代码

1
2
3
4
5
6
7
8
9
找到所有的引用: Shift+F12
同时修改本文件中所有匹配的: Ctrl+F12
重命名:比如要修改一个方法名,可以选中后按 F2,输入新名字,回车,则所有该方法的引用也都同步更新了
跳转到下一个 Error 或 Warning:当有多个错误时可以按 F8 逐个跳转
查看 diff: 在 explorer 里选择文件右键 Set file tocompare,然后需要对比的文件上右键选择 Compare with file_name_you_chose
查找替换
查找 Ctrl+F
查找替换 Ctrl+H
整个文件夹中查找 Ctrl+Shift+F

显示相关

1
2
3
4
5
6
7
8
全屏:F11
zoomIn/zoomOut:Ctrl +/-
侧边栏显/隐: Ctrl+B
显示资源管理器 Ctrl+Shift+E
显示搜索 Ctrl+Shift+F
显示 Git Ctrl+Shift+G
显示 Debug Ctrl+Shift+D
显示 Output Ctrl+Shift+U

其他

1
2
3
自动保存:File -> AutoSave,或者 Ctrl+Shift+P,输入 auto
修改默认快捷键
打开默认键盘快捷方式设置:File -> Preferences -> Keyboard Shortcuts,或者:Alt+F -> p -> k

修改 keybindings.json:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Place your key bindings in this file to overwrite the defaults
[
// ctrl+space 被切换输入法快捷键占用
{
"key": "ctrl+alt+space",
"command": "editor.action.triggerSuggest",
"when": "editorTextFocus"
},
// ctrl+d 删除一行
{
"key": "ctrl+d",
"command": "editor.action.deleteLines",
"when": "editorTextFocus"
},
// 与删除一行的快捷键互换
{
"key": "ctrl+shift+k",
"command": "editor.action.addSelectionToNextFindMatch",
"when": "editorFocus"
},
// ctrl+shift+/多行注释
{
"key":"ctrl+shift+/",
"command": "editor.action.blockComment",
"when": "editorTextFocus"
},
// 定制与 sublime 相同的大小写转换快捷键
{
"key": "ctrl+k ctrl+u",
"command": "editor.action.transformToUppercase",
"when": "editorTextFocus"
},
{
"key": "ctrl+k ctrl+l",
"command": "editor.action.transformToLowercase",
"when": "editorTextFocus"
}
]

前端开发必备插件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PostCSS Sorting
stylelint
stylefmt
ESLint
javascript standard format
beautify
Babel ES6/ES7
Debugger for Chrome
Add jsdoc comments
javascript(ES6) code snippets
vue
weex
Reactjs code snippets
React Native Tools
Npm Intellisense
Instant Markdown
Markdown Shortcuts
TextTransform
使用等宽字体:
Fira Code

vscode 自定义配置参考:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
"editor.fontSize": 16,
"editor.tabSize": 2,
"editor.fontLigatures": true,
// 使用等宽字体 Fira Code
"editor.fontFamily": "Fira Code, 'Noto Sans CJK SC Medium', Consolas, 'Courier New', monospace",
// 关闭右侧的 minimap
"editor.minimap.enabled": false,
"editor.minimap.renderCharacters": false,
"files.associations": {
"*.es": "javascript",
"*.es6": "javascript"
},
// 控制编辑器是否应呈现空白字符
"editor.renderWhitespace": "all",
// 启用后,将在保存文件时剪裁尾随空格。
"files.trimTrailingWhitespace": true,
// File extensions that can be beautified as javascript or JSON.
"beautify.JSfiles": [
"",
"es",
"es6",
"js",
"json",
"jsbeautifyrc",
"jshintrc"
],
// 关闭 git 自动刷新、fetch、add 操作
"git.enableSmartCommit": false,
"git.enabled": true,
"git.autorefresh": false,
"git.autofetch": false,
}

更多参考官方快捷键大全:https://code.visualstudio.com/docs/customization/keybindings

java流库

发表于 2019-04-02 | 分类于 java

流与集合的差异

  • 流并不储存其元素,这些元素可能存储于底层的集合中,或者按需生成的
  • 流的操作不会修改数据源
  • 流的操作是尽可能惰性的,这意味着直至需要其结果时,操作才进行。

流的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
List<String> list = new ArrayList<>();
list.add("123");
list.add("456");
list.add("789");

Stream<String> stream = list.stream();//通过集合创建流

Stream<String> stream1 = Stream.of(new String[]{"123","456","789"});//通过数组创建流

Stream<String> stream2 = Stream.of("123","456","789");//通过具体数据创建流

Stream<Double> stream3 = Stream.generate(Math::random); //创建无限流,上面的都是有限流

Stream<Integer> stream4 = Stream.iterate(0, integer -> ++integer);//创建无限序列

Stream<String> stream5 = Stream.empty();//产生空的流

String[] strings = new String[]{"123","456","789"};//使用Arrays创建流
Stream stream6 = Arrays.stream(strings);

filter

filter方法产生一个流,其中包含当前流中满足指定条件的所有元素

1
2
3
4
5
6
7
8
9
10
11
 List<String> list = new ArrayList<>();
list.add("12345");
list.add("123");
list.add("6789");
Stream filterTest = list.stream().filter(p->p.length() >= 4);//获取长度大于等于4的字符串的流
filterTest.forEach(System.out::println);

输出结果:

12345
6789

map

将目标流转化为指定流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
List<String> list = new ArrayList<>();
list.add("12345");
list.add("123");
list.add("6789");
Stream mapTest = list.stream().map(String::length);
mapTest.forEach(System.out::println);

等同于

List<String> list = new ArrayList<>();
list.add("12345");
list.add("123");
list.add("6789");
Stream mapTest = list.stream().map(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return s.length();
}
});
mapTest.forEach(System.out::println);

flatMap

1
2
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

flatMap与map的区别:

map是将mapper应用于当前流中所有的元素产生的结果,而flatMap是通过将mapper应用于当前流
中所有元素产生的结果连接在一起而获得。

limit、skip和concat

1
2
3
4
5
6
7
8
9
List<String> list = new ArrayList<>();
list.add("12345");
list.add("123");
list.add("6789");
list.stream().limit(2).forEach(System.out::println);//limit只保留前面指定元素个数
list.stream().skip(1).forEach(System.out::println);//skip跳过前面指定元素个数
System.out.println("===============");
//concat连接两个流(a,b),它产生的流是a元素后面跟着b元素,注意:第一个流不能是无限流,比如第二个元素永远不能得到处理的机会
Stream.concat(list.stream(),list.stream()).forEach(System.out::println);

流转换

1
2
3
4
5
6
7
8
Stream<T> distinct() //产生一个流,包含原来流中所有不同的元素

Stream<T> sorted()//排序流,要求流中元素都实现 Comparable 接口
Stream<T> sorted(Comparator<? super T> comparator)//排序流

//peek方法会产生另一个流,它的元素与原来流中相同,但是在每一次获取一个元素时
//都会调用action中的方法,这对于调试很方便
Stream<T> peek(Consumer<? super T> action)

流操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
List<String> list = new ArrayList<>();
list.add("12345");
list.add("123");
list.add("6789");

Optional<String> v = list.stream().max(Comparator.comparingInt(String::length));
Optional<String> v1 = list.stream().min(Comparator.comparingInt(String::length));
Optional<String> v2 = list.stream().findFirst();
Optional<String> v3 = list.stream().findAny();
boolean v4 = list.stream().anyMatch(t->t.length()>3);//是否有任意个匹配
boolean v5 = list.stream().noneMatch(t->t.length()>3);//是否没有一个匹配
boolean v6 = list.stream().allMatch(t->t.length()>3);//是否都匹配
System.out.println(v.get());
System.out.println(v1.get());
System.out.println(v2.get());
System.out.println(v3.get());
System.out.println(v4);
System.out.println(v5);
System.out.println(v6);

结果为:

12345
123
12345
12345
true
false
false

Optional

创建Optional:

1
2
3
4
5
6
//of 方法产生具有给定值的Optional
Optional<String> value = Optional.of("123");
//产生一个空的Optional,不能使用 get 方法,否则会保错
Optional<String> value1 = Optional.empty();
//如果给定的值为null,则产生一个空的Optional,否则产生具有给定值的Optional
Optional<String> value2 = Optional.ofNullable(null);

常用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Optional<String> value = Optional.empty();
String result = value.orElse("");//如果Optional的给定值为null,则会返回默认值 ""
String result1 = value.orElseGet(()->"");//如果Optional的给定值为null,则会调用指定方法返回值
String result2 =value.orElseThrow(IllegalStateException::new);//如果Optional的给定值为null,则会抛出错误
value.ifPresent(System.out::println);//只有Optional的值不为null时,才执行指定方法

Optional<String> value = Optional.of("12345");
//Optional的 map 和流的 map 方法差不多,它可以转换Optional的类型,例如下面从 Optional<String> 转换为 Optional<Integer>
//注意:如果上面的 value 为空的Optional,或者 value.map(v->null) ,则会返回空的Optional(即Optional.empty())
Optional<Integer> optional = value.map(String::length);
System.out.println(optional);

//和map差不多
Optional<String> value = Optional.of("12345");
Optional<Integer> integer = value.flatMap(s->Optional.of(s.length()));

流转换为集合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
    //流转化为数组
String[] array = (String[]) stream.toArray();
String[] arrays = stream.toArray(String[]::new);

//流转化为指定的集合
List<String> list1 = stream.collect(Collectors.toList());
Set<String> set = stream.collect(Collectors.toSet());
ArrayList<String> arrayList = stream.collect(Collectors.toCollection(ArrayList::new));
//映射表
Map<Integer,String> map = stream.collect(Collections.toMap(Person::getId,Person::getName))
Map<Integer,Person> map = stream.collect(Collections.toMap(Person::getId,Function.identity()))
//第三个参数用来处理键发生冲突的情况,默认是抛出异常
Map<Integer,String> map = stream.collect(Collections.toMap(Person::getId,Person::getName,(existValue,newValue)->existValue))
//转化为指定的 Map
Map<Integer,String> map = stream.collect(Collections.toMap(Person::getId,Person::getName),TreeMap::new)

//连接流中的元素,可以指定分隔符
String result = stream.collect(Collectors.joining());
String result1 = stream.collect(Collectors.joining(","));

//有(Double|Long|Int)三种,下面只列举了Int,其他类似
IntSummaryStatistics summaryStatistics = stream.collect(Collectors.summarizingInt(String::length));
double average = summaryStatistics.getAverage();//平均值
double maxWordLength = summaryStatistics.getMax();//最大值
long sum = summaryStatistics.getSum();//总和
long count = summaryStatistics.getCount();//元素个数

//通过群组和分区来指定映射表
List<Student> students = new ArrayList<>();
Student student1=new Student(1,"小明");
Student student2=new Student(2,"小军");
Student student3=new Student(1,"小红");
Student student4=new Student(1,"小键");
students.add(student1);
students.add(student2);
students.add(student3);
students.add(student4);
//通过班级的名字来分组
Map<Integer,List<Student>> cl=students.stream().collect(Collectors.groupingBy(Student::getClassName));
//通过判断班级名是否为2来分组
Map<Boolean,List<Student>> cl1=students.stream().collect(Collectors.partitioningBy(l->l.getClassName()==2));

List<Teacher> teachers = new ArrayList<>();
Teacher teacher1=new Teacher("北京","小明",26);
Teacher teacher2=new Teacher("重庆","小军",30);
Teacher teacher3=new Teacher("上海","小红",26);
Teacher teacher4=new Teacher("北京","小键",34);
teachers.add(teacher1);
teachers.add(teacher2);
teachers.add(teacher3);
teachers.add(teacher4);
//通过工作地点把教师分组后,使用 Collectors.counting() 方法计算每组的人数
//和 Collectors.counting() 类似的对分好组的集合进行操作的方法有:
//summingInt/Long/Double 通过传递一个 返回值为 int/long/double 的函数来对已经分好组的集合进行操作,并求其和
//maxBy和minBy 获取分好组集合中的最大值或最小值
Map<String,Long> c=teachers.stream().collect(Collectors.groupingBy(Teacher::getWorkplace,Collectors.counting()));
//通过工作地点把教师分组后,通过 Collectors.mapping() 方法对已经分好组的集合使用 getAge()方法获取这个集合中各个老师的年龄
//并放在一个新的集合中
Map<String,Set<Integer>> c =
teachers.stream().collect(Collectors.groupingBy(Teacher::getWorkplace,Collectors.mapping(Teacher::getAge,Collectors.toSet())));
}

基本类型流

IntStream:可以存储short、char、byte和boolean
DoubleStream:存储float和double
LongStream:存储long

基本类型流的方法和上面介绍的方法类似,下面介绍基本类型流的特有方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//独有构造方法
//range()、rangeClosed(),IntStream和LongStream独有
IntStream t=IntStream.range(0,10);//范围 [0,10)
IntStream t1=IntStream.rangeClosed(0,10);//[0,10]

//codePonts chars 生成由字符的Unicode码或由UTF-16编码机制的码元构成的IntStream,这两个方法的区别暂时还不清楚
String s = "gkdl;iofk;slaksdk,sdki\uD835";
IntStream i1 = s.chars();
IntStream i2 = s.codePoints();

//mapToInt mapToLong mapToDouble
IntStream stream = teachers.stream().mapToInt(Teacher::getAge);

//将基本类型流转化为对象流
Stream<Integer> t=IntStream.range(0,10).boxed();

//这里的OptionalInt和Optional类似
OptionalInt o1= t.max();//最大值
OptionalInt o2=t.min();//最小值
OptionalDouble o3=t.average();//平均值
int sum = t.sum();//总和

//返回由随机数构成的基本类型
Random random = new Random();
IntStream r1 = random.ints();
DoubleStream r2 = random.doubles();
LongStream r3 = random.longs();

并行流

1
2
3
4
//创建并行流
Collection.parallelStream()//集合转为并行流

Stream.parallel()//流转为并行流

并行流正常工作的条件

  • 数据应该在内存中,等待数据到达是低效的
  • 流一个分为高效的几部分,由数组或平衡二叉树支撑的流都可以正常工作,但是Stream.iterate不行
  • 流操作的工作量应该有很大的规模,不然使用并行流没有意义
  • 流操作不应该被堵塞

java图片处理

发表于 2019-04-02 | 分类于 java

转载Thumbnailator轻松搞定图片缩放、旋转、加水印

Thumbnailator的GitHub地址

Maven依赖

1
2
3
4
5
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.8</version>
</dependency>

Gradle依赖

1
implementation group: 'net.coobird', name: 'thumbnailator', version: '0.4.1'

json的处理

发表于 2019-04-02 | 分类于 java

JAVA中常用的JSON解析方式

  • JSON官方,官网下载
  • GSON 谷歌的框架
  • FastJSON 阿里的框架
  • jackson Spring自带框架

Gson的下载

Gradle依赖

在Gradle中添加依赖

1
implementation 'com.google.code.gson:gson:2.8.5'

Maven依赖

1
2
3
4
5
<dependency>  
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>

Gson教程

  • 你真的会用Gson吗?Gson使用指南(一)
  • 你真的会用Gson吗?Gson使用指南(二)
  • 你真的会用Gson吗?Gson使用指南(三)
  • 你真的会用Gson吗?Gson使用指南(四)

FastJson的下载

Gradle

1
2
compile 'com.alibaba:fastjson:1.2.55'
compile 'com.alibaba:fastjson:1.1.70.android'

Maven

1
2
3
4
5
6
7
8
9
10
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.55</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.70.android</version>
</dependency>

FastJson的教程

菜鸟教程

lambda表达式

发表于 2019-04-02 | 分类于 java

函数式接口

函数式接口是指接口中只有一个抽象方法(Java8中接口中可以声明非抽象方法),例如Comparator:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@FunctionalInterface
public interface Comparator<T> {

int compare(T o1, T o2);

//重新声明Object中的方法会让方法不再是抽象方法
boolean equals(Object obj);

default Comparator<T> reversed() {
return Collections.reverseOrder(this);
}

public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
return new Comparators.NullComparator<>(true, comparator);
}

....

}

使用实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//注意:由于带Comparator的sort方法参数声明为泛型,所有无法使用基本类型
Integer word[] = new Integer[]{12,23};
Arrays.sort(word,(e1, e2)-> e1 - e2);

//上面的代码相当于下面的代码,可以看出lambda可以使代码简洁
Integer word[] = new Integer[]{12,23};
Arrays.sort(word, new Comparator<Integer>() {
@Override
public int compare(Integer e1, Integer e2) {
return e1 - e2;
}
});


Predicate<Boolean> isNull = Objects::isNull;
System.out.println(isNull.test(null));
//等同于
Predicate<Boolean> isNull = new Predicate<Boolean>() {
@Override
public boolean test(Boolean aBoolean) {
return Objects.isNull(aBoolean);
}
};
System.out.println(isNull.test(null));

lambda表达式的方法引用

方法引用的三种情况

  • object::instanceMethod(对象方法)
  • Class::staticMethod (静态方法)
  • Class::instanceMethod (对象方法)

前两种情况中,方法引用等价于提供方法参数的lambda,例如Math::pow等价于(x,y)->Math.pow(x,y)

第三种情况,第一个参数会成为方法的目标,例如String::compareToIgnoreCase等价于(x,y)->x.compareToIgnore(y)

lambda表达式的构造器引用

例如A::new是A的构造器引用,至于使用哪一个构造器,取决于上下文。

lambda表达式变量的作用域

1
2
3
4
5
6
public void requestMessage(String text,int delay){
ActionListener actionListener = e -> {
System.out.println(text);//使用变量text
...
};
}

可以把一个lambda表达式转化为包含一个方法的对象,这样自由变量的值就会复制到这个对象的实例变量中去

注意:lambda表达式中捕获的变量必须是最终值;而且在lambda中声明于一个局部变量同名的参数或局部变量是不合法的;在lambda表达式中使用this关键字时,是指创建这个lambda表达式的方法的this参数

常用的函数式接口

函数式接口 参数类型 返回类型 抽象方法名 描述
Runnable 无 void run 作为无参数或返回值的动作运行
Predicate<T> T boolean test 布尔值函数
BiPredicate<T,U> T,U boolean test 有两个参数的boolean函数
Supplier<T> 无 T get 提供一个T型的值
Consumer<T> T void accept 处理一个T类型的值
BiConsumer<T,U> T,U void accept 处理T和U类型的值
Function<T,R> T R apply 由T转化为R类型
BiFunction<T,U,R> T,U R apply 由T,U类型转化为R类型
UnaryOperator<T> T T applay UnaryOperator继承Function,由T类型返回T类型
BinaryOperator<T> T,T T applay BinaryOperator继承BiFunction

基本类型的函数式接口

为了减少自动装箱,一个尽量使用基本类型的函数式接口,如图:

函数式接口

java文件操作---概述

发表于 2019-04-02 | 分类于 java

字符与字节

该部分转载字符与字节

在Java中有输入、输出两种IO流,每种输入、输出流又分为字节流和字符流两大类。关于字节,我们在学习8大基本数据类型中都有了解,每个字节(byte)有8bit组成,每种数据类型又几个字节组成等。关于字符,我们可能知道代表一个汉字或者英文字母。

但是字节与字符之间的关系是怎样的?

Java采用unicode编码,2个字节来表示一个字符,这点与C语言中不同,C语言中采用ASCII,在大多数系统中,一个字符通常占1个字节,但是在0~127整数之间的字符映射,unicode向下兼容ASCII。而Java采用unicode来表示字符,一个中文或英文字符的unicode编码都占2个字节。但如果采用其他编码方式,一个字符占用的字节数则各不相同。可能有点晕,举个例子解释下。

例如:Java中的String类是按照unicode进行编码的,当使用String(byte[] bytes, String encoding)构造字符串时,encoding所指的是bytes中的数据是按照那种方式编码的,而不是最后产生的String是什么编码方式,换句话说,是让系统把bytes中的数据由encoding编码方式转换成unicode编码。如果不指明,bytes的编码方式将由jdk根据操作系统决定。

getBytes(String charsetName)使用指定的编码方式将此String编码为byte序列,并将结果存储到一个新的 byte 数组中。如果不指定将使用操作系统默认的编码方式,我的电脑默认的是GBK编码。

1
2
3
4
5
6
7
8
9
10
public class Hel {  
public static void main(String[] args){
String str = "你好hello";
int byte_len = str.getBytes().length;
int len = str.length();
System.out.println("字节长度为:" + byte_len);
System.out.println("字符长度为:" + len);
System.out.println("系统默认编码方式:" + System.getProperty("file.encoding"));
}
}

输出结果

1
2
3
字节长度为:9
字符长度为:7
系统默认编码方式:GBK

这是因为:在 GB 2312 编码或 GBK 编码中,一个英文字母字符存储需要1个字节,一个汉字字符存储需要2个字节。 在UTF-8编码中,一个英文字母字符存储需要1个字节,一个汉字字符储存需要3到4个字节。在UTF-16编码中,一个英文字母字符存储需要2个字节,一个汉字字符储存需要3到4个字节(Unicode扩展区的一些汉字存储需要4个字节)。在UTF-32编码中,世界上任何字符的存储都需要4个字节。

简单来讲,一个字符表示一个汉字或英文字母,具体字符与字节之间的大小比例视编码情况而定。有时候读取的数据是乱码,就是因为编码方式不一致,需要进行转换,然后再按照unicode进行编码。

字节输入/输出流

这类流的操作是基于单个字节因此不便于处理以Unicode形式储存的信息,比如用这个操作中文字符会出现乱码

字节输入流
字节输出流

字符输入/输出流

这类流的操作都是基于两个字节的char值的(即,Unicode码元),而不是基于byte值的。

字符输入流
字符输出流

流的接口

Closeable

InputStream,OutputStream,Reader和Writer都实现了Closeable接口。

1
2
3
public interface Closeable extends AutoCloseable {
public void close() throws IOException;
}

从上面的定义可知Closeable扩展AutoCloseable.实现AutoCloseable的类都可以使用try-with-resource.

为什么需要两个接口,因为AutoCloseable.close方法可以抛出任何异常,而Closeable.close方法
只能抛出IOException

Flushable

OutputStream和Writer实现了Flushable

Readable

Reader实现了Readable接口

1
2
3
4
public interface Readable {
//CharBuffer可以按顺序或随机进行读写
public int read(java.nio.CharBuffer cb) throws IOException;
}

Appendable

只有Writer实现了Appendable接口

1
2
3
4
5
6
7
8
9

public interface Appendable {

Appendable append(CharSequence csq) throws IOException;

Appendable append(CharSequence csq, int start, int end) throws IOException;

Appendable append(char c) throws IOException;
}

文件的操作

File

File 是磁盘文件和目录路径名的一种抽象形式,其直接继承自 Object,实现了 Serializable 接口和 Comparable 接口;实现 Serializable 接口意味着 File 对象可以被序列化,而实现 Comparable 接口意味着 File 对象可以比较大小;此外 File 并没有什么特殊之处,就是对文件的一种上层抽象封装实现,方便操作文件。

File类共提供了四个不同的构造函数,以不同的参数形式灵活地接收文件和目录名信息。构造函数:

1
2
3
4
File (String pathname)
File(URI uri)
File (String parent , String child)
File (File parent , String child)

File的常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public boolean exists( ) 判断文件或目录是否存在

public boolean isFile( ) 判断是文件还是目录

public boolean isDirectory( ) 判断是文件还是目录

public String getName( ) 返回文件名或目录名

public String getPath( ) 返回文件或目录的路径。

public long length( ) 获取文件的长度

public String[ ] list () 将目录中所有文件名和目录名保存在字符串数组中返回

public File[] listFiles() 返回某个目录下所有文件和目录的绝对路径,返回的是File数组

public String getAbsolutePath() 返回文件或目录的绝对路径

public boolean renameTo( File newFile ); 重命名文件

public void delete( ); 删除文件

public boolean mkdir( ); 创建单个文件夹

public boolean mkdirs();可以建立多级文件夹

public boolean createNewFile(); 创建文件

注意:当需要创建D:\\test\\test1.txt文件时,如果test目录不存在,则不可以直接使用createNewFile()创建;
同样不能使用mkdir(),因为mkdir()会把D:\\test\\test1.txt中的test和test1.txt都当做文件夹而mkdir()
只能创建一个文件夹;如果使用mkdirs()则会生成test和test1.txt两个文件.

创建一个文件夹和文件

1
2
3
4
5
File file = new File("D:\\test");
if(file.mkdir()){
File f = new File(file,"test1.txt");
f.createNewFile();
}

FileDescriptor

转载Little丶Jerry

File 是磁盘文件和目录路径名的一种抽象形式,其直接继承自 Object,实现了 Serializable 接口和 Comparable 接口;实现 Serializable 接口意味着 File 对象可以被序列化,而实现 Comparable 接口意味着 File 对象可以比较大小;此外 File 并没有什么特殊之处,就是对文件的一种上层抽象封装实现,方便操作文件。

FileDescriptor 是文件描述符,用来表示开放文件、开放套接字等。当 FileDescriptor 表示文件时,我们可以通俗的将 FileDescriptor 看成是该文件,但是不能直接通过 FileDescriptor 对该文件进行操作,若要通过 FileDescriptor 对该文件进行操作,则需要新创建 FileDescriptor 对应的 FileOutputStream,然后再对文件进行操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
public final class FileDescriptor {
/**
* 在形式上是一个非负整数。
* 实质是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。
* 当程序打开一个现有文件或者创建一个新文件时内核向进程返回一个文件描述符。
*/
private int descriptor;

public /**/ FileDescriptor() {
descriptor = -1;
}

private /**/ FileDescriptor(int descriptor) {
this.descriptor = descriptor;
}

/**
* (03)err -- 标准错误输出(屏幕)的描述符
* @see java.lang.System#in
*/
public static final FileDescriptor in = dupFd(0);

/**
* (01)in -- 标准输入(键盘)的描述符
* @see java.lang.System#out
*/
public static final FileDescriptor out = dupFd(1);

/**
* (02)out -- 标准输出(屏幕)的描述符
* @see java.lang.System#err
*/
public static final FileDescriptor err = dupFd(2);

......
//工具构造方法
private static FileDescriptor dupFd(int fd) {
try {
return new FileDescriptor(Os.fcntlInt(new FileDescriptor(fd), F_DUPFD_CLOEXEC, 0));
} catch (ErrnoException e) {
throw new RuntimeException(e);
}
}
......
}

所以,针对 System.X 的 API 来说 FileDescriptor 是一种更加底层的操作,其不与文件名相互关联,只与操作系统中对应文件的句柄关联。

RandomAccessFile

RandomAccessFile类可以在文件的任何位置查找或写入数据

构造方法

1
2
3
4
5
6
//模式有四种:r 表示只读 rw 表示读写模式  rws 表示每次更新时都对数据和元数据
//的写磁盘操作进行同步 rwd 表示每次更新时,只对数据的写磁盘操作进行同步
//注:元数据即数据的信息,比如数据的创建修改时间,数据是文件还是目录等
RandomAccessFile file = new RandomAccessFile("文件名","模式");

RandomAccessFile file1 = new RandomAccessFile(File,"mode");

常用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
long getFilePointer() 返回文件指针的位置

void seek(long pos) 将文件指针设置到距文件开头的pos字节处

long length() 返回文件按照字节来度量的长度

final void writeBoolean(boolean val)
final void writeByte(int val)
final void writeBytes(String str)
final void writeChar(int val)
final void writeChars(String str)
final void writeDouble(double val)
final void writeFloat(float val)
final void writeInt(int val)
final void writeLong(long val)
final void writeShort(int val)
final void writeUTF(String str) 使用utf-8编码格式写入字符串
final void write() 写入字节

注:在Java中char有两个字节,double有八个字节,int有四个字节
float有四个字节,boolean有一个字节

详细的参考:

java io系列26之 RandomAccessFile
RandomAccessFile类使用详解
RandomAccessFile 文件读写中文乱码解决方案

java集合---Collection

发表于 2019-03-30 | 分类于 java

Collection类

1
public interface Collection<E> extends Iterable<E>

如上,我们知道Collection是一个泛型类,并且实现了Iterable接口。

Iterable接口

实现Iterable的类可以通过for each循环进行遍历

Iterable中有三个方法,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

/**
* 返回一个迭代器
*/
Iterator<T> iterator();

/**
*Java 8的新特性,之后介绍
*/
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}

/**
* Java 8的新特性,之后介绍
*/
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}

Iterator迭代器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public interface Iterator<E> {
/**
*判断集合中是否还存在元素
*/
boolean hasNext();

/**
*返回下一个元素
*/
E next();

/**
*将迭代器新返回的元素删除
*/
default void remove() {
throw new UnsupportedOperationException("remove");
}

/**
*Java 8新特性,之后介绍
*/
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}

Java集合框架中的类都在内部实现了Iterator接口,例如,在ArrayList中:

1
2
3
4
5
6
7
8
9
10
11
12
13
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{

public Iterator<E> iterator() {
return new Itr();
}


private class Itr implements Iterator<E> {
...
}
}

Collection

Collection是一个容器接口,其中定义操作容器的方法,如下:

1
2
3
4
5
6
7
8
9
10
11
boolean add(Object o)添加对象到集合
boolean remove(Object o)删除指定的对象
int size()返回当前集合中元素的数量
boolean contains(Object o)查找集合中是否有指定的对象
boolean isEmpty()判断集合是否为空
Iterator iterator()返回一个迭代器
boolean containsAll(Collection c)查找集合中是否有集合c中的元素
boolean addAll(Collection c)将集合c中所有的元素添加给该集合
void clear()删除集合中所有元素
void removeAll(Collection c)从集合中删除c集合中也有的元素
void retainAll(Collection c)从集合中删除集合c中不包含的元素

AbstractCollection

AbstractCollection继承Collection,是一个抽象类,它实现了Collection中的部分方法,如:

1
2
3
4
5
6
7
8
9
10
11
public boolean isEmpty()
public boolean contains(Object o)
public Object[] toArray()
public <T> T[] toArray(T[] a)
public boolean add(E e)
public boolean remove(Object o)
public boolean containsAll(Collection<?> c)
public boolean addAll(Collection<? extends E> c)
public boolean removeAll(Collection<?> c)
public boolean retainAll(Collection<?> c)
public String toString()

Colections

Collections 是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。

java集合---线程安全的集合

发表于 2019-03-30

java集合---Map

发表于 2019-03-30 | 分类于 java

如图:

Map

Map

存储键/值对的接口,一个键只能对应一个值。

SortedMap

通过Comparable或Comparator对key值进行进行排序的接口。

NavigableMap

NavigableMap继承SortedMap接口,具有了为给定搜索目标报告最接近匹配项的导航方法。
方法 lowerEntry,floorEntry,ceilingEntry,higherEntry 和higherEntry返回与key相
关联的Entry对象分别小于,小于或等于大于或等于,大于给定键,如果没有这样的键,则返回null
。类似地,方法lowerKey,floorKey,ceilingKey和higherKey仅返回关联的键。所有这些方法
都是为了定位而不是遍历条目而设计的

AbstractMap

AbstractMap是抽象类,它实现了Map接口,其中它只定义了public abstract Set<Entry<K,V>> entrySet();一个抽象方法

TreeMap

TreeMap 是一个有序的key-value集合,它是通过红黑树实现的。该映射根据其键的自然顺序进行排序,
或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法

TreeMap 继承于AbstractMap,所以它是一个Map,即一个key-value集合。

TreeMap 实现了NavigableMap接口,意味着它支持一系列的导航方法。比如返回有序的key集合。

EnumMap

EnumMap的key不允许为null,value可以为null

WeakHashMap

WeakHashMap实现了Map接口,是HashMap的一种实现,他使用弱引用作为内部数据的存储方案,WeakHashMap可以作为简单缓存表的解决方案,当系统内存不够的时候,垃圾收集器会自动的清除没有在其他任何地方被引用的键值对。

IdentityHashMap

区别与其他的键不能重复的容器,IdentityHashMap允许key值重复,但是——key必须是两个不同的对象,即对于k1和k2,当k1==k2时,IdentityHashMap认为两个key相等,而HashMap只有在k1.equals(k2) == true 时才会认为两个key相等。

HashMap

HashMap 是一个散列表,它存储的内容是键值对(key-value)映射。
HashMap 继承于AbstractMap,实现了Map、Cloneable、java.io.Serializable接口。
HashMap 的实现不是同步的,这意味着它不是线程安全的。它的key、value都可以为null。此外,HashMap中的映射不是有序的。

LinkedHashMap

LinkedHashMap是HashMap的一个子类,它保留插入的顺序,如果需要输出的顺序和输入时的相同,那么就选用LinkedHashMap

HashTable

转载这里

HashMap和Hashtable的区别

  • HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)。
    HashMap是非synchronized,而Hashtable是synchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
  • 另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是Enumeration和Iterator的区别。
  • 由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过Hashtable。
    HashMap不能保证随着时间的推移Map中的元素次序是不变的。

要注意的一些重要术语:

  1. sychronized意味着在一次仅有一个线程能够更改Hashtable。就是说任何线程要更新Hashtable时要首先获得同步锁,其它线程要等到同步锁被释放之后才能再次获得同步锁更新Hashtable。
  2. Fail-safe和iterator迭代器相关。如果某个集合对象创建了Iterator或者ListIterator,然后其它的线程试图“结构上”更改集合对象,将会抛出ConcurrentModificationException异常。但其它线程可以通过set()方法更改集合对象是允许的,因为这并没有从“结构上”更改集合。但是假如已经从结构上进行了更改,再调用set()方法,将会抛出IllegalArgumentException异常。
  3. 结构上的更改指的是删除或者插入一个元素,这样会影响到map的结构。

java集合---Queue

发表于 2019-03-30 | 分类于 java

如图:

Queue

Queue

Queue是一个队列接口

Deque

Deque是Queue的子接口,Queue是一种队列形式,而Deque则是双向队列,它支持从两个端点方向检索和插入元素,
因此Deque既可以支持LIFO(后进先出)形式也可以支持FIFO(先进先出)形式.Deque接口是一种比Stack和Vector更为丰富的抽象数据形式,
因为它同时实现了以上两者.

AbstractQueue

AbstractQueue是Queue的抽象类,它实现了下面这些方法

1
2
3
4
5
add(E e);
remove();
element();
clear();
addAll(Collection<? extends E> c);

PriorityQueue

PriorityQueue根据优先级来来储存元素,优先级根据Comparable或Comparator来决定(使用哪个取决于使用哪个
构造方法)

ArrayQueue

ArrayQueue是用数组实现的队列,它实现了Deque接口。ArrayQueue里面不允许放入null,并且它不是线程安全的。

LinkedList

见这里

1234…7
lichukuan

lichukuan

62 博客
12 分类
© 2019 lichukuan
由 Hexo 强力驱动
|
主题 — NexT.Pisces v5.1.4