[关闭]
@liayun 2016-12-01T13:43:24.000000Z 字数 21295 阅读 1603

Java语言基础

java基础


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中的名称规范:

注释

用于注解说明解释程序的文字就是注释。注释提高了代码的阅读性。
java中的注释格式:

对于单行和多行注释,被注释的文字,不会被JVM(java虚拟机)解释执行。
对于文档注释,是java特有的注释,其中注释内容可以被JDK提供的工具javadoc所解析,生成一套以网页文件形式体现的该程序的说明文档。
注释是一个程序员必须要具有的良好编程习惯。
初学者编写程序可以养成习惯:先写注释再写代码。将自己的思想通过注释先整理出来,在用代码去体现,因为代码仅仅是思想的一种体现形式而已。
例,

  1. /**
  2. 这个类是用于演示hello world.
  3. 作者:李阿昀
  4. 版本:V1.0
  5. */
  6. class Demo {
  7. /*
  8. main函数可以保证该类的独立运行
  9. 它是程序的入口
  10. 它会被JVM所调用
  11. */
  12. public static void main(String[] args) {
  13. //这是输出语句用于将括号内的数据打印到控制台。
  14. System.out.println("hello java");
  15. System.out.println("hello world");
  16. }
  17. }

变量与常量

常量表示不能改变的数值。Java中常量的分类:

  1. 整数常量。所有整数
  2. 小数常量。所有小数
  3. 布尔型常量。较为特殊,只有两个数值:truefalse
  4. 字符常量。将一个数字、字母或者符号用单引号( ' ' )标识
  5. 字符串常量。将一个或者多个字符用双引号标识
  6. null常量。只有一个数值就是:null

对于整数,java有四种表现形式:

注意:

  1. 负数在计算机内存中的二进制表现形式:对应的正数二进制取反加1
  2. (在计算机内存中的二进制表现的)负数转换成十进制数:对应的负数二进制减1取反,转成十进制数,最后前面加一个-

变量的概念:

为什么要定义变量?
用来不断的存放同一类型的常量,并可以重复使用。(即当数据不确定时,需要对数据进行存储时,就定义一个变量来完成存储动作。)
定义变量的格式:数据类型 变量名 = 初始化值;。注:格式是固定的,记住格式,以不变应万变。
理解:变量就如同数学中的未知数。
Java语言是强类型语言,对于每一种数据都定义了明确的具体数据类型,在内存中分配了不同大小的内存空间。如图:
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这样的方法直接查看基本类型所占内存空间的大小。通过以下程序就能够查看了:

  1. class JavaType {
  2. public static void main(String[] args) {
  3. System.out.println("Byte: " + Byte.SIZE);
  4. System.out.println("Short: " + Short.SIZE);
  5. System.out.println("Integer: " + Integer.SIZE);
  6. System.out.println("Long: " + Long.SIZE);
  7. System.out.println("Float: " + Float.SIZE);
  8. System.out.println("Double: " + Double.SIZE);
  9. System.out.println("Character: " + Character.SIZE);
  10. }
  11. }

输出结果为:(单位是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基本数据类型举例:

  1. int x = 4;
  2. byte b = 2; //2是一个int常量,但是会自动判断2是不是在byte类型的范围内(-128~127),=运算符在给b赋值时,自动完成了强转操作
  3. byte b1 = 128; //编译未通过,超出byte类型的范围
  4. short s = 30000;
  5. long l = 4l;
  6. float f = 2.3f;
  7. double d = 34.56;
  8. char ch = '4';
  9. char ch1 = 'a';
  10. char ch2 = '+';
  11. char ch3 = ' ';
  12. char ch4 = '昀'; //一个中文在内存中占2个字节,而char就是2个字节空间大小
  13. boolean bo = true;
  14. boolean bo1 = false;

自动类型转换(也叫隐式类型转换)和强制类型转换(也叫显式类型转换)。
表达式的数据类型自动提升

试分析System.out.println('a')System.out.println('a'+1)的区别。

  1. System.out.println('a'); //a
  2. System.out.println('a'+1); //98
  3. System.out.println((char)('a'+1)); //b
  4. System.out.println('A'+0); //65
  5. System.out.println('1'+0); //49

System.out.println((char)5);会输出的形状。

自动类型提升:

  1. byte b = 3;
  2. int x = 4;
  3. x = x + b;//b会自动提升为int类型进行运算。

强制类型转换:

  1. byte b = 3; // 3是一个int常量,但是会自动判断3是不是在byte类型的范围内(-128~127),=运算符在给b赋值时,自动完成了强转操作
  2. b = b + 4;//报错
  3. b = (byte)(b+4);//强制类型转换,强制将b+4的结果转换为byte类型,再赋值给b。

运算符

算术运算符的注意问题

转义字符

转义字符:通过\来转变后面字母或者符号的含义。如:
\n:换行
\b:退格。相当于backspace键
\r:按下回车键。windos系统中,回车符是由两个字符来表示\r\n
\t:制表符。相当于tab键
例,

  1. System.out.println("hello\tworld");
  2. System.out.println("\"hello java\"");
  3. System.out.println("\\hello java\\");
  4. char ch = '\'';

赋值运算符

例,

  1. int a, b, c;
  2. a = b = c = 5;

一个面试题:
思考如下代码中s = s + 2;s += 2;的区别?

  1. short s = 3;
  2. s = s + 2;
  3. s += 2;

解:

  1. short s = 3;
  2. s = s + 2; // 编译失败,因为s会被提升为int类型,运算后的结果还是int类型,无法赋值给short类型
  3. s += 2; // 编译通过,因为+=运算符在给s赋值时,自动完成了强转操作

比较运算符

注意:

  1. 比较运算符的结果都是boolean型,也就是要么是true,要么是false。
  2. 比较运算符==不能误写成=

逻辑运算符

逻辑运算符用于连接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

位运算是直接对二进制进行运算。
注意:

  1. <<:其实就是乘以2的移动的位数次幂。
  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

例,

  1. 7 ^ 4
  2. 111
  3. ^ 100 (加密)
  4. ------
  5. 011
  6. ^ 100 (解密),密钥:100
  7. ------
  8. 111 = 7; ----> 7 ^ 4 ^ 4 = 7;

结论:一个数异或同一个数两次,结果还是那个数。
练习:对两个整数变量的值进行互换(不需要第三方变量)。
解:
第一种方式:(使用第三方变量,实际开发时使用)

  1. int n = 3, m = 8;
  2. System.out.println("n = " + n + ", m = " + m);
  3. int temp;
  4. temp = n;
  5. n = m;
  6. m = temp;
  7. System.out.println("n = " + n + ", m = " + m);

第二种方式:(不需要第三方变量)

  1. int n = 3, m = 8;
  2. System.out.println("n = " + n + ", m = " + m);
  3. n = n + m; // 如果n和m的值非常大,容易超出int范围
  4. m = n - m;
  5. n = n - m;
  6. System.out.println("n = " + n + ", m = " + m);

第三种方式:(不需要第三方变量,炫技型)

  1. int n = 3, m = 8;
  2. System.out.println("n = " + n + ", m = " + m);
  3. n = n ^ m;
  4. m = n ^ m; // (n ^ m) ^ m = n;
  5. n = n ^ m; // n ^ (n ^ m) = m;
  6. System.out.println("n = " + n + ", m = " + m);

三元运算符

格式:(条件表达式) ? 表达式1 : 表达式2

例,

  1. int x = 2, y;
  2. y = (x > 1) ? 'a' : 200;
  3. System.out.println("y = " + y); // 97,自动类型提升

程序流程控制

判断结构

if else结构简写格式:变量 = (条件表达式) ? 表达式1 : 表达式2;
三元运算符的特点:

if语句练习:
练习一:根据用户定义的数值不同,打印对应的星期英文。
解:

  1. //需求1:根据用户定义的数值不同,打印对应的星期英文
  2. class IfTest {
  3. public static void main(String[] args) {
  4. int num = 2;
  5. if(num == 1) {
  6. System.out.println("monday");
  7. } else if(num == 2) {
  8. System.out.println("tsd");
  9. } else if {
  10. ...
  11. } else {
  12. System.out.println("nono");
  13. }
  14. }
  15. }

练习二:根据用户指定的月份,打印该月份所在的季节。
解:
第一种方式:

  1. //需求2:根据用户指定的月份,打印该月份所在的季节
  2. // 3,4,5 春季 6,7,8 夏季 9,10,11 秋季 12,1,2 冬季
  3. class IfTest {
  4. public static void main(String[] args) {
  5. int x = 4;
  6. if(x == 3 || x == 4 || x == 5) {
  7. System.out.println(x + "春季");
  8. } else if(x == 6 || x == 7 || x == 8) {
  9. System.out.println(x + "夏季");
  10. } else if(x == 9 || x == 10 || x == 11) {
  11. System.out.println(x + "秋季");
  12. } else if(x == 12 || x == 1 || x == 2) {
  13. System.out.println(x + "冬季");
  14. } else {
  15. System.out.println(x + "月份不存在");
  16. }
  17. }
  18. }

第二种方式:

  1. class IfTest {
  2. public static void main(String[] args) {
  3. int x = 4;
  4. if(x > 12 || x < 1) {
  5. System.out.println(x + "月份不存在");
  6. } else if(x >= 3 && x <= 5) {
  7. System.out.println(x + "春季");
  8. } else if(x >= 6 && x <= 8) {
  9. System.out.println(x + "夏季");
  10. } else if(x >= 9 && x <= 11) {
  11. System.out.println(x + "秋季");
  12. } else {
  13. System.out.println(x + "冬季");
  14. }
  15. }
  16. }

选择结构

switch语句特点:

  1. switch语句选择的类型只有四种:byteshortintchar。但JDK7.0对switch语句进行了增强,可以判断字符串,JDK5.0对switch语句也进行了增强,可以对枚举类型进行判断。
  2. case之间与default没有顺序。先执行第一个case,没有匹配的case执行default。
  3. 结束switch语句的两种情况:遇到break,执行到switch语句结束。
  4. 如果匹配的case或者default没有对应的break,那么程序会继续向下
    执行,运行可以执行的语句,直到遇到break或者switch结尾结束。

例,

  1. int x = 3;
  2. switch(x) { // byte,short,int,char
  3. default:
  4. System.out.println("d");
  5. case 4:
  6. System.out.println("a");
  7. case 6:
  8. System.out.println("b");
  9. break;
  10. case 2:
  11. System.out.println("c");
  12. break;
  13. }

以上代码输出:

d
a
b

switch语句练习:
练习:根据用户指定的月份,打印该月份所在的季节。

  1. class SwitchTest {
  2. public static void main(String[] args) {
  3. int x = 4;
  4. switch(x) {
  5. case 3:
  6. case 4:
  7. case 5:
  8. System.out.println(x + "春季");
  9. break;
  10. case 6:
  11. case 7:
  12. case 8:
  13. System.out.println(x + "夏季");
  14. break;
  15. case 9:
  16. case 10:
  17. case 11:
  18. System.out.println(x + "秋季");
  19. break;
  20. case 12:
  21. case 1:
  22. case 2:
  23. System.out.println(x + "冬季");
  24. break;
  25. default:
  26. System.out.println("nono");
  27. }
  28. }
  29. }

ifswitch语句很像,具体什么场景下,应用哪个语句呢?
答:如果判断的具体数值不多,而且符合byte short int char这四种类型,虽然两个语句都可以使用,但建议使用switch语句,因为效率稍高。
其他情况:对区间判断,对结果为boolean类型判断,使用if,if的使用范围更广。

循环结构

while:先判断条件,只有条件满足才执行循环体。
do while:先执行循环体,再判断条件,条件满足,再继续执行循环体。
简单一句话:do while无论条件是否满足,循环体至少执行一次。
for循环格式:

for(初始化表达式;循环条件表达式;循环后的操作表达式)
{
    执行语句;
}

例1,

  1. class ForDemo {
  2. public static void main(String[] args) {
  3. for (int x = 0; x < 3; x++) {
  4. System.out.println("x = " + x);
  5. }
  6. //System.out.println("x ======= " + x);
  7. int y = 0;
  8. while (y < 3) {
  9. System.out.println("y = " + y);
  10. y++;
  11. }
  12. System.out.println("y ======= " + y);
  13. }
  14. }

从以上代码中可得:

  1. 变量有自己的作用域,对于for来讲,如果将用于控制循环的增量定义在for语句中,那么该变量只在for语句内有效,for语句执行完毕,该变量在内存中被释放。而while循环使用的变量在循环结束后还可以继续使用。
  2. for和while可以进行互换。如果需要定义循环增量,用for更为合适。

总结:
什么时候使用循环结构?
答:当要对某些语句执行很多次时,就使用循环结构。
例2,for循环还可以写成:

  1. int x = 1;
  2. for (System.out.println("a"); x < 3; System.out.println("c"), x++) {
  3. System.out.println("d");
  4. }

输出结果为:

a
d
c
d
c

例3,

  1. for (int y = 0; y < 3; y++) {
  2. }

以上代码可以写成如下代码:

  1. int y = 0;
  2. for ( ; y < 3; ) {
  3. y++;
  4. }

例4,最简单无限循环格式:

  1. for(;;) {
  2. }
  3. while(true) {
  4. }

无限循环存在的原因是并不知道循环多少次,而是根据某些条件,来控制循环。
练习一:获取1~10的和,并打印。
解:

  1. class ForTest2 {
  2. public static void main(String[] args) {
  3. int sum = 0;
  4. for (int x = 1; x <= 10; x++) {
  5. sum += x;
  6. }
  7. System.out.println("sum = " + sum);
  8. }
  9. }

其实这就是累加思想。原理:通过变量记录住每次变化的结果,并通过循环的形式,进行累加动作。
练习二:获取1~100之间7的倍数的个数,并打印。
解:

  1. /*
  2. 获取1~100之间7的倍数的个数,并打印
  3. 思路:
  4. 1.先对1~100进行循环(遍历)
  5. 2.在遍历的过程中,定义条件,只对7的倍数进行操作
  6. 3.因为7的倍数不确定,只要符合条件,就通过一个变量来记录这个变化的次数。
  7. 步骤:
  8. 1.定义循环语句,选择for语句
  9. 2.在循环中定义判断,只要是7的倍数即可,使用if语句。条件:7的倍数 x % 7 == 0;
  10. 3.定义变量,该变量随着7的倍数的出现而自增
  11. */
  12. class ForTest3 {
  13. public static void main(String[] args) {
  14. int count = 0;
  15. for (int x = 1; x <= 100; x++) {
  16. if(x % 7 == 0) {
  17. count++;
  18. }
  19. }
  20. System.out.println("count = " + count);
  21. }
  22. }

其实这就是计数器思想,原理:通过一个变量记录住数据的状态变化,也需要通过循环完成。
for循环嵌套
例1,使用for循环打印如下所示的图形:
****
****
****
解:对于打印长方形,外循环控制行数,内循环控制每一行的列数,也就是一行中元素的个数。代码如下:

  1. class ForForDemo {
  2. public static void main(String[] args) {
  3. for (int x = 0; x < 3; x++) {
  4. for (int y = 0; y < 4; y++) {
  5. System.out.print("*");
  6. }
  7. System.out.println();
  8. }
  9. }
  10. }

例2,使用for循环打印如下所示的图形:
*****
****
***
**
*
解:发现图形有很多行,每一个行有很多列,所以得使用嵌套循环。原理:形象说法,大圈套小圈。代码如下:

  1. class ForForDemo {
  2. public static void main(String[] args) {
  3. for (int x = 0; x < 5; x++) {
  4. for (int y = x; y < 5; y++) {
  5. System.out.print("*");
  6. }
  7. System.out.println();
  8. }
  9. }
  10. }

例3,使用for循环打印如下所示的图形:
*
**
***
****
*****
解:

  1. class ForForDemo {
  2. public static void main(String[] args) {
  3. for (int x = 0; x < 5; x++) {
  4. for (int y = 0; y <= x; y++) {
  5. System.out.print("*");
  6. }
  7. System.out.println();
  8. }
  9. }
  10. }

总结:不是规律的规律:

  1. 尖朝上,可以改变条件,让条件随着外循环变化
  2. 尖朝下,可以初始化值,让初始化值随着外循环变化

例4,使用for循环打印如下所示的图形:
1
12
123
1234
12345
解:

  1. class ForForDemo {
  2. public static void main(String[] args) {
  3. for (int x = 0; x < 5; x++) {
  4. for (int y = 1; y <= x + 1; y++) {
  5. System.out.print(y);
  6. }
  7. System.out.println();
  8. }
  9. }
  10. }

例5,打印一个99乘法表。
解:

  1. class ForForDemo {
  2. public static void main(String[] args) {
  3. for (int x = 1; x <= 9; x++) {
  4. for (int y = 1; y <= x; y++) {
  5. System.out.print(y + "*" + x + "=" + (y*x) + "\t");
  6. }
  7. System.out.println();
  8. }
  9. }
  10. }

例6,打印如下所示的图形:
□□□□*
□□□*□*
□□*□*□*
□*□*□*□*
*□*□*□*□*
注意:表示空格。
解:

  1. class ForForDemo {
  2. public static void main(String[] args) {
  3. for (int x = 0; x < 5; x++) {
  4. for (int y = x + 1; y < 5; y++) {
  5. System.out.print(" ");
  6. }
  7. for (int z = 0; z <= x; z++) {
  8. System.out.print("* ");
  9. }
  10. System.out.println();
  11. }
  12. }
  13. }

其他流程控制语句

break(跳出),应用于选择结构和循环结构。
例,

  1. w:for (int x = 0; x < 3; x++) {
  2. for (int y = 0; y < 4; y++) {
  3. System.out.println("x = " + x);
  4. break w;
  5. }
  6. }

可以给循环标号,然后通过break指定跳出哪个循环。
continue(继续),只能作用于循环结构,继续循环。特点:结束本次循环,继续下一次循环。
例1,

  1. for (int x = 1; x <= 10; x++) {
  2. if(x % 2 == 1)
  3. continue;
  4. System.out.println("x = " + x);
  5. }

例2,也可以给循环标号,然后通过continue指定继续哪个循环。

  1. w:for (int x = 0; x < 3; x++) {
  2. for (int y = 0; y < 4; y++) {
  3. System.out.println("x = " + x);
  4. continue w;
  5. }
  6. }

记住:

  1. breakcontinue语句作用的范围
  2. breakcontinue单独存在时,下面不可以有任何语句,因为都执行不到

函数

函数的定义
函数就是定义在类中的具有特定功能的一段独立小程序。函数也称为方法。
函数的格式:

  1. 修饰符 返回值类型 函数名(参数类型 形式参数1, 参数类型 形式参数2, ... )
  2. {
  3. 执行语句;
  4. return 返回值;
  5. }

试看以下代码:

  1. int x = 4;
  2. System.out.println(x*3+5);
  3. x = 6;
  4. System.out.println(x*3+5);

发现以上的运算,因为获取不同数据的运算结果,代码出现了重复。为了提高代码的复用性,对代码进行了抽取,将这个部分定义成一个独立的功能,方便于日后使用,java中对该功能的定义是通过函数的形式来体现的。
所以,我们有了一个需求:定义一个功能,完成一个整数*3+5的运算,并打印结果。

  1. public static int getResult(int num) {
  2. return num * 3 + 5;
  3. }

当函数运算后没有具体的返回值,这时返回值类型用一个特殊的关键字来标识,该关键字就是voidvoid:代表的是函数没有具体返回值的情况。当函数的返回值类型是void时,函数中的return语句可以省略不写。即:

  1. public static void getResult(int num) {
  2. System.out.println(num * 3 + 5);
  3. return; // 可以省略
  4. }

函数的特点

注意:

  1. 函数中只能调用函数,不可以在函数内部定义函数。
  2. 定义函数时,函数的结果应该返回给调用者,交由调用者处理。

函数的应用
如何定义一个函数呢?

  1. 既然函数是一个独立的功能,那么该功能的运算结果是什么呢?所以得先明确。因为这是在明确函数的返回值类型。
  2. 再明确在定义该功能的过程中,是否需要未知的内容参与运算。因为这是在明确函数的参数列表(参数的类型和参数的个数)。

例1,需求:定义一个功能:完成3+4的运算,并将结果返回给调用者。
解:

  1. 明确功能的结果:是一个整数的和。
  2. 在实现该功能的过程中,是否有未知内容参与运算:没有。

其实这两个功能就是在明确函数的定义:

  1. 明确函数的返回值类型
  2. 明确函数的参数列表(参数的类型和参数的个数)
  1. public static int getSum() {
  2. return 3 + 4;
  3. }

以上这个函数的功能,结果是固定的,毫无扩展性而言,为了方便用户需求,由用户来指定加数和被加数,这样,功能才有意义。
例2,定义一个功能,可以实现两个整数的加法运算。
解:

  1. 功能结果是一个和,返回值类型是int
  2. 有未知内容参与运算,有2个,这2个未知内容的类型都是int
  1. public static int getSum(int x, int y) {
  2. return x + y;
  3. }

例3,需求:判断两个数是否相同?
解:

  1. 明确功能结果:结果是boolean类型
  2. 功能是否有未知内容参与运算:有,两个整数
  1. public static boolean compare(int a, int b) {
  2. if(a == b)
  3. return true;
  4. else
  5. return false;
  6. }

第一次优化后:

  1. public static boolean compare(int a, int b) {
  2. if(a == b)
  3. return true;
  4. return false;
  5. }

第二次优化后:

  1. public static boolean compare(int a, int b) {
  2. return a == b ? true : false;
  3. }

最后一次优化:

  1. public static boolean compare(int a, int b) {
  2. return a == b;
  3. }

例4,需求:定义功能,对两个数进行比较,获取较大的数。
解:

  1. public static int getMax(int a, int b) {
  2. return (a > b) ? a : b;
  3. }

练习一:定义一个功能,用于打印矩形。
解:

  1. 确定结果:没有,因为直接打印,所以返回值类型是void
  2. 有未知内容吗?:有,2个,因为矩形的行和列不确定
  1. public static void draw(int row, int col) {
  2. for (int x = 0; x < row; x++) {
  3. for (int y = 0; y < col; y++) {
  4. System.out.print("*");
  5. }
  6. System.out.println();
  7. }
  8. }

练习二:定义一个打印99乘法表功能的函数。
解:

  1. public static void print99() {
  2. for (int x = 1; x <= 9; x++) {
  3. for (int y = 1; y <= x; y++) {
  4. System.out.print(y+"*"+x+"="+y*x+"\t");
  5. }
  6. System.out.println();
  7. }
  8. }

函数的重载(overload)
重载的概念:在同一个类中,允许存在一个以上的同名函数,只要它们的参数个数或者参数类型不同即可。
重载的特点:与返回值类型无关,只看参数列表。
重载的好处:方便于阅读,优化了程序设计。
例,定义一个加法运算,获取两个整数的和。

  1. public static int add(int x, int y) {
  2. return x + y;
  3. }

定义一个加法运算,获取三个整数的和:

  1. public static int add(int x, int y, int z) {
  2. return x + y + z;
  3. }

这两段代码就是函数的重载,当然函数中可调用函数,所以以上代码可以写成:

  1. public static int add(int x, int y, int z) {
  2. return add(x, y) + z;
  3. }

例,定义一个打印99乘法表功能的函数,很简单,我们已经做了,代码如下:

  1. public static void print99() {
  2. for (int x = 1; x <= 9; x++) {
  3. for (int y = 1; y <= x; y++) {
  4. System.out.print(y+"*"+x+"="+y*x+"\t");
  5. }
  6. System.out.println();
  7. }
  8. }

此时,如果我们还要打印一个任意数乘法表功能的函数,我们可以这样做:

  1. public static void print99(int num) {
  2. for (int x = 1; x <= num; x++) {
  3. for (int y = 1; y <= x; y++) {
  4. System.out.print(y+"*"+x+"="+y*x+"\t");
  5. }
  6. System.out.println();
  7. }
  8. }

同理,打印99乘法表功能的函数,我们还可以写成:

  1. public static void print99() {
  2. print99(9);
  3. }

什么时候用重载?
答:当定义的功能相同,但参与运算的未知内容不同,那么,这时就定义一个函数名称以表示其功能,方便阅读,而通过参数列表的不同来区分多个同名函数。
注意:

  1. 参数列表是有顺序的
  2. 重载和返回值类型没关系

练习:以下哪些函数与函数void show(int a, char b, double c) {}重载?

  1. a.
  2. void show(int x, char y, double z) {}
  3. b.
  4. int show(int a, double c, char b) {}
  5. c.
  6. void show(int a, double c, char b) {}
  7. d.
  8. boolean show(int c, char b) {}
  9. e.
  10. void show(double c) {}
  11. f.
  12. double show(int x, char y, double z) {}

解:

  1. a.
  2. void show(int x, char y, double z) {} // 没有,因为和原函数一样
  3. b.
  4. int show(int a, double c, char b) {} // 重载,因为参数类型不同。注意:重载和返回值类型没有关系
  5. c.
  6. void show(int a, double c, char b) {} // 重载,因为参数类型不同。注意:重载和返回值类型没有关系
  7. d.
  8. boolean show(int c, char b) {} // 重载,因为参数个数不同
  9. e.
  10. void show(double c) {} // 重载,因为参数个数不同
  11. f.
  12. double show(int x, char y, double z) {} // 没有,这个函数不可以和给定函数同时存在于一个函数当中。

数组

数组的定义:同一种类型数据的集合。其实数组就是一个容器。
数组的好处:可以自动给数组中的元素从0开始编号,方便操作这些元素。
数组的格式①:元素类型[] 数组名 = new 元素类型[元素个数或数组长度];
例,需求:想定义一个可以存储3个整数的容器。

  1. int[] x = new int[3];

内存结构
Java程序在运行时,需要在内存中分配空间。为了提高运算效率,对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。共划分了5个不同的区域:

数组内存结构
对于代码int x = 3,在内存中的情况为:
内存分析图
对于如下代码:

  1. int[] x = new int[3];
  2. x[0] = 59;
  3. x = null;

有如下内存分析图:
数组内存分析图
对于以下代码:

  1. int[] x = new int[3];
  2. int[] y = x;
  3. y[1] = 89;
  4. System.out.println(x[1]); // 89
  5. x = null;

内存结构图如下:
数组内存分析图
对于如下代码:

  1. int[] x = new int[3];
  2. int[] y = new int[3];
  3. y[1] = 89;
  4. System.out.println(x[1]); // 89
  5. x = null;

内存结构图如下:
数组内存分析图
而对于以下代码来说:

  1. int a = 5;
  2. int b = a;
  3. b = 8;
  4. System.out.println(a); // 5

内存结构图如下:
变量内存分析图
数组的格式②:元素类型[] 数组名 = new 元素类型[]{元素, 元素, ……};
例,

  1. int[] arr = new int[] {3, 1, 6, 5, 4};

又可简写成:

  1. int[] arr = {3, 1, 6, 5, 4};

数组操作常见问题

  1. int[] arr = new int[3];
  2. System.out.println(arr[3]); // ArrayIndexOutOfBoundsException: 3:操作数组时,访问到了数组中不存在的角标
  1. int[] arr = new int[3];
  2. arr = null;
  3. System.out.println(arr[1]); // NullPointerException:空指针异常,当引用没有任何指向,值为null的情况,该引用还在用于操作实体。

数组的常见操作
获取数组中的元素,通常会用到遍历,数组中有一个属性可以直接获取到数组的元素个数:length,使用方式:数组名称.length
例1,

  1. int[] arr = {3, 6, 5, 1, 8, 9, 67};
  2. System.out.println("length: "+arr.length);
  3. for (int x = 0; x < arr.length; x++) {
  4. System.out.println("arr["+x+"]="+arr[x]+";");
  5. }

此时,直接输出变量arr

  1. System.out.println(arr);

会得到诸如[I@139a55的值,[表示是一个一维数组,I表示数组中的元素类型是int139a55是有哈希算法算出来的地址值。
例2,定义一个功能,用于打印数组中的元素,元素间用逗号隔开。

  1. public static void printArray(int[] arr) {
  2. System.out.print("[");
  3. for (int x = 0; x < arr.length; x++) {
  4. if(x != arr.length - 1)
  5. System.out.print(arr[x] + ", ");
  6. else
  7. System.out.println(arr[x]+"]");
  8. }
  9. }

例3,给定一个数组,如{5, 1, 6, 4, 2, 8, 9},获取数组中的最大值,以及最小值。
获取最大值的原理图:
获取数组最大值
用文字描述即为:

  1. 获取最值需要进行比较。每一次比较都会有一个较大的值,因为该值不确定,通过一个变量进行存储。
  2. 让数组中的每一个元素都和这个变量中的值进行比较,如果大于了变量中的值,就用该变量记录较大值。
  3. 当所有的元素都比较完成,那么该变量中存储的就是数组中的最大值了。

解:
步骤:

  1. 定义变量,初始化为数组中的任意一个元素即可。
  2. 通过循环语句对数组进行遍历。
  3. 在遍历过程中定义判断条件,如果遍历到的元素比变量中的元素大,就赋值给该变量。

需要定义一个功能来完成,以便提高复用性。

  1. 明确结果:数组中的最大元素,int
  2. 未知内容:一个数组,int[]
  1. public static int getMax(int[] arr) {
  2. int max = arr[0];
  3. for (int x = 1; x < arr.length; x++) {
  4. if(arr[x] > max)
  5. max = arr[x];
  6. }
  7. return max;
  8. }

获取最大值的另一种方式,可不可以将临时变量初始化为0呢?可以,这种方式其实是在初始化为数组中的任意一个角标。

  1. public static int getMax_2(int[] arr) {
  2. int max = 0;
  3. for (int x = 1; x < arr.length; x++) {
  4. if(arr[x] > arr[max])
  5. max = x;
  6. }
  7. return arr[max];
  8. }

同理,获取数组中的最小值,代码如下:

  1. public static int getMin(int[] arr) {
  2. int min = 0;
  3. for (int x = 1; x < arr.length; x++) {
  4. if(arr[x] < arr[min])
  5. min = x;
  6. }
  7. return arr[min];
  8. }

那如何获取double类型数组的最大值呢?
因为功能一致,所以定义相同函数名称,以重载形式存在。这里只略写。

  1. public static double getMax(double[] arr) {
  2. }

例4,对给定数组进行排序,如{5, 1, 6, 4, 2, 8, 9}
方式一:选择排序
选择排序的原理图:
选择排序原理图
内循环结束一次,最值出现在头角标位置上。

  1. public static void selectSort(int[] arr) {
  2. for (int x = 0; x < arr.length - 1; x++) {
  3. for (int y = x + 1; y < arr.length; y++) {
  4. if(arr[x] > arr[y]) {
  5. int temp = arr[x];
  6. arr[x] = arr[y];
  7. arr[y] = temp;
  8. }
  9. }
  10. }
  11. }

方式二:冒泡排序
冒泡排序的原理图:
冒泡排序的原理图
相邻的2个元素进行比较,如果符合条件,换位。第一圈,最值出现在最后位...

  1. public static void bubbleSort(int[] arr) {
  2. for (int x = 0; x < arr.length - 1; x++) {
  3. for (int y = 0; y < arr.length - x - 1; y++) { // -x:让每一次比较的元素减少,-1:避免角标越界
  4. if(arr[y] > arr[y+1]) {
  5. int temp = arr[y];
  6. arr[y] = arr[y+1];
  7. arr[y+1] = temp;
  8. }
  9. }
  10. }
  11. }

发现无论什么排序,都需要对满足条件的元素进行位置置换,所以可以把这部分相同的代码提取出来,单独封装成一个函数。

  1. public static void swap(int[] arr, int a, int b) {
  2. int temp = arr[a];
  3. arr[a] = arr[b];
  4. arr[b] = temp;
  5. }

那么,选择排序可以写为:

  1. public static void selectSort(int[] arr) {
  2. for (int x = 0; x < arr.length - 1; x++) {
  3. for (int y = x + 1; y < arr.length; y++) {
  4. if(arr[x] > arr[y]) {
  5. swap(arr, x, y);
  6. }
  7. }
  8. }
  9. }

冒泡排序可以写为:

  1. public static void bubbleSort(int[] arr) {
  2. for (int x = 0; x < arr.length - 1; x++) {
  3. for (int y = 0; y < arr.length - x - 1; y++) { // -x:让每一次比较的元素减少,-1:避免角标越界
  4. if(arr[y] > arr[y+1]) {
  5. swap(arr, y, y+1);
  6. }
  7. }
  8. }
  9. }

例5,数组的查找操作——折半查找。折半查找,可以提高效率,但是必须要保证该数组是有序的数组。
折半查找原理图:
折半查找原理图
折半查找的第一种形式:

  1. public static int halfSearch(int[] arr, int key) {
  2. int min, max, mid;
  3. min = 0;
  4. max = arr.length - 1;
  5. mid = (max + min) / 2;
  6. while(arr[mid] != key) {
  7. if(key > arr[mid])
  8. min = mid + 1;
  9. else if(key < arr[mid])
  10. max = mid - 1;
  11. if(min > max)
  12. return -1;
  13. mid = (max + min) / 2;
  14. }
  15. return mid;
  16. }

折半查找的第二种形式:

  1. public static int halfSearch_2(int[] arr, int key) {
  2. int min = 0, max = arr.length - 1, mid;
  3. while(min <= max) {
  4. mid = (max + min) >> 1;
  5. if(key > arr[mid])
  6. min = mid + 1;
  7. else if(key < arr[mid])
  8. max = mid - 1;
  9. else
  10. return mid;
  11. }
  12. return -1;
  13. }

练习:有一个有序的数组,想要将一个元素插入到该数组中,还要保证该数组是有序的。
原理:如何获取该元素在数组中的位置。使用折半查找。

  1. public static int getIndex(int[] arr, int key) {
  2. int min = 0, max = arr.length - 1, mid;
  3. while(min <= max) {
  4. mid = (max + min) >> 1;
  5. if(key > arr[mid])
  6. min = mid + 1;
  7. else if(key < arr[mid])
  8. max = mid - 1;
  9. else
  10. return mid;
  11. }
  12. return min;
  13. }

例6,进制转换。
如,十进制→二进制

  1. public static void toBin(int num) {
  2. StringBuffer sb = new StringBuffer();
  3. while(num > 0) {
  4. // System.out.println(num%2);
  5. sb.append(num%2);
  6. num /= 2;
  7. }
  8. System.out.println(sb.reverse());
  9. }

该方法有局限性,即转换的十进制数只能是正数,还有此刻我们并不熟悉StringBuffer类。
十进制→十六进制

  1. public static void toHex(int num) {
  2. StringBuffer sb = new StringBuffer();
  3. for (int x = 0; x < 8; x++) {
  4. int temp = num & 15;
  5. if(temp > 9)
  6. // System.out.println((char)(temp - 10 + 'A'));
  7. sb.append((char)(temp - 10 + 'A'));
  8. else
  9. // System.out.println(temp);
  10. sb.append(temp);
  11. num = num >>> 4;
  12. }
  13. System.out.println(sb.reverse());
  14. }

我们使用查表法进一步优化。如对于十进制→十六进制。查表法,将所有的元素临时存储起来,建立对应关系,每一次&15后的值作为索引去查建立好的表,就可以找对应的元素,这样比-10+'A'简单的多。
这个表怎么建立呢?可以通过数组的形式来定义。

  1. public static void toHex(int num) {
  2. char[] chs = {'0', '1', '2', '3',
  3. '4', '5', '6', '7',
  4. '8', '9', 'A', 'B',
  5. 'C', 'D', 'E', 'F'};
  6. // 定义一个临时容器
  7. char[] arr = new char[8]; // '\u0000',u指代unicode码,空格
  8. int pos = arr.length;
  9. while(num != 0) {
  10. int temp = num & 15;
  11. // System.out.println(chs[temp]);
  12. arr[--pos] = chs[temp];
  13. num = num >>> 4;
  14. }
  15. System.out.println("pos = " + pos);
  16. // 存储数据的arr数组遍历
  17. for (int x = pos; x < arr.length; x++) {
  18. System.out.print(arr[x]+",");
  19. }
  20. }

那么接下来,就该十进制→二进制了。

  1. public static void toBin(int num) {
  2. // 定义二进制的表
  3. char[] chs = {'0', '1'};
  4. // 定义一个临时存储容器
  5. char[] arr = new char[32];
  6. // 定义一个操作数组的指针
  7. int pos = arr.length;
  8. while(num != 0) {
  9. int temp = num & 1;
  10. arr[--pos] = chs[temp];
  11. num = num >>> 1;
  12. }
  13. for (int x = pos; x < arr.length; x++) {
  14. System.out.print(arr[x]);
  15. }
  16. }

最后,做进制转换的最优优化:

  1. // 十进制→二进制
  2. public static void toBin(int num) {
  3. trans(num, 1, 1);
  4. }
  5. // 十进制→八进制
  6. public static void toOtc(int num) {
  7. trans(num, 7, 3);
  8. }
  9. // 十进制→十六进制
  10. public static void toHax(int num) {
  11. trans(num, 15, 4);
  12. }
  13. public static void trans(int num, int base, int offset) {
  14. if(num == 0) {
  15. System.out.println(0);
  16. return;
  17. }
  18. char[] chs = {'0', '1', '2', '3',
  19. '4', '5', '6', '7',
  20. '8', '9', 'A', 'B',
  21. 'C', 'D', 'E', 'F'};
  22. char[] arr = new char[32];
  23. int pos = arr.length;
  24. while(num != 0) {
  25. int temp = num & base;
  26. arr[--pos] = chs[temp];
  27. num = num >>> offset;
  28. }
  29. for (int x = pos; x < arr.length; x++) {
  30. System.out.print(arr[x]);
  31. }
  32. }

二维数组
格式1:int[][] arr = new int[3][2];
定义了名称为arr的二维数组,二维数组中有3个一维数组,每一个一维数组中有2个元素,一维数组的名称分别为arr[0]arr[1]arr[2],给第一个一维数组1脚标位赋值为78写法是: arr[0][1] = 78;
对于如下代码:

  1. int[][] arr = new int[2][3];
  2. arr[1][2] = 8;
  3. arr[0][3] = 90;
  4. System.out.println(arr); // [[I@139a55
  5. System.out.println(arr[0]); // [I@1db9742
  6. System.out.println(arr[0][1]); // 0

内存图如下:

格式2:int[][] arr = new int[3][];
二维数组中有3个一维数组, 每个一维数组都是默认初始化值null,可以对这三个一维数组分别进行初始化。
如:

  1. int[][] arr = new int[3][];
  2. System.out.println(arr[0]); // null
  3. arr[0] = new int[3];
  4. arr[1] = new int[1];
  5. arr[2] = new int[2];
  6. System.out.println(arr); // [[I@139a55
  7. System.out.println(arr.length); // 打印的是二维数组的长度:3
  8. System.out.println(arr[0].length); // 打印二维数组中第1个一维数组的长度

内存图如下:

格式3:int[][] arr = {{3,8,2},{2,7},{9,0,1,6}};
练习:从以下代码可以看出哪个选项正确与否?

  1. int[] x, y[];
  1. a.
  2. x[0] = y;
  3. b.
  4. y[0] = x;
  5. c.
  6. y[0][0] = x;
  7. d.
  8. x[0][0] = y;
  9. e.
  10. y[0][0] = x[0];
  11. f.
  12. x = y;

解:一维数组可以写为:

  1. int[] x;
  2. int x[];

二维数组可以写为:

  1. int[][] y;
  2. int y[][];
  3. int[] y[]; // 注意这种特殊写法

所以:

  1. a.
  2. x[0] = y; // error
  3. b.
  4. y[0] = x; // yes
  5. c.
  6. y[0][0] = x; // error
  7. d.
  8. x[0][0] = y; // error
  9. e.
  10. y[0][0] = x[0]; // yes
  11. f.
  12. x = y; // error
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注