Java自我学习路线
一、方法
1. 方法的本质及作用
- 方法其实就是一段普通的代码片段,并且这段代码可以完成某个特定的功能,而且可以被重复的调用,Java 中的方法又叫做 method,在 C 语言中叫做函数
- 方法定义在类体当中;方法定义的先后顺序没有关系
- 程序开始执行的时候是先执行 main 方法,因为 main 方法是一个入口,而且 main 方法不需要手动调用,是由JVM调用的,其他方法需要手动调用
- 在Java语言中所有方法体中的代码都必须遵循自上而下的顺序依次逐行执行
2. 方法的定义
- 语法机制
[修饰符列表] 返回值类型 方法名 (形式参数列表){
方法体; //方法体由Java语句构成
}
修饰符列表是可选的,不是必须的
方法只有调用的时候才会执行,不调用不执行
2.1 返回值类型
2.1.1 什么是返回值
返回值是一个方法执行结束之后的结果,结果通常是一个数据,所以被称为“值”
返回值可以是Java中任何合法的数据类型
2.1.2 什么是返回值类型
方法结束之后大部分情况下都是有一个结果的,体现结果的一般都是数据,而数据有类型之分,所以被称为返回值类型
2.1.3 return的作用
- “return;”的作用是用来终止当前的方法
- “return 值;”的作用是返回方法的执行结果
- 当一个方法执行结束不返回任何值时,返回值类型要求不能空白,则需写上 void 关键字,所以 void 表示该方法执行结束后不返回任何结果
- 如果返回值类型是 void,那么在方法体中不能有“return 值;”这样的语句,但是可以有“return;”语句
- 如果返回值类型不是 void ,则在方法体执行结束的时候必须使用"return 值;"来完成“值”的返回,否则编译报错
只要有“return”关键字的语句执行,当前方法必然结束 - 除了 void 之外,剩下的都必须有“return 值;”这样的语句
- 在同一个域中,return语句下面不能再编写其他代码,否则编译报错
2.2 方法名
- 方法名要见名知意(驼峰命名)
- 方法名在标识符命名规范当中,要求首字母小写,后面每个单词首字母大写,并且只要是合法的标识符就行
附:标识符的命名规则与规范
2.3 形式参数列表(形参)
- 形参中的每一个参数都是“局部变量”,方法结束之后内存释放
- 形参的个数是0~n个
- 形参有多个则使用英文逗号隔开
- 形参的数据类型起决定性作用,形参对应的变量名是随意的
2.4 方法体
- 方法体中的代码遵循自上而下的顺序依次逐行执行
- 方法体当中编写的是业务逻辑代码,完成某个特定的功能,处理业务逻辑代码的时候需要数据,数据来源就是形参
3. 方法的调用
- 方法只有调用的时候才会执行,不调用不执行
- 语法机制
类名.方法名(实际参数列表);
- 以上是标准语法,如果在同一个类中调用时,则可以省略类名,直接 方法名(实际参数列表);
- 方法执行结束之后的返回值实质上给了调用者,及谁调用就返回给谁,并且使用一个对应类型的变量接收这个方法的返回值
- 对于没有返回值的方法,变量不能接收
- 对于有返回值的方法,变量可以选择不接收(但是此举没有意义),但是会正常返回,返回之后内存马上释放,因为没有使用变量接收
3.1 实际参数列表
- 调用方法时传递的实际数据
- 实参与形参必须一一对应,即类型、个数要对应
3.2 类名
- 方法在同一个类中,“类名.”可以省略,如果不在同一个类中(跨类调用),“类名.”不能省略
- 不止是main方法可以调用其他方法
4. 方法执行过程中内存的变化
引用图片:
- 方法区:
存储代码片段,存储xxx.class字节码文件,这个空间是最先有数据的,类加载器首先将代码加载到这里 - 栈内存(Stack):
存储方法执行时的栈帧,以及存储每个方法执行时所需要的内存空间,因为局部变量存在于方法中,所以栈最主要存储的是局部变量 - 堆内存(Heap):
主要存储 new 出来的对象,以及对象内部的实例变量,对象进行实例化后在堆内存中为其分配地址
凡是通过new运算符创建的对象,都存储在堆内存中,new运算符的作用就是在堆内存中开辟一块空间 - Java 程序开始执行的时候先通过类加载器子系统找到硬盘上的字节码(class)文件,然后将其加载到 Java 虚拟机的方法区当中,开始调用 main 方法,main 方法被调用的瞬间,会给 main 方法在“栈”内存中分配所属的活动空间,此时发生压栈动作,main 方法的活动空间处于栈底
- 方法只定义不去调用的话,只是把它的代码片段存储在方法区当中,Java 虚拟机是不会在栈内存当中给该方法分配活动空间的,只有在调用的瞬间,Java 虚拟机才会在“栈内存”当中给该方法分配活动空间,此时发生压栈动作,直到这个方法执行结束的时候,这个方法在栈内存中所对应的活动空间就会释放掉,此时发生弹栈动作。由于栈的特点是先进后出,所以最先调用的方法(最先压栈)一定是最后结束的(最后弹栈)
二、方法重载
1. Java中怎么区分方法
- Java编译器会通过方法名进行区分,但是在Java语言中允许方法名相同的情况出现
- 方法名相同时,Java编译器会通过方法的参数类型进行方法的区分
2. 方法重载什么时候使用
同一个类中,多个方法的功能相似,可以使用相同的方法名进行方法重载
3. 方法重载的条件
- 在同一个类中
- 方法名相同
- 形式参数列表不同(参数的个数、类型、顺序不同)
只要同时满足以上三个条件,则可以认为方法与方法之间发生了重载机制
- 方法重载和方法的“返回值类型”无关
- 方法重载和方法的“修饰符列表”无关
三、方法递归
1. 什么是方法递归
方法自身调用自身
- 使用递归的时候,必须添加结束条件,没有结束条件,会发生栈内存溢出错误:Exception in thread “main” java.lang.StackOverflowError
原因:一直压栈,没有弹栈,栈内存不够用 - 假设递归是有合法的结束条件的,那么也有可能发生栈内存溢出错误,因为有可能递归的太深,一直压栈,导致栈内存不够
- 在实际开发过程中,不建议轻易的选择递归,尽量使用循环(for、while、do…while),因为循环的效率高,耗费的内存小,递归耗费内存大,使用不当还可能造成JVM崩溃(但在极少数的情况下,不用递归,这个程序不能实现)
- 递归优缺点
递归使程序结构更简洁
递归占用CPU的处理时间更多;要消耗大量的内存空间,程序执行慢,甚至无法执行;递归法比递推法的执行效率低
2. 解决StackOverflowError思路
- 检查递归的结束条件是否正确
- 若递归条件没问题,那么手动调整JVM的栈内存初始化大小
java -X查看调整堆栈大小的参数
java -Xss xxxGB xx.java //指定Java文件栈内存大小
- 重复第二步,继续手动调整JVM的栈内存初始化大小
四、方法重写(覆盖)
1. 什么时候需要方法重写
- 父类中继承过来的方法无法满足当前子类业务的需求时
2. 方法重写的条件
- 方法重写发生在具有继承关系的父子类之间
- 重写之后的方法与原方法具有相同的方法名、相同的形式参数列表
- 重写之后的方法不能比原方法拥有更低的访问权限,可以更高
- 重写之后的方法不能比原方法抛出更多的异常,可以相同或更少
- 方法重写的条件中有一条 :相同的返回值类型(可以写成对于返回值类型是基本数据类型来说,必须一致,对于引用类型来说,大转小可以,小转大不可以,即对于引用数据类型来说,重写之后返回值类型可以变得更小,但意义不大,实际开发中没人这样写)
3. 注意事项
- 私有的方法不能被继承,所以不能被重写
- 构造方法不能被继承,所以也不能被重写
- 方法重写只是和方法有关,和属性无关
- 方法重写只针对于实例方法,静态方法不存在重写(因为静态方法执行不需要对象)
public class Animal {
public void move() {
System.out.println("Animal move...");
}
}
public class Bird extends Animal {
// 子类Bird对move()方法重写(父类Animal中继承过来的方法无法满足当前子类Bird业务fly的需求)
@Override
public void move() {
System.out.println("Bird fly...");
}
}
public class Dog extends Animal {
// 子类Dog对move()方法重写(父类Animal中继承过来的方法无法满足当前子类Dog业务run的需求)
@Override
public void move() {
System.out.println("Dog run...");
}
}
public class AnimalTest {
public static void main(String[] args) {
// 创建子类Bird对象
Bird bird = new Bird();
bird.move(); // 输出Bird fly...
// 创建子类Dog对象
Dog dog = new Dog();
dog.move(); // 输出Dog run...
}
}