Thinking in Java 第五章 初始化与清理

用构造器初始化类:

构造器是一个很特殊的方法,方法名必须大写,并且没有返回类型(与返回void是不同的),没有参数的构造器称为无参构造器,例:

 

public class SimpleConstructor {

	// 默认无参构造器
	public SimpleConstructor() {
		
	}
	
	// 带参数的构造器
	public SimpleConstructor(String str){
		System.out.println(str);
	}
}

方法重载

每个重载的方法都必须有一个独一无二的参数类型列表,甚至参数顺序不同也可以,但这样不便于区分;例:

 

public class OverLoadingOrder {

	static void f(String str, int i){
		System.out.println("str:" + str + ",int:" + i);
	}
	
	static void f(int i, String str){
		System.out.println("int:" + i + ",str:" + str);
	}
	
	public static void main(String[] args) {
		f("abc", 1);
		f(1, "abc");
	}
}

 这样做是合法的,但并不便于区分两个方法的功能,所以一般重载的方法都有不同的类型或不同数量的参数。

基本类型的重载

如果传入的数据类型(实参)小于小于方法声明的参数类型(形参),实际数据类型就会被提升;如果形参类型范围小于实参,需要先进行窄化转换,例:

 

public class PrimitiveOverloading {

	// 该方法接收long类型参数
	static void f1(long x){
		System.out.println("f with param type long");
	}
	
	// 该方法接收int类型参数
	static void f2(int x){
		System.out.println("f with param type int");
	}
	
	public static void main(String[] args) {
		// 定义一个int类型的变量,在调用f1方式时,由于找不到有最佳匹配参数int形的方法,所以直接将int提升成了long型
		int x = 5;
		f1(x);
		
		long y = 5L;
		// 以下编译会报错,形参类型范围不能小于实参	
//		f2(y);
		// 进行窄化转换后可以使用,需要注意这样的转换是不安全的
		f2(Integer.parseInt(String.valueOf(y)));
	}
}

 

返回值是不能用来区分重载的,因为编译器无法区分你需要调用哪个方法。

默认构造器

在创建对象的时候,如果没有自定义构造器,编译器会自动创建一个无参的构造器;一旦自定义了构造器,不管有无参数,编译器都不会再自动创建构造器,例:

 

package com.test.tij4.initialization;

public class NoSynthesis {

	public static void main(String[] args) {
		// 编译器会报错,因为自定义了有参数的构造器,所以在创建对象时必须传递参数
		// new Car();
		// 成功创建对象,并将i的值传递给Car的属性number
		new Car(1);
	}
}

class Car {
	private int number;

	Car(int i) {
		this.number = i;
	}
}

 this 关键字

this所指代的是当前对象,操作方式和直接操作该对象的引用相同;在方法的调用时,在我们能见的参数之外,编译器还会将当前对象的引用作为第一个参数传递到被调用方法中,例:

 

public class Banana {
	public static void main(String[] args) {
		Person p = new Person();
		p.eat(1);
		// 上面一句的方法调用等同于下面一句,但无法编译通过,只需理解即可
		Person.eat(p, 1);
	}
}
class Person{ void eat(int count){} }

 如果有多个构造器,可以在构造器中调用构造器,但调用构造器必须放于当前构造器的第一句,并且一个构造器只能调用一个构造器

 

public class Flower {

	private int x;
	
	Flower(int x, int y){
		// 构造器中调用构造器
		this(x);
		// 编译错误,必须放置第一位,其实必须放置第一位已经明确一个构造器中是不可能调用两个构造器的
		this();
		// this的用法之一,用于区分同名的全局变量和局部变量
		this.x = x;
		System.out.println(y);
	}
	Flower(int x){
		
	}
	Flower(){
		
	}
	public static void main(String[] args) {
		new Flower(1, 2);
	}
}

static的含义

static的方法中不能使用this关键字,因为它本身就没有对象的实例,因此也不能调用非静态方法;仅仅通过类本身调用static方法,这也是static方法的主要作用之一;Java中没有全局方法的概念,但通过static方法可以做到互相访问。

finalize()方法

该方法用于垃圾回收中,可以理解为垃圾回收准备方法,因为finalize()方法一被调用,在下一次的垃圾回收时对象就会被回收,而不是说调用了该方法就直接进行回收。

在Java中对象并非总被回收或者说:

 

  1. 对象可能不被垃圾回收
  2. 垃圾回收并不等于c、c++中的析构(finalize())
  3. 垃圾回收只与内存有关
在Java中,需要记住垃圾回收不一定会发生,如果JVM未面临内存耗尽的情况,垃圾回收并不会执行。

 

成员初始化

对于局部变量,必须手动赋值初始化;对于成员遍历,如果不手动初始化,编译器会给出默认值;需要注意的是即使比如在构造器中给成员变量赋值,但其实在成员变量已经被自动初始了,这样也保证了成员变量的默认初始化。

初始化顺序:

 

class Window{
	Window(int count){
		System.out.println("new Window:" + count);
	}
}

class House{
	Window w1 = new Window(1);
	House(){
		System.out.println("new House");
		w2 = new Window(12);
	}
	void f(){
		System.out.println("execute method f");
	}
	Window w2 = new Window(2);
}
/**
 * 初始化顺序
 */
public class OrderOfInitialization {
	public static void main(String[] args) {
		House h = new House();
		h.f();
	}
}

/* result
new Window:1
new Window:2
new House
new Window:12
execute method f
*/

从以上例子结果可以看出,不论位置,变量的初始化先于构造方法,构造方法先于普通方法;w2被初始化了两次,一次是在Window w2 = new Window(2)时,第二次是在构造器中。

带有staic的初始化顺序:

class Table{
	public Table() {
		System.out.println("init table");
	}
	void f(){
		System.out.println("execute method f");
	}
}
class Cupboard{
	{
		System.out.println("cupboard area");
	}
	static {
		System.out.println("cupboard with static area");
	}
	public Cupboard() {
		System.out.println("init cupboard");
	}
	
}
public class StaticInitialization {
	public static void main(String[] args) {
		table.f();
		Cupboard cupboard = new Cupboard();
	}
	static Table table = new Table();
}
/* result
init table 静态成员变量初始化先于方法执行
execute method f
cupboard with static area 静态代码块执行现有构造器
cupboard area 非静态代码块执行次于静态代码块,但同样先于构造器
init cupboard
 */

可变参数列表

public class Varargs {
	static void f(Object... objects){
		for(Object obj : objects){
			System.out.println(obj);
		}
	}
	static void o(int... is){
		for(int i : is){
			System.out.println(i);
		}
	}
	static void o(String... strings){
		for(String str : strings){
			System.out.println(str);
		}
	}
	static void s(int size, int... is){
		for(int i : is){
			System.out.println(i);
		}
	}
	static void s(String c, String... strings){
		for(String str : strings){
			System.out.println(str);
		}
	}
	public static void main(String[] args) {
		f(1,2,3); // 可变参数的意义就在于可以输入任意长度的参数
		f(); // 即使一个也没有也是合法的
		o("1","2"); // 由此可见可变参数的重载和普通方法的没两样
//		o(); 但这样就不行了,因为编译器无法知道你调用的是那个o方法
		// 解决的办法就是分别给有可变参数的方法加上足以区分的其他参数列表
		s(1,2,3);
		s("1","2","3");
	}
}

枚举类型

定义一个枚举,完全可以看做是一个类来处理

public enum SimpleEnme {
	RED, BLUE, GREY, WHITE, BLACK
}

 使用枚举

public class UseEnum {
	public static void main(String[] args) {
		// 遍历值和查看顺序
		for(SimpleEnum e : SimpleEnum.values()){
			System.out.println(e + "ordinal:" + e.ordinal());
		}
		// 在Switch中使用
		s(SimpleEnum.RED);
		s(SimpleEnum.WHITE);
	}
	static void s(SimpleEnum e){
		switch(e){
			case RED : System.out.println("RED"); break;
			case BLACK : System.out.println("BLACK"); break;
			default : System.out.println("Deafult");
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值