@liayun
2016-12-01T21:43:24.000000Z
字数 21295
阅读 1733
java基础
关键字的定义和特点
定义:被Java语言赋予了特殊含义的单词。
特点:关键字中所有字母都为小写。
用于定义数据类型的关键字 | ||||
---|---|---|---|---|
class | interface | byte | short | int |
long | float | double | char | boolean |
void | ||||
用于定义数据类型值的关键字 | ||||
true | false | null | ||
用于定义流程控制的关键字 | ||||
if | else | switch | case | default |
while | do | for | break | continue |
return | ||||
用于定义访问权限修饰符的关键字 | ||||
private | protected | public | ||
用于定义类,函数,变量修饰符的关键字 | ||||
abstract | final | static | synchronized | |
用于定义类与类之间关系的关键字 | ||||
extends | implements | |||
用于定义建立实例及引用实例,判断实例的关键字 | ||||
new | this | super | instanceof | |
用于异常处理的关键字 | ||||
try | catch | finally | throw | throws |
用于包的关键字 | ||||
package | ||||
其他修饰符关键字 | ||||
native | strictfp | transient | volatile | assert |
注意:在起名字的时候,为了提高阅读性,要尽量有意义。
Java中的名称规范:
xxxyyyzzz
。XxxYyyZzz
。xxxYyyZzz
。XXX_YYY_ZZZ
。用于注解说明解释程序的文字就是注释。注释提高了代码的阅读性。
java中的注释格式:
//注释文字
/* 注释文字 */
/** 注释文字 */
对于单行和多行注释,被注释的文字,不会被JVM(java虚拟机)解释执行。
对于文档注释,是java特有的注释,其中注释内容可以被JDK提供的工具javadoc
所解析,生成一套以网页文件形式体现的该程序的说明文档。
注释是一个程序员必须要具有的良好编程习惯。
初学者编写程序可以养成习惯:先写注释再写代码。将自己的思想通过注释先整理出来,在用代码去体现,因为代码仅仅是思想的一种体现形式而已。
例,
/**
这个类是用于演示hello world.
作者:李阿昀
版本:V1.0
*/
class Demo {
/*
main函数可以保证该类的独立运行
它是程序的入口
它会被JVM所调用
*/
public static void main(String[] args) {
//这是输出语句用于将括号内的数据打印到控制台。
System.out.println("hello java");
System.out.println("hello world");
}
}
常量表示不能改变的数值。Java中常量的分类:
true
、false
' '
)标识null
对于整数,java有四种表现形式:
0-9
,满10进10-7
,满8进1,用0
开头表示0-9、A-F
,满16进1,用0x
或0X
开头表示0、1
,满2进1注意:
-
。变量的概念:
为什么要定义变量?
用来不断的存放同一类型的常量,并可以重复使用。(即当数据不确定时,需要对数据进行存储时,就定义一个变量来完成存储动作。)
定义变量的格式:数据类型 变量名 = 初始化值;
。注:格式是固定的,记住格式,以不变应万变。
理解:变量就如同数学中的未知数。
Java语言是强类型语言,对于每一种数据都定义了明确的具体数据类型,在内存中分配了不同大小的内存空间。如图:
Java基本数据类型
数据类型 | 说明 | 所占内存 | 举例 | 备注 |
---|---|---|---|---|
byte | 字节型 | 1 byte | 3, 127 | |
short | 短整型 | 2 bytes | 3, 32767 | |
int | 整型 | 4 bytes | 3, 21474836 | |
long | 长整型 | 8 bytes | 3L, 92233720368L | long最后要有一个L字母(大小写无所谓) |
float | 单精度浮点型 | 4 bytes | 1.2F, 223.56F | float最后要有一个F字母(大小写无所谓) |
double | 双精度浮点型 | 8 bytes | 1.2, 1.2D, 223.56, 223.56D | double最后最好要有一个D字母(大小写无所谓) |
char | 字符型 | 2 bytes | 'a', 'A' | 字符型数据只能是一个字符,由单引号包围 |
boolean | 布尔型 | 1 bit | true, false |
注意:整数默认为int
,小数默认为double
。
在Java中可以通过Integer.SIZE
这样的方法直接查看基本类型所占内存空间的大小。通过以下程序就能够查看了:
class JavaType {
public static void main(String[] args) {
System.out.println("Byte: " + Byte.SIZE);
System.out.println("Short: " + Short.SIZE);
System.out.println("Integer: " + Integer.SIZE);
System.out.println("Long: " + Long.SIZE);
System.out.println("Float: " + Float.SIZE);
System.out.println("Double: " + Double.SIZE);
System.out.println("Character: " + Character.SIZE);
}
}
输出结果为:(单位是bit
)
Byte: 8(一个字节)
Short: 16(二个字节)
Integer: 32(四个字节)
Long: 64(八个字节)
Float: 32(四个字节)
Double: 64(八个字节)
Character: 16(二个字节)
Boolean
类型有点奇怪,官方文档是这么说的:
This data type represents one bit of information, but its "size" isn't something that's precisely defined.
中文翻译:这种数据类型保存一位的信息,但是它的大小却不是精确定义的。
例,Java基本数据类型举例:
int x = 4;
byte b = 2; //2是一个int常量,但是会自动判断2是不是在byte类型的范围内(-128~127),=运算符在给b赋值时,自动完成了强转操作
byte b1 = 128; //编译未通过,超出byte类型的范围
short s = 30000;
long l = 4l;
float f = 2.3f;
double d = 34.56;
char ch = '4';
char ch1 = 'a';
char ch2 = '+';
char ch3 = ' ';
char ch4 = '昀'; //一个中文在内存中占2个字节,而char就是2个字节空间大小
boolean bo = true;
boolean bo1 = false;
自动类型转换(也叫隐式类型转换)和强制类型转换(也叫显式类型转换)。
表达式的数据类型自动提升:
试分析System.out.println('a')
与System.out.println('a'+1)
的区别。
System.out.println('a'); //a
System.out.println('a'+1); //98
System.out.println((char)('a'+1)); //b
System.out.println('A'+0); //65
System.out.println('1'+0); //49
System.out.println((char)5);
会输出♣
的形状。
自动类型提升:
byte b = 3;
int x = 4;
x = x + b;//b会自动提升为int类型进行运算。
强制类型转换:
byte b = 3; // 3是一个int常量,但是会自动判断3是不是在byte类型的范围内(-128~127),=运算符在给b赋值时,自动完成了强转操作
b = b + 4;//报错
b = (byte)(b+4);//强制类型转换,强制将b+4的结果转换为byte类型,再赋值给b。
算术运算符的注意问题
5%-2=1
。但被模数是负数就另当别论,如:-5%2=-1
。面试的时候可能会考到。对于除号“/”,它的整数除和小数除是有区别的:整数之间做除法时,只保留整数部分而舍弃小数部分。如:
int x = 4270;
x = x / 1000 * 1000;
System.out.println(x); // 4000
字符串数据和任何数据使用+
都是相连接,最终都会变成字符串。如:
System.out.println("5+5="+5+5); //"5+5=55"
System.out.println("5+5="+(5+5)); // "5+5=10"
转义字符:通过\
来转变后面字母或者符号的含义。如:
\n
:换行
\b
:退格。相当于backspace键
\r
:按下回车键。windos系统中,回车符是由两个字符来表示\r\n
\t
:制表符。相当于tab键
例,
System.out.println("hello\tworld");
System.out.println("\"hello java\"");
System.out.println("\\hello java\\");
char ch = '\'';
例,
int a, b, c;
a = b = c = 5;
一个面试题:
思考如下代码中s = s + 2;
与s += 2;
的区别?
short s = 3;
s = s + 2;
s += 2;
解:
short s = 3;
s = s + 2; // 编译失败,因为s会被提升为int类型,运算后的结果还是int类型,无法赋值给short类型
s += 2; // 编译通过,因为+=运算符在给s赋值时,自动完成了强转操作
注意:
==
不能误写成=
逻辑运算符用于连接boolean类型的表达式。
&
——AND
(与)
boolean类型的表达式 | 逻辑运算符 | boolean类型的表达式 | 结果 |
---|---|---|---|
true | & | true | true |
true | & | false | false |
false | & | true | false |
false | & | false | false |
特点:只要两边的布尔表达式结果有一个为false,那么结果就为false。只有两边都为true,结果为true。
|
——OR
(或)
boolean类型的表达式 | 逻辑运算符 | boolean类型的表达式 | 结果 |
---|---|---|---|
true | | | true | true |
true | | | false | true |
false | | | true | true |
false | | | false | false |
特点:两边只要有一个为true,结果为true,两边都为false,结果为false。
^
——XOR
(异或),就是和|
有点不一样,当true ^ true = false
boolean类型的表达式 | 逻辑运算符 | boolean类型的表达式 | 结果 |
---|---|---|---|
true | ^ | true | false |
true | ^ | false | true |
false | | | true | true |
false | ^ | false | false |
特点:两边相同,结果是false;两边不同,结果为true。
!
——Not
(非),即!true = false
,!fasle = true
。
注意:&和&&的区别以及|和||的区别
&
:无论左边是true是false,右边都运算。
&&
:当左边为false时,右边不运算。
|
:两边都参与运算。
||
:当左边为true时,右边不运算。
位运算符 | 运算 | 范例 |
---|---|---|
<< | 左移 | 3 << 2 = 12 → 3 * 2 * 2 = 12 |
>> | 右移 | 3 >> 1 = 1 → 3 / 2 = 1 |
>>> | 无符号右移 | 3 >>> 1 = 1 → 3 / 2 = 1 |
& | 与运算 | 6 & 3 = 2 |
| | 或运算 | 6 |
^ | 异或运算 | 6 ^ 3 = 5 |
~ | 反码 | ~6 = -7 |
位运算是直接对二进制进行运算。
注意:
<<
:其实就是乘以2的移动的位数次幂。>>
:其实就是除以2的移动的位数次幂。位运算符的细节
位运算符 | 细节 |
---|---|
<< | 空位补0,被移除的高位丢弃,空缺位补0 |
>> | 被移位的二进制最高位是0,右移后,空缺位补0;最高位是1,空缺位补1 |
>>> | 被移位二进制最高位无论是0或者是1,空缺位都用0补 |
& | 二进制位进行&运算,只有1&1时结果是1,否则是0 |
| | 二进制位进行|运算,只有0|0时结果是0,否则是1 |
^ | 任何相同二进制位进行^运算,结果是0:1^1=0,0^0=0;不相同二进制位^运算结果是1:1^0=1,0^1=1 |
例,
7 ^ 4
111
^ 100 (加密)
------
011
^ 100 (解密),密钥:100
------
111 = 7; ----> 7 ^ 4 ^ 4 = 7;
结论:一个数异或同一个数两次,结果还是那个数。
练习:对两个整数变量的值进行互换(不需要第三方变量)。
解:
第一种方式:(使用第三方变量,实际开发时使用)
int n = 3, m = 8;
System.out.println("n = " + n + ", m = " + m);
int temp;
temp = n;
n = m;
m = temp;
System.out.println("n = " + n + ", m = " + m);
第二种方式:(不需要第三方变量)
int n = 3, m = 8;
System.out.println("n = " + n + ", m = " + m);
n = n + m; // 如果n和m的值非常大,容易超出int范围
m = n - m;
n = n - m;
System.out.println("n = " + n + ", m = " + m);
第三种方式:(不需要第三方变量,炫技型)
int n = 3, m = 8;
System.out.println("n = " + n + ", m = " + m);
n = n ^ m;
m = n ^ m; // (n ^ m) ^ m = n;
n = n ^ m; // n ^ (n ^ m) = m;
System.out.println("n = " + n + ", m = " + m);
格式:(条件表达式) ? 表达式1 : 表达式2
例,
int x = 2, y;
y = (x > 1) ? 'a' : 200;
System.out.println("y = " + y); // 97,自动类型提升
if else
结构简写格式:变量 = (条件表达式) ? 表达式1 : 表达式2;
三元运算符的特点:
if else
代码if语句练习:
练习一:根据用户定义的数值不同,打印对应的星期英文。
解:
//需求1:根据用户定义的数值不同,打印对应的星期英文
class IfTest {
public static void main(String[] args) {
int num = 2;
if(num == 1) {
System.out.println("monday");
} else if(num == 2) {
System.out.println("tsd");
} else if {
...
} else {
System.out.println("nono");
}
}
}
练习二:根据用户指定的月份,打印该月份所在的季节。
解:
第一种方式:
//需求2:根据用户指定的月份,打印该月份所在的季节
// 3,4,5 春季 6,7,8 夏季 9,10,11 秋季 12,1,2 冬季
class IfTest {
public static void main(String[] args) {
int x = 4;
if(x == 3 || x == 4 || x == 5) {
System.out.println(x + "春季");
} else if(x == 6 || x == 7 || x == 8) {
System.out.println(x + "夏季");
} else if(x == 9 || x == 10 || x == 11) {
System.out.println(x + "秋季");
} else if(x == 12 || x == 1 || x == 2) {
System.out.println(x + "冬季");
} else {
System.out.println(x + "月份不存在");
}
}
}
第二种方式:
class IfTest {
public static void main(String[] args) {
int x = 4;
if(x > 12 || x < 1) {
System.out.println(x + "月份不存在");
} else if(x >= 3 && x <= 5) {
System.out.println(x + "春季");
} else if(x >= 6 && x <= 8) {
System.out.println(x + "夏季");
} else if(x >= 9 && x <= 11) {
System.out.println(x + "秋季");
} else {
System.out.println(x + "冬季");
}
}
}
switch语句特点:
byte
,short
,int
,char
。但JDK7.0
对switch语句进行了增强,可以判断字符串,JDK5.0
对switch语句也进行了增强,可以对枚举类型进行判断。例,
int x = 3;
switch(x) { // byte,short,int,char
default:
System.out.println("d");
case 4:
System.out.println("a");
case 6:
System.out.println("b");
break;
case 2:
System.out.println("c");
break;
}
以上代码输出:
d
a
b
switch语句练习:
练习:根据用户指定的月份,打印该月份所在的季节。
class SwitchTest {
public static void main(String[] args) {
int x = 4;
switch(x) {
case 3:
case 4:
case 5:
System.out.println(x + "春季");
break;
case 6:
case 7:
case 8:
System.out.println(x + "夏季");
break;
case 9:
case 10:
case 11:
System.out.println(x + "秋季");
break;
case 12:
case 1:
case 2:
System.out.println(x + "冬季");
break;
default:
System.out.println("nono");
}
}
}
if
和switch
语句很像,具体什么场景下,应用哪个语句呢?
答:如果判断的具体数值不多,而且符合byte
short
int
char
这四种类型,虽然两个语句都可以使用,但建议使用switch
语句,因为效率稍高。
其他情况:对区间判断,对结果为boolean类型判断,使用if,if的使用范围更广。
while
:先判断条件,只有条件满足才执行循环体。
do while
:先执行循环体,再判断条件,条件满足,再继续执行循环体。
简单一句话:do while
无论条件是否满足,循环体至少执行一次。
for
循环格式:
for(初始化表达式;循环条件表达式;循环后的操作表达式)
{
执行语句;
}
例1,
class ForDemo {
public static void main(String[] args) {
for (int x = 0; x < 3; x++) {
System.out.println("x = " + x);
}
//System.out.println("x ======= " + x);
int y = 0;
while (y < 3) {
System.out.println("y = " + y);
y++;
}
System.out.println("y ======= " + y);
}
}
从以上代码中可得:
总结:
什么时候使用循环结构?
答:当要对某些语句执行很多次时,就使用循环结构。
例2,for
循环还可以写成:
int x = 1;
for (System.out.println("a"); x < 3; System.out.println("c"), x++) {
System.out.println("d");
}
输出结果为:
a
d
c
d
c
例3,
for (int y = 0; y < 3; y++) {
}
以上代码可以写成如下代码:
int y = 0;
for ( ; y < 3; ) {
y++;
}
例4,最简单无限循环格式:
for(;;) {
}
while(true) {
}
无限循环存在的原因是并不知道循环多少次,而是根据某些条件,来控制循环。
练习一:获取1~10的和,并打印。
解:
class ForTest2 {
public static void main(String[] args) {
int sum = 0;
for (int x = 1; x <= 10; x++) {
sum += x;
}
System.out.println("sum = " + sum);
}
}
其实这就是累加思想。原理:通过变量记录住每次变化的结果,并通过循环的形式,进行累加动作。
练习二:获取1~100之间7的倍数的个数,并打印。
解:
/*
获取1~100之间7的倍数的个数,并打印
思路:
1.先对1~100进行循环(遍历)
2.在遍历的过程中,定义条件,只对7的倍数进行操作
3.因为7的倍数不确定,只要符合条件,就通过一个变量来记录这个变化的次数。
步骤:
1.定义循环语句,选择for语句
2.在循环中定义判断,只要是7的倍数即可,使用if语句。条件:7的倍数 x % 7 == 0;
3.定义变量,该变量随着7的倍数的出现而自增
*/
class ForTest3 {
public static void main(String[] args) {
int count = 0;
for (int x = 1; x <= 100; x++) {
if(x % 7 == 0) {
count++;
}
}
System.out.println("count = " + count);
}
}
其实这就是计数器思想,原理:通过一个变量记录住数据的状态变化,也需要通过循环完成。
for
循环嵌套
例1,使用for
循环打印如下所示的图形:
****
****
****
解:对于打印长方形,外循环控制行数,内循环控制每一行的列数,也就是一行中元素的个数。代码如下:
class ForForDemo {
public static void main(String[] args) {
for (int x = 0; x < 3; x++) {
for (int y = 0; y < 4; y++) {
System.out.print("*");
}
System.out.println();
}
}
}
例2,使用for
循环打印如下所示的图形:
*****
****
***
**
*
解:发现图形有很多行,每一个行有很多列,所以得使用嵌套循环。原理:形象说法,大圈套小圈。代码如下:
class ForForDemo {
public static void main(String[] args) {
for (int x = 0; x < 5; x++) {
for (int y = x; y < 5; y++) {
System.out.print("*");
}
System.out.println();
}
}
}
例3,使用for
循环打印如下所示的图形:
*
**
***
****
*****
解:
class ForForDemo {
public static void main(String[] args) {
for (int x = 0; x < 5; x++) {
for (int y = 0; y <= x; y++) {
System.out.print("*");
}
System.out.println();
}
}
}
总结:不是规律的规律:
例4,使用for
循环打印如下所示的图形:
1
12
123
1234
12345
解:
class ForForDemo {
public static void main(String[] args) {
for (int x = 0; x < 5; x++) {
for (int y = 1; y <= x + 1; y++) {
System.out.print(y);
}
System.out.println();
}
}
}
例5,打印一个99乘法表。
解:
class ForForDemo {
public static void main(String[] args) {
for (int x = 1; x <= 9; x++) {
for (int y = 1; y <= x; y++) {
System.out.print(y + "*" + x + "=" + (y*x) + "\t");
}
System.out.println();
}
}
}
例6,打印如下所示的图形:
□□□□*
□□□*□*
□□*□*□*
□*□*□*□*
*□*□*□*□*
注意:□
表示空格。
解:
class ForForDemo {
public static void main(String[] args) {
for (int x = 0; x < 5; x++) {
for (int y = x + 1; y < 5; y++) {
System.out.print(" ");
}
for (int z = 0; z <= x; z++) {
System.out.print("* ");
}
System.out.println();
}
}
}
break(跳出),应用于选择结构和循环结构。
例,
w:for (int x = 0; x < 3; x++) {
for (int y = 0; y < 4; y++) {
System.out.println("x = " + x);
break w;
}
}
可以给循环标号,然后通过break
指定跳出哪个循环。
continue(继续),只能作用于循环结构,继续循环。特点:结束本次循环,继续下一次循环。
例1,
for (int x = 1; x <= 10; x++) {
if(x % 2 == 1)
continue;
System.out.println("x = " + x);
}
例2,也可以给循环标号,然后通过continue
指定继续哪个循环。
w:for (int x = 0; x < 3; x++) {
for (int y = 0; y < 4; y++) {
System.out.println("x = " + x);
continue w;
}
}
记住:
break
和continue
语句作用的范围break
和continue
单独存在时,下面不可以有任何语句,因为都执行不到函数的定义
函数就是定义在类中的具有特定功能的一段独立小程序。函数也称为方法。
函数的格式:
修饰符 返回值类型 函数名(参数类型 形式参数1, 参数类型 形式参数2, ... )
{
执行语句;
return 返回值;
}
试看以下代码:
int x = 4;
System.out.println(x*3+5);
x = 6;
System.out.println(x*3+5);
发现以上的运算,因为获取不同数据的运算结果,代码出现了重复。为了提高代码的复用性,对代码进行了抽取,将这个部分定义成一个独立的功能,方便于日后使用,java中对该功能的定义是通过函数的形式来体现的。
所以,我们有了一个需求:定义一个功能,完成一个整数*3+5的运算,并打印结果。
public static int getResult(int num) {
return num * 3 + 5;
}
当函数运算后没有具体的返回值,这时返回值类型用一个特殊的关键字来标识,该关键字就是void
。void
:代表的是函数没有具体返回值的情况。当函数的返回值类型是void
时,函数中的return
语句可以省略不写。即:
public static void getResult(int num) {
System.out.println(num * 3 + 5);
return; // 可以省略
}
函数的特点
void
表示,那么该函数中的return
语句如果在最后一行可以省略不写。注意:
函数的应用
如何定义一个函数呢?
例1,需求:定义一个功能:完成3+4的运算,并将结果返回给调用者。
解:
其实这两个功能就是在明确函数的定义:
public static int getSum() {
return 3 + 4;
}
以上这个函数的功能,结果是固定的,毫无扩展性而言,为了方便用户需求,由用户来指定加数和被加数,这样,功能才有意义。
例2,定义一个功能,可以实现两个整数的加法运算。
解:
public static int getSum(int x, int y) {
return x + y;
}
例3,需求:判断两个数是否相同?
解:
public static boolean compare(int a, int b) {
if(a == b)
return true;
else
return false;
}
第一次优化后:
public static boolean compare(int a, int b) {
if(a == b)
return true;
return false;
}
第二次优化后:
public static boolean compare(int a, int b) {
return a == b ? true : false;
}
最后一次优化:
public static boolean compare(int a, int b) {
return a == b;
}
例4,需求:定义功能,对两个数进行比较,获取较大的数。
解:
public static int getMax(int a, int b) {
return (a > b) ? a : b;
}
练习一:定义一个功能,用于打印矩形。
解:
public static void draw(int row, int col) {
for (int x = 0; x < row; x++) {
for (int y = 0; y < col; y++) {
System.out.print("*");
}
System.out.println();
}
}
练习二:定义一个打印99乘法表功能的函数。
解:
public static void print99() {
for (int x = 1; x <= 9; x++) {
for (int y = 1; y <= x; y++) {
System.out.print(y+"*"+x+"="+y*x+"\t");
}
System.out.println();
}
}
函数的重载(overload)
重载的概念:在同一个类中,允许存在一个以上的同名函数,只要它们的参数个数或者参数类型不同即可。
重载的特点:与返回值类型无关,只看参数列表。
重载的好处:方便于阅读,优化了程序设计。
例,定义一个加法运算,获取两个整数的和。
public static int add(int x, int y) {
return x + y;
}
定义一个加法运算,获取三个整数的和:
public static int add(int x, int y, int z) {
return x + y + z;
}
这两段代码就是函数的重载,当然函数中可调用函数,所以以上代码可以写成:
public static int add(int x, int y, int z) {
return add(x, y) + z;
}
例,定义一个打印99乘法表功能的函数,很简单,我们已经做了,代码如下:
public static void print99() {
for (int x = 1; x <= 9; x++) {
for (int y = 1; y <= x; y++) {
System.out.print(y+"*"+x+"="+y*x+"\t");
}
System.out.println();
}
}
此时,如果我们还要打印一个任意数乘法表功能的函数,我们可以这样做:
public static void print99(int num) {
for (int x = 1; x <= num; x++) {
for (int y = 1; y <= x; y++) {
System.out.print(y+"*"+x+"="+y*x+"\t");
}
System.out.println();
}
}
同理,打印99乘法表功能的函数,我们还可以写成:
public static void print99() {
print99(9);
}
什么时候用重载?
答:当定义的功能相同,但参与运算的未知内容不同,那么,这时就定义一个函数名称以表示其功能,方便阅读,而通过参数列表的不同来区分多个同名函数。
注意:
练习:以下哪些函数与函数void show(int a, char b, double c) {}
重载?
a.
void show(int x, char y, double z) {}
b.
int show(int a, double c, char b) {}
c.
void show(int a, double c, char b) {}
d.
boolean show(int c, char b) {}
e.
void show(double c) {}
f.
double show(int x, char y, double z) {}
解:
a.
void show(int x, char y, double z) {} // 没有,因为和原函数一样
b.
int show(int a, double c, char b) {} // 重载,因为参数类型不同。注意:重载和返回值类型没有关系
c.
void show(int a, double c, char b) {} // 重载,因为参数类型不同。注意:重载和返回值类型没有关系
d.
boolean show(int c, char b) {} // 重载,因为参数个数不同
e.
void show(double c) {} // 重载,因为参数个数不同
f.
double show(int x, char y, double z) {} // 没有,这个函数不可以和给定函数同时存在于一个函数当中。
数组的定义:同一种类型数据的集合。其实数组就是一个容器。
数组的好处:可以自动给数组中的元素从0开始编号,方便操作这些元素。
数组的格式①:元素类型[] 数组名 = new 元素类型[元素个数或数组长度];
例,需求:想定义一个可以存储3个整数的容器。
int[] x = new int[3];
内存结构
Java程序在运行时,需要在内存中分配空间。为了提高运算效率,对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。共划分了5个不同的区域:
new
建立的实例都存放在堆内存中数组内存结构
对于代码int x = 3
,在内存中的情况为:
对于如下代码:
int[] x = new int[3];
x[0] = 59;
x = null;
有如下内存分析图:
对于以下代码:
int[] x = new int[3];
int[] y = x;
y[1] = 89;
System.out.println(x[1]); // 89
x = null;
内存结构图如下:
对于如下代码:
int[] x = new int[3];
int[] y = new int[3];
y[1] = 89;
System.out.println(x[1]); // 89
x = null;
内存结构图如下:
而对于以下代码来说:
int a = 5;
int b = a;
b = 8;
System.out.println(a); // 5
内存结构图如下:
数组的格式②:元素类型[] 数组名 = new 元素类型[]{元素, 元素, ……};
例,
int[] arr = new int[] {3, 1, 6, 5, 4};
又可简写成:
int[] arr = {3, 1, 6, 5, 4};
数组操作常见问题
ArrayIndexOutOfBoundsException
)
int[] arr = new int[3];
System.out.println(arr[3]); // ArrayIndexOutOfBoundsException: 3:操作数组时,访问到了数组中不存在的角标
NullPointerException
)
int[] arr = new int[3];
arr = null;
System.out.println(arr[1]); // NullPointerException:空指针异常,当引用没有任何指向,值为null的情况,该引用还在用于操作实体。
数组的常见操作
获取数组中的元素,通常会用到遍历,数组中有一个属性可以直接获取到数组的元素个数:length
,使用方式:数组名称.length
。
例1,
int[] arr = {3, 6, 5, 1, 8, 9, 67};
System.out.println("length: "+arr.length);
for (int x = 0; x < arr.length; x++) {
System.out.println("arr["+x+"]="+arr[x]+";");
}
此时,直接输出变量arr
:
System.out.println(arr);
会得到诸如[I@139a55
的值,[
表示是一个一维数组,I
表示数组中的元素类型是int
,139a55
是有哈希算法算出来的地址值。
例2,定义一个功能,用于打印数组中的元素,元素间用逗号隔开。
public static void printArray(int[] arr) {
System.out.print("[");
for (int x = 0; x < arr.length; x++) {
if(x != arr.length - 1)
System.out.print(arr[x] + ", ");
else
System.out.println(arr[x]+"]");
}
}
例3,给定一个数组,如{5, 1, 6, 4, 2, 8, 9}
,获取数组中的最大值,以及最小值。
获取最大值的原理图:
用文字描述即为:
解:
步骤:
需要定义一个功能来完成,以便提高复用性。
public static int getMax(int[] arr) {
int max = arr[0];
for (int x = 1; x < arr.length; x++) {
if(arr[x] > max)
max = arr[x];
}
return max;
}
获取最大值的另一种方式,可不可以将临时变量初始化为0呢?可以,这种方式其实是在初始化为数组中的任意一个角标。
public static int getMax_2(int[] arr) {
int max = 0;
for (int x = 1; x < arr.length; x++) {
if(arr[x] > arr[max])
max = x;
}
return arr[max];
}
同理,获取数组中的最小值,代码如下:
public static int getMin(int[] arr) {
int min = 0;
for (int x = 1; x < arr.length; x++) {
if(arr[x] < arr[min])
min = x;
}
return arr[min];
}
那如何获取double类型数组的最大值呢?
因为功能一致,所以定义相同函数名称,以重载形式存在。这里只略写。
public static double getMax(double[] arr) {
}
例4,对给定数组进行排序,如{5, 1, 6, 4, 2, 8, 9}
。
方式一:选择排序
选择排序的原理图:
内循环结束一次,最值出现在头角标位置上。
public static void selectSort(int[] arr) {
for (int x = 0; x < arr.length - 1; x++) {
for (int y = x + 1; y < arr.length; y++) {
if(arr[x] > arr[y]) {
int temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
}
}
}
方式二:冒泡排序
冒泡排序的原理图:
相邻的2个元素进行比较,如果符合条件,换位。第一圈,最值出现在最后位...
public static void bubbleSort(int[] arr) {
for (int x = 0; x < arr.length - 1; x++) {
for (int y = 0; y < arr.length - x - 1; y++) { // -x:让每一次比较的元素减少,-1:避免角标越界
if(arr[y] > arr[y+1]) {
int temp = arr[y];
arr[y] = arr[y+1];
arr[y+1] = temp;
}
}
}
}
发现无论什么排序,都需要对满足条件的元素进行位置置换,所以可以把这部分相同的代码提取出来,单独封装成一个函数。
public static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
那么,选择排序可以写为:
public static void selectSort(int[] arr) {
for (int x = 0; x < arr.length - 1; x++) {
for (int y = x + 1; y < arr.length; y++) {
if(arr[x] > arr[y]) {
swap(arr, x, y);
}
}
}
}
冒泡排序可以写为:
public static void bubbleSort(int[] arr) {
for (int x = 0; x < arr.length - 1; x++) {
for (int y = 0; y < arr.length - x - 1; y++) { // -x:让每一次比较的元素减少,-1:避免角标越界
if(arr[y] > arr[y+1]) {
swap(arr, y, y+1);
}
}
}
}
例5,数组的查找操作——折半查找。折半查找,可以提高效率,但是必须要保证该数组是有序的数组。
折半查找原理图:
折半查找的第一种形式:
public static int halfSearch(int[] arr, int key) {
int min, max, mid;
min = 0;
max = arr.length - 1;
mid = (max + min) / 2;
while(arr[mid] != key) {
if(key > arr[mid])
min = mid + 1;
else if(key < arr[mid])
max = mid - 1;
if(min > max)
return -1;
mid = (max + min) / 2;
}
return mid;
}
折半查找的第二种形式:
public static int halfSearch_2(int[] arr, int key) {
int min = 0, max = arr.length - 1, mid;
while(min <= max) {
mid = (max + min) >> 1;
if(key > arr[mid])
min = mid + 1;
else if(key < arr[mid])
max = mid - 1;
else
return mid;
}
return -1;
}
练习:有一个有序的数组,想要将一个元素插入到该数组中,还要保证该数组是有序的。
原理:如何获取该元素在数组中的位置。使用折半查找。
public static int getIndex(int[] arr, int key) {
int min = 0, max = arr.length - 1, mid;
while(min <= max) {
mid = (max + min) >> 1;
if(key > arr[mid])
min = mid + 1;
else if(key < arr[mid])
max = mid - 1;
else
return mid;
}
return min;
}
例6,进制转换。
如,十进制→二进制
public static void toBin(int num) {
StringBuffer sb = new StringBuffer();
while(num > 0) {
// System.out.println(num%2);
sb.append(num%2);
num /= 2;
}
System.out.println(sb.reverse());
}
该方法有局限性,即转换的十进制数只能是正数,还有此刻我们并不熟悉StringBuffer
类。
十进制→十六进制
public static void toHex(int num) {
StringBuffer sb = new StringBuffer();
for (int x = 0; x < 8; x++) {
int temp = num & 15;
if(temp > 9)
// System.out.println((char)(temp - 10 + 'A'));
sb.append((char)(temp - 10 + 'A'));
else
// System.out.println(temp);
sb.append(temp);
num = num >>> 4;
}
System.out.println(sb.reverse());
}
我们使用查表法进一步优化。如对于十进制→十六进制。查表法,将所有的元素临时存储起来,建立对应关系,每一次&15
后的值作为索引去查建立好的表,就可以找对应的元素,这样比-10+'A'
简单的多。
这个表怎么建立呢?可以通过数组的形式来定义。
public static void toHex(int num) {
char[] chs = {'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'A', 'B',
'C', 'D', 'E', 'F'};
// 定义一个临时容器
char[] arr = new char[8]; // '\u0000',u指代unicode码,空格
int pos = arr.length;
while(num != 0) {
int temp = num & 15;
// System.out.println(chs[temp]);
arr[--pos] = chs[temp];
num = num >>> 4;
}
System.out.println("pos = " + pos);
// 存储数据的arr数组遍历
for (int x = pos; x < arr.length; x++) {
System.out.print(arr[x]+",");
}
}
那么接下来,就该十进制→二进制了。
public static void toBin(int num) {
// 定义二进制的表
char[] chs = {'0', '1'};
// 定义一个临时存储容器
char[] arr = new char[32];
// 定义一个操作数组的指针
int pos = arr.length;
while(num != 0) {
int temp = num & 1;
arr[--pos] = chs[temp];
num = num >>> 1;
}
for (int x = pos; x < arr.length; x++) {
System.out.print(arr[x]);
}
}
最后,做进制转换的最优优化:
// 十进制→二进制
public static void toBin(int num) {
trans(num, 1, 1);
}
// 十进制→八进制
public static void toOtc(int num) {
trans(num, 7, 3);
}
// 十进制→十六进制
public static void toHax(int num) {
trans(num, 15, 4);
}
public static void trans(int num, int base, int offset) {
if(num == 0) {
System.out.println(0);
return;
}
char[] chs = {'0', '1', '2', '3',
'4', '5', '6', '7',
'8', '9', 'A', 'B',
'C', 'D', 'E', 'F'};
char[] arr = new char[32];
int pos = arr.length;
while(num != 0) {
int temp = num & base;
arr[--pos] = chs[temp];
num = num >>> offset;
}
for (int x = pos; x < arr.length; x++) {
System.out.print(arr[x]);
}
}
二维数组
格式1:int[][] arr = new int[3][2];
定义了名称为arr的二维数组,二维数组中有3个一维数组,每一个一维数组中有2个元素,一维数组的名称分别为arr[0]
, arr[1]
, arr[2]
,给第一个一维数组1
脚标位赋值为78
写法是: arr[0][1] = 78;
对于如下代码:
int[][] arr = new int[2][3];
arr[1][2] = 8;
arr[0][3] = 90;
System.out.println(arr); // [[I@139a55
System.out.println(arr[0]); // [I@1db9742
System.out.println(arr[0][1]); // 0
内存图如下:
格式2:int[][] arr = new int[3][];
二维数组中有3个一维数组, 每个一维数组都是默认初始化值null
,可以对这三个一维数组分别进行初始化。
如:
int[][] arr = new int[3][];
System.out.println(arr[0]); // null
arr[0] = new int[3];
arr[1] = new int[1];
arr[2] = new int[2];
System.out.println(arr); // [[I@139a55
System.out.println(arr.length); // 打印的是二维数组的长度:3
System.out.println(arr[0].length); // 打印二维数组中第1个一维数组的长度
内存图如下:
格式3:int[][] arr = {{3,8,2},{2,7},{9,0,1,6}};
练习:从以下代码可以看出哪个选项正确与否?
int[] x, y[];
a.
x[0] = y;
b.
y[0] = x;
c.
y[0][0] = x;
d.
x[0][0] = y;
e.
y[0][0] = x[0];
f.
x = y;
解:一维数组可以写为:
int[] x;
int x[];
二维数组可以写为:
int[][] y;
int y[][];
int[] y[]; // 注意这种特殊写法
所以:
a.
x[0] = y; // error
b.
y[0] = x; // yes
c.
y[0][0] = x; // error
d.
x[0][0] = y; // error
e.
y[0][0] = x[0]; // yes
f.
x = y; // error