目录
变量分类
局部变量:在方法体里面定义的变量,该变量会在程序执行到方法体时被初始化,存储在栈( stack)内存中。
成员变量:类体之内,方法体之外定义的变量,它又分为实例变量和静态变量。
实例变量:没有被static修饰的成员变量,实例变量是对象所拥有的,在创建对象时被初始化,存 储在堆内存中。
静态变量:被static修饰的成员变量,静态变量是被所有对象共享的,在类加载时会被初始化,存储在方法区中。
静态变量与实例变量
上面对Java中的变量进行了简单概述,下面我们详细讲解一下静态变量与实例变量
代码1:
//定义了一个people类 class people { //实例变量 //不能直接访问,需要new对象之后再访问 String name; int age; } public class Test01 { public static void main(String[] args){ //创建了people类的对象 people s1 = new people(); s1.name = " 张三 "; s1.age = 18; System.out.println("姓名->" + s1.name + "年龄->" + s1.age); people s2 = new people(); s2.name = " 李四 "; s2.age = 19; System.out.println("姓名->" + s2.name + "年龄->" + s2.age); } }
运行结果:
总结如下:
(1)实例变量的访问必须首先创建对象,然后通过 " 引用 . "的方式访问。
(2)实例变量如果直接通过" 类名. "的方式访问,会报以下错误。
(3)实例变量是每个对象独自拥有的,存在多个副本,每个副本互不影响。(s1,s2都有自己对应的名字和年龄,二者互不影响)。
代码2:
class people { //静态变量 static String country = "中国"; } public class Test01 { public static void main(String[] args){ System.out.println(people.country); people s1 = new people(); people s2 = new people(); System.out.println("s1->" + s1.country + " s2->" + s2.country); //更改s1对应的country s1.country = "中华"; System.out.println("s1->" + s1.country + " s2->" + s2.country); } }
运行结果:
总结如下:
(1)静态变量既可以通过" 引用 . "的方式访问,也可以通过" 类名. "的方式访问(此方法不需要对象)。
(2)静态变量是所有对象所共享的,在内存中只有一个副本(通过s1访问country和通过s2访问country其实访问的是同一个)。
(3)其实程序在运行时,遇见用引用访问静态变量,系统会将引用自动看成类名。
(4)在写代码时,访问静态变量最好还是使用" 类名. "的方式访问,以免其他人在读代码时造成不必要的误区。
那么什么时候用实例变量,什么时候用静态变量呢?
简单的说就是,如果一个变量所描述的属性是每一个对象都不一样的,那么就用实例变量,如果这个属性是属于这个类的整体特征,每一个对象都一样,那么就使用静态变量。
如果大家还有疑惑,想必看完下面代码就会清楚。
//中国人的类 class Chinese{ //姓名,每个人都有自己的姓名 String name; //身份证号,每个人都有自己的身份证号 String idCard; //国籍,中国人的国籍都是中国,这是属于类的整体特征 //如果声明为实例变量,每个对象都一样,就会浪费空间 static String country = "中国"; public Chinese(String x1 , String x2){ name = x1; idCard = x2; } }
静态方法和实例方法
学会了静态变量与实例变量的区别,再来学习静态方法与实例方法就会很简单。
静态方法与实例方法的语法结构有什么不同呢?
静态方法:修饰符+static 返回值类型 方法名(形式参数列表)
实例方法:修饰符(不含static) 返回值类型 方法名(形式参数列表)
静态方法与实例方法有着静态变量与实例变量相同的区别
代码3:
class test { //静态方法A public static void A(){ System.out.println("A"); } //实例变量B public void B(){ System.out.println("B"); } } public class Test01 { public static void main(String[] args){ //通过"类名."的方式访问静态方法A(不需要对象) test.A(); //创建test类的对象 test s = new test(); //通过"引用."的方式访问静态方法A s.A(); //通过"引用."的方式访问静态方法B s.B(); } }
运行结果:
总结如下:
(1)实例方法只能通过" 引用 . "的方式访问,静态方法既可以通过" 引用 . "的方式访问,也可以通过" 类名. "的方式访问(此方法不需要对象)。
(2)实例方法是每个对象独自拥有的,存在多个副本,每个副本互不影响,静态方法是所有对象所共享的,在内存中只有一个副本。
(3)其实程序在运行时,遇见用引用访问静态方法,系统会将引用自动看成类名。
上面都照搬静态变量与实例变量之间的区别,除此之外还有什么是需要注意的呢?
(4)静态方法中不能访问实例变量,若访问了,会报以下错误:
报的错误虽然和使用" 类名. " 的方式访问实例变量一样,但是要清楚,这是两个不同的错误。
(5)无论是静态方法还是实例方法,甚至是构造方法,在运行时都需要压栈。
那么什么时候用实例方法,什么时候用静态方法呢?
我总结出了以下两点:
(1)当该方法体当中访问了实例变量,该方法必须是实例方法,否则会报错。
(2)若不同对象进行这个"行为"(方法)时结果不同,那么需要使用实例方法,反之则用静态方法。
静态代码块与实例代码块
这块儿内容十分简单,我们先看看二者的语法结构
//静态代码块 static{ java语句; java语句; }
//实例代码块 { java语句; java语句; }
静态代码块就是一个static加一个花括号,括号里面写Java语句。
实例代码块直接就是一个花括号,括号里面写Java语句。
我们先来说一下他们的特点:
(1)静态代码块在类加载时执行,且只执行一次(在main方法之前执行)。
(2)实例代码块并未在类加载时执行,只要是构造方法执行(创建对象时),一定会在构造方法执行之前执行实例代码块。
(3)静态代码块和实例代码块要放在类体之内,方法体之外。
下面我们分别来感受一下两种代码块:
静态代码块
代码4:
public class Test01 { public static void A(){ System.out.println("方法A开始执行!"); } //静态代码块 static{ System.out.println("静态代码块1开始执行!"); } public static void main(String[] args){ A(); } static{ System.out.println("静态代码块2开始执行!"); } }
运行结果:
实例代码块
代码5
class test { //实例代码块1 { System.out.println("实例代码块1执行!"); } //构造方法1 public test(){ System.out.println("构造方法执行!"); } //实例代码块2 { System.out.println("实例代码块2执行!"); } } public class Test01 { public static void main(String[] args){ test s1 = new test(); test s2 = new test(); } }
运行结果:
JVM的内存结构
我们学习了上面内容后,再了解一下它们是如何在内存中存在的
代码6
class Chinese{ //姓名,每个人都有自己的姓名 String name; //身份证号,每个人都有自己的身份证号 String idCard; //国籍,中国人的国籍都是中国,这是属于类的整体特征 //如果声明为实例变量,每个对象都一样,就会浪费空间 static String country = "中国"; public Chinese(String x1 , String x2){ name = x1; idCard = x2; } } public class Test { public static void main(String[] args) { System.out.println(Chinese.country); Chinese people1 = new Chinese("张三" , "610234434344243"); System.out.println(people1.name); System.out.println(people1.idCard); Chinese people2 = new Chinese("李四" , "610238838238237"); System.out.println(people2.name); System.out.println(people2.idCard); } }
下面是以上代码在JVM中的内存分配 :