方法
- 方法:实现了代码复用
[修饰符列表] + 返回值 + 方法名 + (形式参数列表){
方法体;
}
形式参数列表中每一个参数都是一个局部变量,方法结束之后内存释放;
形参的数据类型起决定性作用,形参对应的变量名是随意的
方法调用语句: 类名.方法名
public class MethodTest01 {
public static void main(String[] args) {
MethodTest01.pr1();
MethodTest01.pr2();//若方法都在同一个类中,类名.是可以省略的
}
public static void pr1(){}
public static void pr2(){}
}
class My{
//若在一个类中调用另一个类的方法,类名.是不能省略的
}
方法调用时参数传递
public static void main(String[] args){
Person p = new Person();
p.age = 10;
add(p);
System.out.println(p.age); //11
}
public static void add(Person p){
p.age++;
System.out.println(p.age); //11
}
以上的两个p,在栈中属于两个不同的方法栈帧,但是指向了堆内存中的同一个对象,所以访问到了同一个实例变量
练习题
生成五位验证码:
StringBuilder builder = new StringBuilder();
char[] chs = new char[52];
for (int i = 0; i < 52; i++) {
chs[i] = i < 26 ? (char)('A' + i) : (char)('a' + (i - 26)); //注意减26
}
Random random = new Random();
for (int i = 0; i < 4; i++) {
builder.append(chs[random.nextInt(chs.length)]);
}
builder.append(random.nextInt(10));
内存结构
栈内存 | 堆内存 | 方法区 |
---|---|---|
方法执行所需的空间及该方法的局部变量 | 对象及对象的实例变量 | 类加载器将字节码文件装在到JVM的时候,会将字节码文件存入方法区,存储的是代码片段 |
局部变量 | 对象 | 静态变量 |
- 类先加载:方法区先有数据
- new 实际上是在堆内存中开辟了一块空间
- 栈:方法被调用时,该方法需要的内存空间在栈中分配
局部变量:只在方法体中有效,方法结束后局部变量的内存空间就被释放了
注意:
- 方法区最先有数据,方法区中存放代码片段,class字节码
- 栈内存:方法调用时,该方法所需的内存在栈中分配,方法不调用时不会在栈中分配内存的
栈内存 stack
存储:局部变量。
- 基本数据类型的局部变量,在栈内存中存储的是“数据值”。
- 引用数据类型的局部变量,在栈内存中存储的是“地址值”。
栈内存特点:
- 栈内存具备“先进后出”或“后进先出”的特点。
- 栈内存是一块连续的内存空间,由系统自动分配,效率高!
- 虚拟机会为每个线程创建一个栈内存,用于存放该线程执行方法的信息。
栈内存的组成:
堆内存heap
存储:数组或对象
- 在堆内存中创建数组或对象成功后,还可以在栈内存中定义一个局部变量,让该局部变量来存储创建在堆内存中数组或对象的首地址,这样该变量就引用了堆内存中的数组或对象。
- 数组和对象在没有变量指向(引用)它的时候,则数组或对象本身占用的堆内存空间就变成垃圾,然后就被Java虚拟机的自动垃圾回收器(GC)释放了该存储空间,这样该数组或对象就不能被使用了,否则就会抛出空指针异常(NullPointerException)。
堆内存特点:
- 虚拟机中只有一个堆内存,被所有的线程共享。
- 堆内存是一个不连续的内存空间,分配灵活,效率低!
方法重载
方法重载机制:功能相似的,可以让其方法名相同
编译器在区分的时候,会首先根据方法名进行区分;如果方法名相同的,再用参数类型进行区分 (也就是说返回值类型是无法区分的)
重载时通过参数的静态类型而不是实际类型进行区分;静态类型在编译期可知,在编译阶段,javac编译器就根据静态类型决定来调用哪个版本的方法。
静态分派发生在编译阶段,确定静态分派的动作实际上不是由虚拟机执行的。
满足三个条件:
- 在同一个类当中
- 方法名不同
- 参数列表不同
- 方法重载与返回值类型、修饰符列表无关
对于println方法来说,参数类型是随意的,说明println()是方法重载
System.out.println();
也可以手动对println方法进行封装:
class S{
public static void p(){
System.out.println();
}
public static void p(byte b){
System.out.println(b);
}
public static void p(int b){
System.out.println(b);
}
public static void p(long b){
System.out.println(b);
}
public static void p(char b){
System.out.println(b);
}
public static void p(String b){
System.out.println(b);
}
public static void p(boolean b){
System.out.println(b);
}
public static void p(float b){
System.out.println(b);
}
public static void p(double b){
System.out.println(b);
}
}
调用的时候就可以:
public class OverLoadTest01 {
public static void main(String[] args) {
S.p(10);
S.p(10.0);
S.p(true);
}
}
注意:源码当中一个方法以;结尾并且用native关键字修饰 表示调用了底层C++写的dll程序
构造方法重载
构造方法默认支持方法重载,因为方法名必须和类名保持一致
但是注意,方法重载要求参数列表不同,也就是说:
public Person(String name){
}
public Person(String age){
}
这两个构造方法是不能重载的,因为参数名称不同并不是参数列表不同。
构造方法虽然在没有返回值类型,但是执行结束之后会返回会该对象的内存地址,也就是用定义好的 引用 来接受
char类型参数的方法重载
public static void main(String[] args) {
sayHi('a');
}
public static void sayHi(int i){
}
public static void sayHi(char c){
}
这时调用的是char类型参数的sayHi方法,如果注释调这个方法,只保留int类型的sayHi,编译运行都是没问题的,因为 'a'
除了代表一个字符之外,还可以代表数字97
以下程序是没有问题的:
public static void main(String[] args) {
sayHi('a'); //char自动转换为int
}
public static void sayHi(int i){
System.out.println(i);
}
// public static void sayHi(char c) {
//
// }
但是如果注释int类型参数的方法,在main方法中就不能将int类型赋值给char类型:
public static void main(String[] args) {
sayHi(97); //java: 不兼容的类型: 从int转换到char可能会有损失
}
/* public static void sayHi(int i){
System.out.println(i);
}*/
public static void sayHi(char c) {
}
编译器只知道传递的参数是int类型的,不知道具体的值
byte、short类型参数的重载
public class Overload {
public static void sayHello(byte arg){
System.out.println("hello byte");
}
public static void sayHello(short arg){
System.out.println("hello short");
}
public static void main(String[] args) {
sayHello(1);
}
}
编译报错:
java: 对于sayHello(int), 找不到合适的方法
方法 Overload.sayHello(byte)不适用
(参数不匹配; 从int转换到byte可能会有损失)
方法 Overload.sayHello(short)不适用
(参数不匹配; 从int转换到short可能会有损失)
因为字面量默认被当作int类型处理,int类型不能自动转型到byte或short,要解决这个问题,就要指明参数的静态类型:
sayHello((byte) 1);
sayHello((short) 1);
可变长参数
例如:
public static void test(int...num){
}
表示test方法可以接受0 - n个int类型的参数,也就是可以不传参数,也可以传多个参数
public static void main(String[] args) {
test();
test(1);
test(1,2,3,4);
}
public static void test(int...num){
System.out.println("test method execute");
}
注意:可变长参数列表只能出现1个,并且只能出现在参数列表的最后一个位置上
另外,可变长参数可以当作数组处理:
public static void test(int...num){
for (int i = 0; i < num.length; i++) {
System.out.print(num[i]);
}
System.out.println();
}
可变长参数可以是任何类型的值
class Group {
private String[] names;
public void setNames(String... names) {
this.names = names;
}
}
上面的setNames()
就定义了一个可变参数。调用时,可以这么写:
Group g = new Group();
g.setNames("Xiao Ming", "Xiao Hong", "Xiao Jun"); // 传入3个String
g.setNames("Xiao Ming", "Xiao Hong"); // 传入2个String
g.setNames("Xiao Ming"); // 传入1个String
g.setNames(); // 传入0个String
完全可以把可变参数改写为String[]
类型:
class Group {
private String[] names;
public void setNames(String[] names) {
this.names = names;
}
}
但是,调用方需要自己先构造String[]
,比较麻烦。例如:
Group g = new Group();
g.setNames(new String[] {"Xiao Ming", "Xiao Hong", "Xiao Jun"}); // 传入1个String[]
另一个问题是,调用方可以传入null
:
Group g = new Group();
g.setNames(null);
而可变参数可以保证无法传入null
,因为传入0个参数时,接收到的实际值是一个空数组而不是null
。
方法递归
归化递归
方法递归三要素:
- 递归公式
- 递归终结点
- 递归方向必须走向终结点
计算 n!
归化递归都可以根据以上三要素计算出递归过程
递归公式:f(n) = f(n - 1) * n
递归终结点:f(1) = 1
递归方向必须走向终结点:从 f(10)
递向f(1)
,所以递归公式是f(n) = f(n - 1) * n
public static int factorial(int n){
if (n == 1)
return 1;
return factorial(n - 1) * n;
}
猴子摘桃
猴子第一天摘下若干个桃子,当即吃了一半,还不过瘾,又多吃了一个 第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少。
递归公式:f(n + 1) = f(n) / 2 - 1
递归终结点:f(10) = 1
递归方向:f(1)
递归到 f(10)
,所以要改变递归公式为 f(n) = 2 * f(n + 1) + 2
public static int monkey (int day){
if(day == 10)
return 1;
return 2 * monkey(day + 1) + 2;
}
上台阶
小明一次可以爬一阶或两阶楼梯,20层楼有几种走法?
由以上内容可知:
$$
F(20) = F(19) + F(18)
$$
走到20层楼的走法 = 走到19楼再走向20楼的走法 + 走到18楼再走向20楼的走法是一致的。
- 走到19楼再走向20楼的走法:上一阶
- 走到18楼再走向20楼的走法:上两阶,因为从18楼上一阶的走法和19楼再走向20楼的上一阶走法重复了
递归公式为:f(n) = f(n - 1) + f(n - 2)
递归终结点为:f(1) = 1
到1楼有一层走法 和 f(2) = 2
到2楼有两层走法
递归方向:f(20)
到 f(1)
f(2)
非归化递归
见[[011-IO#统计每种文件个数并打印|统计每种文件个数并打印 BFS]]
标签:String,int,void,003,static,方法,public From: https://www.cnblogs.com/euneirophran/p/18069229