2020年一月份笔记
20100107
1、Arraylist和Linkedlist
Arraylist:底层是基于动态数组,查找快,增删慢,因为比如要在第一个位置插入一个元素后面所有的元素都要向前移动1位,即使在最后插入一个元素速度也比Linkedlist慢
Linkedlist基于双向链表的动态数组,增删快,查找慢。数据添加删除效率高,只需要改变指针指向即可,但是访问数据的平均效率低,需要对链表进行遍历。Java 常见面试题之“Arraylist和Linkedlist的区别
2、==和equals的区别
obj的equals方法底层也是调的==。==是判断基本数据类型的值是否相等和判断引用类型的对象引用地址是否相等。equals方法只能判断引用类型即对象的地址是否相等。但是String类重写了equals方法,它比较的是两个String的每个字符是否相同。细谈java中==和equals的区别
另外Java 对于 eqauls 方法和 hashCode 方法是这样规定的:
1、 如果两个对象相同, 那么它们的 hashCode 值一定要相同;
2、 如果两个对象的 hashCode 相同, 它们并不一定相同
上面说的对象相同指的是用 eqauls 方法比较
3、oracle分页查询
首先说明,oracle的ROWNUM只能写<,<=,!=,不能写>、>=、=、between…and。
oracle分页的语句(不带order by)
SELECT *
FROM (SELECT ROWNUM AS rowno, t.*
FROM emp t
WHERE ROWNUM <= 20) A
WHERE A.rowno >= 10;
//就是把需要大于的那一部分放到一个单独查询里,而这个查询里的ROWNUM是可以任意大于小于等于的
oracle分页查询(带order by)
SELECT *
FROM (SELECT tt.*, ROWNUM AS rowno
FROM ( SELECT t.*
FROM emp t
ORDER BY create_time DESC, emp_no) tt
WHERE ROWNUM <= 20) A
WHERE A.rowno >= 10;
//比不带order by的又多套了一层,最内层是先根据order by查好
带查出总记录数的分页语句,注意这个是在每一行数据后加一列TOTAL来显示总记录数
SELECT * FROM (
SELECT A.*, ROWNUM RN FROM (
SELECT
c1,
c2,
count(*) over () total -----依靠此句查出 数量总和
FROM TABLE_NAME
) A WHERE ROWNUM <= 40
)WHERE RN >= 21
oracle怎样查出前几条数据
select * from 用户名.表名 where rownum <= 100则查询前100条数据,oracle会根据查询结果筛选前100条,记得如果增加查询条件,oracle首先会根据查询条件进行筛选,然后再取前100条,而不是筛选100条后才根据查询条件进行筛选
mybatis的分页是通过pagehelper插件实现的,它能拦截sql,加上分页语句,再发送执行。具体祥见mybatis之工作流程及分页原理
count(*) over() total
4.静态变量和实例变量的区别
静态变量前用static修饰,它是属于类的,不用创建对象,只要类字节码文件被加载,静态变量就被分配了空间,就可以直接类名.变量名使用。实例变量是属于对象的,只有创建了对象其中的实例变量才分配空间。
例如, 对于下面的程序, 无论创建多少个实例对象, 永远都只分配了一个 staticVar 变量, 并且每创建一个实例对象, 这个 staticVar 就会加 1; 但是, 每创建一个实例对象, 就会分配一instanceVar, 即可能分配多个 instanceVar, 并且每个 instanceVar 的值都只自加了 1 次。
public class VariantTest{
public static int staticVar = 0;
public int instanceVar = 0;
public VariantTest(){
staticVar++;
instanceVar++;
System.out.println(“staticVar=” + staticVar + ”,instanceVar=” + instanceVar);
}
}
5、java基础相关
静态代码块、代码块、构造函数的执行顺序
只有创建对象才执行构造代码块和构造函数,他们都是给对象初始化的,但是构造代码快执行顺序优先于构造方法,因为构造代码块是给所有对象进行统一初始化,而构造函数是给对应的对象初始化,因为构造函数是可以多个的,运行哪个构造函数就会建立什么样的对象,但无论建立哪个对象,都会先执行相同的构造代码块。也就是说,构造代码块中定义的是不同对象共性的初始化内容。
静态代码块是随着类的加载而执行的,只执行一次,并优先于主函数。静态代码块其实就是给类初始化的,而构造代码块是给对象初始化的。静态代码块中的变量是局部变量,与普通函数中的局部变量性质没有区别。
情况一:只有一个类,没有new对象,静态代码块
情况二:只有一个类,new对象,静态代码块》构造代码块》构造函数
情况三:当有继承时,new子类对象时,
1.执行父类的静态代码块,并初始化父类静态成员变量
2.执行子类的静态代码块,并初始化子类静态成员变量
3.执行父类的构造代码块,执行父类的构造函数,并初始化父类普通成员变量
4.执行子类的构造代码块, 执行子类的构造函数,并初始化子类普通成员变量
总结就是:先执行静态代码块父先,子后。然后执行父类的构造代码块和构造函数,执行完了最后是子类的构造代码块和构造函数
情况四:
父类代码
package com.company.java8.classtest;
public class Parent {
/* 静态变量 */
public static String p_StaticField = "父类--静态变量";
/* 变量 */
public String p_Field = "父类--变量";
protected int i = 9;
protected int j = 0;
/* 静态初始化块 */
static {
System.out.println( p_StaticField );
System.out.println( "父类--静态初始化块" );
}
/* 初始化块 */
{
System.out.println( p_Field );
System.out.println( "父类--初始化块" );
}
/* 构造器 */
public Parent(){
System.out.println( "父类--构造器" );
System.out.println( "i=" + i + ", j=" + j );
j = 20;
}
}
子类代码块
package com.company.java8.classtest;
public class SubClass extends Parent {
/* 静态变量 */
public static String s_StaticField = "子类--静态变量";
/* 变量 */
public String s_Field = "子类--变量";
/* 静态初始化块 */
static {
System.out.println( s_StaticField );
System.out.println( "子类--静态初始化块" );
}
/* 初始化块 */
{
System.out.println( s_Field );
System.out.println( "子类--初始化块" );
}
/* 构造器 */
public SubClass()
{
System.out.println( "子类--构造器" );
System.out.println( "i=" + i + ",j=" + j );
}
/* 程序入口 */
public static void main( String[] args )
{
System.out.println( "子类main方法" );
new SubClass();
}
}
执行顺序:
/*运行顺序:
父类--静态变量
父类--静态初始化块
子类--静态变量
子类--静态初始化块
子类main方法
父类--变量
父类--初始化块
父类--构造器
i=9, j=0
子类--变量
子类--初始化块
子类--构造器
i=9,j=20
* */
总结就是:先找父类的静态代码块,再找子类的静态代码块,这是随着类加载就要执行的。然后是main方法,然后是创建对象需要执行的,也就是代码块和构造函数(先父后子),这些都是描述对象的。
关于构造函数注意两点:
- 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
- 构造方法的作用是初始化成员变量的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个 super() ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。
集合
单列集合Collection:
先来一张关系图
Collection接口的方法它下边的类都适用,包括:
共性方法:
public boolean add(E e): 把给定的对象添加到当前集合中 。
public void clear() :清空集合中所有的元素。
public boolean remove(E e): 把给定的对象在当前集合中删除。
public boolean contains(E e): 判断当前集合中是否包含给定的对象。
public boolean isEmpty(): 判断当前集合是否为空。
public int size(): 返回集合中元素的个数。
public Object[] toArray(): 把集合中的元素,存储到数组中。
遍历Collection集合:Iterator迭代器
Iterator也是一个接口,有两个常用方法
public E next():返回迭代的下一个元素。
public boolean hasNext()`:如果仍有元素可以迭代,则返回 true。
举个例子如下
public class IteratorDemo {
public static void main(String[] args) {
// 使用多态方式 创建对象
Collection<String> coll = new ArrayList<String>();
// 添加元素到集合
coll.add("串串星人");
coll.add("吐槽星人");
coll.add("汪星人");
//遍历
//使用迭代器 遍历 每个集合对象都有自己的迭代器
Iterator<String> it = coll.iterator();
// 泛型指的是 迭代出 元素的数据类型
while(it.hasNext()){ //判断是否有迭代元素
String s = it.next();//获取迭代出的元素
System.out.println(s);
}
}
}
注意:在进行集合元素取出时,如果集合中已经没有元素了,
还继续使用迭代器的next方法,将会发生
java.util.NoSuchElementException没有集合元素的错误。
遍历Collection集合:增强for
增强for循环(也称for each循环)是JDK1.5以后出来的一个高级for循环,专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作,否则抛ConcurrentModificationException异常(删除倒数第二个元素不抛,因为不用调用next()方法做检测了)。
但是有两个正确的写法怎样正确迭代集合以及原理
//第一种
List<String> list4 = new ArrayList<>(list);
for(int i = 0; i < list4.size(); i++) {
if ("2".equals(list4.get(i))) {
list4.remove(i);
i--; // 应当有此操作
}
}
//第二种,一般用这种
List<String> list5 = new ArrayList<>(list);
Iterator<String> iterator = list5.iterator();
while (iterator.hasNext()) {
String str = iterator.next();
if ("2".equals(str)) {
iterator.remove();
}
}
增强for格式:
for(元素的数据类型 变量 : Collection集合or数组){
//写操作代码
}
泛型
1、定义一个含有泛型的类,在new这个类的对象的时候指定泛型,也可以不指定
public class GenericClass<E> {
private E name;
public E getName() {
return name;
}
public void setName(E name) {
this.name = name;
}
}
2、 定义含有泛型的方法:泛型定义在方法的修饰符和返回值类型之间
格式:
修饰符 <泛型> 返回值类型 方法名(参数列表(使用泛型)){
方法体;
}
含有泛型的方法,在调用方法的时候确定泛型的数据类型
传递什么类型的参数,泛型就是什么类型
public class GenericMethod {
//定义一个含有泛型的方法
public <M> void method01(M m){
System.out.println(m);
}
//定义一个含有泛型的静态方法
public static <S> void method02(S s){
System.out.println(s);
}
}
使用的时候new出对象调用方法,传什么参数就是什么
3、含义泛型的接口
/*
定义含有泛型的接口
*/
public interface GenericInterface<I> {
public abstract void method(I i);
}
第一种使用方式:定义接口的实现类,实现接口,指定接口的泛型
public class GenericInterfaceImpl1 implements GenericInterface<String>{
@Override
public void method(String s) {
System.out.println(s);
}
}
含有泛型的接口第二种使用方式:接口使用什么泛型,实现类就使用什么泛型,类跟着接口走
public class GenericInterfaceImpl2<I> implements GenericInterface<I> {
@Override
public void method(I i) {
System.out.println(i);
}
}
以上两种方式,实现接口时指定了泛型,new类的对象时就只能传那种类型,第二种方式可以在new对象时指定类型。
泛型通配符
不知道使用什么类型来接收的时候,此时可以使用?,?表示未知通配符。不能创建对象使用,只能作为方法的参数使用。一旦使用泛型的通配符后,只能使用Object类中的共性方法,集合中元素自身方法无法使用。 泛型没有继承概念。
public static void printArray(ArrayList<?> list){
//使用迭代器遍历集合
Iterator<?> it = list.iterator();
while(it.hasNext()){
//it.next()方法,取出的元素是Object,可以接收任意的数据类型
Object o = it.next();
System.out.println(o);
}
}
泛型的上下限:
/*
泛型的上限限定: ? extends E 代表使用的泛型只能是E类型的子类/本身
泛型的下限限定: ? super E 代表使用的泛型只能是E类型的父类/本身
*/
public class Demo06Generic {
public static void main(String[] args) {
Collection<Integer> list1 = new ArrayList<Integer>();
Collection<String> list2 = new ArrayList<String>();
Collection<Number> list3 = new ArrayList<Number>();
Collection<Object> list4 = new ArrayList<Object>();
getElement1(list1);
//getElement1(list2);//报错
getElement1(list3);
//getElement1(list4);//报错
//getElement2(list1);//报错
//getElement2(list2);//报错
getElement2(list3);
getElement2(list4);
}
// 泛型的上限:此时的泛型?,必须是Number类型或者Number类型的子类
public static void getElement1(Collection<? extends Number> coll){}
// 泛型的下限:此时的泛型?,必须是Number类型或者Number类型的父类
public static void getElement2(Collection<? super Number> coll){}
}
List、Set
List和Set的区别
(1)重复对象
list方法可以允许重复的对象,而set方法不允许重复对象
(2)null元素
list可以插入多个null元素,而set只允许插入一个null元素
(3)容器是否有序
list是一个有序的容器,保持了每个元素的插入顺序。即输出顺序就是输入顺序,而set方法是无序容器,无法保证每个元素的存储顺序,TreeSet通过 Comparator 或者 Comparable 维护了一个排序顺序
java.util.HashSet 底层的实现其实是一个 java.util.HashMap 支持。HashSet 是根据对象的哈希值来确定元素在集合中的存储位置,因此具有良好的存取和查找性能。保证元素唯一性的方式依赖于: hashCode 与 equals 方法。
HashSet集合存储数据的结构:哈希表
LinkedHashSet
set都是无序的,想要有序可以用HashSet的一个子类LinkedHashSet,它是链表和哈希表组合的一个数据存储结构。
Collections
它是集合的工具类,常用方法如下
对象比较器
简单的说就是两个对象之间比较大小,那么在JAVA中提供了两种比较实现的方式,一种是比较死板的采用 java.lang.Comparable 接口去实现,一种是灵活的当我需要做排序的时候在去选择的java.util.Comparator 接口完成。
public class CollectionsDemo3 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("cba");
list.add("aba");
list.add("sba");
list.add("nba");
//排序方法 按照第一个单词的降序
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.charAt(0) ‐ o1.charAt(0);
}
});
System.out.println(list);
}
}
结果如下:
[sba, nba, cba, aba]
Comparable:强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo方法被称为它的自然比较方法。只能在类中实现compareTo()一次,不能经常修改类的代码实现自己想要的排序。实现此接口的对象列表(和数组)可以通过Collections.sort(和Arrays.sort)进行自动排序,对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
Comparator强行对某个对象进行整体排序。可以将Comparator 传递给sort方法(如Collections.sort或Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用Comparator来控制某些数据结构(如有序set或有序映射)的顺序,或者为那些没有自然顺序的对象collection提供排序。
相当于Comparable是本来就有的,要定制的话就弄一个比较器Comparator,想怎么排就怎么排
Map
键值对,键不可以重复,值可以重复
HashMap :存储数据采用的哈希表结构,元素的存取顺序不能保证一致。由于要保证键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
LinkedHashMap :HashMap下有个子类LinkedHashMap,存储数据采用的哈希表结构+链表结构。通过表结构可以保证元素的存取顺序一致;通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()方法、equals()方法。
map遍历
一、值不重复,所以可以放到一个set集合中,用keySet方法,返回键的set集合,然后遍历set集合,用get方法通过键找值
Set<String> keys = map.keySet();
// 遍历键集 得到 每一个键
for (String key : keys) {
//key 就是键
//获取对应值
String value = map.get(key);
System.out.println(key+"的CP是:"+value);
二、Entry对象方式
public Set<Map.Entry<K,V>> entrySet() : 获取到Map集合中所有的键值对对象的集合(Set集合)
public K getKey() :获取Entry对象中的键
public V getValue() :获取Entry对象中的值
// 获取 所有的 entry对象 entrySet
Set<Entry<String,String>> entrySet = map.entrySet();
// 遍历得到每一个entry对象
for (Entry<String, String> entry : entrySet) {
// 解析
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key+"的CP是:"+value);
}
6、框架相关
@Transactional注解
作用于类上,该类所有的方法都加了这个注解,方法单独加以方法上的为准
@Transactional的属性
propagation :代表事务的传播行为,默认值为Propagation.REQUIRED,如果当前存在事务,则加入该事务,如果当前不存在事务,则创建一个新的事务。( 也就是说如果A方法和B方法都添加了注解,在默认传播模式下,A方法内部调用B方法,会把两个方法的事务合并为一个事务 )
isolation属性 :事务的隔离级别,默认值为 Isolation.DEFAULT。
timeout :事务的超时时间,默认值为 -1。
readOnly :指定事务是否为只读事务,默认值为 false;为了忽略那些不需要事务的方法,比如读取数据,可以设置 read-only 为 true。
rollbackFor :用于指定能够触发事务回滚的异常类型,可以指定多个异常类型。
noRollbackFor:抛出指定的异常类型,不回滚事务,也可以指定多个异常类型。
@Transactional失效场景
1、@Transactional 应用在非 public 修饰的方法上。protected、private 修饰的方法上使用 @Transactional 注解,虽然事务无效,但不会有任何报错,这是我们很容犯错的一点。
2、@Transactional 注解属性 propagation 设置错误(要么有事物挂起,要么抛出异常,或者干脆以非事物方式运行)
3、@Transactional 注解属性 rollbackFor 设置错误(默认 RuntimeException 或者 Error才回滚事务;其他异常不会触发回滚事务。如果在事务中抛出其他类型的异常,但却期望 Spring 能够回滚事务,就需要指定 rollbackFor属性。)
4、同一个类中方法调用,导致@Transactional失效
同类的AB两个方法,A没有标注这个注解,B标注了,A调用B,外部类再调用A方法,这样方法B的事务不会起作用。
5、异常被你的 catch“吃了”导致@Transactional失效
方法A调用B,B出异常标识当前事物需要回滚,但是这个异常被A给catch住了,这样就会导致A的前半段生效了,B失效了,没有回滚。
6、数据库不支持