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

    }
}
相关推荐
除了代码啥也不会1 小时前
Java基于SSE流式输出实战
java·开发语言·交互
虹科网络安全1 小时前
艾体宝干货 | Redis Java 开发系列#2 数据结构
java·数据结构·redis
sg_knight1 小时前
SSE 技术实现前后端实时数据同步
java·前端·spring boot·spring·web·sse·数据同步
Slow菜鸟1 小时前
Java项目基础架构(二)| 通用响应与异常
java·开发语言
毕设源码-钟学长1 小时前
【开题答辩全过程】以 个人理财系统界面化设为例,包含答辩的问题和答案
java
LQxdp1 小时前
复现-[Java Puzzle #2 WP] HEAD权限绕过与字符截断CRLF
java·开发语言·漏洞复现·java 代码审计
小坏讲微服务1 小时前
SpringBoot4.0整合Scala完整使用
java·开发语言·spring boot·后端·scala·mybatis
泉城老铁1 小时前
windows服务器mysql数据库备份脚本
java·后端·mysql