CH1 数据类型

基本数据类型

Java的数据类型:
Java基础 - 图1
基本数据类型包括 boolean(布尔型)、float(单精度浮点型)、char(字符型)、byte(字节型)、short(短整型)、int(整型)、long(长整型)和 double (双精度浮点型)共 8 种。

注意:long 类型的数据后面要加上L ,否则将会作为整数解析。

包装类型

基本数据类型都有对应的包装类型。他们之间的赋值操作使用自动装箱与自动拆箱来完成。

基本类型: boolean,char,byte,short,int,long,float,double
包装类型:Boolean,Character,Byte,Short,Integer,Long,Float,Double

  1. Integer x = 2; // 装箱 调用了 Integer.valueOf(2)
  2. int y = x; // 拆箱 调用了 X.intValue()

基本类型和包装类型的区别

  • 基本类型比包装类型更高效。基本类型在栈中存储的数据,而包装类型在栈中存储的是堆中的引用。
  • 包装类型可以为null,基本类型不可以。 它使得包装类型可以应用于 POJO 中,而基本类型则不行。那为什么 POJO 的属性必须要用包装类型呢?《阿里巴巴 Java 开发手册》上有详细的说明, 数据库的查询结果可能是 null,如果使用基本类型的话,因为要自动拆箱(将包装类型转为基本类型,比如说把 Integer 对象转换成 int 值),就会抛出 NullPointerException的异常。
  • 包装类型可以用于泛型,基本类型不可以。因为泛型在编译的时候会进行类型擦除,最后只保留原始类型,原始类型只能是Object类及其子类。基本类型不可以,编译会报错。

为什么要进行自动拆装箱呢?

因为包装类型实际上是对象,不能直接参与运算,转化成基本数据类型再参与运算。

包装类型的New

  • 关于对象的比较,最好还是使用equals(),不要使用 ==

如果使用“==”来比较:

  1. 两个通过new生成的Integer变量永远是不相等的。
  2. Integer变量和int变量比较时,只要两个变量的值是向等的,则结果为true。
  3. 非new生成的Integer变量和new Integer()生成的变量比较时,结果为false。
  4. 两个非new生成的Integer对象,进行比较时,如果两个变量的值在区间-128到127之间,则比较结果为true,如果两个变量的值不在此区间,则比较结果为false

CH2 String

String 被声明为 final,这意味着 value 数组初始化之后就不能再引用其它数组。并且 String 内部没有改变 value 数组的方法,因此可以保证 String 不可变。不可被继承。(Integer 等包装类也不能被继承)

在 Java 8 中,String 内部使用 char 数组存储数据。在 Java 9 之后,String 类的实现改用 byte 数组存储字符串,同时使用 coder 来标识使用了哪种编码。

String 的不可变性保证了它是线程安全的。

StringBuffer 和 StringBuilder

  • 与String的不可变相比,这两个都是可变的。
  • StringBuffer 是线程安全的,它的内部使用 synchronized 进行同步
  • StringBuilder 不是线程安全的,效率更高。

字符串常量池:直接 String 出来的字符串,是放在常量池里的,之后再 String 出来相同的字符串,其实指向的是同一块空间。

String的一些常用函数

  • trim() : 用来去掉首尾空白字符串
  • split() : 用来分割,里面是正则表达式
  • toCharArray() : 将字符串变更为字符数组
  • charAt( int n) : 将字符串的第n个元素返回为字符
  • join() : String.join("-","a","b","c"),输出结果a-b-c,第二个参数可以是数组或者集合

CH3 关键字

final

final可以修饰数据,方法和类。

  1. 数据
    1. 基本数据类型,final保证其数值不变。
    2. 包装类型,保证其引用不可以变,但是引用指向的对象是可以变化的。
  2. 方法
    1. 用final声明的方法不能被子类重写。
    1. 被final修饰的类不能被继承。

final、finally、finalize的区别?

final可以修饰类、变量、方法,修饰类表示该类不能被继承、修饰方法表示该方法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。
finally一般作用在try-catch代码块中,在处理异常的时候,通常我们将一定要执行的代码方法finally代码块中,表示不管是否出现异常,该代码块都会执行,一般用来存放一些关闭资源的代码。
finalize 是一个方法,属于 Object 类的一个方法,而 Object 类是所有类的父类,该方法一般由垃圾回收器来调用,当我们调用System 的 gc() 方法的时候,由垃圾回收器调用 finalize() ,回收垃圾。

static

通常来说,用new创建类的对象时,数据存储空间才被分配,方法才供外界调用。但有时我们只想为特定域分配单一存储空间,不考虑要创建多少对象或者说根本就不创建任何对象,再就是我们想在没有创建对象的情况下也想调用方法。在这两种情况下,static关键字,满足了我们的需求。

静态变量:又称为类变量,也就是说这个变量属于类的,类所有的实例都共享静态变量,可以直接通过类名来访问它。静态变量在内存中只存在一份。
实例变量:每创建一个实例就会产生一个实例变量,它与该实例同生共死。

静态方法:静态方法在类加载的时候就存在了,它不依赖于任何实例。所以静态方法必须有实现,也就是说它不能是抽象方法。只能访问所属类的静态字段和静态方法,方法中不能有 this 和 super 关键字,因为这两个关键字与具体对象关联。

静态语句块:在类初始化的时候运行了一次。

this

  1. this 可以表示构造函数传递。this(a,b) 表示调用另外一个构造函数。这里的this就是一个特殊语法,不是变量,没有什么类型。
  2. 可以在非 static 成员内部使用,表示当前这个对象。此时,this 就是一个 final 的普通变量。它有静态类型,就是这个类本身,它有动态类型,就是当前这个对象的类型。可以对它调用成员函数,把它传递给别的函数等。

    super

super 和 this 类似

  1. super 调用父类的构造函数,是特殊语法,不是变量,没有类型。
  2. 可以在一个非 static 成员内部使用。比如 super.method() 。
  3. super 的另外一个作用是调用父类的 protected 函数。只有通过 super 我们才能操作父类的 protected 成员。

CH4 面向对象

重载和重写的区别

简单来说,重载就是方法名字相同,函数参数不同,实现方法不一样。重写就是方法名字和参数都相同,只是重写了实现方法。即外壳不变,核心重写。

构造器可以重写吗?

不能,因为构造器不能被继承,也就不能被重写。但是可以被重载。

抽象类和接口的区别

语法层面上的区别:

  • 抽象类可以提供成员方法的实现细节,而接口中只能存在public abstract 方法;
  • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的;
  • 接口中不能含有静态代码块以及静态方法,而抽象类可以有静态代码块和静态方法;
  • 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

设计层面上的区别:

  • 抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。
  • 设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计。

抽象类和抽象方法都使用 abstract 关键字进行声明。如果一个类中包含抽象方法,那么这个类必须声明为抽象类。
抽象类和普通类最大的区别是,抽象类不能被实例化,只能被继承。

抽象类可以让final修饰吗

不可以,定义抽象类就是为了让其他类继承的,使用了final就不能被继承了。

Java创建对象有哪几种方式?

有四种方式。

  • new创建新对象
  • 通过反射机制
  • 采用clone机制
  • 通过序列化机制

CH5 继承

super

  • 访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始化的工作。应该注意到,子类一定会调用父类的构造函数来完成初始化工作,一般是调用父类的默认构造函数,如果子类需要调用父类其它构造函数,那么就可以使用 super() 函数。
  • 访问父类的成员:如果子类重写了父类的某个方法,可以通过使用 super 关键字来引用父类的方法实现。

CH 6反射

每个类都有一个 Class 对象,包含了与类有关的信息。当编译一个新类时,会产生一个同名的 .class 文件,该文件内容保存着 Class 对象。
类加载相当于 Class 对象的加载,类在第一次使用时才动态加载到 JVM 中。也可以使用 Class.forName(“com.mysql.jdbc.Driver”) 这种方式来控制类的加载,该方法会返回一个 Class 对象。
反射可以提供运行时的类信息,并且这个类可以在运行时才加载进来,甚至在编译时期该类的 .class 不存在也可以加载进来。
Class 和 java.lang.reflect 一起对反射提供了支持,java.lang.reflect 类库主要包含了以下三个类:

  • Field :可以使用 get() 和 set() 方法读取和修改 Field 对象关联的字段;
  • Method :可以使用 invoke() 方法调用与 Method 对象关联的方法;
  • Constructor :可以用 Constructor 的 newInstance() 创建新的对象。

    反射的优点:

  • 可扩展性 :应用程序可以利用全限定名创建可扩展对象的实例,来使用来自外部的用户自定义类。

  • 类浏览器和可视化开发环境 :一个类浏览器需要可以枚举类的成员。可视化开发环境(如 IDE)可以从利用反射中可用的类型信息中受益,以帮助程序员编写正确的代码。
  • 调试器和测试工具 : 调试器需要能够检查一个类里的私有成员。测试工具可以利用反射来自动地调用类里定义的可被发现的 API 定义,以确保一组测试中有较高的代码覆盖率。

    反射的缺点:

    尽管反射非常强大,但也不能滥用。如果一个功能可以不用反射完成,那么最好就不用。在我们使用反射技术时,下面几条内容应该牢记于心。

  • 性能开销 :反射涉及了动态类型的解析,所以 JVM 无法对这些代码进行优化。因此,反射操作的效率要比那些非反射操作低得多。我们应该避免在经常被执行的代码或对性能要求很高的程序中使用反射。

  • 安全限制 :使用反射技术要求程序必须在一个没有安全限制的环境中运行。如果一个程序必须在有安全限制的环境中运行,如 Applet,那么这就是个问题了。
  • 内部暴露 :由于反射允许代码执行一些在正常情况下不被允许的操作(比如访问私有的属性和方法),所以使用反射可能会导致意料之外的副作用,这可能导致代码功能失调并破坏可移植性。反射代码破坏了抽象性,因此当平台发生改变的时候,代码的行为就有可能也随着变化。

CH7 泛型

使用泛型的好处

  1. 类型安全
    • 泛型的主要目标是提高 Java 程序的类型安全
    • 编译时期就可以检查出因 Java 类型不正确导致的 ClassCastException 异常
    • 符合越早出错代价越小原则
  2. 消除强制类型转换
    • 泛型的一个附带好处是,使用时直接得到目标类型,消除许多强制类型转换
    • 所得即所需,这使得代码更加可读,并且减少了出错机会
  3. 潜在的性能收益
    • 由于泛型的实现方式,支持泛型(几乎)不需要 JVM 或类文件更改
    • 所有工作都在编译器中完成
    • 编译器生成的代码跟不使用泛型(和强制类型转换)时所写的代码几乎一致,只是更能确保类型安全而已