Java学习笔记

  1. 定义一个类时,重写equals()方法必须要重写hashCode()方法,不然在基于散类集合中储存时会出错,比如 Set集合中储存比较两个对象是否重复就是比较hashCode;两个类相等的严格定义是,equals()相等,hashCode()也相等

  2. 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递;深拷贝:对基本数据类型进行值传递,对引用数据类型创建一个新的对象,复制其内容;Java.lang.Object的clone()方法在不重写的情况下是浅拷贝,若要深拷贝,那就继承Cloneable类,然后重写clone方法,在方法内新建当前类对象,赋值后返回新建的对象。

  3. 除了8种基本数据类型外,void也可以作为基本类型,作为方法的返回类型。String不是基本数据类型,而是引用类型

  4. byte,int,short默认值为0,long为0L,在数据发生溢出时,不会抛出异常。

  5. Integer在自动装箱时,如果数字在-128到127之间会直接使用缓存(可以查看Integer.valueOf()方法的实现),而不是重新创建对象,Integer a = 3; Integer b = 3;     a=b为true,超出范围则新建两个不同对象,比较数值也要用equals()比较

  6. 除了Integer还有Byte、Short、Long、Character(0-127)有缓存机制,

  7. 当包装类型值为null时,拆箱会报错,在循环中使用封箱拆箱会消耗大量资源

  8. 在pojo定义属性时,必须使用包装类,这样使用时必须赋值,RPC接口返回也必须是包装类。局部变量使用基本数据类型,避免浪费资源

  9. String创建出来的对象不能修改值,所有有关的方法都是返回新的String对象,StringBuffer和StringBuilder创建出来的对象可以修改值

  10. jvm专门开辟了一部分空间来储存Java字符串,字符串池,两个内容相同的字符串只想同一个字符串对象,当其中一个改变了内容则新建了String,不会影响另一个的内容

  11. StringBuilder和StringBuffer的对象内容都是可以被修改的,他们之间的区别:StringBuilder的方法不是线程安全的,StringBuffer是线程安全的,比如他的append方法是被synchronized修饰,在并发场景中要使用StringBuffer

  12. 拼接代码效率StringBuilder > StringBuffer > String.concat() > “+” > StringUtils.join  在循环体内,用StringBuilder的append方法进行字符串扩展而不要使用“+”

  13. 通过List进行拼接字符串,可以使用java8的StringJoiner

  14. 删除字符串空格

  15. trim():删除ASCII码表值小于等于32的字符,空格,换行、退格

  16. strip():Java11引入,除了ASCII码表还有很多Unicode标准中的空格,

  17. stripLeading() 删除开头

  18. stripTrailing() 删除结尾

  19. replace() 替换,只能ASCII码中

  20. replaceAll() 可以使用正则表达式替换

  21. JAVA8中支持switch(String) ,比较时,比对hashCode(), 然后再equals()比对,实际上 switch比对的还是整型值

  22. String池,再jdk1.6及以前是定义可stringTable 位于JVM的永久代(可能导致内存泄漏),jdk1.7之后位于堆栈上

  23. Stirng.intern()方法,可以吧一个String类型常量放入String池,如果已经存在则返回引用地址

  24. 区分String常量池,字符串池,非new的对象的字符串在编译时就加入了String常量池,在new的String是在运行时加入了字符串池,String.intern()方法是将字符串池中的字符串的引用加到常量池,编译时,String s1 = “aaa” 若aaa在常量池,则s1指向常量池的引用,若aaa不在常量池,在编译时新加入常量池,String s2 = new String(“aa”),编译时,“aa” 作为字符串已经加入常量池,但s2 是String对象的引用不是"aa"在常量池的引用。使用s2.intern()方法吧s2加入常量池,但"aa"已经存在,则s2指向“aa”常量池的引用(若"aa"原来不存在在常量池中,则加入常量池。s2对象的地址就是常量池中的引用)

  25. String的长度在编译期间,作为常量不能超过2^16-1(JAVA虚拟机规范中定义编译期String的长度为2字节无符号数),在运行期不能超过2^31-1(大概4GB ,String源码定义length为int类型)

  26. 不可以使用异常来控制业务,应该用if-else控制,对于无法捕获的异常应该往上抛,而不是捕获在catch里面再抛出去

  27. 异常中,不要使用IDE自动生成的 e.printStackTrace(),此方法是把异常输出到控制台,并不输出到日志!

  28. 不要在finally中抛出异常,会导致捕获的异常信息丢失

  29. 在JAVA中对于文件操作的I/O流、数据库链接等开销非常大的资源,用完之后还要close,JAVA7开始JDK提供了更好的关闭资源的方式

import java.io.BufferedReader;
import java.io.FileReader; 
public class TryCatch {
    public static void main(String... args) {         try(BufferedReader bf = new BufferedReader(new FileReader("H:\\\\\logs\\\\\service.log"))){
            String line;
            while ((line= bf.readLine()) != null){
                System.out.println(line);
            }
        }catch (Exception e){
            System.out.println("error");
        }
    }
}
```
  1. finally执行的条件:对应的try块被执行,程序正常运行(jvm正常运行)

  2. 为了弥补数组的固定大小,只能存相同的数据类型、按照索引存取的局限。Java提供了Collectionhe Map集合类

  3. Collection是一个接口,提供了通用接口方法主要分为3类,List,Queue,Set

  4. List和Set之间区别就是存入的元素是否有序,是否可以重复,前者有序可重复,后者无序不可重复

  5. List具体实现类包含ArrayList、LinkedList、Vector

  6. Set具体实现类包含HashSet、LinkedSet、TreeSet

  7. Queue是队列,只能在队尾加入,对头取出具体实现有,PriorityQueue和LinkedList

  8. Map也是接口,提供了键值对对对象进行基本操作,主要有HashMap、Hashtable、LinkedHashMap和ConcurrentHashMap

  9. 除了Collection和Map接口,Java还提供了Collections类,此类不能被实例化,提供了很多操作集合的静态方法

  10. 遍历集合:for循环遍历,foreach循环遍历,迭代器Iterator、Enumeration(判断是否还有下一个),用Stream遍历集合strings.stream().forEach(System.out::println);

  11. 删除ArrayList元素时,不可以使用增强for或普通for删除remove,可以用以下方法删除 “s”

  12. 使用Iterator.remove, if( ! “s”.equals(iterator.next()){ iterator.remove(); }

  13. 使用JAVA8的过滤filter   list = list.stream().filter(ls -> ! ls.equals(“s”)).collect(Collectors.toList());

  14. Map遍历:foreach循环中使用entries(map.entrySet())遍历;foreach中通过key遍历,map.keySet();Iterator遍历 Iterator<Map.Entry> entries = map.entrySet().iterator(); Iterator使用key遍历 Iteratori = map.keySet().iterator();

  15. ArrayList和Vector基于数组实现,方便查找;LinkedList基于链表实现,方便插入删除。

  16. ArrayList默认长度是10,可以通过自己的grow(int minCapacity)方法扩容,默认扩容1.5倍,如果输入的扩容数量大于1.5则使用输入的,如果新的数量超出数组最大值且输入的也超出数组最大用Int的最大范围2^32-1,新的超出数组最大但输入的没超过使用数组最大。

  17. Vector默认长度是10,扩容使用grow(int minCapacity)默认扩容两倍。若输入的大于0,则按照输入的扩容

  18. ArrayList、Vector、LinkedList只有Vector是线程安全的,他的所有方法都加了锁synchronized,除了Vector是线程安全的,SynchronizedList也是线程安全的,推荐使用后者,Liststrings = Collections.synchronizedList(a);可以吧ArrayList和LinkedList转换成线程安全的

  19. ArrayList的subList()方法返回的是SubList对象(这是ArrayList的内部类),subList对象引用了原来的对象,对父子做的非结构性改变(只改变数值)会对子父产生同样改变,对子做结构性改变(改变长短)会对父产生相同影响,对父做结构性改变会异常,实在要修改subList可以创建副本。subList  = Lists.newArrayList(subList); 或者不用subList:list.stream().skip(start).limit(end).collect(Collectors.toList());

  20. Set主要有HashSet、LinkedHashSet、TreeSet等具体实现,Set是用Map实现的,HashMap和LinkedHashMap是基于哈希表实现的,所以HashSet和LinkedHashSet也是通过哈希表实现的;TreeMap是通过红黑树,所以TreeSet也是通过红黑树(自平衡二叉查找树)实现的。

  21. TreeSet是通过红黑树实现的,红黑树是平衡二叉树,则是有序的。

  22. 想HashSet中添加元素时,首先计算元素的hashCode,然后计算出这个元素的位置,如果位置上有元素,判断元素是否相等,相等则不添加,不相等或者无元素则找空位添加

  23. HasHSet和LinkedHashSet是可以为空的且只能存在一个,TreeSet不能有Null值,他们通过equals()和hashCode()比较对象,TreeSet通过compare()和compareTo()比较

  24. HashMap实现方式是JDK8数组+链表+红黑树,插入链表尾巴(JDK7是数组+链表,插入链表头),TreeMap是通过红黑树,LinkedHashMap是HashMap的子类是通过双向链表实现;扩容(resize方法)都是扩大到原来两倍

  25. HashMap线程不安全,可以通过Collections.synchronizedMap(map)获得线程安全的HashMap;推荐使用ConcurrentHashMap实现同步,性能更好

  26. HashMap可以认为是一个大数组,用key的hashCode作为标value作为值,如果key有重复那么覆盖;如果链表长度大于等于8且数组大小大于等于64,则把链表转化成红黑树,为了提高效率,如果红黑树节点小于6则转化成链表

  27. HashMap默认容量是16,size是已经存了多少元素。

  28. 不要用双花括号法初始化集合,可以用以下方法

  29. 使用Arrays工具类,Listl = Arrays.asList(“a”,“b”);

  30. 使用Stream  Listl = Stream.of(“a”,“b”).collect(Collectors.toList);

  31. 使用第三方工具类,ImmutableMap.of(“k1”,“v1”,“k2”,“v2”),ImmutableList.of(“a”,“b”)

  32. 虽然单独方法是加锁线程安全的,但是多个方法同时执行时,当前事务不是安全的,要对被执行的对象加锁synchronized(Object){ … }