this和static
1 this
1.1 this概述
- this是一个关键字,全部小写。
- 一个对象是一个this。this是一个变量,是一个引用。this保存当前对象的内存地址,指向自身。所以严格意义上来说,this代表的就是“当前对象”,this存储在堆内存当中对象的内部。
- this只能使用在实例方法中。谁调用这个实例方法,this就是谁。所以this代表的是:当前对象。
- this大部分情况下是可以省略的。
- this不能用在静态方法中,因为this代表当前对象,静态方法中不存在当前对象。
1.2 this在实例方法中
/**
* @author wcs
* @date 2021/8/7 10:27
*/
public class ThisTest01 {
public static void main(String[] args) {
Student stu1 = new Student();
stu1.setNo(111111);
stu1.setName("张三");
System.out.println(stu1.getNo());
System.out.println(stu1.getName());
Student stu2 = new Student(222222, "李四");
System.out.println(stu2.getNo());
System.out.println(stu2.getName());
}
}
class Student {
private int no;
private String name;
public Student() {
}
public Student(int no, String name) {
this.no = no;//this不能省略
this.name = name;// this不能省略
}
public int getNo() {
return no;//其实返回的是this.no,这里的this可以省略
}
public void setNo(int no) {
this.no = no;//this不能省略
}
public String getName() {
return name;//其实返回的是this.name,这里的this可以省略
}
public void setName(String name) {
this.name = name;//this不能省略
}
}
以上代码当中this.name = name,其中this.name表示实例变量name,等号右边的name是局部变量name,此时如果省略“this.”,则变成name = name,这两个都是局部变量,和实例变量name无关了,显示是不可以省略“this.”的。
最终结论是,this不能出现在static的方法中,可以出现在实例方法中,代表当前对象,大部分情况下this可以省略,只有当在实例方法中区分局部变量和实例变量的时候不能省略。
1.3 this在构造方法中
this还有另外一种用法,使用在构造方法的第一行,通过当前构造方法调用本类当中其他的构造方法,其目的是为了代码服用。调用时语法格式:this(实际列表参数)。
示例:调用无参构造方法时默认日期为1970-1-1。
//创建日期类
/**
* @author wcs
* @date 2021/8/7 11:06
*/
public class Date {
private int year;
private int month;
private int day;
public Date() {
this.year = 1970;
this.month = 1;
this.day = 1;
}
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
public int getYear() {
return year;
}
public void setYear(int year) {
this.year = year;
}
public int getMonth() {
return month;
}
public void setMonth(int month) {
this.month = month;
}
public int getDay() {
return day;
}
public void setDay(int day) {
this.day = day;
}
}
//测试类
/**
* @author wcs
* @date 2021/8/7 11:09
*/
public class DateTest {
public static void main(String[] args) {
Date d1 = new Date();
System.out.println(d1.getYear() + "年" + d1.getMonth() + "月" + d1.getDay() + "日");
Date d2 = new Date(2020, 2, 2);
System.out.println(d2.getYear() + "年" + d2.getMonth() + "月" + d2.getDay() + "日");
}
}
运行结果:

无参数构造和有参数构造对比:

以上程序中无参构造方法中的代码和有参构造方法中的代码是一样的,代码没有得到复用性,这个时候就可以在无参构造方法中使用“this(实际参数列表);"来调用有参数的构造方法,这样可以让代码得到复用性。
public Date() {
this(1970,1,1);//使用this()调用有参构造方法
}
public Date(int year, int month, int day) {
this.year = year;
this.month = month;
this.day = day;
}
DateTest测试结果:

注意:this()语法只能出现在构造方法的第一行,在其他位置都会报错。
2 static
2.1 static概述
-
static翻译为“静态”。
-
所有static关键字修饰的都是类相关的,类级别的。
-
所有static修饰的,都是采用“类名.”的方式访问。
-
static修饰的变量:静态变量
-
static修饰的方法:静态方法
2.2 静态变量
变量的分类:
变量根据声明的位置进行划分:
在方法体当中声明的变量叫做:局部变量。
在方法体外声明的变量叫做:成员变量。
成员变量又可以分为:
实例变量
静态变量
/**
* @author wcs
* @date 2021/8/6 17:36
*/
public class VarTest01 {
//以下实例的,都是对象相关的,访问时采用“引用.”的方式访问。需要先new对象。
//实例相关的,必须先有对象才能访问,可能会出现空指针异常。
//成员变量中的实例变量
int i;
//实例方法
public void m1() {
}
//以下静态的,都是类相关的,访问时采用“类名.”的方式访问。不需要new对象
//成员变量中的静态变量
static int j;
//静态方法
public static void m2() {
}
}
未使用静态变量的程序:
/**
* @author wcs
* @date 2021/8/6 19:06
*/
public class VarTest02 {
public static void main(String[] args) {
//创建m1对象并为m1对象设置属性值
Man m1 = new Man(20, "张三", true);
System.out.println("年龄:" + m1.age + " 姓名:" + m1.name + " 性别:" + (m1.sex ? "男" : "女"));
//创建对象m2
Man m2 = new Man(18, "李四", true);
System.out.println("年龄:" + m2.age + " 姓名:" + m2.name + " 性别:" + (m2.sex ? "男" : "女"));
}
}
class Man {
int age;
String name;
Boolean sex;
public Man(int _age, String _name, Boolean _sex) {
age = _age;
name = _name;
sex = _sex;
}
}
JVM内存图:

“男人类”创建的所有“男人对象”,每个“男人对象”的年龄、姓名都不一样,该属性应该是每个对象都持有一份,所以应该定义为实例变量,而每一个“男人对象”的性别都是“男”,不会随着对象的改变而变化,性别值永远都是“男”,这种情况下,如果定义为实例变量,每个“男人对象”都有一份,在堆内存当中浪费空间,所以应该将“sex=男”属性定义为类级别,声明为静态变量,这样的变量不需要创建对象直接使用“类名.”访问。
public class VarTest02 {
public static void main(String[] args) {
//直接类名.进行访问
System.out.println(Man.sex);
//创建m1对象并为m1对象设置属性值
Man m1 = new Man(20, "张三");
//使用引用来访问静态变量也是可以的(不建议)
System.out.println("年龄:" + m1.age + " 姓名:" + m1.name + " 性别:" + (m1.sex ? "男" : "女"));
//创建对象m2
Man m2 = new Man(18, "李四");
System.out.println("年龄:" + m2.age + " 姓名:" + m2.name + " 性别:" + (m2.sex ? "男" : "女"));
}
}
class Man {
int age;
String name;
//使用静态变量赋初始值
static Boolean sex = true;
public Man(int _age, String _name) {
age = _age;
name = _name;
}
}
静态变量内存图:

当一个类的所有对象的某个“属性值”不会随着对象的改变而改变的时候,建议将该属性定义为静态属性(也就是静态变量),静态变量在类加载的时候初始化,存储在方法去当中,不需要创建对象,直接通过“类名.”来访问。
上段代码中静态变量也使用了“引用”来访问静态变量,但实际执行过程中,“引用”所指的对象并没有参与。如果是空引用访问实例变量,程序也不会发生空指针异常,表面看起来是“引用”去访问,实际上还是直接通过”类“去访问。静态方法也是如此。
2.3 静态代码块
静态代码块在类加载时执行,并且只执行一次。
一般用于记录类加载的日志信息。
静态代码块语法格式:
类{
//静态代码块
static{
Java语句;
}
}
静态代码块测试1:
/**
* @author wcs
* @date 2021/8/6 20:55
*/
public class StaticTest01 {
static {
System.out.println(3);
}
public static void main(String[] args) {
System.out.println("hello world");
}
static {
System.out.println(2);
}
static {
System.out.println(1);
}
}
运行结果:

通过以上的测试可以得知一个类当中可以编写多个静态代码块(尽管大部分情况下只编写一个),并且静态代码块遵循自上而下的顺序依次执行,所以有的时候放在类体当中的代码是有执行顺序的(大部分情况下类体当中的代码没有顺序要求,方法体当中的代码是有顺序要求的,方法体当中的代码必须遵守自上而下的顺序依次逐行执行),另外静态代码块当中的代码在 main 方法执行之前执行,这是因为静态代码块在类加载时执行,并且只执行一次。
静态代码块测试2:
/**
* @author wcs
* @date 2021/8/6 21:01
*/
public class StaticTest02 {
//静态变量在方法区,类加载时初始化
static int i = 10;
//静态代码块也在方法区,类加载时执行
static {
System.out.println(i);
}
//在main方法执行前执行了以上代码
public static void main(String[] args) {
}
}
输出结果为:10
类体当中的代码也是有顺序要求的,静态代码块在类加载时执行,静态变量在类加载时初始化,它们在同一时间发生,所以必然会有顺序要求,如果在静态代码块中要访问 i 变量,那么i 变量必须放到静态代码块之前。
当然也是不可以访问实例变量,实例变量必须先创建对象才能访问,静态代码块在类加载时执行,那个时候对象还没有创建,所有不可以访问实例变量。
实例语句块
除了静态代码块之外,还有一种语句块叫做:实例语句块
实例语句块在类加载时并没有执行。
实例语句块语法格式:
类体{
{
Java语句;
}
}
实例语句块在什么时候执行?
只要是构造方法执行,必然在构造方法执行之前,自动执行”实例语句块“中的代码。
实例语句块测试:
/**
* @author wcs
* @date 2021/8/6 21:17
*/
public class Test {
public static void main(String[] args) {
System.out.println("main begin");
new Test();
new Test();
new Test("hello");
new Test("world");
}
{
System.out.println("实例语句执行");
}
public Test() {
System.out.println("无参构造方法");
}
public Test(String test) {
System.out.println("有参构造方法");
}
}
运行结果:

2.4 静态方法
方法实际上描述的是行为动作,我认为当某个动作在触发的时候需要对象的参与,这个应该定义为实例方法。例如:每个人都会考试,但是考试的分数不一样,显然这是因人而异,这个考试方法应该定义为实例方法。
示例:
/**
* @author wcs
* @date 2021/8/6 21:34
*/
public class StaticTest {
public static void main(String[] args) {
Student stu1 = new Student("张三");
stu1.exam();
Student stu2 = new Student("李四");
stu2.exam();
}
}
class Student {
String name;
public Student(String _name) {
name = _name;
}
public void exam() {
System.out.println(name + "在考试");
Count();
}
public void Count(){
System.out.println(name+"可能没考好");
}
}
上段代码中张三、李四都参加考试,两个人的水平当然是不一样的,所以说最终结果也不同。所以考试这个方法应该为实例方法。
在实际开发中,”工具类“当中的方法一般定义为静态方法,因为工具类就是为了方便大家使用,将方法定义为静态方法,方便调用,不需要创建对象,直接类名就可以访问。比如”System.out.println()“。
仿照System.out.println()”写个工具类:
/**
* @author wcs
* @date 2021/8/7 8:56
*/
public class Tool {
public static void p(int data) {
System.out.println(data);
}
public static void p(Long data) {
System.out.println(data);
}
public static void p(float data) {
System.out.println(data);
}
public static void p(double data) {
System.out.println(data);
}
public static void p(boolean data) {
System.out.println(data);
}
public static void p(char data) {
System.out.println(data);
}
public static void p(String data) {
System.out.println(data);
}
public static void main(String[] args) {
p("hello");
p(true);
p(2.34);
p('A');
}
}
运行结果:
