static关键字
- static关键字在我们实际开发中是一个非常常见的关键字,它可以定义成员属性、普通方法、定义类
static属性
-
static属性在程序中表示的是一个公共数据区,下面我们通过案例的对比来进行分析
案例:
class Book{ private String author; private String title; private double price; String pub="马姓出版社";//为了方便调用,暂时不封装 public Book(String author,String title,double price){ this.author=author; this.title=title; this.price=price; } public String getInfo(){ return "图书的作者为:"+this.author+"、图书的名称为:"+this.title+"、图书的价格为:"+this.price+"、出版社为:"+this.pub; } } public class YootkDemo{ public static void main (String[] ages){ Book bookA=new Book("腾讯","地下城与勇士",99.8); Book bookB=new Book("拳头","英雄联盟",98.8); System.out.println(bookA.getInfo()); System.out.println(bookB.getInfo()); } } /* 程序的执行结果为: 图书的作者为:腾讯、图书的名称为:地下城与勇士、图书的价格为:99.8、出版社为:马姓出版社 图书的作者为:拳头、图书的名称为:英雄联盟、图书的价格为:98.8、出版社为:马姓出版社 */
这个程序的代码是没有任何问题的,但是,如果这个时候我们出现一个问题,这个图书更换了出版社,我们需要对所有图书的出版社进行更改(所有的书一定是属于一个出版社的),此时我们就需要去更改pub属性,观察下面的程序:
class Book{ private String author; private String title; private double price; String pub="马姓出版社";//为了方便调用,暂时不封装 public Book(String author,String title,double price){ this.author=author; this.title=title; this.price=price; } public String getInfo(){ return "图书的作者为:"+this.author+"、图书的名称为:"+this.title+"、图书的价格为:"+this.price+"、出版社为:"+this.pub; } } public class YootkDemo{ public static void main (String[] ages){ Book bookA=new Book("腾讯","地下城与勇士",99.8); Book bookB=new Book("拳头","英雄联盟",98.8); bookA.pub="盛大出版社"; System.out.println(bookA.getInfo()); System.out.println(bookB.getInfo()); } } /* 程序的执行结果为: 图书的作者为:腾讯、图书的名称为:地下城与勇士、图书的价格为:99.8、出版社为:盛大出版社 图书的作者为:拳头、图书的名称为:英雄联盟、图书的价格为:98.8、出版社为:马姓出版社 */
此时我们可以发现,我们修改了pub属性,仅仅只是一个进行了修改,我们必须要手动的更改每一本图书,太麻烦了,这时候我们就需要使用static属性。
class Book{ private String author; private String title; private double price; static String pub="马姓出版社";//为了方便调用,暂时不封装 public Book(String author,String title,double price){ this.author=author; this.title=title; this.price=price; } public String getInfo(){ return "图书的作者为:"+this.author+"、图书的名称为:"+this.title+"、图书的价格为:"+this.price+"、出版社为:"+this.pub; } } public class YootkDemo{ public static void main (String[] ages){ Book bookA=new Book("腾讯","地下城与勇士",99.8); Book bookB=new Book("拳头","英雄联盟",98.8); bookA.pub="盛大出版社"; System.out.println(bookA.getInfo()); System.out.println(bookB.getInfo()); } } /* 程序的执行结果为: 图书的作者为:腾讯、图书的名称为:地下城与勇士、图书的价格为:99.8、出版社为:盛大出版社 图书的作者为:拳头、图书的名称为:英雄联盟、图书的价格为:98.8、出版社为:盛大出版社 */
此时的pub属性由于使用了static关键字进行定义,所以成为了一个公共的属性,当我们进行修改的时候,所有进行调用的对象也会一起修改。所以static就非常适合公共属性的定义。
-
对于static属性,还有一个很重要的特点,由它所描述的属性与普通属性最大的区别就是,可以在类未被实例化的时候,就可以直接进行调用
class Book{ private String author; private String title; private double price; static String pub="马姓出版社";//为了方便调用,暂时不封装 public Book(String author,String title,double price){ this.author=author; this.title=title; this.price=price; } public String getInfo(){ return "图书的作者为:"+this.author+"、图书的名称为:"+this.title+"、图书的价格为:"+this.price+"、出版社为:"+this.pub; } } public class YootkDemo{ public static void main (String[] ages){ Book.pub="盛大出版社"; Book bookA=new Book("腾讯","地下城与勇士",99.8); Book bookB=new Book("拳头","英雄联盟",98.8); System.out.println(bookA.getInfo()); System.out.println(bookB.getInfo()); } } /* 程序的执行结果为: 图书的作者为:腾讯、图书的名称为:地下城与勇士、图书的价格为:99.8、出版社为:盛大出版社 图书的作者为:拳头、图书的名称为:英雄联盟、图书的价格为:98.8、出版社为:盛大出版社 */
-
static属性的应用案例
-
static关键字描述的是一个公共的概念,由于对象实例化后都会调用类中的构造方法,所以我们可以在类中追加一个static的属性,帮助我们进行对象个数的统计
-
示例:统计新产生的对象个数
class Pet{ //宠物类 private static int count=0; public Pet(){ this.count++; System.out.println("【当前产生的实例化对象个数为】:"+this.count); } } public class YootkDemo{ public static void main(String[] ages){ for(int i=0;i<10;i++){ new Pet(); } } } /* 程序的运行结果为: 【当前产生的实例化对象个数为】:1 【当前产生的实例化对象个数为】:2 【当前产生的实例化对象个数为】:3 【当前产生的实例化对象个数为】:4 【当前产生的实例化对象个数为】:5 【当前产生的实例化对象个数为】:6 【当前产生的实例化对象个数为】:7 【当前产生的实例化对象个数为】:8 【当前产生的实例化对象个数为】:9 【当前产生的实例化对象个数为】:10 */
-
-
我们也可以利用static关键字这一个特点,对一些编号进行自动的命名
-
示例:
class Pet{ //宠物类 private String name; private int age; private String kind; private static int count=0; public Pet(){ this("宠物编码-"+count); this.count++; } public Pet(String name){ this.name=name; } public String getInfo(){ return "【宠物】名字为:"+this.name+"、年龄为:"+this.age+"、种类为:"+this.kind; } public void setName(String name){ this.name=name; } public void setAge(int age){ this.age=age; } public void setKind(String kind){ this.kind=kind; } public String getName(){ return this.name; } public int getAge(){ return this.age; } public String getKind(){ return this.kind; } } public class YootkDemo14{ public static void main(String[] ages){ System.out.println(new Pet().getInfo()); System.out.println(new Pet().getInfo()); } } /* 程序执行结果为: 【宠物】名字为:宠物编码-0、年龄为:0、种类为:null 【宠物】名字为:宠物编码-1、年龄为:0、种类为:null */
这种方式自动命名的方式,是避免属性内容出现重复。
-
static方法
-
与static属性一样,static的方法同样拥有和普通方法的最大区别就是可以在类未被实例化的时候通过类名称进行调用,
-
示例:在类中定义一个static的方法
class Book{ private String author; private String title; private double price; private static String pub="马姓出版社";//属性封装 public static void setPub(String p){//进行Static属性的设置 pub=p; } public Book(String author,String title,double price){ this.author=author; this.title=title; this.price=price; } public String getInfo(){ return "图书的作者为:"+this.author+"、图书的名称为:"+this.title+"、图书的价格为:"+this.price+"、出版社为:"+this.pub; } } public class YootkDemo{ public static void main (String[] ages){ Book.setPub("盛大出版社"); Book bookA=new Book("腾讯","地下城与勇士",99.8); Book bookB=new Book("拳头","英雄联盟",98.8); System.out.println(bookA.getInfo()); System.out.println(bookB.getInfo()); } } /* 程序的执行结果为: 图书的作者为:腾讯、图书的名称为:地下城与勇士、图书的价格为:99.8、出版社为:盛大出版社 图书的作者为:拳头、图书的名称为:英雄联盟、图书的价格为:98.8、出版社为:盛大出版社 */
static的方法和属性都是不受到类实例化的限制,通过类名称都可以直接进行调用,于是类中的属性就分为:static属性和非static属性,方法就分为static方法和非static方法,这时候,就会有一个调用限制的问题:
-
static方法只能调用static的属性和方法。
-
非static的方法可以调用static的属性和方法,也可以调用非static的属性和方法。
- 以上的限制是由java编译器本身给出来的,然而为什么会有这样的限制呢?我们可以这样理解
- 类中的非static的属性和方法是必须要在类对象实例化后才进行堆内存空间的分配,当对象被实例化后才调用属性和普通方法,那当然也就可以太用static的属性和方法
- static的方法和和属性都是可以在未实例化对象的时候进行调用,然而一个非static的属性和方法,还未存在,当然不能进行调用,可以理解为,一个已经存在的东西,无法调用未存在的东西。
-
static的方法中,不能使用this关键字,因为this表示的是当前对象,而static的方法是在类未被实例化的时候就可以进行调用,何来当前对象呢!
static的案例演示
-
声明一个图书类,其数据成员包括书名、编号(利用静态数据进行自动编号)、书价,并拥有静态数据成员册数,记录图书的总册数,在构造方法中利用此变量为编号赋值,在主方法总定义多个对象,求出总册数:
class Book{ private String title; private long nunber; private double price; private static long count; public Book(){ this(null,0.0); } public Book(String title,double price){ this.nunber=1001+count; this.count++; this.title=title; this.price=price; } public String getInfo(){ return "【图书】名称为:"+this.title+"、价格为:"+this.price+"、编号为:"+this.nunber; } public void setTitle(String title){ this.title=title; } public void setNunber(long nunber){ this.nunber=nunber; } public void setPrice(double price){ this.price=price; } public void setCount(long count){ this.count=count; } public long getNunber(){ return this.nunber; } public String getTitle(){ return this.title; } public double getPrice(){ return this.price; } public static long getCount(){ return count; } } public class YootkDemo15{ public static void main(String[] ages){ for(int i=0; i<10;i++){ System.out.println(new Book("python从入门到实践",66.9).getInfo()); } System.out.println("【图书】总册数为:"+Book.getCount()); } } /* 程序的执行结果为: 【图书】名称为:python从入门到实践、价格为:66.9、编号为:1001 【图书】名称为:python从入门到实践、价格为:66.9、编号为:1002 【图书】名称为:python从入门到实践、价格为:66.9、编号为:1003 【图书】名称为:python从入门到实践、价格为:66.9、编号为:1004 【图书】名称为:python从入门到实践、价格为:66.9、编号为:1005 【图书】名称为:python从入门到实践、价格为:66.9、编号为:1006 【图书】名称为:python从入门到实践、价格为:66.9、编号为:1007 【图书】名称为:python从入门到实践、价格为:66.9、编号为:1008 【图书】名称为:python从入门到实践、价格为:66.9、编号为:1009 【图书】名称为:python从入门到实践、价格为:66.9、编号为:1010 【图书】总册数为:10 */
-
此案例的核心概念为:
- 不管有多少个实例化对象,这些对象都只共享一个static的属性
- static的方法不受到类实例化的限制,可以直接使用类名进行调用
代码块
- 代码块一共分为4种,普通代码块、构造代码块、静态代码块、同步代码块,我们目前先对前三个代码块进行讲解:
普通代码块
-
普通代码块是编写在方法之中的代码块,下面我们通过观察程序的来理解代码块的含义
public class YootkDemo16{ public static void main(String[] ages){ if(true){ String title="Andy"; System.out.println("【if语句中的title】="+title); } String title="浩鑫"; System.out.println("【main方法中的title】="+title); } } /* 程序的执行结果为: 【if语句中的title】=Andy 【main方法中的title】=浩鑫 */
此时我们里利用了if语句的局部变量和全局变量的区别,我们的程序才能在变量重名的情况下正常的执行,如果我们main方法中的title方法if语句的前面,会出现什么情况呢?我们来观察一下
public class YootkDemo16{ public static void main(String[] ages){ String title="浩鑫"; System.out.println("【main方法中的title】="+title); if(true){ String title="Andy"; System.out.println("【if语句中的title】="+title); } } } /* 程序的编译结果为: YootkDemo16.java:6: 错误: 已在方法 main(String[])中定义了变量 title String title="Andy"; ^ 1 个错误 */
此时程序会直接报错,由于我们的程序为顺序结构的执行过程,当程序在前面已经检测到全局变量中已有变量名title时,会默认为后面所出现的变量title都是同一个,此时将不能再重新创建一个变量名相同的变量,这是一个全局变量与局部变量的区别。
事实上,在我们演示的第一个案例中,我们只要把if语句去掉,使用一个大括号{}对代码进行包裹,就是一个普通代码块
public class YootkDemo16{ public static void main(String[] ages){ {String title="Andy"; System.out.println("【if语句中的title】="+title); } String title="浩鑫"; System.out.println("【main方法中的title】="+title); } } /* 程序的执行结果为: 【if语句中的title】=Andy 【main方法中的title】=浩鑫 */
通过程序的执行结果我们可以发现,普通代码块的核心意义在于,定义的一个方法中,如果程序代码过多,那么避免由于变量名重复导致程序的编译错误,我们可以使用普通代码块对程序进行有效的分割;
构造代码块
-
构造代码块是直接定义在类中的,每当对象实例化的时候就会对构造块进行调用,每次都会进行调用,下面我们观察构造块
class Book{ { System.out.println("【构造代码块】" ); } public Book(){ System.out.println("【构造方法】"); } } public class YootkDemo16{ public static void main(String[] ages){ new Book(); new Book(); } } /* 程序的执行结果为: 【构造代码块】 【构造方法】 【构造代码块】 【构造方法】 */
通过程序的执行结果我们可以发现,在对象被实例化后,构造代码块比构造方法先进行调用,且每次实例化都会进行调用。
静态代码块
-
静态代码块实际上就是使用static关键字定义的代码块,我们通过代码来观察静态代码块
class Book{ { System.out.println("【构造代码块】" ); } public Book(){ System.out.println("【构造方法】"); } static { System.out.println("【静态代码块】" ); } } public class YootkDemo16{ public static void main(String[] ages){ new Book(); new Book(); } } /* 程序的执行结果为: 【静态代码块】 【构造代码块】 【构造方法】 【构造代码块】 【构造方法】 */
通过程序我们可以发现,静态代码块是优先于构造代码块和构造方法执行,且静态代码块只在对象第一次被实例化的时候执行一次,后面就不再执行了。
-
我们在程序的开发中如果要在类中对static的属性进行处理,由于static属性的特殊性,我们直接在构造方法中进行处理当然是不合适,这时候最佳的方法就是使用静态代码块来进行处理
class Book{ static String title; static{ title="Andy"; title=title+"浩鑫"; System.out.println("【静态代码块】"+title ); } public Book(){ } } public class YootkDemo16{ public static void main(String[] ages){ System.out.println("【主方法】"+Book.title); } } /* 程序的折行结果为: 【静态代码块】Andy浩鑫 【主方法】Andy浩鑫 */
-
由于静态代码块的特殊性,我们在程序中一般使用静态代码块来为静态成员属性进行初始化操作。