JAVA 基础

JAVA 核心基础功能介绍

JAVA是一个面向对象的编程语言,面向对象就是一种抽象开发的思想包含封装、继承、多态,去掉这些概念也就是面向过程。应该是最初很多程序员使用面向过程编程,总结各种经验,对编程过程进行各种改进抽象出的更接近现实世界的最佳实鉴。通过面向对象有助与复杂系统的开发,有效提高编程效率。

  • 抽象 将一类实体的共同特性抽象出来,封装在一个抽象类中
  • 封装 将抽象得到的数据和行为(或功能)相结合,形成一个有机的整体,也就是将数据与操作数据的源代码进行有机的结合,形成“类”,其中数据和函数都是类的成员。封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法。
  • 继承 在一个现有类型的基础上,通过增加新的方法或者重定义已有方法的方式(重载/重写),产生一个新的类型。
  • 多态 通过传递给父类对象引用不同的子类对象从而表现出不同的行为,多态可为程序提供更好的可扩展性,同样也可以代码重用。

类直接的关系:

  1. 继承
  2. 实现
  3. 依赖 一个类A使用到了类B,属于非常弱的关系。a.append(B b)。UML中虚线箭头
  4. 关联 强依赖,长期的关系。B是A的成员变量public class A { private B b = new B();}。UML中实线箭头
  5. 聚合 整体与部分的关系,has-a,是可以分离的,各自有各自的生命周期。比如公司与员工。UML中空心菱形加实线箭头
  6. 组合 是contains-a的关系,比关系比聚合强就是强聚合。整体和部分不可分割。比如人和手。UML中实心菱形加实线箭头

基础写法

循环

for循环目前有4种写法

1
2
3
4
for (int i=0; i<100; i++) {}
for (String s : list) {}
items.forEach((k,v)->{...});
items.stream().filter(s->s.contains("B")).forEach(System.out::println);
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
for (int i=0; i<100; i++) {
// break 退出循环
// continue 跳过单次循环
}

//lambada表达式中foreach使用break
List<String> c = Arrays.asList("1", "2", "3", "4");
c.stream().forEach(str ->{
if("3".equals(str)) {
return; //跳出当前循环,继续下一轮
}
System.out.println(str);
});
//lambada表达式中foreach抛异常退出循环
List<String> c = Arrays.asList("1", "2", "3", "4");
try {
c.stream().forEach(str ->{
if("3".equals(str)) {
throw new RuntimeException();
}
System.out.println(str);
});
} catch (Exception e) {

}

常用函数

空值判断,最常见的问题之一

1
2
3
4
String[] photo = new String[0]; // 初始化
if (camera.getImages()!=null && camera.getImages().length()>0) {
photo = camera.getImages().split(",");
}

类型转换

  • String.valueOf() Integer.parseInt(String s)
  • 错误用法:强制转换 (String)data //Cannot cast from Integer to String

split

注意: . 、 | 和 * 等转义字符,必须得加 \\

注意:多个分隔符,可以用 | 作为连字符。

public String[] split(String regex, int limit)

regex – 正则表达式分隔符。

limit – 分割的份数

split(" ", -1):

  1. 如果字符串最后一位有值则没有区别
  2. 如果最后N位都是分隔符,split(" ")不会继续切分,split(" ", -1)会继续切分。
  3. split(" ")不会保留空值,但是split(" ")会保留空值
  4. 具体说明可以参看java API文档说明,通过开发工具可以直接查看注释
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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public class StringTest {
public static void split() {
String split = "a b c d ";// String split = "a_b_c_d____";//还可以用这个字符串试验split.split("_", -1);
String [] splitArray = split.split(" ");
for(int i=0;i<splitArray.length;i++){
System.out.println(i+"="+splitArray[i]);
}
System.out.println("----------");
String [] splitArrayLimit = split.split(" ", -1);
for(int i=0;i<splitArrayLimit.length;i++){
System.out.println(i+"="+splitArrayLimit[i]);
}
}
public static void splitWithEnd() {
String split = "a b c d d";
String [] splitArray = split.split(" ");
for(int i=0;i<splitArray.length;i++){
System.out.println(i+"="+splitArray[i]);
}
System.out.println("----------");
String [] splitArrayLimit = split.split(" ", -1);
for(int i=0;i<splitArrayLimit.length;i++){
System.out.println(i+"="+splitArrayLimit[i]);
}
}
public static void main(String[] args) {
split();
System.out.println("==========");
splitWithEnd();
}
}
执行结果
0=a
1=b
2=c
3=d
----------
0=a
1=b
2=c
3=d
4=
5=
6=
7=
==========
0=a
1=b
2=c
3=d
4=
5=
6=
7=d
----------
0=a
1=b
2=c
3=d
4=
5=
6=
7=d

public static void main(String[] rags) {
String jsonData = "a,b,c,";
System.out.println(jsonData.split(",").length);
System.out.println(jsonData.split(",", 0).length);
System.out.println(jsonData.split(",", 1).length);
System.out.println(jsonData.split(",", -1).length);
}
-----
3
3
1
4

反射

Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类:

  • Field :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段;
  • Method :可以使用 invoke() 方法调用与 Method 对象关联的方法;
  • Constructor :可以用 Constructor 创建新的对象。

Method invoke()调用的例子:

1
2
3
4
5
6
7
8
9
// 1.获取字节码对象
Class<ReflectTest> clazz = (Class<ReflectTest>) Class.forName("vip.techinfo.reflect.ReflectTest");
// 2.获取一个对象
Constructor con = clazz.getConstructor();
ReflectTest intance = (ReflectTest) con.newInstance();
// 3.获取setFF Method对象
Method method = clazz.getMethod("setFF", String.class);
// 4.调用invoke方法来调用
method.invoke(intance, "abc");

List

LinkedList

  • jdk1.6 双向循环列表
  • jdk1.7 去掉了Head节点 变变成双向链表 但是有first last指针方便对头尾插入删除操作,节省了header节点空间
  • 适合频繁插入删除

ArrayList

  • 基于动态数组数组
  • 可以通过下标定位
  • 随机访问 get set优于LinkedList,LinkedList需要移动指针
  • 插入效率没有LinkedList高

demo

1
List<TreeNode> tmpList = new ArrayList<>(treeNodeMap.values()); // Map转List values()

Set

具有去重功能,无序。
需要重写equals()与hashCode()方法,Set集合判断对象是否先等用到这两个方法(相当于通过hashcode生成箱子的编号),可以让工具自动生成

Object的代码:

1
2
3
4
5
6
7
8
9
10
public String toString () {
return this.getClass().getName() + "@" + Integer.toHexString(this.hashCode());
}

public boolean equals (Object o) {
return this == o;
}

// 内部方法生成hashCode,常规情况与地址无关
public native int hashCode();

hashCode() 方法用于返回字符串的哈希码。
字符串对象的哈希码根据以下公式计算:
s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]
从实现来说,一般的HashCode方法会这样:
return Attribute1.HashCode() + Attribute2.HashCode()…[+super.HashCode()]

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
TestObject that = (TestObject) o;
return Objects.equals(path, that.path);
}

@Override
public int hashCode() {

return Objects.hash(path);
}

TreeMap

  1. 存入TreeMap的键值对的key是要能自然排序的(实现了Comparable接口),否则就要自定义一个比较器Comparator作为参数传入构造函数。
  2. TreeMap是以红黑树将数据组织在一起,在添加或者删除节点的时候有可能将红黑树的结构破坏了,所以需要判断是否对红黑树进行修复。
  3. 由于底层是红黑树结构(一种自平衡二叉查找树),所以TreeMap的基本操作 containsKey、get、put 和 remove 的时间复杂度是O(log n)。
  4. 由于TreeMap实现了NavigableMap,所以TreeMap有一系列的导航方法。

时间复杂度对比

TreeMap基于红黑树(一种自平衡二叉查找树)实现的是一个有序集合,时间复杂度平均能达到O(log n)。
hashSet(底层通过HashMap实现),hashtable,HashMap是基于散列表实现的,时间复杂度平均能达到O(1),最差O(n)。
ConcurrentSkipListMap是基于跳表实现的,时间复杂度平均能达到O(log n)。
数组 查询的时间复杂度 O(n)
链表 查询的时间复杂度 O(n)
TreeSet O(log(n)) TreeSet是基于TreeMap实现的是一个有序集合,同HashSet、LinkedHashSet一样,它使用TreeMap的键。TreeSet 搜索都是基于TreeMap红黑树搜索。TreeSet中的元素要实现Comparable接口

给定值的比较次数等于给定值节点在二叉排序树中的层数。如果二叉排序树是平衡的,则n个节点的二叉排序树的高度为Log2(n+1),其查找效率为O(Log2n),近似于折半查找。如果二叉排序树完全不平衡,则其深度可达到n,查找效率为O(n),退化为顺序查找。一般的,二叉排序树的查找性能在O(Log2n)到O(n)之间。因此,为了获得较好的查找性能,就要构造一棵平衡的二叉排序树。平衡二叉树 查找O(logn) ,二叉查找树 查找最坏O(n)

关键字

  • transient关键字 实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化.transient关键字只能修饰变量,而不能修饰方法和类
  • legth size() length是数组的一个属性,在获取值的时候是按属性的方法获取。而size()是链表的一个方法,用于获取链表的长度
  • <<左移动 >>右移动相当于除以2 ~ 取反
  • static 静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化,按照定义的顺序,顺序初始化,调用内部类的静态成员变量,会先执行静态代码块。静态代码块:全局,不管new多少次,只会初始化1次

选择单例还是static

只有加载资源/需要初始化并比较慢的类,才去使用单例。单例:多方法,有状态。静态方法:工具,无状态(也能支持有状态),纯函数。其实全局静态变量(类)就是单例模式在语言层面上的一种实现。static变量使用的时候是可以多个类定义相同的对象的。static是在容器加载的时候就已经加载到内存中,所以static方法和变量不宜过度使用,有选择的使用,如果一个方法和他所在类的实例对象无关,那么它就应该是静态的,否则就应该是非静态。因此像工具类,一般都是静态的。如果我们确实应该使用非静态的方法,但是在创建类时又确实只需要维护一份实例时,就需要用单例模式了。从功能上讲:单例模式可以控制单例数量;可以进行有意义的派生;对实例的创建有更自由的控制;比如加载的配置信息,从面向对象的角度考虑应该使用单例模式。
连接池可以使用单例,是说连接池只有一个,池里面的链接可以有多个比如30个。还有Service层与dao层,service层要频繁调用dao层,但是不需要每次都new,dao层没有跟对象相关的值变量,可以用单例实现dao,spring就是这么管理的,dao可以去连接池里取一个链接进行访问数据库,单例支持多线程访问要实现同步得加synchronized

具体使用可以参考:Springboot、Mybatis、Netty等开业框架源码
Netty:ByteToMessageDecoder.MERGE_CUMULATOR
Mybatisplus:

1
2
3
4
SqlCondition 
public static final String EQUAL = "%s=#{%s}";
// SqlHelper(工具类);
MybatisMapperRefresh MybatisMapperRefresh.jarMapper

注意:new一个对象里面的成员变量也会被new一份出来,虽然里面成员变量使用了单例创建但是外层对象new一下还是会导致创建多个不同成员对象。线程比较多的程序可用通过dump查看内存状态

static test

A:随着类的加载而加载
B:优先与对象存在
C:被类的所有对象共享
D:可以通过类名调用

static 随着类的初始化而初始化,随着类的销毁而销毁。 静态变量:方法区的静态区, 成员变量:堆内存。静态变量:属于类,类变量, 成员变量:属于对象,对象变量,实例变量。
修改static值后其他对象获取该值也一样跟着修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class StaticTest {
public static String a = "1";
public static String b = "2";
public String getA() {
return a;
}
public void setA(String value) {
a = value;
}
public static void main(String[] args) {
StaticTest staticTest1 = new StaticTest();
StaticTest staticTest2 = new StaticTest();
staticTest1.setA("abc");
System.out.println(staticTest1.getA());
System.out.println(staticTest2.getA());
}
}
/* result:
abc
abc
*/

递归

递归边界,递归公式
递归次数过多容易造成栈溢出

尾递归可以复用栈帧

回调的写法

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
public interface Callback {
abstract void callback(String result);
}
public class CallbackThread implements Runnable {
boolean stop = false;
ShellCallback callback = null;
CallbackThread(Callback callback) {
this.callback = callback;
}
@Override
public void run() {
String result = "abc";
callback.callback(result);
}
}
public class Test { // 如果是通过new Test()创建多个对象,那么这个对象里面的单线程线程池也会有多个,否则就只有一个线程
public static void main(String[] args) throws InterruptedException {
CallbackThread callbackThread = new CallbackThread(new Callback(){ // 在调用的时候设置回调方法执行方式
@Override
public void callback(String result) {
System.out.println(result)
}
});
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("thread-call-runner-%d").build();
ExecutorService singleThreadExecutor = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(), namedThreadFactory);
singleThreadExecutor.execute(callbackThread);
//singleThreadExecutor.execute(callbackThread2); 写多份也只会有一个线程
}
}

正则表达式

匹配出.数字.部分内容

1
2
3
4
5
6
String str = "a.b.32.cd.323.f";
Pattern pattern = Pattern.compile("\\.\\d+\\.");
Matcher matcher = pattern.matcher(str);
while(matcher.find()) {
System.out.println(matcher.group());
}

IO

写文件最靠谱的做法

1
2
3
4
5
6
7
8
9
10
11
public static void saveStreamToFile(InputStream in, File file) throws IOException {
FileOutputStream out = new FileOutputStream(file);
int len = 0;
byte[] b = new byte[1024];
while ((len = in.read(b)) != -1) {
out.write(b, 0, len);
}
out.close();
in.close();
}
// 还可以直接用 org.apache.commons.io.FileUtils.copyInputStreamToFile(request.getInputStream(), file);

java WEB 部署

注意: 部署多个tomcat 8080访问端口跟关闭端口都要改。

概念理解

值传递与引用传递

新建Test.java

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
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/**
* 值传递: 在方法被调用时,实参通过形参把它的内容副本传入方法内部,此时形参接收到的内容是实参值的一个拷贝,
* 因此在方法内对形参的任何操作,都仅仅是对这个副本的操作,不影响原始值的内容。
*
*/
public class Test {
public static void change(int a) {
a = 20; // 形参
System.out.println(a);
}

public static void main(String[] args) {
int a = 10; // 实参
change(a);
System.out.println(a);
}
}

/***
* 执行结果
* 20
* 10
*/

/**
* 引用传递: "引用"也就是指向真实内容的地址值,在方法调用时,实参的地址通过方法调用被传递给相应的形参,在方法体内,
* 形参和实参指向同一快内存地址,对形参的操作会影响真实内容。
*
* 如果是对基本数据类型的数据进行操作,由于原始内容和副本都是存储实际值,并且是在不同的栈区,因此形参的操作,不影响原始内容。
* 如果是对引用类型的数据进行操作,分两种情况,一种是形参和实参保持指向同一个对象地址,则形参的操作,会影响实参指向的对象的内容。
* 一种是形参被改动指向新的对象地址(如重新赋值引用),则形参的操作,不会影响实参指向的对象的内容。
*/
class Test2 {

public static void main(String[] args) {
Dog dog = new Dog(); // 实参
Change.dogToNull(dog);
dog.eat();// 會出現空指針異常嗎?
System.out.println(dog.getAge());
}
}

class Dog {

int age = 0;

void eat() {
System.out.println("dog eat()");
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}

class Change {

static void dogToNull(Dog dog) { // 会创建形参→指向实参传入的地址
dog.setAge(5); // 形参与实参指向相同地址空间,内容会同时改变
dog = null; // 改变形参数指向的地址为空,不影响实参
}
}

/***
* 执行结果:不会报空指针异常
* eat()
* 5
*/

class Person {

private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

}

/**
* 无论是基本类型和是引用类型,在实参传入形参时,都是值传递,也就是说传递的都是一个副本,而不是内容本身。
* 引用传递,在Java中并不存在
*
*/
class Test3 {

public static void change(Person person) {
System.out.println("传入的person的name:" + person.getName());
person.setName("我是小白");
System.out.println("方法内重新赋值后的name:" + person.getName());
}

public static void change2(Person person) {
System.out.println("传入的person的name:" + person.getName());
person = new Person();// 加多此行代码,并不会改变实参的地址指向.引用传递,在Java中并不存在
person.setName("我是小白");
System.out.println("方法内重新赋值后的name:" + person.getName());
}

// 测试
public static void main(String[] args) {
Person p = new Person(); // 实参
p.setName("我是小明");
change(p); // change2(p);
System.out.println("方法执行后的name:" + p.getName());
}
}

重写

子类重写父类方法的时候还可以继续调用父类的方法

1
2
3
4
5
6
7
@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("Transport error in " + session, exception);
}
super.handleTransportError(session, exception); // 调用父类的方法
}

开发中可能会遇到的处理

用整数表示下拉框多个值

比如 3的二进制11 代表 1,2 。每个二进制位可以代表一个下拉框输入项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 处理方式循环二进制字符串位数,把各个位数非0的数值计算出来. 1*2 + 1*1 → '2,1'
public String numHandlingCheckBox(String str) {
if (str != null) {
Integer value = Integer.parseInt(str);
String display = Integer.toBinaryString(value);
String resultValue = "";
for (int i=0; i<display.length(); i++) {
int oper = Integer.parseInt(String.valueOf(display.charAt(i))) * (int)Math.pow(2, display.length()-1-i);
if(oper != 0) {
resultValue = resultValue + oper + ",";
}
}
return resultValue;
}
return null;
}

运行

jar包运行方式

  1. java -jar xxx.jar 。 需要jar包中manifest.mf文件包含入口类内容 Main-Class: xxx.xx.XxxMainClass
  2. .sh .bat 执行脚本。可以通过脚本配置各种启动参数环境变量,方便引用第三方包。例子:创建test.sh,lib目录,下面放入a.jar,b.jar,c.jar,启动AppMain.class可以放在其中jar中
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #!/bin/bash
    dir=`dirname $0`
    workpath=`cd $dir;pwd`
    JAVA_OPTS="-server"
    JAVA_OPTS="$JAVA_OPTS -Xmx1024m"
    JAVA_OPTS="$JAVA_OPTS -Duser.dir=$workpath"
    for oneJar in ./lib/*.jar
    do cp=$cp:$oneJar
    done
    java $JAVA_OPTS -classpath $cp vip.infotech.AppMain
    echo " =============================================================================="
    echo $dir
    echo $0
    echo $JAVA_OPTS
    echo $cp
    echo $oneJar
    echo "java" $JAVA_OPTS "-classpath" $cp "vip.infotech.AppMain"
    运行结果
    1
    2
    3
    4
    5
    6
    7
    ==============================================================================
    .
    ./test.sh
    -server -Xmx1024m -Duser.dir=/root
    :./lib/a.jar:./lib/b.jar:./lib/c.jar
    ./lib/c.jar
    java -server -Xmx1024m -Duser.dir=/root -classpath :./lib/a.jar:./lib/b.jar:./lib/c.jar vip.infotech.AppMain

war

web项目tomcat下用的是war包。可以用7zip压缩包工具更新war包中的文件。

新特性

声明式编程

类似sql语句。

1
2
3
int count = students.stream()
.filter(s -> s.getAge<18)
.count();

其他功能

  • Runtime.getRuntime().addShutdownHook(shutdownHook);当jvm关闭的时候,会执行系统中已经设置的所有通过方法addShutdownHook添加的钩子,当系统执行完这些钩子后,jvm才会关闭。所以这些钩子可以在jvm关闭的时候进行内存清理、对象销毁等操作
  • 实体类类型统一,比如对于数据库bigint使用Long类型存储,其他整型都统一使用Integer

JNI

JNI是Java Native Interface的缩写,通过使用 Java本地接口书写程序,可以确保代码在不同的平台上方便移植。

JNI调用姿势:Java —> JNI —> C/C++(SO库)

如果用eclipse,需将dll或so文件放在项目下,而不是src及其子目录下。
如果用命令行编译,把dll文件放在该包的同目录下。

常见代码有:

1
2
3
4
5
6
7
8
9
10
11
public class Object {
public native int hashCode();
}

public class RandomAccessFile implements DataOutput, DataInput, Closeable {
private native int read0() throws IOException;
}

class Thread implements Runnable {
private native void start0();
}

1、在Java中声明native()方法,然后编译;
2、用javah产生一个.h文件;
3、写一个.cpp文件实现native导出方法,其中需要包含第二步产生的.h文件(注意其中又包含了JDK带的jni.h文件);
4、将第三步的.cpp文件编译成动态链接库文件;
5、在Java中用System.loadLibrary()方法加载第四步产生的动态链接库文件,这个native()方法就可以在Java中被访问了。

JAVA本地方法适用的情况:
1.为了使用底层的主机平台的某个特性,而这个特性不能通过JAVA API访问
2.为了访问一个老的系统或者使用一个已有的库,而这个系统或这个库不是用JAVA编写的
3.为了加快程序的性能,而将一段时间敏感的代码作为本地方法实现。
4.也就是与Java程序外交互、与操作系统交互

异常可能原因

栈溢出

调用层级过深或者递归调用没有正常退出,函数无限循环调用自己等

gc overhead limit exceeded

由于数据库问题,业务积压也可能导致内存不够用。也就是本来业务处理完内存是要释放的,由于某些原因导致业务无法完全执行完毕,新业务有过来处理。
while循环创建对象?
读取大文件
-XX:-UseGCOverheadLimit 延后出错时间

java.net.SocketTimeoutException: Read timed out

增加超时时长
删掉超时时长配置

tomcat配置,增加disableUploadTimeout="true"或者keepAliveTimeout="100000"

1
2
3
4
5
6
<!--disableUploadTimeout默认值是false-->
<Connector executor="tomcatThreadPool"
port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
keepAliveTimeout="100000"
redirectPort="8443" />

集合工具类

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
/**
* lambada test
*/
public class FilterUtils {
/**
* 从map中根据key模糊匹配map项
*/
public static Map<String, Object> parseMapForFilter(Map<String, Object> map, String filters) {
if (map == null) {
return null;
} else {
map = map.entrySet().stream()
.filter((e) -> e.getKey().contains(filters)) // contains实现就是indexOf,还可以用startsWith
.collect(Collectors.toMap(
(e) -> (String) e.getKey(),
(e) -> e.getValue()
));
}
return map;
}

/**
* 从map中根据key模糊匹配返回key Set
*/
public static Set<String> parseSetForFilter(Map<String, Object> map, String filters) {
Set set = null;
if (map == null) {
return null;
} else {
set = map.keySet().stream()
.filter(o -> o.contains(filters))
.collect(Collectors.toSet());
}
return (Set<String>) set;
}
public static void main(String[] args) {
Map<String,Object> params = new HashMap<>(16);
params.put("abc-123","123");
params.put("def-123","123");
Map<String, Object> map = parseMapForFilter(params, "a");
System.out.println(JSON.toJSONString(map));
Set<String> set = parseSetForFilter(params, "de");
System.out.println(JSON.toJSONString(set));
}
}

类的选择

String StringBuffer StringBuilder

如果是固定值的拼接可以使用String
如果是带循环的字符串不确定的使用StringBuffer或者StringBuilder

内存分析与排查

1
2
3
4
5
6
7
ps -ef|grep java #获取进程号
jps -l -m # JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。
jstat gc 15800 # jc统计,jstat -options 查看相关参数
jmap -heap 150988 # 堆使用情况
jmap -dump:live,format=b,file=dump.hprof 150988 #直接用MAT(Memory Anlysis Tool)打开
jstack -l 150988 # 查看线程快照
jinfo -flags 150988 # 查看虚拟机参数配置

扩展

参考