1. 环境的搭建
CSDN
1.1. 常用的DOS命令
怎么打开DOS命令窗口
win键 + r (组合键):可以打开“运行”窗口在运行窗口文本框中输入: cmd怎么打开画图
mspaint 画图软件
calc 计算器
- 常用命令
- mkdir abc ( 这个命令不是必须掌握的) make directory(创建目录)
含义:创建一个目录,起名abc
- cd 路径
路径在windows系统上包括:相对路径和绝对路径。
相对路径:是以当前路径为起点
绝对路径:是以盘符为起点
注意:
cd . 保留在当前路径
cd .. 回到上级路径。
cd \ 直接回到根路径。
. 一个点,代表当前路径。
cls 清屏
dir 查看当前目录下有啥东西。
exit 退出DOS命令窗口。
del 删除命令
del .class 精确删除 __ del .ass 模糊删除ipconfig(ip地址的配置信息。)
ipconfig /all 该命令后面添加一个/all参数可以查看更详细的网络信息。ping命令 检查两个电脑是否能正常通信语法格式:
ping IP地址 或者ping 域名java -version 查看编译器版本
c: 切换盘符 盘符
javac *.java 编译该文件夹下所有的.Java、结尾的文件
- (java -X) 这个可以查看调整堆栈大小的参数X要大写
1.2. 常用文本的快捷键与Markdown语法
- win+shift+s windos系统截图
1.3. JDK、JRE、JVM三者之间的关系
JDK:java开发工具箱
JRE:Java运行环境
JVM:Java虚拟机
JVM是不能独立安装的。JRE 和 JDK 都是可以独立安装的。
有单独的JDK安装包,也有单独的JRE安装包,没有单独的JVM安装包。
思考题:如果想让编写号的程序,在别用户的电脑上面跑起来应该应该给用户安装什么环境?
答:安装 JRE环境即可
1.4. java语言的特点
- 简单性
- 健壮性
Gc回收机机制缺点:不能及时回收 要攒够一定的量才能回收 - 多线程
- 面向对象
- 跨平台
优点:一次编写到处运行,可以跨平台。Java不容易导致内存的泄漏
缺点:麻烦。对于运行java程序来说必须先有一个JVM。就像你要想在网页上看视频,你必须先安 装一个flash是一样的。
1.5. java执行原理
- 两个重要阶段:
- 编译阶段: Javac 源文件路径 ( javac HelloWrold.java )
- 运行阶段:Java 类名 ( java HelloWorld )
- 执行原理图解
java HelloWorld的执行过程以及原理
第一步:会先启动JVM(java虚拟机)
第二步:JVM启动之后,JVM会去启动“类加载器classloader”
类加载器的作用:加载类的。默认情况下类加载器(classloader)会从当前路径下找。
类加载器如果在硬盘上找到了对应的字节码文件,类加载器会将该字节码文件装载到JVM当中,JVM启动“解释器”将字节码解释“101010000…”这种二进制码,操作系统执行二进制码和硬件交互。
说明:类加载器如果在当前路径下找不到对应的字节码文件,会报错,报什么错?
错误: 找不到或无法加载主类
1.6. 计算机的原理
- 计算机永远存储的是二进制的补码形式,二进制最左边的数字代表正负,若为0即为整数,若为1即为负数。
- 计算机只能识别二进制二进制与十进制之间转换
- 二进制转换为十进制
- 二进制 00000101 转换为十进制
- 十进制转换为二进制
- 十进制 125 转换为 二进制 :
- 短除法 办法:除以二 ,然后余数逆序输出逆序输出 1111101
1.8. 二进制的原码、补码与反码 (了解)
2. 标识符与关键字
2.1. 标识符
2.1.1. 什么是标识符
程序员有权利命名的单词都是标识符
2.1.2 标识符命名规则
- 标识符首字母只能为英文、下划线、$、中文
- 标识符只能出现英文、下划线、$、中文、数字
- 关键字不能为标识符
- 标识符严格区分大小写
- 标识符理论上没有长度限制
- java中的标识符严格区分大小写的说明:
作用域不同标识符可以重名 - 但是对于类名来说,如果一个java源文件中同时出现了:B类和b 类,那么谁在前就生成谁。大家以后最好不 要让类名“相同”,最好类名是不同的。
- java中的标识符严格区分大小写的说明:
2.1.3. 标识符的命名规范
- 变量名、方法名:首字母小写第二个因为字母首字母大写
- 类名、接口名:首字母大写第二个英文单词首字母大写
- 常量名:所有字母大写,第一个与第二个字母之间用下划线连接
- 均遵守驼峰命名法
2.2. 关键字
2.2.1. 什么是关键字
- sun公司提前编写好具有特殊含义的词
- 关键字不把你作为标识符
- 关键字全部小写
3. 变量
3.1. 什么是变量
- 内存中存储数据的最基本的单元
- 变量的三要素:变量的数据类型、变量的名字、变量中保存的值
- 变量的数据类型:决定数据的空间大小
- 变量的名字:方便程序员调用
- 变量的值:变量存储的数据
- 在java语言中有一个规定,变量必须先声明,再赋值才能访问。(没有值相当于这个空间没有开辟。)
- int n1=100,n2=200,n3 = 300 ; // 一行上面可以声明多个变量的正确方式
public class VarTest1{
public static void main(String args){
int age ;
}
}
/*
在这里的age系统会默认赋值为0 所以符合Java语法,系统不会报错
若为String 类型的变量这里默认为 null
但若要输出age就会报错,未完成变量的初始化
*/
public class VarTest2{
public static void main(String[] args){
/*
int n1,n2,n3 = 300 ; // 错误的一行上面可以声明多个变量
System.out.println(n3);
System.out.println(n2);
System.out.println(n1);
java允许一行申明多个变量,若 为 int a,b,c = 300 ;输出c 有值300,其余
两未赋值如输出 a, b 会报错 未完成初始化,若只输出System.out.println(c);不会报
错
*/
//正确一行声明多个变量的方法
int n1=100,n2=200,n3 = 300 ; // 一行上面可以声明多个变量的正确方式
System.out.println(n3);
System.out.println(n2);
System.out.println(n1);
}
}
变量的作用域
- 成员变量:类体中声明的变量
- 局部变量:方法体内声明的变量
- } 为 一个作用域,关于变量的有效范围:出了大括号就不认识了。
总结:
在java语言中“数据”被称为“字面量”。
- 1个字节=8个比特位
1个比特位就是一个1或0.
注意:比特位是二进制位。
int i = 1; 实际上在内存中是这样表示的:
00000000 00000000 00000000 00000001
int i = 2;
00000000 00000000 00000000 00000010
4.1数据类型
- 数据类型用来声明变量,程序在运行过程中根据不同的数据类型分配不同大小的空间。
- 数据类型分类:
- 基本数据类型:八种基本数据类型 | 数据类型 | 占用字节数量(byte) | 取值范围 | | —- | —- | —- | | byte | 1 | [-128,127] | | shot | 2 | [-32768,32767] | | int | 4 | [-2147483648,2147483647] | | long | 8 | | | float | 4 | | | double | 8 | | | boolean | 1 | true为(00000001) | | char | 2 | [0,65535] |
- 说明:
- short和char实际上容量相同,不过char可以表示更大的数字。因为char表示的是文字,文字 没有正负之分,所以char可以表示更大的数字。
- 引用数据类型:字符串型String属于引用数据类型
- java中除了基本数据类型之外,剩下的都是引用数据类型。
4.2. 字符编码
- 字符编码是人为的定义的一套转换表。
- 在字符编码中规定了一系列的文字对应的二进制
- 编码和解码的时候必须采用同一套字符编码方式,不然就会出现乱码。需要记住的字符编码名称:
- ASCII(‘a’ 是97 ‘A ‘是65 ‘0 ‘是47)
- GBK 编 码方式简体中文
- Big5 繁体中文
- utf unicode编码统一了全球所有的文字,支持所有文字。
4.3. 转义字符
- java语言中“\”负责转义。
- ‘\t’ 表示一个制表位
System.out.println("adc\tdef");
//打印出来的结果为:adc tdef
// \t 表示一个TAB键
- ‘\u4e2d’
char x ='\u4e2d' ;
System.out.println(X);
//输出的结果为'中'
- ‘\n’表示换行
System.out.print('\n');
//表示换一行
😄在控制台输出一个 ‘ 字符怎么办?
System.out.println("'");
System.out.println('\'');
😄 在控制台输出一个 \ 字符怎么办?
System.out.println('\\');
4.4. 基本数据类型
- 字符型字面量
char占用两个字节,可以存储一个汉字 - 整数型字面量
- byte 、 short 、int 、long
对于以上的四个类型来说,最常用的是int。开发的时候不用斤斤计较,直接选择使用int就行了。 - 在任何情况下,整数型的字面量默认被当做int类型处理。(记住就行)
- 如果希望该“整数型字面量”被当做long类型来处理,需要在“字面量”后面添加L
- 任意一个浮点型都比整数型空间大。
- Boolean型字面量
在java语言中boolean类型只有两个值:true和false。没有其它值( 00000001 表示 true 00000000 表示false) - 浮点型字面量
- float是单精度,double是双精度,double更精确float和double存储数据的时候都是存储的近似值。
思考:0.6 - 0.3 =0.30000001
在银行方面或者说使用在财务方面,double也是远远不够的,在java中提供了一种精度更高 的类型,这种类型专门使用在财务软件方面:java.math.BigDecimal (不是基本数据类型, 属于引用数据类型。)
java中规定,任何一个浮点型数据默认被当做double来处理。如果想让这个浮点型字面量被当做float类型来处理,那么请在字面量后面添加F/f。
4.5. 数据类型转换
- 基本数据类型转换
- 小容量可以直接赋值给大容量,称为自动类型转换。
大容量不能直接赋值给小容量,需要使用强制类型转换符进行强转。但需要注意的是:加强 制类型转换符之后,虽然编译通过了,但是运行的时候可能会损失精度。
java中有一个语法规则:当这个整数型字面量没有超出byte、short、char的取值范围,那么 这个整数型字面量可以直接赋值给byte、short、char类型的变量。这种语法机制是为了方便 写代码,而存在的。
目前 double 类型转换成 int 类型的强制转换不需要四舍五入直接舍去小数点后面的任何数
- 整数型的混合运算
- byte、short、char的混合运算
结论:byte、char、short做混合运算的时候,各自先转换成int再做运算。
- 多种数据类型
多种数据类型做混合运算的时候,最终的结果类型是“最大容量”对应的类型。char+short+byte 这个除外。 因为char + short + byte混合运算的时候,会各自先转换成int再做运算。
- 总结:
- 八种基本数据类型中,除 boolean 类型不能转换,剩下七种类型之间都可以进行转换;
- 如果整数型字面量没有超出 byte,short,char 的取值范围,可以直接将其赋值给byte,short,char 类型的变量;
- 小容量向大容量转换称为自动类型转换,容量从小到大的排序为:
byte < short(char) < int < long < float < double
其中 short和 char 都占用两个字节,但是char 可以表示更大的正整数; - 大容量转换成小容量,称为强制类型转换,编写时必须添加“强制类型转换符”,但运行时可能出现精度损失,谨慎使用;
- byte,short,char 类型混合运算时,先各自转换成 int 类型再做运算;
- 多种数据类型混合运算,各自先转换成容量最大的那一种再做运算;
- 所有的笔试题都超不出以上的6条规则。死记硬背。
5. 运算符
5.1 算术运算符
- 运算符号:
小数运算特点:对于小数运算出来的结果都是一个近似值 | +求和 | * 乘积 | ++ 自加1 | | —- | —- | —- | | - 相减 | / 商 | — 自减1 | | % 求余数(求模) | | |
- 对于++运算符来说:可以出现在变量前,也可以出现在变量后。
- 不管出现在变量前还是后,总之++执行结束之后,变量的值一定会自加1。
当++出现在变量后面先赋值在运算,出现在变量的前面先运算再复值
出现在变量后面
int a = 10 ;
int b = a++ ;
System.out.println(b);//此时b的值为:10
System.out.println(a);//此时a的值为:11
当++出现在变量前先运算在赋值
int i = 20 ;
int c = ++i ;
System.out.println(c);//此时c的值为:21
System.out.println(i);//此时i的值为:21
😄 :分析以下代码
int c = 90 ;
System.out.println(c++); // 输出结果为90
/*
分析如下:
拆分第二行代码得:
int temp = c++
System.out.println(temp);
即temp的值为90
*/
% 小数求余数公式
5.2. 关系运算符
= < <= == !=
- 所有的关系运算符的运算结果都是布尔类型,不是true就是false,不可能是其他值
- 在java语言中: = : 赋值运算符 == :关系运算符,判断是否相等
- 关系运算符中如果有两个符号的话,两个符号之间不能有空格。
5.3. 逻辑运算符
- & 逻辑与(可以翻译成并且)
对于逻辑与&运算符来说,只要有一边是false,结果就是false。 只有两边同时为true,结果才是true。 - | 逻辑或(可以翻译成或者)
对于逻辑或呢只要有一边是true,结果就是true。 - ! 逻辑非(取反)
- 逻辑运算符两边要求都是布尔类型,并且最终的运算结果也是布尔类型。这是逻辑运算符的特点。
- && 短路与
- || 短路或
- 短路现象:右边表达式不执行,这种现象叫做短路现象。
- 什么时候发生短路或现象
- || 短路或
- “或”的时候只要有一边是true,结果就是true。所以,当左边的表达式结果是true的时候,右边的表达式不需要执行,此时会短路。
- 什么时候使用&&,什么时候使用&
- 从效率方面来说,&&比&的效率高一些。因为逻辑与&不管第一个表达式结果是什么,第二个表达式一定会执行。
- 以后的开发中,短路与&&和逻辑与还是需要同时并存的。大部分情况下都建议使用短路与 &&只有当既需要左边表达式执行,又需要右边表达式执行的时候,才会选择逻辑与&。其余同理
5.4. 赋值运算符
- 赋值运算符包括“基本赋值运算符”和“扩展赋值运算符”:基本的、扩展的。
- 基本赋值运算符: =
- 扩展的赋值运算符:
+= -= *= /= %=
注意: 扩展赋值运算符在编写的时候,两个符号之间不能有空格。 + = 错误的。 += 正确的。
- 使用扩展赋值运算符的时候,永远都不会改变运算结果类型。
- 问题:byte i = 100 ; i += 10 和 i = i + 10 真的是完全一样吗?
答案:不一样,只能说相似,其实本质上并不是完全相同。
i += 110 ; // 可以理解为 :i=(byte)(100+110) 超出了 byte的取值范围 会自然损失精度
5.5. 条件运算符(三目运算符)
- 语法格式: 布尔表达式 ? 表达式1 : 表达式2
- 执行原理:
布尔表达式的结果为true时,表达式1的执行结果作为整个表达式的结果。
布尔表达式的结果为false时,表达式2的执行结果作为整个表达式的结果。 - 分析以下代码是否存在语法错误
5.6. +运算符
+运算符在java语言中有两个作用。
作用1:求和
作用2:字符串拼接什么时候求和?什么时候进行字符串的拼接呢?
当 + 运算符两边都是数字类型的时候,求和。
当 + 运算符两边的“任意一边”是字符串类型,那么这个+会进行字符串拼接操作。字符串拼接完之后的结果还是一个字符串。当一个表达式当中有多个加号的时候遵循“自左向右”的顺序依次执行。
5.7. 键盘输入
- 方法一:
import java.util.Scanner; //导入Scanner扫描仪
public clss Test{
public static void main(String[] args){
Scanner n = new Scanner(System.in); //创建键盘扫描对象n,n可以改动
System.out.print("请输入一个数字:
"); //向控制台输出一个欢迎信息
int num = n.nextInt(); //从键盘接收一个int类型的变量,然后复制一份值,赋
值给 num
}
}
- 方法二:
public clss Test{
public static void main(String[] args){
java.util.Scanner n = new java.util.Scanner(System.in); //创建键盘
扫描对象
System.out.print("请输入一个数字:
"); //向控制台输出一个欢迎信息
int number = n.nextInt();
}
}
6. 控制语句
6.1. 控制语句
- 什么是控制语句:控制语句的出现可以让我们的程序具有逻辑性/条理性,可以使用控制语句来实 现一个“业务”了。
- 控制语句包括三类:
选择语句(分支语句): if语句 switch语句
循环语句:主要循环反复的去执行某段特定的代码块。 for循环 while循环 do..while..循环
转向语句:break continue return
6.2. 分支语句
- if语句
if语句是分支语句,也可以叫做条件语句。
- 对于if语句来说,在任何情况下只能有1个分支执行,不可能存在2个或者更多个分支执行。
- if语句 中只要有1个分支执行了,整个if语句就结束了。(对于1个完整的if语句来说的。)
- 4种语法机制中,凡是带有else分支的,一定可以保证会有一个分支执行。
- { }只有1条语句时,那么大括号{}可以省略,但为了可读性,最好不要省略。
- 控制语句和控制语句之间是可以嵌套的,但是嵌套的时候大家最好一个语句一个语句进行分析,不 要冗杂在一起分析。
- 程序分析
- switch语句
- switch语句的语法格式:
- switch后面的()里面不能出现空格等其它字符,()里面支持int类型以及String类型。
- switch语句中“值”与“值1”、“值2”比较的时候会使用“==”进行比较。
- switch语句的执行原理:
拿“值”与“值1”进行比较,如果相同,则执行该分支中的java语句,然后遇到”break;”语句,switch语句就结束了。
注意:如果分支执行了,但是分支最后没有“break;”,此时会发生case穿透现象。
所有的case都没有匹配成功,那么最后default分支会执行。
- 语法:
- 关于case合并的写法:
case 值1 : case 值2 :
java语句;
breal;
6.3. 循环语句
- for循环
- 语法机制:
for(初始化表达式; 条件表达式; 更新表达式){ 循环体; // 循环体由java语句构成
java语句; java语句;
….
} - 注意:
- 第一:初始化表达式最先执行,并且在整个循环中只执行一次。
- 第二:条件表达式结果必须是一个布尔类型,也就是:true或false
- 第三:for循环结束内存就释放了
- 执行原理:
先执行初始化表达式,并且初始化表达式只执行1次。
然后判断条件表达式的结果,如果条件表达式结果为true,则执行循环体。
循环体结束之后,执行更新表达式。更新完之后,再判断条件表达式的结果,如果还是true,继续 执行循环体。
直到更新表达式执行结束之后,再次判断条件时,条件为false,for循环终止。 - 更新表达式的作用是:控制循环的次数,换句话说,更新表达式会更新某个变量的值,这样条件表达式的结果才有可能从true变成false,从而终止for循 环的执行,如果没有更新表达式,很有可能会导致死循环。
- 初始化表达式、条件表达式、更新表达式 其实都不是必须的!!!
- 分析以下程序:
分析:15行 语法格式错误
改正: 将红线的number 改成 int i = number
只能出现这种语法格式 (int number= 0其 余不对或者将number 去除
- while语句
- while循环的语法机制以及执行原理语法机制:
while(布尔表达式){
循环体;
} - 执行原理:
判断布尔表达式的结果,如果为true就执行循环体,循环体结束之后,再次判断布尔表达 式的 结果,如果还是true,继续执行循环体,直到布尔表达式结果为false,while循环结束。 - while的循环次数:0~n次。
- for与while可以互换,只是语法格式不同
- do while 循环
- 语法机制:
do {
循环体;
} while(布尔表达式);
注意:do..while循环最后的时候别漏掉“分号”
执行原理:(先执行在判断)
**对于do..while循环来说,循环体至少执行1次。循环体的执行次数是:1~n次。**
6.4. 转向语句
- break语句
break语句比较特殊,特殊在:break语句是一个单词成为一个完整的java语句。另外:continue也是这样,他俩都是一个单词成为一条语句。
break 翻译为折断、弄断。
break;语句可以用在哪里呢?
- switch语句当中,用来终止switch语句的执行。
- 用在switch语句当中,防止case穿透现象,用来终止switch。
- break;语句用在循环语句当中,用来终止循环的执行。以下程序主要是以for循环为例学习break转向语句。
break;语句的执行并不会让整个方法结束,break;语句主要是用来终止离它最近的那个循环语句。
- break语句终止指定的循环: 语法 :
- 终止了外层的 for循环,这种方法用来终止指定的循环
- continue语句
- continue翻译为:继续
- continue语句的作用是:终止当前”本次”循环,直接进入下一次循环继续执行。
- continue语句指定循环
- return
- 用来终止当前方法的执行
7. 方法
7.1. 方法
- 方法的语法:
- 方法(英语单词: method )是可以完成某个特定功能的并且可以被重复利用的代码片段。
在C语言中,方法被称为“函数”。在java中不叫函数,叫做方法。 - 对于一个java程序来说,如果没有“方法”,代码无法得到复用。(怎么提高复用性,可以定义方法, 然后需要使用该功能的时候,直接调用一下方法即可。这样代码就得到复用了。)
- 程序开始执行的时候是先执行main方法。因为main方法是一个入口。
- 在java语言中所有的方法体中的代码都必须遵循自上而下顺序依次逐行执行。
- main方法不需要程序员手动调用,是由JVM调用的。
- 但是除了main方法之外其他的方法,都需要程序员手动调用,方法只有调用的时候才会执行 ,方法不调用是不会执行的。
- 语法机制:
[修饰符列表] 返回值类型 方法名( 形式参数列表 ) {
方法体;
}
注意:
- 符号叫做中括号,以上中括号 [ ] 里面的内容表示不是必须的,是可选的。方法体由Java语句构成。
- 方法定义之后需要去调用,不调用是不会执行的。
- 关于修饰符列表:修饰符列表不是必选项,是可选的。目前为止,大家统一写成:public static后面你就理解应该怎么写了。
- 关于返回值类型:
第一:返回值类型可以是任何类型,只要是java中合法的数据类型就行。
第二:什么是返回值?
- 返回值一般指的是一个方法执行结束之后的结果。结果通常是一个数据,所以被 称为“值”,而且还叫“返回值”。
- 方法就是为了完成某个特定的功能,方法结束之后大部分情况下都是有一个结果 的,而体现结果的一般都是数据。数据得有类型。这就是返回值类型。
- 方法执行结束之后的返回值实际上是给调用者了,谁调用就返回给谁。
第三:当一个方法执行结束不返回任何值的时候,返回值类型也不能空白,必须写上void关键字。所 以void表示该方法执行结束后不返回任何结果。
第四:如果返回值类型“不是void”,那么你在方法体执行结束的时候必须使用"return值;"这样的语句来完成“值”的返回,如果没有“return 值;”这样的语句那么编译器会报错。
- return 值; 这样的语句作用是什么?
作用是“返回值”,返回方法的执行结果。
第五:只要有“return”关键字的语句执行,当前方法必然结束。return只要执行,当前所在的方法结束,记住:不是整个程序结束。return后面不能跟java语句。
第六:如果返回值类型是void,那么在方法体当中不能有“return 值;”这样的语句。但是可以有”return;”语句。这个语句“return;”的作用就是用来终止当前方法的。
第七:除了void之外,剩下的都必须有“return 值;”这样的语句。
- 方法名称
方法名要见名知意。(驼峰命名方式)
方法名在标识符命名规范当中,要求首字母小写,后面每个单词首字母大写。只要是合法的标识符就行。
形式参数列表
简称:形参
注意:形式参数列表中的每一个参数都是局部变量,方法结束之后内存释放。形参的个数是:0~N个。
形参有多个的话使用“逗号,”隔开。逗号是英文的。
形参的数据类型起决定性作用,形参对应的变量名是随意的。形参对应数据可以与形参数据类型不同
实参要与形参数据相匹配方法体
- 由Java语句构成。java语句以“;”结尾。
- 方法体当中编写的是业务逻辑代码,完成某个特定功能。
- 在方法体中的代码遵循自上而下的顺序依次逐行执行。
- 在方法体中处理业务逻辑代码的时候需要数据,数据来源就是这些形参。
方法定义之后怎么调用呢?
- 方法必须调用才能执行。
- 调用语法:
类名.方法名(实际参数列表); - 在同一个类当中,调用方法可以不加类名。
- 实参和形参的类型必须一一对应,另外个数也要一一对应。
- 方法定义应在类体中,在类体中没有顺序的要求
方法调用的代码优化
也可以 直接System.out.println(sum(100,200));
- 在方法调用的时候,什么时候“类名.”是可以省略的。什么时候不能省略?
a()方法调用b()方法的时候,a和b方法都在同一个类中,“类名.”可以省略。如果不在同一个类中 “类名.”不能省略。 - 注意:
- 任何一个方法体当中的代码都是遵循自上而下的顺序依次逐行执行的。
- main方法最先执行, 并且 main方法是最后一个结束。
- main结束,整个程序就结束了。调用程序不一定写到main方法中,不要把main方法特殊化。main方法也是一个普通方法。
- break;语句和return;语句有什么区别?
不是一个级别。- break;用来终止switch和离它最近的循环。
- return;用来终止离它最近的一个方法。
- return语句后面不能跟代码 否则编译器会报错。
- 分析以下代码
![](https://gitee.com/hei-yubai/javase/raw/master/img/202203041349901.png#crop=0&crop=0&crop=1&crop=1&id=M9u4i&originHeight=410&originWidth=895&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
7.2. jvm内存结构图
- javac 命令是编译期
- 将字节码文件装载是运行期(即Java命令后)
- 要明白这个的原理:
Jvm内存重要的三块内存结构图:
- 画图时在内存中应该只能有数据,不能有代码,出现代码是为了 方便理解。
- JVM中栈数据结构详解:
7.3. 代码的升级
- 第一个
7.4. 方法的重载
- 方法重载的详解
使用方法重载机制。解决之前的两个缺点。
优点1:代码整齐美观。
优点2:“功能相似”的,可以让“方法名相同”,更易于以后的代码编写。 在java语言中,是怎么进行方法区分的呢?
首先java编译器会通过方法名进行区分。
但是在java语言中允许方法名相同的情况出现。如果方法名相同的情况下,编译器会通过方法的参数类型进行方法的区分。eg:
什么时候需要考虑使用方法重载?
在同一个类当中,如果“功能1”和“功能2”它们的功能是相似的,那么可以考虑将它们的方法名一 致,这样代码既美观,又便于后期的代码编写(容易记忆,方便使用)。注意:方法重载 **overload** 不能随便使用,如果两个功能压根不相干,不相似,根本没关系,此时两个方法使用重载机制的话,会导致编码更麻烦。无法进行方法功能的区分。
什么时候代码会发生方法重载?
条件1:在同一个类当中
条件2:方法名相同
条件3:参数列表不同
参数的个数不同算不同
参数的类型不同算不同
参数的顺序不同算不同
只要同时满足以上3个条件,那么我们可以认定方法和方法之间发生了重载机制。
注意:
不管代码怎么写,最终一定能让java编译器很好的区分开这两个方法。
方法重载和方法的返回值类型无关。
方法重载和方法的“修饰符列表”无关。
7.5. println()详解
7.6. 代码的简单封装
目前就是将方法写在另一个类中。后面详解
7.7. 方法递归
- 什么是方法递归?
方法自己调用自己,这就是方法递归
- 当递归时程序没有结束条件,一定会发生:
栈内存溢出错误:StackOverflowError
所以:递归必须要有结束条件。(这是一个非常重要的知识点。)
JVM发生错误之后只有一个结果,就是退出JVM。
- 递归假设是有结束条件的,就一定不会发生栈内存溢出错误吗?
假设这个结束条件是对的,是合法的,递归有的时候也会出现栈内存溢出错误。 因为有可能递归的太深,栈内存不够了。因为一直在压栈。
4. 在实际的开发中,不建议轻易的选择递归,能用for循环while循环代替的,尽量使用循环来做。
因为循环的效率高,耗费的内存少。递归耗费的内存比较大,另外递归的使用不当,会导致JVM死掉。(但在极少数的情况下,不用递归,这个程序没法实现。)
所以:递归我们还是要认真学习的。
方法里面调用方法时,方法这一行不结束,后面部分先不执行。
在实际的开发中,假设有一天你真正的遇到了:StackOverflowError你怎么解决这个问题,可以 谈一下你的思路吗?
我来谈一下我的个人思路:
第一步:先检查递归的结束条件对不对。如果递归结束条件不对,必须对条件进一步修改,直到正确为止。
第二步:假设递归条件没问题,怎么办?
这个时候需要手动的调整JVM的栈内存初始化大小。可以将栈内存的空间调大点。(可以调整大一些。)
第三步:调整了大小,如果运行时还是出现这个错误,没办法,只能继续扩大栈的内存大小。
(java -X)这个可以查看调整堆栈大小的参数
6.1 此时jvm内存图:
此时的栈内存结构图:
7.8. subString方法
- Java中String类的substring() 方法作用:返回字符串的 子 字符串。
- 语法: ```java public String substring(int beginIndex)
或
public String substring(int beginIndex, int endIndex)
3. 画图了解
![image.png](https://cdn.nlark.com/yuque/0/2022/png/26328757/1646477416797-f3ea68d6-6477-4c44-92e6-0b08196f2292.png#clientId=u3c41cf38-a790-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=252&id=u18c3b021&margin=%5Bobject%20Object%5D&name=image.png&originHeight=504&originWidth=1045&originalType=binary&ratio=1&rotation=0&showTitle=false&size=61411&status=done&style=none&taskId=u2b23535b-e271-4ab0-9335-704ebbde847&title=&width=522.5)
4. 示例
```java
public class RunoobTest {
public static void main(String args[]) {
String Str = new String("This is text");
System.out.print("返回值 :" );
System.out.println(Str.substring(4) );
System.out.print("返回值 :" );
System.out.println(Str.substring(4, 10) );
}
}
7.9 Java中String类的replace()方法
- 作用:replace() 方法通过用 newChar 字符替换字符串中出现的所有 searchChar 字符,并返回替换后的新字符串。
- 语法:public String replace(char searchChar, char newChar)
- 参数: searchChar — 原字符 newChar — 新字符
- 返回值:替换后生成的新字符串
- 实例:
7.9 HomeWork
题目一:使用递归的方式计算n的阶乘,并画出内存结构图,使用递归的方式实现一个 , 使用for循环方式实现一个5的阶乘为:5x4x3x2x1;
注意:数字不能太大否则会超出整数的取值范围,目前实现整数5就行。
8. 面向对象
8.1. 认识面向过程
c语言 面向过程
c++ 一半面向对象 一半面向过程
java 完全面向对象
面向过程(耦合度高,可扩展性差):注重功能的步骤以及整个过程一体机,某个零部件坏了,所有的都坏了。
缺点:面向过程最主要是每一步与每一步的因果关系,假设其中任何一个因果关系出现问题(错 误),此时
整个系统的运转都会出现问题。(代码和代码之间的耦合度太高,扩展力太差。)
优点:对于小型项目(功能),采用面向过程的方式进行开发,效率较高耦合度:就是粘连程度。
8.2. 认识面向对象
面向对象(耦合度底,可扩展性高):关注度在对象,更符合人类的组装电脑,某个零件坏了,只需要更换那某个零件。采用面向对象开发,更符合人类的思维方式
采用面向对象的方式贯穿整个系统的话,涉及到三个术语:
OOA面向对象分析
OOD面向对象设计
OOP面向对象编程整个软件开发的过程,都是采用OO进行贯穿的。
实现一个软件的过程:分析(A) —> 设计(D) —> 编程(P)
8.3. 面向对象三大特征
封装 继承 多态
任何一个面向对象的编程语言都包括这三个特征
8.4. 类和对象
8.4.1. 类
- 什么是类:类实际上在现实世界当中是不存在的,是一个抽象的概念。类就是是一个模板,通过一个 类,是可以创建N多个对象的。
- java中所有的“类”都属于引用数据类型。
8.4.2. 对象
- 什么是对象:对象是实际存在的个体。(真实存在的个体)
- 在java语言中,要想得到“对象”,必须先定义“类”,“对象”是通过“类”这个模板创造出来的。
- 这几个术语你需要自己能够阐述出来:
- 类:不存在的,人类大脑思考总结一个模板(这个模板当中描述了共同特征。)
- 对象:实际存在的个体。
- 实例:对象还有另一个名字叫做实例。
- 实例化:通过类这个模板创建对象的过程,叫做:实例化。
- 抽象:多个对象具有共同特征,进行思考总结抽取共同特征的过程。
类 —【实例化】—> 对象(实例)
对象 —【抽象】—> 类 - 类 = 属性 + 方法
属性来源于:状态 n 属性其实就是成员变量
方法来源于:动作 v
😸思考:“java软件工程师”在开发中起到的一个作用是什么?
- 软件开发既然是为了解决现实世界当中的问题,那么首先java软件必须能够模拟现实世界。
- 其实软件是一个虚拟的世界。这个虚拟的世界需要和现实世界一一对应,这才叫模拟。
8.4.3. 类的定义
- 语法格式:
[修饰符列表] class 类名 {
//类体 = 属性 + 方法
// 属性在代码上以变量的形式存在(描述状态),属性其实就是变量。
// 方法描述动作/行为
}
- 属性对应的是“数据”,数据在程序中只能放到变量中。属性其实就是对象级别的变量。
- 变量根据出现位置进行划分:
方法体当中声明的变量:局部变量。
方法体外声明的变量:成员变量。(这里的成员变量就是属性)
8.5. 编译的过程
第一种方式:
javac XueSheng.java javac XueShengTest.java
第二种方式:
java XueShengTest.java jdk14的新特性
第三种方式:
javac *.java
8.6. 对象的创建
语法级别上是怎么完成对象创建: 类名 变量名 = new 类名();这样就完成了对象的创建。
new是一个运算符。专门负责对象的创建。
public class XueSheng{ // 这个程序编译之后,会生成XueSheng.class字节码文件。
// 属性
// 学号(成员变量)
int xueHao;
// 姓名
String xingMing;
// 年龄
int nianLing;
// 性别
boolean xingBie;
// 住址
String zhuZhi;
}
public class XueShengTest{
public static void main(String[] args){
int i = 100;
System.out.println("i =" + i);
// 在这里可以访问XueSheng类吗?
// 当然可以。
/*
创建对象的语法是什么? new 类名();
目前死记硬背,先记住。后面你就理解了。
XueSheng s1 = new XueSheng();
和
int i = 100;
解释一下:i是变量名
int是变量的数据类型
100是具体的数据。
s1是变量名(s1不能叫做对象。s1只是一个变量名字。)
XueSheng是变量s1的数据类型(引用数据类型)
new XueSheng() 这是一个对象。(学生类创建出来的学生对象。)
数据类型包括两种:
基本数据类型:byte short int long float double boolean char
引用数据类型:String、XueSheng.....
java中所有的“类”都属于引用数据类型。
*/
XueSheng s1 = new XueSheng(); // 和 int i = 10;一个道理。
// 再通过该类创建一个全新的对象
XueSheng s2 = new XueSheng();
// 再创建一个呢?
XueSheng xsh = new XueSheng();
// 以上的这个程序就相当于通过XueSheng类实例化了3个XueSheng对象。
// 创建对象的个数没有限制,可以随意。只要有模板类就行。
// 3个对象都属于学生类型。
}
}
什么是实例变量?
对象又被称为实例。
实例变量实际上就是:对象级别的变量。
成员变量也叫实例变量。对象和引用的区别
对象是通过new出来的,在堆内存中存储。
引用是:但凡是变量,并且该变量中保存了内存地址指向了堆内存当中的对象的。
对象级的变量(实例变量)的调用 引用.实例变量 ;
实例变量的赋值
引用.实例变量 = ;
通过“=”赋值的方式将内存中实例变量的值修改一下。- 实例变量的访问
先创建对象,然后””引用.”的方式访问。
8.7. 对象的使用
public class StudentTest{
public static void main(String[] args){
//局部变量,错误: 可能尚未初始化变量k
/*
int k;
System.out.println(k);
*/
/ 创建学生对象1
// s1属于局部变量吗?当然是。
// s1这个局部变量叫做引用
Student s1 = new Student();
// 怎么访问实例变量?
// 语法:引用.实例变量名
System.out.println(s1.no);
System.out.println(s1.name);
System.out.println(s1.age);
System.out.println(s1.sex);
System.out.println(s1.addr);
// 程序执行到此处我可以修改s1这个学生的学号吗?
// 通过“
=
”赋值的方式将内存中实例变量的值修改一下。
s1.no = 110;
s1.name =
"张三";
s1.age = 20;
s1.sex = true;
s1.addr =
"深圳宝安区";
System.out.println("学号=
" + s1.no);
System.out.println("姓名=
" + s1.name);
System.out.println("年龄=
" + s1.age);
System.out.println("性别=
" + s1.sex);
System.out.println("住址=
" + s1.addr);
}
public static void method(){
// i s1 s2都是main方法中的局部变量,在这里是无法访问的。
/*
System.out.println(i);
System.out.println(s1);
*/
}
}
- Student s1 = new Student();
- s1属于局部变量 ,叫做引用。
- s1是变量名
- Student是数据类型(引用数据类型)
- new Student() 这是一个对象(学生类创建出来的学生对象。)
8.8 对象和引用
- jvm内存执行图
- java中没有指针概念,没法直接使用堆内存
- 将堆内存中对应的对象地址复制一份,赋值给s1变量,实现间接调用
- 引用和对象的区分:
引用是存储对象地址的一个变量,引用不一定是局部变量
对象是堆里new出来的。成员变量里面包含实例变量
- 此图中的代码只是为了方便理解,画内存图是应没有代码,只有数据。
- 引用 指向对象的过程 在c语言里面叫指针。
8.9. 成员变量的默认值
注意:对于成员变量来说,没有手动赋值时,系统默认赋值
- 引用数据类型是null。
- null是一个java关键字,全部小写,表示空。是引用类型的默认值。byte、short、int是0。
- long 是 0L
- float 是 0f double是0.0
- Boolean 是 false
- char是 \u0000
8.10. JVM内存图复习
- 注意:只要是方法,不管静态方法、实例方法、构造方法,它们在运行的时候都会在栈内存压栈。
8.11. Homework
题目1:根据程序画执行内存图
答案:
只有地址指向对象,没有对象指向地址。
题目二:难度较大 画程序执行内存图 并完成思考题
思考题:
答案:Address.u.city;
正确答案:u.addr.city
拆分代码:
内存图:
9. 面向对象 (二)
💡day 14
9.1. 内存图 (重要)
9.1.1. 画内存图注意事项
- 不要在内存图体现代码,应该体现数据
- 画图时,应分析代码。根据代码一行一行的去画。
- 引用的箭头永远指向对象。
9.1.2. HomeWork1
画内存图
// 把这个内存图画出来。
一定要按照程序的执行顺序一步一步画。
public class T{
A o1; // 成员变量中的实例变量。必须先创建对象,通过“引用”来访问。
public static void main(String[] args){
D d = new D();
C c = new C();
B b = new B();
A a = new A();
T t = new T();
//这里不写代码会出现 NullPointerException 异常。(空指针异常。)
c.o4 = d;
b.o3 = c;
a.o2 = b;
t.o1 = a;
// 编写代码通过 t 来访问 d 中的 i
//System.out.println(T.a); //错误的。
System.out.println(t.o1.o2.o3.o4.i);
}
}
class A{
B o2;
}
class B{
C o3;
}
class C{
D o4;
}
class D{
int i;
}
- 为什么要画内存图(非常重要)?
第一:有了内存图,程序不运行,我也知道结果。(可以推算出结果)
第二:有了内存图,有助于你调试程序。
9.2. 空指针异常( NullPointerException )
- 关于垃圾回收器:GC机制
在java语言中,垃圾回收器主要针对的是堆内存。当一个java对象没有任何引用指向该对象 的时候,GC会考虑将该垃圾数据释放回收掉。
- 出现空指针异常的前提条件是?
“空引用”访问实例【对象相关】相关的数据时,都会出现空指针异常。
空引用: 引用 = null ,没有指向对象,即为空引用。
语法上是可以通过空指针异常。
public class NullPointerTest{
public static void main(String[] args){
// 创建客户对象
Customer c = new Customer();
// 访问这个客户的id
System.out.println(c.id); // 0
// 重新给id赋值
c.id = 9521; // 终身代号
System.out.println("客户的id是=
" + c.id);
c = null; // 没用引用指向对象 Gc机制清理堆内存的对象
// NullPointerException 空指针异常
// 编译器没问题,因为编译器只检查语法,编译器发现c是Customer类型,
// Customer类型中有id属性,所以可以:c.id。语法过了。
// 但是运行的时候需要对象的存在,但是对象没了,尴尬了,就只能出现一个异常。
System.out.println(c.id);
}
}
class Customer{
// 客户id
int id; // 成员变量中的实例变量,应该先创建对象,然后通过“引用.
”的方式访问。
}
内存图:
9.3. 方法调用时参数传递的问题
- java中规定:参数传递的时候,和类型无关,不管是基本数据类型还是引用数据类型,统一都是将 盒子中保存的那个“值”复制一份,传递下去。
java中只有一个规定:参数传递的时候,一定是将”盒子”中的东西复制一份传递过去。 内存地址也是值,也是盒子中保存的一个东西。
方法的参数可以是引用数据类型也可以是基本数据类型
- 分析以下代码
- int x = 1; int y = x;
分析:把x中保存1复制一份传给y,x和y都是两个局部变量。 - Person p1 = 0x1234; Person p2 = p1;
分析:把p1中保存的0x1234复制一份传给p2,p1和p2都是两个局部变量。
- int x = 1; int y = x;
public class Test1{
public static void main(String[] args){
int x = 100;
int y = x; // x赋值给y,是怎么传递的?将x变量中保存的100这个值复制一份传给y
// 局部变量,域是main
int i = 10;
// 将i变量中保存的10复制一份,传给add方法。
add(i);
System.out.println("main ---> " + i); //10
}
/*
public static void add(int i){ // i是局部变量,域是add
i++;
System.out.println("add ----> " + i); //11
}
*/
public static void add(int k){
k++;
System.out.println("add ----> " + k);
}
}
public class Test2{
public static void main(String[] args){
Person p = new Person();
p.age = 10;
add(p);
System.out.println("main--->" + p.age); //11
}
// 方法的参数可以是基本数据类型,也可以是引用数据类型,只要是合法的数据类型就行。
public static void add(Person p){ // p是add方法的局部变量。
p.age++;
System.out.println("add--->" + p.age); //11
}
}
class Person{
// 年龄属性,成员变量中的实例变量。
int age;
}
内存图Test1:
内存图Test2
![](https://gitee.com/hei-yubai/javase/raw/master/img/202203041450180.png#crop=0&crop=0&crop=1&crop=1&id=diOIK&originHeight=549&originWidth=1359&originalType=binary&ratio=1&rotation=0&showTitle=false&status=done&style=none&title=)
9.4. 构造方法(重点)
9.4.1. 构造方法特点( Constructor )
- 什么是构造方法?
- 构造方法是用来创建对象,并同时给对象的属性赋值。(注意:实例变量没有手动赋值的时候,系统会赋默认值。)
- 构造方法语法:
[修饰符列表] 构造方法名(形式参数列表){
构造方法体;
通常在构造方法体当中给属性赋值,完成属性的初始化。
}
- 修饰符列表目前统一写:public,千万不要写public static,
- 构造方法名和类名必须一致。
- 构造方法不需要指定返回值类型,也不能写void,写上void表示普通方法,就不是构造方法 了。
当一个类中没有提供任何构造方法,系统默认提供一个无参数的构造方法。这个无参数的构造方法叫做缺省构造器。
调用构造方法:使用new运算符来调用构造方法。语法格式:new 构造方法名(实际参数列表);
当一个类中手动的提供了构造方法,那么系统将不再默认提供无参数构造方法。建议将无参数构造 方法手动的写出来,这样一定不会出问题。
无参数构造方法和有参数的构造方法都可以调用。
Student x = new Student();
Student y = new Student(123);构造方法支持方法重载吗?
- 构造方法是支持方法重载的。在一个类当中构造方法可以有多个。并且所有的构造方法名字 都是一样的。
- 方法重载特点:在同一个类中,方法名相同,参数列表不同。
对于实例变量来说,只要你在构造方法中没有手动给它赋值,统一都会默认赋值。默认赋系统值。 并且在构造方法中不显示,但存在。
public class ConstructorTest01{
public static void main(String[] args){
// 调用Student类的无参数构造方法
new Student(); // 没用引用指向 会被自动清理
// 调用普通方法
ConstructorTest01.doSome();
doSome();
// 创建Student类型的对象
Student s1 = new Student();
// 输出“引用”
//只要输出结果不是null,说明这个对象一定是创建完成了。
// 此处的输出结果大家目前是看不懂的,后期再说。
System.out.println(s1); //Student@54bedef2
// 这是调用另一个有参数的构造方法。
Student s3 = new Student(100);
System.out.println(s3); //Student@5caf905d
}
public static void doSome(){
System.out.println("do some!!!!");
}
}
- 思考:实例变量没有手动赋值的时候,系统会默认赋值,那么这个默认赋值操作是在什么时间进行的?
实例变量是在构造方法执行的过程中完成初始化的,完成赋值的。
public class User{
// 3个属性,3个实例变量【对象变量】
// 用户id
int id; //System.out.println(User.id);错误的。需要先new对象,只有对象有
了才能谈id
// 用户名
String name;
// 年龄
int age;
// 手动定义有参数的构造方法,无参数构造方法将消失。
public User(int a){
}
public User(){
//这里实际上有三行代码你看不见。
// 无参数构造方法体当中虽然什么代码都没写,
// 但是实际上是在这个方法体里面进行的实例变量默认值初始化
/*
id = 0;
name = null;
age = 0;
*/
// 这就表示不再采用系统默认值,手动赋值了。
id = 111;
name =
"lisi";
age = 30;
}
}
- 构造方法需要掌握的知识点:
- 构造方法有什么作用?
- 构造方法怎么定义,语法是什么?
- 构造方法怎么调用,使用哪个运算符? 什么是缺省构造器?
- 怎么防止缺省构造器丢失?
- 实例变量在类加载是初始化吗?实例变量在什么时候初始化?
10. 封装 (第14天)
10.1. 封装的特性
- 面向对象的三大特征:
封装
继承
多态
有了封装,才有继承,有了继承,才能说多态。
- 封装有什么用:
保证内部结构的安全。
屏蔽复杂,暴露简单。 - 码级别上,封装有什么用:
一个类体当中的数据,假设封装之后,对于代码的调用人员来说,不需要关心代码的复杂实现,只 需要通过一个简单的入口就可以访问了。另外,类体中安全级别较高的数据封装起来,外部人员不 能随意访问,来保证数据的安全性。(优点:第一数据安全了。第二调用者也方便了。)
// 这是没有封装的Person。
public class Person{
// 实例变量(属性)
int age; //age属性是暴露的,在外部程序中可以随意访问。导致了不安全。
}
10.2. 封装的步骤
- 怎么进行封装:
- 属性私有化(使用 关键字进行修饰。)
- 对外提供简单的操作入口。(电视机的遥控器就相当于是电视机的访问入口,简单明了。
- 代码说明:
//尝试封装一下,不再对外暴露复杂的数据,封装起来, 对外只提供简单的操作入口。
public class Person{
// 出了这个类,age属性就无法访问了。私有的。
private int age; // 每一个人年龄值不同,对象级别的属性。
// 对外提供简单的访问入口
// 外部程序只能通过调用以下的代码来完成访问
// 思考:你应该对外提供几个访问入口?
// 思考:这些操作入口是否应该是方法呢?
// 写一个方法专门来完成读。(get)
// 写一个方法专门来完成写。(set)
// get和set方法应该带有static,还是不应该有static,get和set方法应该定义为实例方法
吗?
// get读年龄,set改年龄,这个读和改都是操作的一个对象的年龄。(没有对象何来年龄)
public int getAge(){
return age;
}
// set方法
public void setAge(int nianLing){
// 能不能在这个位置上设置关卡!!!!
if(nianLing < 0 || nianLing > 150){
System.out.println("对不起,年龄值不合法,请重新赋值!");
return; //直接终止程序的执行。
}
//程序能够执行到这里,说明年龄一定是合法的。
age = nianLing;
}
}
- 封装的第一步:使用 private 关键字将实例变量私有化
- private 表示私有的,被这个关键字修饰之后,该数据只能在本类中访问。出了这个类无法访问
- 封装的第二步:对外提供公开的set方法和get方法作为操作入口。 并且都不带 static ,都是实例方法。
[修饰符列表] 返回值类型 方法名(形式参数列表){
}
注意:
java开发规范中有要求,set方法和get方法要满足以下格式。
get方法的要求:
public 返回值类型 get+属性名首字母大写(无参){
return xxx;
}
set方法的要求:
public void set+属性名首字母大写(有1个参数){
xxx = 参数;
}
大家尽量按照java规范中要求的格式提供set和get方法。如果不按照这个规范格式来,那么你的
程序将不是一个通用的程序。
10.3. get方法与set方法的说明
- get 方法与 set 方法都是实例方法
- 不带static的方法称为实例方法,实例方法的调用必须先new对象。
- 一个属性对应一个get、set方法
- get方法用来读(需要将结果输出 需要返回值)、set方法是用来写(修改实例变量的 不需要返回值)
- 有多少个实例变量就可以申明多少个get、set方法
- 可以在set方法中设立关卡来保证数据的安全性。
10.4. 封装步骤总结
- 第一步:属性私有化
- 第二步:1个属性对外提供两个set和get方法。外部程序只能通过set方法修改,只能通过get方法 读取,**可以在set方法中设立关卡来保证数据的安全性。**
10.5. 初步了解 static 关键字
- 实例的回顾
对象被称为实例。
实例相关的有:实例变量、实例方法。
实例方法:没有带有 static 的方法称为实例方法
实例方法是对象相关的方法,对象级别的方法,应该是一个对象级别的行为。实例变量是对象变量,实例方法是对象方法
实例相关的都需要先new对象,通过“引用.”的方式去访问 - 实例方法的初步了解
public class MethodTest{
public static void main(String[] args){
MethodTest.doSome();
//类名. 可以省略(在同一个类中。)
doSome();
// 创建对象
MethodTest mt = new MethodTest();
// 通过"引用.
"的方式访问实例方法。
mt.doOther();
}
// 带有static
public static void doSome(){
System.out.println("do some!");
}
//这个方法没有static,这样的方法被称为:实例方法。(对象方法,对象级别的方法)
//这个没法解释,大家目前死记硬背。
public void doOther(){
System.out.println("do other.... ");
}
}
以上代码的注释:
带 static 的方法与不带 static 的方法的访问:
带 static 方法的访问:
- 类名.方法名(实际参数列表);
- 也可以用“引用.”的方式访问,但意义不大。
不带 static 的方法的访问:
- 现在类中创建一个该类的对象
- 然后用 引用. 方法名(实际参数列表的方式访问);
- static修饰的统一都是静态的,都是类相关的,不需要new对象。直接采用“类名.访问’’。 当一个属性是类级别的属性,所有对象的这个属性的值是一样的,建议定义为静态变量。**
10.6. 实例方法导致的空指针异常
一个实例方法的调用也必须有对象的存在,若 引用为 null 也会存在 NullPointer (空指针异
常)
10.7. 封装中构造方法与set方法的说明
- 代码封装里面的set方法是:创建完成对象后,需要修改值的话调用set
- 有参构造方法是:创建对象时赋值
- 所以两者都要有
10.8. 第十四天作业
- 第一题:设计日期类,每个日期对象都可以描述年月日信息。
- 第二题:设计男人类,每个男人都有身份证号、姓名、性别、女人。设计女人类,每个女人都有身 份证号、姓名、性别、男人。
- 第三题:设计银行账户类,每个账户都有账号、密码、余额等信息。
- 第四题:设计微信账号类,每个微信账号都有微信号、手机号、昵称等信息。第五题:定义丈夫类 Husband 和妻子类 Wife,
丈夫类的属性包括:身份证号,姓名,出生日期,妻子。
妻子类的属性包括:身份证号,姓名,出生日期,丈夫。
分别给这两个类提供构造方法(无参数构造方法和有参数构造方法都要提供), 编写测试程序,创建丈夫对象,然后再创建妻子对象,丈夫对象关联妻子对象, 妻子对象关联丈夫对象,要求能够输出这个“丈夫对象”的妻子的名字,或者能够输出这个“妻子对象”的丈夫的名字。要求能够画出程序执行过程的内存图。 并且要求在程序中演示出空指针异常的效果。
11. this 和 static (第十五天)
11.1. static关键字
- static
- static 翻译为“静态”
- 所有 static 关键字修饰的都是类相关的,类级别的。
- 所有 static修饰的,都是采用“类名.”的方式访问。
- static 修饰的变量:静态变量 , static修饰的方法:静态方法
变量的分类:变量根据声明的位置进行划分:
- 在方法体当中声明的变量叫做:局部变量。
在方法体外声明的变量叫做:成员变量。
- 成员变量又可以分为:实例变量、静态变量
- 实例相关的,必须先有对象,才能访问,可能会出现空指针异常。
// 成员变量中的实例变量
int i;
// 实例方法
public void m2(){
// 局部变量
int x = 20;
}
- 实例相关的,必须先有对象,才能访问,可能会出现空指针异常。
- 成员变量又可以分为:实例变量、静态变量
静态的,都是类相关的,访问时采用“类名.”的方式访问。不需要new对象。不需
要对象的参与即可访问。没有空指针异常的发生。
// 成员变量中的静态变量
static int k;
// 静态方法
public static void m1(){
// 局部变量
int m = 100;
}
11.1静态变量
- 什么时候变量为静态变量
- 类特征(描述的状态是所有对象共有的特征,时为静态变量。)
- 当变量为实例变量:一个对象一份的是实例变量。
public class StaticTest02{
public static void main(String[] args){
Chinese c1 = new Chinese("1231456456456456","张三","中国");
System.out.println(c1.idCard);
System.out.println(c1.name);
System.out.println(c1.country);
Chinese c2 = new Chinese("7897897896748564","李四","中国");
System.out.println(c2.idCard);
System.out.println(c2.name);
System.out.println(c2.country);
}
}
// 定义一个类:中国人
class Chinese{
// 身份证号
// 每一个人的身份证号不同,所以身份证号应该是实例变量,
一个对象一份。
String idCard;
// 姓名
// 姓名也是一个人一个姓名,姓名也应该是实例变量。
String name;
// 国籍
// 对于“中国人”这个类来说,国籍都是“中国”
,不会随着对象的改变而改变。
// 显然国籍并不是对象级别的特征。
// 国籍属于整个类的特征。整个族的特征。
// 假设声明为实例变量,内存图是怎样的?
// 假设声明为静态变量,内存图又是怎样的?
String country;
// 无参数
public Chinese(){
}
// 有参数
public Chinese(String s1,String s2, String s3){
idCard = s1;
name = s2;
country = s3;
}
}
*/
- 实例变量内存图的内存图
- 当变量为静态变量
- 静态变量的内存图
public class StaticTest02{
public static void main(String[] args){
// 访问中国人的国籍
// 静态变量应该使用类名.的方式访问
System.out.println(Chinese.country);
Chinese c1 = new Chinese("1231456456456456","张三");
System.out.println(c1.idCard);
System.out.println(c1.name);
Chinese c2 = new Chinese("7897897896748564","李四");
System.out.println(c2.idCard);
System.out.println(c2.name);
// idCard是实例变量,必须先new对象,通过“引用.
” 访问
// 错误: 无法从静态上下文中引用非静态 变量 idCard
//System.out.println(Chinese.idCard);
}
}
// 定义一个类:中国人
class Chinese{
// 身份证号
// 每一个人的身份证号不同,所以身份证号应该是实例变量,
一个对象一份。
String idCard;
// 姓名
// 姓名也是一个人一个姓名,姓名也应该是实例变量。
String name;
// 国籍
// 重点重点五颗星:加static的变量叫做静态变量
// 静态变量在类加载时初始化,不需要new对象,静态变量的空间就开出来了。
// 静态变量存储在方法区。
static String country =
"中国";
// 无参数
public Chinese(){
}
// 有参数
public Chinese(String s1,String s2){
idCard = s1;
name = s2;
}
}
- 静态变量的内存图
- Jvm 中的变量
- 栈:方法只要执行,会压栈。(局部变量)
- 堆:new出来的对象都在堆中。垃圾回收器主要针对。(实例变量)
- 方法区:类的信息,字节码信息,代码片段。(静态变量)
- 变量的初始化:
- 静态变量:类加载时初始化。
- 实例变量:new运算符调用构造方法时完成初始化
11.1.2. 静态变量的空指针异常
- 静态变量不需要对象存在不会出现空指针异常
- 静态、实例的访问
- 实例的:一定需要使用“引用.”来访问。
- 静态的:
建议使用“类名.”来访问,但使用“引用.”也行(不建议使用”引用.”)。
静态的如果使用“引用.”来访问会让程序员产生困惑:程序员以为是实例的呢。 - 结论:
空指针异常只有在什么情况下才会发生呢?
只有在“空引用”访问“实例”相关的,都会出现空指针异常。
- eg:
public class StaticTest03{
public static void main(String[] args){
// 通过"类名.
"的方式访问静态变量
System.out.println(Chinese.country);
// 创建对象
Chinese c1 = new Chinese("1111111"
,
"张三");
System.out.println(c1.idCard); // 1111111
System.out.println(c1.name); // 张三
System.out.println(c1.country); // 中国
// c1是空引用
c1 = null;
// 分析这里会不会出现空指针异常?
// 不会出现空指针异常。
// 因为静态变量不需要对象的存在。
// 实际上以下的代码在运行的时候,还是:
System.out.println(Chinese.country);
System.out.println(c1.country);
// 这个会出现空指针异常,因为name是实例变量。
//System.out.println(c1.name);
}
}
class Chinese{
// 实例变量
String idCard;
String name;
// 静态变量
static String country =
"中国";
//构造方法
public Chinese(String x, String y){
idCard = x;
name = y;
}
}
- 总结:从第一天开始讲解HelloWorld到目前为止,一个类当中一共就写过这些东西。
- 类{
- 实例相关的都是需要new对象的,通过”引用.”访问。
实例变量;
实例方法; - 静态相关的都是采用“类名.”访问。也可以使用“引用.”,只不过不建议。
静态变量;
静态方法;
}
- 实例相关的都是需要new对象的,通过”引用.”访问。
- 类{
11.1.3. 静态方法、实例方法
- 关于方法来说,什么时候定义为实例方法?什么时候定义为静态方法?
- 此方法一般都是描述了一个行为,如果说该行为必须由对象去触发。那么该方法定义为实例方法。
当这个方法体当中,直接访问了实例变量,这个方法一定是实例方法。
我们以后开发中,大部分情况下,如果是工具类的话,工具类当中的方法一般都是静态的。
(静态方法有一个优点,是不需要new对象,直接采用类名调用,极其方便。工具类就是为了
方便,所以工具类中的方法一般都是static的。)什么是工具类?????
以后讲。(工具类就是为了方便编程而开发的一些类。)
一个方法代表了一个动作。
方法的代码片段放在方法区,但是方法执行过程当中需要的内存在栈中。
什么时候方法定义为实例方法?
张三考试,得分90;
李四考试,得分100
不同的对象参加考试的结果不同,我们可以认定“考试”这个行为是与对象相关的行为。
建议将“考试”这个方法定义为实例方法。
11.1.4. 静态代码快
静态代码块语法是:
static {
java语句;
java语句;
}
static静态代码块在类加载时执行,只执行一次,并且在main方法执行之前执行。
静态代码块有啥作用?
第一:静态代码块不是那么常用。(不是每一个类当中都要写的东西。)
第二:静态代码块这种语法机制实际上是SUN公司给我们java程序员的一个特殊的时刻时机。
这个时机叫做:类加载时机态代码快的执行顺序:
- 在类体中的静态方法没有先后执行顺序,到你了你在执行。
- 静态代码块一般是按照自上而下的顺序执行(谁在前先执行谁)。
具体的业务
- 项目经理说了:大家注意了,所有我们编写的程序中,只要是类加载了,请记录一下类加载
的日志信息(在哪年哪月哪日几时几分几秒,哪个类加载到JVM当中了)。
思考:这些记录日志的代码写到哪里呢?
写到静态代码块当中。
- 项目经理说了:大家注意了,所有我们编写的程序中,只要是类加载了,请记录一下类加载
eg:
public class StaticTest06{
// 静态代码块(特殊的时机:类加载时机。)
static {
System.out.println("A");
}
// 一个类当中可以编写多个静态代码块
static {
System.out.println("B");
}
// 入口
public static void main(String[] args){
System.out.println("Hello World!");
}
// 编写一个静态代码块
static{
System.out.println("C");
}
}
/*
A
B
C
Hello World!
*/
11.1.5. 实例语句块(InstanceCode)
- 实例语句块语法:
{
java语句;
} - 实例语句块在什么时候执行?
- 只要是构造方法执行,必然在构造方法执行之前,自动执行“实例语句块”中的代码。
- 实际上这也是SUN公司为java程序员准备一个特殊的时机,叫做对象构建时机。
- eg:
public class InstanceCode{
//入口
public static void main(String[] args){
System.out.println("main begin");
new InstanceCode();
new InstanceCode();
new InstanceCode("abc");
new InstanceCode("xyz");
}
//实例语句块
{
System.out.println("实例语句块执行!");
}
// Constructor
public InstanceCode(){
System.out.println("无参数构造方法");
}
// Constructor
public InstanceCode(String name){
System.out.println("有参数的构造方法");
}
}
11.1.6. 代码顺序要求
- 到目前为止,你遇到的所有java程序,有顺序要求的是哪些?
第一:对于一个方法来说,方法体中的代码是有顺序的,遵循自上而下的顺序执行。
第二:静态代码块1和静态代码块2是有先后顺序的。
第三:静态代码块和静态变量是有先后顺序的,谁在前 先执行谁
第四:方法与方法之间没有先后顺序
第五:执行顺序:
(1)父类静态对象和静态代码块
(2)子类静态对象和静态代码块
(3)父类非静态对象和非静态代码块
(4)父类构造函数
(5)子类非静态对象和静态代码块
(6)子类构造函数
其中:类中静态块按照声明顺序执行,并且(1)和(2)不需要调用 new 类实例的时候就执行了 (就是在类加载到方法区前就执行了 )
Eg:
总结到目前为止类可以出现的代码:
类体{
实例变量;
实例方法;
静态变量;静态方法;
构造方法;
静态代码块; 实例语句块;
方法(){
// 局部变量
int i = 100;
}
}判读以下程序的代码执行顺序
//判断以下程序的执行顺序
public class CodeOrder{
// 静态代码块
static{
System.out.println("A");
}
// 入口
// A X Y C B Z
public static void main(String[] args){
System.out.println("Y");
new CodeOrder();
System.out.println("Z");
}
// 构造方法
public CodeOrder(){
System.out.println("B");
}
// 实例语句块
{
System.out.println("C");
}
// 静态代码块
static {
System.out.println("X");
}
}
11.2. this关键字
11.2.1. this
- this是一个关键字,全部小写。
- this是什么,在内存方面是怎样的? 一个对象一个this。
- 一个对象一个this。
- this是一个变量,是一个引用。
- this保存当前对象的内存地址,指向自身。 所以,严格意义上来说,this代表的就是“当前对
象” this存储在堆内存当中对象的内部
3. this只能使用在实例方法中。谁调用这个实例方法,this就是谁。
所以this代表的是:当前对象。
- “this.”大部分情况下是可以省略的。
- 为什么this不能使用在静态方法中?
this代表当前对象,静态方法中不存在当前对象。 - eg:
public class ThisTest01{
public static void main(String[] args){
Customer c1 = new Customer("张三");
c1.shopping();
Customer c2 = new Customer("李四");
c2.shopping();
Customer.doSome();
}
}
// 顾客类
class Customer{
// 属性
// 实例变量(必须采用“引用.
”的方式访问)
String name;
//构造方法
public Customer(){
}
public Customer(String s){
name = s;
}
// 顾客购物的方法
// 实例方法
public void shopping(){
// 这里的this是谁?this是当前对象。
// c1调用shopping(),this是c1
// c2调用shopping(),this是c2
//System.out.println(this.name + "正在购物!");
// this. 是可以省略的。
// this. 省略的话,还是默认访问“当前对象”的name。
System.out.println(name + "正在购物!");
}
// 静态方法
public static void doSome(){
// this代表的是当前对象,而静态方法的调用不需要对象。矛盾了。
// 错误: 无法从静态上下文中引用非静态 变量 this
//System.out.println(this);
}
}
class Student{
// 实例变量,怎么访问?必须先new对象,通过“引用.
”来访问。
String name =
"zhangsan";
// 静态方法
public static void m1(){
//System.out.println(name);
// this代表的是当前对象。
//System.out.println(this.name);
// 除非你这样
Student s = new Student();
System.out.println(s.name);
}
//为什么set和get方法是实例方法?
public static void setName(String s){
name = s;
}
public String getName(){
return name;
}
11.3. this在jvm中的内存图
11.4. 重新认识作用域
- 在大多数情况下,出了大括号就不认识了
- 下面的这个int i 虽然在同一个作用域,但是它是实例变量访问需要创建对象,通过 “引用.” 的方式访问。
- 分析:i变量在main方法中能不能访问????
public class ThisTest02{
// 实例变量
int i = 100; // 这个i变量是不是必须先new对象才能访问。
// 静态变量
static int k = 111;
// 静态方法
public static void main(String[] args){
// 错误: 无法从静态上下文中引用非静态 变量 i
// System.out.println(i);
// 怎么样访问i
ThisTest02 tt = new ThisTest02();
System.out.println(tt.i);
// 静态变量用“类名.
”访问。
System.out.println(ThisTest02.k);
// 类名. 能不能省略?
// 可以
System.out.println(k);
}
}
11.5. day15天作业
- 题目:
一、请通过代码封装,实现如下需求: 编写一个类Book,代表教材:
1. 具有属性:名称(title)、页数(pageNum)
2. 其中页数不能少于200页,否则输出错误信息,并赋予默认值200
3. 为各属性提供赋值和取值方法
4. 具有方法:detail,用来在控制台输出每本教材的名称和页数
5. 编写测试类BookTest进行测试:为Book对象的属性赋予初始值,并调用Book对象的detail方 法,看看输出是否正确
二、写一个名为Account的类模拟账户。该类的属性和方法如下所示。
该类包括的属性:账户id,余额balance,年利率annualInterestRate;
包含的方法:各属性的set和get方法。取款方法withdraw(),存款方法deposit()
写一个测试程序
(1) 创建一个Customer,名字叫Jane Smith,他有一个账号为1000,余额为2000,年利率为1.23%的账 户
(2) 对Jane Smith操作:
存入100元,再取出960元,再取出2000。打印Jane Smith的基本信息
信息如下显示:
成功存入:100 成功取出:960
余额不足,取钱失败
Customer [Smith,Jane] has a account :id is 1000 annualInterestRate is 1.23% balance is
1140.0
三、(封装)已知一个类 Student 代码如下:
class Student{ String name; int age;
String address; String zipCode; String mobile;
}
要求:
1、把Student 的属性都作为私有,并提供get/set 方法以及适当的构造方法。
2、为Student 类添加一个getPostAddress 方法,要求返回Student 对象的地址和邮编。
11.6. this不能省略
- this可以使用在实例方法中,不能使用在静态方法中。
- this关键字大部分情况下可以省略,什么时候不能省略呢?
在实例方法中,或者构造方法中,为了区分局部变量和实例变量,这种情况下:this. 是不能省略
的。 - this. 的作用是:区分局部变量和实例变量。
// 学生类
class Student{
//学号
private int no;
//姓名
private String name;
//构造方法无参
public Student(){
}
// 分析一下:以下代码哪里写的不好。
//可读性差
//构造方法有参
/*
public Student(int i, String s){
no = i;
name = s;
}
*/
// 上面的构造方法也增强以下可读性
public Student(int no, String name){
this.no = no;
this.name = name;
}
/*
public void setNo(int no){ // 就近原则。
no = no; //这两个no都是局部变量no,和实例变量no没关系。
}
*/
public void setNo(int no){
//no是局部变量
//this.no 是指的实例变量。
this.no = no; // this. 的作用是:区分局部变量和实例变量。
}
*/
public String getName(){ // getName实际上获取的是“当前对象”的名字。
//return this.name; // 严格来说,这里是有一个 this. 的。只不过这个 this.
是可以省略的。
return name;
}
}
11.7. this出现在构造方法中
- this除了可以使用在实例方法中,还可以用在构造方法中。
- 通过当前的构造方法1去调用另一个本类的构造方法2,可以使用以下语法格式:this(实际参
数列表); - “构造方法1”和“构造方法2” 都是在同一个类当中。
- this() 这个语法作用:代码复用。
- 死记硬背:对于this()的调用只能出现在构造方法的第一行
- 通过当前的构造方法1去调用另一个本类的构造方法2,可以使用以下语法格式:this(实际参
eg:
class Date{ // 以后写代码都要封装,属性私有化,对外提供setter and getter
//年
private int year;
//月
private int month;
//日
private int day;
// 构造方法无参
// 调用无参数构造方法,初始化的日期是固定值。
public Date(){
//错误: 对this的调用必须是构造器中的第一个语句
//System.out.println(11);
/*
this.year = 1970;
this.month = 1;
this.day = 1;
*/
this(1970, 1, 1);
}
// 构造方法有参数
public Date(int year, int month, int day){
this.year = year;
this.month = month;
this.day = day;
}
11.8. this总结
- this 是一个关键字,是一个引用,保存内存地址,指向自身。
- this 可以使用在实例方法中,也可以使用在构造方法中。
- this出现在实例方法中其实代表的是当前对象。
- this不能使用在静态方法中。
- this. 大部分情况下可以省略,但是用来区分局部变量和实例变量的时候不能省略。
- this() 这种语法只能出现在构造方法第一行,表示当前构造方法调用本类其他的构造方法,目的是代码复用
11.9. 面向对象编程思想的培养
- 先分析 有哪几类对象
- 然后 找到类与类之间的关联
- 不同的类应该放在不同文件
11.10. day 16作业
- 题目:
- 1、猜数字游戏
一个类A有一个实例变量v,从键盘接收一个正整数作为实例变量v的初始值。另外再定义一个类B,对A类的实例变量v进行猜测。
如果大了则提示大了小了则提示小了
等于则提示猜测成功 2、请定义一个交通工具(Vehicle)的类其中有属性:
速 度 (speed) 体积(size)等等
方法移动(move())
设置速度(setSpeed(int speed))
加速speedUp(),
减速speedDown()等等.
最后在测试类Vehicle中的main()中实例化一个交通工具对象并通过方法给它初始化speed,size的值并且打印出来。另外调用加速减速的方法对速度进行改变。
3、在程序中经常要对时间进行操作但是并没有时间类型的数据。
那么我们可以自己实现一个时间类来满足程序中的需要。
定义名为MyTime的类其中应有三个整型成员时hour分minute秒second
为了保证数据的安全性这三个成员变量应声明为私有。
为MyTime类定义构造方法以方便创建对象时初始化成员变量。
再定义diaplay方法用于将时间信息打印出来。
为MyTime类添加以下方法
addSecond(int sec)
addMinute(int min)
addHour(int hou)
subSecond(int sec)
subMinute(int min)
subHour(int hou)
分别对时、分、秒进行加减运算。- 4、编写Java程序模拟简单的计算器。
定义名为Number的类其中有两个整型数据成员n1和n2应声明为私有。
编写构造方法赋予n1和n2初始值
再为该类定义加addition()、减subtration()、乘multiplication()、除division()等公有实例方法
分别对两个成员变量执行加、减、乘、除的运算。
在main方法中创建Number类的对象调用各个方法并显示计算结果
5、编写Java程序用于显示人的姓名和年龄。
定义一个人类Person该类中应该有两个私有属性姓名name和年龄age。
定义构造方法用来初始化数据成员。再定义显示display方法将姓名和年龄打印出来。
在main方法中创建人类的实例然后将信息显示
12. 继承(第十六天)
12.1. 复习变量的访问
- 类加载机制中,是这样的:在程序执行之前,凡是需要加载的类全部加载到JVM当中。
- 先完成加载才会执行main方法。
- 私有的是可以在本类中访问的。在其它类中必须使用set和get方法。
- 程序再怎么变化,万变不离其宗,有一个固定的规律:
- 所有的实例相关的都是先创建对象,通过“引用.”来访问。
- 所有的静态相关的都是直接采用“类名.”来访问。
- 大结论:
只要负责调用的方法a和被调用的方法b在同一个类当中:this. 可以省略、类名. 可以省略
12.2. 继承(extends)
12.2.1. 继承作用
- 什么是继承?
基本作用:子类继承父类,代码可以得到复用。(这个不是重要的作用,是基本作用。)
主要(重要)作用:因为有了继承关系,才有了后期的方法覆盖和多态机制。 - 继承的相关特性(六点):
- B类继承A类,则称A类为超类(superclass)、父类、基类,
B类则称为子类(subclass)、派生类、扩展类。
class A{}
class B extends A{}
我们平时聊天说的比较多的是:父类和子类
superclass 父类
subclass 子类
java 中的继承只支持单继承,不支持多继承
class B extends A,C{ } 这是错误的。
java 中不支持多继承,但有的时候会产生间接继承的效果。
class C extends B,class B extends A,
也就是说,C 直接继承 B其实 C 还间接继承 A。
java 中规定,子类继承父类,除构造方法不能继承之外,剩下都可以继承。
父类中private修饰的不能在子类中直接访问。可以通过间接的手段来访问。
java 中的类没有显示的继承任何类,则默认继承 Object类,Object类是 java 语言提供的
根类(老祖宗类),也就是说,一个对象与生俱来就有 Object类型中所有的特征。
java这么庞大的一个继承结构,最顶点是:Object继承也存在一些缺点:耦合度非常高,父类修改,子类受牵连。
12.3. day16天作业(详解)
12.4. 继承的补充
什么情况下可以使用继承
凡是采用“is a”能描述的,都可以继承。Cat is a Animal:猫是一个动物
Dog is a Animal:狗是一个动物
CreditAccount is a Account:信用卡账户是一个银行账户
假设以后的开发中有一个A类,有一个B类,A类和B类确实也有重复的代码,那么他们两个之间就
可以继承吗?
- 不一定,还是要看一看它们之间是否能够使用“is a”来描述。
- 任何一个类,没有显示继承任何类,默认继承Object,那么Object类当中有哪些方法呢?
以后慢慢的大家一定要适应看JDK的源代码(多看看牛人写的程序自己才会变成牛人。)
JDK源代码在什么位置? (根据自己的安装路径对应查找)
C:\Program Files\Java\jdk-13.0.2\lib\src.zip
- 你现在能看懂以下代码了吗?
System.out.println("Hello World!");
- System.out 中,out后面没有小括号,说明out是变量名。
- 另外System是一个类名,直接使用类名System.out,说明out是一个静态变量。
- System.out 返回一个对象,然后采用“对象.”的方式访问println()方法。
12.4.1 object类
- 子类继承父类
实质上,子类继承父类之后,是将父类继承过来的方法归为自己所有。实际上调用的也不是父类的
方法,是他子类自己的方法(因为已经继承过来了就属于自己的。)。 - 默认继承object类
- object类中有哪些方法?
public class Object {
// 注意:当源码当中一个方法以“;”结尾,并且修饰符列表中有“native”关键
字
// 表示底层调用C++写的dll程序(dll动态链接库文件)
private static native void registerNatives();
// 静态代码块
static {
// 调用registerNatives()方法。
registerNatives();
}
equals方法你应该能看懂。
// public是公开的
// boolean 是方法的返回值类型
// equals 是一个方法名:相等
// (Object obj) 形参public boolean equals(Object obj) {
//方法体
return (this == obj);
}
public String toString() {
return getClass().getName() + "@" +
Integer.toHexString(hashCode());
}
tostring方法
- System.out.println(引用);
当直接输出一个“引用”的时候,println()方法会先自动调用“引用.toString()”,然
后输出toString()方法的执行结果。
- System.out.println(引用);
public static void main(String[] args){
分析这个代码可以执行吗?
- ExtendsTest05.toString();
- 分析:不能执行,因为toString方法是实例方法不能通过类名的方式访问
// 实际上是内存地址经过“哈希算法”得出的十六进制结果。
System.out.println(retValue); // ExtendsTest05@2f92e0f4// 2f92e0f4 可以“等同”看做对象在堆内存当中的内存地址
public String toString() {
return getClass().getName() + "@" +
Integer.toHexString(hashCode());
}
12.5. Editplus字体颜色说明
- editPlus中蓝色是关键字
- 黑色是标识符
- 在 editplus中的红色字体,表示这个类是SUN的JDK写好的一个类。
13. 方法覆盖和多态
13.1. 方法覆盖
- 什么时候考虑方法的覆盖
父类中的方法无法满足子类的业务需求,子类有必要对继承过来的方法进行覆盖。
方法覆盖又叫:方法重写、override
什么条件满足的时候构成方法覆盖?
第一:有继承关系的两个类
第二:具有相同方法名、返回值类型、形式参数列表
第三:访问权限不能更低。
第四:抛出异常不能更多。//protected表示受保护的。没有public开放。
// 错误:正在尝试分配更低的访问权限; 以前为public
/*
protected void move(){
System.out.println("鸟儿在飞翔!!!");
}
*/
//错误:被覆盖的方法未抛出Exception
/*
public void move() throws Exception{
System.out.println("鸟儿在飞翔!!!");
}
*/
分析:这个sing()和父类中的sing(int i)有没有构成方法覆盖呢?
class Animal{
public void move(){
System.out.println("动物在移动!");
}
public void sing(int i){
System.out.println("Animal sing....
");
}
}
class Bird extends Animal{
public void sing(){
// 没有,原因是,这两个方法根本就是两个完全不同的方法。
// 可以说这两个方法构成了方法重载吗?可以。
System.out.println("Bird sing.....
");
}
}
关于Object类中toString()方法的覆盖?
toString()方法存在的作用就是:将java对象转换成字符串形式。- 大多数的java类toString()方法都是需要覆盖的。因为Object类中提供的toString()方法输出的是一个java对象的内存地址。
- 至于toString()方法具体怎么进行覆盖?
格式可以自己定义,或者听需求的。(听项目要求的。)
重要结论:
- 当子类对父类继承过来的方法进行“方法覆盖”之后,子类对象调用该方法的时候,一定执行覆盖之后的方法。
- 一定要注意:方法覆盖/重写的时候,建议将父类的方法复制粘贴,这样比较保险。
- 这里还有几个注意事项:(这几个注意事项,当学习了多态语法之后自然就明白了!)
注意1:方法覆盖只是针对于方法,和属性无关。
注意2:私有方法无法覆盖。
注意3:构造方法不能被继承,所以构造方法也不能被覆盖。
注意4:方法覆盖只是针对于“实例方法”,“静态方法覆盖”没有意义。
13.2. 关于Object类中的toString()方法
- toString()方法的作用是什么?
作用:将“java对象”转换成“字符串的形式” Object类中toString()方法的默认实现是什么?
public String toString() {
return getClass().getName() + “@” + Integer.toHexString(hashCode());
}
toString: 方法名的意思是转换成String
含义:调用一个java对象的toString()方法就可以将该java对象转换成字符串的表示形式。那么toString()方法给的默认实现够用吗?
- 不满意,希望输出:xxxx年xx月xx日
重写MyDate的toString()方法之前的结果
//System.out.println(t1.toString()); //MyDate@28a418fc
// 重写MyDate的toString()方法之后的结果
System.out.println(t1.toString());
System.out.println(t1);
MyDate t2 = new MyDate(2008, 8, 8);
System.out.println(t2); //2008年8月8日
13.3. 方法重载和方法覆盖有区别
方法重载发生在同一个类当中。
方法覆盖是发生在具有继承关系的父子类之间。
方法重载是一个类中,方法名相同,参数列表不同。
方法覆盖是具有继承关系的父子类,并且重写之后的方法必须和之前的方法一致: 方法名一致、参数列表一致、返回值类型一致。
13.4. 多态的基础语法
- 向上转型和向下转型的概念:
- 向上转型:子—->父 (upcasting)
又被称为自动类型转换:Animal a = new Cat(); - 向下转型:父—->子 (downcasting)
又被称为强制类型转换:Cat c = (Cat)a; 需要添加强制类型转换符。
什么时候需要向下转型?
需要调用或者执行子类对象中特有的方法,必须进行向下转型,才可以调用。
- 向上转型:子—->父 (upcasting)
向下转型有风险吗?
容易出现ClassCastException(类型转换异常)怎么避免这个风险?
instanceof运算符,可以在程序运行阶段动态的判断某个引用指向的对象是否为某一种类型。养成好习惯,向下转型之前一定要使用instanceof运算符进行判断。
不管是向上转型还是向下转型,首先他们之间必须有继承关系,这样编译器就不会报错。
- 注意事项:
- java中允许向上转型,也允许向下转型。
- 无论是向上转型,还是向下转型,两种类型之间必须有继承关系,没有继承关系编译器报
错。- 以后在工作过程中,和别人聊天的时候,要专业一些,说向上转型和向下转型,不要说
自动类型转换,也不要说强制类型转换,因为自动类型转换和强制类型转换是使用在基
本数据类型方面的,在引用类型转换这里只有向上和向下转型。
- 以后在工作过程中,和别人聊天的时候,要专业一些,说向上转型和向下转型,不要说
- 什么是多态
多种形态,多种状态,编译和运行有两个不同的状态。
- 编译期叫做静态绑定:绑定父类的方法。
- 运行期叫做动态绑定:动态绑定子类型对象的方法。
Animal a = new Cat();
// 编译的时候编译器发现a的类型是Animal,所以编译器会去Animal类中找
move()方法
// 找到了,绑定,编译通过。但是运行的时候和底层堆内存当中的实际对象有关
// 真正执行的时候会自动调用“堆内存中真实对象”的相关方法。
a.move();
- 多态的典型代码:父类型的引用指向子类型的对象。(java中允许这样写代码!!!
Test01.java 完整代码
public class Test01{
public static void main(String[] args){
Animal a1 = new Animal();
a1.move(); //动物在移动!!!
Cat c1 = new Cat();
c1.move(); //cat走猫步!
Bird b1 = new Bird();
b1.move(); //鸟儿在飞翔!!!
// 代码可以这样写吗?
/*
1、Animal和Cat之间有继承关系吗?有的。
2、Animal是父类,Cat是子类。
3、Cat is a Animal,这句话能不能说通?能。
4、经过测试得知java中支持这样的一个语法:
父类型的引用允许指向子类型的对象。
Animal a2 = new Cat();
a2就是父类型的引用。
new Cat()是一个子类型的对象。
允许a2这个父类型引用指向子类型的对象。
*/
Animal a2 = new Cat();
Animal a3 = new Bird();
/*
分析:a2.move();
java程序分为编译阶段和运行阶段。
先来分析编译阶段:对于编译器来说,编译器只知道a2的类型是Animal,所以编译器在检
查语法的时候,会去Animal.class字节码文件中找move()方法,找到了,绑定上move()方
法,编译通过,静态绑定成功。(编译阶段属于静态绑定。)
再来分析运行阶段:运行阶段的时候,实际上在堆内存中创建的java对象是Cat对象,所以
move的时候,真正参与move的对象是一只猫,所以运行阶段会动态执行Cat对象的move()方
法。这个过程属于运行阶段绑定。(运行阶段绑定属于动态绑定。)
*/
a2.move(); //cat走猫步!
分析这个程序能否编译和运行呢?
- 分析程序一定要分析编译阶段的静态绑定和运行阶段的动态绑定。
只有编译通过的代码才可能运行。没有编译,根本轮不到运行。
Animal a5 = new Cat(); // 底层对象是一只猫。
a5.catchMouse();
// 错误: 找不到符号
// why??? 因为编译器只知道a5的类型是Animal,去Animal.class文件中找catchMouse()方法
// 结果没有找到,所以静态绑定失败,编译报错。无法运行。(语法不合法。)
假设代码写到了这里,我非要调用catchMouse()方法怎么办?
- 这个时候就必须使用“向下转型”了。(强制类型转换)
// 以下这行代码为啥没报错????
// 因为a5是Animal类型,转成Cat,Animal和Cat之间存在继承关系。所以没报
错。
Cat x = (Cat)a5;
x.catchMouse(); //猫正在抓老鼠!!!!
- 这个时候就必须使用“向下转型”了。(强制类型转换)
// 向下转型有风险吗?
Animal a6 = new Bird();
这一行代码表面上a6是一个Animal,运行的时候实际上是一只鸟儿。/*
分析以下程序,编译报错还是运行报错???
编译器检测到a6这个引用是Animal类型,
而Animal和Cat之间存在继承关系,所以可以向下转型。
编译没毛病。
运行阶段,堆内存实际创建的对象是:Bird对象。
在实际运行过程中,拿着Bird对象转换成Cat对象
就不行了。因为Bird和Cat之间没有继承关系。
运行是出现异常,这个异常和空指针异常一样非常重要,也非常经典:
java.lang.ClassCastException:类型转换异常。
java.lang.NullPointerException:空指针异常。这个也非常重要。
*/
//Cat y = (Cat)a6;
//y.catchMouse();
什么时候必须进行向下转型?
调用子类对象上特有的方法时。
- java中“ . ‘’的用法
- java中只有“类名”或者“引用”才能去“点”
类名.
引用.
万变不离其宗,只要你想“点”,“点”前面要么是一个类名,要么是一个引用。
- java中只有“类名”或者“引用”才能去“点”
13.5. 运算符( instanceof )
- 怎么避免ClassCastException异常的发生???
- 新的内容,运算符:instanceof (运行阶段动态判断)
- 第一:instanceof可以在运行阶段动态判断引用指向的对象的类型。
- 第二:instanceof的语法:
(引用 instanceof 类型) - 第三:instanceof运算符的运算结果只能是:true/false
- 第四:c是一个引用,c变量保存了内存地址指向了堆中的对象。
假设(c instanceof Cat)为true表示:
c引用指向的堆内存中的java对象是一个Cat。
假设(c instanceof Cat)为false表示:
c引用指向的堆内存中的java对象不是一个Cat。 - 程序员要养成一个好习惯:任何时候,任何地点,对类型进行向下转型时,一定要使用instanceof 运算符进行判断。(java规范中要求的。)
- 这样可以很好的避免:ClassCastException
System.out.println(a6 instanceof Cat); //false
if(a6 instanceof Cat){ // 如果a6是一只Cat
Cat y = (Cat)a6; // 再进行强制类型转换
y.catchMouse();
}
}
- 这样可以很好的避免:ClassCastException
- 为什么使用 instanceof 运算符? 我们为什么还要进行instanceof的判断呢?
原因是:你以后可能肉眼看不到。
public class AnimalTest{
// test方法是程序员B负责编写。
// 这个test()方法的参数是一个Animal
public void test(Animal a){ // 实例方法
// 你写的这个方法别人会去调用。
// 别人调用的时候可能给你test()方法传过来一个Bird
// 当然也可能传过来一个Cat
// 对于我来说,我不知道你调用的时候给我传过来一个啥。
if(a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse();
}else if(a instanceof Bird){
Bird b = (Bird)a;sing();
}
}
}
- 养成好的素养 按照以下格式 编写程序
public class Test02{
public static void main(String[] args){
Animal x = new Bird();
Animal y = new Cat();
if(x instanceof Bird){
Bird b = (Bird)x;
b.sing();
} else if(x instanceof Cat){
Cat c = (Cat)x;
c.catchMouse();
}
if(y instanceof Bird){
Bird b = (Bird)y;
b.sing();
} else if(y instanceof Cat){
Cat c = (Cat)y;
c.catchMouse();
}
}
day18
+++
13.6. 多态在开发中的作用
- 多态在开发中有什么作用?
多态在开发中的作用是:降低程序的耦合度,提高程序的扩展力。
public class Master{
public void feed(Dog d){}
public void feed(Cat c){}
}
以上的代码中表示:Master和Dog以及Cat的关系很紧密(耦合度高)。导致扩展力很差。
public class Master{
public void feed(Pet pet){
pet.eat();
}
}
以上的代表中表示:Master和Dog以及Cat的关系就脱离了,Master关注的是Pet类。
这样Master和Dog以及Cat的耦合度就降低了,提高了软件的扩展性。
什么是软件扩展性?
- 假设电脑中的内存条部件坏了,我们可以买一个新的插上,直接使用。这个电脑的设计就考
虑了“扩展性”。内存条的扩展性很好。 - 面向父类型编程,面向更加抽象进行编程,不建议面向具体编程。因为面向具体编程会让
软件的扩展力很差
- 假设电脑中的内存条部件坏了,我们可以买一个新的插上,直接使用。这个电脑的设计就考
- 方法覆盖需要和多态机制联合起来使用才有意义
Animal a = new Cat();
a.move();
要的是什么效果?
编译的时候move()方法是Animal的。
运行的时候自动调用到子类重写move()方法上。
- 假设没有多态机制,只有方法覆盖机制,你觉得有意义吗?
- 没有多态机制的话,方法覆盖可有可无。
- 没有多态机制,方法覆盖也可以没有,如果父类的方法无法满足
- 子类业务需求的时候,子类完全可以定义一个全新的方法。
- 方法覆盖和多态不能分开
2、静态方法存在方法覆盖吗?
- 多态自然就和对象有关系了。
- 而静态方法的执行不需要对象。
- 所以,一般情况下,我们会说静态方法“不存在”方法覆盖。不探讨静态方法的覆盖。
public class OverrideTest05{
public static void main(String[] args){
// 静态方法可以使用“引用.
”来调用吗?可以
// 虽然使用“引用.
”来调用,但是和对象无关。
Animal a = new Cat(); //多态
// 静态方法和对象无关。
// 虽然使用“引用.
”来调用。但是实际运行的时候还是:Animal.doSome()
a.doSome();
Animal.doSome();
Cat.doSome();
}
}
class Animal{
// 父类的静态方法
public static void doSome(){
System.out.println("Animal的doSome方法执行!");
}
}
class Cat extends Animal{
// 尝试在子类当中对父类的静态方法进行重写
public static void doSome(){
System.out.println("Cat的doSome方法执行!");
}
}
- 面向对象的三大特征:封装、继承、多态
- 真的是一环扣一环。
- 有了封装,有了这种整体的概念之后。
- 对象和对象之间产生了继承。
- 有了继承之后,才有了方法的覆盖和多态。
- 软件ocp开发原则
- 这里提到了一个软件开发原则:
- 七大原则最基本的原则:OCP(对扩展开放,对修改关闭)
- 目的是:降低程序耦合度,提高程序扩展力。
- 面向抽象编程,不建议面向具体编程。
- 解释之前遗留的问题:
- 私有方法无法覆盖。
public class OverrideTest06{
// 私有方法
private void doSome(){
System.out.println("OverrideTest06's private method doSome
execute!");
}
// 入口
public static void main(String[] args){
// 多态
OverrideTest06 ot = new T();
ot.doSome(); //OverrideTest06's private method doSome execute!
}
}
/*
// 在外部类中无法访问私有的。
class MyMain{
public static void main(String[] args){
OverrideTest06 ot = new T();
//错误: doSome() 在 OverrideTest06 中是 private 访问控制
//ot.doSome();
}
}
*/
// 子类
class T extends OverrideTest06{
// 尝试重写父类中的doSome()方法
// 访问权限不能更低,可以更高。
public void doSome(){
System.out.println("T's public doSome method execute!");
}
}
- 方法覆盖只是针对于“实例方法”,“静态方法覆盖”没有意义。(这是因为方法覆盖通常和多态联合 起来)
- 总结两句话: 私有不能覆盖; 静态不谈覆盖。
- 在方法覆盖中,关于方法的返回值类型。
什么条件满足之后,会构成方法的覆盖呢?
发生具有继承关系的两个类之间。
父类中的方法和子类重写之后的方法:具有相同的方法名、相同的形式参数列表、相同的返
回值类型。 - 返回值类型
- 学习了多态机制之后:
- “相同的返回值类型”可以修改一下吗?
- 对于返回值类型是基本数据类型来说,必须一致。
- 对于返回值类型是引用数据类型来说,重写之后返回值类型可以变的更小(但意义不大,实际开发中没人这样写).
- “相同的返回值类型”可以修改一下吗?
14. super
14.1. super 基本概念
- super是一个关键字,全部小写。
- super和this对比着学习。
- super、this关键字
- this回顾:
this能出现在实例方法和构造方法中。
this的语法是:“this.”、“this()”
this不能使用在静态方法中。
this. 大部分情况下是可以省略的。
this.什么时候不能省略呢? 在区分局部变量和实例变量的时候不能省略。
public void setName(String name){
this.name = name;
}
- this回顾:
this() 只能出现在构造方法第一行,通过当前的构造方法去调用“本类”中其它的构造方
法,目的是:代码复用。
super:
super能出现在实例方法和构造方法中。
super的语法是:“super.”、“super()”
super不能使用在静态方法中。
super. 大部分情况下是可以省略的。- super.什么时候不能省略呢? ???????
- super() 只能出现在构造方法第一行,通过当前的构造方法去调用“父类”中的构造方法,目的是:创建子类对象的时候,先初始化父类型特征。
- super.什么时候不能省略呢? ???????
super():表示通过子类的构造方法调用父类的无参构造方法。
- 模拟现实世界中的这种场景:要想有儿子,需要先有父亲。
- 重要的结论:
- 当一个构造方法第一行:既没有this()又没有super()的话,默认会有一个super();
表示通过当前子类的构造方法调用父类的无参数构造方法。所以必须保证父类的无参数
构造方法是存在的。
- 当一个构造方法第一行:既没有this()又没有super()的话,默认会有一个super();
- 注意:this()和super() 不能共存,它们都是只能出现在构造方法第一行。
- 无论是怎样折腾,父类的构造方法是一定会执行的。(百分百的。)
2.super注意事项:
- super能出现在实例方法和构造方法中。
- super的语法是:“super.”、“super()”
- super不能使用在静态方法中。
- super. 大部分情况下是可以省略的。
- super.什么时候不能省略呢?
父类和子类中有同名属性,或者说有同样的方法,想在子类中访问父类的,super. 不能省略。 - super() 只能出现在构造方法第一行,通过当前的构造方法去调用“父类”中的构造方法,目的是:
创建子类对象的时候,先初始化父类型特征。 - super的使用:
super.属性名 【 访问父类的属性】
super.方法名(实参) 【访问父类的非私有方法】
super(实参) 【调用父类的构造方法】
- 判断程序的输出结果:
```public class SuperTest02{
public static void main(String[] args){
new C();
}
}
/*
class Object{
public Object(){
}
}
*/
class A extends Object{
public A(){
System.out.println("1"); //1
}
}
class B extends A{
public B(){
System.out.println("2"); //2
}
public B(String name){
super();
System.out.println("3"); // 3
}
}
class C extends B{
public C(){ // 这个是最先调用的。但是最后结束。
this("zhangsan");
System.out.println("4");//4
}
public C(String name){
this(name, 20);
System.out.println("5");//5
}
public C(String name, int age){
super(name);
System.out.println("6");//6
}
}
//结果: 1 3 6 5 4
- 程序分析:
- 在java语言中不管是是new什么对象,最后老祖宗的Object类的无参数构造方法一定会执 行。(Object类的无参数构造方法是处于“栈顶部”)
- 栈顶的特点:最后调用,但是最先执行结束。后进先出原则。
- 大家要注意: 以后写代码的时候, 一个类的无参数构造方法还是建议大家手动的写出来。如果无参数构造方 法丢失的话,可能会影响到“子类对象的构建” ```
14.2. super(实际参数列表)
- 注意:在构造方法执行过程中一连串调用了父类的构造方法,父类的构造方法又继续向下调用它的
父类的构造方法,但是实际上对象只创建了一个。 - 思考:“super(实参)”到底是干啥的?
super(实参)的作用是:初始化当前对象的父类型特征。
并不是创建新对象。实际上对象只创建了1个。
3. super关键字代表什么呀?
super关键字代表的就是“当前对象”的那部分父类型特征。
4. 测试程序
public class SuperTest03{
public static void main(String[] args){
CreditAccount ca1 = new CreditAccount();
System.out.println(ca1.getActno() + "," + ca1.getBalance() +","+ca1.getCredit());
CreditAccount ca2 = new CreditAccount("1111", 10000.0, 0.999);
System.out.println(ca2.getActno() + "," + ca2.getBalance() +","+ca2.getCredit());
}
}
}
// 账户
class Account extends Object{
// 属性
private String actno;
private double balance;
// 构造方法
public Account(){
//super();
//this.actno = null;
//this.balance = 0.0;
}
public Account(String actno, double balance){
// super();
this.actno = actno;
this.balance = balance;
}
// setter and getter
public void setActno(String actno){
this.actno = actno;
}
public String getActno(){
return actno;
}
public void setBalance(double balance){
this.balance = balance;
}
public double getBalance(){
return balance;
}
}
// 信用账户
class CreditAccount extends Account{
// 属性:信誉度(诚信值)
// 子类特有的一个特征,父类没有。
private double credit;
// 构造方法
// 分析以下程序是否存在编译错误????
public CreditAccount(String actno, double balance, double credit){
// 私有的属性,只能在本类中访问。
/*
this.actno = actno;
this.balance = balance;
*/
// 以上两行代码在恰当的位置,正好可以使用:super(actno, balance);
// 通过子类的构造方法调用父类的构造方法。
super(actno, balance);
this.credit = credit;
}
// 提供有参数的构造方法
public CreditAccount(){
//super();
//this.credit = 0.0;
}
// setter and getter方法
public void setCredit(double credit){
this.credit = credit;
}
public double getCredit(){
return credit;
}
}
- 以上代码的内存图
- 以上代码的super原理内存图:
14.3. super和this的关系
- super表示的是当前对象的父类型特征。(super是this指向的那个对象中的一块空间。)
- 参考代码如下:
public class SuperTest04{
public static void main(String[] args){
Vip v = new Vip("张三");
v.shopping();
}
}
class Customer{
String name;
public Customer(){}
public Customer(String name){
super();
this.name = name;
}
}
class Vip extends Customer{
public Vip(){}
public Vip(String name){
super(name);
}
// super和this都不能出现在静态方法中。
public void shopping(){
// this表示当前对象。
System.out.println(this.name + "正在购物!");
// super表示的是当前对象的父类型特征。(super是this指向的那个对象中的一块空
间。)
System.out.println(super.name + "正在购物!");
System.out.println(name + "正在购物!");
}
}
- 程序内存图:
14.4. super.不能省略的情况
- “this.”和“super.”大部分情况下都是可以省略的。
- this. 什么时候不能省略?
public void setName(String name){
this.name = name;
} - super. 什么时候不能省略?
父中有,子中又有,如果想在子中访问“父的特征”
,super. 不能省略。 - java中允许在子类中出现和父类一样的同名变量/同名属性。
- java是怎么来区分子类和父类的同名属性的?
this.name:当前对象的name属性
super.name:当前对象的父类型特征中的name属性。 源码如下
public class SuperTest05{
public static void main(String[] args){
Vip v = new Vip("张三");
v.shopping();
}
}
class Customer {
String name;
public Customer(){}
public Customer(String name){
super();
this.name = name;
}
public void doSome(){
System.out.println(this.name + " do some!");
System.out.println(name + " do some!");
//错误: 找不到符号
//System.out.println(super.name + " do some!");
}
}
class Vip extends Customer{
// 假设子类也有一个同名属性
// java中允许在子类中出现和父类一样的同名变量/同名属性。
String name; // 实例变量
public Vip(){
}
public Vip(String name){
super(name);
// this.name = null;
}
public void shopping(){
/*
java是怎么来区分子类和父类的同名属性的?
this.name:当前对象的name属性
super.name:当前对象的父类型特征中的name属性。
*/
System.out.println(this.name + "正在购物!"); // null 正在购物
System.out.println(super.name + "正在购物!"); // 张三正在购物
System.out.println(name + "正在购物!"); //null 正在购物
}
}
- 程序内存图
14.5. super 基本概念(二)
通过已下测试得出的结论:
- super 不是引用,super也不保存内存地址,super也不指向任何对象。
- super 只是代表当前对象内部的那一块父类型的特征。
- 关键字super的作用:
- 用来访问父类被隐藏的非私有成员变量
- 用来调用父类中被重写的方法
- 用来调用父类的构造函数
public class SuperTest06 {
// 实例方法
public void doSome(){
// SuperTest06@2f92e0f4
System.out.println(this);
// 输出“引用”的时候,会自动调用引用的toString()方法。
//System.out.println(this.toString());
//编译错误: 需要'
.
'
//System.out.println(super);
}
// this和super不能使用在static静态方法中。
/*
public static void doOther(){
System.out.println(this);
System.out.println(super.xxx);
}
*/
// 静态方法,主方法
public static void main(String[] args){
SuperTest06 st = new SuperTest06();
st.doSome();
// main方法是静态的
// 错误的。
/*
System.out.println(this);
System.out.println(super.xxxx);
*/
}
}
在父和子中有同名的属性,或者说有相同的方法,如果此时想在子类中访问父中的数据,必须使用
“super.”加以区分。- super.属性名 【访问父类的属性】
super.方法名(实参) 【访问父类的方法】
super(实参) 【调用父类的构造方法】 - 参考代码如下:
public class SuperTest07{
public static void main(String[] args){
/*
Cat move!
Cat move!
Animal move!
*/
Cat c = new Cat();
c.yiDong();
}
}
class Animal{
public void move(){
System.out.println("Animal move!");
}
}
class Cat extends Animal{
// 对move进行重写。
public void move(){
System.out.println("Cat move!");
}
// 单独编写一个子类特有的方法。
public void yiDong(){
this.move();
move();
// super. 不仅可以访问属性,也可以访问方法。
super.move();
}
}
14.6. 上册单词总结:
- 栈溢出错误:StackOverflowError
- 空指针异常:NullPointer
- 父类:superclass
- 子类:subclass
- 类型转换异常:ClassCastException
- 关键字:instanceof 类型判断