高级类特性----static关键字

时间:2021-08-27 20:59:08

static 关键字

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

class Circle {
  private double radius;
  public Circle(double radius) {
    this.radius=radius;
  }
  public double findArea() {
    return Math.PI*radius*radius;
  }
}

创建两个Circle对象
  Circle c1=new Circle(2.0); //c1.radius=2.0
  Circle c2=new Circle(3.0); //c2.radius=3.0
Circle类中的变量radius是一个实例变量(instance variable),它属于类的每一个对象,不能被同一个类的不同对象所共享。

上例中c1的radius独立于c2的radius,存储在不同的空间。c1中的radius变化不会影响c2的radius,反之亦然。

如果想让一个类的所有实例共享数据,请用类变量

在Java类中声明变量方法内部类时,可使用关键字static做为修饰符。

static标记的变量或方法由整个类(所有实例)共享,如访问控制权限允许,可不必创建该类对象而直接用类名加‘.’调用

static成员也称类成员或静态成员,如:类变量、类方法、静态方法等。

类变量(class Variable)

类变量(类属性)由该类的所有实例共享

public class Person {
  private int id;
  public static int total = 0;
  public Person() {
    total++;
    id = total;
  }
}

类属性类似于全局变量

类属性应用举例

 class Person {
  private int id;
  public static int total = 0;
4   public Person() {
  total++;
id = total;
}
public static void main(String args[]){
  Person Tom=new Person()
10     Tom.id=0;
11     total=100; // 不用创建对象就可以访问静态成员
}
} public class OtherClass {
  public static void main(String args[]) {
  Person.total = 100; // 不用创建对象就可以访问静态成员
//访问方式:类名.类属性类名.类方法
System.out.println(Person.total);
Person c = new Person();
System.out.println(c.total); //输出101
}
}

类方法(class Method)

没有对象的实例时,可以用类名.方法名()的形式访问由static标记的类方法

  class Person {
private int id;
private static int total = 0;
public static int getTotalPerson() {
return total;
}
public Person() {
total++;
id = total;
}
}
public class TestPerson {
public static void main(String[] args) {
System.out.println("Number of total is " +Person.getTotalPerson()); //没有创建对象也可以访问静态方法
Person p1 = new Person();
System.out.println( "Number of total is "+ Person.getTotalPerson());
}
}

在static方法内部只能访问类的static属性,不能访问类的非static属性

 class Person {
private int id;
private static int total = 0;
public static int getTotalPerson() {
  id++; //非法
  return total;
}
public Person() {
  total++;
  id = total;
}
}

因为不需要实例就可以访问static方法,因此static方法内部不能有this(也不能有super)

 class Person {
  private int id;
private static int total = 0;
public static void setTotalPerson(int total){
this.total=total; //非法,在static方法中不能有this,也不能有super
}
public Person() {
total++;
id = total;
}
}
public class TestPerson {
public static void main(String[] args) {
  Person.setTotalPerson();
}
}

在静态方法里只能直接调用同类中其它的静态成员(包括变量和方法),而不能直接访问类中的非静态成员。这是因为,对于非静态的方法和变量,需要先创建类的实例对象后才可使用,而静态方法在使用前不用创建任何对象

静态方法不能以任何方式引用this和super关键字。与上面的道理一样,因为静态方法在使用前不用创建任何实例对象,当静态方法被调用时,this所引用的对象根本就没有产生。

main() 方法是静态的,因此JVM在执行main方法时不创建main方法所在的类的实例对象,因而在main()方法中,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情况,我们在以后的例子中会多次碰到。

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

类属性作为该类各个对象之间共享的变量。在设计类时,分析哪些类属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法

如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用

练习

1、编写一个类,实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”,定义封装这些属性的方法。账号要自动生成。
编写主类,使用银行账户类,输入、输出3个储户的上述信息。
考虑:哪些属性可以设计成static属性。

 /**
* 编写一个类,实现银行账户的概念,包含的属性有“帐号”、“密码”、“存款余额”、“利率”、“最小余额”,定义封装这些
* 属性的方法。账号要自动生成。
*/
public class Account { //初始化的 id
private static int initId = 1000; //账号
private String id; //密码
private String password; //余额
private int balance; //利率
private static double rate; //最小余额
private static int minBalance; public Account(String password, int balance) {
this.id = "" + (initId++);
this.password = password;
this.balance = balance;
} public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public int getBalance() {
return balance;
} public void setBalance(int balance) {
this.balance = balance;
} public static double getRate() {
return rate;
} public static void setRate(double rate) {
Account.rate = rate;
} public static int getMinBalance() {
return minBalance;
} public static void setMinBalance(int minBalance) {
Account.minBalance = minBalance;
} @Override
public String toString() {
return "Account [id=" + id + ", password=" + password + ", balance="
+ balance + ", rate=" + rate + ", minBalance=" + minBalance + "]";
}
}
 /**
* 编写主类,使用银行账户类,输入、输出3个储户的上述信息。
*/
public class TestBank {
public static void main(String[] args) { //统一设置 rate 和 minBalance
Account.setMinBalance(100);
Account.setRate(0.01); Account acc1 = new Account("1234", 100);
Account acc2 = new Account("1235", 200);
Account acc3 = new Account("1236", 300); System.out.println(acc1);
System.out.println(acc2);
System.out.println(acc3);
}
}

静态初始化

一个类中可以使用不包含在任何方法体中的静态代码块(static block ),当类被载入时,静态代码块被执行,且只被执行一次,静态块经常用来进行类属性的初始化
static块通常用于初始化static (类)属性
class Person {
  public static int total;
  static {
    total = 100;//为total赋初值
  }
  …… //其它属性或方法声明
}

静态初始化举例
class Person {
  public static int total;
  static {
    total = 100;
    System.out.println("in static block!");
  }
}

public class Test {
  public static void main(String[] args) {
    System.out.println("total = "+ Person.total);
    System.out.println("total = "+ Person.total);
  }
}

输出:
in static block
total=100
total=100


 public class Chinese {

     String name;
int age; static String country; //静态代码块: 使用 static 修饰的代码块
//在类被加载时执行一次. 且执行一次. 可以在静态代码块中对静态成员变量进行初始化.
static{
System.out.println("静态代码块");
} //非静态代码块: 先于构造器执行, 没创建一个对象都会执行一次.
{
System.out.println("非静态代码块");
} //对非静态成员进行初始化.
public Chinese(String name, int age) {
super();
this.name = name;
this.age = age; country = "";
} public Chinese() {
System.out.println("构造器");
} String getInfo(){
return "name: " + name;
} static void test(){
System.out.println("test...");
System.out.println("country: " + country); // System.out.println(name);
}
}
 /**
* static: 静态的.
* 1. 若需要一个类的多个对象共享一个变量, 则该变量需要使用 static 修饰.
* 2. 因为 static 修饰的变量为类的所有的实例所共享, 所以 static 成员不属于某个类的实例, 而属于整个类.
* 所以在访问权限允许的情况下, 可以使用 "类名." 直接访问静态成员(成员包括属性和方法)
* 3. 注意: 在静态方法里只能直接调用同类中其它的静态成员(包括变量和方法),而不能直接访问类中的非静态成员。
* 这是因为,对于非静态的方法和变量,需要先创建类的实例对象后才可使用,而静态方法在使用前不用创建任何对象。
* 4. 同 3 的道理: 静态方法不能以任何方式引用this和super关键字
* 5. 非静态方法中可以直接来访问类的静态成员.
* 6. main() 方法是静态的,因此JVM在执行main方法时不创建main方法所在的类的实例对象
*
* 7. 静态初始化指对类的静态属性进行初始化.
* 7.1 不应该在构造器中对静态成员进行初始化: 因为静态成员不因类的实例而改变.
* 7.2
* //非静态代码块: 先于构造器执行, 每创建一个对象都会执行一次.
* {
* System.out.println("非静态代码块");
* }
* 7.3
* //静态代码块: 使用 static 修饰的代码块
* //在类被加载时执行一次. 且执行一次. 可以在静态代码块中对静态成员变量进行初始化.
* //先于非静态代码块和构造器执行.
* static{
* System.out.println("静态代码块");
* } */
public class TestStatic {
public static void main(String[] args) {
Chinese.country = "中国";
Chinese.test(); Chinese p1 = new Chinese();
p1.name = "Tom";
p1.age = 12;
p1.country = "China"; Chinese p2 = new Chinese();
p2.name = "Jerry";
p2.age = 13;
// p2.country = "中国"; System.out.println(p1.name);
System.out.println(p1.country); System.out.println(p2.name);
System.out.println(p2.country); System.out.println(p1.getInfo());
}
}

单例模式

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

所谓类的单态设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法

如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造方法的访问权限设置为private,这样,就不能用new 操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。

单例 Singleton 设计模板

class Single {
  private static Single onlyone = new Single();//私有的,只能在类的内部访问
  private String name;
  public static Single getSingle() { //getSingle()为static,不用创建对象即可访问
    return onlyone;
  }
  private Single() {} //private的构造器,不能在类的外部创建该类的对象
}

public class TestSingle{
  public static void main(String args[]) {
    Single s1 = Single.getSingle(); //访问静态方法
    Single s2 = Single.getSingle();
    if (s1==s2){
      System.out.println("s1 is equals to s2!");
    }
  }
}


/**

* 设计为单子模式
*/
public class SingleInstance {

  //构造器私有化
  private SingleInstance() {}

  //在类的内部创建实例
  private static SingleInstance instance = new SingleInstance();

  //提供 get 方法
  public static SingleInstance getInstance() {
    return instance;
  }
}

/*

* 所谓类的单态设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例
*   不能在类的外部通过 new 关键字的方式创建新的实例: 构造器私有化.
*   在类的内部创建实例
*   为了让类的外部能够访问到类的内部创建的实例, 该实例必须使用 static 修饰.
*   不能允许在类的外部修改内部创建的实例的引用. SingleInstance.instance = null;
* 需要把该属性用 private 修饰
*   为了让外部进行读, 添加对应的 get 方法.
*/

// SingleInstance s1 = new SingleInstance();
// SingleInstance s2 = new SingleInstance();

SingleInstance s1 = SingleInstance.getInstance();
// SingleInstance.getInstance() = null;

SingleInstance s2 = SingleInstance.getInstance();

System.out.println(s1 == s2);

理解main方法的语法

由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数。

命令行参数用法举例
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 lisa "bily" "Mr Brown"
//输出结果:

args[0] = lisa
args[1] = bily
args[2] = Mr Brown


if(args != null){

  if(args.length > 1){
    for(String arg: args){
      System.out.println(arg);
    }
  }
}

Run As》Run Configuration...》Arguments 输入a b c d e 》Run

这就是main方法在运行时传参