记录
- 1、Lombok 的 @Builder 注解
- 2、Lombok 的 @Accessors 注解
- 3、CharSequence
- 4、springboot 参数校验
- 5、Mybatis 方法单个参数
- 6、@Configuration 注解
- 7、Assert工具类
- 8、Optional类
- 9、LocalDate类的plusDays()方法
- 10、Mybatis 报错 Cause: org.apache.ibatis.type.TypeException: Error setting non null for parameter
- 11、Mybatis——foreach用法
- 12、Spring 常用注解@Configuration,@Bean,@Component,@Service,@Controller,@Repository,@Entity的区分与学习
- 13、sql的四种连接——左外连接、右外连接、内连接、全连接
- 14、mybatis 中的<![CDATA[ ]]>用法及说明
- 15、parallelStream() 并行流操作
- 16、Stream流的用法
- 17、mybatis 映射内部类
- 18、java判断字符串中是否包含某个字符
- 19、IDEA的Debug技巧
- 20、IntelliJ IDEA 推荐设置
- 21、IDEA常用细节
- 22、IntelliJ IDEA 常用快捷键讲解
- 23、Java如何判断两个字符串是否相等
- 24、Java字符串拼接
- 25、如何在Java中优雅地分割String字符串?
- 26、类型转换
- 27、Vector 和 Stack
- 28、ArrayList和LinkedList的区别(含时间复杂度)
- 29、JSON.parseArray使用详解
- 30、Queue、Deque、Stack相对应的接口区别
- 31、管道
- 32、编码和解码
- 33、try-catch-finally
- 34、Arrays.asList()方法
- 35、分隔字符串 StringUtils.split()
- 36、网络模型
- 37、BIO、NIO、AIO
- 38、Java 命名规范
- 39、拆箱和装箱
- 40、IDEA 调试技巧
- 41、IDEA插件
- 42、Cron表达式
- 43、
1、Lombok 的 @Builder 注解
@Builder 注释为类生成相对略微复杂的构建器API。@Builder 可以让你以下面显示的那样调用你的代码,来初始化实例对象:
Student.builder()
.sno( "001" )
.sname( "admin" )
.sage( 18 )
.sphone( "110" )
.build();
我们在对实体对象进行操作时,往往会存在对某些实体对象的某个字段进行二次赋值。@Builder 注解里有个 toBuilder() 方法,使用 toBuilder() 可以实现以一个实例为基础继续创建一个对象,也就是重用原来对象的值。但是,这会创建一个新的对象,而不是原来的对象,原来的对象属性是不可变的,除非你自己想要给这个实体类再添加上 @Data 或者 @Setter 方法。
2、Lombok 的 @Accessors 注解
(1)该注解主要作用是:当属性字段在生成 getter 和 setter 方法时,做一些相关的设置。
(2)当它可作用于类上时,修饰类中所有字段,当作用于具体字段时,只对该字段有效。
该字段共有三个属性,分别是 fluent,chain,prefix。
2.1 fluent 属性
不写默认为false,当该值为 true 时,对应字段的 getter 方法前面就没有 get,setter 方法就不会有 set。
2.2 chain 属性
不写默认为false,当该值为 true 时,对应字段的 setter 方法调用后,会返回当前对象。
2.3 prefix 属性
该属性是一个字符串数组,当该数组有值时,表示忽略字段中对应的前缀,生成对应的 getter 和 setter 方法。
比如现在有 xxName 字段和 yyAge 字段,xx 和 yy 分别是 name 字段和 age 字段的前缀。
那么,我们在生成的 getter 和 setter 方法如下,它也是带有 xx 和 yy 前缀的。
如果,我们把它的前缀加到 @Accessors 的属性值中,则可以像没有前缀那样,去调用字段的 getter和 setter 方法。
转载自:@Accessors 注解详解
3、CharSequence
CharSequence 在 java.lang 包下,是一个描述字符串结构的接口,表示 char 值的一个可读序列。此接口对许多不同种类的 char 序列提供统一的自读访问。此接口不修改 equals 和 hashCode 方法的常规协定,因此,通常未定义比较实现 CharSequence 的两个对象的结果。他有几个实现类:CharBuffer、String、StringBuffer、StringBuilder。
CharSequence 与 String 都能用于定义字符串,但 CharSequence 的值是可读可写序列,而 String 的值是只读序列。
对于抽象类或者接口来说不可以直接使用new的方式创建对象,但是可以直接给它赋值进行实例的创建:
CharSequence cs="hello";
参考自:CharSequence详情介绍
4、springboot 参数校验
实际业务开发中,为了避免入参错误对业务系统有影响,一般会都会在Controller层进行参数校验,常见的方式主要包含了两种:
get、delete等请求,参数形式为RequestParam/PathVariable
post、put等请求,参数形式为RequestBoty
RequestParam/PathVariable形式的参数校验
Get、Delete请求一般会使用RequestParam/PathVariable形式参数参数,这种形式的参数校验一般需要以下两个步骤,如果校验失败,会抛出ConstraintViolationException异常。
必须在Controller类上标注@Validated注解;
在接口参数前声明约束注解(如@NotBlank等)
@Validated
@RestController
@RequestMapping("/validate")
public class ValidationController {
private Logger log = LoggerFactory.getLogger(ValidationController.class);
/**
* get、delete请求使用requestParam/PathVariable形式传递参数的,
* 参数校验需要在Controller添加上@Validated注解,并在参数列表中添加对应的校验注解即可
*
* @param id ID
* @return true/false 成功或失败
*/
@GetMapping("/get")
public ResultObject<Boolean> validateGetRequest(@NotBlank(message = "id不能为空") String id,
@NotBlank(message = "appkey不能为空") String appkey) {
// 具体业务逻辑调用
log.info("id [{}] appKey [{}]", id, appkey);
return ResultObject.success();
}
}
转载自:springboot 参数校验
5、Mybatis 方法单个参数
正常情况下,mybatis 在进行参数判断的时候,直接用就可以了,使用 entity 实体或者 Map 的时候,下面的代码是正确的:
是单个参数和多参数的判断有个不同点,当我们的入参为 entity 实体,或者 map 的时候,使用 if 参数判断没任何问题。但是当我们的入参为 java.lang.Integer 或者 java.lang.String 的时候,这时候就需要注意一些事情了。
我们需要使用 if 参数判断 引入参数,会抛异常 nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter for property named ‘deptId’ in ‘class java.lang.String’
<select id="queryList" resultType="com.soft.back.model.OutdevFactory"
parameterType="java.lang.String">
select
<include refid="Base_Column_List"/>
from op_outdev_factory
<where>
<if test="factoryName != null">
and name like concat('%',#{factoryName},'%')
</if>
</where>
</select>
原因就是对于这类单个入参然后用 if 判断的,mybatis 有自己的内置对象,Mybatis 默认采用 OGNL 解析参数,所以会自动采用对象树的形式取 string.xxx 值,如果没在方法中定义,则会抛异常报错。
解决方案:
方案一:把 #{xxx} 修改为 #{_parameter}
<select id="queryList" resultType="com.soft.back.model.OutdevFactory"
parameterType="java.lang.String">
select
<include refid="Base_Column_List"/>
from op_outdev_factory
<where>
<if test="_parameter != null">
and name like concat('%',#{_parameter},'%')
</if>
</where>
</select>
方案二:在方法中提前定义
/**
* 查询厂家列表
* @param factoryName
* @return
*/
List<OutdevFactory> getFactoryList(@Param("factoryName") String factoryName);
<select id="queryList" resultType="com.soft.back.model.OutdevFactory"
parameterType="java.lang.String">
select
<include refid="Base_Column_List"/>
from op_outdev_factory
<where>
<if test="factoryName!= null">
and name like concat('%',#{factoryName},'%')
</if>
</where>
</select>
转载自:【Mybatis异常】nested exception is org.apache.ibatis.reflection.ReflectionException: There is no getter
6、@Configuration 注解
@Configuration,功能:将想要的组件添加到容器中。使用注解 @Configuration,告诉Spring Boot这是一个配置类。
在类里边使用 @bean 注解。@bean:给容器中添加组件,以方法名作为组件的id。返回类型为组件类型,返回的值,就是组件在容器中的实例。
在Spring Boot 5.2之后的@Configuration注解多了一个属性proxyBeanMethods,默认为true(翻译:代理bean的方法)
为true了,也就是默认代理。代理就会保持组件的单实例。
也就是说,虽然写的配置类是在容器中注册组件用的,但是在注册组件之前会在容器中查找有没有该组件。如果有,则取该组件用于保证单实例,如果没有再注册一个新的组件。
如果将@Configuration(proxyBeanMethods = true)改为false,每一个方法都会新注册一个组件,改为false后久不会再保存代理对象了。
proxyBeanMethods:代理bean的方法
有两种模式:
(1)Full:(proxyBeanMethods = true) //全模式
使用代理模式,保证组件的单实例,启动不如false快,但是重复利用率高,适用于会重复使用组件的场景。
(2)lite:(proxyBeanMethods = false) //轻量级
不是用代理模式,不用保证组件的单实例,启动最快。单每次调用组件都会重新创建一个新的组件,组件可重复使用率低。适用于需要组件但不会重复使用的场景
总结:用于解决组件依赖
转载自:@Configuration详解
7、Assert工具类
从JDK1.4版本开始,Java语言引入了断言(assert)机制。该类在 package org.springframework.util 包下。
断言常用的方法:
(1)notNull(Object object)当 object 为 null 时抛出异常,notNull(Object object, String message) 方法允许通过 message 定制异常信息。和 notNull() 方法断言规则相反的方法是 isNull(Object object) / isNull(Object object, String message),它要求入参一定是 null;
(2)isTrue(boolean expression) / isTrue(boolean expression, String message)当 expression 不为 true 抛出异常;
(3)notEmpty(Collection collection) / notEmpty(Collection collection, String message)当集合未包含元素时抛出异常。
notEmpty(Map map) / notEmpty(Map map, String message) 和 notEmpty(Object[] array, String message) / notEmpty(Object[] array, String message) 分别对 Map 和 Object[] 类型的入参进行判断;
8、Optional类
Optional类主要解决空指针异常NullPointerException。
Optional 类(java.util.Optional)是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
创建Optional 对象
声明一个空的Optional:
通过静态工厂方法Optional.empty(),创建一个空的Optional 对象,Optional中只有私有的构造方法,所以只能在其类中查找已经提供的创建方法。
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
Optional<Car> optCar = Optional.empty();
依据一个非空值创建Optional:
使用静态工厂方法Optional.of(T),依据一个非空值创建一个Optional对象:
public static <T> Optional<T> of(T value) {
return new Optional<>(value);
}
Optional<String> optCar = Optional.of("sdfs");
由于这种方法创建时,会new Optional(T t),而在new的过程中,私有的构造函数对其传入的变量有检验:
private Optional(T value) {
this.value = Objects.requireNonNull(value);
}
当传入的参数为 null 时会爆 NullPointerException
转载自:java8中的Optional
9、LocalDate类的plusDays()方法
plusDays() 方法用于将以天为单位的给定持续时间添加到此 LocalDate 并返回 LocalDate。此方法是一个非静态方法,只能通过类对象访问,如果尝试使用类名访问该方法,则会收到错误消息。
例子:
// Java program to demonstrate the example
// of plusDays(long day_val) method of LocalDate
import java.time.*;
public class PlusDaysOfLocalDate {
public static void main(String args[]) {
long days = 10;
// Instantiates two LocalDate
LocalDate l_da1 = LocalDate.parse("2007-04-04");
LocalDate l_da2 = LocalDate.of(2008, Month.FEBRUARY, 06);
// Display l_da1,l_da2 and days
System.out.println("LocalDate l_da1,l_da2 : ");
System.out.println("l_da1: " + l_da1);
System.out.println("l_da2: " + l_da2);
System.out.println("days to add: " + days);
System.out.println();
// Here, this method adds the given
// days to this date l_da1
// i.e. here we are adding 10
// days to the date l_da1
LocalDate plus_days = l_da1.plusDays(days);
// Display plus_days
System.out.println("l_da1.plusDays(days): " + plus_days);
// Here, this method adds the given
// days to this date l_da2
// i.e. here we are adding 10
// days to the date l_da2
plus_days = l_da2.plusDays(days);
// Display plus_days
System.out.println("l_da2.plusDays(days): " + plus_days);
}
}
输出结果:
LocalDate l_da1,l_da2 :
l_da1: 2007-04-04
l_da2: 2008-02-06
days to add: 10
l_da1.plusDays(days): 2007-04-14
l_da2.plusDays(days): 2008-02-16
转载自:Java LocalDate类 | plusDays()方法与示例
10、Mybatis 报错 Cause: org.apache.ibatis.type.TypeException: Error setting non null for parameter
Cause: org.apache.ibatis.type.TypeException: Error setting non null for parameter #2 wit
解决办法:把注释去掉
转载自:Mybatis 报错 Cause: org.apache.ibatis.type.TypeException: Error setting non null for parameter
11、Mybatis——foreach用法
foreach 元素的属性
(1)collection:要做 foreach 的对象,作为入参时,List<?> 对象默认用 list 代替作为键,数组对象有 array 代替作为键,Map 对象没有默认的键。
当然在作为入参时可以使用 @Param(“keyName”) 来设置键,设置 keyName 后,list、array 将会失效。 除了入参这种情况外,还有一种作为参数对象的某个字段的时候。举个例子:
如果 User 有属性 List ids。入参是 User 对象,那么这个 collection = “ids”
如果 User 有属性 Ids ids;其中 Ids 是个对象,Ids 有个属性 List id;入参是 User 对象,那么 collection = “ids.id”
上面只是举例,具体 collection 等于什么,就看你想对那个元素做循环。
该参数为必选。
(2)item: 循环体中的具体对象。支持属性的点路径访问,如item.age,item.info.details。
具体说明:在list和数组中是其中的对象,在map中是value。该参数为必选。
(3)index: 在list、array中,index为元素的序号索引。但是在Map中,index为遍历元素的key值,该参数为可选项;
(4)open: 遍历集合时的开始符号,通常与close=")"搭配使用。使用场景IN(),values()时,该参数为可选项;
(5)separator: 元素之间的分隔符,类比在IN()的时候,separator=“,”,最终所有遍历的元素将会以设定的(,)逗号符号隔开,该参数为可选项;
(6)close: 遍历集合时的结束符号,通常与open="("搭配使用,该参数为可选项;
如果传入的参数类型为 List 时,collection 的默认属性值为 list,同样可以使用 @Param 注解自定义 keyName。
示例:
Mapper接口定义的方法:UserList 为模拟返回的数据对象
List<UserList> getUserInfo(@Param("userName") List<String> userName);
Mapper.xml 动态 sql 构建,Mapper 接口的方法名和 xml 文件的 id 值,必须一一对应,否则会报错:
-----建议做 if test="xxxx !=null and xxxx.size()>0"的校验,比较严谨。如果传入的参数类型为 array ,则校验为.length();
<select id="getUserInfo" resultType="com.test.UserList">
SELECT
*
FROM user_info
where
<if test="userName!= null and userName.size() >0">
USERNAME IN
<foreach collection="userName" item="value" separator="," open="(" close=")">
#{value}
</foreach>
</if>
</select>
使用默认属性值 list 作为 keyname:
对应的 Dao 中的 Mapper 文件是:
public List<User> selectByIds(List<Integer> userIds);
xml 文件代码片段:
<select id="selectByIds" resultType="com.olive.pojo.User">
select * from t_user where id in
<foreach collection="list" index="index" item="item" open="(" separator="," close=")">
#{item}
</foreach>
</select>
转载自:1、Mybatis——foreach用法
2、mybatis for each循环详解
12、Spring 常用注解@Configuration,@Bean,@Component,@Service,@Controller,@Repository,@Entity的区分与学习
(1)@Component
作用于类上,相当于一个基类,跟 @Bean 一样,可以托管到Spring容器进行管理。
(2)@Service,@Controller,@Repository
作用于类上,={@Component + 一些特定的功能}。这些注解在部分功能上是一样的,但有一些细分:
@Controller注解类:SpringMVC 的理念,进行前端请求的处理,转发,重定向。包括调用Service层的方法。
@Service注解类:处理业务逻辑
@Repository注解类:作为 DAO 对象(数据访问对象,Data Access Objects),这些类可以直接对数据库进行操作。
(3)@Entity注解
作用于类上,数据表对应到实体类的映射。
(4)@Configuration
作用于类上,从 Spring3.0 开始,@Configuration 用于定义配置类,可替换 xml 配置文件,被注解的类内部包含有一个或多个被@Bean 注解的方法, 这些方法将会被 AnnotationConfigApplicationContext 或 AnnotationConfigWebApplicationContext 类进行扫描,并用于构建 bean 定义,初始化 Spring 容器。
(5)@Bean
标注在方法上,返回某个实例的方法,这个实例就会交给 Spring 容器管理。 此注解其实就等价于 spring 的 xml 配置文件中的,作用为:注册 bean 对象。 @Bean 一般是跟 @Configuration 一起用的相当于 < beans >< bean >< /bean >< /beans >。
POJO:什么是 POJO 呢,就是 Plain Ordinary Java Object 的缩写,一般在 Web 应用程序中建立一个数据库的映射对象时,我们称它为 POJO,这类对象不继承或不实现任何其它 Java 框架的类或接口。
转载自:Spring 常用注解@Configuration,@Bean,@Component,@Service,@Controller,@Repository,@Entity的区分与学习
13、sql的四种连接——左外连接、右外连接、内连接、全连接
(1)内连接
满足条件的记录才会出现在结果集中。
语法1:select f1,f2,f3...
from table1 inner join table2
on 关联匹配条件
语法2: select f1,f2,f3...
from table1,table2,table3
where 关联匹配条件
①自连接
概念:是内连接查询中一种特殊的等值连接,所谓的自连接就是指表与其自己当前表进行连接。自己和自己做连接。
②等值连接
就是在关键字 on 后的匹配条件是通过=来实现的;
自连接是一种特殊的等值连接。自连接是自己连接自己,等值连接通常是一个表连接另外一张表。
③不等值连接
在关键字 on 后匹配条件中使用除了=以外的关系运算符实现的不等条件(> >= < <= !=)
(2)左外连接(left outer join,outer 可省略)
左表全部出现在结果集中,若右表无对应记录,则相应字段为 NULL
(3)右外连接(right outer join,outer 可省略)
右表全部出现在结果集中,若左表无对应记录,则相应字段为 NULL
(4)全连接(full outer join,outer 可省略)
全外连接 = 左外连接 + 右外连接
转载自:1、sql的四种连接——左外连接、右外连接、内连接、全连接
2、MySQL–内连接查询(inner join)
14、mybatis 中的<![CDATA[ ]]>用法及说明
(1)平时在 mybatis 的映射文件写 sql 时,很多时候都需要写一些特殊的字符。例如:“<” 字符 “>” 字符 “>=” 字符 “<=” 字符,但是在 xml 文件中并不能直接写上述列举的字符,否则就会报错。
(2)因为在解析 xml 文件时候,我们如果书写了特殊字符,在没有特殊处理的情况下。这些字符会被转义,但我们并不希望它被转义,所以我们要使用 <![CDATA[ ]]> 来解决。
(3)那为什么要这样书写呢?<![CDATA[ ]]> ,不言而喻:这是 XML 语法。在 CDATA 内部的所有内容都会被解析器忽略(在该标签中的语句和字符原本是什么样的,在拼接成 SQL 后还是什么样的)。
(4)所以,当我们在 xml 文本中包含了很多的 “<” 字符 “<=” 和 “&” 字符—就像程序代码一样,那么最好把他们都放到 CDATA 部件中。
注意:
(1)使用动态 SQL 时要像 if、foreach、where 等标签一但被 <![CDATA[ ]]> 标签包裹,将忽略 xml 的解析并出错
(2)<![CDATA[ ]]> 标签中不可嵌套 <![CDATA[ ]]> 标签
(3)<![CDATA[ ]]> 尽量缩小范围,以免出错
转载自:1、CDATA[]的简单使用
2、mybatis 中的CDATA[]用法
15、parallelStream() 并行流操作
这是流编程,stream() 是串行操作的。但是 parallelStream() 恰恰相反,是并行操作的,两者的执行效率的话肯定是 parallelStream() 要快,毕竟是多线程的,但是这就就会涉及到线程安全的问题了,一旦涉及到线程安全的问题,那些线程不安全的集合类型都不能用,像 HashMap、ArrayList 都不能用,只能用线程安全的集合。
16、Stream流的用法
steam():把一个源数据,可以是集合,数组,I/O channel, 产生器 generator 等,转化成流。
forEach():迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数。
Random random = new Random();
random.ints().limit(10).forEach(System.out::println);
map():用于映射每个元素到对应的结果。以下代码片段使用 map 输出了元素对应的平方数:
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 获取对应的平方数
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
Collectors.toMap 有三个重载方法:
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction);
toMap(Function<? super T, ? extends K> keyMapper, Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier);
参数解释:
(1)keyMapper:Key 的映射函数
(2)valueMapper:Value 的映射函数
(3)mergeFunction:当 Key 冲突时,调用的合并方法
(4)mapSupplier:Map 构造器,在需要返回特定的 Map 时使用
示例:
过去的做法(List 转 Map)
List<User> userList = new ArrayList<>();
userList.add(new User().setId("A").setName("张三"));
userList.add(new User().setId("B").setName("李四"));
userList.add(new User().setId("C").setName("王五"));
//遍历
Map<String, String> map = new HashMap<>();
for (User user : userList) {
map.put(user.getId(), user.getName());
}
用 Collectors.toMap:
userList.stream().collect(Collectors.toMap(User::getId, User::getName));
转载自:1、java8 .stream().map().collect() 的用法
2、关于collect.stream().collect()方法的使用
3、Collectors.toMap 使用技巧 (List 转 Map超方便)
17、mybatis 映射内部类
参考文章:mybatis映射内部类的使用及注意事项
18、java判断字符串中是否包含某个字符
使用 String 类的 contains() 方法
contains() 方法用于判断字符串中是否包含指定的字符或字符串。语法如下:
public boolean contains(CharSequence chars)
参数类型:CharSequence
CharSequence 是一个接口,实现这个接口的类有:CharBuffer、String、StringBuffer和StringBuilder。可以理解为“CharSequence 描述的就是字符串”。所以,用 contains 方法判断字符串中是否包含某个字符时,不能使用字符类型的变量,应该将字符转化为字符串再使用 contains() 方法判断。
从图片中我们可以看出 String 类的 contains() 方法中的参数类型不能为 char。将字符转化为字符串的方法一般有两种:
(1)使用加号连接空字符串 + “”
char ch = 'a';
String s = "abandon";
System.out.println(s.contains(ch + ""));
(2)使用 String.valueOf() 方法
char ch = 'a';
String s = "abandon";
System.out.println(s.contains(String.valueOf(ch)));
19、IDEA的Debug技巧
https://cdk8s.gitbook.io/github/debug-introduce
20、IntelliJ IDEA 推荐设置
https://cdk8s.gitbook.io/github/settings-recommend-introduce
21、IDEA常用细节
选中要被折叠的代码按 Ctrl + Alt + T 快捷键,选择自定义折叠代码区域功能。
22、IntelliJ IDEA 常用快捷键讲解
https://cdk8s.gitbook.io/github/keymap-introduce
23、Java如何判断两个字符串是否相等
仅作补充:
如果要进行两个字符串对象的内容比较,除了 .equals()
方法,还有其他两个可选的方案。
(1)Objects.equals()
Objects.equals()
这个静态方法的优势在于不需要在调用之前判空。
public static boolean equals(Object a, Object b) {
return (a == b) || (a != null && a.equals(b));
}
如果直接使用 a.equals(b)
,则需要在调用之前对 a 进行判空,否则可能会抛出空指针java.lang.NullPointerException
。Objects.equals()
用起来就完全没有这个担心。
Objects.equals("小萝莉", new String("小" + "萝莉")) // --> true
Objects.equals(null, new String("小" + "萝莉")); // --> false
Objects.equals(null, null) // --> true
String a = null;
a.equals(new String("小" + "萝莉")); // throw exception
(2)String 类的 .contentEquals()
.contentEquals()
的优势在于可以将字符串与任何的字符序列(StringBuffer、StringBuilder、String、CharSequence)进行比较。
public boolean contentEquals(CharSequence cs) {
// Argument is a StringBuffer, StringBuilder
if (cs instanceof AbstractStringBuilder) {
if (cs instanceof StringBuffer) {
synchronized(cs) {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
} else {
return nonSyncContentEquals((AbstractStringBuilder)cs);
}
}
// Argument is a String
if (cs instanceof String) {
return equals(cs);
}
// Argument is a generic CharSequence
int n = cs.length();
if (n != length()) {
return false;
}
byte[] val = this.value;
if (isLatin1()) {
for (int i = 0; i < n; i++) {
if ((val[i] & 0xff) != cs.charAt(i)) {
return false;
}
}
} else {
if (!StringUTF16.contentEquals(val, cs, n)) {
return false;
}
}
return true;
}
从源码上可以看得出,如果 cs 是 StringBuffer,该方法还会进行同步,非常的智能化;如果是 String 的话,其实调用的还是 equals()
方法。当然了,这也就意味着使用该方法进行比较的时候,多出来了很多步骤,性能上有些损失。
24、Java字符串拼接
仅作补充:
实际的工作中,org.apache.commons.lang3.StringUtils
的 join()
方法也经常用来进行字符串拼接。该方法不用担心 NullPointerException
。
StringUtils.join(null) = null
StringUtils.join([]) = ""
StringUtils.join([null]) = ""
StringUtils.join(["a", "b", "c"]) = "abc"
StringUtils.join([null, "", "a"]) = "a"
源码:
public static String join(final Object[] array, String separator, final int startIndex, final int endIndex) {
if (array == null) {
return null;
}
if (separator == null) {
separator = EMPTY;
}
final StringBuilder buf = new StringBuilder(noOfItems * 16);
for (int i = startIndex; i < endIndex; i++) {
if (i > startIndex) {
buf.append(separator);
}
if (array[i] != null) {
buf.append(array[i]);
}
}
return buf.toString();
}
内部使用的是 StringBuilder。
25、如何在Java中优雅地分割String字符串?
26、类型转换
如上图所示,byte 可以向上转换为 short、int、long、float 和 double,short 可以向上转换为 int、long、float 和 double,char 可以向上转换为 int、long、float 和 double,依次类推。
27、Vector 和 Stack
List 的实现类还有一个 Vector,是一个元老级的类,比 ArrayList 出现得更早。ArrayList 和 Vector 非常相似,只不过 Vector 是线程安全的,像 get、set、add 这些方法都加了 synchronized 关键字,就导致执行执行效率会比较低,所以现在已经很少用了。
更好的选择是并发包下的 CopyOnWriteArrayList。
Stack 是 Vector 的一个子类,本质上也是由动态数组实现的,只不过还实现了先进后出的功能(在 get、set、add 方法的基础上追加了 pop、peek 等方法),所以叫栈。
不过,由于 Stack 执行效率比较低(方法上同样加了 synchronized 关键字),就被双端队列 ArrayDeque 取代了。
28、ArrayList和LinkedList的区别(含时间复杂度)
参考文章:面试官问我Java中ArrayList和LinkedList的区别,我和他扯了半小时
29、JSON.parseArray使用详解
参考文章:JSON.parseArray使用详解
30、Queue、Deque、Stack相对应的接口区别
Deque与Queue:
Queue Method | Equivalent Deque Method | 说明 |
---|---|---|
add(e) | addLast(e) | 向队尾插入元素,失败则抛出异常 |
offer(e) | offerLast(e) | 向队尾插入元素,失败则返回false |
remove() | removeFirst() | 获取并删除队首元素,失败则抛出异常 |
poll() | pollFirst() | 获取并删除队首元素,失败则返回null |
element() | getFirst() | 获取但不删除队首元素,失败则抛出异常 |
peek() | peekFirst() | 获取但不删除队首元素,失败则返回null |
Deque与Stack:
Stack Method | Equivalent Deque Method | 说明 |
---|---|---|
push(e) | addFirst(e) | 向栈顶插入元素,失败则抛出异常 |
无 | offerFirst(e) | 向栈顶插入元素,失败则返回false |
pop() | removeFirst() | 获取并删除栈顶元素,失败则抛出异常 |
无 | pollFirst() | 获取并删除栈顶元素,失败则返回null |
peek() | getFirst() | 获取但不删除栈顶元素,失败则抛出异常 |
无 | peekFirst() | 获取但不删除栈顶元素,失败则返回null |
转载自文章:详解 Java 中的双端队列
31、管道
Java 中的管道和 Unix/Linux 中的管道不同,在 Unix/Linux 中,不同的进程之间可以通过管道来通信,但 Java 中,通信的双方必须在同一个进程中,也就是在同一个 JVM 中,管道为线程之间的通信提供了通信能力。
32、编码和解码
在计算机中,数据通常以二进制形式存储和传输。
编码就是将原始数据(比如说文本、图像、视频、音频等)转换为二进制形式。
解码就是将二进制数据转换为原始数据,是一个反向的过程。
常见的编码和解码方式有很多,举几个例子:
ASCII 编码和解码:在计算机中,常常使用 ASCII 码来表示字符,如键盘上的字母、数字和符号等。例如,字母 A 对应的 ASCII 码是 65,字符 + 对应的 ASCII 码是 43。
Unicode 编码和解码:Unicode 是一种字符集,支持多种语言和字符集。在计算机中,Unicode 可以使用 UTF-8、UTF-16 等编码方式将字符转换为二进制数据进行存储和传输。
Base64 编码和解码:Base64 是一种将二进制数据转换为 ASCII 码的编码方式。它将 3 个字节的二进制数据转换为 4 个 ASCII 字符,以便在网络传输中使用。例如,将字符串 “Hello, world!” 进行 Base64 编码后,得到的结果是 “SGVsbG8sIHdvcmxkIQ==”。
图像编码和解码:在图像处理中,常常使用 JPEG、PNG、GIF 等编码方式将图像转换为二进制数据进行存储和传输。在解码时,可以将二进制数据转换为图像,以便显示或处理。
视频编码和解码:在视频处理中,常常使用 H.264、AVC、MPEG-4 等编码方式将视频转换为二进制数据进行存储和传输。在解码时,可以将二进制数据转换为视频,以便播放或处理。
简单一点说就是:
编码:字符(能看懂的)–>字节(看不懂的)
解码:字节(看不懂的)–>字符(能看懂的)
转载自文章:Java 转换流:Java 字节流和字符流的桥梁
33、try-catch-finally
try 块中的 return 语句执行成功后,并不会马上返回,而是继续执行 finally 块中的语句,如果 finally 块中也存在 return 语句,那么 try 块中的 return 就将被覆盖。
举例:
private int x = 0;
public int checkReturn() {
try {
return ++x;
} finally {
return ++x;
}
}
try 块中 x 返回的值为 1,到了 finally 块中就返回 2 了。
34、Arrays.asList()方法
数组转 List
String[] intro = new String[] { "一", "二", "三", "四" };
List<String> rets = Arrays.asList(intro);
System.out.println(rets.contains("二"));
需要注意的是,Arrays.asList() 返回的是 java.util.Arrays.ArrayList ,并不是 java.util.ArrayList ,它的长度是固定的,无法进行元素的删除或者添加。
rets.add("三");
rets.remove("二");
这个在编码的时候一定要注意,否则在执行这两个方法的时候,会抛出异常:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.base/java.util.AbstractList.add(AbstractList.java:153)
at java.base/java.util.AbstractList.add(AbstractList.java:111)
要想操作元素的话,需要多一步转化,转成真正的 java.util.ArrayList:
List<String> rets1 = new ArrayList<>(Arrays.asList(intro));
rets1.add("三");
rets1.remove("二");
35、分隔字符串 StringUtils.split()
分隔字符串是常见需求,如果直接使用 String 类的 split 方法,就可能会出现空指针异常。
String str1 = null;
System.out.println(StringUtils.split(str1,","));
System.out.println(str1.split(","));
执行结果:
null
Exception in thread "main" java.lang.NullPointerException
\tat com.sue.jump.service.test1.UtilTest.main(UtilTest.java:21)
使用 StringUtils 的 split 方法会返回 null,而使用 String 的 split 方法会报指针异常。
36、网络模型
由于计算机网络从底层的传输到高层的软件设计十分复杂,要合理地设计计算机网络模型,必须采用分层模型,每一层负责处理自己的操作。OSI(Open System Interconnect)网络模型是 ISO 组织定义的一个计算机互联的标准模型,注意它只是一个定义,目的是为了简化网络各层的操作,提供标准接口便于实现和维护。这个模型从上到下依次是:
应用层,提供应用程序之间的通信;
表示层:处理数据格式,加解密等等;
会话层:负责建立和维护会话;
传输层:负责提供端到端的可靠传输;
网络层:负责根据目标地址选择路由来传输数据;
链路层和物理层负责把数据进行分片并且真正通过物理网络传输,例如,无线网、光纤等。
互联网实际使用的 TCP/IP 模型并不是对应到 OSI 的 7 层模型,而是大致对应 OSI 的 5 层模型:
37、BIO、NIO、AIO
BIO (Blocking I/O):同步阻塞 I/O 模式。
NIO (New I/O):同步非阻塞模式。
AIO (Asynchronous I/O):异步非阻塞 I/O 模型。
同步阻塞模式:这种模式下,我们的工作模式是先来到厨房,开始烧水,并坐在水壶面前一直等着水烧开。
同步非阻塞模式:这种模式下,我们的工作模式是先来到厨房,开始烧水,但是我们不一直坐在水壶前面等,而是回到客厅看电视,然后每隔几分钟到厨房看一下水有没有烧开。
异步非阻塞 I/O 模型:这种模式下,我们的工作模式是先来到厨房,开始烧水,我们不一直坐在水壶前面等,也不隔一段时间去看一下,而是在客厅看电视,水壶上面有个开关,水烧开之后他会通知我。
阻塞 VS 非阻塞:人是否坐在水壶前面一直等。
同步 VS 异步:水壶是不是在水烧开之后主动通知人。
适用场景:
BIO 方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4 以前的唯一选择,但程序直观简单易理解。
NIO 方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4 开始支持。
AIO 方式适用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用 OS 参与并发操作,编程比较复杂,JDK7 开始支持。
小结:
BIO(Blocking I/O):采用阻塞式 I/O 模型,线程在执行 I/O 操作时被阻塞,无法处理其他任务,适用于连接数较少且稳定的场景。
NIO(New I/O 或 Non-blocking I/O):使用非阻塞 I/O 模型,线程在等待 I/O 时可执行其他任务,通过 Selector 监控多个 Channel 上的事件,提高性能和可伸缩性,适用于高并发场景。
AIO(Asynchronous I/O):采用异步 I/O 模型,线程发起 I/O 请求后立即返回,当 I/O 操作完成时通过回调函数通知线程,进一步提高了并发处理能力,适用于高吞吐量场景。
38、Java 命名规范
POJO 类中的任何布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误,比如 fastjson。
Service/DAO 层的方法命名规约:
(1)获取单个对象的方法用 get 做前缀
(2)获取多个对象的方法用 list 做前缀,复数结尾,如:listObjects
(3)获取统计值的方法用 count 做前缀
(4)插入的方法用 save/insert 做前缀
(5)删除的方法用 remove/delete 做前缀
(6)修改的方法用 update 做前缀
39、拆箱和装箱
当需要进行自动装箱时,如果数字在 -128 至 127 之间时,会直接使用缓存中的对象,而不是重新创建一个对象。
// 1)基本类型和包装类型
int a = 100;
Integer b = 100;
System.out.println(a == b); //true
// 2)两个包装类型
Integer c = 100;
Integer d = 100;
System.out.println(c == d); //true
// 3)
c = 200;
d = 200;
System.out.println(c == d); //false
40、IDEA 调试技巧
https://tobebetterjavaer.com/ide/4-debug-skill.html
41、IDEA插件
(1)Maven Helper
(2)Vuesion Theme ---- 主题
(3)lombok
(4)Translation
(5)Free Mybatis plugin ---- mapper 和 xml 之间自由切换
(6)CheckStyle-IDEA ---- 代码规范
42、Cron表达式
Cron 这个词来源于希腊语 chronos,原意也就是时间。
Cron 表达式是一个含有时间意义的字符串,以 5 个空格隔开,分成 6 个时间元素。举几个例子就一目了然了。
示例 | 说明 |
---|---|
0 15 10 ? * * | 每天上午10:15执行任务 |
0 0 10,14,16 * * ? | 每天10 点、14 点、16 点执行任务 |
0 0 12 ? * 3 | 每个星期三中午 12 点执行任务 |
0 15 10 15 * ? | 每月 15 日上午 10 点 15 执行任务 |
Cron 的语法格式可以总结为:
Seconds Minutes Hours DayofMonth Month DayofWeek
每个时间元素的取值范围,以及可出现的特殊字符如下所示。
时间元素 | 取值范围 | 可出现的特殊字符 |
---|---|---|
秒 | [0,59] | *,-/ |
分钟 | [0,59] | *,-/ |
小时 | [0,59] | *,-/ |
日期 | [0,31] | *,-/?LW |
月份 | [1,12] | *,-/ |
星期 | [1,7] | *,-/?L# |
特殊字符的含义和示例如下所示。
特殊字符 | 含义 | 示例 |
---|---|---|
* | 所有可能的值 | 很好理解,月域中为每个月,星期域中每个星期几 |
, | 枚举的值 | 很好理解,小时域中 10,14,16,就表示这几个小时可选 |
- | 范围 | 很好理解,分钟域中 10-19,就表示 10-19 分钟每隔一分钟执行一次 |
/ | 指定数值的增量 | 很好理解,分钟域中 0/15,就表示每隔 15 分钟执行一次 |
? | 不指定值 | 很好理解,日期域指定了星期域就不能指定值,反之亦然,因为日期域和星期域属于冲突关系 |
L | 单词 Last 的首字母 | 很好理解,日期域和星期域支持,表示月的最后一天或者星期的最后一天 |
W | 除周末以外的工作日 | 很好理解,仅日期域支持 |
# | 每个月的第几个星期几 | 很好理解,仅星期域支持,4#2表示某月的第二个星期四 |