Java基本功不牢_java基础不牢固容易踩的坑

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 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,一般1到2个。

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

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

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

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

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

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

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

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值