使用构造器确保初始化
许多程序的错误都源于程序员忘记初始化变量。因此可以为每个类都顶一个initialize()方法。该方法会提示你在使用对象之前,应首先调用intitialize()。但构造器的命名存在这两个问题首先,无论构造方法起任何名称都有可能会与类中某个成员相冲突。其次,调用构造器是编译器的职责,编译器必须明确知道构造方法的名称。因此Java中的构造器命名采用与类相同的名称。
class Rock{
Rock(){
System.out.print(“Rock ”);
}
}
public class SimpleConstructor{
public static void main(String [] args){
for(int i=0;i<10;i++){
new Rock();
}
}
}
//输出
Rock Rock Rock Rock Rock Rock Rock Rock Rock Rock
new Rock();
将会为对象分配存储空间,并调用相应的构造器。这就确保了你再能操作对象之前,他已经被恰当的初始化了。
由于构造方法需要与类名同名,所以每个方法首字母小写的编码风格不适合构造方法使用。
构造器同样可以接收参数,无参的构造器叫做默认构造器。
class Rock2{
Rock2(int i){
System.out.print(“Rock “ + i +” ”);
}
}
public class SimpleConstructor2{
public static void main(String [] args){
for(int i =0;i<0;i++){
new Rock(i);
}
}
}
输出
Rock 0 Rock 1 Rock 2 Rock 3 Rock 4 Rock 5 Rock 6 Rock 7 Rock 8 Rock 9
有了构造器参数形式,我们就可以在初始化对象时对其提供实际参数。例如类Tree有一个构造器,他接受一个整型变量来表示树高。
Tree t=new Tree(12); //12米高的树
如果Tree(int),是Tree类的唯一构造器,那么编译器不会允许你以其他的方式对Tree对象进行初始化。
概念上来讲“初始化”和“创建”是彼此独立的,然而上面代码中你却找不到initialize()方法的明确调用。在Java中,”初始化”和”创建”捆绑在一起,两者不能分开。
构造方法是一种特殊的类型的方法,本身没有返回值,这与void不同,构造器绝对不允许返回任何东西(new明确返回了对新对象的引用,但构造器本身不会返回任何值),如果构造器可以返回任何值,那么编译器必须要知道该如何处理此返回值。
方法重载
日常生活中,人类的语言本身就存在着很强的冗余性,一个相同的词在不同的语境下被赋予不同的含义-----它们被“重载”了。你可以说“洗车”,“洗衣服”,“洗澡”。这三个“洗”字明显包含了不同的处理过程,然而你并不需要说“以洗车的方式洗车”,“以洗衣服的方式洗澡”,“以洗澡的方式洗澡”。这是因为听的人并不需要对明确的动作进行区分。
如果想用多种方式创建一个对象该怎么办呢,Java中为了让方法名相同而形式参数不同的构造器同时蹲在,必须用到方法重载。同时,尽管方法重载是构造器所必须的,但他也可以用于其它方法,且用法同样方便。
class Tree{
int height;
Tree(){
System.out.print(“种下一粒种子”);
height=0;
}
Tree(int initialHeight){
height=initialHeight;
System.out.println(“种下了一颗”+ height +”米高的树”);
}
void info(){
System.out.println(“这课树”+ height +”米高”);
}
void info(String s){
System.our.println(s+“:这棵树”+height+”米高”);
}
}
public class Overloading{
for(int i = 0;i < 5;i++){
Tree t=new Tree(i);
t.info();
t.info(“重载方法”);
}
new Tree();
}
输出
种下了一颗0米高的树
这课树0米高
重载方法:这棵树1米高
种下了一颗0米高的树
这课树1米高
重载方法:这棵树1米高
种下了一颗2米高的树
这课树2米高
重载方法:这棵树2米高
种下了一颗3米高的树
这课树3米高
重载方法:这棵树3米高
种下了一颗4米高的树
这课树4米高
重载方法:这棵树4米高
种下一粒种子
创建Tree的时候可以带参数也可以不带参数,前者表示种下一颗有高度的树,后者则表示种下种子。并且你还可以以多种方式调用info()以查看树的信息。
区分方法重载
那么Java是如何区分你所调用的是哪个重载方法呢?其实规则很简单:每个重载的方法都必须有一个独一无二的参数类型列表。
毕竟,一个名字相同的方法,除了参数类型差异以外,还能有什么办法将他们区分开呢?
甚至参数顺序不同也足以区分两个方法。不过,一般不要这样做,因为会是代码难以维护。
public class OverloadingOrder{
static void f(String s,int i){
System.out.print(“String:”+s+”,int:”+i);
}
static void f(int i,String s){
System.out.print(int :”+i+”,String:”+s);
}
public static void main(String [] args){
f(“String first”,11);
f(99,”Int first”);
}
}
输出
String: String first,int:11
int :99,String:int first
涉及基本类型的重载
基本类型能从一个较小的类型自动提升至一个较大的类型,此过程一但涉及重载,可能会有些混淆。
public class Test2 {
void f1(char x){
System.out.print("f1(char)");
}
void f1(byte x){
System.out.print("f1(byte");
}
void f1(short x){
System.out.print("f1(short)");
}
void f1(int x){
System.out.print("f1(int)");
}
void f1(long x){
System.out.print("f1(long)");
}
void f1(float x){
System.out.print("f1(float)");
}
void f1(double x){
System.out.print("f1(double)");
}
void f2(byte x){
System.out.print("f2(byte)");
}
void f2(short x){
System.out.print("f2(short)");
}
void f2(int x){
System.out.print("f2(int)");
}
void f2(long x){
System.out.print("f2(long)");
}
void f2(float x){
System.out.print("f2(float)");
}
void f2(double x){
System.out.print("f2(double)");
}
void f3(short x){
System.out.print("f3(short)");
}
void f3(int x){
System.out.print("f3(int)");
}
void f3(long x){
System.out.print("f3(long)");
}
void f3(float x){
System.out.print("f3(float)");
}
void f3(double x){
System.out.print("f3(double)");
}
void f4(int x){
System.out.print("f4(int)");
}
void f4(long x){
System.out.print("f4(long)");
}
void f4(float x){
System.out.print("f4(float)");
}
void f4(double x){
System.out.print("f4(double)");
}
void f5(long x){
System.out.print("f5(long)");
}
void f5(float x){
System.out.print("f5(float)");
}
void f5(double x){
System.out.print("f5(double)");
}
void f6(float x){
System.out.print("f6(float)");
}
void f6(double x){
System.out.print("f6(double)");
}
void f7(double x){
System.out.print("f7(double)");
}
void testConstVal(){
System.out.print("5: ");
f1(5);f2(5);f3(5);f4(5);f5(5);f6(5);f7(5);System.out.println();
}
void testChar(){
char x='x';
System.out.print("char: ");
f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);System.out.println();
}
void testByte(){
byte x=0;
System.out.print("byte: ");
f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);System.out.println();
}
void testInt(){
int x=0;
System.out.print("int: ");
f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);System.out.println();
}
void testLong(){
long x=0;
System.out.print("long: ");
f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);System.out.println();
}
void testFloat(){
float x=0;
System.out.print("float: ");
f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);System.out.println();
}
void testDouble(){
double x=0;
System.out.print("double: ");
f1(x);f2(x);f3(x);f4(x);f5(x);f6(x);f7(x);System.out.println();
}
public static void main(String[]args){
Test2 t2=new Test2();
t2.testConstVal();
t2.testChar();
t2.testByte();
t2.testInt();
t2.testLong();
t2.testFloat();
t2.testDouble();
}
}
输出
5: f1(int)f2(int)f3(int)f4(int)f5(long)f6(float)f7(double)
char: f1(char)f2(int)f3(int)f4(int)f5(long)f6(float)f7(double)
byte: f1(bytef2(byte)f3(short)f4(int)f5(long)f6(float)f7(double)
int: f1(int)f2(int)f3(int)f4(int)f5(long)f6(float)f7(double)
long: f1(long)f2(long)f3(long)f4(long)f5(long)f6(float)f7(double)
float: f1(float)f2(float)f3(float)f4(float)f5(float)f6(float)f7(double)
double: f1(double)f2(double)f3(double)f4(double)f5(double)f6(double)f7(double)
常数5被当做int值处理,若有某个重载方法接收int型参数,他就会被调用。至于其他情况如果传入参数小于方法中声明的形式参数类型,实际数据类型就会被提升。char略有不同,如果无法找到恰好接收char参数的方法,就会把char转化成int型。
如果传入参数大于方法声明的参数呢?
改写上面的例子
public class Test2 {
void f1(char x){
System.out.print("f1(char)");
}
void f1(byte x){
System.out.print("f1(byte");
}
void f1(short x){
System.out.print("f1(short)");
}
void f1(int x){
System.out.print("f1(int)");
}
void f1(long x){
System.out.print("f1(long)");
}
void f1(float x){
System.out.print("f1(float)");
}
void f1(double x){
System.out.print("f1(double)");
}
void f2(char x){
System.out.print("f2(char)");
}
void f2(byte x){
System.out.print("f2(byte)");
}
void f2(short x){
System.out.print("f2(short)");
}
void f2(int x){
System.out.print("f2(int)");
}
void f2(long x){
System.out.print("f2(long)");
}
void f2(float x){
System.out.print("f2(float)");
}
void f3(char x){
System.out.print("f3(char)");
}
void f3(byte x){
System.out.print("f3(byte)");
}
void f3(short x){
System.out.print("f3(short)");
}
void f3(int x){
System.out.print("f3(int)");
}
void f3(long x){
System.out.print("f3(long)");
}
void f4(char x){
System.out.print("f4(char)");
}
void f4(byte x){
System.out.print("f4(byte)");
}
void f4(short x){
System.out.print("f4(short)");
}
void f4(int x){
System.out.print("f4(int)");
}
void f5(char x){
System.out.print("f5(char)");
}
void f5(byte x){
System.out.print("f5(byte)");
}
void f5(short x){
System.out.print("f5(short)");
}
void f6(char x){
System.out.print("f6(char)");
}
void f6(byte x){
System.out.print("f6(byte)");
}
void f7(char x){
System.out.print("f7(char)");
}
void testDouble(){
double x=0;
System.out.print("double: ");
f1(x);f2((float)x);f3((long)x);f4((int)x);f5((short)x);f6((byte)x);f7((char)x);System.out.println();
}
public static void main(String[]args){
Test2 t2=new Test2();
t2.testDouble();
}
}
输出
double: f1(double)f2(float)f3(long)f4(int)f5(short)f6(byte)f7(char)
在这里方法接收较小的基本类型参数。如果参入的实际参数太大,就得通过类型转化来进行窄化转换。如果不这样做,编译器就会报错。
以返回值区分重载方法
那么在重载方法的时候时候能通过返回值的不同来区分呢?
void f(){}
int f(){return 1;}
这两个方法当我们这样调用时
int x=f();
编译器很明显知道我们想调用的是第二个方法,但是如果我们只想输出时并不关心返回值,而是想进行其他操作时:
f();
这样调用方法此时Java编译器无法判断该调用哪个一个f(),别人也无法理解。因材,根据返回值来区分方法重载是行不通的。
默认构造器
默认狗操起又称无参构造器是没有形式参数的---它的作用是创建一个默认对象。如果你写的类中没有构造器,则编译器会自动帮你创建一个默认构造器。
class Bird(){
public class DefaultConstructor{
public static void main(String[] args){
Bird b=new Bird();
}
}
}
这里new Bird(),创建了一个Brid对象,并调用了其默认构造器。可见没有他的话就没有方法可以调用,就没法创建对象。但是,如果已经定义了一个构造器(无论是否有参),编译器就不会帮你自动创建默认构造器:
class Brid2{
Brid2(int i){}
Brid2(double d){}
}
public class NoSynthesis{
public static void main(String[] args){
//! Brid2 b=new Bird2();//No default
Brid2 b2=new Bird2(1);
Brid2 b3=new Brid2(1.0);
}
}
若你这样初始化 new Brid2();编译器就会报错