当前位置: 首页 > article >正文

02 javase面向对象-狂神说课程笔记

文章目录

  • Java面向对象
    • 1.面向对象的一些概念
    • 2.三大特性
      • 封装(Encapsulation):
      • 继承(Inheritance):
      • 多态(Polymorphism):
        • 编译时多态
        • 运行时多态
    • 3.构造器
        • Alt+Insert快捷键迅速得到构造器方法
  • Java面向对象-方法
    • 1.方法
    • 2.静态方法和非静态方法
        • 2.1静态方法
        • 2.2非静态方法
    • 3 参数传递方式
    • 4.方法的重载
    • 5.命令行传参
    • 6.可变参数
    • 7.递归
    • 作业
    • MyStringBuffer-自定义实现

Java面向对象

1.面向对象的一些概念

Java中的面向对象编程是一种编程范式,它将程序中的数据和操作视为一个相互关联的对象,并通过类、对象、继承、封装、多态等概念来实现。具体来说,Java中的面向对象编程包括以下几个方面:

  • 类和对象:Java中的对象是由类创建的,一个类定义了对象的属性和行为,对象则是该类的一个实例,拥有类的属性和方法。
  • 封装:Java中的封装是指将数据和方法包含在类中,并对外部代码隐藏类的内部实现细节,只提供公共接口让其他类进行使用。
  • 继承:Java中的继承是指一个类可以从另一个类继承属性和方法,子类可以拥有父类的所有数据和方法,也可以重写或添加新的方法。
  • 多态:Java中的多态是指同一类型的对象,在不同的情况下会表现出不同的行为。Java中的多态有两种实现方式:方法重载和方法重写。
  • 接口:Java中的接口是一种特殊的抽象类,它只包含方法的声明而没有方法的实现,用于定义类的公共行为规范,一个类可以实现多个接口。

通过面向对象编程,可以提高代码的可读性、可维护性和可扩展性,使程序更加灵活和易于开发。对于描述复杂的事物,为了从宏观上把握,从整体上合理分析,我们需要使用面向对象的思路,来分析整个系统,但是,具体到微观操作,仍然需要面向过程的思路去处理!面向过程思想步骤清晰简单,第一步做什么,第二步做什么…

类是一种模板或蓝图,用于定义对象的属性和方法。相比之下,对象则是类的一个实例,拥有类定义的属性和方法。
具体来说,我们可以将类看做是一个抽象的概念,它包含了一组属性和方法的定义,描述了一个对象所具有的特征和行为。而对象则是这个类的具体实例,拥有类中定义的属性和方法,同时还有自己的状态和行为。
类和对象之间的关系可以用以下几个点来总结:
对象是类的实例。类定义了对象的属性和方法,对象则是该类的一个实例,拥有类的属性和方法。
类是对象的模板。类可以看作是对象的模板或蓝图,它描述了对象的共同属性和行为。
类是抽象的概念。类是一种抽象的概念,它只描述了对象的特征和行为,而不是具体的实例。
对象是具体的实体。对象是具体的实体,它拥有类定义的属性和方法,并具有自己的状态和行为。
总之,类和对象是面向对象编程中最基本的两个概念,理解它们之间的关系对于理解和设计面向对象程序非常重要。

2.三大特性

封装(Encapsulation):

封装是将数据和方法包含在类中,并对外部代码隐藏内部实现细节的机制。通过封装,我们可以将数据和相关的操作封装在一个类中,只对外提供公共接口进行访问。这种方式可以保护数据的安全性并提高代码的可维护性。例如,通过使用private修饰符来限制对类的属性的直接访问,然后提供public方法来控制属性的访问和修改,比如说set,get方法。
高内聚,低耦合
记住这句话: 属性私有,get/set
示例:
Day11607.java

package com.determination;

public class Day11607 {
    public static void main(String[] args) {
        Student student=new Student();
        student.setName("小罗同学");
        System.out.println("他是"+student.getName());
    }
}

Student.java

package com.determination;

public class Student {
    //属性私有
    private String name;
    private int age;
    private char sex;
    //提供了一些可以操纵这些属性的方法
    public void setName(String name)
    {
        this.name=name;
    }
    public String getName() {
        return this.name;
    }
}

同样使用Alt+Insert,自动生成
在这里插入图片描述
总:

  1. 统一了接口
  2. 提高安全性,保护数据
  3. 隐藏代码实现细节
  4. 可维护性增加。

继承(Inheritance):

继承是指一个类可以从另一个类继承属性和方法。通过继承,子类可以拥有父类的所有数据和方法,并且可以根据需要重写或添加新的方法。继承可以实现代码的重用性和层次化结构,使得类之间的关系更加清晰和灵活。在Java中,使用关键字"extends"来建立类之间的继承关系。
在这里插入图片描述

输出结果:

luozhang
wind
lz

Crtl+H
在这里插入图片描述
隐藏代码super()这里显示的给出了,也可省略,是一样得结果.
在这里插入图片描述
私有的东西无法被继承.

继承具有以下重要的特点:

子类可以继承父类的变量、方法和内部类。
子类可以覆盖(即重写)父类的方法。
子类不能继承父类的构造器,但可以通过 super() 调用父类的构造器。
子类可以定义自己的变量、方法和内部类,这些成员与父类的成员同名时,子类的成员将隐藏父类的同名成员。
子类的实例可以向上转型为父类的类型,这样就可以使用父类的方法和变量了。

在 Java 中,重写(Override)是指子类定义一个与父类具有相同名称、参数列表和返回类型的方法,并且在子类中提供了自己的实现。通过重写,子类可以改变继承自父类的方法的行为,以适应自己的需求。

下面是重写方法的一些要点:

方法签名:重写的方法必须与父类方法具有相同的方法签名,即方法名称、参数列表和返回类型必须完全匹配。
访问修饰符:重写的方法可以具有与父类方法相同或更宽松(即更大范围的可见性)的访问修饰符。例如,如果父类方法是 public,则子类方法可以是 public 或 protected,但不能是 private。
异常:重写的方法可以抛出与父类方法相同的异常或该异常的子类,或者不抛出任何异常。不能抛出比父类方法声明的更多或更宽泛的异常。
调用父类方法:在重写的方法中,如果需要调用父类的方法实现,可以使用 super 关键字。通过 super.methodName() 来调用父类的方法。

总:
重写:需要有继承关系,子类重写父类的方法!
1.方法名必须相同
2.参数列表必须相同
3.修饰符:范围可以扩大,但是不能缩小: public>protected>Default>private
4.抛出的异常:范围,可以被缩小,但不能扩大:classNotFoundException–>Exception(大)
为什么需要重写?
父类的方法字类不需要,或者不一定满足!

class Animal {
    public void makeSound() {
        System.out.println("The animal makes a sound");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("The dog barks");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Animal();
        Dog dog = new Dog();

        animal.makeSound(); // 输出:The animal makes a sound
        dog.makeSound();    // 输出:The dog barks
    }
}

在上述示例中,我们定义了一个 Animal 类和一个 Dog 类,其中 Dog 类继承自 Animal 类并重写了 makeSound() 方法。
在主程序中,我们创建了一个 Animal 对象和一个 Dog 对象,并分别调用它们的 makeSound() 方法。由于 Dog 类重写了父类的方法,因此 dog.makeSound() 的输出是 The dog barks,而不是父类的实现。
需要注意的是,当我们使用 @Override 注解来标记一个方法时,如果方法签名与父类的方法签名不匹配,编译器将引发错误。
几个关键字

  • public:公共访问级别,是最高级别的访问权限。使用 public 关键字修饰的成员可以被任何类访问,无论是同一个包中的类还是不同包中的类。

  • protected:受保护访问级别。使用 protected 关键字修饰的成员可以被同一个包中的类访问,也可以被其他包中的子类访问。换句话说,只有当前类、同一个包中的类以及其他包中的子类可以访问。

  • 默认(包访问级别):如果没有明确指定访问权限修饰符,默认的访问权限就是包访问级别。在同一个包中的类可以访问默认访问级别的成员,但是在其他包中的类无法访问。

  • private:私有访问级别,是最低级别的访问权限。使用 private 关键字修饰的成员只能在当前类内部进行访问,其他任何类都无法访问。

public class MyClass {
    public int publicField;
    protected int protectedField;
    int defaultField; // 默认访问级别,等同于没有明确指定访问权限修饰符
    private int privateField;

    public void publicMethod() {
        System.out.println("This is a public method");
    }

    protected void protectedMethod() {
        System.out.println("This is a protected method");
    }

    void defaultMethod() {
        System.out.println("This is a default method");
    }

    private void privateMethod() {
        System.out.println("This is a private method");
    }
}

MyClass 类有四个成员字段和四个成员方法,分别使用了不同的访问权限修饰符。
其他类可以访问 publicField 和 publicMethod(),而 protectedField 和 protectedMethod() 只能在同一个包或其子类中访问。defaultField 和 defaultMethod() 只能在同一个包中访问。而 privateField 和 privateMethod() 只能在当前类内部访问。
通过合理地使用这些访问权限修饰符,我们可以控制类成员的可见性和访问范围,从而保证代码的封装性和安全性。

多态(Polymorphism):

多态是指同一类型的对象,在不同的情况下会表现出不同的行为。多态分为编译时多态和运行时多态。编译时多态是通过方法的重载实现的,即同一个类中有多个方法名相同但参数列表不同的方法。运行时多态是通过方法的重写和接口实现的,即子类可以重写父类的方法,实现自己特定的行为,或者一个类实现多个接口,提供不同的行为。多态可以提高代码的灵活性和可扩展性,使得程序更具适应性。

在 Java 中,多态(Polymorphism)是指一个对象可以具有多种形态。具体来说,同一个类的不同实例,或不同类之间的实例,可以共享同一种类型或接口,并以各自特定的方式进行操作,多态分为两种类型:编译时多态和运行时多态。

编译时多态

编译时多态也称为静态多态,是指在编译时就能确定变量或表达式的类型,从而确定要调用的方法。在 Java 中,编译时多态主要通过重载实现。重载是指定义了多个方法,它们具有相同的名称但不同的参数列表。
下面是一个示例,展示编译时多态的用法:

class Animal {
    public void makeSound() {
        System.out.println("The animal makes a sound");
    }
}

class Dog extends Animal {
    public void makeSound() {
        System.out.println("The dog barks");
    }

    public void makeSound(int times) {
        for (int i = 0; i < times; i++) {
            System.out.println("The dog barks");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Animal();
        Dog dog = new Dog();

        animal.makeSound(); // 输出:The animal makes a sound
        dog.makeSound();    // 输出:The dog barks
        dog.makeSound(3);   // 输出:The dog barks The dog barks The dog barks
    }
}

我们定义了一个 Animal 类和一个 Dog 类,其中 Dog 类重载了 makeSound() 方法,分别定义了不同的参数列表。
在主程序中,我们创建了一个 Animal 对象和一个 Dog 对象,并分别调用它们的 makeSound() 方法。由于方法的选择是在编译时确定的,因此编译器会根据方法签名选择相应的方法。在这个例子中,animal.makeSound() 调用了父类的实现,输出 The animal makes a sound;dog.makeSound() 调用了子类的实现,输出 The dog barks;dog.makeSound(3) 调用了子类重载的方法,输出 The dog barks The dog barks The dog barks。

运行时多态

运行时多态也称为动态多态,是指在运行时通过实际类型来确定要调用的方法。在 Java 中,运行时多态主要通过继承和重写实现。

下面是一个示例,展示运行时多态的用法:

class Animal {
    public void makeSound() {
        System.out.println("The animal makes a sound");
    }
}

class Dog extends Animal {
    @Override
    public void makeSound() {
        System.out.println("The dog barks");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal animal = new Animal();
        Dog dog = new Dog();

        animal.makeSound(); // 输出:The animal makes a sound
        dog.makeSound();    // 输出:The dog barks

        Animal anotherDog = new Dog();
        anotherDog.makeSound(); // 输出:The dog barks
    }
}

我们定义了一个 Animal 类和一个 Dog 类,其中 Dog 类重写了父类的 makeSound() 方法。
在主程序中,我们创建了一个 Animal 对象和一个 Dog 对象,并分别调用它们的 makeSound() 方法。由于方法的选择是在运行时确定的,因此当我们将 Dog 实例向上转型为 Animal 类型并调用 makeSound() 方法时,实际上调用的是 Dog 类的实现,输出 The dog barks。
需要注意的是,多态只适用于实例方法,而不适用于静态方法或属性。此外,final 方法也不能被重写,因此也不会发生多态。
当我们谈论多态时,可以用一个简单的比喻来解释。假设有一家动物园,里面有各种各样的动物,包括狗、猫、鸟等等。

在这个动物园中,每个动物都可以发出声音。但是不同的动物发出的声音是不一样的,比如狗会叫汪汪,猫会叫喵喵。

现在我们站在动物园门口,看到一个动物走过来。我们并不知道它是什么动物,只是知道它可以发出声音。于是我们问它:“你能发出声音吗?”这时候,无论这个动物是狗、猫还是其他动物,它都会回答:“我会发出声音。”

这里的关键是,我们对这个动物的了解只限于它能发出声音这个特征,而不关心它究竟是什么动物。这就是多态的一种体现。我们无需关心对象的具体类型,只需要知道它们具备某个共同的特征或行为,然后以统一的方式与它们进行交互。

在这个比喻中,动物园中的动物就像是不同类的对象,它们都实现了一个共同的接口或继承了一个共同的父类。这个共同的接口或父类规定了它们要实现的方法,比如发出声音。而我们站在门口询问动物是否能发出声音,就像是调用这个共同的方法。

通过多态,我们可以以一种统一的方式处理不同类型的对象,而无需关心它们的具体类型。这使得代码更加灵活和可扩展,也方便了代码的维护和管理。

3.构造器

在 Java 中,构造器是一种特殊类型的方法,它用于创建和初始化对象。构造器的名称必须与类的名称完全相同,它没有返回类型(包括 void),并且通常具有公共访问修饰符,以便其他类可以使用它来创建该类的对象。

以下是几个示例类,其中每个类包含一个构造器:
示例1:

public class Person {
    private String name;
    private int age;
    // 构造器
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // 方法
    public void sayHello() {
        System.out.println("Hello, my name is " + name + ", and I am " + age + " years old.");
    }
}

我们定义了一个名为 Person 的类,它具有两个私有字段 name 和 age,以及一个公共构造器。构造器的参数与字段名称匹配,并且我们使用 this 关键字来引用类的字段。

当我们使用 new 操作符创建 Person 对象时,将调用构造器来初始化对象。例如:

Person p = new Person("Bob", 30);
p.sayHello(); // 输出:Hello, my name is Bob, and I am 30 years old.

我们首先使用 new 操作符创建一个 Person 对象,并传递 “Bob” 和 30 作为参数。这将调用 Person 类的构造器,并将参数值分配给 name 和 age 字段。
然后,我们调用 sayHello() 方法,它将输出一个字符串,其中包含创建的 Person 对象的名称和年龄信息。

需要注意的是,如果我们没有显式定义任何构造器,则 Java 编译器将生成一个默认构造器。默认构造器没有参数,并且不执行任何操作(除了隐式地调用超类的默认构造器,如果有的话)。

示例2: 无参构造
Day11606.java

package com.determination;

public class Day11606 {
    public static void main(String[] args) {
        Dog dog=new Dog(); //无参构造
        dog.name="大黄";
        dog.age=2;
        dog.shot();
    }
}

Dog.java

package com.determination;

public class Dog {
    public String name;
    public int age;
    public void shot()
    {
        System.out.println(name+"叫了一声!!");
    }
}

示例3: 有参无参构造均有

Day11605.java

package com.determination;

public class Day11605 {
    //一个项目只存在一个main方法
    public static void main(String[] args) {
        //new 实例化一个对象
        Person person=new Person("wind");
        System.out.println("这是个人(类),这个人是谁(对象)"+person.name);
    }
}

Person.java

package com.determination;

import java.security.PublicKey;

public class Person {
    //一个类即使什么都不写,也会存在一个方法
    //显示的定义构造器
    String name;
    //1.使用new关键字,本质就是调用构造器
    //2.用构造器来初始化
    public Person()
    {

    }
    //有参构造,一旦定义了有参构造,无参就要显示定义
    public Person(String name)
    {
        this.name=name;
    }
}
Alt+Insert快捷键迅速得到构造器方法

在这里插入图片描述
在这里插入图片描述
总的来说
1.构造器方法名和类名一致
2. 没有返回值
作用:
1.new本质是调用构造方法
2. 初始化对象的指值
注意:
1.定义有参构造之后,如果想要使用无参构造,我们必须显示定义一个有参构造。

java 创建对象的内存图解 (通俗易懂)

Java面向对象-方法

1.方法

在面向对象编程中,方法(Method)是类或对象中用于执行特定任务的一段代码。方法可以访问和操作对象的属性,并提供了一种封装行为的方式。
方法通常用于描述对象的行为或操作,它们定义了对象可以执行的操作,并在需要时接受输入参数并返回结果。方法可以被其他代码调用,并且可以在需要时被重复使用,从而实现了代码的模块化和重用。
方法一般由以下几个组成部分:

  • 方法名:方法的名称用于标识和区分不同的方法。
  • 参数列表:方法可以接受零个或多个输入参数,这些参数用于传递数据给方法进行处理。
  • 返回类型:方法可能会返回一个值作为结果,返回类型指定了返回值的数据类型。
  • 方法体:方法体是包含实际代码的部分,它描述了方法的具体实现。
package com.determination;

public class Day11601 {
    //main方法
    public static void main(String[] args) {
       sayHello();
       System.out.println("两个数中最大的是:"+max(4,5));
    }
    /*
    修饰符 返回值类型 方法名(。。。){
        方法体
        返回值
    }
     */
    public static void sayHello()
    {
        System.out.println("Hello world!");
        return;
    }
    public static int max(int a,int b)
    {
        return a>b?a:b;
    }
}

当我们调用一个方法时,程序会跳转到该方法的代码块中执行相应的操作。在方法内部,我们可以通过参数来访问传递进来的数据,并通过方法体中的代码来处理这些数据。最后,方法可以根据需要返回一个结果给调用方。

2.静态方法和非静态方法

2.1静态方法

在面向对象编程中,静态方法(Static Method)是一种特殊的方法,不需要创建对象实例即可调用。静态方法是直接与类相关联的,而不是与类的对象相关联的。
使用static关键字来定义静态方法。在静态方法内部,不能直接访问非静态成员变量或方法,因为非静态成员变量和方法需要依赖于对象的实例才能被访问。但是,静态方法可以访问静态成员变量或静态方法。
静态方法在设计模式中有广泛应用,例如工厂模式。在工厂模式中,我们使用静态方法来创建对象,而不是通过new操作符来创建对象。这种方式可以将对象的创建逻辑封装在方法中,并提供更加灵活的对象创建方式。
静态方法还可以用于工具类或辅助类中,这些类通常只提供一些公用方法,而不需要维护任何状态或对象实例。使用静态方法可以避免创建多余的对象实例,从而提高程序的性能和效率。
Day11602.java

package com.determination;

public class Day11602 {
    public static void main(String[] args) {
        Student.say();
    }
}

Student.java

package com.determination;

public class Student {
    //静态方法·
    public static void say()
    {
        System.out.println("学生说话了!!");
    }
}

输出

学生说话了!!
2.2非静态方法

在面向对象编程中,非静态方法(Non-static Method)是与类的对象相关联的方法。也就是说,只有当类的一个实例化对象被创建后,才能调用非静态方法。
非静态方法是通过对象来调用的,因此它们可以直接访问和操作对象的非静态成员变量或方法。在非静态方法内部,可以使用this关键字来引用当前对象,从而获取或修改对象的状态。
使用非静态方法可以实现面向对象编程中的封装和抽象特性。非静态方法通常用于描述对象的行为或操作,例如读取或修改对象的状态、执行某些动作、计算属性等等。
当我们调用一个非静态方法时,程序首先需要创建一个对象实例,然后通过该对象来调用方法。由于每个对象都有一份独立的状态,因此同一个类的不同对象可能会产生不同的结果。
在这里插入图片描述
11602.java

package com.determination;

public class Day11602 {
    public static void main(String[] args) {
        //Student.say();
        Student student=new Student();
        student.say();
    }
}

Student.java

package com.determination;

public class Student {
    //静态方法·
//    public static void say()
//    {
//        System.out.println("学生说话了!!");
//    }
    //非静态方法
    public void say()
    {
        System.out.println("学生说话了!");
    }
}

static 关键字修饰的成员(包括静态方法和静态变量)是与类一起加载的。

在 Java 中,当类被加载到内存中时,静态成员会随着类的加载而加载,并且在类加载过程中只会加载一次。静态成员的加载顺序是按照定义的顺序进行的。
当类被加载时,Java 虚拟机会为该类分配内存,并且静态成员会被初始化。静态变量会被赋予默认值(如果有的话),或者通过显式赋值来初始化。静态方法也会被加载到内存中,供类直接调用。
由于静态成员是和类相关联的,它们可以在没有创建对象实例的情况下被访问和使用。我们可以通过类名直接调用静态方法或访问静态变量,无需创建对象。
需要注意的是,静态成员在内存中存在于整个程序执行期间,直到程序结束或类被卸载。因此,静态成员应谨慎使用,避免滥用静态变量,特别是在多线程环境下,需要考虑线程安全性。
需要注意的是:
实参与形参的参数类型要对应,不能出现定义的形参是int型,结果传递过来的是字符型char

3 参数传递方式

  • 值传递
    值传递是指将实参的值复制一份传递给形参,在方法内部对形参进行修改不会影响到实参本身。简单来说,值传递就是把数据的副本传递给方法。
public void swap(int x, int y) {
    int temp = x;
    x = y;
    y = temp; //没有返回值,仅仅是对a,b的副本x,y进行了交换数值
}
int a = 1, b = 2;
swap(a, b);
System.out.println("a=" + a + ", b=" + b); // 输出 a=1, b=2

在上面的例子中,swap 方法接收两个 int 类型的参数 x 和 y,通过将 x 和 y 的值进行交换来实现交换两个变量的值。但是,当我们调用 swap 方法时,并没有改变 a 和 b 的值,因为 swap 方法接收的是 a 和 b 的副本,交换操作只是在副本中进行的。

  • 引用传递
    引用传递是指将实参的引用传递给形参,在方法内部对形参进行修改会影响到实参本身。简单来说,引用传递就是把数据的地址传递给方法。
public void changeName(Student s, String name) {
    s.setName(name);
}
Student stu = new Student("Tom", 18);
changeName(stu, "Jerry");
System.out.println(stu.getName()); // 输出 Jerry

changeName 方法接收一个 Student 类型的对象 s 和一个 String 类型的参数 name,通过修改 s 对象的姓名来实现修改学生的姓名。当我们调用 changeName 方法时,传递的是 stu 对象的引用,因此在方法内部对 s 对象的修改会影响到 stu 对象本身。

4.方法的重载

方法的重载(Method Overloading)是指在一个类中可以定义多个方法,它们具有相同的名称但参数列表不同(参数个数、参数类型或参数顺序)。通过方法的重载,可以方便地使用相同的方法名进行不同的操作。

方法的重载有以下特点:

  • 方法名相同:重载的方法必须具有相同的名称。
  • 参数列表不同:重载的方法必须在参数列表上有所区别,包括参数个数、参数类型或参数顺序。
  • 返回类型可以相同也可以不同:重载的方法可以具有相同的返回类型,也可以有不同的返回类型。返回类型不是重载方法的区分标准。

例如,下面是一个计算两个整数之和的方法的重载示例:

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
    public double add(double a, double b) {
        return a + b;
    }
}

Calculator 类定义了两个名为 add 的方法,一个接收两个 int 类型的参数,另一个接收两个 double 类型的参数。这样,当我们调用 add 方法时,可以根据传入的参数类型自动选择匹配的方法进行计算。
使用方法重载的好处包括:

  • 提高代码的可读性和可维护性:通过使用相同的方法名进行不同的操作,可以降低方法命名的复杂性,使代码更加清晰易懂。
  • 简化代码逻辑:当我们需要进行类似但略有差异的操作时,可以直接调用重载的方法,避免编写多个类似的方法。
  • 方便使用:通过方法重载,可以根据传入参数的不同灵活地选择合适的方法,方便使用者。
    需要注意的是,在进行方法重载时,应该确保方法的功能是相似的,即具有相同的语义。否则,如果方法的功能不同,容易引起混淆和错误的调用。

5.命令行传参

在 Java 中,命令行参数以字符串数组的形式传递给 main 方法。main 方法的声明如下:

public static void main(String[] args)

其中,args 是一个字符串数组,包含了传递给程序的所有命令行参数。args 数组的长度即为传递参数的个数。

package com.determination;

public class Day11603 {
    public static void main(String[] args) {
        //args.length 数组长度
        for(int i=0;i<args.length;i++)
        {
            System.out.println("args["+i+"]:"+args[i]);
        }
    }
}

然后如下图所示操作
在这里插入图片描述
显示结果:
在这里插入图片描述
需要注意的是,命令行参数总是以字符串的形式传递给程序。如果需要将其转换为其他类型,需要进行相应的类型转换操作。

6.可变参数

可变参数(Varargs)是 Java 5 中引入的一个特性,它允许我们在方法中接受不定数量的参数。可变参数使得方法在调用时更加灵活,可以传递任意数量的参数。
在 Java 中,可变参数通过使用省略号 (…) 来声明。可变参数必须是方法的最后一个参数,且只能有一个可变参数。

下面是一个使用可变参数的示例:

public class VarargsExample {
    public static void main(String[] args) {
        printNumbers(1, 2, 3);
        printNumbers(4, 5, 6, 7, 8);
        printNumbers(9);
    }
    public static void printNumbers(int... numbers) {
        System.out.println("Numbers:");
        for (int number : numbers) {
            System.out.println(number);
        }
    }
}

在上述示例中,printNumbers 方法使用了可变参数 numbers。我们可以向该方法传递任意数量的整数参数。在方法内部,我们可以像操作普通数组一样来处理可变参数。在printNumbers 方法中,我们遍历可变参数数组,并打印每个数字。

Numbers:
1
2
3
Numbers:
4
5
6
7
8
Numbers:
9

需要注意以下几点:

  • 可变参数可以接受零个或多个参数,也可以传递一个数组作为参数。
  • 如果方法有多个参数,可变参数必须放在最后一个参数的位置。
  • 如果方法除了可变参数外还有其他参数,那么传递给可变参数的值将作为数组存储在参数中。
  • 可变参数在编译时被转换为数组。因此,我们可以像处理数组一样来处理可变参数。
  • 如果方法同时存在其他重载方法,编译器会优先选择非可变参数的方法。

7.递归

递归(Recursion)是一种常见的算法设计技巧,它是通过函数自身调用来解决问题的方法。在递归过程中,函数会不断调用自身,直到达到某个终止条件为止。递归通常可以让代码更加简洁而优雅,但同时也需要注意可能带来的性能问题和栈溢出等问题。

public class RecursionExample {
    public static void main(String[] args) {
        int result = factorial(5);
        System.out.println("Factorial of 5 is " + result);
    }

    public static int factorial(int n) {
        if (n == 0) {
            return 1;
        } else {
            return n * factorial(n - 1);
        }
    }
}

上述代码使用递归的方式计算阶乘。在 factorial 方法内部,我们首先判断传入的参数是否为 0,如果是,则返回 1。否则,我们将 n 乘以 (n-1) 的阶乘结果,实现递归调用。

Factorial of 5 is 120

递归虽然在某些情况下可以使代码更加简洁而优雅,但有时也可能会带来一些问题。递归调用需要占用栈空间,如果递归层数过多,可能会导致栈溢出。另外,递归的效率可能不如迭代法,因为在递归调用过程中需要不断地压栈和出栈,增加了额外的开销。
在这里插入图片描述
递归详细讲解

作业

写一个计算器要求实现加减乘除功能,并且能够循环接收新的数据

package com.determination;
import java.util.List;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

public class Day11604 {
    public static void main(String[] args) {
        System.out.println("请输入数字,输入'exit'结束:");
        Scanner scanner=new Scanner(System.in);
        // 创建一个动态大小的数组列表
        List<Integer> numbers = new ArrayList<>();
        while (scanner.hasNext()) {
            String input = scanner.next();

            // 判断是否输入 'exit',如果是则退出循环
            if (input.equals("exit")) {
                break;
            }

            try {
                // 将用户输入的字符串解析为整数,并添加到数组列表中
                int number = Integer.parseInt(input);
                numbers.add(number);
            } catch (NumberFormatException e) {
                System.out.println("无效的输入,请重新输入数字或 'exit' 来结束。");
            }
        }
        // 将数组列表转换为数组
        double[] array = new double[numbers.size()];
        for (int i = 0; i < numbers.size(); i++) {
            array[i] = numbers.get(i);
        }
       System.out.println("相加的结果为:"+add(array));
       System.out.println("相减的结果为:"+decrease(array));
       System.out.println("相乘的结果为:"+multiple(array));
       System.out.println("相除的结果为:"+divde(array));

    }
    public static double  add(double ... number)
    {
        double sum=0;
        for(int i=0;i<number.length;i++)
        {
            sum+=number[i];
        }
        return sum;
    }
    public static double decrease(double ... number)
    {
        double res=number[0];
//        System.out.println(res);
        for(int i=1;i<number.length;i++)
        {
            res-=number[i];
        }
        return res;
    }
    public static double multiple(double ... number)
    {
        double res=number[0];
        for(int i=1;i<number.length;i++)
        {
           res*=number[i];
        }

        return res;
    }
    public static double divde(double ... number)
    {
        double res=number[0];
        for(int i=1;i<number.length;i++)
        {
           res/=number[i];
        }
        return res;
    }
}

运行:

请输入数字,输入'exit'结束:
10 5 exit
相加的结果为:15.0
相减的结果为:5.0
相乘的结果为:50.0
相除的结果为:2.0

MyStringBuffer-自定义实现

在Java中,StringBuffer是一个可变的字符串类,它允许对字符串进行动态修改。与String类不同的是,StringBuffer对象的长度和内容都可以被改变。这使得StringBuffer在需要频繁修改字符串内容时比String更加高效。

StringBuffer类提供了许多方法来操作字符串,比如添加字符、插入字符、替换字符、反转字符串等。由于StringBuffer是可变的,所以它的性能比直接对String对象进行字符串拼接要好,尤其是当需要频繁地修改字符串时。

另外,需要注意的是,StringBuffer是线程安全的,因此适合在多线程环境下使用。如果不需要线程安全性,则可以使用StringBuilder类,它与StringBuffer类功能类似,但不是线程安全的。
自定义MyStringBuffer实现了增加append,插入Insert,删除delete,自定义异常,自动扩容,字符串反转等等。
接口:
在Java中,接口(Interface)是一种抽象类型,它只包含方法的声明而没有方法的实现。接口定义了一组方法的签名,但具体的实现由实现接口的类来完成。任何实现了接口的类都必须实现接口中定义的所有方法。

接口在Java中有以下好处:

实现多继承:Java中一个类只能继承一个类,但可以实现多个接口。通过接口,可以让一个类具备多个行为,实现更灵活的设计。
规范和约定:接口定义了一组方法的规范,强制实现类提供这些方法的实现。这有助于代码的规范化和约定性,提高代码的可读性和可维护性。
解耦合:接口将接口与实现分离,降低了类之间的耦合度。通过接口,可以让类只依赖于接口而不依赖于具体的实现,提高了代码的灵活性和可复用性。
扩展性:接口可以在不影响已有代码的情况下扩展新的功能。实现类可以根据需要实现新的接口,而不需要修改现有代码。

package Basic.String_Num;

public interface IStringBuffer {
   public void append(String str) throws MyStringBuffer.indexIsOutofRangeException,MyStringBuffer.indexIsNagetiveException;
   public  void append(char c) throws MyStringBuffer.indexIsOutofRangeException,MyStringBuffer.indexIsNagetiveException;
   public void insert(int pos,char c) throws MyStringBuffer.indexIsOutofRangeException,MyStringBuffer.indexIsNagetiveException;
   public void insert(int pos,String str) throws MyStringBuffer.indexIsOutofRangeException, MyStringBuffer.indexIsNagetiveException;
   public  void delete(int start) throws MyStringBuffer.indexIsOutofRangeException,MyStringBuffer.indexIsNagetiveException;
   public  void delete(int start,int end) throws MyStringBuffer.indexIsOutofRangeException,MyStringBuffer.indexIsNagetiveException;
   public int length();
   public void reverse();
}

代码实现:

package Basic.String_Num;

import javax.swing.plaf.synth.SynthDesktopIconUI;

public class MyStringBuffer implements IStringBuffer{
    int length=0;
    int capacity=16;
    char []value;
    class indexIsNagetiveException extends Exception{
        public indexIsNagetiveException(){
        }
        public indexIsNagetiveException(String msg){
            super(msg);
        }
    }
    class indexIsOutofRangeException extends Exception{
        public indexIsOutofRangeException(){

        }
        public indexIsOutofRangeException(String msg){
            super(msg);
        }
    }
    public MyStringBuffer(){
        value=new char[capacity];
    }
    public MyStringBuffer(String str){
        this();
        if(str==null)
        {
            throw new NullPointerException("空指针错误!");
        }
        if(str.length()>capacity)
        {
            capacity=2*str.length();
            value=new char[capacity];
        }
        if(capacity>=str.length()){
            System.arraycopy(str.toCharArray(),0,value,0,str.length());
            length=str.length();
        }
    }
    @Override
    public void append(String str) throws  indexIsNagetiveException,indexIsOutofRangeException{
        insert(length,str);
    }

    @Override
    public void append(char c) throws  indexIsNagetiveException,indexIsOutofRangeException{
        append(String.valueOf(c));
    }

    @Override
    public void insert(int pos, char c)throws indexIsNagetiveException,indexIsOutofRangeException {
        insert(pos,String.valueOf(c));
    }

    @Override
    public void insert(int pos, String b) throws indexIsOutofRangeException, indexIsNagetiveException {
        //边界条件判断
        if(pos<0){
            throw new indexIsNagetiveException(String.valueOf(pos)+"下标为负异常");
        }
        if(pos>length)
        {
            throw new indexIsOutofRangeException(String.valueOf(pos)+"下标超出范围异常");
        }
        if(b==null)
        {
            throw new NullPointerException(String.valueOf(pos)+"空指针异常");
        }
        //扩容
        while((length+b.length())>capacity)
        {
            capacity=(int)((length+b.length())*1.5f);
            char[] newvalue=new char[capacity];
            System.arraycopy(value,0,newvalue,0,length);
            value=newvalue;
        }
        char[] cs=b.toCharArray();
        System.arraycopy(value,pos,value,pos+cs.length,length-pos);
        System.arraycopy(cs,0,value,pos,cs.length);
        length=cs.length+length;
    }

    @Override
    public void delete(int start) throws indexIsNagetiveException,indexIsOutofRangeException {
        delete(start,length);
    }

    @Override
    public void delete(int start, int end) throws indexIsNagetiveException, indexIsOutofRangeException {
        if(start<0)
            throw new indexIsNagetiveException(String.valueOf(start)+"下标为负异常");
        if(start>length)
            throw new indexIsOutofRangeException(String.valueOf(start)+"下标超出范围");
        if(end<0)
            throw new indexIsNagetiveException(String.valueOf(end)+"下标为负异常");
        if(end>length)
            throw new indexIsOutofRangeException(String.valueOf(end)+"下标超出范围");
        if(start>=end)
            throw new indexIsOutofRangeException(String.valueOf(start)+"下标超出范围");
        System.arraycopy(value,end,value,start,length-end);
        length-=(end-start);
    }

    @Override
    public int length() {
        return length;
    }

    @Override
    public void reverse() {
        for(int i=0;i<length/2;i++){
            char temp=value[i];
            value[i]=value[length-i-1];
            value[length-i-1]=temp;
        }
    }
    public String toString(){
        char[] realString=new char[length];
        System.arraycopy(value,0,realString,0,length);
        return new String(realString);
    }
    public static void main(String[] args) {
        String str = "dsjflabdji";
        MyStringBuffer sb = new MyStringBuffer(str);
        try {
//            sb.insert(0,null);
            sb.insert(-1,"test");
            sb.insert(11,"test");

            sb.delete(-1, 1);
            sb.delete(6,5);
            sb.delete(11,0);

        } catch (Exception e) {
            // TODO: handle exception
            if(e instanceof NullPointerException)
                System.out.println("出现空指针异常");
            if(e instanceof indexIsNagetiveException | e instanceof indexIsOutofRangeException )
                System.out.println("出现下标异常");
            e.printStackTrace();
        }
    }

}

http://www.kler.cn/a/590456.html

相关文章:

  • 自学Python创建强大AI:从入门到实现DeepSeek级别的AI
  • 多任务学习与持续学习微调:深入探索大型语言模型的性能与适应性
  • 便携版:随时随地,高效处理 PDF 文件
  • matlab 火电厂给水控制系统仿真
  • linux(centos8)下编译ffmpeg
  • AndroidStudio+Android8.0下的Launcher3 导入,编译,烧录,调试
  • K8S学习之基础三十三:K8S之监控Prometheus部署程序版
  • 深度学习项目--基于DenseNet网络的“乳腺癌图像识别”,准确率90%+,pytorch复现
  • 基于YOLOv8与SKU110K数据集实现超市货架物品目标检测与计算
  • 4-001:MySQL 中的索引数量是否越多越好?为什么?
  • dify 源码部署操作记录
  • 微信小程序面试内容整理-事件处理
  • 通向AGI的未来之路!首篇2D/视频/3D/4D统一生成框架全景综述(港科大中山等)
  • Vue中的publicPath释义
  • Vuex知识点总结
  • Unity AI 技术浅析(三):智能代理(Agents)
  • 设计模式学习记录
  • EF框架数据库更新后自动更新类及上下文
  • 【计量地理学】实验一 地理数据的基本统计分析
  • wow-rag学习|搞定模型