Java基础中一些需要注意的问题
Integer的比较问题
以下输出结果为false的是? Integer i01=59; int i02=59; Integer i03=Integer.valueOf(59); Integer i04=new Integer(59); A:System.out.println(i01==i02); B:System.out.println(i01==i03); C:System.out.println(i03==i04); D:System.out.println(i02==i04);
这题的答案是C,为什么呢?
解答:- Java中基本类型的包装类大部分都实现了常量池技术,这些类包括Byte,Short,Integer,Long,Character
本题中的Integer内部有一个IntegerCache类 ,该类的作用是将数值等于-128-127区间的Integer实例
缓存到cache数组中。当使用valueOf方法创建Integer实例时,会直接复用缓存中的实例
即,Integer i03=Integer.valueOf(59);
这种方式并不会创建对象,而是直接从缓存中拿值是59的
实例,跟Integer i01=59;
一样,i01是装箱操作,其本质也是调用valueOf方法,从缓存中拿59的实例
所以他们的对象地址是一样的,即是获取的同一个对象,如果获取的值并不在-128到127范围内,
那就会在堆中创建对象了。 - 我们很好判断A,B是对的,那为什么D也是对的呢?因为在i02与i04对比的时候,会将i04进行拆箱操作
再去比较,即不管i04是不是new出来的,在与基本类型比较时,都会拆箱后再去比较。 - C是错误的,因为在比较时,两者都是Integer类型,并不会进行拆箱操作,所以比较对象地址,为false
总结
- 无论如何,Integer与new Integer不会相等。不会经历拆箱过程
- 两个都是非new出来的Integer,如果数在-128到127之间,则是true,否则为false
java在编译Integer i2 = 128的时候,被翻译成-> Integer i2 = Integer.valueOf(128);
而valueOf()函数会对-128到127之间的数进行缓存 - 两个都是new出来的,都为false
- int和integer(无论new否)比,都为true,因为会把Integer自动拆箱为int再去比
看大佬做的图
**练习**
Integer a = 1; Integer b = 1; Integer c = 500; Integer d = 500; System.out.print(a == b); System.out.print(c == d); 答案是true,false,原理就是上面的解析。
- Java中基本类型的包装类大部分都实现了常量池技术,这些类包括Byte,Short,Integer,Long,Character
BigDecimal的使用
BigDecimal作用
BigDecimal主要用来操作(大)浮点数,BigInteger 主要用来操作大整数(超过long类型)。
BigDecimal可以防止出现浮点数精度丢失问题。例如以下代码就会因为精度丢失而产生问题float a = 1.0f - 0.9f; float b = 0.9f - 0.8f; System.out.println(a);// 0.100000024 System.out.println(b);// 0.099999964 System.out.println(a == b);// false
BigDecimal的大小比较
a.compareTo(b)
: 返回 -1 表示a
小于b
,0 表示a
等于b
, 1表示a
大于b
。BigDecimal a = new BigDecimal("1.0"); BigDecimal b = new BigDecimal("0.9"); System.out.println(a.compareTo(b));// 1
BigDecimal保留几位小数
通过setScale
方法设置保留几位小数以及保留规则。保留规则一般是向上向下取整BigDecimal m = new BigDecimal("1.255433"); BigDecimal n = m.setScale(3,BigDecimal.ROUND_HALF_DOWN); System.out.println(n);// 1.255
BigDecimal的使用注意事项
我们在使用BigDecimal时,为了防止精度丢失,推荐使用它的BigDecimal(String)构造方法来创建对象。
而不是使用BigDecimal(double),因为会存在精度损失的风险。BigDecimal bigDecimal = new BigDecimal("1.23");//推荐 BigDecimal bigDecimal2 = new BigDecimal(1.23);//不推荐
Arrays.asList()的使用
Arrays.asList()将一个数组转换为一个List集合。但我们并不能使用该集合的,add/remove/clear方法,不然会抛出
UnsupportedOperationException
,通过这个方式获得的集合是Arrays的一个内部类,
并没有实现集合的修改方法,而且当我们对原数组修改时,集合中的元素也会被修改第二个需要注意的点是:我们使用该方法传递的数组必须是对象数组,而不是基本类型。
Arrays.asList()是泛型方法,传入的对象必须是对象数组。int[] myArray = {1, 2, 3}; List myList = Arrays.asList(myArray); System.out.println(myList.size());//1 System.out.println(myList.get(0));//数组地址值 System.out.println(myList.get(1));//报错:ArrayIndexOutOfBoundsException int[] array = (int[]) myList.get(0); System.out.println(array[0]);//1
当传入一个原生数据类型数组时,Arrays.asList()的真正得到的参数就不是数组中的元素,
而是数组对象本身!此时List 的唯一元素就是这个数组,这也就解释了上面的代码。
使用包装类型数组就可以解决这个问题Integer[] myArray = {1, 2, 3};
怎么正确的将数组转换成ArrayList
List list = new ArrayList<>(Arrays.asList("a", "b", "c"))
foreach循环中修改元素的问题
不能在foreach中进行元素的add/remove操作,不然会抛出ConcurrentModificationException,
如果要进行修改操作,可以通过迭代器的修改方法。//下面这段代码就是问题代码, //经过测试,新版jdk中,foreach循环中可以调用集合remove方法不会报错,而add方法仍然报错 ArrayList<String> strings = new ArrayList<>(); strings.add("hello"); strings.add("world"); for(String s:strings){ if (strings.contains("hello")){ strings.add("wanYi"); } } //使用iterator方式 ArrayList<String> strings = new ArrayList<>(); strings.add("hello"); strings.add("world"); Iterator<String> iterator = strings.iterator(); while (iterator.hasNext()){ String s = iterator.next(); if (Objects.equals(s,"hello")){ iterator.remove(); } } //更简单的方式,使用集合的removeIf方法 strings.removeIf(s -> Objects.equals(s, "hello"));
异常需要注意的地方
- 如果try块中抛出了一个在catch子句中说明的异常类,那么程序将跳过try语句的其余代码,
执行catch子句中的处理器代码。 - 无论是否抛出异常finally语句块最终都会执行。原因是:编译器会将finally块中的代码复制两份
并分别添加在try和catch的后面 - 当try块中包含return语句时,在执行return语句前会先执行finally块,如果finally块中也有return语句,这个return语句的返回值会将try块中return语句的返回值覆盖掉。
- 如果try块中抛出异常,finally中也抛出相同类型异常,那么原始异常将会丢失,转而抛出finally中的异常
示例1
public static void main(String[] args) { int result = test3(); System.out.println(result); } public static int test3() { //try 语句块中有 return 语句时的整体执行顺序 int i = 1; try { i++; System.out.println("try block, i = " + i); return i; } catch (Exception e) { i++; System.out.println("catch block i = " + i); return i; } finally { i = 10; System.out.println("finally block i = " + i); } } //输出结果如下: try block, i = 2 finally block i = 10 2 /* 当try块中有return语句时,仍然会执行finally块的代码, 并且返回值是try块中的i值 */
示例2
public static int test4() { //finally 语句块中有 return 语句 int i = 1; try { i++; System.out.println("try block, i = " + i); return i; } catch (Exception e) { i++; System.out.println("catch block i = " + i); return i; } finally { i++; System.out.println("finally block i = " + i); return i; } } //输出结果如下: try block, i = 2 finally block i = 3 3 /* 当try块和finally块中都有return时,返回的是finally中的i */
处理异常时的建议
public void retrieveObjectById(Long id) { try { //..some code that throws SQLException } catch (SQLException ex) { throw new RuntimeException("Exception in retieveObjectById”, ex); } finally { //clean up resultset, statement, connection etc } } /* 当有异常时就进行处理,抛出,如果只是ex.printStacktrace();会使得异常并没有中断程序 进而调用代码继续执行,导致更多的异常。 */
- 如果try块中抛出了一个在catch子句中说明的异常类,那么程序将跳过try语句的其余代码,