object.equals("str")
容易报空指针异常,应使用 "str".equals(object);
还可以使用JDK7引入的工具类 object#equals : objects.equals(null, "str"); // false
java.util.Objects#equals
源码:
public static boolean equals(Object a, Object b) { // 可以避免空指针异常。如果a==null的话此时a.equals(b)就不会得到执行,避免出现空指针异常。 return (a == b) || (a != null && a.equals(b)); } 复制代码
Java中equals方法造成空指针异常的原因及解决方案
null == null
所有整型包装类(如 Integer
)对象值的比较必须使用equals方法。
==比较的是值,只不过基本数据类型变量存的是值,引用数据类型变量存放的是对象的地址罢了
equals不能用于比较基本数据类型的变量,equals()方法存在于Object类中,底层还是return (this == obj) 实现的
特殊:String的equals() 是被重写过的,而重写过的equals方法用法和==相同,即String中equals比较的是值而不是地址
对象的相等,比的是内存中存放的内容是否相等。而引用相等,比较的是他们指向的内存地址是否相等。
多态就是同一个接口,使用不同的实例而执行不同操作, 如空调是一个接口,不同品牌对空调的实现落地就是不同的实例执行不同的操作
float a = 1.0f - 0.9f; float b = 0.9f - 0.8f; System.out.println(a == b);// false:精度丢失 复制代码
BigDecimal a = new BigDecimal("1.0"); BigDecimal b = new BigDecimal("0.9"); BigDecimal c = new BigDecimal("0.8"); BigDecimal x = a.subtract(b);// a-b=0.1 BigDecimal y = b.subtract(c);// b-c=0.1 System.out.println(x.equals(y));// true System.out.println(a.compareTo(b));// a>b结果为1 复制代码
Reference:《阿里巴巴Java开发手册》
BigDecimal 主要用来操作(大)浮点数,BigInteger 主要用来操作大整数(超过 long 类型)
【强制】所有的 POJO 类属性必须使用包装数据类型。
【强制】RPC 方法的返回值和参数必须使用包装数据类型。
【推荐】所有的局部变量使用基本数据类型。
Arrays.asList()
是泛型方法,传入的对象必须是对象数组。它的底层实现:
/** *返回由指定数组支持的固定大小的列表。此方法作为基于数组和基于集合的API之间的桥梁,与 Collection.toArray()结合使用。返回的List是可序列化并实现RandomAccess接口。 */ public static <T> List<T> asList(T... a) { return new ArrayList<>(a); } 复制代码
对Arrays.asList()的集合使用集合的修改方法:add()、remove()、clear()会抛UnsupportedOperationException
正确将数组转为集合的方法: List list = new ArrayList<>(Arrays.asList("a", "b", "c"))
Collections.reverse(list); // list集合反转为数组 s=list.toArray(new String[0]);//反转为数组没有指定类型的话会报错,[0]为了节省空间,只声明类型 复制代码
如果要进行 remove
操作,可以调用迭代器的 remove
方法而不是集合类的 remove 方法。因为如果列表在 任何时间从结构上修改创建迭代器之后,以任何方式除非通过迭代器自身 remove/add
方法,迭代器都将抛出一个 ConcurrentModificationException
,这就是单线程状态下产生的 fail-fast 机制 。
fail-fast 机制:多个线程对 fail-fast 集合进行修改的时,可能会抛出 ConcurrentModificationException
,单线程下也会出现这种情况,上面已经提到过。
java.util
包下面的所有的集合类都是fail-fast的,而 java.util.concurrent
包下面的所有的类都是fail-safe的。
Interator<T> iterator=list.iterator(); //创建list的迭代器后才能在foreach里进行remoe/add操作 while(iterator.hasNext()){...} // 不能直接for(T item:list){list.remove(item)} 复制代码
泛型提供了编译时类型含权检测机制,该机制允许程序员在编译时检测到非法的类型
类型擦除: Java的泛型是伪泛型,这是因为Java在编译期间,所有的泛型信息都会被擦掉
List<Integer> list = new ArrayList<>(); Class<? extends List> clazz = list.getClass(); Method add = clazz.getDeclaredMethod("add", Object.class); // 返回反映clazz对象所表示的类的指定已声明方法 add.invoke(list, "kl"); //通过反射添加String类型,可以添加String类型的元素 复制代码
hashCode()作用是获取哈希码,也叫散列码,返回一个int整数即该对象的内存地址,作用是确定该对象在哈希表的索引位置,定义在Object类中
为什么要有hashCode:如HashSet检查重复时,根据加入的对象的hashCode作比较,若有hashCode相同的对象出现,则通过equals比较内存地址是否相同;若hashCode不同,可避免equals操作而大大减少equals次数,提高执行速度
为什么重写 equals
时必须重写 hashCode
方法 :两个对象相等,则hashCode一定相同,调用equals进行比较后返回true;但是,两个对象hashCode相同,它们不一定相等。因此euqals方法被覆盖过,则hashCode方法也必须被覆盖。
hashCode()
的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode()
,则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据) hashCode()
所使用的杂凑算法也许刚好会让多个对象传回相同的杂凑值。越糟糕的杂凑算法越容易碰撞,但这也与数据值域分布的特性有关(所谓碰撞也就是指的是不同的对象得到相同的 hashCode
。因此需要equals来判断两个对象是否真的相同 Java 基本类型的包装类的大部分都实现了常量池技术,即 Byte,Short,Integer,Long,Character,Boolean;
前面 4 种包装类默认创建了数值[-128,127] 的相应类型的缓存数据,Character创建了数值在[0,127]范围的缓存数据,Boolean 直接返回True Or False。如果超出对应范围仍然会去创建新的对象
两种浮点数类型的包装类 Float,Double 并没有实现常量池技术
Integer i=40;Java 在编译的时候会直接将代码封装成 Integer i=Integer.valueOf(40);,从而使用常量池中的对象。Integer i=new Integer(40)会创建对象
Java 程序在执行子类的构造方法之前,如果没有用 super()
来调用父类特定的构造方法,则会调用父类中无参构造,若父类没有无参构造则编译出错
StringBuilder
与 StringBuffer
都继承自 AbstractStringBuilder
类,在 AbstractStringBuilder
中使用字符数组保存字符串 char[] value
,两种对象都是可变的。 StringBuffer
对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。 StringBuilder
没有加同步锁,所以是非线程安全的。
String
中用 final 保存字符串, private final char value[]
,所以 String
对象是不可变的。可以理解为常量,线程安全,每次对 String
类型改变时,都会生成新的 String
对象,然后将指针指向新的对象
StringBuffer
每次都会对 StringBuffer
对象本身进行操作,而不是生成新的对象并改变对象引用
transient 关键字只能修饰变量来禁止该变量不被序列化;当对象被反序列化时,被 transient 修饰的变量值不会被持久化和恢复
两种常用键盘输入方法:
Scanner input = new Scanner(System.in) BufferedReader input = new BufferedReader(new InputStreamReader(System.in)); String s = input.nextLine(); String s = input.readLine(); 复制代码
排序:
void reverse(List list)//反转 void shuffle(List list)//随机排序 void sort(List list)//按自然排序的升序排序 void sort(List list, Comparator c)//定制排序,由Comparator控制排序逻辑 void swap(List list, int i , int j)//交换两个索引位置的元素 void rotate(List list, int distance)//旋转。当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将 list的前distance个元素整体移到后面。 复制代码
查找、替换
int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的 int max(Collection coll)//根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll) int max(Collection coll, Comparator c)//根据定制排序,返回最大元素,排序规则由Comparatator类控制。类比int min(Collection coll, Comparator c) void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素。 int frequency(Collection c, Object o)//统计元素出现次数 int indexOfSubList(List list, List target)//统计target在list中第一次出现的索引,找不到则返回-1,类比int lastIndexOfSubList(List source, list target). boolean replaceAll(List list, Object oldVal, Object newVal), 用新元素替换旧元素 复制代码
同步控制
HashSet,TreeSet,ArrayList,LinkedList,HashMap,TreeMap 都是线程不安全的, 线程安全的实现类有vector,stack,hashtable
JUC集合包中的List和Set实现类包括: CopyOnWriteArrayList, CopyOnWriteArraySet和ConcurrentSkipListSet 。实现线程安全,支持高并发
1, CopyOnWriteArrayList相当于线程安全的ArrayList,它实现了List接口。CopyOnWriteArrayList是支持高并发的。
2,CopyOnWriteArraySet相当于线程安全的HashSet,它继承于AbstractSet类。CopyOnWriteArraySet 内部包含一个CopyOnWriteArrayList对象(聚合关系),它是通过CopyOnWriteArrayList实现的。
JUC集合包中Map的实现类包括: ConcurrentHashMap和ConcurrentSkipListMap 。
ConcurrentHashMap是线程安全的哈希表(相当于线程安全的HashMap);它继承于AbstractMap类,并且实现ConcurrentMap接口。ConcurrentHashMap是通过“锁分段”来实现的,它支持并发。
-ConcurrentSkipListMap是线程安全的有序的哈希表(相当于线程安全的TreeMap); 它继承于AbstractMap类,并且实现ConcurrentNavigableMap接口。ConcurrentSkipListMap是通过“跳表”来实现的,它支持并发。
ConcurrentSkipListSet是线程安全的有序的集合(相当于线程安全的TreeSet);它继承于AbstractSet,并实现了NavigableSet接口。ConcurrentSkipListSet是通过ConcurrentSkipListMap实现的,它也支持并发。
JUC集合包中Queue的实现类包括: ArrayBlockingQueue, LinkedBlockingQueue, LinkedBlockingDeque, ConcurrentLinkedQueue和ConcurrentLinkedDeque
(01) ArrayBlockingQueue 是数组实现的线程安全的有界的阻塞队列。
(02) LinkedBlockingQueue是单向链表实现的(指定大小)阻塞队列,该队列按 FIFO(先进先出)排序元素。
(03) LinkedBlockingDeque是双向链表实现的(指定大小)双向并发阻塞队列,该阻塞队列同时支持FIFO和FILO两种操作方式。
(04) ConcurrentLinkedQueue 是单向链表实现的无界队列,该队列按 FIFO(先进先出)排序元素。
(05) ConcurrentLinkedDeque 是双向链表实现的无界队列,该队列同时支持FIFO和FILO两种操作方式。
ArrayList:
sort()
、查找 : binarySearch()
、比较: equals()
、填充 : fill()
、转列表: asList()
、 转字符串 : toString()
、复制: copyOf()
Exception(异常):是程序本身可以处理的异常。重要子类: RuntimeException(JVM异常) , NullPointerException , ArithmeticException , ArrayIndexOutOfBoundsException 下标越界
public string getMessage()
:返回异常发生时的简要描述 public string toString()
:返回异常发生时的详细信息 public string getLocalizedMessage()
:返回异常对象的本地化信息。使用 Throwable
的子类覆盖这个方法,可以生成本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与 getMessage()
返回的结果相同 public void printStackTrace()
:在控制台上打印 Throwable
对象封装的异常信息 线程有6种状态:
线程创建之后它将处于 NEW(新建) 状态,调用 start()
方法后开始运行,线程这时候处于 READY(可运行) 状态。可运行状态的线程获得了 cpu 时间片(timeslice)后就处于 RUNNING(运行) 状态。
当线程执行 wait()
方法之后,线程进入 **WAITING(等待)**状态。进入等待状态的线程需要依靠其他线程的通知才能够返回到运行状态,而 TIME_WAITING(超时等待) 状态相当于在等待状态的基础上增加了超时限制,比如通过 sleep(long millis)
方法或 wait(long millis)
方法可以将 Java 线程置于 TIMED WAITING 状态。当超时时间到达后 Java 线程将会返回到 RUNNABLE 状态。当线程调用同步方法时,在没有获取到锁的情况下,线程将会进入到 BLOCKED(阻塞) 状态。线程在执行 Runnable 的 run()
方法之后将会进入到 TERMINATED(终止) 状态。
Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的:
按照操作单元划分,可以划分为字节流和字符流;
按照流的角色划分为节点流和处理流。
Socket
和 ServerSocket
相对应的 SocketChannel
和 ServerSocketChannel
两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞 I/O 来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发