// ::表示引用,比如类的构造方法,对象方法,静态方法的引用
stream.forEach(System.out::println);
等价
stream.forEach(item->System.out.println(item));
java 8中stream相关用法
注意:Arrays.asList得到的只是一个 Arrays 的内部类,一个原来数组的视图 List,因此如果对它进行增删操作会报错。用 ArrayList 的构造器可以将其转变成真正的 ArrayList。
String[] str = new String[]{"1","2","3"};
ArrayList al = new ArrayList(Arrays.asList(str));
2.MyBatis-Plus | 最简单的查询操作教程(Lambda)
4.VO与PO的区别:
- VO是用new关键字创建,由GC回收的。PO是向数据库中添加新数据时创建,删除数据库中数据时删除的。并且它只能存活在一个数据库连接中,当连接断开时,将被销毁。
- VO是值对象,精确点讲它是业务对象,是存活在业务层的,是业务逻辑使用的,它存活的目的就是为数据提供一个生存的地方。PO是有状态的,每个属性代表其当前的状态。它是物理数据的对象表示。使用它,可以使我们的程序与物理数据解耦,并且可以简化对象数据与物理数据之间的转换。
- VO的属性是根据当前业务的不同而不同的,也就是说,它的每一个属性都一一对应当前业务逻辑所需要的数据的名称。PO的属性是跟数据库表的字段一一对应的。
- VO是独立的Java Object。PO是由Hibernate纳入其实体容器(Entity Map)的对象,它代表了与数据库中某条记录对应的Hibernate实体,PO的变化在事务提交时将反应到实际数据库中。
- 不用po代替vo的主要原因还有业务层的方法的参数和返回类型不能是POJO,因为当POJO处于持久化状态时,会同步更新数据库,会带来一定的危险性,即在用户不知觉的情况下会对数据进行修改,所以,不能在控制器中对POJO进行操作,而用VO代替。使用VO可以避免Session已关闭的异常。
5.单元测试
- @Mock:创建一个Mock类,模拟类中的方法是空的,只负责返回模拟结果。
- @InjectMocks:创建一个真实的类实例。然后将用@Mock(或@Spy)注解创建的mock对象注入到用该实例中。
- 在单元测试中,没有启动 spring 框架,此时就需要通过
@ InjectMocks完成依赖注入。 - @InjectMocks会将带有@Spy 和@Mock 注解的对象尝试注入到被 测试的目标类中。
- 单元测试只负责测试@InjectMocks类中方法。对应依赖的@Mock类,只需要它返回的模拟结果。不对@Mock类中的方法测试和负责。
// 判断某字符串是否为空,为空的标准是 str==null 或 str.length()==0
StringUtils.isEmpty()
StringUtils.isNotEmpty()
// 是否为 null
// 是否为 ""
// 是否为空字符串(引号中间有空格) 如: " "。
StringUtils.isBlank()
StringUtils.tokenizeToStringArray()
7.ObjectUtils.isEmpty()
8.CollectionUtils.isEmpty()
CollectionUtils.arrayToList()
9.MapUtils.isNotEmpty()
10.// 对象的拷贝
BeanUtils.copyPropertiies(source,result)
11.EnumUtils.convertEnumToCode(a)
EnumUtils.convertEnumToDesc(b)
for(Map.Entry<fa,fb> entry: funcs.entrySet()){
}
13.Collections.addAll()
List<String> taskTypes = tasks.stream().map(Entity::getTaskType).distinct().collect(Collectors.toList());
.stream().collect(Collectors.toMap(a->a.getBizid,b->b.getBizType))
// REQUIRED:支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
@Transaction(propagation=Propagation.REQUIED,rollbackFor=Throwable.class)
// NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
@Transaction(propagation=Propagation.NOT_SUPPORTED)
// Require_New:不管当前有没有事务,会新开一个事务
@Transaction(propagation=Propagation.Require_New)
类调用自身函数时,不会产生动态代理方法,此时事务注解的功能会失效。
A,B两个方法。可能在同一个类中,或不同类中。
A调用B:
(1)A有事务,事务正常生效。
(2)A没有事务,B有事务,事务失效
(3)A没有事务,B有事务。A注入类自身类,并且通过自身类去调用B。这种情况B的事务正常生效。
16.orgIds.stream().mapToInt(Integer::valueof).toArray()
17.//获取Spring容器中指定类型的所有JavaBean对象
Map<String, AbstractTaskHandler> handlerMap = SpringContextUtils.getBeanOfType(AbstractTaskHandler.class);
AbstractTaskHandler handle = SpringContextUtils.getBean(name ,AbstractTaskHandler.class)
18.转int:
Integer.parseInt()
aa.intValue()
19.compareTo方法:
// 字符串转BigDecimal
BigDecimal b1 = new BigDecimal("-121454125453.145");
// -1:小于; 0 :等于; 1 :大于
if(b1.compareTo(BigDecimal.ZERO)==-1) {
System.out.println("金额为负数!");
}
最常见的多态就是将子类传入父类参数中,运行时调用父类方法时通过传入的子类决定具体的内部结构或行为。
Liskov替换原则:子类可以替换基类,但是基类不一定能替换子类。通俗讲子类可以扩展父类的功能,但不能改变父类原有的功能。
依赖倒置原则:依赖于抽象,就是对接口编程,不要对实现编程
接口隔离原则:使用多个小的专门的接口,而不要使用一个大的总接口
多态的3个必要条件:
有类继承或者接口实现
子类要重写父类的方法
父类的引用指向子类的对象
继承的根本原因是因为要复用,而实现的根本原因是需要定义一个标准。
Java中支持一个类同时实现多个接口,但是不支持同时继承多个类。
在接口中只能定义全局常量(static final)和无实现的方法(Java 8以后可以有defult方法);而在继承中可以定义属性方法,变量,常量等。
组合与继承的区别和联系
- 在继承结构中,父类的内部细节对于子类是可见的。所以我们通常也可以说通过继承的代码复用是一种白盒式代码复用。(如果基类的实现发生改变,那么派生类的实现也将随之改变。这样就导致了子类行为的不可预知性;)
- 组合是通过对现有的对象进行拼装(组合)产生新的、更复杂的功能。因为在对象之间,各自的内部细节是不可见的,所以我们也说这种方式的代码复用是黑盒式代码复用。(因为组合中一般都定义一个类型,所以在编译期根本不知道具体会调用哪个实现类的方法)
- 继承,在写代码的时候就要指名具体继承哪个类,所以,在编译期就确定了关系。(从基类继承来的实现是无法在运行期动态改变的,因此降低了应用的灵活性。)
- 组合,在写代码的时候可以采用面向接口编程。所以,类的组合关系一般在运行期确定。
构造函数与默认构造函数
- 构造器没有返回类型,不会被继承,且可以有范围修饰符。构造器的函数名称必须和它所属的类的名称相同。
- 默认构造函数一般会把成员变量的值初始化为默认值,如int -> 0,Integer -> null。 它承担着初始化对象数据成员的任务。
Java中共有三种变量,分别是类变量、成员变量和局部变量。他们分别存放在JVM的方法区、堆内存和栈内存中。
对于不同的硬件和操作系统,最主要的区别就是指令不同。
有了Java虚拟机,想要执行a+b操作,A操作系统上面的虚拟机就会把指令翻译成10001000,B操作系统上面的虚拟机就会把指令翻译成11101110。
Java的平台无关性是建立在Java虚拟机的平台有关性基础之上的,是因为Java虚拟机屏蔽了底层操作系统和硬件的差异。
Java中引用数据类型参数(如对象)也是按值传递给方法的。这意味着,当方法返回时,传入的引用仍然引用与以前相同的对象。但是,如果对象字段具有适当的访问级别,则可以在方法中更改这些字段的值。
Java传参只有值传递,只不过是把对象的引用当做值传递给方法。也就是将对象的地址的拷贝传递给被调函数的形式参数。
mybatispuls里面getOne需要注意返回多条结果会报错。需要在queryWrapper中加.last("FETCH FIRST 1 ROWS ONLY WITH UR")
32.For循环和While区别:
从应用场景方面考虑:
for循环适用于已知循环次数,while循环适用于未知循环次数。
那么在已知循环次数时,最好选择for循环;
如果循环次数是未知的。最好选择while循环.
从内存角度考虑:
局部变量在栈内存中存在,当for循环语句结束,那么变量会及时被gc(垃圾回收器)及时的释放掉,不浪费空间;
如果使用循环之后还想去访问循环语句中控制那个变量,使用while循环。
Java中的整型主要包含byte、short、int和long这四种,表示的数字范围也是从小到大的,之所以表示范围不同主要和他们存储数据时所占的字节数有关。
- byte:byte用1个字节来存储,范围为-128(-27)到127(27-1),在变量初始化的时候,byte类型的默认值为0。
- short:short用2个字节存储,范围为-32,768 (-2^15)到32,767 (2^15-1),在变量初始化的时候,short类型的默认值为0,一般情况下,因为Java本身转型的原因,可以直接写为0。
- int:int用4个字节存储,范围为-2,147,483,648 (-2^31)到2,147,483,647 (2^31-1),在变量初始化的时候,int类型的默认值为0。
- long:long用8个字节存储,范围为-9,223,372,036,854,775,808 (-2^63)到9,223,372,036, 854,775,807 (2^63-1),在变量初始化的时候,long类型的默认值为0L或0l,也可直接写为0。
- 单精度浮点数在计算机存储器中占用4个字节(32 bits),利用“浮点”(浮动小数点)的方法,可以表示一个范围很大的数值。
- 比起单精度浮点数,双精度浮点数(double)使用 64 位(8字节) 来存储一个浮点数。
- 由于计算机中保存的小数其实是十进制的小数的近似值,并不是准确值,所以,千万不要在代码中使用浮点数来表示金额等重要的指标。建议使用BigDecimal或者Long(单位为分)来表示金额。
对于不需要任何准确计算精度的数字可以直接使用float或double,但是如果需要精确计算的结果,则必须使用BigDecimal类,而且使用BigDecimal类也可以进行大数的操作。
在Java中,==比较的是对象应用,而equals比较的是值。
Integer a = 12;
Integer b =12;
a==b; // true
Integer c = 1234;
Integer d =1234;
c==d; // false
所有相同类型的包装类对象之间的比较,推荐使用equals方法比较。避免IntegerCache大坑。
一旦一个string对象在堆中被创建出来,他就无法被修改。特别要注意的是,String类的所有方法都没有改变字符串本身的值,都是返回了一个新的对象。
如果你需要一个可修改的字符串,应该使用StringBuffer 或者 StringBuilder。否则会有大量时间浪费在垃圾回收上,因为每次试图修改都有新的string对象被创建出来。
String 字符串常量
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(非线程安全,项目中常用)
String.valueOf(i)也是调用Integer.toString(i)来实现的
Long.valueOf()注意空字符串和null异常
Collection 是一个集合接口。 它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。是list,set等的父接口。
Collections 是一个包装类。 它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。
List特点:元素有放入顺序,元素可重复
Set特点:元素无放入顺序,元素不可重复
JDK中序列化和反序列化的API:
- java.io.ObjectInputStream:对象输入流。
该类的readObject()方法从输入流中读取字节序列,然后将字节序列反序列化为一个对象并返回。 - java.io.ObjectOutputStream:对象输出流。
该类的writeObject(Object obj)方法将传入的obj对象进行序列化,把得到的字节序列写入到目标输出流中进行输出。
41.序列化
当试图对⼀个对象进行序列化的时候, 如果遇到不支持Serializable
接口的对象。 在此情况下, 将抛NotSerializableException
。
如果要序列化的类有父类, 要想同时将在父类中定义过的变量持久化下来, 那么父类也应该实现java.io.Serializable
接口。
transient 关键字的作用是控制变量的序列化, 在变量声明前加上该关键字, 可以阻⽌该变量被序列化到⽂件中, 在被反序列化后, transient 变量的值被设为初始值, 如 int 型的是 0,对象型的是 null。
异常链
try {
} catch (IOException e) {
throw new SampleException("Other IOException", e);
}
在此示例中,当捕获到IOException时,将创建一个新的SampleException异常,并附加原始的异常原因,并将异常链抛出到下一个更高级别的异常处理程序。
如果try中有return语句, 那么finally中的代码还是会执⾏。因为return表⽰的是整个⽅法体返回, 所以,finally中的语句会在return之前执⾏。
有些特殊字符是无法在URL中展示的,所以,遇到这种字符,如中文,就需要进行URLEncode编码和解码。
int和Integer的转换。从反编译得到内容可以看出,在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。
当我们使用enmu来定义一个枚举类型的时候,编译器会自动帮我们创建一个final类型的类继承Enum类,所以枚举类型不能被继承。
在java 7中,数值字面量,不管是整数还是浮点数,都允许在数字之间插入任意多个下划线。这些下划线不会对字面量的数值产生影响,目的就是方便阅读。
比如:
public class Test {
public static void main(String... args) {
int i = 10_000;
System.out.println(i);
}
}
增强for循环,实现原理其实就是使用了普通的for循环和迭代器。
从Java 7开始,jdk提供了一种更好的方式关闭资源,使用try-with-resources语句。
之前是
try {}
catch(){}
finally{}
现在是
try(资源){}
catch(){}
for (Student stu : students) {
if (stu.getId() == 2)
students.remove(stu);
}
以上增强型for循环删除元素时会抛出ConcurrentModificationException异常。
解决方法:用传统for循环或者用Iterator 本身的方法remove()来删除对象
java普通for循环和增强for循环中做集合增删会不会出错?
无论何时这个equals方法被重写那么都是有必要去重写hashCode方法,这个是因为为了维持hashCode的一种约定,相同的对象必须要有相同的hashCode值。
被synchronized修饰的代码块及方法,在同一时间,只能被单个线程访问。
在springboot yml配置文件中写以0开头的字符串,当写为01001时,通过注解获取到的值是1001,springboot会自动的把0过滤掉。因此在使用0开头的字符串时,应该写为“01001” 。
原子性是指一个操作是不可中断的,要全部执行完成,要不就都不执行。
在pom.xml中通过properties元素用户可以自定义一个或多个Maven属性,然后在POM的其他地方使用${属性名}的方式引用该属性。这种做法的最大意义在于消除重复和统一管理。
try-with-resources 是 JDK 7 中一个新的异常处理机制。
所有实现了 [java.lang.AutoCloseable]接口(其中,它包括实现了 [java.io.Closeable] 的所有对象),可以使用作为资源。
<trim prefix="Where" prefixOverrides="AND|OR">
</trim>
对trim中的sql,前缀添加where,
移除sql开始的AND|OR
判空的代码Java8新处理-Optional。
Optional.ofNullable(...).orElse("");
Optional<...> abc = ...;
abc.isPresent()? ... : null;
而常见的判断Optional结果的方法有,orElse()、isPresent()、get()、empty()、map()、flatMap()。
java8 Optional理解及示例
Java的堆是一个运行时数据区,类的对象从堆中分配空间。
堆的优势是在运行时动态地分配内存空间,需要多少内存空间不必事先告诉编译器。
堆缺点是,由于需要在运行时动态分配内存,所以存取速度较慢。
栈中主要存放一些基本数据类型的变量(byte,short,int,long,float,double,boolean,char)和对象的引用。
栈的优势是,存取速度比堆快,栈数据可以共享。
栈缺点是,存放在栈中的数据占用多少内存空间需要在编译时确定下来,缺乏灵活性。
与 Tomcat 相比,Jetty 可以同时处理大量链接并且长时间的保持这些链接,例如,一些 Web 聊天应用非常适合用 Jetty 服务器,比如说淘宝的 Web 版旺旺。
- 加载器的任务就是把处于虚拟机外部存储空间的字节码资源载入到虚拟机运行时环境里。
- 解释器每次运行程序时都要一行一行先转成另一种语言再运行。
- JIT编译器工作阶段基本是java程序运行期的最后阶段了,它的工作是将加载的字节码转换为机器码。当使用JIT编译器时,硬件可以执行JIT编译器生成的机器码,而不是让JVM通过解释器重复解释执行相同的字节码导致相对冗长的翻译过程。这样可以带来执行速度的性能提升。
redis可以给每个key设置一个过期时间,这样当达到过期时间之后直接删除。像redis是直接使用单线程处理,所以就不存在线程安全问题。分布式缓存如redis是有持久化功能的。
- 缓存穿透是指大量查询一些数据库中不存在的数据,从而影响数据库的性能。
- 缓存击穿是指并发用户特别多,缓存中没有但数据库中有的数据
- 缓存雪崩是指由于某种原因造成Redis突然失效,从而造成MySQL瞬间压力骤增,进而严重影响MySQL性能甚至造成MySQL服务宕机。
如果RuntimeException没有被捕获而直达main(),那么程序退出前将调用异常的printStackTrace()方法。
如果像是订单库等数据量非常庞大,一般会进行分库分表。这个时候不建议使用数据库的 id 作为唯一标识,而应该使用分布式的高并发唯一 id 生成器来生成,并在数据表中使用另外的字段来存储这个唯一标识。
spring默认的实例化方法就是无参构造函数实例化。
如我们在xml里定义的 <bean id="xxx" class="yyy"/> 以及用注解标识的bean都是通过默认实例化方法实例化的。
Spring Boot Bean生命周期
this() 就是调用自己的无参构造方法,和super()一个道理。
在子类的构造方法中,只要里面没有显示的通过super去调用父类相应的构造方法,默认都是调用super(),即无参构造方法,因此要确保父类有相应的构造方法。
Spring Boot缓存注解@Cacheable、@CacheEvict、@CachePut使用
在实际的开发中,看到有一些对象被手动赋值为NULL,很大可能就是为了“特意提醒”JVM这块资源可以进行垃圾回收了。
在Java中,其实是通过值传递实现的参数传递,只不过对于Java对象的传递,传递的内容是对象的引用。绝对不能认为Java中有引用传递。
Vector与ArrayList一样,也是通过数组实现的,不同的是它支持线程的同步,即某一时刻只有一 个线程能够写 Vector。
HashMap 非线程安全,即任一时刻可以有多个线程同时写 HashMap,可能会导 致数据的不一致。如果需要满足线程安全,可以用 Collections 的 synchronizedMap 方法使 HashMap 具有线程安全的能力,或者使用 ConcurrentHashMap。
ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构类似,数组+链表/红黑二叉树。synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。
抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。synchronized和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。
类型擦除:如在代码中定义的 List<Object>和 List<String>等类型,在编译之后 都会变成List。JVM看到的只是List,而由泛型附加的类型信息对JVM来说是不可见的。
BigDecimal和0比较
用num.compareTo(BigDecimal.ZERO)
不要用BigDecimal.ZERO.equals(null)//认为0和0.00不等
动态代理一般在代理类中做些额外的操作。比如代理买房,还可能额外进行看房,贷款等操作。
vo中日期类型和decimal类型,统一定义为String类型。
原因:
用jackson A序列化和B反序列化日期时可能有会时区问题。
decimal可能有会精度丢失问题。
常见的引用类型:类类型,接口类型和数组。
类类型:String
接口interface:List 接口,Map<K,V>
数组:int data[] = new int[3];
流与集合的异同:
- 集合的主要功能是以一定的时间和空间复杂度存储和访问元素,而流主要是用于元素计算
- 集合中的元素可以随意添加和删除,而流不能添加和删除元素
- 流的元素是按需计算的,只有当用到时他才会参与计算,而集合中的元素必须提前全都准备好
- 流只能遍历一次,下面的代码会报错 java.lang.IllegalStateException: stream has already been operated upon or closed 流已经被消费掉
List<String> names = Arrays.asList("Java8", "Lambdas", "In", "Action");
Stream<String> s = names.stream();
s.forEach(System.out::println);
s.forEach(System.out::println);
- 集合采用外部迭代,流采用内部迭代。内部迭代意味着 Java 可以替你选择更优的迭代策略和并行处理。而外部迭代如果程序员想着做个更优的迭代/采用并行就相当于“下次一定”了
Stream和parallelStream
网友评论