07 - 面向对象编程(高级部分一)
一、类变量和类方法
1. 类变量
类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象取访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的都是同一个变量。
定义语法:
访问修饰符 static 数据类型 变量名
访问:
类名.类变量名
对象名.类变量名(静态变量的访问修饰符的访问权限和普通属性是一样的)
使用场景
- 需要让某个类的所有对象都共享使用一个变量时,就可以考虑使用类变量。
- 比如:定义一个学生类,每个学生缴纳班费,统计所有的学生一共交了多少钱。
package OOP_Highlevel.static_;
public class Student { //学生类
private String name;
private Long money;
//静态变量/类变量 班费的声明
public static Long fee = 0L;
public Student(String name, Long money) {
this.name = name;
this.money = money;
}
public Long getMoney() {
return money;
}
}
package OOP_Highlevel.static_;
public class static_demo01 {
public static void main(String[] args){
Student student1 = new Student("Lee",20L);
Student.fee += student1.getMoney();
Student student2 = new Student("Kity",30L);
Student.fee += student2.getMoney();
Student student3 = new Student("Jack",100L);
Student.fee += student3.getMoney();
System.out.println(Student.getFee());
}
}
注意事项
1.类变量,即静态变量,会被同一个类的所有实例对象共享
2.static变量,在类加载的时候就生成了
3.类变量是随着类的加载而创建,所以即使没有创建对象实例也可以访问
4.类变量的访问必须遵守相关的访问权限
5.当需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量
6.类变量是该类的所有对象共享的,而实例变量是每个对象独享的
7.加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量
8.类变量可以通过 类名.类变量名 或者 对象名.类变量名 来访问,推荐使用类名.对象名方式访问
9.实例变量不能通过 类名.类变量名 方式访问
10.类变量是在类加载时就初始化了,也就是说,即使你没有创建对象,只有类加载了,就可以使用类变量了
11.类变量的生命周期是随着类 的加载开始,随着类的消亡而销毁
2. 类方法
基本介绍
类方法也叫静态方法
形式
访问修饰符 static 数据返回类型 方法名(){}
类方法调用
类名.类方法名
对象名.类方法名
使用场景
当方法中不涉及到任何和对象相关的成员,则可以将方法设计成静态方法,提高开发效率
在程序开发中,往往会将一些通用的方法,设计成静态方法,比如工具类
注意事项
1.类方法和普通方法都是随着类的加载而加载,将结构信息存储到方法区
2.类方法中无this的参数,普通方法中隐含着this的参数
3.类方法可以通过类名调用,也可以通过对象名调用
4.普通方法和对象有关,需要通过对象名调用,比如对象名.方法名,不能通过类名调用
5.类方法中不允许使用和对象有关的关键字,比如this和super,普通方法可以
6.类方法中只能访问静态变量或静态方法
7.普通成员方法即可以访问非静态成员,也可以访问静态成员
二、理解 main 方法语法
public static void main(String[] args) {
}
说明
main 方法是虚拟机调用
Java虚拟机需要调用类的 main 方法,所以该方法的访问权限必须是public
Java虚拟机在执行 main 方法时不必创建对象,所以该方法必须是 static
该方法接收 String 类型的数组参数,该数组中保存执行Java命令时传递给运行类的参数
Java执行的程序 参数1 参数2 参数3
可在运行时传参
注意
在main()方法中,可以直接调用 main 方法所在类的静态方法或者静态属性
但是不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
三、代码块
基本介绍
-
代码块又称初始化块,属于类中的成员(即是类的一部分)类似于方法,将逻辑语句封装在方法体中,通过
{}
包围起来 -
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或创建对象时隐式调用。
基本语法
修饰符 {
代码
};
- 注意:
- 修饰符可选,要写的话,也只能写static
- 代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的,叫普通代码块/非静态代码块
- 逻辑语句可以为任何逻辑语句(输入 输出 方法调用 循环 判断)
;
分号可以写,也可以省略
代码块好处
相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性
注意事项
1)static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且只执行一次
如果是普通代码块,每创建一个对象,就执行一次
2)类什么时候被加载
1 - 创建对象实例时(new)
2 - 创建子类对象实例,父类也会被加载
3 - 使用类的静态成员时(静态属性 静态方法)
3)普通的代码块,在创建实例时,会被隐式的调用
被创建一次,就会调用一次
如果只是使用类的静态成员时,普通代码块并不会执行
小结:
1 - static代码块是类加载时 执行的,并且只会执行一次。
2 - 普通代码块是在创建对象时调用的,创建一次,调用一次。
4)创建一个对象时,在一个类的调用顺序是:
1 - 调用静态代码块和静态属性初始化
(注意 静态代码块 和 静态属性初始化 调用的优先级一样
如果有多个 静态代码块 和多个 静态属性变量初始化,则按照他们定义的顺序调用)
2 - 调用 普通代码块 和 普通属性初始化
(注意 普通代码块 和 普通属性初始化 调用的优先级一样
如果有多个普通代码块和多个 普通属性初始化,则按定义属性调用)
3 - 调用构造方法
class A {
{ //普通代码块
System.out.println("A 普通代码块 01");
}
private int n2 = getN2();//普通属性的初始化
static { //静态代码块
System.out.println("A 静态代码块 01");
}
private static int n1 = getN1();//静态属性的初始化
public static int getN1() {//静态方法
System.out.println("getN1 被调用...");
return 100;
}
public int getN2() { //普通方法/非静态方法
System.out.println("getN2 被调用...");
return 200;
}
//无参构造器
public A() {
System.out.println("A() 构造器被调用");
}
}
public class CodeBlockDetail02 {
public static void main(String[] args) {
A a = new A();
/*
(1) A静态代码块
(2) getN1 被调用...
(3) A 普通代码块
(4) getN2 被调用...
(5) A() 构造器被调用
*/
}
}
5)构造器的最前面其实隐含了 super() 和 调用普通代码块。
静态相关的代码块,属性初始化,在类加载时就执行完毕,因此是优先于构造器和普通代码块执行的
class AAA { //父类 Object
{
System.out.println("AAA 的普通代码块");
}
public AAA() {
//(1)super()
//(2)调用本类的普通代码块
System.out.println("AAA() 构造器被调用....");
}
}
class BBB extends AAA {
{
System.out.println("BBB 的普通代码块...");
}
public BBB() {
//(1)super()
//(2)调用本类的普通代码块
System.out.println("BBB() 构造器被调用....");
}
}
public class CodeBlockDetail03 {
public static void main(String[] args) {
new BBB();
/*
(1)AAA 的普通代码块
(2)AAA() 构造器被调用
(3)BBB 的普通代码块
(4)BBB() 构造器被调用
*/
}
}
6)创建子类对象时,静态代码块、静态属性初始化、普通代码块、普通属性初始化构造方法的调用顺序:
1 - 父类的静态代码块和静态属性
2 - 子类的静态代码块和静态属性
3 - 父类的普通代码块和普通属性
4 - 父类的构造方法
5 - 子类的普通代码块和普通属性初始化
6 - 子类的构造方法
7)静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员。
四、单例设计模式
什么是设计模式
1. 静态方法和属性的经典使用
2. 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式。
什么是单例设计模式
1. 所谓类的单例设计模式,就是采取一定的方法保证在整个软件系统中
对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
2. 单例模式有两种方式:
1)饿汉式
2)懒汉式
单例设计模式应用实例
演示 饿汉式 和 懒汉式 单例设计模式的实现
步骤如下:
1 - 构造器私有化 ----> 防止直接 new 对象
2 - 在类的内部创建对象
3 - 向外暴露一个静态的公共方法---getInstance
- 饿汉式—单例设计模式
/**
* 演示饿汉式的单例模式
*/
public class SingleTon01 {
public static void main(String[] args) {
//GirlFriend xh = new GirlFriend("小红");
//GirlFriend xb = new GirlFriend("小白");
//通过方法可以获取对象
GirlFriend instance = GirlFriend.getInstance();
System.out.println(instance);
GirlFriend instance2 = GirlFriend.getInstance();
System.out.println(instance2);
System.out.println(instance == instance2);//T //表示是同一个对象
}
}
class GirlFriend {
private String name;
public static int n1 = 999;
// 为了能够在静态方法中,返回 gf 对象,需要将其修饰为 static
// 饿汉式表示创建对象但是沒有使用
// 比如调用该类的静态变量时,类会加载完毕
// 即使没有使用该对象,但是也创建了
private static GirlFriend gf = new GirlFriend("小红红");
// 如何保障我们只能创建一个 GirlFriend 对象
// 步骤[单例模式-饿汉式]
//1. 将构造器私有化
//2. 在类的内部直接创建对象(该对象是 static)
//3. 提供一个公共的 static 方法,返回 gf 对象
private GirlFriend(String name) {
System.out.println("构造器被调用.");
this.name = name;
}
//获取构造器的方法
public static GirlFriend getInstance() {
return gf;
}
}
}
- 懒汉式—单例设计模式
/**
* 演示懒汉式的单例模式
*/
class Cat {
private String name;
public static int n1 = 999;
//步骤
//1.仍然构造器私有化
private Cat(String name) {
System.out.println("构造器调用...");
this.name = name;
}
//2.定义一個 static 静态属性
private static Cat cat ;
//3.提供一個 public 的 static 方法,可以返回一個 Cat 對象
public static Cat getInstance() {
if(cat == null) {
//如果还没有创建 cat 对象
cat = new Cat("小可爱");
}
//4.懒汉式,只有当用户使用 getInstance 时,才返回 cat 对象
//再次调用時,会返回上次创建的 cat 对象 ,从而保证了单例
return cat;
}
}
public class SingleTon02 {
public static void main(String[] args) {
Cat instance = Cat.getInstance();
System.out.println(instance);
//再次调用 getInstance
Cat instance2 = Cat.getInstance();
System.out.println(instance2);
System.out.println(instance == instance2);//T,表示是同一个对象
}
}
饿汉式 VS 懒汉式
1. 二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建。
2. 饿汉式不存在线程安全问题,懒汉式存在线程安全问题。(后面学习线程后,会完善一把)
3. 饿汉式存在浪费资源的可能。
因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了
懒汉式是使用时才创建,就不存在这个问题。
4. 在 JavaSE 的标准类中,java.lang.Runtime 就是经典的单例模式
Record Our Mind
本文作者:SpockC
本文链接:https://www.cnblogs.com/SpockC/p/15779635.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步