Java中的类成员及单例类的实现
# 类成员
# 类成员的定义
Java类里只能包含成员变量
、方法
、构造器
、初始化块
、内部类(包括接口、枚举)
5种成员,目前已经介绍了前面4种,其中static
可以修饰成员变量
、方法
、初始化块
、内部类(包括接口、枚举)
,static
修饰的成员就是类成员。
static
关键字修饰的成员
就是类成员
,前面已经介绍的类成员
有类变量
、类方法
、静态初始化块
3个成分static
关键字不能修饰构造器
。static
修饰的类成员
属于整个类
,不属于单个实例
。static
修饰的初始化块叫做静态初始化块也是一种类成员。
# 如何访问类变量
类变量
既可通过类
来访问,也可通过类的对象
来访问。通过对象访问类变量只是一种假象,通过对象访问的依然是该类的类变量,可以这样理解:通过对象来访问类变量时,系统会在底层转换为通过该类来访问类变量。因此,从程序运行表面来看,即可看到同一类的所有实例的类变量共享同一块内存区。- 很多对象都不允许通过对象来访问类变量,对象只能访问实例变量;类变量必须用类来访问。
- 一个null对象访问实例成员(包括实例变量和实例方法),将会引发
java.lang.NullPointerException
空指针异常,因为null表明该实例根本不存在,既然实例不存在,那么它的实例变量和实例方法自然也不存在.
# 代码示例
package com.abc.part5;
/**
* @author mi
*/
public class NullAccessStatic {
public static void say() {
System.out.println("static修饰的类方法~");
}
static String name;
int age = 21;
public String eat() {
return "我正在吃饭";
}
public static void main(String[] args) {
// NullAccessStatic nullAccessStatic = new NullAccessStatic();
NullAccessStatic nullAccessStatic = null;
// say();
//以下代码虽然会正常输出。但是在IDEA中会报红,提示:不应该通过类实例访问静态成员,所以类成员必须使用类名来访问。
nullAccessStatic.say();
nullAccessStatic.name = "哈哈";
System.out.println(nullAccessStatic.name);
System.out.println("************************************************");
NullAccessStatic.say();
System.out.println(NullAccessStatic.name);
System.out.println("************************************************");
//一个null对象访问实例成员(包括实例变量和实例方法),将会引发`java.lang.NullPointerException`空指针异常,因为null表明该实例根本不存在,既然实例不存在,那么它的实例变量和实例方法自然也不存在.
//System.out.println(nullAccessStatic.age);
//nullAccessStatic.eat();
/**
* 以上代码会输出
* static修饰的类方法~
* 哈哈
* ************************************************
* static修饰的类方法~
* 哈哈
* ************************************************
*/
}
}
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
40
41
42
43
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
40
41
42
43
# 单例类
# 背景
大部分时候都把类的构造器
定义成public访问权限
,允许任何类自由创建该类的对象。但在某些时候,允许其他类自由创建该类的对象没有任何意义,还可能造成系统性能下降
(因为频繁地创建对象、回收对象
带来的系统开销问题
)。
# 基于以上背景引入单例类的概念
如果一个类始终只能创建一个实例,则这个类被称为单例类。
# 实现一个单例类的步骤
- 根据良好封装的原则:一旦把
该类
的构造器隐藏起来
,就需要提供public方法
作为该类
的访问点
,用于创建该类的对象
,且该方法
必须使用static修饰(因为调用该方法之前还不存在对象,因此调用该方法的不可能是对象,只能是类)
。 该类
还必须缓存已经创建的对象
,否则该类
无法知道是否曾经创建过对象
,也就无法保证只创建一个对象
。为此该类
需要使用一个成员变量
来保存曾经创建的对象
,因为该成员变量
需要被上面的静态方法访问
,故该成员变量
必须使用static
修饰。
# 单例类代码示例
package com.abc.part5;
/**
* @author mi
*/
public class Singleton {
/**
* 使用一个类变量来缓存曾经创建过的实例
*/
private static Singleton instance;
/**
* 对构造器使用private修饰,隐藏该构造器
*/
private Singleton() {
}
/**
* 提供一个静态方法,用于返回Singleton实例
* 该方法可以加入自定义控制,保证只产生一个Singleton对象
*
* @return Singleton对象
*/
public static Singleton getInstance() {
/**
* 如果instance为null,则表明还不曾创建过Singleton对象
* 如果instance不为null,则表明已经创建了Singleton对象,将不会重新创建新的实例
*/
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
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
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
# 单例测试类
package com.abc.part5;
public class SingletonTest {
public static void main(String[] args) {
/**
* 创建Singleton类的对象不能通过构造器
* 只能通过getInstance方法来创建类实例
*/
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println("s1:" + s1);
System.out.println("s2:" + s2);
System.out.println(s1.equals(s2));
System.out.println(s1 == s2);
/**
* 输出
* s1:com.abc.part5.Singleton@60e53b93
* s2:com.abc.part5.Singleton@60e53b93
* true
* true
*/
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
正是通过上面getInstance()
方法提供的自定义控制(这也是封装的优势:不允许自由访问类的成员变量和实现细节,而是通过方法来控制合适暴露) ,保证Singleton类只能产生一个实例。
所以,在SingletonTest类的main()方法中,看到两次产生的Singleton对象实际上是同一个对象。
编辑 (opens new window)
上次更新: 2021/08/10, 00:23:06
- 01
- Python实现对字符串的加解密02-25
- 02
- Python3对大文件中指定字符进行排序再写入到新的文件10-24
- 03
- Ubuntu下配置adb环境连接Android设备进行调试08-17