Saul's blog Saul's blog
首页
后端
分布式
前端
更多
分类
标签
归档
友情链接
关于
GitHub (opens new window)

Saul.J.Wu

立身之本,不在高低。
首页
后端
分布式
前端
更多
分类
标签
归档
友情链接
关于
GitHub (opens new window)
  • Java入门基础

    • 计算机常识

    • Java语言概述

    • 基本语法

    • 数组

    • 面向对象

      • 面向对象
      • 类和对象
      • 类的成员-属性
      • 类的成员-方法
        • 类的成员
        • 方法method
        • 语法格式
        • 方法的调用
        • 方法的重载
        • 可变形参的方法
        • 方法参数的值传递机制
          • 面试题
          • 面试题2
          • 面试题3
          • 练习
        • 递归 recursion
      • 类的成员-构造器
      • 面向对象特性-封装
      • this关键字
      • package关键字
      • import关键字
      • 面向对象特征-继承
      • 方法的重写
      • 访问权限修饰符
      • super关键字
      • 子类对象实例化的全过程
      • 面向对象特征-多态
      • 强制类型转换
      • Object类
      • 包装类
      • static关键字
      • 单例设计模式
      • main方法
      • 类的成员-代码块
      • final关键字
      • 抽象类与抽象方法
      • 接口
      • 类的成员-内部类
    • 异常处理

  • Java核心基础

  • 设计模式

  • Web开发

  • SpringBoot

  • 微服务

  • Elasticsearch

  • 运维

  • 后端
  • Java入门基础
  • 面向对象
SaulJWu
2020-12-11

类的成员-方法

# 类的成员

Java的基本单位是类class,Java成员即类中的成员,包括以下五个部分:

  • 属性
  • 方法
  • 构造器
  • 代码块/初始化块
  • 内部类

# 方法method

什么是方法?

方法是类或对象行为特征的抽象,用来完成某个功能操作。在某些语言中也称为函数或过程。

将功能封装为方法的目的是,可以实现代码重用,简化代码。

==Java里的方法不能独立存在,所有的方法必须定义在类里。==

方法的声明与使用

# 语法格式

修饰符 返回值类型 方法名(形参列表){
	……
	return 返回值;
}
1
2
3
4
  • 修饰符

    • public,缺省,private, protected
    • static、final (暂不考虑)
  • 返回值类型:声明出返回值的类型。与方法体中“return 返回值”搭配使用

    • 可以是基本数据类型
    • 如果返回值类型是void,代表没有返回值,不需要用return
    • 也可以是==对象==
  • 方法名:属于标识符,命名时遵循标识符命名规则和规范,“见名知意”

    形参列表:可以包含零个,一个或多个参数。多个参数时,中间用“,”隔开

    • 参数类型可以是基本数据类型,也可以是对象
    • 语法格式:数据类型 变量名1,数据类型 变量名2,数据类型 变量名3……
    • 形参属于局部变量。
  • return:代表方法结束,作用是方法在执行完毕后返还给调用它的程序的数据。来返回指定类型的变量或常量。

    • 如果返回值类型是void,就不用使用return关键字;
    • 在同一个作用域(就是大括号的意思),return后面不能有执行语句
  • 返回值:可以是变量或者常量,配合return关键字一起用

    • 返回值的类型必须和返回值类型是一致的。

例如:

public class Person{
    private String name;
    private int age;
    
    public int getAge(){
        return age;
    }
    
    public void setAge(int i){
        age = i;
    }
    
    public void eat(){
        System.out.println("吃饭");
    }
    
    public void sleep(int hour){
        System.out.println("睡了"+hour+"小时");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

如果按照是否有形参及返回值来对方法分类:

无返回值 有返回值
无形参 void 方法名(){} 返回值的类型方法名(){}
有形参 void 方法名(形参列表){} 返回值的类型方法名(形参列表){}

# 方法的调用

方法通过方法名被调用,且只有被调用才会执行

方法的调用过程分析:

image-20201210211004197

注意:

  • 方法被调用一次,就会执行一次

  • 没有具体返回值的情况,返回值类型用关键字void表示,那么方法体中可以不必使用return语句。如果使用,仅用来结束方法。

  • 定义方法时,方法的结果应该返回给调用者,交由调用者处理。

  • 方法中只能调用方法或属性,不可以在方法内部定义方法。

# 方法的重载

重载的概念overload

在同一个类中,存在多个同名的方法,方法的参数个数或者参数类型不同,与返回值无关,这叫做重载。

重载的特点

与返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表的不同来区别。

重载实例

//返回两个整数的和
int add(intx,inty){return x+y;}
//返回三个整数的和
intadd(intx,inty,intz){return x+y+z;}
//返回两个小数的和
double add(double x,doubley){return x+y;}
1
2
3
4
5
6

在通过对象调用方法时,如何确定某一个指定的方法

看好方法名和参数列表,重载的参数列表都是不相同的,所以只要找到对应的方法所需要的参数,就可以了。

其实我们用到最多的是重载是System.out.println

image-20201210220527030

练习

package overload;

/**
 * 编写程序,定义三个重载方法并调用。方法名为mOL。
 * 三个方法分别接收一个int参数、两个int参数、一个字符串参数。分别执行平方运算并输出结果,相乘并输出结果,输出字符串信息。
 * 在主类的main ()方法中分别用参数区别调用三个方法。
 */
public class Demo1 {

    public static void main(String[] args) {
        new Demo1().mOL(7);
        new Demo1().mOL(9, 9);
        new Demo1().mOL("test");
    }

    public void mOL(int num) {
        System.out.println(num * num);
    }

    public void mOL(int num1, int num2) {
        System.out.println(num1 * num2);
    }

    public void mOL(String str) {
        System.out.println(str);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package overload;

/**
 * 定义三个重载方法max(),
 * 第一个方法求两个int值中的最大值,
 * 第二个方法求两个double值中的最大值,
 * 第三个方法求三个double值中的最大值,
 * 并分别调用三个方法。
 */
public class Demo2 {

    public static void main(String[] args) {
        System.out.println(new Demo2().max(34, 46));
        System.out.println(new Demo2().max(3.4, 1.2));
        System.out.println(new Demo2().max(4.7, 45.3, 72.1));
    }

    public int max(int num1, int num2) {
        return num1 > num2 ? num1 : num2;
    }

    public double max(double d1, double d2) {
        return d1 > d2 ? d1 : d2;
    }

    public double max(double d1, double d2, double d3) {
        return d1 > d2 ? (d1 > d3 ? d1 : d3) : (d2 > d3 ? d2 : d3);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

# 可变形参的方法

JavaSE 5.0 中提供了Varargs(variable number of arguments)机制,允许直接定义能和多个实参相匹配的形参。从而,可以用一种更简单的方式,来传递个数可变的实参。

//JDK 5.0以前:采用数组形参来定义方法,传入多个同一类型变量
public static void test(int a ,String[] books);
1
2
//JDK5.0:采用可变个数形参来定义方法,传入多个同一类型变量
public static void test(int a ,String...books);
1
2

说明:

  • 声明格式:==方法名(参数的类型名...参数名)==
  • 可变参数:方法参数部分指定类型的参数个数是可变多个:0个,1个或多个
  • 可变个数形参的方法与同名的方法之间,彼此构成重载
  • 可变参数方法的使用与方法参数部分使用数组是一致的。二者不能共存,不能构成重载。
  • 方法的参数部分有可变形参,需要放在形参声明的最后
  • 在一个方法的形参位置,最多只能声明一个可变个数形参

例如:

package com.sauljwu.mybatisdemo;

public class TestOverload {
    public void test(String[] msg) {
        System.out.println("含字符串数组参数的test方法");
    }

    public void test1(String book) {
        System.out.println("****与可变形参方法构成重载的test1方法****");
    }

    public void test1(String... books) {
        System.out.println("****形参长度可变的test1方法****");
        for(int i=0;i<books.length;i++){
            System.out.println(books[i]);
        }
    }

    public static void main(String[] args) {
        TestOverload to = new TestOverload();
        //下面两次调用将执行第二个test方法
        to.test1();to.test1("aa" , "bb");
        //下面将执行第一个test方法
        to.TestOverload(new String[]{"aa"});
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

# 方法参数的值传递机制

方法,必须由其所在类或对象调用才有意义。若方法含有参数:

  • 形参:方法声明时的参数
  • 实参:方法调用时实际传给形参的参数值

Java的实参值如何传入方法呢?

Java里方法的参数传递方式只有一种:值传递。即将实际参数值的副本(复制品)传入方法内,而参数本身不受影响。

  • 形参是==基本数据类型==:将实参基本数据类型变量的“数据值”传递给形参

  • 形参是==引用数据类型==:将实参引用数据类型变量的“地址值”传递给形参

意思是,

基本数据类型的参数传递

package method;

import switchTest.demo1;

public class Demo1 {
    public static void main(String[] args) {
        int x = 5;
        System.out.println("main方法中的x = " + x);
        Demo1 demo1 = new Demo1();
        demo1.change(x);
        System.out.println("main方法中的x = " + x);
    }

    public void change(int x){
        System.out.println("change方法中的x = " + x);
        x = 3;
        System.out.println("change方法中的x = " + x);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
main方法中的x = 5
change方法中的x = 5
change方法中的x = 3
main方法中的x = 5
1
2
3
4

image-20201210223825718

如果变量是基本数据类型,此时赋值是变量保存数据值,就算其他变量也赋值相同的数据,不会有影响,相互独立。

引用数据类型的参数传递

package method;

public class Demo2 {
    public static void main(String[] args) {
        Person person = new Person();
        person.age = 5;
        System.out.println("main方法中,未调用change,person.age = " + person.age);
        change(person);
        System.out.println("main方法中,已调用change,person.age = " + person.age);
    }
    public static void change(Person person){
        System.out.println("change方法中,改变之前,person.age = " + person.age);
        person.age = 3;
        System.out.println("change方法中,改变之之后,person.age = " + person.age);
    }

}


class Person{
    int age;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
main方法中,未调用change,person.age = 5
change方法中,改变之前,person.age = 5
change方法中,改变之之后,person.age = 3
main方法中,已调用change,person.age = 3
1
2
3
4

内存分析:

image-20201210224905019

如果变量是引用数据类型,此时赋值是变量保存对象内存整数地址,当2个变量都指向同一个对象时,其中一个变量改变数据时,另一个变量访问时,也会发现已经改变了的值。

还记得我前面提到的话吗?

如果把一个新的引用指向已经存在的对象,通过这个变量改变对象的值时,那么原来的变量访问对象的值时,会发现也改变了,因为2个引用都指向同一个对象的内存地址。

如果非要比喻的话:

引用(变量),可以理解为遥控器。

对象,理解为电视机。

引用可以多个,那么遥控器可以多个,也可以独立存在,任意一个遥控器改变了电视频道,就像代码中,使用其中一个引用改变对象的值,那么第二个引用去访问对象时,发现对象的值已经改变了,因为2个(变量)引用都指向同一个对象的内存地址。

# 面试题

image-20201210230130261

方法一:调用完method方法退出系统

package method;

public class Test {
    public static void main(String[] args) {
        int a= 10;
        int b= 10;
        method(a, b);
        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }

    public static void method(int a, int b) {
        a *= 10;
        b *= 20;
        System.out.println("a = " + a);
        System.out.println("b = " + b);
        System.exit(0);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

方法2:重写System.out.println

package method;

import java.io.PrintStream;

public class Test {
    public static void main(String[] args) {
        int a = 10;
        int b = 10;
        method(a, b);
        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }

    public static void method(int a, int b) {
        PrintStream ps = new PrintStream(System.out) {
            @Override
            public void println(String x) {
                if ("a = 10".equals(x)) {
                    x = "a = 100";
                } else if ("b = 10".equals(x)) {
                    x = "b = 200";
                }
                super.println(x);
            }
        };
        System.setOut(ps);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

# 面试题2

image-20201210231247481

# 面试题3

int[] arr = new int[10];
System.out.println(arr);//地址值? 是地址值

char[] arr1 = new char[10];
System.out.println(arr1);//地址值? 不是
1
2
3
4
5

int调用的方法如下:

public void println(Object x) {
    String s = String.valueOf(x);
    synchronized (this) {
        print(s);
        newLine();
    }
}
1
2
3
4
5
6
7

因为传递的是引用数据类型,所以是相当于把地址值传递过去

char调用的方法如下:

public void println(char x[]) {
    synchronized (this) {
        print(x);
        newLine();
    }
}
1
2
3
4
5
6

因为传递的基本数据类型,所以打印的不是地址,直接拷贝一份数据,输出真实数据。

# 练习

  1. 定义一个Circle类,包含一个double型的radius属性代表圆的半径,一个findArea()方法返回圆的面积。

  2. 定义一个类PassObject,在类中定义一个方法printAreas(),该方法的定义如下:public void printAreas(Circle c,int time)在printAreas方法中打印输出1到time之间的每个整数半径值,以及对应的面积。例如,times为5,则输出半径1,2,3,4,5,以及对应的圆面积。

  3. 在main方法中调用printAreas()方法,调用完毕后输出当前半径值。程序运行结果如图所示。

package method;

public class Circle {

    double radius;

    public double findArea(){
        return Math.PI * radius * radius;
    }
}
1
2
3
4
5
6
7
8
9
10
package method;

public class PassObject {

    public void printAreas(Circle c,int time){
        System.out.println("半径\t面积");
        for(int i=1;i<=time;i++){
            c.radius = i;
            System.out.println(i+"\t"+c.findArea());
        }
    }

    public static void main(String[] args) {
        PassObject passObject = new PassObject();
        passObject.printAreas(new Circle(), 5);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
半径	面积
1	3.141592653589793
2	12.566370614359172
3	28.274333882308138
4	50.26548245743669
5	78.53981633974483
1
2
3
4
5
6

# 递归 recursion

什么是递归?

递归方法:一个方法体内调用它自身。

方法递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。

递归一定要向已知方向递归,否则这种递归就变成了无穷递归,类似于死循环。

计算1-100之间所有自然数之和:

package recursion;

public class Demo1 {

    public int recursion(int num){
        if(num == 1){
            return 1;
        }
        return num + recursion(num-1);
    }


    public static void main(String[] args) {
        //计算1-100之间所有自然数之和
        int recursion = new Demo1().recursion(100);
        System.out.println("recursion = " + recursion);

    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
recursion = 5050
1

请用Java写出递归求阶乘(n!)的算法:

已经超出int范围了,如果是100,只能用BigInteger

package recursion;

import java.math.BigInteger;

public class Demo2 {

    public BigInteger recursion(BigInteger num){
        if(num.compareTo(new BigInteger("1")) == 0){
            return new BigInteger("1");
        }
        return num.multiply(recursion(num.subtract(new BigInteger("1"))));
    }


    public static void main(String[] args) {
        //计算1-100之间所有自然数之积
        BigInteger recursion = new Demo2().recursion(new BigInteger("100"));
        System.out.println("recursion = " + recursion);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
recursion = 93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000
1

已知有一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),其中n是大于0的整数,求f(10)的值。

f(n+2)=2*f(n+1) + f(n)

假设n等于0

f(0 + 2) = 2 * f(0 + 1) + f(0)

f(2) = 2*f(1)+f(0)

f(2) = 2 * 4 +1 = 9

求f(10) = f(8+2) = 2 * f(8+1) + f(8)

所以f(n)= 2*f(n-1)+f(n-2)

package recursion;

public class Demo3 {

    public static void main(String[] args) {
        //已知有一个数列:f(0) = 1,f(1) = 4,f(n+2)=2*f(n+1) + f(n),其中n是大于0的整数,求f(10)的值。
        int f = new Demo3().f(10);
        System.out.println("f = " + f);
    }

    public int f(int n) {
        if(n == 0){
            return 1;
        }else if(n == 1){
            return 4;
        }else{
            return  2 * f(n - 1) + f(n-2);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
f = 10497
1

而且这个公式,当n=2时,也能计算出结果是9。

输入一个数据n,计算斐波那契数列(Fibonacci)的第n个值1 1 2 3 5 8 13 21 34 55

规律:一个数等于前两个数之和

要求:计算斐波那契数列(Fibonacci)的第n个值,并将整个数列打印出来

package recursion;

/**
 * 输入一个数据n,计算斐波那契数列(Fibonacci)的第n个值 1  1  2  3  5  8  13  21  34  55
 * <p>
 * 规律:一个数等于前两个数之和
 * <p>
 * 要求:计算斐波那契数列(Fibonacci)的第n个值,并将整个数列打印出来
 */
public class Demo4 {
    public static void main(String[] args) {
        int n = 10;
        for (int i = 1; i <= 10; i++) {
            int fibonacci = new Demo4().fibonacci(i);
            System.out.print(fibonacci + "\t");
        }
    }

    public int fibonacci(int n) {
        if (n < 3) {
            return 1;
        } else {
            return fibonacci(n - 1) + fibonacci(n - 2);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1	1	2	3	5	8	13	21	34	55	
1

汉诺塔(港台:河内塔)是根据一个传说形成的数学问题:

有三根杆子A,B,C。A杆上有 N 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆:

  1. 每次只能移动一个圆盘;
  2. 大盘不能叠在小盘上面。

提示:可将圆盘临时置于 B 杆,也可将从 A 杆移出的圆盘重新移回 A 杆,但都必须遵循上述两条规则。

问:如何移?最少要移动多少次?

package recursion;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * **汉诺塔**(港台:**河内塔**)是根据一个传说形成的数学问题:
 * <p>
 * 有三根杆子A,B,C。A杆上有 N 个 (N>1) 穿孔圆盘,盘的尺寸由下到上依次变小。要求按下列规则将所有圆盘移至 C 杆:
 * <p>
 * 1. 每次只能移动一个圆盘;
 * 2. 大盘不能叠在小盘上面。
 * <p>
 * 提示:可将圆盘临时置于 B 杆,也可将从 A 杆移出的圆盘重新移回 A 杆,但都必须遵循上述两条规则。
 * <p>
 * 问:如何移?最少要移动多少次?
 */
public class Demo5 {
    public static void main(String[] args) {
//        new Demo5().hanoi(3, 'A', 'B', 'C');
        List list = new ArrayList<>();
        list.add(15);
        list.add(10);
        list.add(5);
        new Demo5().init(list);
    }

    public void init(List a) {
        int n = a.size();
        List b = new ArrayList();
        List c = new ArrayList();
        test(n, a, b, c,new int[]{0});
    }



    public void test(int n, List a, List b, List c,int[] count) {
        if (n == 1) {
            count[0]++;
            System.out.println("--------第"+count[0]+"次-------");
            c.add(a.remove(a.size() - 1));
            System.out.println(a.toString());
            System.out.println(b.toString());
            System.out.println(c.toString());
            System.out.println("---------------------");
        }else{
            test(n - 1, a, c, b,count);
            test(1, a, b, c,count);
            test(n - 1, b, a, c,count);
        }
    }

    public void hanoi(int n, char a, char b, char c) {
        if (n == 1) {
            System.out.println(a + ">>" + c);
        } else {
            hanoi(n - 1, a, c, b);
            hanoi(1, a, b, c);
            hanoi(n - 1, b, a, c);
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
帮我改善此页面 (opens new window)
#method#重载#可变参数#传递机制#递归
上次更新: 2020/12/18, 12:50:58
类的成员-属性
类的成员-构造器

← 类的成员-属性 类的成员-构造器→

最近更新
01
zabbix学习笔记二
02-28
02
zabbix学习笔记一
02-10
03
Linux访问不了github
12-08
更多文章>
Theme by Vdoing | Copyright © 2020-2022 Saul.J.Wu | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式