static修饰符可以用来修饰类的成员变量、成员方法和代码块:
1、用static修饰的成员变量表示静态变量,可以直接通过类名来访问;
2、用static修饰的成员方法表示静态方法,可以直接通过类名来访问;
3、用static修饰的程序代码表示静态代码块,当Java虚似机加载类时,就会执行该代码块。
4、被static所修饰的成员变量和成员方法表明归某个类所有,它不依赖于类的特定实例,被类的所有实例共享。
static变量(静态变量)
成员变量:定义在类里面、方法外面的变量,分两种:
a. 实例变量
b. 静态变量: 形式和实例变量类似,在实例变量前面加static关键字;
static变量和实例变量的区别:
1、static变量对于每个类而言在内存中只有一个,能被类的所有实例所共享;实例变量对于每个类的每个实例都有一份,它们之间互不影响;
2、Java虚拟机在加载类的过程中为static变量分配内存,实例变量在加载完类后创建对象时分配内存;
3、static变量可以直接通过类名访问,实例变量通过引用类型变量访问;
4、 先有静态变量,后有对象。静态变量优先于对象,类加载时,静态变量就在方法区产生,堆中就不在生成。
Q1:下面程序运行完毕后,count1、count2对应的值是多少。
public class Counter {
public int count1 = 0;
public static int count2 = 0;
public static void main(String[] args) {
Counter counterA = new Counter();
Counter counterB = new Counter();
counterA.count1++; //1
counterA.count2++; //1
counterB.count1++; //1
counterB.count2++; //2
}
}
作用:统计一个类创建实例的个数;
public Student{
public static int num = 0;
public Student(){
num++;
}
}
总结:
static变量也称作静态变量,静态变量和非静态变量的区别是:静态变量被所有的对象所共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化。而非静态变量是对象所拥有的,在创建对象的时候被初始化,存在多个副本,各个对象拥有的副本互不影响。
static作用: 同一类事物存在共同的属性的时候使用,改变集体中的一个,相当与改变集体
public class Student{
public static String country = "美国";
Student s1 = new Student();
s1.country = "中国"; // 通过对象名调用静态变量(静态变量凌驾于对象之上) 不好,可读性差。
Student.country = "中国"; // 通过类名调用静态变量
}
比如:类名.方法名();
Math.random(); random()静态方法 工具类中工具方法,一般用static修饰,这样不用实例化,可以通过类名直接调用的方法
static方法(静态方法)
成员方法分为静态方法和实例方法。用static修饰的方法叫静态方法,或类方法。静态方法也和静态变量一样,不需要创建类的实例,可以直接通过类名来访问。
static方法一般称作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说,是没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。并且由于这个特性,在静态方法中不能访问类的非静态成员变量和非静态成员方法,因为非静态成员方法/变量都是必须依赖具体的对象才能够被调用。
(静态方法没有对象,就没有this,而非静态方法变量必须有对象才能调用)
但是要注意的是,虽然在静态方法中不能访问非静态成员方法和非静态成员变量,但是在非静态成员方法中是可以访问静态成员方法/变量的。(静态方法中只能调用静态成员)
static方法可以直接访问所属类的静态变量和静态方法;
public class Sample1 {
public static int add(int x, int y) {
return x+y;
}
}
public class Sample2 {
public void method() {
int result = Sample1.add(1,2);
System.out.println("result= " + result);
}
}
——static方法注意事项:
1、不能使用this关键字;
this代表什么?this代表当前对象,但是对于静态方法来说没有对象,也就没有this的,因为它不依附于任何对象,既然都没有对象,就谈不上this了。
2、不能使用super关键字;
super关键字用来访问当前实例从父类中继承的方法和属性。super关键字与类的特定实例相关;
3、静态方法必须被实现;
静态方法用来表示某个类所特有的功能,这种功能的实现不依赖于类的具体实例,也不依赖于它的子类。既然如此,当前类必须为静态方法提供实现。
4、父类的静态方法不能被子类覆为非静态方法;
public class Base {
public static void method() {}
}
public class Sub extends Base {
public void method() {}//编译出错
}
5、父类的非静态方法不能被子类覆盖为静态方法;
父类中方法是非静态的,子类重写的时候也只能是非静态的。父类中方法是静态的,子类重写的时候也只能是静态的。(要有static都有,没有都没有)
代码块
public class Sample {
static int i = 5;
static {//第一个静态代码块
System.out.println("First Static code i="+i++);
}
static {//第二个静态代码块
System.out.println("Second Static code i="+i++);
}
public static void main(String[] args) {
Sample s1 = new Sample();
Sample s2 = new Sample();
System.out.println("At last, i= "+i);
}
}
局部代码块:被定义在局部位置 ,限制了局部变量的生命周期
public static void main(String[] args){
{
int a = 3;
}
{
System.out.println("a");//不能输出3
}
}
构造代码块:在类中方法外出现;多个构造方法方法中相同的代码存放到一起,每次调用构造都执行,并且在构造方法前执行
Student t = new Student(); //先执行A再执行B 实例化类先执行构造代码块,在执行构造器。
class Student{
{
System.out.println("初始化对象"); //A
//构造代码块,可以做一些初始化对象的操作
}
public Student(){
System.out.println("构造器"); //B
}
}
静态代码块:
static关键字还有一个比较关键的作用就是用来形成静态代码块以优化程序性能。static块不存于任何方法中,可以置于类中的任何地方,类中可以有多个static块。在Java虚拟机初次被加载的时候时会执行这些静态代码块类,会按照static块的顺序来执行每个static块,并且只会执行一次。
很多时候会将一些只需要进行一次的初始化操作都放在static代码块中进行。
类的构造方法用于初始化类的实例,而类的静态代码块则可用于初始化类,给类的静态变量赋初始值。
静态代码块与静态方法一样,也不能直接访问类的实例变量和实例方法,而必须通过实例的引用来访问它们。
Student t1 = new Student(); //先执行A再执行B 实例化类先执行构造代码块,再执行构造器。但无论实例化多少对象,只在首次类加载的时候执行一次静态代码块
Student t2 = new Student(); //执行B
class Student{
static{
System.out.println("静态代码块"); //A
//静态代码块,对类的初始化操作
}
public Student(){
System.out.println("构造器"); //B
}
}
new一个对象的时候JVM都做了那些事情:
1.之前没有进行类加载
(1)类加载,同时初始化类中静态的属性(赋默认值)
(2)执行静态代码块
(3)分配内存空间,同时初始化非静态的属性(赋默认值)
(4)调用父类构造器
(5)父类构造器执行完后,如果自己声明属性的同时有显示的赋值,那么进行显示赋值把默认值覆盖
(6)执行匿名代码块
(7)执行构造器
(8)返回内存地址
public class Test {
public static void main(String[] args) {
A a = new B();
}
}
class A{
protected String name = "lisi";
public A() {
System.out.println("父类构造器A");
System.out.println("父类构造器A中调用test方法开始,由于子类重写过test方法 所以这里执行子类的test方法");
test();
System.out.println("父类构造器A中调用test方法结束");
}
public void test(){
}
}
class B extends A{
private String name = "tom";
{
System.out.println("子类匿名代码块中:"+name);
}
public B() {
System.out.println("子类构造器B");
}
public void test(){
System.out.println("test方法中:this.name="+this.name);
System.out.println("test方法中:super.name="+super.name);
}
}
打印结果:
父类构造器A //子类构造器调用父类构造器
父类构造器A中调用test方法开始,由于子类重写过test方法所以这里执行子类的test方法
test方法中:this.name=null //这个时候父类构造器还没有执行完 所以子类中的属性不会显示赋值 所以只有初始的默认值null
test方法中:super.name=lisi //这个时候父类构造器开始调用 了 所以父类中的属性已经有了显示赋的值了
父类构造器A中调用test方法结束
子类匿名代码块中:tom //这个时候父类构造器已经调用结束 所以子类中的属性已经有了显示赋的值了
子类构造器B
结论: 子类中的属性的显示赋值的时机 是在 父类构造器执行完之后和子类的匿名代码块执行之前的某个时候
2.之前已经进行了类加载
(1)分配内存空间,同时初始化非静态的属性(赋默认值)
(2)调用父类构造器
(3)父类构造器执行完后,如果自己声明属性的同时有显示的赋值,那么进行显示赋值把默认值覆盖
(4)执行匿名代码块
(5)执行构造器
(6)返回内存地址
Q1:static关键字会改变类中成员的访问权限吗?
Q2:能通过this访问静态成员变量吗?
静态里面就没有this和super
Q3:为什么main方法要加static修饰
java都是以类作为程序的组织单元,当我们要执行的时候,我们并不知道这个main方法会放到哪个类当中,也不知道是否是要产生类的一个对象,为了解决程序的运行问题,我们将这个main方法定义为static,这样的话,当我们在执行一个java代码的时候,我们在命令提示符中写:java Point(Point为一个类),解释器就会在Point这个类当中,去调用这个静态的main方法,而不需要产生Point这个类的对象,当我们加载Point这个类的时候,那么main方法也被加载了,作为我们java程序的一个入口。
public 被jvm调用,访问权限足够大。
static 被jvm调用,不用创建对象,直接类名访问
void被jvm调用,不需要给jvm返回值
main 一个通用的名称,虽然不是关键字,但是被jvm识别
String[] args 以前用于接收键盘录入的