1、构造器
每个类都必须至少有一个构造器,如果没有显式的定义,编译器会自动为该类创建一个隐式的无参构造器(无参构造器也称为默认构造器)。
构造器没有返回值,不会返回任何东西(返回值为空和没有返回值是两码事),但是下面的语句会让人疑惑:
1 Object object = new Object();
实际上是new表达式返回了新建对象的引用,构造器没有返回任何东西。
2、方法的重载
方法名、方法参数表(参数数量、参数类型、参数顺序)区分了两个的方法。
改变参数顺序也可以实现方法重载,但是不推荐使用,因为不利于维护和使用。一般是利用参数数量、类型来实现方法重载。
由于基本类型能从一个“较”小的类型自动提升到一个“较大”的类型,所以当重载涉及到基本类型时,容易发生混淆。看下面的代码:
1 import static java.lang.System.*; 2 3 public class Overload { 4 5 /** 6 * @param args 7 */ 8 public static void main(String[] args) { 9 byte b = 1; 10 char c = 1; 11 short s = 1; 12 int i = 1; 13 long l = 1; 14 float f = 1; //f=1 没有问题,因为1是整数,可以被提升为float而不丢失精度 15 //! float f = 1.0; //f=1.0则会提示错误,因为小数默认是double。需要进行强制类型转换,或者通过 f=1.0F 明确告诉编译器我要定义一个float型数据 16 double d = 1; // d=1,提升为double,正确; d=1.0,正确。 17 18 19 Overload ol = new Overload(); 20 ol.f1(1); //整数字面常量被看做是int型,所以调用f1(int i) 21 ol.f1(1.0); //小数字面常量被看做是double型,所以调用f1(double i) 22 23 //! ol.f2(b); //错误,byte不会被自动提升到char 24 //! c=b //错误,byte不会被自动提升到char 25 ol.f3(b); //byte会自动提升到short,所以调用f3(short i) 26 27 //首先寻找完全匹配的方法,有则调用,反之如果实际传入的数据类型小于方法声明的形参类型,实参会被就近提升,然后调用相应的方法。自动转型遵循下面规律: 28 //byte < short < int < long < float < double 29 //char < int < long < float < double 30 31 } 32 33 void f1(byte i) { 34 out.println("f1(byte i)"); 35 } 36 void f1(char i) { 37 out.println("f1(char i)"); 38 } 39 void f1(short i) { 40 out.println("f1(short i)"); 41 } 42 void f1(int i) { 43 out.println("f1(int i)"); 44 } 45 void f1(long i) { 46 out.println("f1(long i)"); 47 } 48 void f1(float i) { 49 out.println("f1(float i)"); 50 } 51 void f1(double i) { 52 out.println("f1(double i)"); 53 } 54 55 void f2(char i) { 56 out.println("f2(char i)"); 57 } 58 void f3(short i) { 59 out.println("f3(short i)"); 60 } 61 }
注意,不能用返回值类型来区分方法
3、this
表示调用方法的当前对象的引用,只能在方法内部使用。
this对于将当前对象传递给其他方法也很有用,例如:
1 class Peeler { 2 static Apple peel(Apple apple) { 3 //... 4 //... 5 return apple; 6 } 7 } 8 9 class Apple { 10 Apple getPeeled() { 11 return Peeler.peel(this); //为了将自身传递给外部方法,Apple必须使用this关键字 12 } 13 }
this的另一种用法是在一个构造器中调用另一个,但是必须位于构造器方法体的第一行。
static方法中不存在this
4、类被加载的时机
当调用类中的静态成员变量、静态方法、创建该类的对象时,即使用该类时,类才被加载。
5、初始化顺序
类被加载时,静态成员、静态块中的变量依照定义顺序完成初始化;
创建对象时,先完成静态成员、静态块中的变量的初始化,然后初始化非静态成员,最后调用构造器。
静态成员、静态块中的变量尽在类被加载时被初始化一次,之后不再被初始化。
1 public class Initialization { 2 public static void main(String[] args) { 3 Cups cups = new Cups(); 4 Cups cups2 = new Cups(); 5 Cup cup_ = Cups.cup1; 6 Cup[] cup_arr = {new Cup(12),new Cup(11)}; 7 //! Cup cup8 = Cups.cup7; //error, can not be seen 8 } 9 } 10 11 class Cup { 12 public Cup(int i) { 13 System.out.println("cup is "+i); 14 } 15 void f(int i) { 16 System.out.println("f() is "+i); 17 } 18 } 19 20 class Cups { 21 static Cup cup1, cup2, cup3 = new Cup(3); 22 Cup cup4 = new Cup(4), cup5; 23 static { 24 cup1 = new Cup(1); 25 cup2 = new Cup(2); 26 cup3 = new Cup(33); 27 Cup cup7 = new Cup(7); //local variable 28 } 29 public Cups() { 30 System.out.println("Cups()"); 31 } 32 static Cup cup6 = new Cup(6); 33 } 34 //output: 35 /* 36 cup is 3 37 cup is 1 38 cup is 2 39 cup is 33 40 cup is 7 41 cup is 6 42 cup is 4 43 Cups() 44 cup is 4 45 Cups() 46 cup is 12 47 cup is 11 48 */
静态块中,
a、其所操作的field须是静态的
b、静态的field初始化顺序与其定义顺序一致,包括静态块中的初始化语句
c、静态块中定义的变量时局部变量,但同样是静态的。如上面的cup7。
6、数组初始化
数组属于对象,数组变量是其引用。
int [] array = new int[8]; //array是一个数组引用,它指向一个int型数组对象
三种定义数组的方式:
1 int [] int_arr_1 = {1,2,3}; 2 Object [] obj_arr_2 = {new Object(), new Object()}; 3 //这种方式下,数组的初始化必须位于数组的定义处
int [] int_arr_2 = new int[3]; for(int i = 0; i<3; i++) { int_arr_2[i] = i; } //这种定义方式在定义时为数组对象本省分配了空间,数组元素执行默认初始化,即如果是基本类型的数组,初始化为基本类型的默认初始值;如果是引用类型的数组,则初始化为空,此时在使用数组元素之前需要对数组元素进行初始化,使其指向一个实际的对象
1 int [] int_arr_1 = new int[]{1,2,3}; 2 Object [] obj_arr_2; 3 obj_arr_2 = new Object[]{new Object(), new Object()}; 4 //这种初始化的位置很随意,多用与方法调用,如: 5 6 //方法定义 7 void test(String[] s) {//...} 8 //方法调用 9 test(new String[]{"now", "you", "see"});
下面这种方式是正确的
1 Integer arr = {new Integer(1), new Integer(2), 3}; 2 //SE 5出现的auto-boxing功能
7、可变参数
1 public class Initialization { 2 static void printArray(Object... args) { 3 for (Object object : args) { 4 System.out.println(object + "------" + object.getClass()); 5 } 6 } 7 8 public static void main(String[] args) { 9 printArray(1, 2, 3);//自动包装 10 printArray(new Integer(11),new Integer(12)); 11 printArray(new Integer(11),new Float(3.14)); 12 printArray(47,3.14f,11.11); 13 printArray(new Integer[] {41,42,43,44});//提示:The argument of type Integer[] should explicitly be cast to Object[] 14 //for the invocation of the varargs method printArray(Object...) from 15 //type Initialization. It could alternatively be cast to Object for 16 //a varargs invocation 17 printArray((Object[])new Integer[] {413,423,433,443}); 18 printArray(); //可以为空 19 } 20 } 21 //output 22 /* 23 1------class java.lang.Integer 24 2------class java.lang.Integer 25 3------class java.lang.Integer 26 11------class java.lang.Integer 27 12------class java.lang.Integer 28 11------class java.lang.Integer 29 3.14------class java.lang.Float 30 47------class java.lang.Integer 31 3.14------class java.lang.Float 32 11.11------class java.lang.Double 33 41------class java.lang.Integer 34 42------class java.lang.Integer 35 43------class java.lang.Integer 36 44------class java.lang.Integer 37 413------class java.lang.Integer 38 423------class java.lang.Integer 39 433------class java.lang.Integer 40 443------class java.lang.Integer 41 */
涉及重载时,考虑基本数据类型的提升问题
1 static void F(Character... args) { 2 System.out.println("Character... args"); 3 } 4 5 static void F(float f, Character... args) { 6 System.out.println("Character... args"); 7 } 8 9 public static void main(String[] args) { 10 F(1,'2'); 11 //!F('2','2');//'2'能够被提升为float,造成歧义,编译器不知道应该调用哪个函数 12 }
1 static void F(Character c, Character... args) { 2 System.out.println("Character... args"); 3 } 4 5 static void F(float f, Character... args) { 6 System.out.println("Character... args"); 7 } 8 9 public static void main(String[] args) { 10 F(1,'2'); 11 //F('2','2');//依旧造成歧义!!!!!!!!!!!!!!!! 12 }
如果改成这样就是可以的
1 static void F(char c, Character... args) { 2 System.out.println("Character... args"); 3 } 4 5 static void F(float f, Character... args) { 6 System.out.println("Character... args"); 7 } 8 9 public static void main(String[] args) { 10 F(1,'2'); 11 F('2','2');//完全匹配,调用第一个F 12 }