jdk25 API文档

Chapter 07 面向对象编程(基础)

可变参数

  • 可变参数的使用:

    可变参数示例:

    1
    2
    3
    4
    5
    6
    /**
    * 实现多个参数求和
    */
    public int sum(int... nums){
    //方法体
    }

    代码解读:

    1.int… 表示接收的是可变参数,类型是int,可以接收多个int类型参数。

    2.使用可变参数时,可以当作数组来使用。

    3.遍历nums求和。

  • 注意事项和使用细节:

    1.可变参数的实参可以为数组。

    2.可变参数的本质就是数组。

    3.可变参数可以和普通类型的参数一起放在形参列表,但是需要保证可变参数在最后

    1
    2
    3
    4
    5
    6
    /**
    * 示例
    */
    public void f(String str,double... nums){
    //方法体
    }

    4.一个形参列表中只能出现一个可变参数。

作用域

  • 作用域的基本使用:

    1.局部变量:一般指在成员方法中定义的变量,是除了属性之外的其他变量,作用域为定义它的代码块中。局部变量赋值后才可以使用,即无默认值。

    2.全局变量:即属性,作用域为整个类体。全局变量可以不赋值,直接使用(默认值)。

  • 注意事项和使用细节:

    1.属性和局部变量可以重名,访问时遵循就近原则

    2.在同一个作用域中,两个局部变量不能重名。

    3.属性生命周期较长:伴随对象;局部变量生命周期较短:伴随代码块。

    4.属性不仅可以被本类使用,也可以被其他类使用(通过对象调用);局部变量只能在本类的对应方法中使用。

    5.属性可以加修饰符,局部变量不可以加修饰符。

构造方法/构造器

基本语法:

[修饰符] 方法名(形参列表){

​ 方法体;

}

说明:

1.构造器的修饰符可以默认。

2.构造器没有返回值

3.方法名必须和类的名字一致。

4.参数列表的规则和成员方法相同。

5.构造器的调用由系统完成。

主要作用:完成对新对象的初始化

  • 快速入门:

    示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    /**
    * 创建Person对象时,就直接指定这个对象的年龄和姓名
    */
    public class Example{
    public static void main(String[] args){
    //当我们创建一个对象时,直接通过构造器指定名字和年龄
    Person p1 = new Person("Smith",80);
    }
    }

    class Person{
    String name;
    int age;
    public Person(String pName,int pAge){
    name = pName;
    age = pAge;
    }
    }

    解读:

    1.构造器没有返回值**(也不能返回void类型)**

  • 注意事项和使用细节:

    1.一个类可以定义多个不同的构造器,即构造器的重载。

    2.构造器是完成对象的初始化的,不是创造对象

    3.创建对象时,系统自动的调用该类的构造方法。

    4.如果程序员没有定义构造器,系统会自动给类生成一个默认无参构造器(也叫默认构造器)。

    5.一旦定义了自己的构造器,默认的构造器就覆盖了,就不能再使用默认的无参构造器,除非自己显式定义无参构造器。

    无参构造器语法:

    方法名(){

    }

对象创建的流程分析

案例:

1
2
3
4
5
6
7
8
9
class Person{
int age = 90;
String name;
Person(String n,int a){
name = n;
age = a;
}
}
Person p = new Person("小明",20);
  • 流程分析:

    1.加载Person类信息(Person.class),只会加载一次

    2.在堆中分配空间(地址)。

    3.完成对象初始化:

    • 默认初始化。(age = 0,name = null)
    • 显式初始化。(age = 90,name = null)
    • 构造器的初始化。(age = 20,name = 小明)

    4.将对象在堆中的地址返回给p(p是对象名,也可以理解为对象的引用)。

this关键字

this代表当前对象。

  • 注意事项和使用细节:

    1.this关键字可以用来访问本类的属性、方法、构造器。

    2.this用于区分当前类的属性和局部变量。

    3.this关键字访问成员方法的语法:

    this.方法名(参数列表);

    4.this关键字访问构造器的语法:只能在构造器中访问另一个构造器,不可以在普通方法中使用。

    this(参数列表);

    该语句要使用,则必须放在构造器的第一句

    5.this不能在类定义的外部使用,只能在类定义的方法中使用

Chapter 08 面向对象编程(中级)

  • 作用:

    1.区分相同名字的类。

    2.当类很多时,可以很好的管理类。

    3.控制访问范围。

基本语法:

package 包名;

  • 本质:创建不同文件夹来保存类文件。

  • 包的命名:

    1.命名规则:只能包含数字、字母、下划线、小圆点,但不能用数字开头,不能是关键字或保留字。

    2.命名规范:一般是小写字母+小圆点

    com.公司名.项目名.业务模块名

  • 常用的包:

    1.java.lang.*:是基本包,默认引入。

    2.java.util.*:系统提供的工具包、工具类。

    3.java.net.*:网络包,网络开发。

    4.java.awt.*:做java的界面开发,GUI。

  • 如何引入包:

    语法:

    import 包;

    示例

    import java.util.Scanner; 只是引入Scanner类。

    import java.util.*; 将java.util包的所有类都引入。

  • 注意事项和使用细节:

    1.package的作用是声明当前类所在的包,需要放在class的最上面,一个类中最多只有一句package

    2.import指令放在package下面,在类定义前面,可以有多句且没有顺序要求。

访问修饰符

  • 基本介绍:用于控制方法和属性(成员变量)的访问权限(范围)。

    1.公开级别:用public修饰,对外公开。

    2.受保护级别:用protected修饰,对子类和同一个包中的类公开。

    3.默认级别:没有修饰符号,向同一个包的类公开。

    4.私有级别:用private修饰,只有类本身可以访问,不对外公开。

  • 4种访问修饰符的访问范围:

    访问级别 访问控制修饰符 同类 同包 子类 不同包
    公开 public
    受保护 protected ×
    默认 没有修饰符 × ×
    私有 private × × ×
  • 使用的注意事项:

    1.修饰符可以用来修饰类中的属性、成员方法以及类。

    2.只有默认和public才能修饰类,并且遵守上述访问权限的特点。

    3.成员方法的访问规则和属性完全一样。

封装

  • 优点:

    1.隐藏实现细节。

    2.可以对数据进行验证,保证安全合理。

  • 封装实现的步骤:

    1.将属性进行私有化**(不能直接修改属性)**。

    2.提供一个公共的set方法,用于对属性判断并赋值。

    如果使用构造器且需要验证属性,则可以在构造器中使用set方法。

    public void setXxx(类型 参数名){ //Xxx表示某个属性

    ​ //加入数据验证的业务逻辑

    ​ 属性 = 参数名;

    }

    3.提供一个公共的get方法,用于获取属性的值。

    public XX getXxx{ //权限判断

    ​ return xx;

    }

继承

  • 基本介绍:

    继承可以解决代码复用。当多个类存在相同的属性(变量)和方法时,可以从这些类中抽象出父类(基类)。在父类(基类)中定义这些相同的属性和方法,所有的子类(派生类)不需要重新定义这些属性和方法,只需要通过extends来声明继承父类(基类)即可。

  • 基本语法:

    class 子类 extends 父类{

    }

  • 注意事项和使用细节:

    1.子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问,但是私有属性和方法不能在子类直接访问(可以间接访问),要通过父类提供公共的方法(get方法)去访问。

    2.子类必须调用父类的构造器,完成父类的初始化。

    3.当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器。如果父类没有提供无参构造器,则必须在子类的构造器中用super(参数列表)去指定使用父类的哪个构造器完成对父类的初始化工作,否则编译不会通过

    4.如果希望指定去调用父类的某个构造器,则显式调用super()

    5.super在使用时,必须放在构造器第一行。(super只能在构造器中使用)

    6.super()this()都只能放在构造器第一行,因此这两个方法不能共存在一个构造器中

    7.Java所有类都是Object类的子类。

    8.父类构造器的调用不限于直接父类,将一直往上追溯直到Object类。

    9.子类最多只能直接继承一个父类,即Java中是单继承机制

    10.不能滥用继承,子类和父类之间必须满足is-a的逻辑关系。

  • 本质分析:按照查找关系来返回信息

    1.首先看子类是否有该属性。

    2.如果子类有这个属性,并且可以访问,则返回信息;如果子类没有这个属性,则看父类有没有这个属性(如果父类有该属性,并且可以访问,则返回信息)。

    3.如果父类没有该属性,则按照2的规则,继续找上级父类,直到Object类。

    如果在查找过程中找到了该属性,但是无法访问,则直接报错。

super关键字

  • 基本介绍:

    super代表父类的引用,用于访问父类的属性、方法、构造器。

  • 基本语法:

    1.访问父类的属性,但不能访问父类的private属性。

    super.属性名;

    2.访问父类的方法,不能访问父类的private方法。

    super.方法名(参数列表);

    3.访问父类的构造器(只能放在构造器的第一句)。

    super(参数列表);

  • 注意事项和使用细节:

    1.当子类中有和父类中成员(属性和方法)重名时,为了访问父类的成员,必须通过super。如果没有重名,使用super(直接查找父类)this(先查找本类)、直接访问是一样的。

    2.super的访问不限于直接父类,如果间接父类和本类中有同名的成员,也可以用super去访问间接父类的成员。如果多个基类中都有同名成员,使用super访问时,遵循就近原则。(也要遵守访问权限的相关规则)

super和this的比较

区别点 this super
访问属性 访问本类中的属性,如果本类没有此属性,则从父类中继续查找。 访问父类中的属性。
调用方法 访问本类中的方法,如果本类没有此方法,则从父类中继续查找。 直接访问父类中的方法。
调用构造器 调用本类构造器,必须放在构造器的首行。 调用父类构造器,必须放在子类构造器的首行。
特殊 表示当前对象。 子类中访问父类对象。

方法重写/覆盖

子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的那个方法。

  • 注意事项和使用细节:

    1.子类的方法的形参列表、方法名称要和父类方法的形参列表、方法名称完全一样。

    2.子类方法的返回类型和父类方法的返回类型一样,或者是父类返回类型的子类。

    3.子类方法不能缩小父类方法的访问权限(可以扩大)。

方法重写和重载的比较

名称 发生范围 方法名 形参列表 返回类型 修饰符
重载(overload) 本类 必须一样 类型、个数或者顺序,至少有一个不同 无要求 无要求
重写(override) 父、子类 必须一样 必须相同 子类重写的方法返回的类型和父类返回的类型一致,或者是其子类 子类方法不能缩小父类方法的访问范围

多态

  • 基本介绍:

    方法或对象具有多种形态,建立在封装和继承的基础之上。

  • 具体体现:

    1.方法的多态。(重写和重载均体现多态)

    2.对象的多态

    • 一个对象的编译类型和运行类型可以不一致。
    • 编译类型在定义对象时就确定了,不能改变。
    • 运行类型是可以变化的。
    • 编译类型看定义时=的左边,运行类型看=的右边。
  • 多态的向上转型

    1.本质:父类的引用指向了子类的对象。

    2.语法:

    父类类型 引用名 = new 子类类型();

    3.特点:

    • 编译类型看左边,运行类型看右边。

    • 可以调用父类中的所有成员(需遵循访问权限)。

    • 不能调用子类中的特有成员。

      在编译阶段,能调用哪些成员是由编译类型来决定的

    • 最终运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类开始查找方法(根据运行类型),然后调用,规则与方法调用规则一致。

  • 多态的向下转型

    1.语法:

    子类类型 引用名 = (子类类型) 父类引用;

    2.特点:

    • 只能强转父类的引用,不能强转父类的对象。
    • 要求父类的引用必须指向的是当前目标类型的对象。
    • 当向下转型后,可以调用子类类型中所有的成员。
  • 多态的注意事项和细节讨论:

    1.属性没有重写之说,属性的值看编译类型

    2.instanceof比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型。

  • 动态绑定机制
    1.当调用对象方法的时候,该方法会和该对象的内存地址/运行类型绑定
    2.当调用对象属性时,没有动态绑定机制,哪里声明,哪里使用。

多态的应用

多态数组

  • 数组的定义类型为父类类型,里面保存的实际元素类型为子类类型。
  • 使用向下转型。

多态参数

  • 方法定义的形参类型为父类,实参类型允许为子类。

Object类详解

equals方法

  • equals==的对比
    1.==比较运算符。既可以判断基本类型,又可以判断引用类型。如果判断基本类型,则判断的是是否相等;如果判断引用类型,则判断的是地址是否相等(即判定是不是同一个对象)。
    2.equalsObject类中的方法,只能判断引用类型。默认判断的是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。

hashcode方法

  • 小结:
    1.提高具有哈希结构的容器的效率。
    2.两个引用,如果指向的是同一个对象,则哈希值一定相同;如果指向的是不同对象,则哈希值不相同。
    3.哈希值主要根据地址号来的,完全将哈希值等价于地址

toString方法

  • 基本介绍:
    默认返回全类名(包名+类名) + @ + 哈希值的十六进制,子类往往重写toString方法,用于返回对象的属性信息。
  • 重写toString方法打印对象或拼接对象时,都会自动调用对象的toString形式。
  • 当直接输出一个对象时,toString方法会被默认地调用。

finalize方法

  • 当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作。
  • 什么时候被回收:当某个对象没有任何引用时,则jvm就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象。在销毁该对象前,会先调用finalize方法。
  • 垃圾回收机制的调用由系统来决定(即由自己的GC),也可以通过System.gc()主动触法垃圾回收机制。

断点调试debug

  • 在断点调试过程中,是运行状态,是以对象的运行类型来执行的。
  • 断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住。然后可以一步一步往下调试,调试过程中可以看各个变量的当前值,出错的话,调试到出错的代码行即显示错误,停下。进行分析从而找到这个bug。

断点调试快捷键

按键 内容
F7 跳入方法内
F8 逐行执行代码
Shift+F8 跳出方法
F9 resume,执行到下一个断点
  • 将光标放在某个变量上,可以看到最新的数据。
  • 可以在debug过程中动态的下断点。

Chapter 10 面向对象编程(高级)

类变量/静态变量

类变量(静态变量/静态属性)是该类所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。

  • 类变量时对着类的加载而创建,所以即使没有创建对象实例也可以访问。
  • 类变量的生命周期是随类的加载开始,随着类消亡而销毁。

定义语法

(1)访问修饰符 static 数据类型 变量名; (推荐)
(2)static 访问修饰符 数据类型 变量名;

访问方法

(1)类名.类变量名 (推荐)
(2)对象名.类变量名

  • 类变量的访问必须遵循访问权限。

类方法/静态方法

定义语法

(1)访问修饰符 static 数据返回类型 方法名(){} (推荐)
(2)static 访问修饰符 数据返回类型 方法名(){}

调用

(1)类名.类方法名
(2)对象名.类方法名

使用场景

当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率。

注意事项和细节讨论

  • 类方法和普通方法都是随着类的加载而加载,将结构信息储存在方法区。类方法中无this的参数,普通方法中隐含着this的参数。
  • 类方法可以通过类名调用,也可以通过对象名调用。
  • 普通方法和对象有关,需要通过对象名调用,不能通过类名调用。
  • 类方法中不允许使用和对象有关的关键字,比如thissuper,普通方法(成员方法)可以。
  • 类方法中只能访问静态变量和静态方法。
  • 普通成员方法既可以访问非静态变量(/方法),也可以访问静态变量(/方法)。

理解main方法语法

  • main方法是由虚拟机调用。
  • Java虚拟机需要调用类的mian()方法,所以该方法的访问权限必须是public。
  • Java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static。
  • 该方法接收String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。

特别提示

在main()方法中,我们可以直接调用main方法所在类的静态方法或静态属性。但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员。

代码块

基本介绍

代码化块又称为初始化块,属于类中的成员**(是类的一部分)。类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。
但代码块
没有方法名,没有返回,没有参数,只有方法体**,而且不用通过对象或类显示调用,而是在加载类或创建对象时隐式调用
代码块的调用优先于构造器。

基本语法

[修饰符]{
代码
};

注意:

  • “修饰符”可选,如果要写只能写static
  • 代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的叫普通代码块/非静态代码块。
  • 逻辑语句可以为任何逻辑语句。
  • ;可以写上,也可以省略。

好处

  • 相当于另一种形式的构造器(对构造器的补充机制),可以做初始化的操作。
  • 如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性。

使用注意事项和细节讨论

  • static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行。并且只会执行一次。如果是普通代码块,每创建一个对象就执行。
  • 类什么时候被加载:
    • 创建对象实例时。
    • 创建子类对象实例,父类也会被加载。
    • 使用类的静态成员时(静态属性、静态方法)。
  • 普通的代码块在创建实例时,会被隐式的调用,被创建一次就被调用一次。如果只是使用类的静态成员时,普通代码块并不会执行。
  • 创建一个对象时,在一个类调用顺序是:
    1.调用静态代码块和静态属性初始化。(注意:静态代码块和静态属性初始化调用的优先级一样。如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用。
    2.调用普通代码块和普通属性的初始化。(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用。)
    3.调用构造方法。
  • 构造方法(构造器)的最前面其实隐含了super()和调用普通代码块。
  • 创建一个子类时(继承关系),他们的静态代码块、静待属性初始化、普通代码块、普通属性初始化、构造方法的调用顺序如下:
    1.父类的静态代码块和静态属性(优先级一样,按定义顺序执行)。
    2.子类的静态代码块和静态属性(优先级一样,按定义顺序执行)。
    3.父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)。
    4.父类的构造方法。
    5.子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)。
    6.子类的构造方法。
  • 静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员。

单例设计模式

理解

采取一定的方法保证在整个软件系统中对某个类只能存在一种对象实例,并且该类只提供一个取得其对象实例的方法。

方式

  • 饿汉式
  • 懒汉式

饿汉式(不使用也创建)单例模式的实现步骤

1.构造器私有化**(防止直接new
2.类的内部创建对象
(该对象是static)**。
3.向外暴露一个静态的公共方法。
4.代码实现。

饿汉式单例模式可能造成资源浪费。

懒汉式(不使用就不创建)单例模式的实现步骤

1.构造器私有化。
2.定义一个static静态属性对象。
3.提供一个public的static方法,可以返回对象。
4.代码实现。

饿汉式和懒汉式单例设计模式的区别

  • 两者最主要的区别在于创建对象的实际不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建。
  • 饿汉式不存在线程安全问题,懒汉式存在线程安全问题。
  • 饿汉式存在资源浪费的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建的,就不存在这个问题。
  • 在JavaSE标准类中,java.lang.Runtime就是经典的单例模式。

final关键字

使用情景

修饰类、属性、方法和局部变量。
1.当不希望类被继承时,可以用final修饰。
2.当不希望父类的某个方法被子类覆盖/重写时,可以使用final关键字修饰。
3.当不希望类的某个属性的值被修改,可以用final修饰。
4.当不希望某个局部变量被修改,可以使用final关键字修饰。

使用注意事项和使用细节

  • final修饰的属性又叫常量,一般用XX_XX_XX来命名。

  • final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一:

    • 定义时。
    • 在构造器中。
    • 在代码块中。
  • 如果final修饰的属性时静态的,则初始化的位置只能是:

    • 定义时。
    • 在静态代码块,不能在构造器中赋值。
  • final类不能继承,但可以实例化对象。

  • 如果类不是final类,但是含有final方法,该方法虽然不能重写,但是可以被继承。

  • 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法。

  • final不能修饰构造方法(即构造器)。

  • final和static往往搭配使用,效率更高,不会导致类加载。底层编译器做了优化处理。

  • 包装类(Integer,Double,Float,Boolean等都是final),String也是final类。

抽象类

当父类的某些方法需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类。当一个类中存在抽象方法时,需要将该类声明为abstract类。一般来说,抽象类会被继承,有其子类实现抽象方法。

介绍

1.用abstract关键字来修饰一个类,这个类就叫抽象类。

访问修饰符 abstract 类名{}

2.用abstract关键字来修饰一个方法时,这个方法就是抽象方法。

访问修饰符 abstract 返回类型 方法名(参数列表); //没有方法体

3.抽象类的价值更多作用是在于设计,是设计者设计好后,让子类继承并实现抽象类。
4.抽象类在框架和设计模式使用较多。

使用的注意事项和细节讨论

  • 抽象类不能被实例化。
  • 抽象类不一定包含abstract方法。
  • 一旦类包含了abstract方法,则这个类必须声明为abstract。
  • abstract只能修饰类和方法,不能修饰属性和其他的。
  • 抽象类可以有任意成员**(抽象类还是类)**,比如:非抽象方法、构造器、静态属性等等。
  • 抽象方法不能有主体,即不能实现。
  • 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类。
  • 抽象方法不能使用private、final和static来修饰,因为这些关键字都是和重写相违背的。

接口

基本介绍

接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,再根据具体情况把这些方法写出来。

  • 语法:

    interface 接口名{
    //属性
    //方法
    }

    class 类名 implements 接口{
    自己的属性/方法;
    必须实现的接口的抽象方法。
    }

小结:再Jdk7.0前,接口里的所有方法都没有方法体;Jdk8.0后,接口类可以有静态方法、默认方法(需要default关键词修饰)。
在接口中,抽象类可以省略abstract

注意事项和使用细节

1.接口不能被实例化。
2.接口中所有的方法是public方法,接口中抽象方法可以不用abstract修饰。
3.一个普通类实现接口,就必须将该接口的所有方法都实现。(快捷键Alt+Enter)
4.使用抽象类实现接口,可以不用实现接口的方法。
5.一个类同时可以实现多个接口。
6.接口中的属性只能是final的,而且是pubic static final修饰符。
7.接口中的属性的访问形式:接口名.属性名
8.一个接口不能继承其他的类,但是可以继承多个别的接口
9.接口的修饰符只能是public和默认,这点和类的修饰符是一样的。

实现接口和继承类的比较

当子类继承了父类,就自动地拥有父类的功能;如果子类需要拓展功能,可以通过实现接口的方式来拓展。可以理解为实现接口是对Java单继承机制的一种补充。接口在一定程度上实现代码的解耦(降低不同模块之间的依赖,让它们可以独立变化和复用)。

接口的多态特性

1.多态参数。
2.多态数组。
3.接口存在多态传递现象。

内部类

基本介绍

一个类的内部又完整地嵌套了另一个类的结构。被嵌套的类称为内部类,嵌套其他类的类称为外部类。内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。

基本语法

class Outer{ //外部类
class Inner{ //内部类
//代码块
}
}
class Other{ //外部其他类
//代码块
}

局部内部类

说明:局部内部类是定义在外部类的局部位置,比如方法中,并且有类名。
1.可以直接访问外部类的所有成员,包含私有成员。
2.不能添加当问修饰符。因为它的地位就是一个局部变量,局部变量不能使用修饰符,但可以用final修饰,因为局部变量也可以用fianl
3.作用域:仅在定义它的方法或代码中。
4.局部内部类访问外部类成员:直接访问。
5.外部类访问局部内部类的成员:先创建对象再访问。(注意:必须在作用域内)
6.外部其他类不能访问局部内部类。(因为局部内部类的地位是一个局部变量)
7.如果外部类和局部内部类的成员重名时,默认遵循就近原则。如果想访问外部类的成员,则可以使用外部类名.this.成员去访问。

匿名内部类

匿名内部类的使用

1.本质是类。
2.是内部类。
3.该类没有名字。(实则名字由系统分配,无法直接看见。对象名.getClass()获得对象的运行类型。)
4.是一个对象。
5.匿名内部类使用一次就不能再使用了。
6.可以使用匿名内部类来简化开发。

基本语法

new 类/接口(参数列表){
//类体
}

注意事项和细节

1.因为匿名内部类既是一个类的定义,也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征。因此可以调用匿名内部类方法
2.可以直接访问外部类的所有成员,包括私有的。
3.不能添加访问修饰符,因为它的地位就是一个局部变量。
4.作用域:仅在定义它的方法或代码块中。
5.匿名内部类访问外部类成员:直接访问。
6.外部其他类不能访问匿名内部类,因为匿名内部类的地位是一个局部变量。
7.如果外部类和匿名内部类的成员重名时,匿名内部类访问的话默认遵循就近原则;如果想访问外部类的成员,则可以使用外部类名.this.成员去访问。

匿名内部类的实践

当作实参直接传递,简洁高效。

成员内部类的使用

说明:成员内部类是定义在外部类的成员位置,并且没有static修饰。
1.可以直接访问外部类的所有成员,包含私有的。
2.可以添加任意访问修饰符,因为它的地位就是一个成员。
3.作用域和外部类的其他成员一样,为整个类体
4.成员内部类访问外部类:直接访问。
5.外部类访问成员内部类:先创建对象,再访问。
6.外部其他类访问成员内部类:

  • 方式1

    1
    2
    Outer outer = new Outer();
    Inner Inner = outer.new Inner();
  • 方式2:使用一个方法来获取,更加简洁

    1
    2
    3
    4
    5
    6
    7
    Outer outer = new Outer();
    Outer.Inner innerInstance = Outer.getInnerInstance();

    //在外部类中编写一个方法:
    public Inner getInnerInstance(){
    return new Inner();
    }

7.如果外部类和成员内部类的成员重名时,成员内部类访问的话,默认遵循就近原则;如果想访问外部类的成员,则可以使用外部类名.this.成员去访问。

静态内部类的使用

说明:静态内部类是定义在外部类的成员位置,**并且有static修饰。
1.可以直接访问外部类的所有静态成员,包括私有的。但不能直接访问非静态成员。
2.可以添加任意访问修饰符,因为它的地位就是一个成员。
3.作用域:同其他的成员,为整个类体
4.静态内部类访问外部类:直接访问所有静态类。
5.外部类访问静态内部类:先创建对象,再访问。
6.外部其他类访问静态内部类:

  • 方式1:

    1
    Outer.Inner inner = new Outer.Inner();

    注意:前提是要满足访问权限。

  • 方式2:编写一个方法,可以返回静态内部类的实例。

    1
    2
    3
    4
    5
    6
    Outer outer = new Outer();
    Outer.Inner inner = outer.getInner();

    //在外部类中编写一个方法:
    public Inner getInner(){
    return new Inner();
  • 方法3:使用静态方法。

    1
    2
    3
    4
    5
    Outer.Inner inner = Outer.getInner();

    //在外部类中编写一个方法:
    public static Inner getInner(){
    return new Inner();

7.如果外部类和静态内部类的成员重名,静态内部类访问时,默认遵循就近原则;如果想访问外部类成员,则可以使用外部类名.成员去访问。

Chapter 11 枚举和注解

枚举的实现方式

自定义类实现枚举

1.不需要提供setXxx方法,因为枚举对象值通常为只读。
2.对枚举对象/属性使用final + static共同修饰,实现底层优化。
3.枚举对象名通常使用全部大写,满足常量的命名规范。
4.枚举对象根据需要,也可以有多个属性

演示自定义类枚举实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
pubilc class Enumeration{
public static void main(String[] args){
System.out.println(Season.AUTUMN);
}
}
class Season{
private String name;
private String desc;

//定义了四个对象(固定)
public final static Season SPRING = new Season("春天","温暖");
public final static Season SUMMER = new Season("夏天","炎热");
public final static Season AUTUMN = new Season("秋天","凉爽");
public final static Season WINTER = new Season("冬天","寒冷");

//1.将构造器私有化,目的:防止直接new。
//2.去掉set相关的方法,目的:防止属性被修改。
//3.在Season类内部,直接创建固定的对象。
private Season(String name,String desc){
this.name = name;
this.desc = desc;
}

public String getName(){
return name;
}

public String getDesc(){
return desc;
}

@Override
public String toString(){
return "Season{" +
"name='" + name + '\'' +
",desc='" + desc + '\'' +
'}';
}
}

特点:
1.构造器私有化。
2.本类内部创建一组对象。
3.对外暴露对象(通过为对象添加public final static修饰符)。
4.可以提供get方法,但是不要提供set方法。

使用enum关键字实现枚举

演示使用enum关键字来实现枚举:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
pubilc class Enumeration{
public static void main(String[] args){
System.out.println(Season.AUTUMN);
}
}
enum Season{
//如果使用了enum来实现枚举类
//1.使用关键字enum替代class。
//2.public final static Season SPRING = new Season("春天","温暖");直接使用SPRING("春天","温暖");替代。
// 解读:常量名(实参列表)
//3.如果有多个常量(对象),使用“,”间隔即可。
//4.如果使用enum来实现枚举,要求将定义的常量对象写在最前面。
SPRING("春天","温暖"),SUMMER("夏天","炎热"),AUTUMN("秋天","凉爽"),WINTER("冬天","寒冷");
private String name;
private String desc;

private Season(String name,String desc){
this.name = name;
this.desc = desc;
}

public String getName(){
return name;
}

public String getDesc(){
return desc;
}

@Override
public String toString(){
return "Season{" +
"name='" + name + '\'' +
",desc='" + desc + '\'' +
'}';
}
}

注意事项

1.当我们使用enum关键字开发一个枚举类时,默认会继承Enum类,而且是一个final类。
2.如果使用无参构造器创建枚举对象,则实参列表和小括号都可以省略。
3.当有多个枚举对象时,使用,间隔,最后有一个;结尾
4.枚举对象必须放在枚举类的行首

常用方法说明

使用关键字enum时,会隐式继承Enum类,这样我们就可以使用Enum类相关的方法。
1.toString:Enum类已经重写过了,返回的是当前对象名,子类可以重写该方法,用于返回对象的属性信息
2.name:返回当前对象名(常量名),子类中不能重写
3.ordinal:返回当前对象的位置号,默认从0开始。
4.values:返回当前枚举类中所有的常量。
5.valueOf:将字符串转换成枚举对象,要求字符串必须为已有的常量名,否则报异常。
执行流程:

  • 根据输入的字符串到枚举对象中去查找。
  • 如果找到了就返回;如果没有找到就报错。

6.compareTo:比较两个枚举常量,比较的就是位置号。如果位置号不相同,则返回前者的位置号-后者位置号的值。

总结

1.使用enum关键字后,就不能再继承其他类了,因为enum隐式继承Enum,而Java是单继承机制。
2.枚举类和普通类一样,可以实现接口:

enum 类名 implements 接口1,接口2{}

注解

理解

1.注解也被称为元数据,用于修饰解释包、类、方法、属性、构造器、局部变量等数据信息。
2.和注释一样,注解不影响程序逻辑,但注解可以被编译或运行,相当于嵌入在代码中的补充信息。
3.再JavaSE中,注解的使用目的比较简单,例如标记过时的功能,忽略警告等。在JavaEE中,注解占据了更重要的角色,例如用来配置应用程序的任何切面,代替JavaEE旧版中所遗留的繁冗代码和XML配置等。

基本介绍

使用注解时要在其前面增加@符号,并把该注解当成一个修饰符使用,用于修饰它支持的程序元素。

@Override

限定某个方法,是重写父类方法,该注释只能用于方法。
如果写了@Override注解,编译器就会去检查该方法是否真的重写了父类的方法。如果的确重写了,则编译通过;如果没有构成重写,则编译错误。(语法校验)

补充:@interface表示一个注解类。

使用说明

1.@Override表示指定重写父类的方法(从编译层面验证),如果父类没有被重写的方法,则会报错。
2.如果不写@Override注解,而父类仍有被重写的方法,仍然构成重写。
3.@Override只能修饰方法,不能修饰其他类、包、属性等等。
4.查看@Override注解源码如下,说明只能修饰方法。

@Target(ElementType.METHOD)

5.@Target是修饰注解的注解,称为元注解。

@Deprecated

说明

1.用于表示某个程序元素(类、方法等)已过时。即不再推荐使用,但是仍然可以使用。
2.可以修饰方法、类、字段、包、参数等等。
3.@Target(value={CONSTRUCTOR,FIELD,LOCAL_VARIABLE,METHOD,PACKAGE,PARAMETER,TYPE}) 4.@Deprecated`的作用可以做到新旧版本的兼容和过度。

@SuppressWarnings

当不希望看见警告时,可以使用@SuppressWarnings抑制编译器警告信息。在{""}中,可以写入希望抑制(不显示)的警告信息。@SuppressWarnings作用范围与放置的位置相关。

@Target({TYPE,FIELD,METHOD,PARAMATER,CONSTRUCTOR,LOCAL_VARIABLE})

该注解类有数组String[] values()设置一个数组。
生成@SuppressWarnings时,直接点击左侧的黄色提示就可以选择抑制警告的类型。(注意可以指定生成的位置)

可以抑制的警告类型有:

all 抑制所有警告
boxing 抑制与封装/拆装作业相关的警告
cast 抑制与强制转型作业相关的警告
dep-ann 抑制与淘汰注释相关的警告
deprecation 抑制与淘汰相关的警告
fallthrough 抑制与switch陈述式中遗漏break相关的警告
finally 抑制与未传回finnlly区块相关的警告
hiding 抑制与隐藏变数的区域变数相关的警告
incomplete-switch 抑制与switch陈述式enum case中遗漏项目相关的警告
javadoc 抑制与javadoc相关的警告
nls 抑制与非nls字串文字相关的警告
null 抑制与空值分析相关的警告
rawtypes 抑制与使用raw类型相关的警告
resource 抑制与使用Closeable类型的资源相关的警告
restriction 抑制与使用不建议或者禁止参照相关的警告
serial 抑制与可序列化的类别遗漏serialVersionUID栏位相关的警告
static-access 抑制与静态存取不正确相关的警告
static-method 抑制与可能宣告位static的方法相关的警告
super 抑制与置换方法相关但不含super呼叫的警告
synthetic-access 抑制与内部类别的存取未最佳化相关的警告
sync-override 抑制因为置换同步方法而遗漏同步化的警告
unchecked 抑制与未检查的作业相关的警告
unqualified-field-access 抑制与栏位存取不合格相关的警告
unused 抑制与未用的程式码及停用的程式码相关的警告

JDK的元注解

JDK的元注解用于修饰其他注解。

@Retention

说明

只能用于修饰一个注释定义,用于指定该注释可以保留多长时间。@Rentention包含一个RetentionPolicy类型的成员变量,使用@Rentention时,必须为该value成员变量指定值。

@Rentention的三种值

1.RetentionPolicy.SOURCE:编译器使用后,直接丢弃这种策略的注解。
2.RetentionPolicy.CLASS:编译器将把注解记录在class文件中,当运行Java程序时,JVM不会保留注解。这是默认值。
3.RetentionPolicy.RUNTIME:编译器将把注解记录在class文件中,当运行Java程序时,JVM会保留注释,程序可以通过反射获取该注解。

@Target

用于修饰注解定义,用于指定被修饰的注解能用于修饰哪些程序元素。@Target也包含一个名为value的成员变量。

@Documented

用于指定被该元注解修饰的注解类将被javadoc工具提取成文档,即在生成文档时,可以看到该注解。
说明:定义为Documented的注解必须设置Retention值为RUNTIME。

@Inherited

被它修饰的注解将具有继承性。如果某个类使用了被@Inherited修饰的注解,则其子类将自动具有该注解。

Chapter 12 异常

将代码块选中,使用快捷键Ctrl+Alt+T,选中try-catch

基本概念

异常:程序执行中发生的不正常行为。(开发过程中的语法错误和逻辑错误不是异常)

分类

1.Error(错误):Java虚拟机无法解决的严重问题,如:JVM系统内部错误、资源耗尽等严重情况。Error是严重错误,程序会崩溃。
2.Exception:其他因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理,如:空指针访问、试图读取不存在的文件、网络连接中断等。
Exception分为两大类:

  • 运行时异常。
  • 编译时异常。

异常体系图

75gZR9.png

1.异常分为两大类:运行时异常、编译时异常。
2.“运行时异常”:编译器检查不出来的异常,一般是指编程时的逻辑错误,是程序员应该避免其出现的异常。java.lang.RuntimeException类及它的子类都是运行时异常。
3.对于“运行时异常”可以不做处理,因为这类异常很普遍,若全处理可能会对程序的可读性和运行效率产生影响。
4.“编译时异常”:是编译器要求必须处置的异常。

常见运行时异常

1.NullPointerException空指针异常:当应用程序试图在需要对象的地方使用null时,抛出该异常。
2.ArithmeticException数学运算异常:当出现异常的运算条件,抛出此异常。
3.ArrayIndexOutOfBoundsException数组下标越界异常:用非法索引访问数组时抛出的异常,如果索引为负或大于等于数组大小,则该索引为非法索引。
4.ClassCastException类型转换异常:当试图将一个对象强制转换为不是实例的子类时,抛出该异常。
5.NumberFormatException数字格式不正确异常:当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为当前格式时,抛出该异常。(使用异常我们可以确保输入是满足条件的数字)

常见的编译异常

1.SQLException:操作数据库时,查询表可能发生异常。
2.IOException:操作文件时,发生的异常。
3.FileNotFoundException:当操作一个不存在的文件时,发生异常。
4.ClassNotFoundException:加载类,而该类不存在时,异常。
5.EOFException:操作文件,到文件末尾,发生异常。
6.IIlegalArguementException:参数异常。

异常处理

基本介绍

异常处理就是当异常发生时,对异常处理的方式。

异常处理的方式

1.try-catch-finally:程序员在代码中捕获发生的异常,自行处理。
2.throws:将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM。

两种异常处理方式二选一。如果程序员没有显式处理异常,默认采用throws

try-catch异常处理

Java提供trycatch块来处理异常,try块用于包含可能出错的代码。catch块用于处理try块中发生的异常。可以根据需要在程序中有多个数量的try...catch块。

基本语法

try{
//可疑代码
//将异常生成对应的异常对象,传递给catch块
}catch(异常){
//对异常的处理
}
//如果没有finally,语法是可以通过的。

注意事项

1.如果异常发生了,则异常发生后面的代码不会执行,直接进入到catch块。
2.如果异常没有发生,则顺序执行try的代码块,不会进入到catch
3.如果希望不管是否发生异常,都执行某段代码(比如:关闭连接、释放资源等),则使用finally
4.可以有多个catch语句,捕获不同的异常(进行不同的业务处理),要求父类异常在后、子类异常在前(比如:Exception在后,NullPointException在前),如果发生异常,只会匹配一个catch
5.可以进行try-finally配合使用,这种用法相当于没有捕获异常,因此程序会直接崩溃。(应用场景:执行一段代码,不管是否发生异常,都必须执行某个业务逻辑)

throws异常处理

基本介绍

1.如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显式地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者(方法)负责处理
2.在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
3.throws关键字后面也可以是异常列表,即可以抛出多个异常。

注意事项和使用细节

1.对于编译异常,程序中必须处理,比如:try-catchthrows
2.对于运行时异常,程序中如果没有处理,默认就是throws的方式处理
3.子类重写父类的方法时,对抛出异常的规定:子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常的类型的子类型
4.在throws过程中,如果有方法try-catch,就相当于处理异常,就可以不必throws

throw和throws的区别
意义 位置 后面跟的东西
throws 异常处理的一种方式 方法声明处 异常类型
throw 手动生成异常对象的关键字 方法体中 异常对象

自定义异常

基本概念

当程序中出现了某些“错误”,但该错误信息并没有在Throwable子类中描述处理,这个时候可以自己设计异常类,用于描述该错误信息。

自定义异常的步骤

1.定义类:自定义异常类名(程序员自己写)继承ExceptionRuntimeException
2.如果继承Exception,属于编译异常。
3.如果继承RuntimeException,属于运行异常。

Chapter 13 常用类

包装类

分类

1.针对八种基本数据类型相应的引用类型——包装类。
2.有了类的特点,就可以调用类中的方法。

基本数据类型 包装类
boolean Boolean
char Character
byte Byte
short Short
int Integer
long Long
float Float
double Double
7DM7aY.png 7DDih0.png 7DMC5b.png

包装类和基本数据的转换

1.jdk5前的手动装箱和拆箱方式:

  • 装箱:基本类型→包装类型
    示例:

    1
    2
    3
    int n1 = 100;
    Integer integer = new Integer(n1); //方法1
    Integer integer = Integer.valueOf(n1); //方法2
  • 拆箱:包装类型→基本类型
    示例:

    1
    int i = integer.intValue();

2.jdk5以后(含jdk5)的自动装箱和拆箱方式。

1
2
3
4
5
//自动装箱
int n2 = 200;
Integer integer2 = n2; //底层使用的是Integer.valueOf(n2)
//自动拆箱
int n3 = integer2; //底层使用的是intValue()方法

包装类型和String类型的相互转换

IntegerString转换为例,其他类似:

1
2
3
4
5
6
7
8
9
10
11
12
13
//包装类型→String类型
Integer i = 10;
//方式1
String s1 = i.toString();
//方式2
String s2 = String.valueOf(i);
//方式3
String s3 = i + "";
//String→包装类型
//方式1
Integer j1 = new Integer(s1);
//方式2
Integer j2 = Integer.valueOf(s2);

Integer类和Character类的常用方法

1
2
3
4
5
6
7
8
9
10
11
System.out.println(integer.MIN_VALUE);			 //返回最小值
System.out.println(integer.MAX_VALUE); //返回最大值

System.out.println(Character.isDigit('a')); //判断是不是数字
System.out.println(Character.isLetter('a')); //判断是不是字母
System.out.println(Character.isUpperCase('a')); //判断是不是大写
System.out.println(Character.isLowerCase('a')); //判断是不是小写

System.out.println(Character.isWhitespace('a')); //判断是不是空格
System.out.println(Character.toUpperCase('a')); //转换成大写
System.out.println(Character.toLowerCase('a')); //传换成小写

Integer创建机制

看看下面代码,输出什么结果?为什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void method1(){
Integer i = new Integer(1);
Integer j = new Integer(1);
System.out.println(i == j); //False

//主要看范围-128~127,就是直接返回;否则就new Integer(xx);
Integer m = 1; //底层:Integer.valueOf(1);
Integer n = 1; //底层:Integer.valueOf(1);
System.out.println(m == n); //True

Integer x = 128;
Integer y = 128;
System.out.println(x == y); //False

//只要有基本数据类型,判断的就是值是否相同
Integer a = 127;
int b = 127;
System.out.println(a == b); //True
}

String类

理解

1.String对象用于保存字符串,也就是一组字符序列。
2.字符串常量对象是用双引号括起的字符序列,如:”你好”、”12.97”等。
3.字符串的字符使用Unicode字符编码,一个字符(不区分字母还是汉字),占两个字节
4.String类较常用的构造器:

方法一:String s1 = new String();
方法二:String s2 = new String(String original);
方法三:String s3 = new String(char[] a);
方法四:String s4 = new String(char[] a,int startIndex,int count);
方法五:String s5 = new String(byte[] b);

5.String类是final类,不能被其他的类继承。
6.String有属性private final char value[]用于存放字符串内容。字符串是不可变的,一个字符串对象一旦被分配,其内容是不可变的。

注意:value是一个final类型,地址不可修改(不可让其指向另一个对象),但是单个字符内容是可以变化的

7Mxtte.png

1.String实现了Serializable接口,说明String可以串行化,可以在网络上传输。
2.String实现了Comparable接口,说明String对象可以比较。

创建String对象的两种方式

1.直接赋值。String s = "xxx";

先从常量池查看是否有”xxx”数据空间,如果有,直接指向;如果没有则重新创建,然后指向。s最终指向的是常量池的空间地址。

2.调用构造器。String s = new String("xxx");

先在堆中创建空间,里面维护了value属性,指向常量池的xxx空间。如果常量池中没有”xxx”,则重新创建;如果有,直接通过value指向。最终指向的是堆中的空间地址。

String c = "a" + "b";常量相加,看的是String c = a + b;变量相加,看的是

常见方法

说明:String类是保存字符串常量的。每次更新都需要重新开辟空间,效率极低。

方法名 作用
equals 区分大小写地判断内容是否相等
equalsIgnoreCase 忽略大小写地判断内容是否相等
length 获取字符的个数,字符串的长度
indexOf 获取字符在字符串中第1次出现的索引,索引从0开始,如果找不到则返回-1
lastIndexOf 获取字符在字符串中最后1次出现的索引,索引从0开始,如果找不到则返回-1
substring 截取指定范围的子串
trim 去前后空格
charAt 获取某索引处的字符,注意不能使用Str[index]这种方式
compareTo 比较两个字符串的的大小(如果前者大,则返回正数;如果后者大,则返回负数;如果相等,则返回0)
toCharArray 转换成字符数组
format 格式字符串,%s字符串,%c字符,%d整型,%.2f浮点型
replace 替换字符串中的字符
split 分隔字符串,对于某些分割字符,我们需要转义,比如`
toUpperCase 转换成大写
toLowerCase 转换成小写

StringBuffer类

基本介绍

1.java.lang.StringBuffer代表可变的字符序列,可以对字符串内容进行增删。
2.很多方法与String相同,但StringBuffer可变长度的。
3.StringBuffer是一个容器。
4.StringBuffer的直接父类是AbstractStringBuilderStringBuffer实现了Serializable,即StringBuffer的对象可以串行化。在父类AbstractStringBuilder中有属性char[] value,不是final。该value数组存放字符串内容,因此存放在堆中。
5.StringBufferfinal类,不能被继承。

String和StringBuffer的对比

1.String保存的是字符串常量,里面的值不能更改,每次String类的更新实际上就是更改地址,效率极低。
2.StringBuffer保存的是字符串变量,里面的值可以更改,每次StringBuffer的更新实际上可以更新内容,不用更新地址,效率较高。

StringBuffer的构造器

1.StringBuffer:构造一个其中不带字符的字符串缓冲区,其初始容量为16个字符。
2.StringBuffer(CharSequence seq):构造一个字符串缓冲区,它包含与指定的CharSequence相同的字符串。
3.StringBuffer(int capacity):构造一个不带字符,但具有指定初始容量的字符串缓冲区。即对char[]大小进行指定。
4.StringBuffer(String str):构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容,容量为创建字符串大小+16。

String和StringBuffer相互转换

String→StringBuffer

1
2
3
4
5
//方式1:使用构造器,返回的是StringBuffer对象,对s本身没有影响
StringBuffer b1 = new StringBuffer(s);
//方式2:使用append方法
StringBuffer b2 = new StringBuffer();
b2.append(s);

StringBuffer→String

1
2
3
4
//方式1:使用StringBuffer提供的toString方法
String s2 = b1.toString(); //b1 [StringBuffer]
//方式2:使用构造器
String s3 = new String(b1);

StringBuffer类的常见方法

1.增append
2.删delete(start,end)(区间前闭后开)
3.改replace(start,end,string)(将start-end间的内容替换掉,不含end
4.查indexOf(查找子串在字符串第1次出现的索引,如果找不到,返回-1)。
5.插insert
6.获取长度length

StringBuilder类

基本介绍

1.一个可变的字符序列。此类提供一个与StringBuffer兼容的API,但不保证同步**(StringBuilder不是线程安全)。该类被设计用作StringBuffer的一个简易替换,用在字符串缓冲区被单个线程使用的时候,如果可能,建议优先采用该类。因为在大多数实现中,它比StringBuffer要快。
2.在StringBuilder上的主要操作是appendinsert方法,可重载这些方法,以接收任意类型的数据。
3.StringBuilderfinal
(不能被继承),继承了AbstractStringBuilder,属性char[] value,内容存到value(因此字符序列在堆中)**。
4.实现了Serializable接口,串行化(即对象可以网络传输,可以保存到文件)。

StringBuilder常用方法

StringBuilderStringBuffer均代表可变的字符序列,方法是一样的。所以,使用和StringBuffer一样。
StringBuilder的方法没有做互斥的处理(即没有synchronized关键字),因此在单线程的情况下使用StringBuilder

String、StringBuffer和StringBuilder的比较

1.StringBuilderStringBuffer非常类似,均代表可变的字符序列,而且方法也一样。
2.String:不可变字符序列,效率低,但是复用率高。
3.StringBuffer:可变字符序列,效率较高(增删)、线程安全。
4.StringBuilder:可变字符序列,效率最高、线程不安全。
5.String使用注意说明:

1
2
3
4
5
6
String s = "a";	//创建了一个字符串
s += "b";
/*
实际上原来的“a”字符串对象已经丢弃了,现在又产生了一个字符串s+"b"(也就是“ab”)。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能。
结论:如果需要对String做大量修改,不要使用String。
*/

String、StringBuffer和StringBuilder的选择

1.如果字符串存在大量的修改操作,一般使用StringBufferStringBuilder
2.如果字符串存在大量的修改操作,并在单线程的情况,使用StringBuilder
3.如果字符串存在大量的修改操作,并在多线程的情况,使用StringBuffer
4.如果字符串很少修改,被多个对象引用,使用String,比如配置信息等。

Math类

基本介绍

Math类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。

常用方法

1.abs:绝对值。
2.pow:求幂。
3.ceil:向上取整。
4.floor:向下取整。
5.round:四舍五入。
6.sqrt:求开方。
7.random:求随机数。

获取一个a-b之间的一个随机整数
int num = (int)(Math.random()*(b - a + 1) + a);

8.max:求两个数的最大值。
9.min:求两个数的最小值。

Arrays类

常见方法

1.toString:返回数组的字符串形式。

Arrays.toString(arr);

2.sort:排序(自然排序和定制排序)

因为数组是引用类型,所以通过sort排序后,会直接影响到实参。
sort重载,可以通过传入一个接口Comparator实现定制排序。调用定制排序时,传入两个参数(排序的数组、实现了Comparator接口的匿名内部类),要求实现compare方法。

3.binarySearch:通过二分搜索法进行查找,要求必须排好序如果该数组是无序的,则不能使用。(如果数组中不存在该元素,则返回-(low + 1)。low:这个元素应该存在的位置)
4.copyOf:数组元素的复制。如果拷贝的长度大于被拷贝数组的长度,就在新数组后面加null;如果拷贝长度小于0,就抛出异常NegativeArraySizeException
5.fill:数组元素的填充(替换原来的所有元素)。
6.equals:比较两个数组元素的内容是否完全一致。
7.asList:将一组值转换成list集合。

System类

常见方法

1.exit:退出当前程序。
2.arraycopy:复制数组元素,比较适合底层调用一般使用Arrays.copy()完成复制数组
3.currentTimeMillens:返回当前时间距离1970-01-01的毫秒数。
4.gc:运行垃圾回收机制。(System.gc()

BigInteger和BigDecimal类

应用场景

1.BigInteger适合保存比较大的整型。
2.BigDecimal适合保存精度更高的浮点型。

常见方法

1.add:加。
2.subtract:减。
3.multiply:乘。
4.divide:除。可能抛出异常ArithmeticException。在调用divide方法时,指定精度即可。BigDecimal.ROUND_CEILING:如果有无限循环小数,就会保留分子的精度)

日期类

第一代日期类

1.Date:精确到毫秒,代表特定的日期。(此处的Datejava.util包下)默认输出的日期格式时国外的格式,所以通常需要对格式进行转换。
2.SimpleDateFormat:格式化和解析日期的具体类。它允许进行格式化(日期 →文本)、解析(文本→日期)和规范化。创建SimpleDateFormat对象,可以指定相应的格式。(format:将日期转换成指定格式的字符串;parse:将文本转换为日期的方法)
7dVcVC.png

第二代日期类

CalendarCalendar类是一个抽象类,构造器为private,可以通过getInstance()来获取实例,它为特定瞬间与一组诸如YEARMONTHDAY_OF_MONTHHOUR日历字段之间的转换提供了一些方法,并为操作日历字段提供了一些方法。
Calendar没有专门的格式化方法,所以需要自己组合输出
默认小时按照12小时进制获取时间,如果需要按照24小时进制获取时间,则将Calendar.HOUR改成Calendar.HOUR_OF_DAY

第三代日期类常见方法

1.LocalDate:只包含日期,可以获取日期字段。
2.LocalTime:只包含时间,可以获取时间字段。
3.LocalDateTime:包含日期和时间,可以获取日期和时间字段。
4.DateTimeFormatter格式日期类:类似于SimpleDateFormat
5.Instant时间戳:类似于Date,提供了一系列和Date类转换的方式。

1
2
3
4
//Instant→Date
Date date = Date.from(instant);
//Date→Instant
Instant instant = date.toInstant();

提供的plusminus方法可以对当前时间进行加或减。

Chapter 14 集合

集合的框架体系图

  • 单列集合7lfsr0.png
  • 双列集合7lf9eY.png

Collection接口和常用方法

Collection接口实现类的特点

1.Collection实现子类可以存放多个元素,每个元素可以是Object
2.有些Collection的实现类,可以存放重复的元素,有些不可以。
3.有些Collection的实现类,有些是有序的(List),有些不是有序的(Set)。(有序指存放顺序和取出顺序保持一致)
4.Collection接口没有直接的实现子类,是通过它的子接口SetList来实现的。

Collection接口的常用方法

1.add:添加单个元素。
2.remove:删除指定元素。
3.contains查找元素是否存在。
4.size:获取元素个数。
5.isEmpty:判断是否为空。
6.clear:清空。
7.addAll:添加多个元素。
8.containsAll:查找多个元素是否都存在。
9.removeAll:删除多个元素。

Collection接口遍历元素的方式

使用Iterator(迭代器)

1.Iterator对象成为迭代器,主要用于遍历Collection集合中的元素。
2.所有实现了Collection接口的集合类都有一个Iterator()方法,用以返回一个实现了Iterator接口的对象,即可以返回一个迭代器。
3.Iterator仅用于遍历集合,Iterator本身并不存放对象。

在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测。若不调用,且下一条记录无效,直接调用iterator.next()会抛出NoSuchElementException异常。
当退出while循环后,这时Iterator迭代器指向最后的元素。如果希望再次遍历,需要重置迭代器

使用for循环增强

增强for循环,可以替代iterator迭代器**(但底层仍然是迭代器)**,特点:增强for就是简化版的iterator,本质一样。只能用于遍历集合或数组。

  • 基本语法

    for(元素类型 元素名 : 集合名或数组名){
    访问元素
    }

List接口和常用方法

List接口基本介绍

List接口是Collection接口的子接口。
1.List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复
2.List集合中的每个元素都有其对应的顺序索引,即支持索引。
3.List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
4.常用的List接口的实现类有:ArrayListLinkedListVector

List接口的常用方法

1.void add(int index,Object ele):在index位置插入ele元素。(前面没有带索引,则默认插入到最后)
2.boolean addAll(int index,Colletcion eles):从index位置开始将eles中的所有元素添加进来。
3.Object get(int index):获取指定index位置的元素。
4.int indexOf(Object obj):返回obj在集合中首次出现的位置。
5.Object remove(int index):移除指定index位置的元素,并返回此元素。
6.int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置。
7.Object set(int index,Object ele):设置指定index位置的元素为ele,相当于是替换。
8.List subList(int fromIndex,int toIndex):返回从fromIndextoIndex位置的子集合。(集合前闭后开)

List的三种遍历方式

使用iterator

1
2
3
4
Iterator iter = col.iterator();
while(iter.hasNext()){
Object o = iter.next();
}

使用增强for

1
2
3
for(Object o : col){
System.out.println("o=" + o);
}

使用普通for

1
2
3
4
for(int i = 0;i < list.size;i++){
Object object = list.get(i);
System.out.println(object);
}

说明:使用LinkedList完成使用方式和ArrayList一样。

ArrayList的注意事项

1.ArrayList可以加入null,并且可以放入多个空值。
2.ArrayList是由数组来实现数据存储的。
3.ArrayList基本等同于Vector,除了ArrayList线程不安全(执行效率高),在多线程情况下,不建议使用ArrayList

ArrayList的底层操作机制源码分析

1.ArrayList中维护了一个Object类型的数组elementData
2.当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0。第1次添加,则扩容elementData为10,如果需要再次扩容,则扩容elementData为1.5倍。
3.如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍。

Vector底层结构与源码剖析

Vector的基本介绍

1.Vector类的定义说明:

1
2
3
public class Vector<E>
extends AbstractList<E>
inplements List<E>,RandomAccess,Cloneable,Serializable

2.Vector底层也是一个对象数组,protected Object[] elementData;
3.Vector是线程同步的,即线程安全Vector类的操作方法带有synchronized。在开发中,需要线程同步安全时,考虑使用Vector

Vector和ArrayList的比较

||底层结构|版本|线程安全(同步)效率|扩容倍数|
|:-:|:-:|:-:|:=;|:-:|
|ArrayList|可变数组|jdk1.2|不安全,效率高|如果有参构造:1.5倍;如果无参构造:第一次10,从第二次开始按1.5倍|
|Vector|可变数组|jdk1.0|安全,效率不高|如果无参构造:默认10,满后按2倍扩容;如果指定大小:每次直接按2倍扩容|

LinkedList底层结构

说明

1.LinkedList底层实现了双向链表和双向队列特点。
2.可以添加任意元素**(元素可以重复),包括null。**
3.线程不安全,没有实现同步。

ArrayList和LinkedList的比较

底层结构 增删的效率 改查的效率
ArrayList 可变数组 较低,数组扩容 较高
LinkedList 双向链表 较高,通过链表追加 较低

Set接口和常用方法

Set接口基本介绍

1.无序**(添加和取出的顺序不一致,但是取出的顺序固定)没有索引
2.不允许重复元素,所以
最多包含一个null**。

Set接口的常用方法

List接口一样,Set接口也是Collection的子接口,因此常用方法和Collection接口一样。

Set接口的遍历方式

Collection的遍历方式一样,因为Set接口是Collection接口的子接口。
1.可以使用迭代器。
2.增强for
3.不能使用索引的方式来获取(即不能用普通for循环)

HashSet

HashSet的全面说明

1.HashSet实现了Set接口。
2.HashSet实际上是HashMap
3.可以存放null值,但**只能有一个null
4.HashSet不保证元素是有序的,取决于hash后,再确定索引的结果(即不保证存入顺粗和取出顺序一致)。
5.不能有重复元素/对象
6.在执行add方法后,会返回一个boolean值。

HashSet底层机制说明(分析HashSet的添加元素底层的实现方法)

1.HashSet底层是HashMap
2.添加一个元素时,先得到hash值会转成索引值。
3.找到存储数据表table,看这个索引位置是否已经存放有元素。如果没有,直接加入;如果有,调用equals比较,如果相同,就放弃添加,如果不同,则添加到最后。
4.在Java8中,如果一条链表的元素个数超过TREEIFY_THRESHOLD(默认是8),并且table的大小大于等于MIN_TREEIFY_CAPACITY(默认64),就会进行树化(红黑树)。