Java基础(二)--this关键字及初始化
构造器:
构造器的名称必须和类名完全相同,所以一般方法的"首字母小写"命名规则并不适合构造器
默认构造器:
也叫无参构造器,作用就是创建一个默认对象,如果你不是手写出来,编译器默认会创建
有参构造器:
也就有拥有参数的构造器,一旦写了有参构造器,编译器不会创建默认构造器。可以写多个构造器,也就是方法的重载
区分重载:
参数,不能通过返回值区分
this关键字:
public class Test1 { private void add(int i) { } public static void main(String[] args) { Test1 a = new Test1(); Test1 b = new Test1(); a.add(1); b.add(2); } }
对象a,b都调用了add(),为了区分,编译器把这个对象的引用作为第一个参数传递给add(),就变成了
Test1.add(a, 1)
这是内部的表现形式,你并不能直接这样写,无法通过编译
this关键字只能在方法内部使用,可以用来获取当前对象(调用当前方法的对象)的引用,所以可以把this理解为一般的对象引用,当前方法
的this会自动应用同一个类的其他方法,所以在一个方法中调用另一个方法不需要使用this,直接调用就可以,可以用,但没必要。
1、通常可以返回当前对象的引用
public class Test { int i = 0; Test increment(){ i++; return this; } }
2、可以将当前对象传递给其他方法
public class Test1 { public static Test add(Test test) { return test; } } public class Test { Test add() { return Test1.add(this); } }
3、构造器互相调用
在jdk源码中和日常开发中很常见,例如:
public class ThreadPoolExecutor extends AbstractExecutorService { public ThreadPoolExecutor(int var1, int var2, long var3, TimeUnit var5, BlockingQueue<Runnable> var6) { this(var1, var2, var3, var5, var6, Executors.defaultThreadFactory(), defaultHandler); } public ThreadPoolExecutor(int var1, int var2, long var3, TimeUnit var5, BlockingQueue<Runnable> var6, ThreadFactory var7) { this(var1, var2, var3, var5, var6, var7, defaultHandler); } public ThreadPoolExecutor(int var1, int var2, long var3, TimeUnit var5, BlockingQueue<Runnable> var6, RejectedExecutionHandler var7) { this(var1, var2, var3, var5, var6, Executors.defaultThreadFactory(), var7); } public ThreadPoolExecutor(int var1, int var2, long var3, TimeUnit var5, BlockingQueue<Runnable> var6, ThreadFactory var7, RejectedExecutionHandler var8) { this.ctl = new AtomicInteger(ctlOf(-536870912, 0)); this.mainLock = new ReentrantLock(); this.workers = new HashSet(); this.termination = this.mainLock.newCondition(); if (var1 >= 0 && var2 > 0 && var2 >= var1 && var3 >= 0L) { if (var6 != null && var7 != null && var8 != null) { this.corePoolSize = var1; this.maximumPoolSize = var2; this.workQueue = var6; this.keepAliveTime = var5.toNanos(var3); this.threadFactory = var7; this.handler = var8; } else { throw new NullPointerException(); } } else { throw new IllegalArgumentException(); } } }
PS:this只能调用一个构造器,不能写多个,而且要放在方法第一行,和super很像
4、区分参数和成员变量的名称
public Test(int i, String s) { this.i = i; this.s = s; }
static method只能调用static,反过来倒是可以。this属于对象的,所以不能在static使用
构造器初始化:
变量定义的先后顺序决定了初始化顺序。即使随便分布,还是在构造方法之前调用
public class Test1 { public Test1(int i) { System.out.println("Test1: " + i); } } public class Test { Test1 test1 = new Test1(1); Test() { System.out.println("Test"); test3 = new Test1(4); } Test1 test2 = new Test1(2); void add() { System.out.println("add"); } Test1 test3 = new Test1(3); public static void main(String[] args) { Test test = new Test(); test.add(); } }
结果:
Test1: 1 Test1: 2 Test1: 3 Test Test1: 4 add
静态数据初始化:
无论创建多少对象,和静态数据没关系,始终只占用一份存储空间
public class Bowl { Bowl(int marker) { System.out.println("Bowl: " + marker); } void fl(int marker) { System.out.println("fl: " + marker); } }
public class Table { static Bowl bowl1 = new Bowl(1); Table() { System.out.println("Table()"); bowl2.fl(2); } void f2(int marker) { System.out.println("f2: " + marker); } static Bowl bowl2 = new Bowl(2); }
public class Cupboard { Bowl bowl3 = new Bowl(3); static Bowl bowl4 = new Bowl(4); Cupboard() { System.out.println("Cupboard()"); bowl4.fl(3); } void f3(int marker) { System.out.println("f3: " + marker); } static Bowl bowl5 = new Bowl(5); }
public class Test { public static void main(String[] args) { System.out.println("Create new Cupboard() in main"); new Cupboard(); System.out.println("Create new Cupboard() in main"); new Cupboard(); table.f2(1); cupboard.f3(1); } static Table table = new Table(); static Cupboard cupboard = new Cupboard(); }
结果:
Bowl: 1 Bowl: 2 Table() fl: 2 Bowl: 4 Bowl: 5 Bowl: 3 Cupboard() fl: 3 Create new Cupboard() in main Bowl: 3 Cupboard() fl: 3 Create new Cupboard() in main Bowl: 3 Cupboard() fl: 3 f2: 1 f3: 1
初始化的顺序:
1、父类的静态变量赋值
2、自身的静态变量赋值
3、父类成员变量赋值和父类块赋值
4、父类构造函数赋值
5、自身成员变量赋值和自身块赋值
6、自身构造函数赋值
PS:
1、静态变量只有在第一次对象被创建(第一次访问静态数据)的时候,才会被初始化,而且只有这一次
2、构造器是静态方法,即使没有显式地使用static关键字
显式的静态初始化:
public class Cup { Cup(int marker) { System.out.println("Cup: " + marker); } void f(int marker) { System.out.println("f: " + marker); } }
public class Cups { static Cup cup1; static Cup cup2; static { cup1 = new Cup(1); cup2 = new Cup(2); } Cups() { System.out.println("Cups()"); } }
public static void main(String[] args) { System.out.println("Inside main()"); Cups.cup1.f(99); }
static Cups cups1 = new Cups();
结果:
Cup: 1 Cup: 2 Cups() Inside main() f: 99
PS:再一次证明,静态初始化只会执行一次
再举个栗子,来自于:https://mp.weixin.qq.com/s/HvElZRVf_iFAh24cwuFu-Q
public class StaticTest { public static void main(String[] args) { staticFunction(); } static StaticTest st = new StaticTest(); static { System.out.println("1"); } { System.out.println("2"); } StaticTest() { System.out.println("3"); System.out.println("a="+a+",b="+b); } public static void staticFunction(){ System.out.println("4"); } int a=110; static int b =112; }
结果:
2
3
a=110,b=0
1
4
结果有点出人意料:实例初始化竟然在类初始化前面
类加载过程:
1、准备:
为类变量分配内存并设置默认值,因此类变量st为null、b为0。
PS:
如果这里b被final修饰,编译时javac将会为value生成ConstantValue属性,在准备阶段虚拟机就会根据ConstantValue的设置将变量设置为
指定的值。
例如:
static final int b=112,那么在准备阶段b的值就是112,而不再是0了。
初始化:
就是执行类构造器,类构造器是编译器收集所有静态语句块和类变量的赋值语句,此时会执行:static StaticTest st = new StaticTest();
冷知识点:
嵌套初始化时有一个特别的逻辑。内嵌的这个变量恰好是个静态成员,而且是本类的实例。
就会导致:"实例初始化竟然出现在静态初始化之前"。
我们在前面已经试验过,如果static StaticTest st = new StaticTest()中StaticTest不是本类,就不会产生这个现象的