当我们编写一个类时,其实就是在描述其对象的属性和行为,而并没有产生实质上的对象,只有通过 new 关键字
才会产生出对象,这时系统才会分配内存空间给对象,其方法才可以供外部调用。我们有时候希望无论是否产生了对象或无论产生了多少对象的情况下,某些特定的数据在内存空间里只有一份,例如所有的中国人都有个国家名称,每一个中国人都共享这个国家名称,不必在每一个中国人的实例对象中都单独分配一个用于代表国家名称的变量。
举个例子来说明关键字 static
的作用
创建一个Circle类
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
(1)Circle 类中的变量 radius 是一个实例变量 (instance variable) ,它属于类的每一个对象,不能被同一个类的不同对象所共享。
(2)上例中 c1 的 radius 独立于 c2 的 radius ,存储在不同的空间。 c1 中的 radius变化不会影响 c2 的 radius ,反之亦然。
如果想让一个类的所有实例共享数据,就用类变量!
添加一个Chinese类,有country、name、age属性,如下:
package day08;
public class Chinese {
String country;//类变量:不用实例化,直接"类名.属性名"就可以使用,是类的一部分,被所有这个类的实例化对象所共享,也叫做"静态变量"。
String name;//实例变量:只有实例化后才能使用,属于实例化对象的一部分,不能共用
int age; //实例变量
}
在Test.java中使用new 关键字
产生对象:
package day08;
public class Test {
public static void main(String[] args) {
Chinese c =new Chinese();
c.country = "中国";//写了一次国籍
c.name = "xx";
c.age = 11;
Chinese c1 =new Chinese();
c.country = "中国";//又写了一次国籍
c.name = "xx";
c.age = 11;
Chinese c2 =new Chinese();
c.country = "中国";//又写了一次国籍
c.name = "xx";
c.age = 11;
//问题:既然这些人都是中国国籍,调用Chinese类,有没有办法能只写一次国籍,让所有对象都使用一个相同的国籍
//答案:使用static
}
}
使用static后,代码如下:
Chinese类:
package day08;
public class Chinese {
static String country;
String name;
int age;
}
Test.java:
package day08;
public class Test {
public static void main(String[] args) {
Chinese.country = "中国";
Chinese c =new Chinese();
//c.country = "中国";//写了一次国籍
c.name = "xx";
c.age = 11;
Chinese c1 =new Chinese();
//c.country = "中国";//又写了一次国籍
c.name = "xx";
c.age = 11;
Chinese c2 =new Chinese();
//c.country = "中国";//又写了一次国籍
c.name = "xx";
c.age = 11;
//打印三个人的国籍
System.out.println(c.country);
System.out.println(c1.country);
System.out.println(c2.country);
//或者使用类变量打印国籍
//System.out.println(Chinese.country);//输出:中国
//因为已知不管什么人只要来自Chinese类就一定是中国人,那我们只要打印一次就知道所有人的国籍了,故使用类变量来打印。
}
}
static除了可以修饰变量,还可以修饰方法(静态方法),还是上面的例子,我给Chinese加一个test()方法,并用static修饰:
Chinese类:
package day08;
public class Chinese {
static String country;
String name;
int age;
public static void test() {
System.out.println("这是一个静态方法");
}
}
Test.java:
package day08;
public class Test {
public static void main(String[] args) {
Chinese.country = "中国";
Chinese c =new Chinese();
//c.country = "中国";//写了一次国籍
c.name = "xx";
c.age = 11;
Chinese c1 =new Chinese();
//c.country = "中国";//又写了一次国籍
c.name = "xx";
c.age = 11;
Chinese c2 =new Chinese();
//c.country = "中国";//又写了一次国籍
c.name = "xx";
c.age = 11;
//打印三个人的国籍
//System.out.println(c.country);
//System.out.println(c1.country);
//System.out.println(c2.country);
//或者使用类变量打印国籍
System.out.println(Chinese.country);//输出:中国
//因为已知不管什么人只要来自Chinese类就一定是中国人,那我们只要打印一次就知道所有人的国籍了,故使用类变量来打印。
Chinese.test();//调用static方法
}
}
总结:类属性、类方法的设计思想
(1)类属性作为该类各个对象之间共享的变量。在设计类时 , 分析哪些类属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法。
(2)如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用。
类方法通常在设计工具类被使用
举个例子:
判断一个字符串是否为空字符串,普通写法如下:
package day08;
public class Test {
public static void main(String[] args) {
String s = "11";
if(s != null && s.equals("")) {//判断s不为空字符串
//在未来的开发中,可能会多次使用这一判断,那么在大量次数的基础上来看,就会发现代码的重复就很多了
//所以我们把这样的代码给抽取到"工具类"做成一个方法
}
}
}
抽取到"工具类"做成一个方法(Utils.java):
package day08;
public class Utils {
//判断字符串是不是一个空字符串
public static boolean isEmpty(String s) {
boolean flag = false;
if(s != null && !s.equals("")) {//判断s不为空字符串
flag = true;
}
return flag;
}
}
Test.java进行调用:
package day08;
public class Test {
public static void main(String[] args) {
String s = "11";
System.out.println(Utils.isEmpty(s));//输出:true
}
}
关键字
使用范围:
在Java类中,可用static修饰属性、方法、代码块、内部类
被修饰后成员具备以下特点:
(1)随着类的加载而加载(类加载后,静态的方法或者属性就能用了,“类名.属性(或方法)”)
(2)优先于对象存在 (不用new就能用)
(3)修饰的成员,被所有对象所共享
(4)访问权限允许时,可不创建对象,直接被类调用
注意:
类变量,这种可以被所有的实例化对象共享的属性,使用起来要慎重,因为只要改动,所有的类对象都能得到变化。
如何证明static修饰的成员被所有对象共享?
创建一个构造方法,在构造方法中使用static修饰的count
,每执行一次构造器就对count进行’+1’操作:
问题:为什么要在构造方法中对count进行+1操作
答:因为new对象会自动执行构造方法。
Chinese类:
package day08;
public class Chinese {
public Chinese() {//构造方法
Chinese.count += 1;
}
static String country;
public static int count;//静态变量count
String name;
int age;
public static void test() {
System.out.println("这是一个静态方法");
}
public static void showCount() {//调用的方法
System.out.println("总共new了" + Chinese.count + "个对象");
}
}
Test.java:
package day08;
public class Test {
public static void main(String[] args) {
//需求:想知道new了多少个Chinese对象
Chinese c =new Chinese();
System.out.println(Chinese.count);
Chinese c1 =new Chinese();
System.out.println(Chinese.count);
Chinese c2 =new Chinese();
System.out.println(Chinese.count);
Chinese c3 =new Chinese();
System.out.println(Chinese.count);
Chinese c4 =new Chinese();
System.out.println(Chinese.count);
Chinese c5 =new Chinese();
System.out.println(Chinese.count);
Chinese.showCount();//输出:总共new了6个对象
//由此可以看出Chinese.count这个类属性被所有的实例化对象共享了(如果不被共享,count的值就不会改变)
}
}
运行结果:
①没有对象的实例时,可以用类名.方法名()
的形式访问由 static 标记的类方法。
②在 static 方法内部只能访问类的 static 属性,不能访问类的非 static 属性。
Class Person{
private int id;
private static int total = 0;
public static int getToalPerson(){
id++; // 非法
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());//0
// 没有创建对象也可以访问静态方法
Person p1 = new Person();
System.out.println( "Number of total is "+ Person.getTotalPerson());//1
}
}
③因为不需要实例就可以访问 static 方法,因此 static 方法内部不能有 this ( 也不能有super)。
④重载的方法需要同时为 static 的或者非 static 的。
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(3);
}
}