文章目录
- 1.变量和数据类型与运算符
- 1.1什么是变量:
- 1.2变量的定义与使用:
- 1.3数据类型:
- 1.3.1整型:
- 1.3.2浮点型:
- 1.3.3char类型:
- 1.3.4boolean类型:
- 1.3.5数据类型转换:
- 1.3.6数值提升:
- 1.4运算符
- 2.程序逻辑控制
- 1.顺序结构
- 2.分支结构
- 2.1 if 语句
- 2.2 switch 语句
- 3. 循环结构
- 3.1 while 循环
- 3.2 break
- 3.3 continue
- 3.4 for 循环
- 3.5 do while 循环
- 3.方法的使用
- 1. 示例:计算 1! + 2! + 3! + 4! + 5!
- 2.方法的调用
- 3.重载
- 4.数组的定义与使用
- 1.1 什么是数组
- 1.2 创建数组
- 1.3 数组的使用
- 2. 数组作为方法的参数
- 3. 数组作为方法的返回值
- 4. 二维数组
- 5.引用
- 1. 初步认识引用(reference)和对象(object)
- 2. 理解引用和对象之间的关系
- 3. null 的理解
- 4. 引用的赋值操作符理解
- 6.类和对象
- 1. 什么是面相对象
- 2. 类定义和使用
- 3. 类的实例化
- 3.1什么是实例化
- 3.2 类和对象的说明
- 4.类的成员
- 4.1 字段/属性/成员变量
- 4.2方法 (method)
- 5.包
- 5.1导入包中的类
- 5.2访问限定符
- 7.认识String类
- 8.面向对象编程
- 1. 继承
- 1.1 语法规则
- 1.2代码示例
- 1.3重写
- 1.3.1静态方法
- 1.3.2非静态方法
- 2 组合
- 3 多态
- 3.1 向上转型
- 3.2动态绑定
- 3.3重载和重写的区别
- 3.4向下转型
- 3.5执行顺序
- 9.认识异常
1.变量和数据类型与运算符
Java是一种强类型的语言。这就意味着必须为每一个变量声明一种类型。在Java中,一共有8中基本类型(primitive type),其中有4种整型、2种浮点类型、1种用于表示Unicode编码的字符单元的字符类型char和1种用于表示真值的boolean类型。
1.1什么是变量:
这里讨论的变量主要是和内存密切相关的,变量由于要用来保存数据,所以需要一块空间,这块空间就在“内存上”。
1.2变量的定义与使用:
int a; //定义一个变量
int b=7; //定义一个变量并且赋值为7
a=3; //将变量a赋值为3
System.out.println(b); //打印出变量b里面保存的数据
总结:
- 变量必须先定义,才能被使用。
- 变量如果先被赋值或者初始化,才能被使用
1.3数据类型:
1.3.1整型:
整型用于表示没有小数部分的数值,它允许是负数。Java提供了4种整型,具体如下:
整数 占用字节数
byte 1
short 2
int 4
long 8
什么是字节:
字节是计算机中表示空间大小的基本单位.
计算机使用二进制表示数据. 我们认为 8 个二进制位(bit) 为一个字节(Byte).
我们平时的计算机为 8GB 内存, 意思是 8G 个字节.
其中 1KB = 1024 Byte, 1MB = 1024 KB, 1GB = 1024 MB.
所以 8GB 相当于 80 多亿个字节.
一般情况下,整数默认是int类型;byte和short类型一般用于一些特定的场景,比如说一些底层的文件操作;long类型一般不会怎么使用,但是一般用来处理一些特别大的数据;要注意的是定义long类型要用后缀用L或者l标记。
1.3.2浮点型:
浮点类型用于表示有小数部分的数值。在Java中有两种浮点类型,如下:
浮点数 占用字节数
float 4
double 8
double表示这种类型的数值精度是float类型的两倍(又称双精度数值)。绝大数的应用程序都采用double类型。在很多情况下,float类型的精度很难满足需求。比如:需要快速地处理单精度数据,或者需要存储大量数据。
float类型的数值有一个后缀F(例如:3.14F)。没有后缀F的浮点数组默认为double类型。当然,也可以在浮点数值后面添加后缀D(例如:3.14D)。
要注意的是:单精度浮点数用F或者f标记。
1.3.3char类型:
基本格式:
char 变量名=初始值;
代码示例:
char ch='a';
注意事项:
- Java 中使用 单引号 + 单个字母 的形式表示字符字面值.
- 计算机中的字符本质上是一个整数. 在 C 语言中使用 ASCII 表示字符, 而 Java 中使用 Unicode 表示字符. 因此一个字符占用两个字节, 表示的字符种类更多, 包括中文.
1.3.4boolean类型:
基本格式:
boolean 变量名=初始值;
代码示例:
boolean h=true;
System.out.println(h);
注意事项:
- boolean 类型的变量只有两种取值, true 表示真, false 表示假.
- Java 的 boolean 类型和 int 不能相互转换, 不存在 1 表示 true, 0 表示 false 这样的用法.
- boolean 类型有些 JVM 的实现是占 1 个字节, 有些是占 1 个比特位, 这个没有明确规定.
1.3.5数据类型转换:
1.int 和 long/double 相互赋值:
int a = 10;
long b = 20;
a = b; // 编译出错, 提示可能会损失精度.
b = a; // 编译通过.
int a = 10;
double b = 1.0;
a = b; // 编译出错, 提示可能会损失精度.
b = a;// 编译通过.
long 表示的范围更大, 可以将 int 赋值给 long, 但是不能将 long 赋值给 int.
double 表示的范围更大, 可以将 int 赋值给 double, 但是不能将 double 赋值给 int.
结论: 不同数字类型的变量之间赋值, 表示范围更小的类型能隐式转换成范围较大的类型, 反之则不行.
2.int 和 boolean 相互赋值:
int a = 10;
boolean b = true;
b = a; // 编译出错, 提示不兼容的类型
a=b;// 编译出错, 提示不兼容的类型
结论: int与boolean是毫不相关的类型,不能相互赋值。
3.int字面值常量给byte赋值:
byte a = 100;// 编译通过
byte b = 256; // 编译报错, 提示 从int转换到byte可能会有损失
注意: byte 表示的数据范围是 -128 -> +127, 256 已经超过范围, 而 100 还在范围之内.
结论: 使用字面值常量赋值的时候, Java 会自动进行一些检查校验, 判定赋值是否合理.
4. int 和 String 之间的相互转换:
1.int 转成 String:
int num = 10;
// 方法1
String str1 = num + "";
// 方法2
String str2 = String.valueOf(num);
2.String 转成 int:
String str = "100";
int num = Integer.paserInt(str);
5.使用强制类型转换:
int a = 0;
double b = 10.5; a = (int)b;
int a = 10;
boolean b = false; b = (boolean)a; // 编译出错, 提示不兼容的类型
结论: 使用 (类型) 的方式可以将 double 类型强制转成 int. 但是
- 强制类型转换可能会导致精度丢失. 如刚才的例子中, 赋值之后, 10.5 就变成 10 了, 小数点后面的部分被忽略.
- 强制类型转换不是一定能成功, 互不相干的类型之间无法强转!
6.自动装箱与自动拆箱:
其实每种内置类型的数据都对应了一个包装类,其中以int类型为例,来介绍一下,自动装箱与自动拆箱的机制:
//自动装箱
Integer a=12; //实际会隐式调用 Integer.valueOf()方法
System.out.println(a);
Integer b=new Integer(30);
//自动拆箱
int num=b; //实际会隐式调用b.intvalueOf()方法
System.out.println(b);
1.3.6数值提升:
**1.**int 和 long 混合运算:
int a = 10;
long b = 20;
int c = a + b; // 编译出错, 提示将 long 转成 int 会丢失精度
long d = a + b; //编译通过
结论: 当 int 和 long 混合运算的时候, int 会提升成 long, 得到的结果仍然是 long 类型, 需要使用 long 类型的变量来接收结果. 如果非要用 int 来接收结果, 就需要使用强制类型转换.
**2.**byte 和 byte 的运算:
byte a = 10;
byte b = 20;
byte c = a + b;
System.out.println(c);
// 编译报错
Test.java:5: 错误: 不兼容的类型: 从int转换到byte可能会有损失
结论: byte 和 byte 都是相同类型, 但是出现编译报错. 原因是, 虽然 a 和 b 都是 byte, 但是计算 a + b 会 先将 a 和 b 都提升成 int, 再进行计算, 得到的结果也是 int, 这是赋给 c, 就会出现上述错误. 由于计算机的 CPU通常是按照 4 个字节为单位从内存中读写数据. 为了硬件上实现方便, 诸如 byte 和 short 这种低于 4 个字节的类型, 会先提升成 int, 再参与计算.
正确的写法:
byte a = 10;
byte b = 20;
byte c = (byte)(a + b);
System.out.println(c);
类型提升小结:
1. 不同类型的数据混合运算, 范围小的会提升成范围大的.
2. 对于 short, byte 这种比 4 个字节小的类型, 会先提升成 4 个字节的 int , 再运算。
1.4运算符
1.赋值运算符:
这个运算符在数学中叫做“等于”,下面来看一下代码:
int a=12;
a=10;//这里的等号就是赋值运算符
2.算术运算符:
a) int / int 结果还是 int, 需要使用 double 来计算:
int a = 1;
int b = 2;
System.out.println(a / b);
// 结果为 0
b) 0 不能作为除数:
int a = 1;
int b = 0;
System.out.println(a / b)
// 运行时异常
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Test.main(Test.java:5)
c) % 表示取余, 不仅仅可以对 int 求模, 也能对 double 来求模:
System.out.println(11.5 % 2.0);
// 输出结果
1.5
d)增量赋值运算符:
int a = 10;
a += 1; // 等价于 a = a + 1
System.out.println(a);
e)自增/自减运算
int a=10;
int b=++a;
int c=a--;
System.out.println(a);
System.out.println(b);
结论:
1. 如果不取自增运算的表达式的返回值, 则前置自增和后置自增没有区别.
2. 如果取表达式的返回值, 则前置自增的返回值是自增之后的值, 后置自增的返回值是自增之前的值
3.关系运算符:
关系运算符主要有六个:
== != < > <= >=
int a = 10;
int b = 20;
System.out.println(a == b);
System.out.println(a != b);
System.out.println(a < b);
System.out.println(a > b);
System.out.println(a <= b);
System.out.println(a >= b);
4.逻辑运算符:
逻辑运算符主要有三个:
&& || !
注意: 逻辑运算符的操作数(操作数往往是关系运算符的结果)和返回值都是 boolean.
逻辑与 &&
规则: 两个操作数都为 true, 结果为 true, 否则结果为 false.
int a = 10;
int b = 20;
int c = 30;
System.out.println(a>b && a<b);
逻辑或 ||
规则: 两个操作数都为 false, 结果为 false, 否则结果为 true.
int a = 10;
int b = 20;
int c = 30;
System.out.println(a < b || b < c);
逻辑非 !
规则: 操作数为 true, 结果为 false; 操作数为 false, 结果为 true(这是个单目运算符, 只有一个操作数).
int a = 10;
int b = 20;
System.out.println(!a<b);
路求值
&& 和 || 遵守短路求值的规则.
System.out.println(10 > 20 && 10 / 0 == 0); // 打印 false
System.out.println(10 < 20 || 10 / 0 == 0); // 打印 true
我们都知道, 计算 10 / 0 会导致程序抛出异常. 但是上面的代码却能正常运行, 说明 10 / 0 并没有真
正被求值.
结论:
1. 对于 && , 如果左侧表达式值为 false, 则表达式的整体的值一定是 false, 无需计算右侧表达式.
2. 对于 ||, 如果左侧表达式值为 true, 则表达式的整体的一定式true,无需计算右侧的值!
& 和 |
& 和 | 如果操作数为 boolean 的时候, 也表示逻辑运算. 但是和 && 以及 || 相比, 它们不支持短路求值。
System.out.println(10 > 20 && 10 / 0 == 0); // 抛异常
System.out.println(10 < 20 || 10 / 0 == 0); // 抛异常
5. 位运算符:
Java 中对数据的操作的最小单位不是字节, 而是二进制位.
位运算符主要有四个:
& | ~ ^
位操作表示 按二进制位运算. 计算机中都是使用二进制来表示数据的(01构成的序列), 按位运算就是在按照二进制位的每一位依次进行计算.
按位与 &: 如果两个二进制位都是 1, 则结果为 1, 否则结果为 0:
int a=10;
int b=20;
System.out.println(a&b);
进行按位运算, 需要先把 10 和 20 转成二进制, 分别为 1010 和 10100
按位或 |: 如果两个二进制位都是 0, 则结果为 0, 否则结果为 1.
int a=10;
int b=20;
System.out.println(a|b);
按位取反 ~: 如果该位为 0 则转为 1, 如果该位为 1 则转为 0
int a = 0xf;
System.out.printf("%x\n", ~a)
====================================
int a=10;
double s=(int)2; //相当于与没写,这个相当于精度提升,但是不能向下转
//计算机存储的是补码 正数的原码反码补码相同
// 10==> 01010 =>按位取反 10101==>得到后要转成补码,先取反码 ==>11010 再加一变成补码
// ==> 11011 ==>第一位为符号位所以是 -11
System.out.println(~a);
System.out.println(s);
注意:
- 0x 前缀的数字为 十六进制 数字. 十六进制可以看成是二进制的简化表示方式. 一个十六进制数字对
应 4 个二进制位. - 0xf 表示 10 进制的 15, 也就是二进制的 1111
- printf 能够格式化输出内容, %x 表示按照十六进制输出.
- \n 表示换行符
按位异或 ^: 如果两个数字的二进制位相同, 则结果为 0, 相异则结果为1
int a = 0x1;
int b = 0x2;
System.out.printf("%x\n", a ^ b);
6.移位运算
移位运算符有三个:
<< >> >>>
都是按照二进制位来运算。
左移 <<: 最左侧位不要了, 最右侧补 0。
int a = 0x10;
System.out.printf("%x\n", a << 1);
// 运行结果
20
右移 >>: 最右侧位不要了, 最左侧补符号位(正数补0, 负数补1)
int a = 0x10;
System.out.printf("%x\n", a >> 1);
// 运行结果(注意, 是按十六进制打印的)
8
无符号右移 >>>: 最右侧位不要了, 最左侧补 0
int a = 0xffffffff;
System.out.printf("%x\n", a >>> 1);
// 运行结果(注意, 是按十六进制打印的)
7fffffff
注意:
1. 左移 1 位, 相当于原数字 * 2. 左移 N 位, 相当于原数字 * 2 的N次方.
2. 右移 1 位, 相当于原数字 / 2. 右移 N 位, 相当于原数字 / 2 的N次方.
3. 由于计算机计算移位效率高于计算乘除, 当某个代码正好乘除 2 的N次方的时候可以用移位运算代替.
4. 移动负数位或者移位位数过大都没有意义
7. 条件运算符
条件运算符只有一个:
表达式1 ? 表达式2 : 表达式3
当 表达式1 的值为 true 时, 整个表达式的值为 表达式2 的值; 当 表达式1 的值为 false 时, 整个表达式的
值为 表达式3 的值.
也是 Java 中唯一的一个 三目运算符, 是条件判断语句的简化写法.
// 求两个整数的最大值
int a = 10;
int b = 20;
int max=a>b?a:b;
2.程序逻辑控制
1.顺序结构
顺序结构比较简单. 像我们之前写过的代码就是顺序结构的, 按照代码书写的顺序一行一行执行。
System.out.println("aaa");
System.out.println("bbb");
System.out.println("ccc");
// 运行结果
aaa
bbb
ccc
2.分支结构
2.1 if 语句
代码示例1: 判定一个数字是奇数还是偶数:
int num = 10;
if (num % 2 == 0) {
System.out.println("num 是偶数");
} else {
System.out.println("num 是奇数");
}
代码示例2: 判定一个数字是正数还是负数:
int num = 10;
if (num > 0) {
System.out.println("num 是正数");
} else if (num < 0) {
System.out.println("num 是负数");
} else {
System.out.println("num 是 0");
}
代码示例3: 判定某一年份是否是闰年:
int year = 2000;
if (year % 100 == 0) {
// 判定世纪闰年
if (year % 400 == 0) {
System.out.println("是闰年");
} else {
System.out.println("不是闰年");
}
} else {
// 普通闰年
if (year % 4 == 0) {
System.out.println("是闰年");
} else {
System.out.println("不是闰年");
}
}
注意事项1 悬垂 else 问题
int x = 10;
int y = 10;
if (x == 10)
if (y == 10)
System.out.println("aaa");
else
System.out.println("bbb");
if / else 语句中可以不加 大括号 . 但是也可以写语句(只能写一条语句). 此时 else 是和最接近的 if 匹配.
但是实际开发中我们 不建议 这么写. 最好加上大括号.
注意事项2 代码风格问题
// 风格1
int x = 10;
if (x == 10) {
// 满足条件
} else {
// 不满足条件
}
2.2 switch 语句
基本语法
switch(整数|枚举|字符|字符串){
case 内容1 : {
内容满足时执行语句;
[break;]
}
case 内容2 : {
内容满足时执行语句;
[break;]
}
...
default:{
内容都不满足时执行语句;
[break;]
}
}
代码示例: 根据 day 的值输出星期:
int day = 1;
switch(day) {
case 1:
System.out.println("星期一");
break;
case 2:
System.out.println("星期二");
break;
case 3:
System.out.println("星期三");
break;
case 4:
System.out.println("星期四");
break;
case 5:
System.out.println("星期五");
break;
case 6:
System.out.println("星期六");
break;
case 7:
System.out.println("星期日");
break;
default:
System.out.println("输入有误");
break;
}
根据 switch 中值的不同, 会执行对应的 case 语句. 遇到 break 就会结束该 case 语句.
如果 switch 中的值没有匹配的 case, 就会执行 default 中的语句.
我们建议一个 switch 语句最好都要带上default.
3. 循环结构
3.1 while 循环
基本语法格式:
while(循环条件){
循环语句;
}
循环条件为 true, 则执行循环语句; 否则结束循环.
代码示例1: 打印 1 - 10 的数字:
int num = 1;
while (num <= 10) {
System.out.println(num);
num++;
}
代码示例2: 计算 1 - 100 的和
int n = 1;
int result = 0;
while (n <= 100) {
result += n;
n++;
}
System.out.println(num);
// 执行结果
5050
3.2 break
break 的功能是让循环提前结束.
代码示例: 找到 100 - 200 中第一个 3 的倍数:
int num = 100;
while (num <= 200) {
if (num % 3 == 0) {
System.out.println("找到了 3 的倍数, 为:" + num);
break;
}
num++;
}
// 执行结果
找到了 3 的倍数, 为:102
执行到 break 就会让循环结束!!!
3.3 continue
continue 的功能是跳过这次循环, 立即进入下次循环
代码示例: 找到 100 - 200 中所有 3 的倍数
int num = 100;
while (num <= 200) {
if (num % 3 != 0) {
num++; // 这里的 ++ 不要忘记! 否则会死循环.
continue;
}
System.out.println("找到了 3 的倍数, 为:" + num);
num++;
}
执行到 continue 语句的时候, 就会立刻进入下次循环(判定循环条件), 从而不会执行到下方的打印语句.
3.4 for 循环
基本语法
for(表达式1;表达式2;表达式3){
循环体;
}
表达式1: 用于初始化循环变量.
表达式2: 循环条件
表达式3: 更新循环变量.
相比于 while 循环, for 循环将这三个部分合并在一起, 写代码时不容易遗漏。
代码示例1: 打印 1 - 10 的数字
for (int i = 1; i <= 10; i++) {
System.out.println(i);
}
3.5 do while 循环
基本语法
do{
循环语句;
}while(循环条件);
先执行循环语句, 再判定循环条件
代码示例: 打印 1 - 10
int num = 1;
do {
System.out.println(num);
num++;
} while (num <= 10);
注意事项
- do while 循环最后的分号不要忘记
- 一般 do while 很少用到, 更推荐使用 for 和 while.
3.方法的使用
Java语言中的“方法”(Method)在其他语言当中也可能被称为“函数”(Function)。对于一些复杂的代码逻辑,如果希望重复使用这些代码,并且做到“随时任意使用”,那么就可以将这些代码放在一个大括号“{}”当中,并且起一个名字。使用代码的时候,直接找到名字调用即可。
修饰符:修饰符,这是可选的,告诉编译器如何调用该方法。定义了该方法的访问类型。 返回值类型
:方法可能会返回值。returnValueType
是方法返回值的数据类型。有些方法执行所需的操作,但没有返回值。在这种情况下,returnValueType 是关键字void。
方法名:是方法的实际名称。方法名和参数表共同构成方法签名。
参数类型:参数像是一个占位符。当方法被调用时,传递值给参数。这个值被称为实参或变量。参数列表是指方法的参数类型、顺序和参数的个数。参数是可选的,方法可以不包含任何参数。
方法体:方法体包含具体的语句,定义该方法的功能。
1. 示例:计算 1! + 2! + 3! + 4! + 5!
1.1 不使用方法完成
public class Test {
public static void main(String[] args) {
int sum = 0;
for (int i = 1; i <= 5; i++) {
int tmp = 1;
for (int j = 1; j <= i; j++) {
tmp *= j;
}
sum += tmp;
}
System.out.println("sum = " + sum);
}
}
1.2 使用方法完成
public class Test {
public static int fac(int n) {
int f = 1;
for (int i = 1; i <= n; i++) {
f *= i;
}
return f;
}
public static void main(String[] args) {
int s = 0;
for (int i = 1; i <= 5; i++) {
s += fac(i);
}
System.out.println("sum = " + s);
}
}
2.方法的调用
方法的使用中,有以下几个重要的元素:
- 调用哪个方法。方法名称。
- 使用哪些具体的值,进行本次方法调用。调用时的实参(argument / actual parameter)。
- 调用方法后可能得到的返回值的后续处理。保存或者直接再次使用。
java 规定了方法定义的标准格式如下:
//不关心返回值
方法名称(实参列表);
// 将返回值保存到变量中
变量 = 方法名称(实参列表);
// 直接使用返回值参与运算
方法名称(实参列表) + 方法名称(实参列表);
3.重载
重载就是在一个类中,有相同的函数名称,但形参不同的函数。
方法重载的规则:方法名必须相同;参数列表必须不同(个数,类型,或参数顺序不同);方法的返回值可以相同也可以不相同;仅仅方 法返回类型不同不足以成为方法的重载。
实现理论:方法名相同时,编译器会根据调用方法的参数个数,参数类型等去逐个匹配,以选择对应的方法,若匹配失效,会报错。
代码示例:
package javaDailyProctice.ChongZai;
public class TestChong {
public static void main(String[] args) {
int a=10;
int b=20;
double c=10.0;
double d=20.0;
int result1=add(a,b);
double result2=add(c,d);
System.out.println(result1);
System.out.println(result2);
}
public static int add(int i,int y) {
return i+y;
}
public static double add(double i,double y){
return i+y;
}
}
运行结果:
4.数组的定义与使用
1.1 什么是数组
数组本质上就是让我们能 “批量” 创建相同类型的变量.
例如:
如果需要表示两个数据, 那么直接创建两个变量即可 int a; int b
如果需要表示五个数据, 那么可以创建五个变量 int a1; int a2; int a3; int a4; int a5;
但是如果需要表示一万个数据, 那么就不能创建一万个变量了. 这时候就需要使用数组, 帮我们批量
创建
1.2 创建数组
基本语法:
// 动态初始化
数据类型[] 数组名称 = new 数据类型 [] { 初始化数据 };
//静态初始化
数据类型[] 数组名称 ={ 初始化数据 };
代码示例:
int[] arr = new int[]{1, 2, 3};
int[] arr = {1, 2, 3};
1.3 数组的使用
int[] arr = {1, 2, 3};
// 获取数组长度
System.out.println("length: " + arr.length); // 执行结果: 3
// 访问数组中的元素
System.out.println(arr[1]); // 执行结果: 2
System.out.println(arr[0]); // 执行结果: 1
arr[2] = 100;
System.out.println(arr[2]); //执行结果:100
注意事项
- 使用 arr.length 能够获取到数组的长度. . 这个操作为成员访问操作符. 后面在面向对象中会经
常用到. - 使用 [ ] 按下标取数组元素. 需要注意, 下标从 0 开始计数
- 使用 [ ] 操作既能读取数据, 也能修改数据.
- 下标访问操作不能超出有效范围 [0, length - 1] , 如果超出有效范围, 会出现下标越界异常。
2. 数组作为方法的参数
2.1 基本用法
代码示例: 打印数组内容
public static void main(String[] args) {
int[] arr = {1, 2, 3};
printArray(arr);
}
public static void printArray(int[] a) {
for (int x : a) {
System.out.println(x);
}
}
// 执行结果
1
2
3
3. 数组作为方法的返回值
代码示例: 写一个方法, 将数组中的每个元素都 * 2
// 返回一个新的数组
class Test {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
int[] output = transform(arr);
printArray(output);
}
public static void printArray(int[] arr) {
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
public static int[] transform(int[] arr) {
int[] ret = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
ret[i] = arr[i] * 2;
}
return ret;
}
}
4. 二维数组
二维数组本质上也就是一维数组, 只不过每个元素又是一个一维数组.
基本语法:
数据类型[][] 数组名称 = new 数据类型 [行数][列数] { 初始化数据 };
代码示例:
int[][] arr = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
for (int row = 0; row < arr.length; row++) {
for (int col = 0; col < arr[row].length; col++) {
System.out.printf("%d\t", arr[row][col]);
}
System.out.println("");
}
// 执行结果
1 2 3 4
5 6 7 8
9 10 11 12
5.引用
1. 初步认识引用(reference)和对象(object)
String s = "Hello World"; // 这里的 s 的类型是 String 类型的引用;该引用指向了一个
String 类型的对象。
int[] a = { 1, 2, 3, 4, 5 }; // 这里的 a 的类型是 int[] 类型的引用;该引用指向了一个
元素类型是 int 的数组类型对象。
2. 理解引用和对象之间的关系
3. null 的理解
int[] a = null; // a 不指向任何的对象
4. 引用的赋值操作符理解
int[] a=null;
int[] b={1,2,3};
b=a; 让 b 指向 a 目前指向的对象
// 由于 a 目前不指向任何的对象。所以 b 也不再指向任何的对象
总结:
1.引用是一种数据类型,用来指向对象。
2. 对引用进行的大部分操作实际上都是操作的该引用指向的对象。
3. 当多个引用指向同一个对象时,通过哪个引用修改了对象,其他引用都可以看到变化
4. 当一个引用不指向对象时,要求访问其指向的对象,就会遇到 NullPointerException。
6.类和对象
1. 什么是面相对象
Java是一门纯面相对象的语言(Object Oriented Program,继承OOP),在面相对象的世界里,一切皆为
对象。面相对象是解决问题的一种思想,主要依靠对象之间的交互完成一件事情。用面相对象的思想来
涉及程序,更符合人们对事物的认知,对于大型程序的设计、扩展以及维护都非常友好。
2. 类定义和使用
// 创建类
class ClassName{
field;//成员属性
method;//成员方法
}
代码示例:
class Person {
public int age;//成员属性
public String name;
public String sex;
public void eat() {//成员方法
System.out.println("吃饭!");
}
public void sleep() {
System.out.println("睡觉!");
}
}
3. 类的实例化
3.1什么是实例化
定义了一个类,就相当于在计算机中定义了一种新的类型,与int,double类似,只不过int和double是
java语言自带的内置类型,而类是用户自定义了一个新的类型,比如上述的:Cat类和Computer类。有
了这些自定义的类之后,就可以使用这些类来定义实例(或者称为对象)。
用类类型创建对象的过程,称为类的实例化,在java中才用new关键字,配合类名来实例化对象。
class Person {
public int age;//成员属性 实例变量
public String name;
public String sex;
public void eat() {//成员方法
System.out.println("吃饭!");
}
public void sleep() {
System.out.println("睡觉!");
}
}
public class Main{
public static void main(String[] args) {
Person person = new Person();//通过new实例化对象
person.eat();//成员方法调用需要通过对象的引用调用
person.sleep();
//产生对象 实例化对象
Person person2 = new Person();
Person person3 = new Person();
}
}
3.2 类和对象的说明
- 类只是一个模型一样的东西,用来对一个实体进行描述,限定了类有哪些成员.
- 类是一种自定义的类型,可以用来定义变量,但是在java中用类定义出来的变量我们成为对象.
- 一个类可以实例化出多个对象,实例化出的对象 占用实际的物理空间,存储类成员变量
- 做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子,类就像是设计图,只设计出需
要什么东西,但是并没有实体的建筑存在,同样类也只是一个设计,实例化出的对象才能实际存储
数据,占用物理空间
4.类的成员
类的成员可以包含以下:字段、方法、代码块、内部类和接口等;重点要掌握前三个。
4.1 字段/属性/成员变量
在类中, 但是方法外部定义的变量. 这样的变量我们称为 “字段” 或 “属性” 或 “成员变量”(三种称呼都可以,一般不会有严格的区分)
用于描述一个类中包含哪些数据:
class Person {
public String name;// 字段
public int age;
}
class Test {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name);
System.out.println(person.age);
}
}
// 执行结果
null
0
4.2方法 (method)
用于描述一个对象的行为:
class Person {
public int age = 18;
public String name = "张三";
public void show() {
System.out.println("我叫" + name + ", 今年" + age + "岁");
}
}
class Test {
public static void main(String[] args) {
Person person = new Person();
person.show();
}
}
// 执行结果
我叫张三, 今年18岁
5.包
包 (package) 是组织类的一种方式,使用包的主要目的是保证类的唯一性。
5.1导入包中的类
Java 中已经提供了很多现成的类供我们使用. 例如:
public class Test {
public static void main(String[] args) {
java.util.Date date = new java.util.Date();
// 得到一个毫秒级别的时间戳
System.out.println(date.getTime());
}
}
可以使用 java.util.Date 这种方式引入 java.util 这个包中的 Date 类。但是这种写法比较麻烦一些, 可以使用 import 语句导入包
import java.util.Date;
public class Test {
public static void main(String[] args) {
Date date = new Date();
// 得到一个毫秒级别的时间戳
System.out.println(date.getTime());
}
}
5.2访问限定符
Java 中对于字段和方法共有四种访问权限
private: 类内部能访问, 类外部不能访问
默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问.
protected: 类内部能访问, 子类和同一个包中的类可以访问, 其他类不能访问.
public : 类内部和类的调用者都能访问
7.认识String类
8.面向对象编程
1. 继承
继承(inheritance)机制:是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原
有类特 性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的
层次结构, 体现了由简单到复杂的认知过程。
1.1 语法规则
class 子类 extends 父类 {
}
1.2代码示例
package com.oop.Demo04;
public class Person {
//public 公共的
//protect 受保护的
//default 默认的(不写)
//private 私有的
public int money = 100000;
private int money1 = 100;
public void say(){
System.out.println("啊啊啊啊");
}
public int getMoney1() {
return money1;
}
public void setMoney1(int money1) {
this.money1 = money1;
}
}
package com.oop.Demo04;
//子类继承父类就会拥有父类的所有方法
public class Student extends Person{
}
package com.oop.Demo04;
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.say();//调用父类方法
student.getMoney1();//调用父类私有属性
System.out.println(student.getMoney1());
}
}
package com.oop.Demo05;
public class Person {
public Person() {
System.out.println("person无参执行了");
}
public void print(){
System.out.println("Person");
}
}
package com.oop.Demo05;
//子类继承父类就会拥有父类的所有方法
public class Student extends Person {
public Student() {
//默认调用父类的无参构造
//super()调用父类的构造方法,必须在构造方法的第一个
System.out.println("Student无参执行了");
}
public void print(){
System.out.println("Student");
}
public void test1(){
print();//Stdent
this.print();//Student
super.print();//Teacher
}
}
package com.oop.Demo05;
public class Application {
public static void main(String[] args) {
Student student = new Student();
//student.test1();
}
}
** 总结:
1.super()调用父类的构造方法,必须在构造方法的第一个。
2.super必须只能出现在子类的方法或者构造方法中。
3.super和this不能同时调用构造方法。
4.this:本身调用者这个对象;super代表父类的对象 。
5.this在没有继承时也i可以使用,super只能在继承条件下才可以使用。
6.this():本类的构造;super():父类的构造。**
1.3重写
有继承关系,子类重写父类的方法,方法名相同方法体不同,非静态的方法才可以重写。
必要条件:方法名必须相同,参数列表必须相同,修饰符范围可以扩大不能缩小。抛出的异常范围可以被缩小不能被放大。
1.3.1静态方法
ackage com.oop.Demo06;
public class B {
public static void test(){
System.out.println("B>>test()");
}
}
ackage com.oop.Demo06;
public class A extends B{
public static void test(){
System.out.println("A>>test()");
}
}
package com.oop.Demo06;
public class Application {
public static void main(String[] args) {
A a = new A();
a.test();//A>>test()
//父类的引用指向子类
B b = new A();
b.test();//B>>test()
}
}
1.3.2非静态方法
package com.oop.Demo06;
public class B {
public void test(){
System.out.println("B>>test()");
}
}
package com.oop.Demo06;
public class A extends B{
//重写
@Override //注解 有功能的注释
public void test(){
System.out.println("A>>test()");
}
}
package com.oop.Demo06;
public class Application {
public static void main(String[] args) {
//静态方法和非静态方法区别很大
A a = new A();
a.test();//A>>test()
//父类的引用指向子类
B b = new A();//子类重写了父类的方法
b.test();//A>>test()
}
}
2 组合
和继承类似, 组合也是一种表达类之间关系的方式, 也是能够达到代码重用的效果.
例如表示一个学校:
public class Student {
...
}
public class Teacher {
...
}
public class School {
public Student[] students;
public Teacher[] teachers;
}
组合并没有涉及到特殊的语法(诸如 extends 这样的关键字), 仅仅是将一个类的实例作为另外一个类的字
段.
这是我们设计类的一种常用方式之一.
组合表示 has - a 语义
在刚才的例子中, 我们可以理解成一个学校中 “包含” 若干学生和教师.
继承表示 is - a 语义
在上面的 “动物和猫” 的例子中, 我们可以理解成一只猫也 “是” 一种动物.
大家要注意体会两种语义的区别
3 多态
3.1 向上转型
代码示例:
package javaDailyProctice.ChongZai;
class Animal {
String name;
public Animal(String name) {
this.name = name;
}
}
class Bird extends Animal{
public Bird(String name) {
super(name);
System.out.println(name);
}
}
public class MainTest {
public static void main(String[] args) {
Animal animal=new Bird("圆圆");
}
}
此时animal 是一个父类 (Animal) 的引用, 指向一个子类 (Bird) 的实例. 这种写法称为 向上转型.
3.2动态绑定
代码示例:
// Animal.java
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println("我是一只小动物");
System.out.println(this.name + "正在吃" + food);
}
}
// Bird.java
public class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void eat(String food) {
System.out.println("我是一只小鸟");
System.out.println(this.name + "正在吃" + food);
}
}
// Test.java
public class Test {
public static void main(String[] args) {
Animal animal1 = new Animal("圆圆");
animal1.eat("谷子");
Animal animal2 = new Bird("扁扁");
animal2.eat("谷子");
}
}
// 执行结果
我是一只小动物
圆圆正在吃谷子
我是一只小鸟
扁扁正在吃谷子
此时, 我们发现:
animal1 和 animal2 虽然都是 Animal 类型的引用, 但是animal1 指向 Animal 类型的实例,animal2 指向 Bird 类型的实例.针对 animal1 和 animal2 分别调用 eat 方法, 发现 animal1.eat() 实际调用了父类的方法, 而animal2.eat() 实际调用了子类的方法.
因此, 在 Java 中, 调用某个类的方法, 究竟执行了哪段代码 (是父类方法的代码还是子类方法的代码) , 要看究竟这个引用指向的是父类对象还是子类对象. 这个过程是程序运行时决定的(而不是编译期), 因此称为动态绑定.
3.3重载和重写的区别
3.4向下转型
向上转型是子类对象转成父类对象, 向下转型就是父类对象转成子类对象. 相比于向上转型来说, 向下转型没那么常见, 但是也有一定的用途。
代码示例:
// Animal.java
public class Animal {
protected String name;
public Animal(String name) {
this.name = name;
}
public void eat(String food) {
System.out.println("我是一只小动物");
System.out.println(this.name + "正在吃" + food);
}
}
// Bird.java
public class Bird extends Animal {
public Bird(String name) {
super(name);
}
public void eat(String food) {
System.out.println("我是一只小鸟");
System.out.println(this.name + "正在吃" + food);
}
public void fly() {
System.out.println(this.name + "正在飞");
}
}
接下来是我们熟悉的操作:
Animal animal = new Bird("圆圆");
animal.eat("谷子");
// 执行结果
圆圆正在吃谷子
接下来我们尝试让圆圆飞起来:
animal.fly();
// 编译出错
找不到 fly 方法
注意事项
编译过程中, animal 的类型是 Animal, 此时编译器只知道这个类中有一个 eat 方法, 没有 fly 方法.虽然 animal 实际引用的是一个 Bird 对象, 但是编译器是以 animal 的类型来查看有哪些方法的.对于 Animal animal = new Bird(“圆圆”) 这样的代码,编译器检查有哪些方法存在, 看的是 Animal 这个类型
执行时究竟执行父类的方法还是子类的方法, 看的是 Bird 这个类型.那么想实现刚才的效果, 就需要向下转型
// (Bird) 表示强制类型转换
Bird bird = (Bird)animal;
bird.fly();
// 执行结果
圆圆正在飞
但是这样的向下转型有时是不太可靠的. 例如:
Animal animal = new Cat("小猫");
Bird bird = (Bird)animal;
bird.fly();
// 执行结果, 抛出异常
Exception in thread "main" java.lang.ClassCastException: Cat cannot be cast to
Bird
at Test.main(Test.java:35)
animal 本质上引用的是一个 Cat 对象, 是不能转成 Bird 对象的. 运行时就会抛出异常.所以, 为了让向下转型更安全, 我们可以先判定一下看看 animal 本质上是不是一个 Bird 实例, 再来转换
Animal animal = new Cat("小猫");
if (animal instanceof Bird) {
Bird bird = (Bird)animal;
bird.fly();
}
instanceof 可以判定一个引用是否是某个类的实例. 如果是, 则返回 true. 这时再进行向下转型就比较
安全了.