第11章(上)--面向对象编程(高级特性)
1.类变量
问题引入:有一群小孩在玩堆雪人,不时有新的小孩加入,请问如何知道现在共有多少人在玩?请使用面向对象的思想,编写程序解决。
- 思路
在main方法中定义一个变量 int totalNum = 0
当有一个小孩加入游戏就 totalNum ++;
- 使用功能传统的方法可以解决问题
不是一个oop的解决方法,破坏了oop的一个基本原则,封装
1.1.1 什么是类变量
类变量也叫静态变量/静态属性/static变量,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。这个从前面的图也可看出来。
1.1.2 如何定义类变量
定义语法:
1) 访问修饰符 static 数据类型 变量名; [推荐]
2) static 访问修饰符 数据类型 变量名;
1.1.3 如何访问类变量
类名.类变量名
或者 对象名.类变量名 【静态变量的访问修饰符的访问权限和范围和普通属性是一样的。】
推荐使用:类名.类变量名;
1.1.4 类变量使用注意事项和细节讨论
1) 什么时候需要用类变量
当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量):比如:定义学生类,统计所有学生共交多少钱。
2) 类变量与实例变量(普通属性)区别
类变量是该类的所有对象共享的,而实例变量是每个对象独享的。
3) 加上static称为类变量或静态变量,否则称为实例变量
4) 类变量可以通过 类名.类变量名 或者 对象名.类变量名 来访问,但java设计者推荐我们使用 类名.类变量名方式访问。【前提是 满足访问修饰符的访问权限和范围】
5) 实例变量不能通过 类名.类变量名 方式访问。
6) 类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只要类加载了(到方法区),就可以使用类变量了。
7) 类变量的声明周期是随类的加载开始,随着类消亡而销毁。
2.类方法
1.1.1 类方法基本介绍
1) 类方法也叫静态方法 。
形式如下:
访问修饰符 static 数据返回类型 方法名(){ } 【推荐】
static 访问修饰符 数据返回类型 方法名(){ }
1.1.2 类方法的调用:
使用方式: 类名.类方法名 或者 对象名.类方法名 【前提是 满足访问修饰符的访问权限和范围
3.类方法和普通方法加载到方法区的比较
这就是为什么静态方法不能使用非静态属性
4.类方法经典的使用场景
- 当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率。
比如:工具类中的方法 utils
Math类、Arrays类、Collections类
- 小结
在程序员实际开发,往往会将一些通用的方法,设计成静态方法,这样我们不需要创建对象就可以使用了.
5.类方法的注意事项和使用细节
1) 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区 :
类方法中无this的参数
普通方法中隐含着this的参数
2) 类方法可以通过类名调用,也可以通过对象名调用。[举例]
3) 普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用。 [举例]
4)
静态/类方法中不允许使用和对象有关的关键字,比如this和super。普通方法(成员方法)可以。 [举例]
5) 类方法(静态方法)中 只能访问 静态变量。【如何理解】
6) 普通成员方法,即可以访问 成员变量,也可以访问静态变量。
6.Main方法的理解
由于java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数,案例演示,接收参数.
- 特别提示:
1.在main()方法中,我们可以直接调用main方法所在类的静态方法。
2.但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,
7.代码块
1) 相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
2) 当构造器重载时,如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性
代码块使用注意事项和细节讨论
1) static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只会执行一次。
2) 类什么时候被加载
① 创建对象实例时
② 创建子类对象实例,父类和子类也会被加载
③ 使用类的静态成员时(静态属性,静态方法)
案例演示:A 类 extends B类 的静态块
1) 普通的代码块,在创建对象实例时,会被隐式的调用。被创建一次,就会调用一次。如果只是使用类的静态成员时,普通代码块并不会执行
2) 创建一个对象时,调用顺序是: (重点来了!!):
① 调用静态代码块和静态属性初始化(注意:静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)
② 调用普通代码块和普通属性的初始化(注意:普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按定义顺序调用)
③ 调用构造方法。
- 小的结论:
//结论:当一个类中,有构造器,静态代码块,静态变量初始化,普通代码块,普通变量初始化则执行顺序如下:
//1. 先执行静态代码块,静态变量初始化, 如果有多个,则按照定义的顺序执行
//2. 再执行普通代码块,普通变量初始化, 如果有多个,则按照定义的顺序执行
//3. 最后执行构造器的内容
5) 构造方法(构造器) 的最前面其实隐含了 { super(); 调用普通代码块; 构造器定义代码;}
新写一个类演示【截图+说明]
6) 我们看一下创建一个子类时,他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
7) 静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码可以调用任意成员。
8.代码块练习
9.单例设计模式
什么是设计模式
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式 。设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免去我们自己再思考和摸索。”套路”
什么是单例模式
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造方法的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
- 单例模式有两种方式:1) 饿汉式 2)懒汉式
单例模式应用实例
演示饿汉式和懒汉式单例模式的实现。
步骤如下:
1) 构造器私有化
2) 类的内部创建对象
3) 向外暴露一个静态的公共方法。getInstance
4) 代码实现
获取的都是同一个:
饿汉式VS懒汉式
1) 二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建。
2) 饿汉式不存在线程安全问题,懒汉式存在线程安全问题。(后面学习线程后,会完善一把)
3) 饿汉式存在浪费资源的可能。因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题。
在我们javaSE标准类中,java.lang.Runtime就是经典的单例模式。
10. final关键字
1.1.1 基本介绍
1) final 中文意思:最后的, 最终的.
2) final 可以修饰类、属性、方法和局部变量.
3) 在某些情况下,程序员可能有以下需求,就会使用到final:
4) 当不希望类被继承时,可以用final修饰.【案例演示】
5) 当不希望父类的某个方法被子类覆盖/重写(override)时,可以用final关键字修饰。【案例演示: 访问修饰符 final 返回类型 方法名 】
6) 当不希望类的的某个属性的值被修改,可以用final修饰. 【案例演示: public final double TAX_RATE=0.08】
7) 当不希望某个局部变量被修改,可以使用final修饰【案例演示: final double TAX_RATE=0.08 】
final使用注意事项和细节讨论
1) final修饰的属性又叫常量,一般 用 xx_xx_xx来命名
2) final修饰的属性在定义时,必须赋初值,并且以后不能再修改,赋值可以在如下位置之一【选择一个位置赋初值即可】:
① 定义时:如 public final double TAX_RATE=0.08;
② 在构造器中
③ 在代码块中。
3) 如果final修饰的属性是静态的,则初始化的位置只能是
① 定义时 ② 在静态代码块 不能在构造器中赋值
4) final类不能继承,但是可以实例化对象
5) 如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承
6) 一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法
7) final不能修饰构造方法(即构造器) ----因为构造器不用被重写
8) final 和 static 往往搭配使用,效率更高,底层编译器做了优化处理
9) 包装类(Integer,Double,Float,Boolean等都是final),String也是final类
10) 当final修饰局部变量,我们称为局部常量,可在声明时赋初值,也可以在它使用前赋值,赋初值不能再修改了