框架是个好东西,可早晚有一天会过时,这世界上就没有亘古不变的东西,来学下Java基础吧
equals的正确使用
equals方法非常容易抛出空指针异常,如以下代码块
public static void main(String[] args) {
boolean b = compareToHello(null);
System.out.println("比较结果为:"+b);
}
// 将传入的字符串和helloworld字符串比较
static boolean compareToHello(String a){
return a.equals("helloworld");
}
常用的改进方法为:
// 将传入的字符串和helloworld字符串比较
static boolean compareToHello(String a){
return "helloworld".equals(a);
}
因为helloworld字符串取自常量池,必不为null,不用担心
如果是要传入两个字符串,比较他们,怎么办呢
常规思路:
因为两个字符串都有可能是null,所以要先判断他们是否为空【只需要判断一个就够了】,然后调用非空对象的equals方法即可
不过更推荐Java7引进的工具类Objects,具体代码如下
public static void main(String[] args) {
boolean b = compareToHello(null, null);
}
// 将传入的字符串和helloworld字符串比较
static boolean compareToHello(String a, String b) {
return Objects.equals(a, b);
}
Objects的equals方法的源码如下
public static boolean equals(Object a, Object b) {
// 可以避免空指针异常。如果a==null的话此时a.equals(b)就不会得到执行,避免出现空指针异常。
return (a == b) || (a != null && a.equals(b));
}
⚠️:Objects.equals(null,null)
的返回值为true,因为null=null
成立
对于浮点数之间的比较,不能直接用equals方法去比较,原因可从下面代码块体现一二
public static void main(String[] args) {
float a = 1f - 0.9f;
float b = 0.9f - 0.8f;
System.out.println(a);
System.out.println(b);
}
<<<0.100000024
<<<0.099999964
了解C和计算机底层的朋友都应该知道出现这个的问题,和计算机对浮点数的存储有关,精度丢失,这时候用equals比较肯定出问题
常见解决方法一,提高精度,使用双精度数:
public static void main(String[] args) {
double a = 1 - 0.9;
double b = 0.9 - 0.8;
System.out.println(a);
System.out.println(b);
System.out.println(a==b);
}
<<<0.09999999999999998
<<<0.09999999999999998
<<<true
解决方法二,使用BigDecimal类:
public static void main(String[] args) {
BigDecimal num1 = new BigDecimal("1.0");
BigDecimal num2 = new BigDecimal("0.9");
BigDecimal num3 = new BigDecimal("0.8");
// 相当于是1.0-0.9
BigDecimal a = num1.subtract(num2);
BigDecimal b = num2.subtract(num3);
System.out.println(a);
System.out.println(b);
System.out.println(a.equals(b));
}
<<<0.1
<<<0.1
<<<true
注意:
为了防止精度丢失,《阿里巴巴Java开发手册》中推荐使用BigDecimal类的BigDecimal(String)构造方法
使用总结:
BigDecimal操作精度要求高的浮点数,BigInteger操作大的整数【用来操作long类型的整数】(用法和BigDecimal差不多,有兴趣的去翻翻源码),并且BigDecimal底层使用了BigInteger
如果对整型数比较有兴趣的朋友,可以去看看以前的博客 Java基础(一),关于常量池的介绍,说不定会有收获哦
基本数据类型和封装类型使用标准
参照《阿里巴巴Java开发手册》
- 所有POJO类属性必须是包装类型
- RPC方法的参数和返回值使用包装类型
- 局部变量使用基本数据类型
POJO:实体类对象,类中的所有字段设置为private,提供get和set方法供外界访问的类
RPC方法:供远程调用的方法,在SpringCloud和Dubbo中会有所涉及
Arrays.asList方法
Arrays.asList是开发中常用的方法,具体作用是将数组转换成一个集合(有关Arrays类更多介绍可以查看Java基础(三))
简单用法:
public static void main(String[] args) {
String[] a = {"hello","world"};
List<String> list = Arrays.asList(a);
}
因为Java设计出的集合类就是为了优化数组,做了越界访问一系列拦截
⚠️:使用Arrays.list获取的数组注意事项
传递的数组里面存储的必然是对象,因为集合里面不能存储基本数据类型,只能存储对象
如果传递的是原生数据类型数组,如下代码所示:
public static void main(String[] args) {
int[] a = {1, 2, 3};
List<int[]> list = Arrays.asList(a);
System.out.println(list);
}
<<<[[I@6b71769e]
会直接将int[]当成一个对象放到list[0]的位置上去
解决办法(使用包装类):
public static void main(String[] args) {
Integer[] a = {1, 2, 3};
List<Integer> list = Arrays.asList(a);
System.out.println(list);
}
<<<[1, 2, 3]
我们可以看下Arrays.asList的源码
@SafeVarargs
public static <T> List<T> asList(T... a) {
return new Arrays.ArrayList(a);
}
返回的List是Arrays的内部类ArrayList,并不是我们熟悉的java.util.ArrayList
private static class ArrayList<E> extends AbstractList<E> implements RandomAccess, Serializable {
}
之所以调用add,remove,clear方法会报错,因为该类没有重写这些方法,进入他的父类AbstractList
public void add(int index, E element) {
throw new UnsupportedOperationException();
}
public E remove(int index) {
throw new UnsupportedOperationException();
}
难怪抛UnsupportedOperationException异常,不抛出才奇怪呢
将其转换为我们熟悉的java.util.ArrayList
1.手动转换(不推荐,典型吃力不讨好,不过可以了解底层数据结构)
只说思路:获取当前List,获取其长度,新建一个等长度的Arraylist,循环遍历list,将数据移动到ArrayList里面即可
2.最简单的办法(推荐)
public static void main(String[] args) {
Integer[] a = {1, 2, 3};
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(a));
list.add(4);
System.out.println(list);
}
<<<[1, 2, 3, 4]
public static void main(String[] args) {
Integer[] a = {1, 2, 3};
List<Integer> list = Arrays.stream(a).collect(Collectors.toList());
list.add(4);
System.out.println(list);
}
<<<[1, 2, 3, 4]
依赖boxed的装箱操作还可以将基本数据类型实现数据转换(本质就是将里面的每个基本数据类型封装)
public static void main(String[] args) {
int[] a = {1, 2, 3};
List<Integer> list = Arrays.stream(a).boxed().collect(Collectors.toList());
list.add(4);
System.out.println(list);
}
<<<[1, 2, 3, 4]
Collection.toArray方法
正好与Arrays.asList方法的功能相反
具体使用如下
public static void main(String[] args) {
// 将数组转换成集合
Integer[] a = {1, 2, 3};
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(a));
// 将集合转换回数组
Integer[] array = list.toArray(new Integer[0]);
System.out.println(Arrays.toString(array));
}
<<<[1, 2, 3]
说明:
方法原型为
public <T> T[] toArray(T[] a) {}
需要在参数位置传入泛型, 由于 JVM 优化,
new Integer[0]
作为Collection.toArray()
方法的参数现在使用更好 根据具体数据类型传参,并不一定都是Integer
不要在foreach中进行remove/add操作
如果要进行remove操作,使用迭代器的remove方法
因为用foreach本质上就是创建了一个迭代器,以任何方式除非通过迭代器自身remove/add
方法,迭代器都将抛出一个ConcurrentModificationException
(并发修改异常)这就是fail-fast机制
Java.util下的所有集合类都是fail-fast的,而java.util.concurrent下的所有的类都是fail-safe的
具体事例:
public static void main(String[] args) {
// 将数组转换成集合
Integer[] a = {1, 2, 3};
ArrayList<Integer> list = new ArrayList<>(Arrays.asList(a));
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()){
Integer next = iterator.next();
if (next==3){
iterator.remove();
}
}
System.out.println(list);
}
<<<[1, 2]
同样来自阿里的开发手册
具体原因还没搞懂,结论先给出来
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
for (String i : list){
if ("1".equals(i)){
list.remove(i);
}
}
System.out.println(list);
}
<<<[2]
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
for (String i : list){
if ("2".equals(i)){
list.remove(i);
}
}
System.out.println(list);
}
<<<Exception in thread "main" java.util.ConcurrentModificationException
<<< at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1042)
<<< at java.base/java.util.ArrayList$Itr.next(ArrayList.java:996)
<<< at cn.luckycurve.demo7.Test.main(Test.java:22)
目前已实验结论:字符串类型的ArrayList集合,用foreach删除第一个元素不会出错,删除其他元素会出错
Integer类型的ArrayList集合,用foreach删除任意一个都会出错
尽管有这样的规律,但小伙伴们在项目和工作中千万不要这么做,出个Bug改到吐血,跟着规范走~
具体的原因还没搞清楚,喜欢探索的同学可以去挖一挖ArrayList和Iterator