static关键字
1.static修饰成员变量
在引入static关键字之前呢,我们先看这样一个示例: 1)设计一个Peple类如下:
public class People {
private String name;
private int age;
private int count;
public People(String name, int age) {
this.name = name;
this.age = age;
count++;
}
public void work() {
System.out.println("好好学习,年薪百万,迎娶白富美!");
}
public int getCount() {
return count;
}
}
2)设计一个TestDemo类如下:
public class TestDemo {
public static void main(String[] args) {
People people1 = new People("王昭君", 18);
People people2 = new People("赵云", 20);
People people3 = new People("安琪拉", 19);
People people4 = new People("吕布", 21);
People people5 = new People("貂蝉", 20);
}
}
?在TestDemo中我们new了5个对象,调用了5次People类中的构造函数,那么count值为多少呢?因为每new一个对象,在堆上开辟一个对象,我们知道对象的内存布局分为三部分,对象头、实例变量、内存填充,在Java new 一个对象在堆上开辟的内存大小博客中有提及到,一个对象一份实例变量,所以我们虽然new了5个对象,只是在每个对象中实现了count++,count的值都为1; ?那么怎么用count来实现统计对象的个数呢?也就是让每个对象共用一份变量?这就要引入static关键字啦! 当类中的变量用static关键字来修饰的时候,变量就变成了静态变量。静态变量是随着类的字节码的加载而被加载进内存的,所以只要程序一启动运行到该类时就会被加载进内存,不管创建了多少个对象在内存中只存储一份,跟对象无关。它是保存在方法区当中的,无论多少个对象该静态变量在内存中都只有一份。 修改上述示例中的代码,count用static关键字来修饰。 People类
public class People {
private String name;
private int age;
private static int count;
public People(String name, int age) {
this.name = name;
this.age = age;
count++;
}
public void work() {
System.out.println("好好学习,年薪百万,迎娶白富美!");
}
public int getCount() {
return count;
}
}
静态变量是使用类名直接调用的,在以前我们设计一个类,如果把类中的实例变量设为公有或默认包访问权限的情况下,是可以通过类生成的对象来访问的;而静态变量只和类有关,可以用类名来访问(在静态变量设为公有或默认包的情况下)
静态变量和实例变量的区别
注意:静态变量是定义在类中,方法体外面的。如果你清楚JVM,应该知道局部变量是保存在栈中的,而静态变量是保存在方法区中的,局部变量出了方法就被栈回收了,而静态变量不会,所以局部变量前不能加static关键字。
2.static修饰方法
在写代码过程中,我们有没有发现有些方法是通过创建对象的方式调用的,如常见的在键盘上输入一个整数,Scanner scanner = new Scanner(System.in); scanner.nextInt();而有的方法是通过类名直接调用的,比如 Arrays.copyOf();那么什么情况下可以直接用类名调用而不用间接的通过对象呢?这就与static关键字修饰方法有关了。 看这样的一个栗子,把上述中static关键字修饰变量的示例中People类的getCount()方法设计成静态方法。 代码如下:
public class People {
private String name;
private int age;
public static int count;
public People(String name, int age) {
this.name = name;
this.age = age;
count++;
}
public void work() {
System.out.println("好好学习,年薪百万,迎娶白富美!");
}
public static int getCount() {
return count;
}
}
在TestDemo测试类中用类名调用一下getcount方法:
public class TestDemo {
public static void main(String[] args) {
People people1 = new People("王昭君", 18);
People people2 = new People("赵云", 20);
People people3 = new People("安琪拉", 19);
People people4 = new People("吕布", 21);
People people5 = new People("貂蝉", 20);
int sum = People.getCount();
System.out.println(sum);
}
}
运行结果: 同时也验证了,静态变量跟对象无关,只和类有关,测试类中产生了5个对象,每产生一个对象调用一次构造函数,count++;故最后count的值为5。
静态方法和实例方法的区别
在静态方法中不可以出现实例变量,而在实例方法中可以出现实例变量,也可以出现静态变量。 因为在实例方法中有隐含的this,举个栗子,在上述People类中添加一个实例方法eat;
public void eat() {
this.name = "高富帅";
count = 1;
}
在实例方法中实例变量是否有this关键字都可以,因为实例方法会提供对象(this),就是指的是当前对象;当然在实例方法中也可以包含静态变量,静态变量不需要提供this,而实例方法会提供,静态变量要不要this就是它自己的事了,也就是给静态变量提供this,可以不要。 静态方法是不会提供this的,实例变量需要this,静态方法提供不了,故在静态方法中不能包含实例变量,如下:
3.类的初始化顺序
首先我们先引入静态块和实例块的概念,静态块是用来初始化静态变量的,实例块是用来初始化实例变量的。 设计一个A类如下:
public class A {
private int a;
private static int b;
static {
System.out.println("静态块");
}
{
System.out.println("实例块");
}
public A(){
System.out.println("构造函数");
}
}
为了能够在类的初始化顺序中看到实例变量和静态变量的初始化顺序,改进代码如下:
class B {
public B() {
System.out.println("实例变量");
}
}
class C {
public C() {
System.out.println("静态变量");
}
}
public class A {
private B a = new B();
private static C b = new C();
{
System.out.println("静态块");
}
{
System.out.println("实例块");
}
public A(){
System.out.println("构造函数");
}
}
初始化实例变量a的时候必定会调用B类的构造函数,初始化静态变量b的时候必定会调用C类的构造函数。 在TestDemo测试类中生成A的对象,并查看运行结果。
public class TestDemo {
public static void main(String[] args) {
A aa = new A();
}
}
运行结果: 通过运行结果可以发现,在生成对象,调用构造函数之前还搞了很多东西。 所以类的初始化的顺序 静态变量、静态块、实例变量、实例块、构造函数
4.单例模式
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例,也就是只能产生一个对象。
懒汉单例模式
懒汉单例模式: 在第一次调用的时候实例化
1)产生对象首先需要在堆上开辟内存,然后调用构造函数来生成一个对象。如果把类的构造函数设为公有的,那么就可以随便new对象了,所以我们把构造函数设为私有的,此时把构造函数设为私有的就会产生在类外无法访问的问题,不能调用构造函数,就不能生成对象了。 2)那么就需要在类中提供一个方法,在类中生成对象,返回出去。那么问题又来了,没有生成对象,在类外怎么调用这个方法呢?这种情况下只能考虑用类名调用了,用类名调用该方法,就要把该方法设为静态方法,在该方法中调用构造函数生成对象,用一个该类的引用变量指向这个对象,但是如果把该引用变量定义在该类中,在类外多次调用该静态方法,还是会生成多个对象,所以不能把这个引用变量定义为局部变量,应该把它定义在方法外,又因为在静态方法中只能调用静态变量,所以把该变量定义为静态变量。 3)最后我们考虑,一开始该静态变量为空,调用静态方法生成对象,就会给该静态变量赋值一个地址,再次调用又会给它重新赋值新的地址,为了只能产生一个对象,我们需要在生成对象的静态方法中给定一个限定条件,当引用变量为空的时候才产生对象。 People类:
public class People {
private static People p;
private People() {
}
public static People getInstance() {
if (p == null) {
p = new People();
}
return p;
}
}
TestDemo类生成对象,类名调用方法。
public class TestDemo {
public static void main(String[] args) {
People p = People.getInstance();
}
}
饿汉单例模式
饿汉单例模式: 在类初始化时,已自行实例化 饿汉单例模式与懒汉单例模式不同的是实例化时期不同,代码如下: People类:
public class People {
private People() {
}
private static People p = new People();
public static People getInstance() {
return p;
}
}
TestDemo类生成对象,类名调用方法。
public class TestDemo {
public static void main(String[] args) {
People p = People.getInstance();
}
}
|