今天来分析一下Java中this关键字
this的几种用法
调用其他构造函数
public class ThisDemo {
private int age;
private String name;
public ThisDemo(String name) {
this();
this.name = name;
}
public ThisDemo() {
this.age = 18;
}
}
这里我们使用一个含参的构造函数调用了一个无参构造函数。有一点需要我们注意:
使用this()调用构造器,必须要放在第一行,如果可以放在其他行,当ThisDemo存在父类Father时,实例化过程如下:
- 第一行没有this()或者super(),默认执行super()
- 完成对父类对象的初始化
- 返回到子类构造函数继续执行
- 此时调用this(),将会再次调用super(),再次对父类对象初始化,这样就会产生两个对象,造成了资源的浪费
所以java为了保持对象的合理性,是禁止这种操作的。
获取当前对象的引用
public class ThisDemo {
private String name;
public void setName(String name) {
this.name = name;
}
}
这里我们调用了this的name属性,在方法中将形参name传递给成员变量name,使用this可以便于区分成员变量和局部变量,提高代码的可读性。
代表自身对象
public class ThisDemo {
private String name;
private String age;
public ThisDemo setName(String name) {
this.name = name;
return this;
}
public void setAge(int age) {
this.age = age;
}
public static void main(String[] args) {
ThisDemo demo = new ThisDemo();
demo.setName("stalary").setAge(21);
}
这里我们使用this来返回的当前的对象,可以把这个this当成一个类型为该类类型,名称为this的隐含成员变量。
引用溢出
@Data
class FinalReferenceEscape{
final int i;
int age;
static FinalReferenceEscape object;
public FinalReferenceEscape() {
i = 1; // 写final域
this.age = 18;
object = this;// this引用溢出
}
public static void write() {
new FinalReferenceEscape();
}
public static void read() {
if (object != null) {
System.out.println(object.i);
}
}
public static void main(String[] args) {
new Thread(() -> FinalReferenceEscape.write()).start();
new Thread(() -> FinalReferenceEscape.read()).start();
System.out.println(FinalReferenceEscape.object);
}
}
结果
1
FinalReferenceEscape(i=1, age=18)
虽然写final域的重排序规则是可以确保的。
但是要除了要保证在引用变量为任意线程可见之前,该引用变量指向的对象的final域已经在构造函数中被正确初始化过,而且在构造函数内部,不能让这个引用为其他线程所见(对象引用不能在构造函数中溢出)
在上面的代码中:
一个线程执行write,一个线程执行read,这时就可能会发生下图的执行顺序
重排序.png
上图表示,在构造函数还没完成时,就已经读取到了不为null的对象,构造函数中的this造成了引用的溢出。
所以,在构造函数返回前,被构造对象的引用不能为其他线程可见,因为此时的 final 域可能还没有被初始化。
主要参考资料:
《Java并发编程的艺术》
网友评论