Skip to content

一、复习

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);
    }
}

image-20250704092549823

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

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++;
    }
}

image-20250704093917273

结论:

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

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、情况二:参数是引用数据类型

txt
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;
    }
}

image-20250704094750104

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

image-20250704101621687

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;
        }
    }
}

image-20250704103818512

image-20250704104317223

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 ){//错误,可变参数必须是最后一个形参

   }*/
}

image-20250704120348525

image-20250704115011798

image-20250704115123564

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]+" ");
        }
    }
}

image-20250704144851863

image-20250704142620712

image-20250704142736115

image-20250704142810544

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

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

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

image-20250704143416229

image-20250704143529593

案例 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);//递归调用
    }
}

image-20250704144044315

案例 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);
    }
}

image-20250704145006770

练习题 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;
    }
}

三、面向对象

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

面向过程的编程思想:

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

面向对象的编程思想:

  • 先把事物抽象成类:冰箱、大象、人
  • 每一个事物中封装对应的属性和方法
txt
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
是否依赖对象不依赖依赖
是否所有对象共享共享每一个对象都是独立的

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

数据类型默认值
byte0
short0
int0
long0L
float0.0F
double0.0
char\u0000
booleanfalse
引用数据类型(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、如何创建包?

image-20250704170954361

image-20250704171249028

image-20250704171342918

image-20250704171458675

4、跨包使用类

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

image-20250704171646337

image-20250704171746367

image-20250704171920957

相关代码:

  • 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();

    }
}