java基础不牢固容易踩的坑

  经过一年java后端代码以及对jdk源码阅读之后的总结,对java中一些基础中的容易忽略的东西写下来,给偏爱技术热爱开源的Coder们分享一下,避免在写代码中误入雷区。

 (注:如无特殊说明,均以jdk8为基础,本文所有例子均已通过编译器通过,且对输出进行了验证)。

1.关于基本类型的包装类的。

  基本类型boolean、char、byte、short、int、long、float、double。是java的特殊类型,特殊性在于区别于对象的存储,对象存储的是引用,引用指向在jvm堆中分配的值,基本类型直接存储的就是值,能提高效率。

  同时java遵循面向对象思想为每个基本类型都提供了封装类:Boolean、Character、Byte、Short、Integer、Float、Double。

坑1:变量赋值与类型转换。

  变量赋值其实并不算是个坑,因为编译器会自动检查,例如long var = 2;编译器会报错。

  类型转换分为自动转换和强制转换,这里不在赘述,具体转换规则自行查询。

  赋值的时候 = 和+=的区别,+=会自动转换类型。

  short num;
  num = num + 1; //error
  num += 1; //ok

坑2:计算

  整数相除默认只保留整数,即使赋值给浮点类型也不行。

  double d = 5 / 2;
  System.out.println(d); //2

  byte相加超出长度后数值会变得很怪异。

  byte num = 127;
  num += 1;
  System.out.println(num);//-128

  两个float相加结果会存在一定的误差等等。

  float a1 = 1.001f;
  float a2= 1.819f;
  float a3 = a1 + a2;
  System.out.println(a3);//jdk8: 2.8200002
  System.out.println(12.0 - 11.9 == 0.1) //false

坑3:装箱与拆箱 

  int a =100;
  Integer b = 100;
  Integer c= 100;
  System.out.println(a==b); //true
  System.out.println(b==a); //true
  System.out.println(b==c); //true
基本类型和包装类型运算时会自动拆箱,所以ab相等;
当把100换成200 b==c会返回false。因为==比较的是引用;
b==c为true的原因是Integer采用了缓存,对-128到127之间的数据不再自动生成,而是直接引用(请看Integer中的IntegerCache内部类),类似于String;

2. null值

  1.null关键字,大小写敏感

  2.null是引用类型的默认值

  4.null既不是对象也不是类型,可以强制转换成任何引用类型。

    String s = (String) null; //ok

    int a = (int) null;  //error

  4.null值的引用类型变量,instance会返回false,如下:

  Integer iAmNull = null;
  System.out.println(iAmNull instanceof Integer); //false

  5.null值的引用变量调用非静态方法,会抛npe,调用静态方法是可以的。

3 void,Void

  void在逻辑上是一种数据类型,但不是基本类型,也不是引用类型。我们暂且不管它到底是什么类型,因为很多人都说不清。

  void提供了包装类Void,看源码我们会发现它被定义成final,而且构造方法是private,也就是说不能实例化。

  Void类型只能赋值为null,而void不能赋值,仅仅用来作为方法返回值输出。

  Void能作为方法输入参数当做占位符,只能传值为null。

4.多态

  1.父类引用能指向子类对象,调用的方法具体取决于引用的对象,而不是取决于引用。

  public class A {
    public String show(D obj){
      return ("A and D");
    }  
    public String show(A obj){
      return ("A and A");
    }
  }
  class B extends A{
    public String show(B obj)...{
      return ("B and B");
    }
    public String show(A obj){
      return ("B and A");
    }
  }
  class C extends B{}
  class D extends B{}

 A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println(a1.show(b)); ①
System.out.println(a1.show(c)); ②
System.out.println(a1.show(d)); ③
System.out.println(a2.show(b)); ④
System.out.println(a2.show(c)); ⑤
System.out.println(a2.show(d)); ⑥
System.out.println(b.show(b)); ⑦
System.out.println(b.show(c)); ⑧
System.out.println(b.show(d)); ⑨
  结果
  ①   A and A
  ② A and A
  ③ A and D
  ④ B and A
  ⑤ B and A
  ⑥ A and D
  ⑦ B and B
  ⑧ B and B
  ⑨ A and D
  
  2.子类对父类方法不可见的情况下是不会覆盖的,而是重新定义了一个方法。
  3.继承关系只有方法会覆盖,成员变量不会被覆盖。  
  public class A {
   protected int i = 1;
  public void show(){
   System.out.println(i);
  }
  }
  public class B extends A{
      private int i = 10;
  public void show(){
   System.out.println(i);
  }
  }
  A a = new A();
  A a1 = new B();
  B b = new B();
  a.show(); //1
  a1.show(); //10
  b.show(); //10

5. super

  super并没有代表超类的一个引用的能力,只是代表调用父类的方法而已。

  public class Test extends Number{
    public static void main(String[] args) {
      new Test().test();
    }
    private void test(){
      System.out.println(super.getClass().getName());  //获取父类方法名getClass().getSuperClass().getName(); 
    }
  }

  这里应结合多态的override来理解上面的输出。

6.字符串

  老生常谈的问题了,字符串采用常量池缓存,不宜创建太多字符串,subString、new、+、等操作慎用,会创建很多字符串常量无法回收,当运行久了之后会占用越多越多的内存。

  字符串做参数,并不会改变改变实参的值。

7.多线程

  线程安全的问题建议单独去看。充分考虑到线程安全问题,不会出现死锁问题。

  Object提供的wait、notify、notify不建议对多线程了解不深入的人去用。

  建议使用可重入锁替代synchronized。

  多线程知识较多,这里不做详细说明。

8.异常处理

  1.异常处理块中可以继续抛异常。

  2.try块可以不需要catch或finally,但二者必须至少有一个

    3.finially块中return 语句会覆盖try块中的return,finally块在try块代码执行完后,return语句之前执行。

  public class MyClass {
  public static void main(String[] args) {
   System.out.print(new MyClass().getNum()); //4
      }
  int getNum(){
   try {
   System.out.println("try block");
   return 3;
   } finally {
   System.out.println("finally block");
   return 4;
  }
   }
  }

   输出结果是:

    try block
    finally block
  4.碰到事务方法,异常处理要特别注意。

  5.finially不一定必执行,当在try块中有system.exit(1);

    try {
    System.out.println("try block begin");
    System.exit(1);
    System.out.println("try block end");
    } catch (Exception e){
     System.out.println("catch block begin");
     System.exit(1);
     System.out.println("catch block end");
    } finally {
     System.out.println("finally block");
    }

    以上代码输出try block begin  

  6.异常不建议往上抛,特殊情况除外。

9.正则表达式

replace、split等所有以正则表达式作为参数的方法

一定要注意正则表达式的含义,例如如下输入
  String a = "acb..";
  System.out.println(a.replaceAll(".","b")); //bbbbb

  转义字符串,尤其是路径问题

10.静态相关

  static可以修饰类(包含内部类)、成员方法、成员变量、类中代码块。

  static只能修饰类变量,不能修饰局部变量,编译器会报错。

  类启动加载顺序。静态>非静态,成员变量>代码块>构造方法,父类>子类。

  静态方法和静态变量会随类的加载而加载,静态内部类只有在使用时才会加载。

  static 不能和abstract同时使用,可以和final同时使用。

11 循环删除

在list中删除a,看起来一切正常。如下所示。

  List<String> list = new LinkedList<>();
  list.add("a");
  list.add("b");
  list.add("c");
  for (String str : list){
   if ("b".equals(str)){
   list.remove(str);
   }
  }
  System.out.println(list); //[a, c]
 假如删除a呢?ConcurrentModificationException,自己模拟下流程思考下原因。

12. 特殊关键字

  1.volatile:一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

    1)保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

    2)禁止进行指令重排序。

  2.transient:修饰成员变量。当对象被序列化时(写入字节序列到目标文件)时,transient阻止实例中那些用此关键字声明的变量持久化。

  3.strictfp:一旦使用了关键字strictfp来声明某个类、接口或者方法时,那么在这个关键字所声明的范围内所有浮点运算都是精确的,符合IEEE-754规范的。

    例如一个类被声明为strictfp,那么该类中所有的方法都是strictfp的。

  4.native:原生态方法,可以调用其他语言。这里不做详细说明。

13 枚举

  1.枚举为每个枚举对象创建一个实例,在首次使用时初始化。

  public static void main(String[] args) {
   weekday mon = weekday.mon;
  }
  public enum weekday {
   mon, tue, wes, thus, fri;
  private weekday() {
   System.out.println("hello"); //输出hello五次
  }
  }
  2.构造方法可以传值。
  
  public static void main(String[] args) {
   weekday mon = weekday.mon;
  }
  public enum weekday {
   mon, tue(1), wes(2), thus, fri;
  private weekday() {
   System.out.print("hello ");
  }
   private weekday(int a) {
   System.out.print("ok ");
  }
  }
  输出:hello  ok  ok  hello  hello

14 泛型

 1. 泛型类、泛型方法,用<>表示,<>内的内容只要符合变量命名规范即可,不要求是T、K、E、V

 2. 泛型可以有多个变量,例如public Class MyClass<T1,T2,T3,T4>,一般1到2个。

 3. Set<Integer> 不是Set<Number>的子类,逻辑上不具备任何继承关系,二者都属于Set类。Set<Integer>赋值给Set<Number>会报错。

 4. 上面一行的解决方式是泛型通配符。

 5. 泛型的类型参数只能是引用类型,如Set<int>编译报错。

 6. 不能对确切的泛型类型使用instance操作,

 7. <T extends Number>作用于方法或者类上,而 <? extends Number> 则不可以。

 8. 泛型运行期即被擦除,所以不能通过Type type = new TypeToken<TestGeneric<String>>(){}.getType(); 这种方式在运行期动态获取泛型类型。

 9. 泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。

  

 

java基础不牢固容易踩的坑的更多相关文章

  1. JAVA基础(零)—— 踩坑与错误(常更)

    JAVA基础(零)-- 踩坑与错误(常更) 1 坑 考虑输入为null的情况 自动转换 x/Math.pow(10,i)*Math.pow(10,i) //由于math.pow()返回double类型 ...

  2. 从零开始学 Java - Spring 支持 CORS 请求踩的坑

    谁没掉进过几个大坑 记得好久之前,总能时不时在某个地方看到一些标语,往往都是上面一个伟人的头像,然后不管是不是他说的话,下面总是有看起来很政治正确且没卵用的屁话,我活到目前为止,最令我笑的肚子痛得是下 ...

  3. JAVA 基础 重新开始

    之前做android开发,因为JAVA基础不牢固的原因,自己对写代码很不自信,很多时候要去找源码或者在相近的代码上修修改改以得到想要的结果,从某种意义上来说这根本算不上真正意义上的程序员.后来看到某位 ...

  4. 【原创】这道Java基础题真的有坑!我也没想到还有续集。

    前情回顾 自从我上次发了<这道Java基础题真的有坑!我求求你,认真思考后再回答.>这篇文章后.我通过这样的一个行文结构: 解析了小马哥出的这道题,让大家明白了这题的坑在哪里,这题背后隐藏 ...

  5. Java基础-面向接口编程-JDBC详解

    Java基础-面向接口编程-JDBC详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.JDBC概念和数据库驱动程序 JDBC(Java Data Base Connectiv ...

  6. Java基础-IO流对象之序列化(ObjectOutputStream)与反序列化(ObjectInputStream)

    Java基础-IO流对象之序列化(ObjectOutputStream)与反序列化(ObjectInputStream) 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.对象的序 ...

  7. Java 基础(6)——关键字 & 标识符 & 转义字符

    依然是基础的一天,看一看就好~ 关键字 之前就有说过关键字哦~ 注:关键字就是程序发明者规定的有特殊含义的单词. from Java基础(2) Java 中除了关键字以外还有暂时没有成为关键字的保留字 ...

  8. [ 转载 ] Java基础二

    前言 关于赢在面试的Java题系列基本收集整理完成了,所有题目都是经过精心挑选的,很基础又考验求职者的基本功,应该说被面试到的几率很大.这里整理挑选出来供大家面试前拿来看一看,所有题目整理自网络,有一 ...

  9. java基础知识 + 常见面试题

    准备校招面试之Java篇 一. Java SE 部分 1.1 Java基础 1. 请你解释Object若不重写hashCode()的话,hashCode()如何计算出来的? Object 的 hash ...

随机推荐

  1. c#-day03学习笔记

    循环语句 一共有三种 1: For循环 2: while 循环 3: do while 循环 //1             //2             //4 For循环  语法       f ...

  2. SpringBoot系列之——整合JPA、mysql

    一.JPA      1. 概念:JPA顾名思义就是Java Persistence API的意思,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中. 2. ...

  3. 冒泡排序,冒泡性能优化--java实现

    冒泡排序说明: 一次比较两个元素,如果他们的顺序错误就把他们交换过来. 重复地进行直到没有再需要交换,也就是说已经排序完成. 越小的元素会经由交换慢慢“浮”到数列的顶端. 冒泡排序算法的运作如下: 比 ...

  4. angular2-搭建环境

    npm  模块将被下载安装到[全局目录]中.[全局目录]通过 npm config set prefix "目录路径" 来设置.通过 npm config get prefix 来 ...

  5. tween.js 插件

    1.是什么? jQueryTween是一款轻量级的jQuery补间动画工具库插件.使用jQueryTween可以制作出各种平滑的动画过渡效果.该插件基于tween.js,旨在简化各种补间动画操作,提供 ...

  6. 零基础逆向工程40_Win32_14_枚举窗口_模拟鼠标键盘

    1 查找窗口 1.1 代码案例 //查找指定窗口 TCHAR szTitle[MAX_PATH] = {0}; HWND hwnd = ::FindWindow(TEXT("#32770&q ...

  7. Android 5.0 以上监听网络变化

    大家好,大概有一个多月没有更新博客了,我是干什么去了呢?很明显,程序员当然要加班……这一次跟大家分享一下新项目的一些心得. 监听网络变化在开发中是经常用到的,例如我们断网有一些友好的提示,或者根据不同 ...

  8. SpringMVC ------JstlView

    摘要: Spring为展现层提供的基于MVC设计理念的优秀的Web框架,是目前最主流的MVC框架之一 .Spring3.0后全面超越Struts,成为最优秀的MVC框架 .SpringMVC通过一套M ...

  9. check_mk手动安装

    官方omd rpm包安装 yum -y install /tmp/check-mk-raw-1.2.6p2.demo-el6-34.x86_64.rpm omd create la omd confi ...

  10. C语言中 fputs() fgets() 的使用方法

    一.读字符串函数fgets函数的功能是从指定的文件中读一个字符串到字符数组中,函数调用的形式为: fgets(字符数组名,n,文件指针): 其中的n是一个正整数.表示从文件中读出的字符串不超过 n-1 ...