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数字格式不正确异常:当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为当前格式时,抛出该异常。(使用异常我们可以确保输入是满足条件的数字)