day07 方法、面向对象1

一、复习

1.1 一维数组之二分查找

java 复制代码
//假设数组是arr,要求数组的元素必须是有序的,例如:从小到大
//要查找的目标值是target

int left = 0;
int right = arr.length-1;

int index = -1;
while(left <= right){
    int mid = left + (right - left)/2;
    
    if(target == arr[mid]){
        index = mid;
        break;
    }else if(target > arr[mid]){
        left = mid + 1;
    }else{
        right = mid - 1;
    }
}

if(index != -1){
    //找到了
}else{
    //没找到
}

//如果没找到,target应该插入到arr[left]的位置

1.2 二维数组

静态初始化:

java 复制代码
元素的类型[][] 数组名 = {{第一组的元素们}, {第二组的元素们}, {第三组的元素们}};

动态初始化:

java 复制代码
元素的类型[][] 数组名 = new 元素的类型[总的行数][每一行的元素的个数];

//例如:int[][] arr = new int[3][5];  一共有3行,每一行有5个元素
java 复制代码
元素的类型[][] 数组名 = new 元素的类型[总的行数][];
数组名[行下标] = new 元素的类型[这一行的长度];

例如:int[][] arr = new int[3][]; 一共有3行
arr[0] = new int[5];
arr[1] = new int[6];
arr[2] = new int[4];

遍历:

java 复制代码
for(int i=0; i<数组名.length; i++){
    for(int j=0; j<数组名[i].length; j++){
        数组名[i][j]代表一个元素
    }
}

1.3 数组工具类

1、java.util.Arrays

  • Arrays.toString(数组):拼接元素为 [元素1,元素2,元素3]
  • Arrays.sort(数组):排序
  • Arrays.copyOf(原数组,新数组的长度):从原数组复制一些元素构成新数组
  • Arrays.copyOfRange(原数组,起始下标from,终止下标end):从原数组复制一些元素构成新数组
  • Arrays.binarySearch(数组, 目标值):二分查找,如果目标值存在,返回目标值的下标,如果目标值不存在,返回-插入点-1

2、System

  • System.arraycopy(原数组,原数组要被复制的元素的最左边下标, 目标数组, 目标数组中存放新元素最左边的下标, 一共复制几个元素)
    • System.arraycopy(arr, 0, nums, 3, 5): 从arr[0]开始取5个元素,复制到 nums数组中,nums从[3]开始存储5个新元素
    • System.arraycopy(arr, 0, arr, 3, 5) :右移3个位置,移动了5个元素
    • System.arraycopy(arr, 3 arr, 0, 5) :左移3个位置,移动了5个元素

3、Hutool工具组件中ArrayUtil

  • ArrayUtil.indexOf(数组,目标值):顺序查找,如果目标值存在,返回目标值的下标,如果目标值不存在,返回-1
  • ArrayUtil.max(数组):返回值最大值
  • ArrayUtil.min(数组):返回值最小值
  • ArrayUtil.reverse(数组):数组反转
  • ArrayUtil.toString(数组):拼接元素为 [元素1,元素2,元素3]

1.4 方法

方法的声明格式:

java 复制代码
public class 类名{
    【①修饰符】 ②返回值类型  ③方法名(【④形参列表】){
        ⑤方法体语句;
    }
}
  • 【①修饰符】:目前都是public static
  • ②返回值类型:
    • void:代表方法没有返回值
    • 非void:代表方法有返回值,方法体中必须有 return 结果值; 来返回结果
  • ③方法名:一个标识符,遵循小驼峰命名法,见名知意
  • (【④形参列表】)
    • () :无参或空参
    • (数据类型 参数名, 数据类型 参数名)
  • ⑤方法体语句:完成方法功能的语句

方法的调用格式:(前提 public static)

  • 同类调用:直接通过方法名调用
  • 跨类调用:类名.方法名 进行调用

二、方法

2.1 方法的调用过程分析(理解)

方法的调用会在栈内存开辟空间,用于存储方法的局部变量的数据等,这个动作称为"入栈"。

方法一次调用结束会释放它占用的栈内存空间,这个动作称为"出栈"。如果有返回值,会将结果返回到调用处。

java 复制代码
public class MethodTest1 {
    //方法:可以实现求任意两个整数的和
    public static int add(int a,int b){
        int he = a + b;
        return he;
    }

    public static void main(String[] args) {
        int x = 1;
        int y = 2;
        int result = add(x, y);

        int a = 3;
        int b = 4;
        int sum = add(a,b);
    }
}

结论:每一次方法调用都会有入栈,调用结束都会出栈。如果同一个方法调用多次,就会入栈多次,出栈多次。栈结构遵循先进后出的原则。

2.2 方法的参数传递机制(理解)

1、情况一:参数是基本数据类型

java 复制代码
public class MethodParamTest1 {
    public static void main(String[] args) {
        int a = 1;
        change(a);
        System.out.println("a = " + a);//1
    }

    public static void change(int a){
        a=100;
        a++;
    }
}

结论:

当参数是基本数据类型时,实参给形参的是数据值的副本,接下来在方法中对形参做任何修改与实参无关

java 复制代码
public class MethodParamTest1_2 {
    public static void main(String[] args) {
        int a = 1;
        a = change(a);//接收返回值
        System.out.println("a = " + a);//101
    }

    public static int change(int a){
        a=100;
        a++;
        return a;
    }
}

2、情况二:参数是引用数据类型

plain 复制代码
Java的数据类型分为2大类:
(1)基本数据类型:8种   byte,short,int,long,float,double,char,boolean
(2)引用数据类型:
    数组:  int[]等
    类:    String,Scanner等
    ....
java 复制代码
public class MethodParamTest2 {
    public static void main(String[] args) {
        int[] arr = {1};
        change(arr);
        System.out.println("arr[0] = " + arr[0]);
    }


    public static void change(int[] arr){
        arr[0] = 100;
    }
}

结论:当参数是引用数据类型时,实参给形参的是地址值的副本,那么此时通过形参修改元素等内容,会影响实参对应元素。

java 复制代码
public class MethodParamTest3 {
    public static void main(String[] args) {
        int[] arr = {1};
        change(arr);
        System.out.println("arr[0] = " + arr[0]);//100
    }


    public static void change(int[] arr){
        arr[0] = 100;
        arr = new int[]{200};
    }
}

结论:当参数是引用数据类型时,实参一开始给形参的是地址值副本,但是如果形参指向新的数组对象时,就与原来的实参无关的。

3、练习题

java 复制代码
public class Exercise1{
    public static void main(String[] args){
        int i=1;
        i=i++;
        change(i);
        System.out.println(i);//1
    }

    public static int change(int i){
        i++;
        return i;
    }
}
java 复制代码
import java.util.Arrays;

public class Exercise2{
    public static void main(String[] args){
        int[] arr = {1,2,3,4,5};
        change(arr);
        System.out.println(Arrays.toString(arr));
    }

    public static void change(int[] arr){
        arr = Arrays.copyOf(arr, arr.length*2);
        for(int i=0; i<arr.length; i++){
            arr[i] *= 2;
        }
    }
}

2.3 方法的重载(要求掌握)

方法的重载(Overload):同一个类中出现两个或多个方法,它们的名称完全相同,但是它们的形参列表不同,这里的形参列表不同是指类型、个数、顺序不同,与形参名无关,这样的两个或多个方法称为重载。方法重载的概念与修饰符、返回值类型无关。

调用重载的方法如何选择?

  • 先找最匹配的。实参的类型、个数、顺序与形参完全一致
  • 如果没有最匹配的,找可以兼容的。 形参的类型 > 实参的类型,当然此时个数与顺序得对得上
java 复制代码
public class OverloadTest1 {
    //定义1个方法,可以求任意2个整数的和
    public static int add(int a, int b){
        return a + b;
    }

    //定义1个方法,可以求任意2个小数的和
    public static double add(double a, double b){
        return a + b;
    }

    //定义1个方法,可以求任意3个整数的和
    public static int add(int a, int b,int c){
        return a + b + c;
    }

/*    public static int add(int x, int y){//不是重载,因为形参的个数与类型无法区分
        return x + y;
    }*/
    /*public static double add(int x, int y) {//不是重载,因为形参的个数与类型无法区分
        return (double)x + y;
    }*/

    public static void main(String[] args) {
        System.out.println(add(1,4));//add(int a, int b)
        System.out.println(add(1.0,4.0));//add(double a,double b)
        System.out.println(add(1,4.0));//add(double a,double b)
        System.out.println(add(1,2,3));//add(int a, int b,int c)
//        System.out.println(add(1.0,2.0,3.0));//错误,没有可以完全匹配的,也没有可以兼容
        System.out.println(add('a','b'));//add(int a, int b)
//        System.out.println(add('a','b','c','d'));//错误,个数对不上
    }
}

2.4 可变参数(能看懂即可)

可变参数:表示某个形参在调用时,可以传入0~n个对应的实参,即实参的个数可变。

优势:

  • 可变参数部分可以传入0~n个对应的实参,或传入对应类型的数组,更灵活

缺点:

  • 一个方法最多只能有1个可变参数
  • 且可变参数必须是形参列表的最后1个

如何选择?可变参数还是数组

如果形参只有1个数组类型且是最后1个形参,那么选择可变参数更灵活。否则依然选择数组。

java 复制代码
public class VarParamTest1 {
    //需求:设计一个方法,可以求任意个整数的和
    public static int add(int... nums){
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }
        return sum;
    }

/*    public static int add(int[] nums){//错误,因为编译器认为int[]和int...是一样的
        int sum = 0;
        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
        }
        return sum;
    }*/

    public static void main(String[] args) {
        System.out.println(add());//0个整数
        System.out.println(add(4));//1个整数
        System.out.println(add(1,2,3,4,5));//5个整数
        System.out.println(add(6,1));//2个整数

        int[] arr = {10,20,3,40};
        System.out.println(add(arr));//也支持传入数组
    }

   /* public static void m1(int... nums, double... args){//错误,因为一个方法只能有1个形参

    }*/
 /*  public static void m2(double... args,int num ){//错误,可变参数必须是最后一个形参

   }*/
}

2.5 命令行参数(了解)

java 复制代码
public class CommandParamTest {
    /*
    (1)main方法的(String[] args)是不是形参? 是
    (2)如何给main方法的形参传值?
    它的参数传递需要单独传:A:命令行
     */
    public static void main(String[] args) {
        for (int i = 0; i < args.length; i++) {
            System.out.print(args[i]+" ");
        }
    }
}

2.6 递归(难,0基础的先放一放)

递归的概念:一个方法自己调用自己,就叫递归。

递归必须有出口,否则出现无限递归,一定会发生StackOverflowError错误。

案例1:n!
java 复制代码
public class RecursionTest2 {
    public static void main(String[] args) {
        System.out.println(f(5));
    }

    /*
    求n!
    n! = n * (n-1)!
    假设 f(n)代表 n!的话
        f(n) = n * f(n-1)
    当n = 1 ,f(1) = 1
     */
    public static int f(int n){
        if(n==1){
            return 1;
        }
        return n * f(n-1);//递归调用
    }
}
案例2:斐波那契数列

案例:计算斐波那契数列(Fibonacci)的第n个值,斐波那契数列满足如下规律,

java 复制代码
1,1,2,3,5,8,13,21,....

即从第三个数开始,一个数等于前两个数之和。假设f(n)代表斐波那契数列的第n个值,那么f(n)满足:

f(n) = f(n-2) + f(n-1);

java 复制代码
public class RecursionTest3 {
    public static void main(String[] args) {
        System.out.println(fibonacci(5));
    }
    /*
    案例:计算斐波那契数列(Fibonacci)的第n个值,斐波那契数列满足如下规律,

    1,1,2,3,5,8,13,21,....
即从第三个数开始,一个数等于前两个数之和。假设f(n)代表斐波那契数列的第n个值,那么f(n)满足:
f(n) = f(n-2) + f(n-1);

     */
    public static int fibonacci(int n){
        if(n<=2){
            return 1;
        }
        return fibonacci(n-1) + fibonacci(n-2);
    }
}
练习题1
java 复制代码
public class RecursionExercise1 {
    public static void main(String[] args) {
        /*System.out.println(f(10));
        System.out.println(f(9));
        System.out.println(f(8));*/

        for(int i=10; i>=1; i--){
            System.out.println(f(i));
        }
    }
    /*
    猴子吃桃子问题,猴子第一天摘下若干个桃子,当即吃了所有桃子的一半,还不过瘾,又多吃了一个。
    第二天又将仅剩下的桃子吃掉了一半,又多吃了一个。
    以后每天都吃了前一天剩下的一半多一个。
    到第十天,只剩下一个桃子。试求第一天共摘了多少桃子?
    假设  f(n)代表第n天桃子的数量
         f(10) = 1
         f(9) ÷ 2 -1 = f(10)
         f(9) = (f(10) + 1 ) * 2
         ...
         f(n) = (f(n+1) + 1 ) * 2
     */
    public static int f(int n){
        if(n>10 || n<1){
            return 0;
        }
        if(n==10){
            return 1;
        }

        return (f(n+1) + 1 ) * 2;
    }
}
练习题2
java 复制代码
public class RecursionExercise2 {
    public static void main(String[] args) {
        for(int i=1; i<=10; i++){
            System.out.println(i +"级台阶共有几种走法:" +f(i));
        }
    }

    /*
    有n级台阶,一次只能上1步或2步,共有多少种走法?
    假设 f(n)代表 n级台阶的总走法
    f(1)  1
    f(2)  11                             2
    f(3)  111  21                        12
    f(4)  1111 211 121                   112 22
    f(5)  11111 2111 1211 1121 221       1112 212 122

    f(n) = f(n-1) + f(n-2)
     */
    public static int f(int n){
        if(n==1){
            return 1;
        }
        if(n==2){
            return 2;
        }
        return f(n-1) + f(n-2);
    }
}
用循环改写递归的代码
java 复制代码
public class LoopTest {
    public static void main(String[] args) {
        System.out.println(f(5));//120

        System.out.println(peach(1));//1534

        System.out.println(step(10));//89
    }

    //n! 使用循环实现
    public static int f(int n){
        int result = 1;
        for(int i=1; i<=n; i++){
            result *= i;
        }
        return result;
    }

    /*
    猴子吃桃子问题,猴子第一天摘下若干个桃子,当即吃了所有桃子的一半,还不过瘾,又多吃了一个。
    第二天又将仅剩下的桃子吃掉了一半,又多吃了一个。
    以后每天都吃了前一天剩下的一半多一个。到第十天,只剩下一个桃子。试求第一天共摘了多少桃子?
     */
    public static int peach(int day){
        int count = 1;//第10天
        for(int i=9; i>=day; i--){
            count = (count+1)*2;
        }
        return count;
    }

    //有n级台阶,一次只能上1步或2步,共有多少种走法?
    public static int step(int n){
        if(n==1){
            return 1;
        }
        if(n==2){
            return 2;
        }

        int lastTwo = 1;//最后一步跨2级
        int lastOne = 2;//最后一步跨1级
        //对于初始值,计算第3级台阶的走法,
        //最后一步跨2级  就1种走法  1(2)
        //最后一步跨1级  就2种走法  11(1)    2(1)
        int current = 0;
        for(int i=3; i<=n; i++) {
            current = lastTwo + lastOne;
            lastTwo = lastOne;
            lastOne = current;
        }

        return current;
    }
}

三、面向对象

举例:把大象装进冰箱的问题?

面向过程的编程思想:

  • 第一步:把冰箱门打开
  • 第二步:把大象装进去
  • 第三步:把冰箱门关上

面向对象的编程思想:

  • 先把事物抽象成类:冰箱、大象、人
  • 每一个事物中封装对应的属性和方法
plain 复制代码
class 冰箱{
    String 品牌;
    int 长;
    int 宽;
    int 高;
    
    void open(){
    
    }
    void close(){
    
    }
    void save(){
    
    }
}

class 大象{
    double 体重;
    void walk(){
        
    }
}

class 人{
    String name;
    
    void pull(东西){
        if(东西是大象){
            大象.walk();
        }else if(东西是冰箱){
            冰箱.open();
        }
    }
    void push(东西){
        if(东西是大象){
            大象.walk();
        }else if(东西是冰箱){
            冰箱.close();
        }
    }
}

class 主类{
    public static void main(String[] args){
        人 r = new 人("张三");
        大象 e = new 大象(5);
        冰箱 b = new 冰箱("格力", 5,10,10);
        
        r.pull(b);
        r.push(e);
        r.push(b);
    }
}

3.1 类与对象(两者关系、语法格式)

  • 类:一类具有相同特征的事物的抽象描述。
    • 例如:学生(属性:姓名、性别、民族、身份证号码,行为:学习、考试)
    • 例如:老师(属性:姓名、性别、民族、身份证号码,行为:教学,监考、出卷子)
  • 对象:是这一类事物中的一个具体的个体、实例、实体。
    • 例如:姓名:张三,性别:男,民族:汉,身份证号码:110250199912031235,
  • 比喻:类是设计图,模板,对象是根据设计图造出来产品

声明类的语法格式:

java 复制代码
【修饰符】 class 类名{
    
}

关键字:class,就代表定义了一个新的类

先有类还是先有对象?

如果从代码的编写角度,一定是先写类,再new对象。

如果从设计代码的角度,先观察和分析各种对象,找到它们的共性,才能抽取出对应类。

创建对象的语法格式:

java 复制代码
new 类名() //匿名对象
    
类名 对象名 = new 类名(); //给对象取名字
java 复制代码
Scanner input = new Scanner(System.in);//Scanner是类名,input是对象名

System.out.print("请输入一个整数:");
int num = input.nextInt();

示例代码:

java 复制代码
public class Student {
    String name;
    char gender;
    String nation;
    String cardId;

    public void study(){
        System.out.println(name + "good good study, day day up");
    }
    public void exam(){
        System.out.println(name + "蒙的全对,不会的不考");
    }
}
java 复制代码
public class TestStudent {
    public static void main(String[] args) {
        Student s = new Student();
        s.name = "李刚";
        s.gender = '男';
        s.nation ="汉族";
        s.cardId = "110250199912031235";
        s.study();
        s.exam();

        Student s2 = new Student();
        s2.name = "李涛";
        s2.gender = '男';
        s2.nation ="汉族";
        s2.cardId = "110250199912031234";
        s2.study();
        s2.exam();

        new Student().study();
    }
}

3.2 类的成员之一:成员变量(未完待续)

类的成员变量是用于描述事物的数据特征的,即属性特征。

平时生活中,说的属性,是事物的数据特征属性,例如:文件的标题、修改日期、大小等

编程中,说的属性,具有get方法的成员变量才叫属性

声明格式:

java 复制代码
【修饰符】 class 类名{
     【修饰符】 数据类型 成员变量名;
    【修饰符】 数据类型 成员变量名;
    【修饰符】 数据类型 成员变量名;
}

分类:

  • 静态的成员变量,简称静态变量
  • 非静态的成员变量,简称实例变量
静态变量 实例变量
前面有没有static
是否依赖对象 不依赖 依赖
是否所有对象共享 共享 每一个对象都是独立的

静态变量与实例变量的相同点:都有默认值

数据类型 默认值
byte 0
short 0
int 0
long 0L
float 0.0F
double 0.0
char \u0000
boolean false
引用数据类型(String、数组等) null

思考题:什么情况下,成员变量应该声明为静态的?什么情况下,成员变量不应该声明为静态的?

如果这个成员变量的值是需要所有对象共享的,就必须声明为静态。

如果这个成员变量的值不应该被共享,那么就必须声明为非静态。

例如:银行卡类:有利率,余额,

  • 利率是静态的,所有人都一样
  • 余额是非静态的,每一个人都不同
java 复制代码
package com.atguigu.oop;

public class Chinese {//中国人
    public static String country;//静态变量,所以中国人的国家名是相同的
    public String name;//实例变量,每一个中国人的名字是独立的
    public int age;//实例变量,每一个中国人的年龄是独立的
}
java 复制代码
package com.atguigu.oop;

public class TestChinese {
    public static void main(String[] args) {
        Chinese.country = "中国";
        System.out.println("country = " + Chinese.country);

//        System.out.println(Chinese.name);//错误
//        System.out.println(Chinese.age);//错误
        //name和age是非静态的实例,它们依赖于Chinese的对象

        Chinese c1 = new Chinese();
        c1.name = "李刚";
        c1.age = 26;

        System.out.println("c1.name = " + c1.name);
        System.out.println("c1.age = " + c1.age);
        System.out.println("c1.country = " + c1.country);

        Chinese c2 = new Chinese();
        c2.name = "李涛";
        c2.age = 28;
        System.out.println("c2.name = " + c2.name);
        System.out.println("c2.age = " + c2.age);
        System.out.println("c2.country = " + c2.country);

        System.out.println("===================");
        c1.country = "中华人民共和国";
        c1.name = "大刚";
        System.out.println("c1.name = " + c1.name);
        System.out.println("c1.age = " + c1.age);
        System.out.println("c1.country = " + c1.country);
        System.out.println("c2.name = " + c2.name);
        System.out.println("c2.age = " + c2.age);
        System.out.println("c2.country = " + c2.country);
    }
}

3.3 包(会建包,会导包)

1、包的作用

  • 就相当于文件,分类管理。

  • 包相当于类的前缀,可以避免类的重名,即包名.类名才是类的全名称

    • 例如:String类的全名称, java.lang.String
    复制代码
            Scanner类的全名称, java.util.Scanner;

2、包名的命名规范

  • 见名知意
  • 所有单词都小写,单词之间使用.分割
  • 采用公司域名倒置的形式,例如:com.atguigu.xxx
  • 除了核心类库,咱们自己的包名,不要以java.开头

3、如何创建包?

4、跨包使用类

java 复制代码
import 包.类名;
import 包.*; //这个包的所有类都可以使用了,只能省略类名

相关代码:

  • one包
java 复制代码
package com.atguigu.one;

public class Teacher {
}
java 复制代码
package com.atguigu.one;

public class TeacherDemo {
    public static void main(String[] args) {
        Teacher t = new Teacher();
    }
}
java 复制代码
package com.atguigu.one;

public class Employee {
}
  • three包
java 复制代码
package com.atguigu.three;

public class Teacher {
}
  • two包
java 复制代码
package com.atguigu.two;

/*import com.atguigu.one.Employee;
import com.atguigu.one.Teacher;*/

import com.atguigu.one.*;

public class TestTeacher {
    public static void main(String[] args) {
        //创建one包的Teacher类型
        Teacher t = new Teacher();
        //创建Employee类
        Employee e = new Employee();

        //创建three包的Teacher类型
        //当两个类同名,但是包不同,只能1个使用import,另一个使用全名称
        com.atguigu.three.Teacher t2 = new com.atguigu.three.Teacher();

    }
}
相关推荐
num_killer19 分钟前
小白的Langchain学习
java·python·学习·langchain
期待のcode1 小时前
Java虚拟机的运行模式
java·开发语言·jvm
程序员老徐1 小时前
Tomcat源码分析三(Tomcat请求源码分析)
java·tomcat
a程序小傲1 小时前
京东Java面试被问:动态规划的状态压缩和优化技巧
java·开发语言·mysql·算法·adb·postgresql·深度优先
仙俊红1 小时前
spring的IoC(控制反转)面试题
java·后端·spring
阿湯哥1 小时前
AgentScope Java 集成 Spring AI Alibaba Workflow 完整指南
java·人工智能·spring
小楼v1 小时前
说说常见的限流算法及如何使用Redisson实现多机限流
java·后端·redisson·限流算法
与遨游于天地2 小时前
NIO的三个组件解决三个问题
java·后端·nio
czlczl200209252 小时前
Guava Cache 原理与实战
java·后端·spring
yangminlei2 小时前
Spring 事务探秘:核心机制与应用场景解析
java·spring boot