小tips

1、Lombok 的 @Builder 注解

@Builder 注释为类生成相对略微复杂的构建器API。@Builder 可以让你以下面显示的那样调用你的代码,来初始化实例对象:

Student.builder()
               .sno( "001" )
               .sname( "admin" )
               .sage( 18 )
               .sphone( "110" )
               .build();

我们在对实体对象进行操作时,往往会存在对某些实体对象的某个字段进行二次赋值。@Builder 注解里有个 toBuilder() 方法,使用 toBuilder() 可以实现以一个实例为基础继续创建一个对象,也就是重用原来对象的值。但是,这会创建一个新的对象,而不是原来的对象,原来的对象属性是不可变的,除非你自己想要给这个实体类再添加上 @Data 或者 @Setter 方法。

转载自:详解Lombok中的@Builder用法

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[] 类型的入参进行判断;

转载自:Spring中Assert工具类的用法

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)内连接
满足条件的记录才会出现在结果集中。

语法1select 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 都不能用,只能用线程安全的集合。

转载自:parallelStream()并行流操作

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)));

转载自:java判断字符串中是否包含某个字符

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.NullPointerExceptionObjects.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() 方法。当然了,这也就意味着使用该方法进行比较的时候,多出来了很多步骤,性能上有些损失。

转载自:Java如何判断两个字符串是否相等?

24、Java字符串拼接

仅作补充:
实际的工作中,org.apache.commons.lang3.StringUtilsjoin()方法也经常用来进行字符串拼接。该方法不用担心 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。

转载自:最优雅的Java字符串拼接是哪种方式?

25、如何在Java中优雅地分割String字符串?

参考文章:如何在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 MethodEquivalent Deque Method说明
add(e)addLast(e)向队尾插入元素,失败则抛出异常
offer(e)offerLast(e)向队尾插入元素,失败则返回false
remove()removeFirst()获取并删除队首元素,失败则抛出异常
poll()pollFirst()获取并删除队首元素,失败则返回null
element()getFirst()获取但不删除队首元素,失败则抛出异常
peek()peekFirst()获取但不删除队首元素,失败则返回null

Deque与Stack:

Stack MethodEquivalent 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表示某月的第二个星期四

43、

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值