在日常生活中,相同的词可以表达多种不同的含义——它们被“重载了”。例如:“冬天能穿多少穿多少。夏天能穿多少穿多少。”、“中国足球谁也打不过。中国乒乓球谁也打不过。”。相同的名字但表达出了不同的含义,这就是方法重载。
构造器重载
我们知道类的构造器名字必须与类名完全相同,不接受任何参数的构造器叫做默认构造器,同其它方法一样,构造器也能带有形式参数,以便指定如何创建对象。这里就体现了方法重载,它们都有相同的名字,即类名,但形式参数不同。
区分方法重载
如果有几个名字相同的方法,Java如何进行区分呢?
请记住方法重载的概念:方法名相同,但形参列表不同。
即使形参顺序不同也足以区分两个方法。但是最好别这样做,细想一下,如果只是参数顺序不同,有什么实际的意义呢?这样的重载也会让代码难以琢磨。
基本数据类型的重载
传入的实际参数范围小于重载方法声明的形式参数范围
我们知道Java中有自动类型转换(隐式转换)的概念,基本数据类型能从一个范围“较小”的类型自动转换至一个范围“较大”的类型,此过程一旦涉及到方法重载,可能会造成一些混淆。
我们来进行一些测试。
foo1方法包含char/byte/short/int/long/float/double这7种基本数据类型的重载;
foo2方法包含byte/short/int/long/float/double这6种基本数据类型的重载;
foo3方法包含short/int/long/float/double这5种基本数据类型的重载;
foo4方法包含int/long/float/double这4种基本数据类型的重载;
foo5方法包含long/float/double这3种基本数据类型的重载;
foo6方法包含float/double这2种基本数据类型的重载;
foo7方法只有一个double类型参数的方法;
分别使用常量值6、char类型的’x’、byte类型的1、short类型的1、int类型的1、long类型的1、float类型的1和double类型的1分别调用foo系列的方法,观察输出结果。完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
| package com.sunchaser.javase.base.overload;
import org.junit.Test;
public class SmallToBigDataTypeOverloadTest {
@Test public void testConstValue() { System.out.println("const: 6"); foo1(6); foo2(6); foo3(6); foo4(6); foo5(6); foo6(6); foo7(6); }
@Test public void testChar() { char c = 'x'; System.out.println("char: " + c); foo1(c); foo2(c); foo3(c); foo4(c); foo5(c); foo6(c); foo7(c); }
@Test public void testByte() { byte b = 1; System.out.println("byte: " + b); foo1(b); foo2(b); foo3(b); foo4(b); foo5(b); foo6(b); foo7(b); }
@Test public void testShort() { short s = 1; System.out.println("short: " + s); foo1(s); foo2(s); foo3(s); foo4(s); foo5(s); foo6(s); foo7(s); }
@Test public void testInt() { int i = 1; System.out.println("int: " + i); foo1(i); foo2(i); foo3(i); foo4(i); foo5(i); foo6(i); foo7(i); }
@Test public void testLong() { long l = 1L; System.out.println("long: " + l); foo1(l); foo2(l); foo3(l); foo4(l); foo5(l); foo6(l); foo7(l); }
@Test public void testFloat() { float f = 1F; System.out.println("float: " + f); foo1(f); foo2(f); foo3(f); foo4(f); foo5(f); foo6(f); foo7(f); }
@Test public void testDouble() { double d = 1D; System.out.println("double: " + d); foo1(d); foo2(d); foo3(d); foo4(d); foo5(d); foo6(d); foo7(d); }
void foo1(char x) { System.out.println("foo1(char x)"); } void foo1(byte x) { System.out.println("foo1(byte x)"); } void foo1(short x) { System.out.println("foo1(short x)"); } void foo1(int x) { System.out.println("foo1(int x)"); } void foo1(long x) { System.out.println("foo1(long x)"); } void foo1(float x) { System.out.println("foo1(float x)"); } void foo1(double x) { System.out.println("foo1(double x)"); }
void foo2(byte x) { System.out.println("foo2(byte x)"); } void foo2(short x) { System.out.println("foo2(short x)"); } void foo2(int x) { System.out.println("foo2(int x)"); } void foo2(long x) { System.out.println("foo2(long x)"); } void foo2(float x) { System.out.println("foo2(float x)"); } void foo2(double x) { System.out.println("foo2(double x)"); }
void foo3(short x) { System.out.println("foo3(short x)"); } void foo3(int x) { System.out.println("foo3(int x)"); } void foo3(long x) { System.out.println("foo3(long x)"); } void foo3(float x) { System.out.println("foo3(float x)"); } void foo3(double x) { System.out.println("foo3(double x)"); }
void foo4(int x) { System.out.println("foo4(int x)"); } void foo4(long x) { System.out.println("foo4(long x)"); } void foo4(float x) { System.out.println("foo4(float x)"); } void foo4(double x) { System.out.println("foo4(double x)"); }
void foo5(long x) { System.out.println("foo5(long x)"); } void foo5(float x) { System.out.println("foo5(float x)"); } void foo5(double x) { System.out.println("foo5(double x)"); }
void foo6(float x) { System.out.println("foo6(float x)"); } void foo6(double x) { System.out.println("foo6(double x)"); }
void foo7(double x) { System.out.println("foo7(double x)"); } }
|
我们会发现常量值6被foo1/foo2/foo3/foo4方法当做int值进行处理,foo5/foo6/foo7没有int类型的重载方法,被自动类型转换成了“较大”的类型(long/float/double)。
对于char类型的’x’,foo1方法包含对应类型的重载,剩余6种方法不包含char类型的重载,但foo2/foo3/foo4直接将char类型自动类型转换成了int类型。foo5/foo6/foo7分别将char类型隐式转换成了long/float/double类型。
char类型是不会被自动类型转换成byte/short类型的。其原因可查看这篇文章:传送门
对于byte类型的1,foo1/foo2方法包含对应类型的重载;剩余5种方法不包含char类型的重载,被分别隐式转换成了short/int/long/float/double类型。
对于short类型的1,foo1/foo2/foo3方法包含对应类型的重载;剩余4种方法不包含short类型的重载,被分别隐式转换成了int/long/float/double类型。
对于int类型的1,foo1/foo2/foo3/foo4方法包含对应类型的重载;foo5/foo6/foo7不包含int类型的重载,被分别隐式转换成了long/float/double类型。
对于long类型的1,foo1/foo2/foo3/foo4/foo5方法包含对应类型的重载;foo6/foo7不包含long类型的重载,被分别隐式转换成了float/double类型。
对应float类型的1,foo7不包含float类型的重载,被隐式转换成了double类型;而其它6种方法包含对应类型的重载。
对于double类型的1,7种方法都有对应类型的重载。
数值基本类型隐式转换图如下:
实线箭头无精度丢失,虚线箭头可能丢失精度。
传入的实际参数范围大于重载方法声明的形式参数范围
反过来,如果传入的实际参数范围大于重载方法声明的形式参数,会是什么情况呢?我们再来测试一下。
foo1方法包含char/byte/short/int/long/float/double这7种基本数据类型的重载;
foo2方法包含char/byte/short/int/long/float这6种基本数据类型的重载;
foo3方法包含char/byte/short/int/long这5种基本数据类型的重载;
foo4方法包含char/byte/short/int这4种基本数据类型的重载;
foo5方法包含char/byte/short这3种基本数据类型的重载;
foo6方法包含char/byte这2种基本数据类型的重载;
foo7方法只有一个char类型参数的方法;
使用double类型的3.1415926分别调用foo系列的方法。我们可以看到编译器提示了错误:Cannot resolve method 'foo2(double)'
等。除了foo1方法外,其余foo系列的方法都不包含double类型的重载,编译器也不会隐式将double类型自动转换成其它基本类型,其原因在于double类型的取值范围(二进制位数)是最大的,如果将double类型的数据隐式转换成其它基本类型,可能会出现精度丢失的问题,这是有风险的,编译器不会自动帮我们做。
我们需要强制类型转换(显式转换)进行窄化处理,这样做可能会丢失精度。但如果不这样做,我们无法正常进行方法的调用。
完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
| package com.sunchaser.javase.base.overload;
import org.junit.Test;
public class BigToSmallDataTypeOverloadTest {
@Test public void testDouble() { double d = 3.1415926; System.out.println("double: " + d); foo1(d); foo2((float) d); foo3((long) d); foo4((int) d); foo5((short) d); foo6((byte) d); foo7((char) d); float f = (float) d; System.out.println("(float) d:" + f); long l = (long) d; System.out.println("(long) d:" + l); int i = (int) d; System.out.println("(int) d:" + i); short s = (short) d; System.out.println("(short) d:" + s); byte b = (byte) d; System.out.println("(byte) d:" + b); char c = (char) d; System.out.println("(char) d:" + (c + 1 - 1)); }
void foo1(char x) { System.out.println("foo1(char x)"); } void foo1(byte x) { System.out.println("foo1(byte x)"); } void foo1(short x) { System.out.println("foo1(short x)"); } void foo1(int x) { System.out.println("foo1(int x)"); } void foo1(long x) { System.out.println("foo1(long x)"); } void foo1(float x) { System.out.println("foo1(float x)"); } void foo1(double x) { System.out.println("foo1(double x)"); }
void foo2(char x) { System.out.println("foo2(char x)"); } void foo2(byte x) { System.out.println("foo2(byte x)"); } void foo2(short x) { System.out.println("foo2(short x)"); } void foo2(int x) { System.out.println("foo2(int x)"); } void foo2(long x) { System.out.println("foo2(long x)"); } void foo2(float x) { System.out.println("foo2(float x)"); }
void foo3(char x) { System.out.println("foo3(char x)"); } void foo3(byte x) { System.out.println("foo3(byte x)"); } void foo3(short x) { System.out.println("foo3(short x)"); } void foo3(int x) { System.out.println("foo3(int x)"); } void foo3(long x) { System.out.println("foo3(long x)"); }
void foo4(char x) { System.out.println("foo4(char x)"); } void foo4(byte x) { System.out.println("foo4(byte x)"); } void foo4(short x) { System.out.println("foo4(short x)"); } void foo4(int x) { System.out.println("foo4(int x)"); }
void foo5(char x) { System.out.println("foo5(char x)"); } void foo5(byte x) { System.out.println("foo5(byte x)"); } void foo5(short x) { System.out.println("foo5(short x)"); }
void foo6(char x) { System.out.println("foo6(char x)"); } void foo6(byte x) { System.out.println("foo6(byte x)"); }
void foo7(char x) { System.out.println("foo7(char x)"); } }
|
从输出结果中可看到:double类型的3.1415926强制转换成float时变成了3.1415925,精度已经丢失;而强制转换成long/int/short/byte/char时浮点部分直接被舍弃。
返回值能区分重载方法吗?
例如有下面两个方法:
1 2 3
| void foo() {}
int foo() {return -1;}
|
我们人用肉眼去看方法的声明很容易分辨这两个同名方法的不同之处。但是当我们去调用foo方法时,如果我们的调用方式是:int x = foo()
,那么的确可以明确我们调用的是int foo() {return -1;}
;但有时候我们并不在意返回值(只是调用方法去执行某个操作),调用方式为:foo()
,这时候Java如何才能判断调用的是哪一个foo()
呢?程序员又该如何理解这种代码呢?因此,返回值是不能区分方法重载的。
int和Integer的形参是重载方法吗?
例如有下面两个方法:
1 2 3 4 5 6 7
| void foo(int x) { System.out.println("int"); }
void foo(Integer y) { System.out.println("Integer"); }
|
它们是重载方法吗?
即使int和Integer之间存在自动装箱/拆箱操作,但它们仍是重载的方法。
我们传递int类型的变量给foo,编译器并不会进行装箱操作,因为有对应的int类型重载方法;相应地,我们传递Integer类型的变量给foo,编译器不会进行拆箱操作,而是调用对应Integer类型的重载方法。
我们传递字面量1给foo方法,编译器会默认当成基本类型int调用foo。
测试代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
@Test public void testIntInteger() { foo(1); int x = 1; foo(x); Integer y = 1; foo(y); }
|
以上就是方法重载Overload的全部内容,完整示例代码地址见:传送门