0%

javaSE基础学习day13

static关键字

当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过new关键字才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。如果想让一个类的所有实例共享数据,就用类变量!

类属性、类方法的设计思想:

  • 类属性作为该类各个对象之间共享的变量。在设计类时,分析哪些属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法。
  • 如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用。

在Java类中,可用static修饰属性、方法、代码块、内部类。被修饰后的成员具备以下特点:
🔸随着类的加载而加载
🔸优先于对象存在
🔸修饰的成员,被所有对象所共享
🔸访问权限允许时,可不创建对象,直接被类调用

1.使用static修饰属性:静态变量(类变量)

属性,按是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量)

实例变量:我们创建了类的多个对象,每个对象都独立的拥有一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。

静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。

① 静态变量随着类的加载而加载,可以通过“class.静态变量”的方式进行调用。
② 静态变量的加载要早于对象的创建。
由于类只会加载一次,则静态变量在内存中也只会存在一份:存在于方法区的静态域中。

调用 静态变量 实例变量
yes no
对象 yes yes

静态属性举例:System.out; Math.PI;

静态变量的内存解析:

2.使用static修饰方法:静态方法(类方法)

静态方法随着类的加载而加载,没有创建对象时,可以通过“**class.方法名()**”的方式进行调用。

调用 静态方法 非静态方法
yes no
对象 yes yes

在static方法内部只能访问类的static修饰的属性或方法,不能访问类的非static的结构。非静态方法中,既可以调用非静态的方法或属性,也可以调用静态的方法或属性。

❗ 因为不需要实例就可以访问static方法,因此在静态方法内,不能使用this关键字和super关键字

static修饰的方法不能被重写

3.开发中,如何确定一个属性是否要声明为static的?

  • 属性是可以被多个对象所共享的,不会随着对象的不同而不同的。
  • 类中的常量也常常声明为static。

4.开发中,如何确定一个方法是否要声明为static的?

  • 操作静态属性的方法,通常设置为static的
  • 工具类中的方法,习惯上声明为static,比如:Math、Arrays、Collections

练习

编写一个类实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”,定义封装这些属性的方法。账号要自动生成。编写主类,使用银行账户类,输入、输出3个储户的上述信息。考虑哪些属性可以设计成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
44
public class Account {	
private int id;
private String pwd = "000000";
private double balance;
private static double interestRate;
private static double minMoney = 1.0;
private static int init = 1001;//用于自动生成id使用的
public Account(){
id = init++;
}
public Account(String pwd,double balance){
id = init++;
this.pwd = pwd;
this.balance = balance;
}
public String getPwd() {
return pwd;
}
public void setPwd(String pwd) {
this.pwd = pwd;
}
public static double getInterestRate() {
return interestRate;
}
public static void setInterestRate(double interestRate) {
Account.interestRate = interestRate;
}
public static double getMinMoney() {
return minMoney;
}
public static void setMinMoney(double minMoney) {
Account.minMoney = minMoney;
}
public int getId() {
return id;
}
public double getBalance() {
return balance;
}
@Override
public String toString() {
return "Account [id=" + id + ", pwd=" + pwd + ", balance=" + balance + "]";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
public class AccountTest {
public static void main(String[] args) {
Account acct1 = new Account();
Account acct2 = new Account("qwerty",2000);
Account.setInterestRate(0.012);
Account.setMinMoney(100);
System.out.println(acct1);//Account [id=1001, pwd=000000, balance=0.0]
System.out.println(acct2);//Account [id=1002, pwd=qwerty, balance=2000.0]
System.out.println(acct1.getInterestRate());//0.012
System.out.println(acct1.getMinMoney());//100.0
}
}

设计模式

设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格以及解决问题的思考方式。设计模免去我们自己再思考和摸索。就像是经典的棋谱,不同的棋局,我们用不同的棋谱。“套路”

创建型模式(共5种):工厂方式模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式(7种):适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式(11种):策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

单例 (Singleton)设计模式

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。

单例(Singleton)设计模式-饿汉式

1
2
3
4
5
6
7
8
9
10
11
12
class Singleton {
// 1.私有化构造器
private Singleton() {
}
// 2.内部提供一个当前类的实例
// 4.此实例也必须静态化
private static Singleton single = new Singleton();
// 3.提供公共的静态的方法,返回当前类的对象
public static Singleton getInstance() {
return single;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class SingletonTest1 {
public static void main(String[] args) {
Bank bank1 = Bank.getInstance();
Bank bank2 = Bank.getInstance();
System.out.println(bank1 == bank2);//true
}
}

//饿汉式
class Bank{
//1.私有化类的构造器
private Bank(){
}
//2.内部创建类的对象
//4.要求此对象也必须声明为静态的
private static Bank instance = new Bank();
//3.提供公共的静态的方法,返回类的对象
public static Bank getInstance(){
return instance;
}
}

单例(Singleton)设计模式-懒汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Singleton {
// 1.私有化构造器
private Singleton() {
}
// 2.内部提供一个当前类的实例
// 4.此实例也必须静态化
private static Singleton single;
// 3.提供公共的静态的方法,返回当前类的对象
public static Singleton getInstance() {
if(single == null) {
single = new Singleton();
}
return single;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SingletonTest2 {
public static void main(String[] args) {
Order order1 = Order.getInstance();
Order order2 = Order.getInstance();
System.out.println(order1 == order2);//true
}
}

//懒汉式
class Order{
//1.私有化类的构造器
private Order(){
}
//2.声明当前类对象,没有初始化
//4.此对象也必须声明为static的
private static Order instance = null;
//3.声明public、static的返回当前类对象的方法
public static Order getInstance(){
if(instance == null){
instance = new Order();
}
return instance;
}
}

懒汉式暂时还存在线程安全问题,讲到多线程时,可修复

单例模式的优点:
由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。举例java.lang.Runtime如下:

单例(Singleton)设计模式-应用场景

  • 网站的计数器,一般也是单例模式实现,否则难以同步。
  • 应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
  • 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
  • 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,都生成一个对象去读取。
  • Application也是单例的典型应用
  • Windows的Task Manager (任务管理器)就是很典型的单例模式
  • Windows的Recycle Bin (回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。

理解main方法的语法

  • 由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数。
  • 又因为main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情况,我们在之前的例子中多次碰到。

main()方法的使用说明:

  1. main()方法作为程序的入口
  2. main()方法也是一个普通的静态方法
  3. main()方法可以作为我们与控制台交互的方式。(之前:使用Scanner)
1
2
3
4
5
6
7
8
//命令行参数用法举例
public class CommandPara {
public static void main(String[] args) {
for (int i = 0; i < args.length; i++) {
System.out.println("args[" + i + "] = " + args[i]);
}
}
}

运行程序CommandPara.java ——> java CommandPara “Tom” Jerry Susin3x

输出结果:
args[0] = Tom
args[1] = Jerry
args[2] = Susin3x

面试题

此处,Something类的文件名叫OtherThing.java,程序是否可以正常编译、运行?

1
2
3
4
5
class Something {
public static void main(String[] something_to_do) {
System.out.println("Do something ...");
}
}

正确。没有规定Java的Class名字必须和其文件名相同。但public class的名字必须和文件名相同。

代码块

代码块(或初始化块)的作用:对Java类或对象进行初始化

代码块(或初始化块)的分类:一个类中代码块若有修饰符,则只能被static修饰,称为静态代码块(static block),没有使用static修饰的,为非静态代码块

static代码块通常用于初始化static的属性

1
2
3
4
5
6
7
class Person {
public static int total;
static {
total = 100;//为total赋初值
}
…… //其它属性或方法声明
}

静态代码块:用static修饰的代码块

  1. 可以有输出语句。
  2. 可以对类的属性、类的声明进行初始化操作。
  3. 不可以对非静态的属性初始化。即:不可以调用非静态的属性和方法。
  4. 若有多个静态的代码块,那么按照从上到下的顺序依次执行。
  5. 静态代码块的执行要先于非静态代码块。
  6. 静态代码块随着类的加载而加载,且只执行一次。

非静态代码块:没有static修饰的代码块

  1. 可以有输出语句。
  2. 可以对类的属性、类的声明进行初始化操作。
  3. 除了调用非静态的结构外,还可以调用静态的属性和方法。
  4. 若有多个非静态的代码块,那么按照从上到下的顺序依次执行。
  5. 每次创建对象的时候,都会执行一次,且先于构造器执行。
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
//静态初始化块举例
class Father {
static {
System.out.println("11111111111");
}
{
System.out.println("22222222222");
}
public Father() {
System.out.println("33333333333");
}
}
public class Son extends Father {
static {
System.out.println("44444444444");
}
{
System.out.println("55555555555");
}
public Son() {
System.out.println("66666666666");
}
public static void main(String[] args) { // 由父及子 静态先行
System.out.println("77777777777");
System.out.println("************************");
new Son();
System.out.println("************************");
new Son();
System.out.println("************************");
new Father();
}
}

输出结果:

程序中成员变量赋值的执行顺序:

final关键字

在Java中声明类、变量和方法时,可使用关键字final来修饰,表示“最终的”。

1、final标记的类不能被继承。提高安全性,提高程序的可读性。
如String类、System类、StringBuffer类

2、final标记的方法不能被子类重写。比如:Object类中的getClass()。

3、final标记的变量(成员变量或局部变量)即称为常量。名称大写,且只能被赋值一次。

  • final修饰属性:可以考虑赋值的位置有:显式初始化、代码块中初始化、构造器中初始化

  • final修饰局部变量:尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值后,就只能在方法体内使用此形参,但不能进行重新赋值。

static final用来修饰属性:全局常量

  • 本文作者: susin3x
  • 本文链接: https://susin3x.cn/posts/29cb4b0d.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!