背景介绍
- 本文主要是对Java后端日常开发常用的知识做一些整理,以便使用时查看。
Java8 Lambda编程
- 为什么lambda表达式使用的局部变量要是final的
- 为什么 Lambda 表达式(匿名类) 不能访问非 final 的局部变量呢?
- 因为实例变量存在堆中,而局部变量是在栈上分配,Lambda 表达(匿名类) 会在另一个线程中执行。如果在线程中要直接访问一个局部变量,可能线程执行时该局部变量已经被销毁了,而 final 类型的局部变量在 Lambda 表达式(匿名类) 中其实是局部变量的一个拷贝,因为之前的存放在栈中(lambda表达式在其他线程运行的时候可能栈已经释放了),所以必须要拷贝出一份来。
- 在Lambda表达式中可以捕获静态变量和实例变量,但是如果想要捕获局部变量的时候就需要声明成final的,即使我们不主动声明,编译器也会为我们自动声明成final的,不能再重新赋值。也就是Lambda表达式中访问的局部变量(隐式被声明为final的)是可读不可写的,但是Lambda表达式中访问的实例变量和静态变量是可读可写的。
- java8将list转为map
public Map<Long, String> getIdNameMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getUserId, Account::getUsername));
}
- 将实体对象转换成Map
public Map<Long, Account> getIdAccountMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getId, account -> account));
}
public Map<Long, Account> getIdAccountMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getId, Function.identity()));
}
- 将List转换为Map,其中Map的key为指定的,value为List对象
Map<String,List<VehicleVO>> vOMap = vehicleVOList.stream()
.collect(Collectors.groupingBy(XXXVO::getPlateNo));
- filter筛选条件
//过滤出并返回符合filter筛选条件的;
list.stream().filter(x->x.indexOf(keyword) >= 0).collect(Collectors.toList());
- 指定具体收集的map
public Map<String, Account> getNameAccountMap(List<Account> accounts) {
return accounts.stream().collect(Collectors.toMap(Account::getUsername, Function.identity(), (key1, key2) -> key2, LinkedHashMap::new));
}
toMap
还有另一个重载方法,可以指定一个Map的具体实现,来收集数据:
- 对List中的元素进行分组
Map<String,List<TestVO>> result = testList.stream().collect(Collectors.groupingBy(e->e.getName()));
- 移除List中的重复元素
protected final <T> List<T> removeDuplicates(List<T> list) {
return new ArrayList(new LinkedHashSet(list));
}
- 获取List中的最小值/最大值
String minStartValidTimeStr = testList.stream().min(String::compareTo).get();
Lambda
表达式中解决计数的问题
/**
* 定义外部计数类
*/
class CountOuter {
private int i = 0;
private final List<String> testList = new ArrayList<>();
public Integer count() {
return i++;
}
public Integer getCount() {
return i;
}
public void addElementToList(String testId) {
testList .add(testId);
}
public List<String> getList() {
return testList;
}
}
在代码中使用上述外部类:
CountOuter countOuter = new CountOuter();
totalMap.put(tet.getPlateNo(),countOuter.getCount());
List<String> testIdList = countOuter.getList();
Gson使用
- 使用
gson
使用gson
将json字符串转换为List<Map<String,object>>
数组
List<Map<String, Object>> resultList= new Gson().fromJson(objListStr, new TypeToken<List<Map<String, Object>>>(){}.getType());
- Gson创建
Gson gson1 = new GsonBuilder().setPrettyPrinting().serializeNulls().create();
Foo foo = new Foo();
String json1 = gson1.toJson(foo);
- Gson使用示例
public class Sample {
public static void main(String[] args) {
String json = "{\"nestedPojo\":[{\"name\":null, \"value\":42}]}";
Gson gson = new Gson();
gson.fromJson(json, Pojo.class);
}
}
class Pojo {
NestedPojo nestedPojo;
}
class NestedPojo {
String name;
int value;
}
{
"nestedPojo": [
{
"name": null,
"value": 42
}
]
}
class Custom implements JsonDeserializer<NestedPojo> {
@Override
public NestedPojo deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
NestedPojo nestedPojo = new NestedPojo();
JsonArray jsonArray = json.getAsJsonArray();
if (jsonArray.size() != 1) {
throw new IllegalStateException("unexpected json");
}
JsonObject jsonObject = jsonArray.get(0).getAsJsonObject(); // get only element
JsonElement jsonElement = jsonObject.get("name");
if (!jsonElement.isJsonNull()) {
nestedPojo.name = jsonElement.getAsString();
}
nestedPojo.value = jsonObject.get("value").getAsInt();
return nestedPojo;
}
}
Gson gson = new GsonBuilder().registerTypeAdapter(NestedPojo.class, new Custom()).create();
Jackson中ObjectMapper属性设置
ObjectMapper om = new ObjectMapper();
// 属性为Null的不进行序列化,只对pojo起作用,对map和list不起作用
om.setSerializationInclusion(JsonInclude.Include.NON_NULL);
// json进行换行缩进等操作
om.enable(SerializationFeature.INDENT_OUTPUT);
// json不进行换行缩进等操作 默认就是不进行操作,写了这行和没写的效果一样
om.disable(SerializationFeature.INDENT_OUTPUT);
// json是否允许属性名没有引号 ,默认是false
om.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
//json是否允许属性名为单引号 ,默认是false
om.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
// 遇到未知属性是否抛出异常 ,默认是抛出异常的
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 当实体类没有setter方法时,序列化不报错,返回一个空对象
om.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
// 所有的字母小写,下划线作为名字之间分隔符,例如 snake_case
om.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
// 所有名字(包括第一个字符)都以大写字母开头,后跟小写字母,没有分隔符,例如 UpperCamelCase
om.setPropertyNamingStrategy(PropertyNamingStrategy.UPPER_CAMEL_CASE);
// 第一个单词以小写字母开头,后续每个单词都是大写字母开头,没有分隔符,例如 lowerCamelCase
om.setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE);
// 所有的字母小写,没有分隔符,例如 lowercase
om.setPropertyNamingStrategy(PropertyNamingStrategy.LOWER_CASE);
// “Lisp” 风格,采用小写字母、连字符作为分隔符,例如 “lower-case” 或 “first-name”
om.setPropertyNamingStrategy(PropertyNamingStrategy.KEBAB_CASE);
- 使用jackson时过滤null值
- 方法一:使用yml配置方式
spring:
jackson:
default-property-inclusion: non_null
- 方法二:使用Bean注入方式配置
@Configuration
public class MyJacksonConfig {
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder)
{
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
// 通过该方法对mapper对象进行设置,所有序列化的对象都将按改规则进行系列化
// Include.Include.ALWAYS 默认
// Include.NON_DEFAULT 属性为默认值不序列化
// Include.NON_EMPTY 属性为 空("") 或者为 NULL 都不序列化,则返回的json是没有这个字段的。这样对移动端会更省流量
// Include.NON_NULL 属性为NULL 不序列化
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
// 允许出现单引号
objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
return objectMapper;
}
}
Java知识整理
BigDecimal
相加
BigDecimal b1 = BigDecimal.ZERO;
BigDecimal b2 = BigDecimal.valueOf(3);
b2 = b2.add(b1); // 这样写的话会把新的对象赋值给b2
b2.add(b1); // 这样的话b2是不会变化的
Java8日期使用
- java8字符串转LocalDateTime
String param;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
LocalDateTime nowDate = LocalDateTime.parse(param,formatter);
- 计算两个LocalDateTime的时间差
LocalDateTime nowDate = LocalDateTime.now();
int interval = 5;
LocalDateTime before3HourOfNow = nowDate.minus(interval, ChronoUnit.HOURS);
long hoursInterval = ChronoUnit.HOURS.between( before3HourOfNow,nowDate);
Duration duration = Duration.between(before3HourOfNow, before3HourOfNow);
duration.toDays();
序列化/反序列化
- 被
transient
关键字修饰的属性不会被序列化,static
属性也不会被序列化.
泛型
-
泛型方法的概念
方法的参数类型是泛型,而不是具体的参数。
注意:是方法的参数是泛型,而不是方法的返回值。 -
泛型方法的定义格式
[访问权限] <泛型> 返回值类型 方法名([泛型标识 参数名称]){方法体;}
- 泛型方法实例
public <T2> void printArray(T2[] arr){}
以上方法就是泛型方法,方法的参数是一个数组,但是这个数组存放的数据类型不是具体的,而是一个泛型。另外,因为这个T2泛型是我们自己定义的,不是Java官方的。
所以为了表示T2也是一种泛型标识,需要在方法的前面也需要添加泛型,用于告诉编译器T2表示泛型。
- 注:泛型的类型参数使用大写形式,且比较短,一般一个字母,这是很常见的。在java库中,使用变量
E
表示集合的元素类型。K
和V
分别表示键与值的类型,比如:Map
的键与值。T
(需要时还可以用临近的字母U
和S
)表示“任意类型”。
CURL 用法整理
- 发送
POST
请求
curl -H "Content-Type: application/json"
-X POST
-d '{"user_id": "123", "coin":100, "success":1, "msg":"OK!" }'
"http://<HTTP地址>:8001/test" -v
参数 内容
-H
请求头
-d POST
内容
-X
请求协议
- 使用示例
详细的请求信息,需要加上-v
参数 - 使用示例
curl -H "token:test string" -X GET https://test address?lng=11.67&lat=39.5254
curl的请求头中有多个参数:
curl -H "token:xxxx,map:true" -X GET http://test.aaa.com/bbb?location=120.88,41.99
curl -H "Content-Type:application/json" -H "Authorization:aaa" -X POST --data '{"number":"京F58XX","startTime":"2021-05-13 00:32:27","endTime":"2021-05-15 00:32:27"}' http://xxxx.com/xxxx/common/xxxx
查看建表语句
show create table 表名;
show full COLUMNS from 表名;
describe 表名;
反射
- 获取反射中的
Class
对象- 在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的
Class
对象。
在Java API
中,获取Class
类对象有三种方法:
- 在反射中,要获取一个类或调用一个类的方法,我们首先需要获取到该类的
- 第一种,使用
Class.forName
静态方法。当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。
Class clz = Class.forName("java.lang.String");
- 第二种,使用
.class
方法。这种方法只适合在编译前就知道操作的Class
。
Class clz = String.class;
- 第三种,使用类对象的
getClass()
方法。
String str = new String("Hello");
Class clz = str.getClass();
- 通过反射创建类对象
通过反射创建类对象主要有两种方式:- 通过
Class
对象的newInstance()
方法、 - 通过
Constructor
对象的newInstance()
方法。
- 通过
- 第一种:通过
Class
对象的newInstance()
方法。
Class clz = Apple.class;
Apple apple = (Apple)clz.newInstance();
- 第二种:通过
Constructor
对象的newInstance()
方法
Class clz = Apple.class;
Constructor constructor = clz.getConstructor();
Apple apple = (Apple)constructor.newInstance();
- 通过
Constructor
对象创建类对象可以选择特定构造方法,而通过Class
对象则只能使用默认的无参数构造方法。下面的代码就调用了一个有参数的构造方法进行了类对象的初始化。
Class clz = Apple.class;
Constructor constructor = clz.getConstructor(String.class, int.class);
Apple apple = (Apple)constructor.newInstance("红富士", 15);
- 与获取类属性一样,当我们去获取类方法、类构造器时,如果要获取私有方法或私有构造器,则必须使用有
declared
关键字的方法。 - 反射获取method对象示例
Class clz = Class.forName("目录名.Apple");
//获取类中指定名称对应的method对象
Method method = clz.getMethod("setPrice", int.class);
Constructor constructor = clz.getConstructor();
Object object = constructor.newInstance();
method.invoke(object, 4);
public void setPrice(int price) {
this.price = price;
}
Method method = clz.getMethod("setPrice", int.class);
method.invoke(object, 4); //就是这里的invoke方法
注意:getMethod
方法中的第一个参数为方法名称,第二个参数表示获取的方法的入参类型
判断两个包装类型是否相等
// 2)两个包装类型
Integer c = 100;
Integer d = 100;
System.out.println(c == d); //结果:true
// 3)
c = 200;
d = 200;
System.out.println(c == d); //结果:false
Integer
中:-128 到 127 之间的数会从 IntegerCache
中取,然后比较,所以第二段代码(100 在这个范围之内)的结果是 true
,而第三段代码(200 不在这个范围之内,所以 new 出来了两个 Integer
对象)的结果是 false
。